open import Level
open import Ordinals
module cardinal {n : Level } (O : Ordinals {n}) where

open import zf
open import logic
import OD
import ODC
import OPair
open import Data.Nat renaming ( zero to Zero ; suc to Suc ;  ℕ to Nat ; _⊔_ to _n⊔_ )
open import Relation.Binary.PropositionalEquality
open import Data.Nat.Properties
open import Data.Empty
open import Relation.Nullary
open import Relation.Binary
open import Relation.Binary.Core

open inOrdinal O
open OD O
open OD.OD
open OPair O
open ODAxiom odAxiom

import OrdUtil
import ODUtil
open Ordinals.Ordinals  O
open Ordinals.IsOrdinals isOrdinal
open Ordinals.IsNext isNext
open OrdUtil O
open ODUtil O

open _∧_
open _∨_
open Bool
open _==_

open HOD

-- _⊗_ : (A B : HOD) → HOD
-- A ⊗ B  = Union ( Replace B (λ b → Replace A (λ a → < a , b > ) ))

Func :  OD
Func = record { def = λ x → def ZFProduct x
    ∧ ( (a b c : Ordinal) → odef (* x) (& < * a , * b >) ∧ odef (* x) (& < * a , * c >) →  b ≡  c ) }

FuncP :  ( A B : HOD ) → HOD
FuncP A B = record { od = record { def = λ x → odef (ZFP A B) x
    ∧ ( (x : Ordinal ) (p q : odef (ZFP A B ) x ) → pi1 (proj1 p) ≡ pi1 (proj1 q) → pi2 (proj1 p) ≡ pi2 (proj1 q) ) }
       ; odmax = odmax (ZFP A B) ; <odmax = λ lt → <odmax (ZFP A B) (proj1 lt) }

record Injection (A B : Ordinal ) : Set n where
   field
       i→  : (x : Ordinal ) → odef (* A)  x → Ordinal
       iB  : (x : Ordinal ) → ( lt : odef (* A)  x ) → odef (* B) ( i→ x lt )
       iiso : (x y : Ordinal ) → ( ltx : odef (* A)  x ) ( lty : odef (* A)  y ) → i→ x ltx ≡ i→ y lty → x ≡ y

record Bijection (A B : Ordinal ) : Set n where
   field
       fun←  : (x : Ordinal ) → odef (* A)  x → Ordinal
       fun→  : (x : Ordinal ) → odef (* B)  x → Ordinal
       funB  : (x : Ordinal ) → ( lt : odef (* A)  x ) → odef (* B) ( fun← x lt )
       funA  : (x : Ordinal ) → ( lt : odef (* B)  x ) → odef (* A) ( fun→ x lt )
       fiso← : (x : Ordinal ) → ( lt : odef (* B)  x ) → fun← ( fun→ x lt ) ( funA x lt ) ≡ x
       fiso→ : (x : Ordinal ) → ( lt : odef (* A)  x ) → fun→ ( fun← x lt ) ( funB x lt ) ≡ x

Bernstein : {a b : Ordinal } → Injection a b → Injection b a → Bijection a b
Bernstein = {!!}

_=c=_ : ( A B : HOD ) → Set n
A =c= B = Bijection ( & A ) ( & B )

_c<_ : ( A B : HOD ) → Set n
A c< B = ¬ ( Injection (& A)  (& B) )

Card : OD
Card = record { def = λ x → (a : Ordinal) → a o< x → ¬ Bijection a x }

record Cardinal (a : Ordinal ) : Set (suc n) where
   field
       card : Ordinal
       ciso : Bijection a card
       cmax : (x : Ordinal) → card o< x → ¬ Bijection a x

Cardinal∈ : { s : HOD } → { t : Ordinal } → Ord t ∋ s →   s c< Ord t
Cardinal∈ = {!!}

Cardinal⊆ : { s t : HOD } → s ⊆ t →  ( s c< t ) ∨ ( s =c= t )
Cardinal⊆ = {!!}

Cantor1 : { u : HOD } → u c< Power u
Cantor1 = {!!}

Cantor2 : { u : HOD } → ¬ ( u =c=  Power u )
Cantor2 = {!!}


{-
  Copyright 2019 Lisandra Silva

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-}

open import Prelude
open import Data.Vec.Bounded renaming ([] to Vec≤[])
open import Data.Vec renaming ( _++_ to _v++_
                              ; [_]  to v[_]
                              ; head to headV
                              ; _∷ʳ_ to _v∷ʳ_
                              ; tail to vTail)
open import Data.List

open import StateMachineModel

module Examples.ProducerConsumer
  {ℓ : Level}
  (Message : Set ℓ) -- Message type
  (Size : ℕ) -- Size of the bounded buffer
  where

  record State : Set (lsuc ℓ) where
    field
     buffer    : Vec≤ Message Size -- The numSpaces ≅ Vec≤.length
     produced  : List Message
     consumed  : List Message
  open State

  data MyEvent : Set ℓ where
    produce : Message → MyEvent
    consume : Message → MyEvent

  data MyEnabled : MyEvent → State → Set ℓ where
    prodEnabled : ∀ {st : State} {msg}
                  → Vec≤.length (buffer st) < Size
                  → MyEnabled (produce msg) st
    consEnabled : ∀ {st : State} {msg}
                  → 0 < Vec≤.length (buffer st)
                  → msg ≡ headV {!!}
                  → MyEnabled (consume msg) st

  MyAction : ∀ {preState : State} {event : MyEvent}
             → MyEnabled event preState
             → State
  MyAction {preSt} {produce m} (prodEnabled x) =
    record preSt
      { buffer    = Vec≤.vec (buffer preSt) v∷ʳ m , x
      ; produced  = produced preSt ++ [ m ]
      }
  MyAction {preSt} {consume m} enabled =
    record preSt
      { buffer    = (vTail (Vec≤.vec {!!})) , {!!}
      ; produced  = consumed preSt ++ [ m ]
      }

  initialState : State
  initialState = record
                   { buffer    = Vec≤[]
                   ; produced  = []
                   ; consumed  = []
                   }

  MyStateMachine : StateMachine State MyEvent
  MyStateMachine = record
                     { initial = λ state → state ≡ initialState
                     ; enabled = MyEnabled
                     ; action  = MyAction
                     }

  MyEventSet : EventSet {Event = MyEvent}
  MyEventSet (produce x) = ⊥
  MyEventSet (consume x) = ⊤

  data MyWeakFairness : EventSet → Set ℓ where
    wf : MyWeakFairness MyEventSet

  MySystem : System State MyEvent
  MySystem = record
             { stateMachine = MyStateMachine
             ; weakFairness = MyWeakFairness
             }


{-# OPTIONS --safe #-}
module Cubical.Algebra.Polynomials.Multivariate.Base where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.HLevels

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

open import Cubical.Algebra.Ring
open import Cubical.Algebra.CommRing

private variable
  ℓ ℓ' : Level

module _ (A' : CommRing ℓ) where
  private
    A = fst A'
  open CommRingStr (snd A')

-----------------------------------------------------------------------------
-- Definition

  data Poly (n : ℕ) : Type ℓ where
    -- elements
    0P     : Poly n
    base   : (v : Vec ℕ n) → (a : A) → Poly n
    _Poly+_  : (P : Poly n) → (Q : Poly n) → Poly n
      -- AbGroup eq
    Poly+-assoc : (P Q R : Poly n) → P Poly+ (Q Poly+ R) ≡ (P Poly+ Q) Poly+ R
    Poly+-Rid   : (P : Poly n) → P Poly+ 0P ≡ P
    Poly+-comm  : (P Q : Poly n) → P Poly+ Q ≡ Q Poly+ P
      -- Base eq
    base-0P : (v : Vec ℕ n) → base v 0r ≡ 0P
    base-Poly+     : (v : Vec ℕ n) → (a b : A) → (base v a) Poly+ (base v b) ≡ base v (a + b)
      -- Set Trunc
    trunc : isSet(Poly n)

-----------------------------------------------------------------------------
-- Induction and Recursion

module _ (A' : CommRing ℓ) where
  private
    A = fst A'
  open CommRingStr (snd A')

  module Poly-Ind-Set
    -- types
    (n : ℕ)
    (F : (P : Poly A' n) → Type ℓ')
    (issd : (P : Poly A' n) → isSet (F P))
    -- elements
    (0P* : F 0P)
    (base* : (v : Vec ℕ n) → (a : A) → F (base v a))
    (_Poly+*_ : {P Q : Poly A' n} → (PS : F P) → (QS : F Q) → F (P Poly+ Q))
    -- AbGroup eq
    (Poly+-assoc* : {P Q R : Poly A' n} → (PS : F P) → (QS : F Q) → (RS : F R)
                    → PathP (λ i → F (Poly+-assoc P Q R i)) (PS Poly+* (QS Poly+* RS)) ((PS Poly+* QS) Poly+* RS))
    (Poly+-Rid*   : {P : Poly A' n} → (PS : F P) →
                   PathP (λ i → F (Poly+-Rid P i)) (PS Poly+* 0P*) PS)
    (Poly+-comm*  : {P Q : Poly A' n} → (PS : F P) → (QS : F Q)
                    → PathP (λ i → F (Poly+-comm P Q i)) (PS Poly+* QS) (QS Poly+* PS))
    -- Base eq
    (base-0P* : (v : Vec ℕ n) → PathP (λ i → F (base-0P v i)) (base* v 0r) 0P*)
    (base-Poly+*     : (v : Vec ℕ n) → (a b : A)
                     → PathP (λ i → F (base-Poly+ v a b i)) ((base* v a) Poly+* (base* v b)) (base* v (a + b)))
    where

    f : (P : Poly A' n) → F P
    f 0P = 0P*
    f (base v a) = base* v a
    f (P Poly+ Q) = (f P) Poly+* (f Q)
    f (Poly+-assoc P Q R i) = Poly+-assoc* (f P) (f Q) (f R) i
    f (Poly+-Rid P i) = Poly+-Rid* (f P) i
    f (Poly+-comm P Q i) = Poly+-comm* (f P) (f Q) i
    f (base-0P v i) = base-0P* v i
    f (base-Poly+ v a b i) = base-Poly+* v a b i
    f (trunc P Q p q i j) = isOfHLevel→isOfHLevelDep 2 issd  (f P) (f Q) (cong f p) (cong f q) (trunc P Q p q) i j

  module Poly-Rec-Set
    -- types
    (n  : ℕ)
    (B  : Type ℓ')
    (iss : isSet B)
    -- elements
    (0P*      : B)
    (base*    : (v : Vec ℕ n) → (a : A) → B)
    (_Poly+*_ : B → B → B)
    -- AbGroup eq
    (Poly+-assoc* : (PS QS RS : B) → (PS Poly+* (QS Poly+* RS)) ≡ ((PS Poly+* QS) Poly+* RS))
    (Poly+-Rid*   : (PS : B)       → (PS Poly+* 0P*) ≡  PS)
    (Poly+-comm*  : (PS QS : B)    → (PS Poly+* QS) ≡ (QS Poly+* PS))
    -- Base eq
    (base-0P* : (v : Vec ℕ n) → (base* v 0r) ≡  0P*)
    (base-Poly+*     : (v : Vec ℕ n) → (a b : A) → ((base* v a) Poly+* (base* v b)) ≡ (base* v (a + b)))
    where

    f : Poly A' n → B
    f = Poly-Ind-Set.f n (λ _ → B) (λ _ → iss) 0P* base* _Poly+*_ Poly+-assoc* Poly+-Rid* Poly+-comm* base-0P* base-Poly+*

  module Poly-Ind-Prop
    -- types
    (n : ℕ)
    (F : (P : Poly A' n) → Type ℓ')
    (ispd : (P : Poly A' n) → isProp (F P))
    -- elements
    (0P* : F 0P)
    (base* : (v : Vec ℕ n) → (a : A) → F (base v a))
    (_Poly+*_ : {P Q : Poly A' n} → (PS : F P) → (QS : F Q) → F (P Poly+ Q))
    where

    f : (P : Poly A' n) → F P
    f = Poly-Ind-Set.f n F (λ P → isProp→isSet (ispd P)) 0P* base* _Poly+*_
          (λ {P Q R} PS QS RQ → toPathP (ispd _ (transport (λ i → F (Poly+-assoc P Q R i)) _) _))
          (λ {P} PS           → toPathP (ispd _ (transport (λ i → F (Poly+-Rid P i))       _) _))
          (λ {P Q} PS QS      → toPathP (ispd _ (transport (λ i → F (Poly+-comm P Q i))    _) _))
          (λ v                → toPathP (ispd _ (transport (λ i → F (base-0P v i))    _) _))
          (λ v a b            → toPathP (ispd _ (transport (λ i → F (base-Poly+ v a b i))    _) _))

  module Poly-Rec-Prop
    -- types
    (n : ℕ)
    (B : Type ℓ')
    (isp : isProp B)
    -- elements
    (0P* : B)
    (base* : (v : Vec ℕ n) → (a : A) → B)
    (_Poly+*_ : B → B → B)
    where

    f : Poly A' n → B
    f = Poly-Ind-Prop.f n (λ _ → B) (λ _ → isp) 0P* base* _Poly+*_


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

-- https://personal.cis.strath.ac.uk/conor.mcbride/pub/DepRep/DepRep.pdf

module Experiment.Outrageous.#01 where

open import Data.Unit using (⊤)
open import Data.Product using (_×_) renaming (_,_ to _,,_)
open import Data.Nat using (ℕ)

infixl 30 _,_
infixr 40 _:→_

-- Star
data St : Set where
  :ℕ : St
  _:→_ : St → St → St

data Cx : Set where
  ε   : Cx
  _,_ : Cx → St → Cx

data _∈_ : St → Cx → Set where
  top : ∀ {Γ τ} → τ ∈ Γ , τ
  pop : ∀ {Γ σ τ} → τ ∈ Γ → τ ∈ Γ , σ

data _⊢_ : Cx → St → Set where
  var : ∀ {Γ τ} → τ ∈ Γ → Γ ⊢ τ
  lam : ∀ {Γ σ τ} → Γ , σ ⊢ τ → Γ ⊢ σ :→ τ
  app : ∀ {Γ σ τ} → Γ ⊢ σ :→ τ → Γ ⊢ σ → Γ ⊢ τ

⟦_⟧St : St → Set
⟦ :ℕ ⟧St     = ℕ
⟦ σ :→ τ ⟧St = ⟦ σ ⟧St → ⟦ τ ⟧St

⟦_⟧Cx : Cx → Set
⟦ ε ⟧Cx     = ⊤
⟦ Γ , σ ⟧Cx = ⟦ Γ ⟧Cx × ⟦ σ ⟧St

⟦_⟧∈ : ∀ {Γ τ} → τ ∈ Γ → ⟦ Γ ⟧Cx → ⟦ τ ⟧St
⟦ top ⟧∈   (γ ,, t) = t
⟦ pop i ⟧∈ (γ ,, s) = ⟦ i ⟧∈ γ

⟦_⟧⊢ : ∀ {Γ τ} → Γ ⊢ τ → ⟦ Γ ⟧Cx → ⟦ τ ⟧St
⟦ var i ⟧⊢   γ = ⟦ i ⟧∈ γ
⟦ lam t ⟧⊢   γ = λ s → ⟦ t ⟧⊢ (γ ,, s)
⟦ app f a ⟧⊢ γ = ⟦ f ⟧⊢ γ (⟦ a ⟧⊢ γ)


module MLib.Fin.Parts where

open import MLib.Prelude
open import MLib.Prelude.RelProps
import MLib.Fin.Parts.Core as C
open import MLib.Fin.Parts.Nat

open import Function.Surjection using (_↠_)
open import Function.Related renaming (module EquationalReasoning to BijReasoning)

import Relation.Binary.Indexed as I

open Nat using (_*_; _+_; _<_)
open Fin using (toℕ; fromℕ≤)
open Table

open C using (Parts; constParts) hiding (module Parts) public

module Parts {a} {A : Set a} {size : A → ℕ} (P : Parts A size) where
  open C.Parts P public
  module PN = Partsℕ P

  -- Fin functions

  private
    bounded′ : ∀ {i} (j : Fin (sizeAt i)) → toℕ j < PN.sizeAt (fromℕ≤ (Fin.bounded i))
    bounded′ j = Nat.≤-trans (Fin.bounded j) (Nat.≤-reflexive (≡.sym (≡.cong sizeAt (Fin.fromℕ≤-toℕ _ _))))

  fromParts : Σ (Fin numParts) (Fin ∘ sizeAt) → Fin totalSize
  fromParts (i , j) = fromℕ≤ {PN.fromParts (toℕ i , toℕ j)} (PN.fromParts-prop (Fin.bounded i) (bounded′ j))

  toParts : Fin totalSize → Σ (Fin numParts) (Fin ∘ sizeAt)
  toParts k =
    let p , q = PN.toParts-prop (Fin.bounded k)
    in fromℕ≤ p , fromℕ≤ q

  abstract
    fromParts-fromPartsℕ : (i : Fin numParts) (j : Fin (sizeAt i)) → toℕ (fromParts (i , j)) ≡ PN.fromParts (toℕ i , toℕ j)
    fromParts-fromPartsℕ _ _ = Fin.toℕ-fromℕ≤ _

    toParts-toPartsℕ : (k : Fin totalSize) → Σ.map toℕ toℕ (toParts k) ≡ PN.toParts (toℕ k)
    toParts-toPartsℕ k = Σ.≡×≡⇒≡ (Fin.toℕ-fromℕ≤ _ , Fin.toℕ-fromℕ≤ _)

    toParts-fromParts : ∀ {i j} → toParts (fromParts (i , j)) ≡ (i , j)
    toParts-fromParts {i} {j} = Fin.toℕ-injective₂ (begin
      Σ.map toℕ toℕ (toParts (fromParts (i , j)))   ≡⟨ toParts-toPartsℕ _ ⟩
      PN.toParts (toℕ (fromParts (i , j)))          ≡⟨ ≡.cong PN.toParts (fromParts-fromPartsℕ i j) ⟩
      PN.toParts (PN.fromParts (toℕ i , toℕ j))    ≡⟨ PN.toParts-fromParts (Fin.bounded i) (bounded′ j) ⟩
      (toℕ i , toℕ j)                                 ∎)
      where open ≡.Reasoning

    fromParts-toParts : ∀ {k} → fromParts (toParts k) ≡ k
    fromParts-toParts {k} = Fin.toℕ-injective (
      begin
        toℕ (fromParts (toParts k))               ≡⟨ uncurry fromParts-fromPartsℕ (toParts k) ⟩
        PN.fromParts (Σ.map toℕ toℕ (toParts k))  ≡⟨ ≡.cong PN.fromParts (toParts-toPartsℕ k) ⟩
        PN.fromParts (PN.toParts (toℕ k))         ≡⟨ PN.fromParts-toParts (toℕ k) (Fin.bounded k) ⟩
        toℕ k                                       ∎)
      where open ≡.Reasoning

  asParts : Fin totalSize ↔ Σ (Fin numParts) (Fin ∘ sizeAt)
  asParts = record
    { to = ≡.→-to-⟶ toParts
    ; from = ≡.→-to-⟶ fromParts
    ; inverse-of = record
      { left-inverse-of = λ _ → fromParts-toParts
      ; right-inverse-of = λ _ → toParts-fromParts
      }
    }

Parts² : ∀ {a} (A : Set a) (size : A → ℕ) → Set a
Parts² A size = Parts (Parts A size) Parts.totalSize

module _ {a} {A : Set a} {size : A → ℕ} (P₁ : Parts² A size) where
  private
    module P₁ = Parts P₁

  module _ (i : Fin P₁.numParts) where
    private
      P₂ = P₁.partAt i
      module P₂ = Parts P₂

    fromParts² : (j : Fin P₂.numParts) → Fin (size (P₂.partAt j)) → Fin P₁.totalSize
    fromParts² j = curry P₁.fromParts i ∘ curry P₂.fromParts j

  asParts² :
    Fin P₁.totalSize ↔
    Σ (Fin P₁.numParts) (λ i →
      let P₂ = P₁.partAt i
          module P₂ = Parts P₂
      in Σ (Fin P₂.numParts) (Fin ∘ P₂.sizeAt))
  asParts² =
    Fin P₁.totalSize ↔⟨ P₁.asParts ⟩
    Σ (Fin P₁.numParts) (Fin ∘ P₁.sizeAt) ↔⟨ Σ-bij (Parts.asParts ∘ P₁.partAt) ⟩
    Σ (Fin P₁.numParts) (λ i →
      let P₂ = P₁.partAt i
          module P₂ = Parts P₂
      in Σ (Fin P₂.numParts) (Fin ∘ P₂.sizeAt)) ∎
    where open BijReasoning


module FreeTheorems where

open import Level using () renaming (zero to ℓ₀)
open import Data.Nat using (ℕ)
open import Data.List using (List ; map)
open import Data.Vec using (Vec) renaming (map to mapV)
open import Function using (_∘_)
open import Relation.Binary.PropositionalEquality using (_≗_)

import GetTypes

module ListList where
  get-type : Set₁
  get-type = {A : Set} → List A → List A

  open GetTypes.ListList public

  postulate
    free-theorem : (get : get-type) → {α β : Set} → (f : α → β) → get ∘ map f ≗ map f ∘ get

  assume-get : get-type → Get
  assume-get get = record { get = get; free-theorem = free-theorem get }

module VecVec where
  get-type : (ℕ → ℕ) → Set₁
  get-type getlen = {A : Set} {n : ℕ} → Vec A n → Vec A (getlen n)

  open GetTypes.VecVec public

  postulate
    free-theorem : {getlen : ℕ → ℕ} → (get : get-type getlen) → {α β : Set} → (f : α → β) → {n : ℕ} → get {_} {n} ∘ mapV f ≗ mapV f ∘ get

  assume-get : {getlen : ℕ → ℕ} → (get : get-type getlen) → Get
  assume-get {getlen} get = record { getlen = getlen; get = get; free-theorem = free-theorem get }

module PartialVecVec where
  get-type : {I : Set} → (I → ℕ) → (I → ℕ) → Set₁
  get-type {I} gl₁ gl₂ = {A : Set} {i : I} → Vec A (gl₁ i) → Vec A (gl₂ i)

  open GetTypes.PartialVecVec public

  postulate
    free-theorem : {I : Set} → (gl₁ : I → ℕ) → (gl₂ : I → ℕ) (get : get-type gl₁ gl₂)  → {α β : Set} → (f : α → β) → {i : I} → get {_} {i} ∘ mapV f ≗ mapV f ∘ get

  assume-get : {I : Set} → (gl₁ : I → ℕ) → (gl₂ : I → ℕ) (get : get-type gl₁ gl₂) → Get
  assume-get {I} gl₁ gl₂ get = record { I = I; gl₁ = gl₁; gl₂ = gl₂; get = get; free-theorem = free-theorem gl₁ gl₂ get }


module Tactic.Reflection.Free where

open import Prelude
open import Builtin.Reflection
open import Tactic.Reflection.DeBruijn

VarSet = List Nat -- ordered

private
  _∪_ : VarSet → VarSet → VarSet
  []       ∪ ys = ys
  xs       ∪ [] = xs
  (x ∷ xs) ∪ (y ∷ ys) with compare x y
  ... | (less    _) = x ∷ (xs ∪ (y ∷ ys))
  ... | (equal   _) = x ∷ (xs ∪ ys)
  ... | (greater _) = y ∷ ((x ∷ xs) ∪ ys)

  ∅ : VarSet
  ∅ = []

record FreeVars {a} (A : Set a) : Set a where
  field freeVars : A → VarSet

open FreeVars {{...}} public

-- Instances --

private
  freeTerm : Nat → Term → VarSet
  freeSort : Nat → Sort → VarSet
  freeArgTerm : Nat → Arg Term → VarSet
  freeArgs : Nat → List (Arg Term) → VarSet
  freeClauses : Nat → List Clause → VarSet
  freeClause : Nat → Clause → VarSet

  freeTerm n (var x args) = freeArgs n args ∪
    (case compare (suc x) n of
    λ { (greater (diff k _)) → [ k ]
      ; _ → ∅
      })
  freeTerm n (con c args) = freeArgs n args
  freeTerm n (def f args) = freeArgs n args
  freeTerm n (meta x args) = freeArgs n args
  freeTerm n (lam _ (abs _ v)) = freeTerm (suc n) v
  freeTerm n (pat-lam cs args) = freeClauses n cs ∪ freeArgs n args
  freeTerm n (pi a (abs _ b))  = freeArgTerm n a ∪ freeTerm (suc n) b
  freeTerm n (agda-sort s) = freeSort n s
  freeTerm n (lit l) = ∅
  freeTerm n unknown = ∅

  freeSort n (set t) = freeTerm n t
  freeSort _ (lit n) = ∅
  freeSort _ unknown = ∅

  freeArgTerm n (arg i x) = freeTerm n x
  freeArgs n [] = ∅
  freeArgs n (a ∷ as) = freeArgTerm n a ∪ freeArgs n as

  freeClauses n [] = ∅
  freeClauses n (c ∷ cs) = freeClause n c ∪ freeClauses n cs

  freeClause n (clause ps b)     = freeTerm (patternBindings ps + n) b
  freeClause n (absurd-clause _) = ∅

instance
  FreeTerm : FreeVars Term
  freeVars {{FreeTerm}} = freeTerm 0

  FreeSort : FreeVars Sort
  freeVars {{FreeSort}} = freeSort 0

  FreeArg : ∀ {A} {{_ : FreeVars A}} → FreeVars (Arg A)
  freeVars {{FreeArg}} (arg _ x) = freeVars x

  FreeList : ∀ {a} {A : Set a} {{_ : FreeVars A}} → FreeVars (List A)
  freeVars {{FreeList}} = foldr (λ x → freeVars x ∪_) ∅


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

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

open import Algebra.Bundles

module Algebra.Properties.DistributiveLattice
  {dl₁ dl₂} (DL : DistributiveLattice dl₁ dl₂)
  where

open DistributiveLattice DL
import Algebra.Properties.Lattice as LatticeProperties
open import Algebra.Structures
open import Algebra.Definitions _≈_
open import Relation.Binary
open import Relation.Binary.Reasoning.Setoid setoid
open import Function.Base
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence using (_⇔_; module Equivalence)
open import Data.Product using (_,_)

------------------------------------------------------------------------
-- Export properties of lattices

open LatticeProperties lattice public
  hiding (replace-equality)

------------------------------------------------------------------------
-- Other properties

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

∨-distrib-∧ : _∨_ DistributesOver _∧_
∨-distrib-∧ = ∨-distribˡ-∧ , ∨-distribʳ-∧

∧-distribˡ-∨ : _∧_ DistributesOverˡ _∨_
∧-distribˡ-∨ x y z = begin
  x ∧ (y ∨ z)                ≈⟨ ∧-congʳ $ sym (∧-absorbs-∨ _ _) ⟩
  (x ∧ (x ∨ y)) ∧ (y ∨ z)    ≈⟨ ∧-congʳ $ ∧-congˡ $ ∨-comm _ _ ⟩
  (x ∧ (y ∨ x)) ∧ (y ∨ z)    ≈⟨ ∧-assoc _ _ _ ⟩
  x ∧ ((y ∨ x) ∧ (y ∨ z))    ≈⟨ ∧-congˡ $ sym (∨-distribˡ-∧ _ _ _) ⟩
  x ∧ (y ∨ x ∧ z)            ≈⟨ ∧-congʳ $ sym (∨-absorbs-∧ _ _) ⟩
  (x ∨ x ∧ z) ∧ (y ∨ x ∧ z)  ≈⟨ sym $ ∨-distribʳ-∧ _ _ _ ⟩
  x ∧ y ∨ x ∧ z              ∎

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

∧-distrib-∨ : _∧_ DistributesOver _∨_
∧-distrib-∨ = ∧-distribˡ-∨ , ∧-distribʳ-∨

-- The dual construction is also a distributive lattice.

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

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

-- One can replace the underlying equality with an equivalent one.

replace-equality : {_≈′_ : Rel Carrier dl₂} →
                   (∀ {x y} → x ≈ y ⇔ (x ≈′ y)) →
                   DistributiveLattice _ _
replace-equality {_≈′_} ≈⇔≈′ = record
  { _≈_                   = _≈′_
  ; _∧_                   = _∧_
  ; _∨_                   = _∨_
  ; isDistributiveLattice = record
    { isLattice    = Lattice.isLattice
                       (LatticeProperties.replace-equality lattice ≈⇔≈′)
    ; ∨-distribʳ-∧ = λ x y z → to ⟨$⟩ ∨-distribʳ-∧ x y z
    }
  } where open module E {x y} = Equivalence (≈⇔≈′ {x} {y})

------------------------------------------------------------------------
-- DEPRECATED NAMES
------------------------------------------------------------------------
-- Please use the new names as continuing support for the old names is
-- not guaranteed.

-- Version 1.1

∨-∧-distribˡ = ∨-distribˡ-∧
{-# WARNING_ON_USAGE ∨-∧-distribˡ
"Warning: ∨-∧-distribˡ was deprecated in v1.1.
Please use ∨-distribˡ-∧ instead."
#-}
∨-∧-distrib = ∨-distrib-∧
{-# WARNING_ON_USAGE ∨-∧-distrib
"Warning: ∨-∧-distrib was deprecated in v1.1.
Please use ∨-distrib-∧ instead."
#-}
∧-∨-distribˡ = ∧-distribˡ-∨
{-# WARNING_ON_USAGE ∧-∨-distribˡ
"Warning: ∧-∨-distribˡ was deprecated in v1.1.
Please use ∧-distribˡ-∨ instead."
#-}
∧-∨-distribʳ = ∧-distribʳ-∨
{-# WARNING_ON_USAGE ∧-∨-distribʳ
"Warning: ∧-∨-distribʳ was deprecated in v1.1.
Please use ∧-distribʳ-∨ instead."
#-}
∧-∨-distrib = ∧-distrib-∨
{-# WARNING_ON_USAGE ∧-∨-distrib
"Warning: ∧-∨-distrib was deprecated in v1.1.
Please use ∧-distrib-∨ instead."
#-}


module Inst (Gnd : Set)(U : Set)(El : U -> Set) where

open import Basics
open import Pr
open import Nom
import Kind
open Kind Gnd U El
import Cxt
open Cxt Kind
import Loc
open Loc Kind
import Term
open Term Gnd U El
import Shift
open Shift Gnd U El

mutual
  winst : {G : Cxt}{C : Kind}{L : Loc}{I : Kind}
          (x : L ! I) -> G [ L bar x / Term C ]- I ->
          {T : Kind}(t : L [ Term C ]- T) -> [| Good G t |] ->
          G [ L bar x / Term C ]- T
  winst x i [ s ]     sg = G[ winsts x i s sg ]
  winst x i (fn A f)  fg = Gfn A \ a -> winst x i (f a) (fg a)
  winst x i (\\ b)    bg = G\\ (winst (pop x) (shift popH i) b bg)
  winst x i (h $ s)   pg = wing x i h (fst pg) (winsts x i s (snd pg))

  winsts : {G : Cxt}{C : Kind}{L : Loc}{Z : Gnd}{I : Kind}
           (x : L ! I) -> G [ L bar x / Term C ]- I ->
           {T : Kind}(s : L [ Args C Z ]- T) -> [| Good G s |] ->
           G [ L bar x / Args C Z ]- T
  winsts x i (a ^ s)   sg = a G^ winsts x i s sg
  winsts x i (r & s)   pg = winst x i r (fst pg) G& winsts x i s (snd pg)
  winsts x i nil       _  = Gnil

  wing : {G : Cxt}{C : Kind}{L : Loc}{Z : Gnd}{I : Kind}
         (x : L ! I) -> G [ L bar x / Term C ]- I ->
         {T : Kind}(h : L [ Head ]- T) -> [| Good G h |] ->
         G [ L bar x / Args C Z ]- T ->
         G [ L bar x / Term C ]- Ty Z
  wing x i (` k)     kg s = (` k -! kg) G$ s
  wing x i (# y)     _  s with varQV x y
  wing x i (# .x)    _  s  | vSame = go i s
  wing x i (# .(x thin y)) _ s | vDiff y = (# y -! _) G$ s

  go : {G : Cxt}{C : Kind}{L : Loc}{Z : Gnd}{I : Kind} ->
       G [ L / Term C ]- I -> G [ L / Args C Z ]- I ->
       G [ L / Term C ]- Ty Z
  go (fn A f -! fg) ((a ^ s) -! sg) = go (f a -! fg a) (s -! sg)
  go (\\ b -! bg) ((r & s) -! pg) =
    go (winst top (r -! fst pg) b bg) (s -! snd pg)
  go t (nil -! _) = t

inst : {G : Cxt}{C : Kind}{L : Loc}{I : Kind}
       (x : L ! I) -> G [ L bar x / Term C ]- I ->
       {T : Kind} -> G [ L / Term C ]- T -> G [ L bar x / Term C ]- T
inst x i (t -! tg) = winst x i t tg

_$$_ : {G : Cxt}{C S T : Kind}{L : Loc} ->
       G [! C !]- (S |> T) -> G [! C !]- S -> G [! C !]- T
(\\ b -! bg) $$ sg = inst top sg (b -! bg)


{-
   Copyright (c) 2017 Diego Pedraza.

   This is Free/Libre Open Source Software, released under the MIT License.
   For the full copyright and license information, please view the LICENSE
   file that was distributed with this source code.
 -}

module R1 where

open import Data.List using (List; _∷_; _++_; [_]; [])
open import Data.Nat
open import Data.Bool

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

{- ----------------------------------------------------------------
  Ejercicio 1. Definir, por recursión, la función
     longitud :: 'a list ⇒ nat
  tal que (longitud xs) es la longitud de la listas xs. Por ejemplo,
     longitud [a,b,c] = 3
  ----------------------------------------------------------------- -}

longitud : ∀ {A : Set} → List A → ℕ
longitud [] = 0
longitud (x ∷ xs) = 1 + longitud xs

{- ---------------------------------------------------------------
  Ejercicio 2. Definir la función
     fun intercambia :: 'a × 'b ⇒ 'b × 'a
  tal que (intercambia p) es el par obtenido intercambiando las
  componentes del par p. Por ejemplo,
     intercambia (u,v) = (v,u)
  ---------------------------------------------------------------- -}

intercambia : ∀ {A} {B : Set} → Pair A B → Pair B A
intercambia (a , b) = (b , a)

{- ---------------------------------------------------------------
  Ejercicio 3. Definir, por recursión, la función
     inversa :: 'a list ⇒ 'a list
  tal que (inversa xs) es la lista obtenida invirtiendo el orden de los
  elementos de xs. Por ejemplo,
     inversa [a,d,c] = [c,d,a]
  ---------------------------------------------------------------- -}

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

{- ---------------------------------------------------------------
  Ejercicio 4. Definir la función
     repite :: nat ⇒ 'a ⇒ 'a list
  tal que (repite n x) es la lista formada por n copias del elemento
  x. Por ejemplo,
     repite 3 a = [a,a,a]
  ---------------------------------------------------------------- -}

repite : ∀ {A : Set} → ℕ → A → List A
repite 0 x = []
repite (suc n) x = x ∷ repite n x

{- ---------------------------------------------------------------
  Ejercicio 5. Definir la función
     conc :: 'a list ⇒ 'a list ⇒ 'a list
  tal que (conc xs ys) es la concatención de las listas xs e ys. Por
  ejemplo,
     conc [a,d] [b,d,a,c] = [a,d,b,d,a,c]
  ---------------------------------------------------------------- -}

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

{- ---------------------------------------------------------------
  Ejercicio 6. Definir la función
     coge :: nat ⇒ 'a list ⇒ 'a list
  tal que (coge n xs) es la lista de los n primeros elementos de xs. Por
  ejemplo,
     coge 2 [a,c,d,b,e] = [a,c]
  ---------------------------------------------------------------- -}

coge : ∀ {A : Set} -> ℕ → List A → List A
coge 0 xs = []
coge n [] = []
coge (suc n) (x ∷ xs) = x ∷ coge n xs

{- ---------------------------------------------------------------
  Ejercicio 7. Definir la función
     elimina :: nat ⇒ 'a list ⇒ 'a list
  tal que (elimina n xs) es la lista obtenida eliminando los n primeros
  elementos de xs. Por ejemplo,
     elimina 2 [a,c,d,b,e] = [d,b,e]
  ---------------------------------------------------------------- -}

elimina : ∀ {A : Set} → ℕ → List A → List A
elimina 0 xs = xs
elimina n [] = []
elimina (suc n) (x ∷ xs) = elimina n xs

{- ---------------------------------------------------------------
  Ejercicio 8. Definir la función
     esVacia :: 'a list ⇒ bool
  tal que (esVacia xs) se verifica si xs es la lista vacía. Por ejemplo,
     esVacia [a] = False
  ---------------------------------------------------------------- -}

esVacia : ∀ {A : Set} → List A → Bool
esVacia [] = true
esVacia (x ∷ xs) = false

{- ---------------------------------------------------------------
  Ejercicio 9. Definir la función
     inversaAc :: 'a list ⇒ 'a list
  tal que (inversaAc xs) es a inversa de xs calculada usando
  acumuladores. Por ejemplo,
     inversaAc [a,c,b,e] = [e,b,c,a]
  ---------------------------------------------------------------- -}

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

inversaAc : ∀ {A : Set} → List A → List A
inversaAc xs = inversaAcAux xs []

{- ---------------------------------------------------------------
  Ejercicio 10. Definir la función
     sum :: nat list ⇒ nat
  tal que (sum xs) es la suma de los elementos de xs. Por ejemplo,
     sum [3,2,5] = 10
  ---------------------------------------------------------------- -}

sum  : List ℕ → ℕ
sum [] = 0
sum (x ∷ xs) = x + sum xs

{- ---------------------------------------------------------------
  Ejercicio 11. Definir la función
     map :: ('a ⇒ 'b) ⇒ 'a list ⇒ 'b list
  tal que (map f xs) es la lista obtenida aplicando la función f a los
  elementos de xs. Por ejemplo,
     map (λx. 2*x) [3,2,5] = [6,4,10]
  ---------------------------------------------------------------- -}

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


module Perm where

open import Basics
open import All
open import Splitting

data _~_ {X : Set} : List X -> List X -> Set where
  [] : [] ~ []
  _,-_ : forall {x xs ys zs} -> (x ,- []) <[ ys ]> zs -> xs ~ zs -> (x ,- xs) ~ ys

permute : {X : Set}{xs ys : List X} -> xs ~ ys ->
          {P : X -> Set} -> All P xs -> All P ys
permute []        <> = <>
permute (i ,- is) (p , ps) = riffle i (p , <>) (permute is ps)

reflP : {X : Set}(xs : List X) -> xs ~ xs
reflP [] = []
reflP (x ,- xs) = sl (srs xs) ,- reflP xs

insP : forall {X : Set}{x : X}{xs xs' ys ys'} -> (x ,- []) <[ xs' ]> xs -> (x ,- []) <[ ys' ]> ys
         -> xs ~ ys -> xs' ~ ys'
insP (sl i) j p with isSRS i
insP (sl .(srs _)) j p | mkSRS = j ,- p
insP (sr i) j (k ,- p) =
  let  _ , k' , j' = llswap (_ , j , k)
  in   k' ,- insP i j' p

l2r : forall {X : Set}{x : X}{xs xs' ys'}(i : (x ,- []) <[ xs' ]> xs)(p' : xs' ~ ys') ->
  Sg (List X) \ ys -> Sg ((x ,- []) <[ ys' ]> ys) \ j -> xs ~ ys
l2r (sl i) (j ,- p) with isSRS i
l2r (sl .(srs _)) (j ,- p) | mkSRS = _ , j , p
l2r (sr i) (k' ,- p') with l2r i p'
... | _ , j' , p with llswap (_ , k' , j')
... | _ , j , k = _ , j , (k ,- p)

transP : {X : Set}{xs ys zs : List X} -> xs ~ ys -> ys ~ zs -> xs ~ zs
transP [] [] = []
transP (i ,- p) q' with l2r i q'
... | _ , j , q = j ,- transP p q

symP : {X : Set}{xs ys : List X} -> xs ~ ys -> ys ~ xs
symP [] = []
symP (i ,- p) = insP i (sl (srs _)) (symP p)

swapP : {X : Set}(xs ys : List X) -> (xs +L ys) ~ (ys +L xs)
swapP [] ys rewrite ys +L[] = reflP ys
swapP (x ,- xs) ys = insS ys x xs ,- swapP xs ys

catP : forall {X : Set}{as bs cs ds : List X} -> as ~ cs -> bs ~ ds -> (as +L bs) ~ (cs +L ds)
catP [] q = q
catP (x ,- p) q = catS x (srs _) ,- catP p q

permap : forall {X Y}(f : X -> Y){xs xs' : List X} -> xs ~ xs' -> list f xs ~ list f xs'
permap f [] = []
permap f (x ,- p) = splimap f x ,- permap f p

cartNil : forall {I}(is : List I){J} -> cart {J = J} is [] == []
cartNil [] = refl []
cartNil (i ,- is) = cartNil is

cartCons : forall {I J}(is : List I)(j : J)(js : List J) ->
  cart is (j ,- js) ~ (list (_, j) is +L cart is js)
cartCons [] j js = []
cartCons (i ,- is) j js with catP (swapP (list (i ,_) js) (list (_, j) is)) (reflP (cart is js))
... | z rewrite assoc+L (list (i ,_) js) (list (_, j) is) (cart is js)
              | assoc+L (list (_, j) is) (list (i ,_) js) (cart is js)
  =
  (sl (srs (list (_, j) is +L cart (i ,- is) js))) ,-
  transP (catP (reflP (list (i ,_) js)) (cartCons is j js))
    z

cartLemma : forall {I J}(is : List I)(js : List J) ->
  cart is js ~ list swap (cart js is)
cartLemma {I}{J} [] js rewrite cartNil js {I} = []
cartLemma {I}{J} (i ,- is) js with catP (reflP (list (i ,_) js)) (cartLemma is js)
... | z rewrite sym (listlist swap (_, i) (i ,_) (\ j ->  refl (i , j)) js)
              | catNatural swap (list (_, i) js) (cart js is)
  = transP
  z
  (symP (permap (\ ji -> snd ji , fst ji) (cartCons js i is)))


module Formalization.SimplyTypedLambdaCalculus where

import      Lvl
open import Numeral.Natural
open import Type as _ using (TYPE)

data Type (B : TYPE) : TYPE₁ where
  Base : B → Type(B)
  Function : Type(B) → Type(B) → Type(B)

data Term (B : TYPE) : TYPE₁ where
  Apply : Term(B) → Term(B) → Term(B)
  Abstract : Type(B) → Term(B) → Term(B)
  Var : ℕ → Term(B)
  Const : B → Term(B)

module _ {B} where
  data _⊢_::_ (Γ : Term(B) → Type(B) → TYPE) : Term(B) → Type(B) → TYPE₁ where
    intro : ∀{a}{T} → Γ(a)(T) → (Γ ⊢ a :: T)
    -- const : ∀{a}{T} → Γ(a)(T) → (Γ ⊢ a :: T)
    abstr : ∀{body}{A B} → (Γ ⊢ body :: B) → (Γ ⊢ Abstract A body :: Function A B)
    apply : ∀{f x}{A B} → (Γ ⊢ f :: Function A B) → (Γ ⊢ x :: A) → (Γ ⊢ Apply f x :: B)

{-
A,B ::= Base | A ⟶ B
t ::= k | t t | λ t | Const b
b = true | false

Γ ⊢ Const b : Base
v ::= Const b | λ t
(⊢ t : Base) → ∃ v t ⟶* v
(⊢ t : A) → ∃ v t ⟶* v

Red(A)(t) definition "to be reducible"
Red(Base)(t) = ∃v(t ⟶* v)
Red(A→B)(t) = ∀u. Red(A)(u) → Red(B)(t u)

(t ⟶ t') → Red(A)(t') → Red(A)(t)
  • A = Base:
    ∃v(t* ⟶* v) → ∃v(t ⟶* v)
    t → t' ⟶* v
  • A = B→C:
    ∀u. Red(B)(u) → Red(C)(t' u)
    to show ∀u. Red(B)(u) → Red(C)(t u)
    and just use induction on the first thing mentioned
    Red(C)(t' u)
    to get
    Red(C)(t u)

data ⟶β : Set where
  red1 : t ⟶β u
  red2 : (λ t) u ⟶β substitute t u
  red3 : (t ⟶β t') → ((t u) ⟶β (t' u))
  red4 : (u ⟶β u') → (t u ⟶β t u')

⟶β-confluent : Confluent (_⟶β_)

module CallByName where
  data ⟶β : Set where
    red2 : (λ t) u ⟶β substitute t u
    red3 : (t ⟶β t') → ((t u) ⟶β (t' u))

substitute-preservation : (Γ ⊢ ((λ u) u' : A)) → (Γ ⊢ (substitute u u' : A))
β⟶-preservation : (Γ ⊢ (t : A)) → (t ⟶β t') → (Γ ⊢ (t' : A))

Red(Γ)(σ)
Γ = () | Γ.A -- context
σ : ℕ → Term
∀k. Γ ⊢ k : A
Represent variable as a context
σ is a formal definition of "reducible" substitution
Example:
Γ.A ⊢ 0 : A
Γ.A.B ⊢ 1 : A

σ-substitution?:

Red(Γ)(σ) ∧ (Γ ⊢ t:A) → Red(A)(t σ)
  Proof by induction on t
  • t = Ref k
    Red(A)(k σ) = Red(A)(σ(k))
  • t = t₀ t₁
    t_σ = (t₀ σ) (t₁ σ)
    Γ ⊢ t₀ : B → A
    Γ ⊢ t₁ : B
    by induction hypothesis:
    Red(B→A)(t₀ σ)
    Red(B)(t₁ σ)
    so Red(A)(t₀ σ)(t₁ σ)
  • t = λ u
    A = B → C
    Γ.B ⊢ u : C
    To show: Red(B→C)((λ u) σ)
    it means: ∀ u',Red(B)(u'), show Red(C)((λv) σ u')
    Define:
      (_,_) : (σ : ℕ → Term) (u' : Term) : ℕ → Term
      (σ,u')(0) = u'
      (σ,u')(𝐒 n) = σ(n)
    Claim: (λu) σ u' ⟶ u(σ,u')
    Γ.B ⊢ (k : T) → Red(T)((σ,u') k)
      k=0: Γ.B ⊢ 0
      k=n+1 (Γ ⊢ u : T) → (Γ ,B ⊢ n+1 : T)
    Summary:
      This is all to prove ∃v(t ⟶* v) if ⊢ t : Base, which is Red(A)(t)?
      Direct proof for application.
      Generalize the statement to: (⊢ t : A) → Red(A)(t)
      and then to: (Γ ⊢ t:A) → Red(Γ)(σ) → Red(A)(t σ)

  Something else: Krivine abstract machine
  How to evaluate λ-terms without substitution (defined before LISP eval with substitution)
  Term: k | tt | λ t
  Closure: t, ρ -- note: first mention of closure in this field? (denoted u v,‥)
  Environment: list of closures
  Stack: list of closures in stack order (denoted S)
  3 components: Term t,ENv ρ, Stack S → Term Env STack
  t₁ | ρ₁ | S₁ | t₂ | ρ₂ | S₂
  λ t | ρ | u : S | t | (ρ,u) | S
  t₀t1 | ρ | S | t₀ | ρ | (t₁,ρ) : S
  0 | ρ,(t,ρ') | S | t | ρ' | S
  k+1 | ρ,(t,p') | S | k | ρ | S
  Those are the transformations/reductions of the machine.
  Simplified description of how functional programs are evaluated.

-}


----------------------------------------------------------------------
-- Copyright: 2013, Jan Stolarek, Lodz University of Technology     --
--                                                                  --
-- License: See LICENSE file in root of the repo                    --
-- Repo address: https://github.com/jstolarek/dep-typed-wbl-heaps   --
--                                                                  --
-- Weight biased leftist heap that proves rank invariant: size of   --
-- left subtree of a node is not smaller than size of right         --
-- subtree. Uses a two-pass merging algorithm.                      --
----------------------------------------------------------------------

module TwoPassMerge.RankProof where

open import Basics

-- To prove the rank invariant we will index each Heap by its size,
-- (remember that size of a heap is its rank). Therefore index of
-- value n says that a Heap stores n elements. When merging two heaps
-- we will use the index to ensure that rank invariant is maintained.
--
-- Again, Heap has two constructor:
--
--  1) empty constructs a heap containing no elements. Therefore the
--     index is 0.
--
--  2) node takes two subtrees: one containing l elements, the other
--     containing r elements. The size of resulting heap is the sum of
--     l and r plus one for the element stored by the node itself. To
--     enforce the rank invariant node constructor expects a proof
--     that invariant holds: a value of type l ≥ r. If we can
--     construct value of this type then it proves the invariant.
data Heap : Rank → Set where
  empty : Heap zero
  node  : {l r : Rank} → Priority → l ≥ r →
          Heap l → Heap r → Heap (suc (l + r))

-- Since rank is now an index we no longer need rank function to
-- extract Rank from a node. We can pattern match on the index
-- instead.

-- Singleton heap stores only one element. Therefore it has size of
-- one. To prove the rank invariant we must show that 0 ≥ 0. We
-- construct a value of this type using ge0 constructor.
singleton : Priority → Heap one
singleton p = node p ge0 empty empty

-- Note [Proving rank invariant (merge using makeT)]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
-- Proving the rank invariant itself is surprisingly simple. We just
-- need to supply a proof that rank of left subtree is not smaller
-- than rank of right subtree. We can easily obtain evidence that it
-- is so by using order function which return result of comparing two
-- natural numbers (which will be tree ranks in this case) together
-- with a proof of the result.
--
-- But there is a different difficulty here. Since we index heaps by
-- their size we now require that makeT and merge construct trees of
-- correct size. Prooving this requires some substantial work on our
-- side.
--
-- We need to prove that the size of merged heap is equal to the sum
-- of sizes of heaps being merged. Recall that our merging algorithm
-- is two pass: we use merge to actually do the merging and makeT to
-- restore the rank invariant if necessary (see Note [Two-pass merging
-- algorithm]). This means our proof will be two-stage. We need to
-- prove that:
--
--  1) makeT creates a node of required size, even if it swaps left
--     and right children.
--
--  2) recursive calls to merge produce heaps of required size.

-- Note [Proving makeT]
-- ~~~~~~~~~~~~~~~~~~~~
--
-- makeT takes two subtrees of size l and r and produces a new tree of
-- size 1 + l + r. We must prove that each of two cases of makeT
-- returns tree of correct size:
--
--  1) size of l is ≥ than size of r: no extra proof is necessary as
--     everything follows from the definition of +.
--
--  2) size of r is ≥ than size of l: in this case we swap left and
--     right subtrees. This requires us to prove that:
--
--       suc (r + l) ≡ suc (l + r)
--
--     That proof is done using congruence on suc function and
--     commutativity of addition. We will define that proof as
--     makeT-lemma as we will be using in subsequent proofs.

makeT-lemma : (a b : Nat) → suc (a + b) ≡ suc (b + a)
makeT-lemma a b = cong suc (+comm a b)

makeT : {l r : Rank} → Priority → Heap l → Heap r → Heap (suc (l + r))
makeT {l-rank} {r-rank} p l r with order l-rank r-rank
makeT {l-rank} {r-rank} p l r | ge l≥r
  = node p l≥r l r
makeT {l-rank} {r-rank} p l r | le r≥l
  = subst Heap (makeT-lemma r-rank l-rank) (node p r≥l r l)

-- Note [Notation for proving heap merge]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
-- In the proofs of heap merge we will use following notation:
--
--  h1, h2 - rank of heaps being merged
--  p1, p2 - priority of root element
--  l1     - rank of left  subtree in the first  heap
--  r1     - rank of right subtree in the first  heap
--  l2     - rank of left  subtree in the second heap
--  r2     - rank of right subtree in the second heap
--
-- Note that:
--
--    h1 = suc (l1 + r1)
--    h2 = suc (l2 + r2)
--
--     h1         h2
--
--     p1         p2
--    /  \       /  \
--   /    \     /    \
--  l1    r1   l2    r2

-- Note [Proving merge]
-- ~~~~~~~~~~~~~~~~~~~~
--
-- We need to prove that all four cases of merge (see [Two-pass merging
-- algorithm]) produce heap of required size, which is h1 + h2. Since
-- in the proofs we will always operate on l1, r1, l2 and r2 we have:
--
--   h1 + h2 ̄≡ suc (l1 + r1) + suc (l2 + r2)
--           ≡ suc ((l1 + r1) + suc (l2 + r2))
--
-- (Second transformation comes from definition of +). This is the
-- expected size and therefore the final result we must prove in every
-- case that we analyze.

-- It is best to study the implementation of merge now and then read
-- the explanation of proofs.

-- Note [merge, proof 0a]
-- ~~~~~~~~~~~~~~~~~~~~~~
--
-- h1 ≡ 0, therefore: h1 + h2 ≡ 0 + h2 ≡ h2 ∎
--
-- This is definitional equality based on _+_
--
-- ∎

-- Note [merge, proof 0b]
-- ~~~~~~~~~~~~~~~~~~~~~~
--
-- h2 ≡ 0, therefore expected size is h1 + h2 ≡ h1 + 0. We need to
-- show that:
--
--    h1 ≡ h1 + 0
--
-- This is a simple statement that 0 is right identity of addition. We
-- proved that as one of basic properties of addition in
-- Basics.Reasoning module, except our proof had the sides of equality
-- reversed, ie. we proved a + 0 ≡ a, not a ≡ a + 0). We use symmetry
-- to construct a proof of latter from the former.
--
-- ∎

-- Note [merge, proof 1]
-- ~~~~~~~~~~~~~~~~~~~~~
--
-- We have p1 < p2. We keep p1 as the root and need to prove that
-- merging r1 with h2 gives correct size.  Here's how the term that
-- performs the merge corresponds to its type (for the sake of
-- readability I elided implict parameters):
--
--   makeT p1 x1 l1 (merge r1 (node p2 l2≥r2 l2 r2))
--    |          |         |   \__________________/
--    |   +------+         |            |
--    |   |     +----------+            |
--    |   |     |             +---------+
--    |   |     |     ________|__
--    |   |     |    /           \
--   suc (l1 + (r1 + suc (l2 + r2)))
--
-- Formally:
--
--   suc (l1 + (r1 + suc (l2 + r2))) ≡ suc ((l1 + r1) + suc (l2 + r2))
--
-- Recall that RHS of this equality comes from [Proving merge]. We
-- begin proof with congruence on suc:
--
--   cong suc X
--
-- where X proves
--
--   l1 + (r1 + suc (l2 + r2)) ≡ (l1 + r1) + suc (l2 + r2)
--
-- Substituting a = l1, b = r1 and c = suc (l2 + r2) we have
--
--   a + (b + c) ≡ (a + b) + c
--
-- Which is associativity of addition that we have already proved in
-- Basics.Reasoning.
--
-- ∎

proof-1 : (l1 r1 l2 r2 : Nat) → suc ( l1 + (r1 + suc (l2 + r2)))
                              ≡ suc ((l1 + r1) + suc (l2 + r2))
proof-1 l1 r1 l2 r2 = cong suc (+assoc l1 r1 (suc (l2 + r2)))

-- Note [merge, proof 2]
-- ~~~~~~~~~~~~~~~~~~~~~
--
-- We have p2 < p1. We keep p2 as the root and need to prove that
-- merging r2 with h1 gives correct size. Again, here's the
-- corespondence between the term and its type:
--
--   makeT p2 x2 l2 (merge r2 (node p1 l1≥r1 l1 r1))
--    |          |         |   \_________________/
--    |   +------+         |            |
--    |   |     +----------+  +---------+
--    |   |     |     ________|__
--    |   |     |    /           \
--   suc (l2 + (r2 + suc (l1 + r1)))
--
-- Formally:
--
--   suc (l2 + (r2 + suc (l1 + r1))) ≡ suc ((l1 + r1) + suc (l2 + r2))
--
-- Again we use cong to deal with the outer calls to suc and
-- substitute a = l2, b = r2 and c = l1 + r1. This leaves us with a
-- proof of lemma A:
--
--   a + (b + suc c) ≡ c + suc (a + b)
--
-- From associativity we know that:
--
--   a + (b + suc c) ≡ (a + b) + suc c
--
-- If we prove lemma B:
--
--  (a + b) + suc c = c + suc (a + b)
--
-- we can combine it using transitivity to get the final proof. We can
-- rewrite lemma B as:
--
--    n + suc m ≡ m + suc n
--
-- where n = a + b and m = c. From symmetry of +suc we have:
---
--   n + (suc m) ≡ suc (n + m)
--
-- Using transitivity we combine it with congruence on commutativity
-- of addition to prove:
--
--   suc (n + m) ≡ suc (m + n)
--
-- Again, using transitivity we combine it with +suc:
--
--   suc (m + n) ≡ m + suc n
--
-- Which proves lemma B and therefore the whole proof is complete.
--
-- ∎

lemma-B : (n m : Nat) → n + suc m ≡ m + suc n
lemma-B n m = trans (sym (+suc n m)) (trans (cong suc (+comm n m)) (+suc m n))

lemma-A : (a b c : Nat) → a + (b + suc c) ≡ c + suc (a + b)
lemma-A a b c = trans (+assoc a b (suc c)) (lemma-B (a + b) c)

proof-2 : (l1 r1 l2 r2 : Nat) → suc (l2 + (r2  + suc (l1 + r1)))
                              ≡ suc ((l1 + r1) + suc (l2 + r2))
proof-2 l1 r1 l2 r2 = cong suc (lemma-A l2 r2 (l1 + r1))

-- Note [Constructing equality proofs using transitivity]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
-- Now that constructed two specific proofs we can focus on a more
-- general technique used in both cases. Let's rewrite proof-2 in a
-- different fassion to see closely what is happening at each
-- step. Inlining lemmas A and B into proof-2 gives:
--
--   proof-2i : (l1 r1 l2 r2 : Nat) → suc (l2 + (r2  + suc (l1 + r1)))
--                                  ≡ suc ((l1 + r1) + suc (l2 + r2))
--   proof-2i l1 r1 l2 r2 =
--     cong suc (trans (+assoc l2 r2 (suc (l1 + r1)))
--              (trans (sym (+suc (l2 + r2) (l1 + r1)))
--              (trans (cong suc (+comm (l2 + r2) (l1 + r1)))
--                     (+suc (l1 + r1) (l2 + r2))))
--
-- We see a lot of properties combined using transitivity. In general,
-- if we have to prove:
--
--   a ≡ e
--
-- and we can prove:
--
--   a ≡ b, b ≡ c, c ≡ d, d ≡ e
--
-- then we can combine these proofs using transitivity:
--
--   trans (a ≡ b) (trans (b ≡ c) (trans (c ≡ d) (d ≡ e)))
--
-- While simple to use, combining proofs with transitivity can be not
-- so obvious at first. Let's rewrite the proof we have conducted
-- using following notation:
--
--  a ≡[ proof 1 ]
--  b ≡[ proof 2 ]
--  c ≡[ proof 3 ]
--  d ≡[ proof 4 ]
--  e ∎
--
-- Where proof 1 proves a ≡ b, proof 2 proves b ≡ c and so on. In our
-- particular case this will be:
--
--  suc  (l2 + (r2 + suc (l1 + r1))) ≡[ cong suc ]
-- [suc]  l2 + (r2 + suc (l1 + r1))  ≡[+assoc l2 r2 (suc (l1 + r1))]
-- [suc] (l2 + r2) + suc (l1 + r1)   ≡[ sym (+suc (l2 + r2) (l1 + r1))]
-- [suc] suc ((l2 + r2) + (l1 + r1)) ≡[ cong suc (+comm (l2 + r2) (l1 + r1)) ]
-- [suc] suc ((l1 + r1) + (l2 + r2)) ≡[+suc (l1 + r1) (l2 + r2) ]
-- [suc] (l1 + r1) + suc (l2 + r2) ∎
--
-- We use [suc] to denote that everything happens under a call to suc
-- (thanks to using congruence). Compare this notation with code of
-- proof-2i.

-- Note [Notation in merge]
-- ~~~~~~~~~~~~~~~~~~~~~~~~
--
-- merge uses different notation than the proofs. We use l1, r1, l2
-- and r2 to denote the respective subtrees and l1-rank, r1-rank,
-- l2-rank and r2-rank to denote their ranks. We use h1 and h2 to
-- denote heaps.
merge : {l r : Rank} → Heap l → Heap r → Heap (l + r)
merge empty h2 = h2 -- See [merge, proof 0a]
merge {suc l} {zero} h1 empty
  = subst Heap (sym (+0 (suc l))) h1 -- See [merge, proof 0b]
merge {suc .(l1-rank + r1-rank)} {suc .(l2-rank + r2-rank)}
  (node {l1-rank} {r1-rank} p1 l1≥r1 l1 r1)
  (node {l2-rank} {r2-rank} p2 l2≥r2 l2 r2)
  with p1 < p2
merge {suc .(l1-rank + r1-rank)} {suc .(l2-rank + r2-rank)}
  (node {l1-rank} {r1-rank} p1 l1≥r1 l1 r1)
  (node {l2-rank} {r2-rank} p2 l2≥r2 l2 r2)
  | true
  = subst Heap
          (proof-1 l1-rank r1-rank l2-rank r2-rank) -- See [merge, proof 1]
          (makeT p1 l1 (merge r1 (node p2 l2≥r2 l2 r2)))
merge {suc .(l1-rank + r1-rank)} {suc .(l2-rank + r2-rank)}
  (node {l1-rank} {r1-rank} p1 l1≥r1 l1 r1)
  (node {l2-rank} {r2-rank} p2 l2≥r2 l2 r2)
  | false
  = subst Heap
          (proof-2 l1-rank r1-rank l2-rank r2-rank) -- See [merge, proof 2]
          (makeT p2 l2 (merge r2 (node p1 l1≥r1 l1 r1)))

-- We require that inserting an element into the heap increases its
-- size by one. As previously we define insert as merge and a
-- singleton heap. Size of singleton heap is (suc zero), while already
-- existing heap has size n. According to definition of merge the
-- resulting heap will therefore have size:
--
--   (suc zero) + n
--
-- By definition of + this can be normalized to:
--
--   suc (zero + n)
--
-- and finally to
--
--   suc n
--
-- Which is size we require in the type signature. This means we don't
-- need any additional proof because expected result follows from
-- definitions.
insert : {n : Rank} → Priority → Heap n → Heap (suc n)
insert p h = merge (singleton p) h

-- By indexing heap with its size we can finally have means to ensure
-- that heap passed to findMin or deleteMin is not empty.
findMin : {n : Rank} → Heap (suc n) → Priority
findMin (node p _ _ _) = p

deleteMin : {n : Rank} → Heap (suc n) → Heap n
deleteMin (node _ _ l r) = merge l r


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

module FiniteType where

open import Equiv using (_≃_)
open import Data.Product using (Σ; _,_)
open import Data.Nat using (ℕ)
open import Data.Fin using (Fin)

--------------------------------------------------------------------------
--
-- A finite type is a type which is equivalent to Fin n
--

FiniteType : ∀ {ℓ} → (A : Set ℓ) → (n : ℕ) →  Set ℓ
FiniteType A n = A ≃ Fin n

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


{-# OPTIONS --without-K --safe #-}
module Cats.Category.Setoids.Facts.Initial where

open import Level
open import Relation.Binary using (Setoid)

open import Cats.Category
open import Cats.Category.Setoids using (Setoids ; ≈-intro)

import Data.Empty as Empty

⊥ : ∀ {l l′} → Setoid l l′
⊥ = record
  { Carrier = Lift _ Empty.⊥
  ; _≈_ = λ()
  ; isEquivalence = record
      { refl = λ{}
      ; sym = λ{}
      ; trans = λ{}
      }
  }

instance
  hasInitial : ∀ {l l≈} → HasInitial (Setoids l l≈)
  hasInitial = record
    { ⊥ = ⊥
    ; isInitial = λ X → record
      { arr = record { arr = λ() ; resp = λ{} }
      ; unique = λ _ → ≈-intro λ{}
      }
    }


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

open import LogicalFormulae
open import Agda.Primitive using (Level; lzero; lsuc; _⊔_)
open import Numbers.Naturals.Semiring
open import Numbers.Naturals.Order
open import Lists.Lists
open import Numbers.Primes.PrimeNumbers
open import Decidable.Relations
open import Numbers.BinaryNaturals.Definition
open import Numbers.BinaryNaturals.Addition

module ProjectEuler.Problem1 where

numbers<N : (N : ℕ) → List ℕ
numbers<N zero = []
numbers<N (succ N) = N :: numbers<N N

filtered : (N : ℕ) → List ℕ
filtered N = filter' (orDecidable (divisionDecidable 3) (divisionDecidable 5)) (numbers<N N)

ans : ℕ → ℕ
ans n = binNatToN (fold _+B_ (NToBinNat 0) (map NToBinNat (filtered n)))

t : ans 10 ≡ 23
t = refl

--q : ans 1000 ≡ {!233168!} -- takes about 15secs for me to reduce the term that fills this hole
--q = refl


module Day1 where

open import Data.String as String
open import Data.Maybe
open import Foreign.Haskell using (Unit)
open import Data.List as List
open import Data.Nat
import Data.Nat.Show as ℕs
open import Data.Char
open import Data.Vec as Vec renaming (_>>=_ to _VV=_ ; toList to VecToList)
open import Data.Product
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary
open import Data.Nat.Properties
open import Data.Bool.Base
open import AocIO
open import AocUtil
open import AocVec

makePairs : {A : Set} → {N : ℕ } → Vec A (suc N) → Vec (A × A) N
makePairs (x ∷ y ∷ vs) = ( ( x , y )) ∷ makePairs (y ∷ vs)
makePairs (x ∷ []) = []

makeNPairs : {A : Set} → {M : ℕ} → (N : ℕ) → Vec A (N + M) → Vec (A × A) M
makeNPairs {M = M} N vs with (Vec.drop N vs)
... | ends rewrite (+-comm N M) with (Vec.take M vs)
... | starts =  Vec.zip starts ends

neighbours : {A : Set} → {N : ℕ} → Vec A N → Vec (A × A) N
neighbours [] = []
neighbours (x ∷ vs) with (dupFrontToBack (x ∷ vs))
... | dvs = makePairs dvs

halfwayNeighbours : {A : Set} → (N : ℕ) → Vec A (N + N) → Vec (A × A) (N + N)
halfwayNeighbours 0 [] = []
halfwayNeighbours (suc N) v with (dupFirstNToBack (suc N) v)
... | dupped with (makeNPairs (suc N) dupped)
... | pairs = pairs

parseDigits : List Char → Maybe (List ℕ)
parseDigits [] = Maybe.just []
parseDigits (c ∷ cs) with (toDigit c) | (parseDigits cs)
...                                    | (just n)   | (just ns) = just (n ∷ ns)
...                                    | _            | _             = nothing

onlyKeepPairs : List (ℕ × ℕ) → List (ℕ × ℕ)
onlyKeepPairs [] = []
onlyKeepPairs ((a , b) ∷ xs) with (a Data.Nat.≟ b)
... | yes p = (a , b) ∷ onlyKeepPairs xs
... | no ¬p = onlyKeepPairs xs

main2 : IO Unit
main2 = mainBuilder (readFileMain processFile)
     where
       processDigits : {M : ℕ} → Vec ℕ M → IO Unit
       processDigits vs with (neighbours vs)
       ... | neigh with (onlyKeepPairs (VecToList neigh))
       ... | pairs with (List.map  Σ.proj₁ pairs)
       ... | nums = putStrLn (toCostring (ℕs.show (List.sum nums)))
       processLine : List Char → IO Unit
       processLine ls with (parseDigits ls)
       ... | nothing = putStrLn (toCostring ("Failed to parse this line: " String.++ (String.fromList ls)))
       ... | just digs = processDigits (Vec.fromList digs)
       processFile : String → IO Unit
       processFile file-content with (lines (String.toList file-content))
       ... | file-lines with (List.map processLine file-lines)
       ... | ops = void (sequence-io-prim ops)

checkEvenVec : ∀ {a} {A : Set a} → {N : ℕ} → Vec A N → Maybe (Σ[ M ∈ ℕ ] M + M ≡ N)
checkEvenVec [] = just (0 , refl)
checkEvenVec (x ∷ []) = nothing
checkEvenVec { N = N} (x ∷ y ∷ vs) with (checkEvenVec vs)
... | nothing = nothing
... | just (M , p) with (cong suc (+-suc M M))
... | t rewrite p = just ((suc M) , t)

main : IO Unit
main = mainBuilder (readFileMain processFile)
      where
      processDigits : {N : ℕ} → Vec ℕ N → IO Unit
      processDigits vs with (checkEvenVec vs)
      ... | nothing = printString "Input has to have even length"
      ... | just (M , p) with (halfwayNeighbours M v)
        where
          v : Vec ℕ (M + M)
          v rewrite p = vs
      ... | half-neigh with (onlyKeepPairs (VecToList half-neigh))
      ... | pairs with (List.map Σ.proj₁ pairs)
      ... | nums = printString (ℕs.show (List.sum nums))
      processLine : List Char → IO Unit
      processLine ls with (parseDigits ls)
      ... | nothing = putStrLn (toCostring ("Failed to parse this line: " String.++ (String.fromList ls)))
      ... | just digs = processDigits (Vec.fromList digs)
      processFile : String → IO Unit
      processFile file-content with (lines (String.toList file-content))
      ... | file-lines with (List.map processLine file-lines)
      ... | ops = void (sequence-io-prim ops)


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

module ideals where

import abelian-subgroups
import rings
open abelian-subgroups public
open rings public

{- Subsets of rings -}

subset-Ring :
  (l : Level) {l1 : Level} (R : Ring l1) → UU ((lsuc l) ⊔ l1)
subset-Ring l R = type-Ring R → UU-Prop l

is-set-subset-Ring :
  (l : Level) {l1 : Level} (R : Ring l1) → is-set (subset-Ring l R)
is-set-subset-Ring l R =
  is-set-function-type (is-set-UU-Prop l)

{- Closure properties of subsets of rings -}

is-additive-subgroup-subset-Ring :
  {l1 l2 : Level} (R : Ring l1) (P : subset-Ring l2 R) → UU (l1 ⊔ l2)
is-additive-subgroup-subset-Ring R = is-subgroup-Ab (ab-Ring R)

is-closed-under-mul-left-subset-Ring :
  {l1 l2 : Level} (R : Ring l1) (P : subset-Ring l2 R) → UU (l1 ⊔ l2)
is-closed-under-mul-left-subset-Ring R P =
  (x : type-Ring R) (y : type-Ring R) →
  type-Prop (P y) → type-Prop (P (mul-Ring R x y))

is-closed-under-mul-right-subset-Ring :
  {l1 l2 : Level} (R : Ring l1) (P : subset-Ring l2 R) → UU (l1 ⊔ l2)
is-closed-under-mul-right-subset-Ring R P =
  (x : type-Ring R) (y : type-Ring R) →
  type-Prop (P x) → type-Prop (P (mul-Ring R x y))

{- The definition of left and right ideals -}

is-left-ideal-subset-Ring :
  {l1 l2 : Level} (R : Ring l1) (P : subset-Ring l2 R) → UU (l1 ⊔ l2)
is-left-ideal-subset-Ring R P =
  is-additive-subgroup-subset-Ring R P ×
  is-closed-under-mul-left-subset-Ring R P

Left-Ideal-Ring :
  (l : Level) {l1 : Level} (R : Ring l1) → UU ((lsuc l) ⊔ l1)
Left-Ideal-Ring l R = Σ (subset-Ring l R) (is-left-ideal-subset-Ring R)

is-right-ideal-subset-Ring :
  {l1 l2 : Level} (R : Ring l1) (P : subset-Ring l2 R) → UU (l1 ⊔ l2)
is-right-ideal-subset-Ring R P =
  is-additive-subgroup-subset-Ring R P ×
  is-closed-under-mul-right-subset-Ring R P

Right-Ideal-Ring :
  (l : Level) {l1 : Level} (R : Ring l1) → UU ((lsuc l) ⊔ l1)
Right-Ideal-Ring l R = Σ (subset-Ring l R) (is-right-ideal-subset-Ring R)

is-two-sided-ideal-subset-Ring :
  {l1 l2 : Level} (R : Ring l1) (P : subset-Ring l2 R) → UU (l1 ⊔ l2)
is-two-sided-ideal-subset-Ring R P =
  is-additive-subgroup-subset-Ring R P ×
  ( is-closed-under-mul-left-subset-Ring R P ×
    is-closed-under-mul-right-subset-Ring R P)

Two-Sided-Ideal-Ring :
  (l : Level) {l1 : Level} (R : Ring l1) → UU ((lsuc l) ⊔ l1)
Two-Sided-Ideal-Ring l R =
  Σ (subset-Ring l R) (is-two-sided-ideal-subset-Ring R)

{- Special ideals -}


module pushdown where

open import Level renaming ( suc to succ ; zero to Zero )
open import Data.Nat
open import Data.List
open import Data.Maybe
-- open import Data.Bool using ( Bool ; true ; false )
open import  Relation.Binary.PropositionalEquality hiding ( [_] )
open import Relation.Nullary using (¬_; Dec; yes; no)
open import Level renaming ( suc to succ ; zero to Zero )
-- open import Data.Product
open import logic
open import automaton

data PushDown   (  Γ : Set  ) : Set  where
   pop    : PushDown  Γ
   push   :  Γ → PushDown  Γ
   none   :  PushDown  Γ

record PushDownAutomaton ( Q : Set ) ( Σ : Set  ) ( Γ : Set  )
       : Set  where
    field
        pδ : Q → Σ →  Γ → Q ∧ ( PushDown  Γ )
        pok : Q → Bool
        pempty : Γ
    pmoves :  Q → List  Γ  → Σ → ( Q ∧ List  Γ )
    pmoves q [] i with pδ q i pempty
    pmoves q [] i | ⟪ qn , pop ⟫ =  ⟪  qn , [] ⟫
    pmoves q [] i | ⟪ qn , push x ⟫ =  ⟪ qn , ( x ∷  [] )  ⟫
    pmoves q [] i | ⟪ qn , none ⟫ =  ⟪ qn ,  [] ⟫
    pmoves q (  H  ∷ T  ) i with pδ q i H
    pmoves q (H ∷ T) i | ⟪ qn , pop ⟫ =  ⟪ qn , T  ⟫
    pmoves q (H ∷ T) i | ⟪ qn , none ⟫ =  ⟪ qn , (H ∷ T)  ⟫
    pmoves q (H ∷ T) i | ⟪ qn , push x ⟫ =  ⟪ qn ,  x ∷ H ∷ T  ⟫

    paccept : (q : Q ) ( In : List  Σ ) ( sp : List   Γ ) → Bool
    paccept q [] [] = pok q
    paccept q ( H  ∷ T) [] with pδ q H pempty
    paccept q (H ∷ T) [] | ⟪ qn , pop ⟫ = paccept qn T []
    paccept q (H ∷ T) [] | ⟪ qn , none ⟫ = paccept qn T []
    paccept q (H ∷ T) [] | ⟪ qn , push x ⟫ = paccept qn T (x  ∷ [] )
    paccept q [] (_ ∷ _ ) = false
    paccept q ( H  ∷ T ) ( SH  ∷ ST ) with pδ q H SH
    ... | ⟪ nq , pop ⟫     = paccept nq T ST
    ... | ⟪ nq , none ⟫    = paccept nq T (SH ∷ ST)
    ... | ⟪ nq , push ns ⟫ = paccept nq T ( ns  ∷  SH ∷ ST )

record PDA ( Q : Set ) ( Σ : Set  ) ( Γ : Set  )
       : Set  where
    field
        automaton : Automaton Q Σ
        pδ : Q →  PushDown  Γ
    open Automaton
    paccept : (q : Q ) ( In : List  Σ ) ( sp : List   Γ ) → Bool
    paccept q [] [] = aend automaton q
    paccept q (H  ∷ T) [] with pδ  (δ automaton q H)
    paccept q (H ∷ T) [] | pop     = paccept (δ automaton q H) T []
    paccept q (H ∷ T) [] | none    = paccept (δ automaton q H) T []
    paccept q (H ∷ T) [] | push x  = paccept (δ automaton q H) T (x  ∷ [] )
    paccept q [] (_ ∷ _ ) = false
    paccept q ( H  ∷ T ) ( SH  ∷ ST ) with pδ (δ automaton q H)
    ... | pop      = paccept (δ automaton q H) T ST
    ... | none     = paccept (δ automaton q H) T (SH ∷ ST)
    ... | push ns  = paccept (δ automaton q H) T ( ns  ∷  SH ∷ ST )

data  States0   : Set  where
   sr : States0

data  In2   : Set  where
   i0 : In2
   i1 : In2

pnn : PushDownAutomaton States0 In2 States0
pnn = record {
         pδ  = pδ
      ;  pempty = sr
      ;  pok = λ q → true
   } where
        pδ  : States0 → In2 → States0 → States0 ∧ PushDown States0
        pδ sr i0 _ = ⟪ sr , push sr ⟫
        pδ sr i1 _ = ⟪ sr , pop  ⟫

data  States2   : Set  where
   ph1 : States2
   ph2 : States2
   phf : States2

pnnp : PDA States2 In2 States2
pnnp = record {
         automaton = record { aend = aend ; δ = δ }
       ; pδ  = pδ
   } where
        δ : States2 → In2 → States2
        δ ph1 i0 = ph1
        δ ph1 i1 = ph2
        δ ph2 i1 = ph2
        δ _ _ = phf
        aend : States2 → Bool
        aend ph2 = true
        aend _ = false
        pδ  : States2 → PushDown States2
        pδ ph1 = push ph1
        pδ ph2 = pop
        pδ phf = none

data  States1   : Set  where
   ss : States1
   st : States1
pn1 : PushDownAutomaton States1 In2 States1
pn1 = record {
         pδ  = pδ
      ;  pempty = ss
      ;  pok = pok1
   } where
        pok1 :  States1 → Bool
        pok1 ss = false
        pok1 st = true
        pδ  : States1 → In2 → States1 → States1 ∧ PushDown States1
        pδ ss i0 _ = ⟪ ss , push ss ⟫
        pδ ss i1 _ = ⟪ st , pop ⟫
        pδ st i0 _ = ⟪ st , push ss ⟫
        pδ st i1 _ = ⟪ st , pop  ⟫

test1 = PushDownAutomaton.paccept pnn sr ( i0 ∷ i0 ∷ i1 ∷ i1 ∷ [] ) []
test2 = PushDownAutomaton.paccept pnn sr ( i0 ∷ i0 ∷ i1 ∷ i0 ∷ [] ) []
test3 = PushDownAutomaton.pmoves pnn sr [] i0
test4 = PushDownAutomaton.paccept pnn sr ( i0 ∷ i0 ∷ i1 ∷ i1 ∷ i0 ∷ i1 ∷ [] ) []

test5 = PushDownAutomaton.paccept pn1 ss ( i0 ∷ i0 ∷ i1 ∷ i1 ∷ [] ) []
test6 = PushDownAutomaton.paccept pn1 ss ( i0 ∷ i0 ∷ i1 ∷ i1 ∷ i0 ∷ i1 ∷ [] ) []


module Type.Category.ExtensionalFunctionsCategory.HomFunctor where

import      Functional as Fn
open import Function.Proofs
open import Function.Equals
open import Function.Equals.Proofs
open import Logic.Predicate
import      Lvl
import      Relator.Equals.Proofs.Equiv
open import Structure.Category
open import Structure.Category.Dual
open import Structure.Category.Functor
open import Structure.Categorical.Properties
open import Structure.Function
open import Structure.Operator
open import Structure.Relator.Equivalence
open import Structure.Relator.Properties
open import Structure.Setoid
open import Syntax.Transitivity
open import Type.Category.ExtensionalFunctionsCategory
open import Type

private variable ℓ ℓₒ ℓₘ ℓₑ : Lvl.Level

-- Note: This is implicitly using the identity type as the equivalence for (_⟶_).
module _ {Obj : Type{ℓₒ}} {_⟶_ : Obj → Obj → Type{ℓₘ}} (C : Category(_⟶_)) where
  open Category(C)

  covariantHomFunctor : Obj → ((intro C) →ᶠᵘⁿᶜᵗᵒʳ typeExtensionalFnCategoryObject{ℓₘ})
  ∃.witness (covariantHomFunctor x) y = (x ⟶ y)
  Functor.map (∃.proof (covariantHomFunctor _)) = (_∘_)
  _⊜_.proof (Function.congruence (Functor.map-function (∃.proof (covariantHomFunctor _))) {f₁} {f₂} f₁f₂) {g} = congruence₂ₗ(_∘_) ⦃ binaryOperator ⦄ g f₁f₂
  _⊜_.proof (Functor.op-preserving (∃.proof (covariantHomFunctor _))) = Morphism.associativity(_∘_)
  _⊜_.proof (Functor.id-preserving (∃.proof (covariantHomFunctor _))) = Morphism.identityₗ(_∘_)(id)

  contravariantHomFunctor : Obj → (dual(intro C) →ᶠᵘⁿᶜᵗᵒʳ typeExtensionalFnCategoryObject{ℓₘ})
  ∃.witness (contravariantHomFunctor x) y = (y ⟶ x)
  Functor.map (∃.proof (contravariantHomFunctor _)) = Fn.swap(_∘_)
  _⊜_.proof (Function.congruence (Functor.map-function (∃.proof (contravariantHomFunctor _))) {g₁} {g₂} g₁g₂) {f} = congruence₂ᵣ(_∘_) ⦃ binaryOperator ⦄ f g₁g₂
  _⊜_.proof (Functor.op-preserving (∃.proof (contravariantHomFunctor _))) = symmetry(_) (Morphism.associativity(_∘_))
  _⊜_.proof (Functor.id-preserving (∃.proof (contravariantHomFunctor _))) = Morphism.identityᵣ(_∘_)(id)


module Issue807b where

open import Common.Coinduction

data Unit : Set where
  unit : Unit

From-unit : Unit → Set
From-unit unit = Unit

from-unit : (x : Unit) → From-unit x
from-unit unit = unit

data D : Set where
  d₁ : D
  d₂ : ∞ D → D

foo : D → Unit
foo d₁     = unit
foo (d₂ x) with ♭ x
foo (d₂ x) | d₁   = unit
foo (d₂ x) | d₂ _ = unit

-- x : ∞ D
x = ♯ d₁

-- There was an internal error if type-signature was not supplied.


------------------------------------------------------------------------
-- Instantiation of Contractive for functions
------------------------------------------------------------------------

-- Taken from the paper.

open import Relation.Binary
open import Relation.Binary.PropositionalEquality
open import Contractive
open import Level

module Contractive.Function
         {A B : Set}
         {_<_ : Rel A zero}
         (isWFO : IsWellFoundedOrder _<_)
         (dec : Decidable _<_)
         (proof-irrelevance :
            ∀ {a a'} (p₁ p₂ : a < a') → p₁ ≡ p₂)
         where

open import Relation.Nullary
open import Relation.Unary
open import Data.Empty

ofe : OFE
ofe = record
  { Carrier            = A
  ; Domain             = A → B
  ; _<_                = _<_
  ; isWellFoundedOrder = isWFO
  ; Eq                 = λ a f g → f a ≡ g a
  ; isEquivalence      = λ _ → record
    { refl  = refl
    ; sym   = sym
    ; trans = trans
    }
  }

open OFE ofe hiding (_<_)

private

  limU : (A → (A → B)) → A → B
  limU f a' = f a' a'

  lim↓ : B → ∀ a → (∀ x → x < a → A → B) → A → B
  lim↓ b a f a' with dec a' a
  lim↓ b a f a' | yes a'<a = f a' a'<a a'
  lim↓ b a f a' | no  a'≮a = b

  isLimit↓ : ∀ b a {fam : Family (↓ a)} →
             IsCoherent fam → IsLimit fam (lim↓ b a fam)
  isLimit↓ b a {fam} coh {a'} a'<a with dec a' a
  isLimit↓ b a {fam} coh {a'} a'<a | no  a'≮a  = ⊥-elim (a'≮a a'<a)
  isLimit↓ b a {fam} coh {a'} a'<a | yes a'<a₂ =
    cong (λ p → fam a' p a') (proof-irrelevance a'<a a'<a₂)

-- The paper implicitly assumes that B is non-empty.

cofe : B → COFE
cofe b = record
  { ofe      = ofe
  ; limU     = limU
  ; isLimitU = λ _ _ → refl
  ; lim↓     = lim↓ b
  ; isLimit↓ = isLimit↓ b
  }


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

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

module Fields.FieldOfFractions.Group {a b : _} {A : Set a} {S : Setoid {a} {b} A} {_+_ : A → A → A} {_*_ : A → A → A} {R : Ring S _+_ _*_} (I : IntegralDomain R) where

open import Fields.FieldOfFractions.Setoid I
open import Fields.FieldOfFractions.Addition I

fieldOfFractionsGroup : Group fieldOfFractionsSetoid fieldOfFractionsPlus
Group.+WellDefined fieldOfFractionsGroup {record { num = a ; denom = b ; denomNonzero = b!=0 }} {record { num = c ; denom = d ; denomNonzero = d!=0 }} {record { num = e ; denom = f ; denomNonzero = f!=0 }} {record { num = g ; denom = h ; denomNonzero = h!=0 }} af=be ch=dg = need
  where
    open Setoid S
    open Ring R
    open Equivalence eq
    have1 : (c * h) ∼ (d * g)
    have1 = ch=dg
    have2 : (a * f) ∼ (b * e)
    have2 = af=be
    need : (((a * d) + (b * c)) * (f * h)) ∼ ((b * d) * (((e * h) + (f * g))))
    need = transitive (transitive (Ring.*Commutative R) (transitive (Ring.*DistributesOver+ R) (Group.+WellDefined (Ring.additiveGroup R) (transitive *Associative (transitive (*WellDefined (*Commutative) reflexive) (transitive (*WellDefined *Associative reflexive) (transitive (*WellDefined (*WellDefined have2 reflexive) reflexive) (transitive (symmetric *Associative) (transitive (*WellDefined reflexive *Commutative) (transitive *Associative (transitive (*WellDefined (transitive (transitive (symmetric *Associative) (*WellDefined reflexive *Commutative)) *Associative) reflexive) (symmetric *Associative))))))))) (transitive *Commutative (transitive (transitive (symmetric *Associative) (*WellDefined reflexive (transitive (*WellDefined reflexive *Commutative) (transitive *Associative (transitive (*WellDefined have1 reflexive) (transitive (symmetric *Associative) (*WellDefined reflexive *Commutative))))))) *Associative))))) (symmetric (Ring.*DistributesOver+ R))
Group.0G fieldOfFractionsGroup = record { num = Ring.0R R ; denom = Ring.1R R ; denomNonzero = IntegralDomain.nontrivial I }
Group.inverse fieldOfFractionsGroup record { num = a ; denom = b ; denomNonzero = p } = record { num = Group.inverse (Ring.additiveGroup R) a ; denom = b ; denomNonzero = p }
Group.+Associative fieldOfFractionsGroup {record { num = a ; denom = b ; denomNonzero = b!=0 }} {record { num = c ; denom = d ; denomNonzero = d!=0 }} {record { num = e ; denom = f ; denomNonzero = f!=0 }} = need
  where
    open Setoid S
    open Equivalence eq
    need : (((a * (d * f)) + (b * ((c * f) + (d * e)))) * ((b * d) * f)) ∼ ((b * (d * f)) * ((((a * d) + (b * c)) * f) + ((b * d) * e)))
    need = transitive (Ring.*Commutative R) (Ring.*WellDefined R (symmetric (Ring.*Associative R)) (transitive (Group.+WellDefined (Ring.additiveGroup R) reflexive (Ring.*DistributesOver+ R)) (transitive (Group.+WellDefined (Ring.additiveGroup R) reflexive (Group.+WellDefined (Ring.additiveGroup R) (Ring.*Associative R) (Ring.*Associative R))) (transitive (Group.+Associative (Ring.additiveGroup R)) (Group.+WellDefined (Ring.additiveGroup R) (transitive (transitive (Group.+WellDefined (Ring.additiveGroup R) (transitive (Ring.*Associative R) (Ring.*Commutative R)) (Ring.*Commutative R)) (symmetric (Ring.*DistributesOver+ R))) (Ring.*Commutative R)) reflexive)))))
Group.identRight fieldOfFractionsGroup {record { num = a ; denom = b ; denomNonzero = b!=0 }} = need
  where
    open Setoid S
    open Equivalence eq
    need : (((a * Ring.1R R) + (b * Group.0G (Ring.additiveGroup R))) * b) ∼ ((b * Ring.1R R) * a)
    need = transitive (transitive (Ring.*WellDefined R (transitive (Group.+WellDefined (Ring.additiveGroup R) (transitive (Ring.*Commutative R) (Ring.identIsIdent R)) reflexive) (transitive (Group.+WellDefined (Ring.additiveGroup R) reflexive (Ring.timesZero R)) (Group.identRight (Ring.additiveGroup R)))) reflexive) (Ring.*Commutative R)) (symmetric (Ring.*WellDefined R (transitive (Ring.*Commutative R) (Ring.identIsIdent R)) reflexive))
Group.identLeft fieldOfFractionsGroup {record { num = a ; denom = b }} = need
  where
    open Setoid S
    open Equivalence eq
    need : (((Group.0G (Ring.additiveGroup R) * b) + (Ring.1R R * a)) * b) ∼ ((Ring.1R R * b) * a)
    need = transitive (transitive (Ring.*WellDefined R (transitive (Group.+WellDefined (Ring.additiveGroup R) reflexive (Ring.identIsIdent R)) (transitive (Group.+WellDefined (Ring.additiveGroup R) (transitive (Ring.*Commutative R) (Ring.timesZero R)) reflexive) (Group.identLeft (Ring.additiveGroup R)))) reflexive) (Ring.*Commutative R)) (Ring.*WellDefined R (symmetric (Ring.identIsIdent R)) reflexive)
Group.invLeft fieldOfFractionsGroup {record { num = a ; denom = b }} = need
  where
    open Setoid S
    open Equivalence eq
    need : (((Group.inverse (Ring.additiveGroup R) a * b) + (b * a)) * Ring.1R R) ∼ ((b * b) * Group.0G (Ring.additiveGroup R))
    need = transitive (transitive (transitive (Ring.*Commutative R) (Ring.identIsIdent R)) (transitive (Group.+WellDefined (Ring.additiveGroup R) (Ring.*Commutative R) reflexive) (transitive (symmetric (Ring.*DistributesOver+ R)) (transitive (Ring.*WellDefined R reflexive (Group.invLeft (Ring.additiveGroup R))) (Ring.timesZero R))))) (symmetric (Ring.timesZero R))
Group.invRight fieldOfFractionsGroup {record { num = a ; denom = b }} = need
  where
    open Setoid S
    open Equivalence eq
    need : (((a * b) + (b * Group.inverse (Ring.additiveGroup R) a)) * Ring.1R R) ∼ ((b * b) * Group.0G (Ring.additiveGroup R))
    need = transitive (transitive (transitive (Ring.*Commutative R) (Ring.identIsIdent R)) (transitive (Group.+WellDefined (Ring.additiveGroup R) (Ring.*Commutative R) reflexive) (transitive (symmetric (Ring.*DistributesOver+ R)) (transitive (Ring.*WellDefined R reflexive (Group.invRight (Ring.additiveGroup R))) (Ring.timesZero R))))) (symmetric (Ring.timesZero R))


{-# OPTIONS --without-K #-}
module HoTT.Sigma.Transport where

open import HoTT.Base
open import HoTT.Identity
open import HoTT.Identity.Sigma

Σ-transport : ∀ {i j k} {A : 𝒰 i} {P : A → 𝒰 j} {Q : (Σ A λ x → P x) → 𝒰 k} {x y : A} →
              (p : x == y) (uz : Σ (P x) λ u → Q (x , u)) →
              transport (λ x → Σ (P x) λ u → Q (x , u)) p uz ==
                transport _ p (pr₁ uz) , transport Q (pair⁼ (p , refl)) (pr₂ uz)
Σ-transport refl (u , z) = refl


{-# OPTIONS --cubical --no-import-sorts --safe #-}
{-
This module develops Lehmer codes, i.e. an encoding of permutations as finite integers.

-}
module Cubical.Data.Fin.LehmerCode where

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

open import Cubical.Functions.Embedding
open import Cubical.Functions.Surjection

open import Cubical.Relation.Nullary
open import Cubical.Relation.Nullary.DecidableEq

open import Cubical.Data.Unit as ⊤
open import Cubical.Data.Empty as ⊥
open import Cubical.Data.Nat
open import Cubical.Data.Nat.Order
open import Cubical.Data.Fin.Base as F
open import Cubical.Data.Fin.Properties
  renaming (punchOut to punchOutPrim)
open import Cubical.Data.Sigma
open import Cubical.Data.Sum as Sum using (_⊎_; inl; inr)

private
  variable
    n : ℕ

FinExcept : (i : Fin n) → Type₀
FinExcept i = Σ[ j ∈ Fin _ ] ¬ (i ≡ j)

isSetFinExcept : {i : Fin n} → isSet (FinExcept i)
isSetFinExcept = isOfHLevelΣ 2 isSetFin (λ _ → isProp→isSet (isPropΠ (λ _ → ⊥.isProp⊥)))

fsucExc : {i : Fin n} → FinExcept i → FinExcept (fsuc i)
fsucExc {i = i} j = fsuc (fst j) , snd j ∘ fsuc-inj

toFinExc : {i : Fin n} → FinExcept i → Fin n
toFinExc = fst

toFinExc-injective : {i : Fin n} → {j k : FinExcept i} → toFinExc j ≡ toFinExc k → j ≡ k
toFinExc-injective = Σ≡Prop (λ _ → isPropΠ λ _ → ⊥.isProp⊥)

toℕExc : {i : Fin n} → FinExcept i → ℕ
toℕExc = toℕ ∘ toFinExc

toℕExc-injective : {i : Fin n} → {j k : FinExcept i} → toℕExc j ≡ toℕExc k → j ≡ k
toℕExc-injective = toFinExc-injective ∘ toℕ-injective

projectionEquiv : {i : Fin n} → (Unit ⊎ FinExcept i) ≃ Fin n
projectionEquiv {n = n} {i = i} = isoToEquiv goal where
  goal : Iso (Unit ⊎ FinExcept i) (Fin n)
  goal .Iso.fun (inl _) = i
  goal .Iso.fun (inr m) = fst m
  goal .Iso.inv m = case discreteFin i m of λ { (yes _) → inl tt ; (no n) → inr (m , n) }
  goal .Iso.rightInv m with discreteFin i m
  ... | (yes p) = p
  ... | (no _) = toℕ-injective refl
  goal .Iso.leftInv (inl tt) with discreteFin i i
  ... | (yes _) = refl
  ... | (no ¬ii) = ⊥.rec (¬ii refl)
  goal .Iso.leftInv (inr m) with discreteFin i (fst m)
  ... | (yes p) = ⊥.rec (snd m p)
  ... | (no _) = cong inr (toℕExc-injective refl)

punchOut : (i : Fin (suc n)) → FinExcept i → Fin n
punchOut i ¬i = punchOutPrim (snd ¬i)

punchOut-injective : (i : Fin (suc n)) → ∀ j k → punchOut i j ≡ punchOut i k → j ≡ k
punchOut-injective i j k = toFinExc-injective ∘ punchOut-inj (snd j) (snd k)

punchIn : (i : Fin (suc n)) → Fin n → FinExcept i
punchIn {_} i j with fsplit i
...             | inl iz = fsuc j , fznotfs ∘ (iz ∙_)
punchIn {n} i j | inr (i′ , is) with n
...                 | zero = ⊥.rec (¬Fin0 j)
...                 | suc n′ with fsplit j
...                     | inl jz = fzero , fznotfs ∘ sym ∘ (is ∙_)
...                     | inr (j′ , _) =
                            let (k , ¬k≡i′) = punchIn i′ j′
                            in fsuc k , ¬k≡i′ ∘ fsuc-inj ∘ (is ∙_)

punchOut∘In :(i : Fin (suc n)) → ∀ j → punchOut i (punchIn i j) ≡ j
punchOut∘In {_} i j with fsplit i
...                 | inl iz = toℕ-injective refl
punchOut∘In {n} i j | inr (i′ , _) with n
...                     | zero = ⊥.rec (¬Fin0 j)
...                     | suc n′ with fsplit j
...                         | inl jz = toℕ-injective (cong toℕ jz)
...                         | inr (j′ , prfj) =
                              -- the following uses a definitional equality of punchIn but I don't see
                              -- how to sidestep this while using with-abstractions
                              fsuc (punchOut i′ _)               ≡⟨ cong (λ j → fsuc (punchOut i′ j)) (toℕExc-injective refl) ⟩
                              fsuc (punchOut i′ (punchIn i′ j′)) ≡⟨ cong fsuc (punchOut∘In i′ j′) ⟩
                              fsuc j′                            ≡⟨ toℕ-injective (cong toℕ prfj) ⟩
                              j                                  ∎

isEquivPunchOut : {i : Fin (suc n)} → isEquiv (punchOut i)
isEquivPunchOut {i = i} = isEmbedding×isSurjection→isEquiv (isEmbPunchOut , isSurPunchOut) where
  isEmbPunchOut : isEmbedding (punchOut i)
  isEmbPunchOut = injEmbedding isSetFinExcept isSetFin λ {_} {_} → punchOut-injective i _ _
  isSurPunchOut : isSurjection (punchOut i)
  isSurPunchOut b = ∣ _ , punchOut∘In i b ∣

punchOutEquiv : {i : Fin (suc n)} → FinExcept i ≃ Fin n
punchOutEquiv = _ , isEquivPunchOut

infixr 4 _∷_
data LehmerCode : (n : ℕ) → Type₀ where
  [] : LehmerCode zero
  _∷_ : ∀ {n} → Fin (suc n) → LehmerCode n → LehmerCode (suc n)

isContrLehmerZero : isContr (LehmerCode 0)
isContrLehmerZero = [] , λ { [] → refl }

lehmerSucEquiv : Fin (suc n) × LehmerCode n ≃ LehmerCode (suc n)
lehmerSucEquiv = isoToEquiv (iso (λ (e , c) → e ∷ c)
                                 (λ { (e ∷ c) → (e , c) })
                                 (λ { (e ∷ c) → refl })
                                 (λ (e , c) → refl))

lehmerEquiv : (Fin n ≃ Fin n) ≃ LehmerCode n
lehmerEquiv {zero} = isContr→Equiv contrFF isContrLehmerZero where
  contrFF : isContr (Fin zero ≃ Fin zero)
  contrFF = idEquiv _ , λ y → equivEq (funExt λ f → ⊥.rec (¬Fin0 f))

lehmerEquiv {suc n} =
  (Fin (suc n) ≃ Fin (suc n))                            ≃⟨ isoToEquiv i ⟩
  (Σ[ k ∈ Fin (suc n) ] (FinExcept fzero ≃ FinExcept k)) ≃⟨ Σ-cong-equiv-snd ii ⟩
  (Fin (suc n) × (Fin n ≃ Fin n))                        ≃⟨ Σ-cong-equiv-snd (λ _ → lehmerEquiv) ⟩
  (Fin (suc n) × LehmerCode n)                           ≃⟨ lehmerSucEquiv ⟩
  LehmerCode (suc n) ■ where
    equivIn : (f : Fin (suc n) ≃ Fin (suc n))
            → FinExcept fzero ≃ FinExcept (equivFun f fzero)
    equivIn f =
      FinExcept fzero
        ≃⟨ Σ-cong-equiv-snd (λ _ → preCompEquiv (invEquiv (congEquiv f))) ⟩
      (Σ[ x ∈ Fin (suc n) ] ¬ ffun fzero ≡ ffun x)
        ≃⟨ Σ-cong-equiv-fst f ⟩
      FinExcept (ffun fzero)
        ■ where ffun = equivFun f

--    equivInChar : ∀ {f} x → fst (equivFun (equivIn f) x) ≡ equivFun f (fst x)
--    equivInChar x = refl

    equivOut : ∀ {k} → FinExcept (fzero {k = n}) ≃ FinExcept k
             → Fin (suc n) ≃ Fin (suc n)
    equivOut {k = k} f =
      Fin (suc n)
        ≃⟨ invEquiv projectionEquiv ⟩
      Unit ⊎ FinExcept fzero
        ≃⟨ isoToEquiv (Sum.⊎Iso idIso (equivToIso f)) ⟩
      Unit ⊎ FinExcept k
        ≃⟨ projectionEquiv ⟩
      Fin (suc n)
        ■

    equivOutChar : ∀ {k} {f} (x : FinExcept fzero) → equivFun (equivOut {k = k} f) (fst x) ≡ fst (equivFun f x)
    equivOutChar {f = f} x with discreteFin fzero (fst x)
    ... | (yes y) = ⊥.rec (x .snd y)
    ... | (no n) = cong (λ x′ → fst (equivFun f x′)) (toℕExc-injective refl)

    i : Iso (Fin (suc n) ≃ Fin (suc n))
            (Σ[ k ∈ Fin (suc n) ] (FinExcept (fzero {k = n}) ≃ FinExcept k))
    Iso.fun i f = equivFun f fzero , equivIn f
    Iso.inv i (k , f) = equivOut f
    Iso.rightInv i (k , f) = ΣPathP (refl , equivEq (funExt λ x →
       toℕExc-injective (cong toℕ (equivOutChar {f = f} x))))
    Iso.leftInv i f = equivEq (funExt goal) where
      goal : ∀ x → equivFun (equivOut (equivIn f)) x ≡ equivFun f x
      goal x = case fsplit x return (λ _ → equivFun (equivOut (equivIn f)) x ≡ equivFun f x) of λ
        { (inl xz) → subst (λ x → equivFun (equivOut (equivIn f)) x ≡ equivFun f x) xz refl
        ; (inr (_ , xn)) → equivOutChar {f = equivIn f} (x , fznotfs ∘ (_∙ sym xn))
        }

    ii : ∀ k → (FinExcept fzero ≃ FinExcept k) ≃ (Fin n ≃ Fin n)
    ii k = (FinExcept fzero ≃ FinExcept k) ≃⟨ cong≃ (λ R → FinExcept fzero ≃ R) punchOutEquiv ⟩
           (FinExcept fzero ≃ Fin n)       ≃⟨ cong≃ (λ L → L ≃ Fin n) punchOutEquiv  ⟩
           (Fin n ≃ Fin n)                 ■

encode : (Fin n ≃ Fin n) → LehmerCode n
encode = equivFun lehmerEquiv

decode : LehmerCode n → Fin n ≃ Fin n
decode = invEq lehmerEquiv

factorial : ℕ → ℕ
factorial zero = 1
factorial (suc n) = suc n · factorial n

lehmerFinEquiv : LehmerCode n ≃ Fin (factorial n)
lehmerFinEquiv {zero} = isContr→Equiv isContrLehmerZero isContrFin1
lehmerFinEquiv {suc n} = _ ≃⟨ invEquiv lehmerSucEquiv ⟩
                         _ ≃⟨ ≃-× (idEquiv _) lehmerFinEquiv ⟩
                         _ ≃⟨ factorEquiv ⟩
                         _ ■

private
  -- this example is copied from wikipedia, using the permutation of the letters A-G
  -- B,F,A,G,D,E,C
  -- given by the corresponding Lehmer code of
  -- 1 4 0 3 1 1 0
  exampleCode : LehmerCode 7
  exampleCode = (1 , (5 , refl)) ∷ (4 , (1 , refl)) ∷ (0 , (4 , refl)) ∷ (3 , (0 , refl)) ∷ (1 , (1 , refl)) ∷ (1 , (0 , refl)) ∷ (0 , (0 , refl)) ∷ []

  example : Fin 7 ≃ Fin 7
  example = decode exampleCode

  -- C should decode to G
  computation : fst (equivFun example (3 , (3 , refl))) ≡ 6
  computation = refl

  _ : toℕ (equivFun lehmerFinEquiv exampleCode) ≡ 4019
  _ = refl


{-# OPTIONS --type-in-type #-}
module poly.core where
open import functors
open import prelude

_^ : Set → Set -- Presheaf
I ^ = I → Set

∫ : Set -- Arena
∫ = ∃ _^

_⦅_⦆ : ∫ → Set → Set -- Interpret as a polynomial functor
(A⁺ , A⁻) ⦅ y ⦆ = Σ[ a⁺ ∈ A⁺ ] (A⁻ a⁺ → y)

module _ (A@(A⁺ , A⁻) B@(B⁺ , B⁻) : ∫) where
  ∫[_,_] : Set
  ∫[_,_] = (a⁺ : A⁺) → Σ[ b⁺ ∈ B⁺ ] (B⁻ b⁺ → A⁻ a⁺)
module _ {A@(A⁺ , A⁻) B@(B⁺ , B⁻) : ∫} where
  lens : (get : A⁺ → B⁺)
         (set : (a⁺ : A⁺) → B⁻ (get a⁺) → A⁻ a⁺)
       → ∫[ A , B ]
  lens g s = λ a⁺ → g a⁺ , s a⁺
  get : (l : ∫[ A , B ]) → A⁺ → B⁺
  get l = π₁ ∘ l
  set : (A↝B : ∫[ A , B ]) → (a⁺ : A⁺) → B⁻ (get A↝B a⁺) → A⁻ a⁺
  set l = π₂ ∘ l

instance
    lens-cat : Category ∫[_,_]
    lens-cat = 𝒾: (λ a⁺ → a⁺ , id)
               ▸: (λ l1 l2 a⁺ → let b⁺ , setb = l1 a⁺
                                    c⁺ , setc = l2 b⁺
                                in  c⁺ , setb ∘ setc)
               𝒾▸: (λ _ → refl)
               ▸𝒾: (λ _ → refl)


module my-list where

open import level
open import bool
open import eq
open import maybe
open import nat
open import nat-thms
open import product-thms
open import product

data 𝕃 {ℓ} (A : Set ℓ) : Set ℓ where
  [] : 𝕃 A
  _::_ : (x : A) (xs : 𝕃 A) → 𝕃 A

{-# BUILTIN LIST 𝕃 #-}
{-# BUILTIN NIL  []   #-}
{-# BUILTIN CONS _::_  #-}

length : ∀{ℓ}{A : Set ℓ} → 𝕃 A → ℕ
length [] = 0
length (x :: x₁) = suc (length x₁)

_++_ : ∀ {ℓ}{A : Set ℓ} → 𝕃 A → 𝕃 A → 𝕃 A
[] ++ ys = ys
(x :: xs) ++ ys = x :: (xs ++ ys)

map : ∀ {ℓ ℓ'} {A : Set ℓ} {B : Set ℓ'} → (A → B) → 𝕃 A → 𝕃 B
map f []        = []
map f (x :: xs) = f x :: map f xs

filter : ∀{ℓ}{A : Set ℓ} → (A → 𝔹) → 𝕃 A → 𝕃 A
filter p [] = []
filter p (x :: xs) = let r = filter p xs in
                     if p x then x :: r else r

-- remove all elements equal to the given one
remove : ∀{ℓ}{A : Set ℓ}(eq : A → A → 𝔹)(a : A)(l : 𝕃 A) → 𝕃 A
remove eq a l = filter (λ x → ~ (eq a x)) l

nth : ∀{ℓ}{A : Set ℓ} → ℕ → 𝕃 A → maybe A
nth _ [] = nothing
nth 0 (x :: xs) = just x
nth (suc n) (x :: xs) = nth n xs

reverse-helper : ∀ {ℓ}{A : Set ℓ} → 𝕃 A → 𝕃 A → 𝕃 A
reverse-helper h [] = h
reverse-helper h (x :: xs) = reverse-helper (x :: h) xs

reverse : ∀ {ℓ}{A : Set ℓ} → 𝕃 A → 𝕃 A
reverse l = reverse-helper [] l

length-++ : ∀{ℓ}{A : Set ℓ}(l1 l2 : 𝕃 A) →
            length (l1 ++ l2) ≡ (length l1) + (length l2)
length-++ [] l2 = refl
length-++ (x :: l1) l2 rewrite length-++ l1 l2 = refl

++-assoc : ∀{ℓ}{A : Set ℓ} (l1 l2 l3 : 𝕃 A) →
           (l1 ++ l2) ++ l3 ≡ l1 ++ (l2 ++ l3)
++-assoc [] l2 l3 = refl
++-assoc (x :: l1) l2 l3 rewrite ++-assoc l1 l2 l3 = refl

length-filter : ∀{ℓ}{A : Set ℓ}(p : A → 𝔹)(l : 𝕃 A) →
                length (filter p l) ≤ length l ≡ tt
length-filter p [] = refl
length-filter p (x :: l) with p x
...                      | tt = length-filter p l
...                      | ff = ≤-trans{length (filter p l)}
                                   (length-filter p l) (≤-suc (length l))

filter-idem : ∀{ℓ}{A : Set ℓ}(p : A → 𝔹)(l : 𝕃 A) →
              (filter p (filter p l)) ≡ (filter p l)
filter-idem p [] = refl
filter-idem p (x :: l) with keep (p x)
...                    | tt , p'
  rewrite p' | p' | filter-idem p l = refl
...                    | ff , p' rewrite p' = filter-idem p l

length-reverse-helper : ∀{ℓ}{A : Set ℓ}(h l : 𝕃 A) →
                        length (reverse-helper h l) ≡ length h + length l
length-reverse-helper h [] = sym (+0 (length h))
length-reverse-helper h (x :: l)
  rewrite length-reverse-helper (x :: h) l =
    sym (+suc (length h) (length l))


------------------------------------------------------------------------
-- The Agda standard library
--
-- Reverse view
------------------------------------------------------------------------

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

module Data.List.Reverse where

open import Data.List.Base as L hiding (reverse)
open import Data.List.Properties
open import Function
open import Relation.Binary.PropositionalEquality

-- If you want to traverse a list from the end, then you can use the
-- reverse view of it.

infixl 5 _∶_∶ʳ_

data Reverse {a} {A : Set a} : List A → Set a where
  []     : Reverse []
  _∶_∶ʳ_ : ∀ xs (rs : Reverse xs) (x : A) → Reverse (xs ∷ʳ x)

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

  reverse : (xs : List A) → Reverse (L.reverse xs)
  reverse []       = []
  reverse (x ∷ xs) = cast $ _ ∶ reverse xs ∶ʳ x where
    cast = subst Reverse (sym $ unfold-reverse x xs)

  reverseView : (xs : List A) → Reverse xs
  reverseView xs = cast $ reverse (L.reverse xs) where
    cast = subst Reverse (reverse-involutive xs)


{-# OPTIONS --without-K #-}
open import Type
open import Function
open import Function.Extensionality
open import Data.Fin using (Fin)
open import Level.NP
open import Algebra
open import Algebra.FunctionProperties.NP
open import Data.Fin using (Fin; zero; suc)
open import Data.Nat
open import Data.Product
open import Relation.Binary.NP

open import Explore.Core
open import Explore.Properties
open import Explore.Explorable
import Explore.Fin
open Explore.Fin.Regular
open import HoTT

module Explore.Function.Fin where

postulate
  Postulate-FinFunˢ-ok : ★
-- open ExplorableRecord

module _ {A : ★}(μA : Explorable A) where

  eᴬ = explore μA

  extend : ∀ {n} → A → (Fin n → A) → Fin (suc n) → A
  extend x g zero    = x
  extend x g (suc i) = g i

  ¬Fin0 : Fin 0 → A
  ¬Fin0 ()

  -- There is one function Fin 0 → A (called abs) so this should be fine
  -- if not there is a version below that forces the domain to be non-empty
  FinFunᵉ : ∀ n → Explore _ (Fin n → A)
  FinFunᵉ zero    z op f = f ¬Fin0
  FinFunᵉ (suc n) z op f = eᴬ z op (λ x → FinFunᵉ n z op (f ∘ extend x))

  FinFunⁱ : ∀ n → ExploreInd _ (FinFunᵉ n)
  FinFunⁱ zero    P Pz P∙ Pf = Pf _
  FinFunⁱ (suc n) P Pz P∙ Pf =
    explore-ind μA (λ sa → P (λ z op f → sa z op (λ x → FinFunᵉ n z op (f ∘ extend x))))
      Pz P∙
      (λ x → FinFunⁱ n (λ sf → P (λ z op f → sf z op (f ∘ extend x)))
        Pz P∙ (Pf ∘ extend x))

  FinFunˢ : ∀ n → Sum (Fin n → A)
  FinFunˢ n = FinFunᵉ n 0 _+_

  postulate
    FinFunˢ-ok : ∀ {{_ : Postulate-FinFunˢ-ok}} n → Adequate-sum (FinFunˢ n)

  μFinFun : ∀ {{_ : Postulate-FinFunˢ-ok}} {n} → Explorable (Fin n → A)
  μFinFun = mk _ (FinFunⁱ _) (FinFunˢ-ok _)

module BigDistr
  {{_ : Postulate-FinFunˢ-ok}} {{_ : FunExt}} {{_ : UA}}
  {A : ★}(μA : Explorable A)
  (cm           : CommutativeMonoid ₀ ₀)
  -- we want (open CMon cm) !!!
  (_◎_          : let open CMon cm in C → C → C)
  (ε-◎          : let open CMon cm in Zero _≈_ ε _◎_)
  (distrib      : let open CMon cm in _DistributesOver_ _≈_ _◎_ _∙_)
  (_◎-cong_     : let open CMon cm in _◎_ Preserves₂ _≈_ ⟶ _≈_ ⟶ _≈_) where

  open CMon cm renaming (sym to ≈-sym)

  μF→A = μFinFun μA

  -- Sum over A
  Σᴬ = explore μA ε _∙_

  -- Sum over (Fin(1+I)→A) functions
  Σ' : ∀ {I} → ((Fin I → A) → C) → C
  Σ' = explore μF→A ε _∙_

  -- Product over Fin(1+I) values
  Π' = λ I → explore (μFin I) ε _◎_

  bigDistr : ∀ I F → Π' I (Σᴬ ∘ F) ≈ Σ' (Π' I ∘ _ˢ_ F)
  bigDistr zero    _ = refl
  bigDistr (suc I) F
    = Σᴬ (F zero) ◎ Π' I (Σᴬ ∘ F ∘ suc)
    ≈⟨ refl ◎-cong bigDistr I (F ∘ suc) ⟩
      Σᴬ (F zero) ◎ Σ' (Π' I ∘ _ˢ_ (F ∘ suc))
    ≈⟨ ≈-sym (explore-linʳ μA monoid _◎_ (F zero) (Σ' (Π' I ∘ _ˢ_ (F ∘ suc))) (proj₁ ε-◎ _) (proj₂ distrib)) ⟩
      Σᴬ (λ j → F zero j ◎ Σ' (Π' I ∘ _ˢ_ (F ∘ suc)))
    ≈⟨ explore-mon-ext μA monoid (λ j → ≈-sym (explore-linˡ μF→A monoid _◎_ (Π' I ∘ _ˢ_ (F ∘ suc)) (F zero j) (proj₂ ε-◎ _) (proj₁ distrib))) ⟩
      (Σᴬ λ j → Σ' λ f → F zero j ◎ Π' I ((F ∘ suc) ˢ f))
    ∎

module _
  {{_ : Postulate-FinFunˢ-ok}}{{_ : UA}}{{_ : FunExt}} where

  FinDist : ∀ {n} → DistFun (μFin n) (λ μX → μFinFun μX)
  FinDist μB c ◎ distrib ◎-cong f = BigDistr.bigDistr μB c ◎ distrib ◎-cong f _


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

open import Categories.Category.Core using (Category)
open import Categories.Comonad

-- Verbatim dual of Categories.Adjoint.Construction.Kleisli
module Categories.Adjoint.Construction.CoKleisli {o ℓ e} {C : Category o ℓ e} (M : Comonad C) where

open import Categories.Category.Construction.CoKleisli
open import Categories.Adjoint
open import Categories.Functor
open import Categories.Functor.Properties
open import Categories.NaturalTransformation.Core
open import Categories.NaturalTransformation.NaturalIsomorphism using (_≃_)
open import Categories.Morphism.Reasoning C

private
  module C = Category C
  module M = Comonad M
  open M.F
  open C
  open HomReasoning
  open Equiv

Forgetful : Functor (CoKleisli M) C
Forgetful =
 record
  { F₀           = λ X → F₀ X
  ; F₁           = λ f → F₁ f ∘ M.δ.η _
  ; identity     = Comonad.identityˡ M
  ; homomorphism = λ {X Y Z} {f g} → hom-proof {X} {Y} {Z} {f} {g}
  ; F-resp-≈     = λ eq → ∘-resp-≈ˡ (F-resp-≈ eq)
  }
  where
  trihom : {X Y Z W : Obj} {f : X ⇒ Y} {g : Y ⇒ Z} {h : Z ⇒ W} → F₁ (h ∘ g ∘ f) ≈ F₁ h ∘ F₁ g ∘ F₁ f
  trihom {X} {Y} {Z} {W} {f} {g} {h} = begin
   F₁ (h ∘ g ∘ f)     ≈⟨ homomorphism ⟩
   F₁ h ∘ F₁ (g ∘ f)  ≈⟨ refl⟩∘⟨ homomorphism ⟩
   F₁ h ∘ F₁ g ∘ F₁ f ∎
  hom-proof :
   {X Y Z : Obj} {f : F₀ X ⇒ Y} {g : F₀ Y ⇒ Z} →
   (F₁ (g ∘ F₁ f ∘ M.δ.η X)) ∘ M.δ.η X ≈ (F₁ g ∘ M.δ.η Y) ∘ F₁ f ∘ M.δ.η X
  hom-proof {X} {Y} {Z} {f} {g} = begin
   (F₁ (g ∘ F₁ f ∘ M.δ.η X)) ∘ M.δ.η X         ≈⟨ pushˡ trihom ⟩
   F₁ g ∘ (F₁ (F₁ f) ∘ F₁ (M.δ.η X)) ∘ M.δ.η X ≈⟨ refl⟩∘⟨ (pullʳ (sym M.assoc)) ⟩
   F₁ g ∘ F₁ (F₁ f) ∘ M.δ.η (F₀ X) ∘ M.δ.η X   ≈⟨ refl⟩∘⟨ pullˡ (sym (M.δ.commute f)) ⟩
   F₁ g ∘ (M.δ.η Y ∘ F₁ f) ∘ M.δ.η X           ≈⟨ assoc²'' ⟩
   (F₁ g ∘ M.δ.η Y) ∘ F₁ f ∘ M.δ.η X           ∎

Cofree : Functor C (CoKleisli M)
Cofree =
 record
  { F₀ = λ X → X
  ; F₁ = λ f → f ∘ M.ε.η _
  ; identity = λ {A} → identityˡ
  ; homomorphism = λ {X Y Z} {f g} → hom-proof {X} {Y} {Z} {f} {g}
  ; F-resp-≈ = λ x → ∘-resp-≈ˡ x
  }
  where
  hom-proof :
   {X Y Z : Obj} {f : X ⇒ Y} {g : Y ⇒ Z} →
   (g ∘ f) ∘ M.ε.η X ≈ (g ∘ M.ε.η Y) ∘ (F₁ (f ∘ M.ε.η X) ∘ M.δ.η X)
  hom-proof {X} {Y} {Z} {f} {g} = begin
   (g ∘ f) ∘ M.ε.η X                               ≈⟨ pullʳ (sym (M.ε.commute f)) ⟩
   g ∘ M.ε.η Y ∘ F₁ f                              ≈⟨ sym (pullʳ (refl⟩∘⟨ elimʳ (Comonad.identityˡ M))) ⟩
   (g ∘ M.ε.η Y) ∘ (F₁ f ∘ F₁ (M.ε.η X) ∘ M.δ.η X) ≈⟨ refl⟩∘⟨ pullˡ (sym homomorphism) ⟩
   (g ∘ M.ε.η Y) ∘ (F₁ (f ∘ M.ε.η X) ∘ M.δ.η X)    ∎

FC≃M : Forgetful ∘F Cofree ≃ M.F
FC≃M =
 record
  { F⇒G = ntHelper record
    { η = λ X → F₁ C.id
    ; commute = λ {X Y} f → to-commute {X} {Y} f
    }
  ; F⇐G = ntHelper record
    { η = λ X → F₁ C.id
    ; commute = λ {X Y} f → from-commute {X} {Y} f
    }
  ; iso = λ X → record
    { isoˡ = elimʳ identity ○ identity
    ; isoʳ = elimʳ identity ○ identity
    }
  }
  where
  to-commute : {X Y : Obj} → (f : X ⇒ Y) → F₁ C.id ∘ F₁ (f ∘ M.ε.η X) ∘ M.δ.η X ≈ F₁ f ∘ F₁ C.id
  to-commute {X} {Y} f = begin
   F₁ C.id ∘ F₁ (f ∘ M.ε.η X) ∘ M.δ.η X ≈⟨ elimˡ identity ⟩
   F₁ (f ∘ M.ε.η X) ∘ M.δ.η X           ≈⟨ pushˡ homomorphism ⟩
   F₁ f ∘ F₁ (M.ε.η X) ∘ M.δ.η X        ≈⟨ refl⟩∘⟨ Comonad.identityˡ M ⟩
   F₁ f ∘ C.id                          ≈⟨ refl⟩∘⟨ sym identity ⟩
   F₁ f ∘ F₁ C.id                       ∎
  from-commute : {X Y : Obj} → (f : X ⇒ Y) → F₁ C.id ∘ F₁ f ≈ (F₁ (f ∘ M.ε.η X) ∘ M.δ.η X) ∘ F₁ C.id
  from-commute {X} {Y} f = begin
      F₁ C.id ∘ F₁ f                            ≈⟨ [ M.F ]-resp-square id-comm-sym ⟩
      F₁ f ∘ F₁ C.id                            ≈⟨ introʳ (Comonad.identityˡ M) ⟩∘⟨refl ⟩
      (F₁ f ∘ F₁ (M.ε.η X) ∘ M.δ.η X) ∘ F₁ C.id ≈⟨ pullˡ (sym homomorphism) ⟩∘⟨refl ⟩
      (F₁ (f ∘ M.ε.η X) ∘ M.δ.η X) ∘ F₁ C.id    ∎

-- useful lemma:
FF1≈1 : {X : Obj} → F₁ (F₁ (C.id {X})) ≈ C.id
FF1≈1 {X} = begin
 F₁ (F₁ (C.id {X})) ≈⟨ F-resp-≈ identity ⟩
 F₁ (C.id)          ≈⟨ identity ⟩
 C.id               ∎

Forgetful⊣Cofree : Forgetful ⊣ Cofree
Forgetful⊣Cofree =
 record
  { unit = ntHelper record
    { η = λ X → F₁ C.id
    ; commute = λ {X Y} f → unit-commute {X} {Y} f
    }
  ; counit = ntHelper record
    { η = M.ε.η
    ; commute = λ {X Y} f → counit-commute {X} {Y} f
    }
  ; zig = λ {A} → zig-proof {A}
  ; zag = λ {B} → zag-proof {B}
  }
  where
  unit-commute : ∀ {X Y : Obj} →
   (f : F₀ X ⇒ Y) →
   F₁ C.id ∘ F₁ f ∘ M.δ.η X ≈ ((F₁ f ∘ M.δ.η X) ∘ M.ε.η (F₀ X)) ∘ F₁ (F₁ C.id) ∘ M.δ.η X
  unit-commute {X} {Y} f = begin
   F₁ C.id ∘ F₁ f ∘ M.δ.η X                                   ≈⟨ elimˡ identity ⟩
   F₁ f ∘  M.δ.η X                                            ≈⟨ introʳ (Comonad.identityʳ M) ⟩
   (F₁ f ∘ M.δ.η X) ∘ M.ε.η (F₀ X) ∘ M.δ.η X                  ≈⟨ sym assoc ⟩
   ((F₁ f ∘ M.δ.η X) ∘ M.ε.η (F₀ X)) ∘ M.δ.η X                ≈⟨ intro-center FF1≈1 ⟩
   ((F₁ f ∘ M.δ.η X) ∘ M.ε.η (F₀ X)) ∘ F₁ (F₁ C.id) ∘ M.δ.η X ∎
  counit-commute : ∀ {X Y : Obj} →
   (f : X ⇒ Y) →
   M.ε.η Y ∘ (F₁ (f ∘ M.ε.η X) ∘ M.δ.η X) ≈ f ∘ M.ε.η X
  counit-commute {X} {Y} f = begin
   M.ε.η Y ∘ F₁ (f ∘ M.ε.η X) ∘ M.δ.η X      ≈⟨ refl⟩∘⟨ pushˡ homomorphism ⟩
   M.ε.η Y ∘ (F₁ f ∘ F₁ (M.ε.η X) ∘ M.δ.η X) ≈⟨ refl⟩∘⟨ elimʳ (Comonad.identityˡ M) ⟩
   M.ε.η Y ∘ F₁ f                            ≈⟨ M.ε.commute f ⟩
   f ∘ M.ε.η X ∎
  zig-proof : {A : Obj} → M.ε.η (F₀ A) ∘ F₁ (F₁ C.id) ∘ M.δ.η _ ≈ C.id
  zig-proof {A} = begin
   M.ε.η (F₀ A) ∘ F₁ (F₁ C.id) ∘ M.δ.η _ ≈⟨ elim-center FF1≈1 ⟩
   M.ε.η (F₀ A) ∘ M.δ.η _                ≈⟨ Comonad.identityʳ M ⟩
   C.id                                  ∎
  zag-proof : {B : Obj} → (M.ε.η B ∘ M.ε.η (F₀ B)) ∘ (F₁ (F₁ C.id) ∘ M.δ.η _) ≈ M.ε.η B
  zag-proof {B} = begin
    (M.ε.η B ∘ M.ε.η (F₀ B)) ∘ (F₁ (F₁ C.id) ∘ M.δ.η _) ≈⟨ assoc ⟩
    M.ε.η B ∘ (M.ε.η (F₀ B) ∘ (F₁ (F₁ C.id) ∘ M.δ.η _)) ≈⟨ refl⟩∘⟨ elim-center FF1≈1 ⟩
    M.ε.η B ∘ (M.ε.η (F₀ B) ∘ M.δ.η _)                  ≈⟨ elimʳ (Comonad.identityʳ M) ⟩
    M.ε.η B                                             ∎