{-# OPTIONS --safe #-}
module Cubical.Algebra.Matrix where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Structure
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Transport
open import Cubical.Functions.FunExtEquiv

import Cubical.Data.Empty as ⊥
open import Cubical.Data.Bool
open import Cubical.Data.Nat hiding (_+_ ; _·_; +-comm ; +-assoc; ·-assoc)
open import Cubical.Data.Vec
open import Cubical.Data.Sigma.Base
open import Cubical.Data.FinData
open import Cubical.Relation.Nullary

open import Cubical.Algebra.Group
open import Cubical.Algebra.AbGroup
open import Cubical.Algebra.Monoid
open import Cubical.Algebra.Ring
open import Cubical.Algebra.Ring.BigOps

open Iso

private
  variable
    ℓ : Level
    A : Type ℓ

-- Equivalence between Vec matrix and Fin function matrix

FinMatrix : (A : Type ℓ) (m n : ℕ) → Type ℓ
FinMatrix A m n = FinVec (FinVec A n) m

VecMatrix : (A : Type ℓ) (m n : ℕ) → Type ℓ
VecMatrix A m n = Vec (Vec A n) m

FinMatrix→VecMatrix : {m n : ℕ} → FinMatrix A m n → VecMatrix A m n
FinMatrix→VecMatrix M = FinVec→Vec (λ fm → FinVec→Vec (M fm))

VecMatrix→FinMatrix : {m n : ℕ} → VecMatrix A m n → FinMatrix A m n
VecMatrix→FinMatrix M fn fm = lookup fm (lookup fn M)

FinMatrix→VecMatrix→FinMatrix : {m n : ℕ} (M : FinMatrix A m n)
                              → VecMatrix→FinMatrix (FinMatrix→VecMatrix M) ≡ M
FinMatrix→VecMatrix→FinMatrix {m = zero} M = funExt (⊥.rec ∘ ¬Fin0)
FinMatrix→VecMatrix→FinMatrix {n = zero} M = funExt₂ (λ _ → ⊥.rec ∘ ¬Fin0)
FinMatrix→VecMatrix→FinMatrix {m = suc m} {n = suc n} M = funExt₂ goal
  where
  goal : (fm : Fin (suc m)) (fn : Fin (suc n))
       → VecMatrix→FinMatrix (_ ∷ FinMatrix→VecMatrix (M ∘ suc)) fm fn ≡ M fm fn
  goal zero zero       = refl
  goal zero (suc fn) i = FinVec→Vec→FinVec (M zero ∘ suc) i fn
  goal (suc fm) fn i   = FinMatrix→VecMatrix→FinMatrix (M ∘ suc) i fm fn

VecMatrix→FinMatrix→VecMatrix : {m n : ℕ} (M : VecMatrix A m n)
                              → FinMatrix→VecMatrix (VecMatrix→FinMatrix M) ≡ M
VecMatrix→FinMatrix→VecMatrix {m = zero} [] = refl
VecMatrix→FinMatrix→VecMatrix {m = suc m} (M ∷ MS) i =
  Vec→FinVec→Vec M i ∷ VecMatrix→FinMatrix→VecMatrix MS i

FinMatrixIsoVecMatrix : (A : Type ℓ) (m n : ℕ) → Iso (FinMatrix A m n) (VecMatrix A m n)
fun (FinMatrixIsoVecMatrix A m n)      = FinMatrix→VecMatrix
inv (FinMatrixIsoVecMatrix A m n)      = VecMatrix→FinMatrix
rightInv (FinMatrixIsoVecMatrix A m n) = VecMatrix→FinMatrix→VecMatrix
leftInv (FinMatrixIsoVecMatrix A m n)  = FinMatrix→VecMatrix→FinMatrix

FinMatrix≃VecMatrix : {m n : ℕ} → FinMatrix A m n ≃ VecMatrix A m n
FinMatrix≃VecMatrix {_} {A} {m} {n} = isoToEquiv (FinMatrixIsoVecMatrix A m n)

FinMatrix≡VecMatrix : (A : Type ℓ) (m n : ℕ) → FinMatrix A m n ≡ VecMatrix A m n
FinMatrix≡VecMatrix _ _ _ = ua FinMatrix≃VecMatrix

-- Define abelian group structure on matrices
module FinMatrixAbGroup (G' : AbGroup ℓ) where

  open AbGroupStr (snd G') renaming ( is-set to isSetG )
  private G = ⟨ G' ⟩

  zeroFinMatrix : ∀ {m n} → FinMatrix G m n
  zeroFinMatrix _ _ = 0g

  negFinMatrix : ∀ {m n} → FinMatrix G m n → FinMatrix G m n
  negFinMatrix M i j = - M i j

  addFinMatrix : ∀ {m n} → FinMatrix G m n → FinMatrix G m n → FinMatrix G m n
  addFinMatrix M N i j = M i j + N i j

  isSetFinMatrix : ∀ {m n} → isSet (FinMatrix G m n)
  isSetFinMatrix = isSetΠ2 λ _ _ → isSetG

  addFinMatrixAssoc : ∀ {m n} → (M N K : FinMatrix G m n)
                    → addFinMatrix M (addFinMatrix N K) ≡ addFinMatrix (addFinMatrix M N) K
  addFinMatrixAssoc M N K i j k = assoc (M j k) (N j k) (K j k) i

  addFinMatrix0r : ∀ {m n} → (M : FinMatrix G m n)
                 → addFinMatrix M zeroFinMatrix ≡ M
  addFinMatrix0r M i j k = rid (M j k) i

  addFinMatrix0l : ∀ {m n} → (M : FinMatrix G m n)
                 → addFinMatrix zeroFinMatrix M ≡ M
  addFinMatrix0l M i j k = lid (M j k) i

  addFinMatrixNegMatrixr : ∀ {m n} → (M : FinMatrix G m n)
                         → addFinMatrix M (negFinMatrix M) ≡ zeroFinMatrix
  addFinMatrixNegMatrixr M i j k = invr (M j k) i

  addFinMatrixNegMatrixl : ∀ {m n} → (M : FinMatrix G m n)
                         → addFinMatrix (negFinMatrix M) M ≡ zeroFinMatrix
  addFinMatrixNegMatrixl M i j k = invl (M j k) i

  addFinMatrixComm : ∀ {m n} → (M N : FinMatrix G m n)
                   → addFinMatrix M N ≡ addFinMatrix N M
  addFinMatrixComm M N i k l = comm (M k l) (N k l) i

  FinMatrixAbGroup : (m n : ℕ) → AbGroup ℓ
  FinMatrixAbGroup m n =
    makeAbGroup {G = FinMatrix G m n} zeroFinMatrix addFinMatrix negFinMatrix
                isSetFinMatrix addFinMatrixAssoc addFinMatrix0r
                addFinMatrixNegMatrixr addFinMatrixComm

-- Define a abelian group structure on vector matrices and prove that
-- it is equal to FinMatrixAbGroup using the SIP
module _ (G' : AbGroup ℓ) where

  open AbGroupStr (snd G')
  private G = ⟨ G' ⟩
  zeroVecMatrix : ∀ {m n} → VecMatrix G m n
  zeroVecMatrix = replicate (replicate 0g)

  negVecMatrix : ∀ {m n} → VecMatrix G m n → VecMatrix G m n
  negVecMatrix = map (map (λ x → - x))

  addVec : ∀ {m} → Vec G m → Vec G m → Vec G m
  addVec [] [] = []
  addVec (x ∷ xs) (y ∷ ys) = x + y ∷ addVec xs ys

  addVecMatrix : ∀ {m n} → VecMatrix G m n → VecMatrix G m n → VecMatrix G m n
  addVecMatrix [] [] = []
  addVecMatrix (M ∷ MS) (N ∷ NS) = addVec M N ∷ addVecMatrix MS NS

  open FinMatrixAbGroup

  -- Proof that FinMatrix→VecMatrix is a group homorphism
  FinMatrix→VecMatrixHomAdd : (m n : ℕ) (M N : FinMatrix G m n)
    → FinMatrix→VecMatrix (addFinMatrix G' M N) ≡
      addVecMatrix (FinMatrix→VecMatrix M) (FinMatrix→VecMatrix N)
  FinMatrix→VecMatrixHomAdd zero n M N = refl
  FinMatrix→VecMatrixHomAdd (suc m) n M N =
    λ i → lem n (M zero) (N zero) i
        ∷ FinMatrix→VecMatrixHomAdd m n (λ i j → M (suc i) j) (λ i j → N (suc i) j) i
     where
     lem : (n : ℕ) (V W : FinVec G n)
       → FinVec→Vec (λ j → V j + W j) ≡ addVec (FinVec→Vec V) (FinVec→Vec W)
     lem zero V W = refl
     lem (suc n) V W = λ i → V zero + W zero ∷ lem n (V ∘ suc) (W ∘ suc) i

  -- Combine everything to get an induced abelian group structure of
  -- VecMatrix that is equal to the one on FinMatrix
  VecMatrixAbGroup : (m n : ℕ) → AbGroup ℓ
  VecMatrixAbGroup m n =
    InducedAbGroup (FinMatrixAbGroup G' m n) addVecMatrix
      FinMatrix≃VecMatrix (FinMatrix→VecMatrixHomAdd m n)

  FinMatrixAbGroup≡VecMatrixAbGroup : (m n : ℕ) → FinMatrixAbGroup G' m n ≡ VecMatrixAbGroup m n
  FinMatrixAbGroup≡VecMatrixAbGroup m n =
    InducedAbGroupPath (FinMatrixAbGroup G' m n) addVecMatrix
      FinMatrix≃VecMatrix (FinMatrix→VecMatrixHomAdd m n)

-- Define identity matrix and matrix multiplication for FinMatrix and
-- prove that square matrices form a ring
module _ (R' : Ring ℓ) where

  open RingStr (snd R') renaming ( is-set to isSetR )
  open RingTheory R'
  open KroneckerDelta R'
  open Sum R'
  open FinMatrixAbGroup (_ , abgroupstr _ _ _ (snd R' .RingStr.+IsAbGroup))

  private R = ⟨ R' ⟩

  oneFinMatrix : ∀ {n} → FinMatrix R n n
  oneFinMatrix i j = δ i j

  mulFinMatrix : ∀ {m1 m2 m3} → FinMatrix R m1 m2 → FinMatrix R m2 m3 → FinMatrix R m1 m3
  mulFinMatrix M N i k = ∑ λ j → M i j · N j k

  ∑Exchange : ∀ {m n} → (M : FinMatrix R m n) → ∑ (λ i → ∑ (λ j → M i j)) ≡ ∑ (λ j → ∑ (λ i → M i j))
  ∑Exchange {m = zero}  {n = n}     M = sym (∑0r n)
  ∑Exchange {m = suc m} {n = zero}  M = cong (λ x → 0r + x) (∑0r m) ∙ +Rid 0r
  ∑Exchange {m = suc m} {n = suc n} M =
     let a  = M zero zero
         L  = ∑ λ j → M zero (suc j)
         C  = ∑ λ i → M (suc i) zero
         N  = ∑ λ i → ∑ λ j → M (suc i) (suc j)
         -- N reindexed
         N' = ∑ λ j → ∑ λ i → M (suc i) (suc j)
     in a + L + ∑ (λ i → ∑ (λ j → M (suc i) j)) ≡⟨ (λ k → a + L + ∑Split (λ i → M (suc i) zero) (λ i → ∑ (λ j → M (suc i) (suc j))) k) ⟩
        a + L + (C + N)                         ≡⟨ (λ k → a + L + (C + ∑Exchange (λ i j → M (suc i) (suc j)) k)) ⟩
        a + L + (C + N')                        ≡⟨ sym (+Assoc _ _ _) ⟩
        a + (L + (C + N'))                      ≡⟨ (λ k → a + +Assoc-comm1 L C N' k) ⟩
        a + (C + (L + N'))                      ≡⟨ +Assoc _ _ _ ⟩
        a + C + (L + N')                        ≡⟨ (λ k → a + C + ∑Split (λ j → M zero (suc j)) (λ j → ∑ (λ i → M (suc i) (suc j))) (~ k)) ⟩
        a + C + ∑ (λ j → ∑ (λ i → M i (suc j))) ∎

  mulFinMatrixAssoc : ∀ {m n k l} → (M : FinMatrix R m n) → (N : FinMatrix R n k) → (K : FinMatrix R k l)
                   → mulFinMatrix M (mulFinMatrix N K) ≡ mulFinMatrix (mulFinMatrix M N) K
  mulFinMatrixAssoc M N K = funExt₂ λ i j →
    ∑ (λ k → M i k · ∑ (λ l → N k l · K l j))   ≡⟨ ∑Ext (λ k → ∑Mulrdist (M i k) (λ l → N k l · K l j)) ⟩
    ∑ (λ k → ∑ (λ l → M i k · (N k l · K l j))) ≡⟨ ∑Ext (λ k → ∑Ext (λ l → ·Assoc (M i k) (N k l) (K l j))) ⟩
    ∑ (λ k → ∑ (λ l → M i k · N k l · K l j))   ≡⟨ ∑Exchange (λ k l → M i k · N k l · K l j) ⟩
    ∑ (λ l → ∑ (λ k → M i k · N k l · K l j))   ≡⟨ ∑Ext (λ l → sym (∑Mulldist (K l j) (λ k → M i k · N k l))) ⟩
    ∑ (λ l → ∑ (λ k → M i k · N k l) · K l j)   ∎

  mulFinMatrixr1 : ∀ {m n} → (M : FinMatrix R m n) → mulFinMatrix M oneFinMatrix ≡ M
  mulFinMatrixr1 M = funExt₂ λ i j → ∑Mulr1 _ (M i) j

  mulFinMatrix1r : ∀ {m n} → (M : FinMatrix R m n) → mulFinMatrix oneFinMatrix M ≡ M
  mulFinMatrix1r M = funExt₂ λ i j → ∑Mul1r _ (λ x → M x j) i

  mulFinMatrixrDistrAddFinMatrix : ∀ {n} (M N K : FinMatrix R n n)
                                 → mulFinMatrix M (addFinMatrix N K) ≡ addFinMatrix (mulFinMatrix M N) (mulFinMatrix M K)
  mulFinMatrixrDistrAddFinMatrix M N K = funExt₂ λ i j →
    ∑ (λ k → M i k · (N k j + K k j))                 ≡⟨ ∑Ext (λ k → ·Rdist+ (M i k) (N k j) (K k j)) ⟩
    ∑ (λ k → M i k · N k j + M i k · K k j)           ≡⟨ ∑Split (λ k → M i k · N k j) (λ k → M i k · K k j) ⟩
    ∑ (λ k → M i k · N k j) + ∑ (λ k → M i k · K k j) ∎

  mulFinMatrixlDistrAddFinMatrix : ∀ {n} (M N K : FinMatrix R n n)
                                 → mulFinMatrix (addFinMatrix M N) K ≡ addFinMatrix (mulFinMatrix M K) (mulFinMatrix N K)
  mulFinMatrixlDistrAddFinMatrix M N K = funExt₂ λ i j →
    ∑ (λ k → (M i k + N i k) · K k j)                 ≡⟨ ∑Ext (λ k → ·Ldist+ (M i k) (N i k) (K k j)) ⟩
    ∑ (λ k → M i k · K k j + N i k · K k j)           ≡⟨ ∑Split (λ k → M i k · K k j) (λ k → N i k · K k j) ⟩
    ∑ (λ k → M i k · K k j) + ∑ (λ k → N i k · K k j) ∎

  FinMatrixRing : (n : ℕ) → Ring ℓ
  FinMatrixRing n =
    makeRing {R = FinMatrix R n n} zeroFinMatrix oneFinMatrix addFinMatrix
             mulFinMatrix negFinMatrix isSetFinMatrix addFinMatrixAssoc
             addFinMatrix0r addFinMatrixNegMatrixr addFinMatrixComm
             mulFinMatrixAssoc mulFinMatrixr1 mulFinMatrix1r
             mulFinMatrixrDistrAddFinMatrix mulFinMatrixlDistrAddFinMatrix


{-# OPTIONS --type-in-type #-}  -- yes, there will be some cheating in this lecture

module Lec3Done where

open import Lec1Done
open import Lec2Done

postulate
  extensionality : {S : Set}{T : S -> Set}
                   {f g : (x : S) -> T x} ->
                   ((x : S) -> f x == g x) ->
                   f == g

imp : {S : Set}{T : S -> Set}(f : (x : S) -> T x){x : S} -> T x
imp f {x} = f x

extensionality' : {S : Set}{T : S -> Set}
                   {f g : {x : S} -> T x} ->
                   ((x : S) -> f {x} == g {x}) ->
                   _==_ {forall {x : S} -> T x} f g
extensionality' {f = f}{g = g} q =
  refl imp =$= extensionality {f = \ x -> f {x}}{g = \ x -> g {x}}
    q

_[QED] : {X : Set}(x : X) -> x == x
x [QED] = refl x
_=[_>=_ : {X : Set}(x : X){y z : X} -> x == y -> y == z -> x == z
x =[ refl .x >= q = q
_=<_]=_ : {X : Set}(x : X){y z : X} -> y == x -> y == z -> x == z
x =< refl .x ]= q = q
infixr 1 _=[_>=_ _=<_]=_
infixr 2 _[QED]

record Category : Set where
  field

    -- two types of thing
    Obj  : Set                  -- "objects"
    _~>_ : Obj -> Obj -> Set    -- "arrows" or "morphisms"
                                -- or "homomorphisms"

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

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

  assocn : {Q R R' S T : Obj}
            {f  : Q ~> R} {g : R ~> S}
            {f' : Q ~> R'}{g' : R' ~> S}
            {h : S ~> T} ->
            (f >~> g) == (f' >~> g') ->
            (f >~> g >~> h) == (f' >~> g' >~> h)
  assocn {f = f} {g = g} {f' = f'} {g' = g'} {h = h} q =
    f >~> g >~> h
      =< law->~>>~> _ _ _ ]=
    (f >~> g) >~> h
      =[ refl _>~>_ =$= q =$= refl h >=
    (f' >~> g') >~> h
      =[ law->~>>~> _ _ _ >=
    f' >~> g' >~> h
      [QED]

  infixr 3 _>~>_

-- Sets and functions are the classic example of a category.
SET : Category
SET = record
        { Obj         = Set
        ; _~>_        = \ S T -> S -> T
        ; id~>        = id
        ; _>~>_       = _>>_
        ; law-id~>>~> = \ f -> refl f
        ; law->~>id~> = \ f -> refl f
        ; law->~>>~>  = \ f g h -> refl (f >> (g >> h))
        }

-- A PREORDER is a category where there is at most one arrow between
-- any two objects. (So arrows are unique.)
NAT->= : Category
unique->= : (m n : Nat)(p q : m >= n) -> p == q
unique->= m zero p q = refl <>
unique->= zero (suc n) () q
unique->= (suc m) (suc n) p q = unique->= m n p q

NAT->= = record
           { Obj         = Nat
           ; _~>_        = _>=_
           ; id~>        = \ {n} -> refl->= n
           ; _>~>_       = \ {m}{n}{p} m>=n n>=p -> trans->= m n p m>=n n>=p
           ; law-id~>>~> = \ {m}{n} m>=n -> unique->= m n _ _
           ; law->~>id~> = \ {m}{n} m>=n -> unique->= m n _ _
           ; law->~>>~>  = \ {m}{n}{p}{q} m>n n>=p p>=q -> unique->= m q _ _
           } where

-- A MONOID is a category with Obj = One.
-- The values in the monoid are the *arrows*.
ONE-Nat : Category
ONE-Nat = record
            { Obj         = One
            ; _~>_        = \ _ _ -> Nat
            ; id~>        = 0
            ; _>~>_       = _+N_
            ; law-id~>>~> = \ n -> zero-+N n
            ; law->~>id~> = \ n -> +N-zero n
            ; law->~>>~>  = \ m n p -> assocLR-+N m n p
            }

eqUnique : {X : Set}{x y : X}(p q : x == y) -> p == q
eqUnique (refl x) (refl .x) = refl (refl x)

-- A DISCRETE category is one where the only arrows are the identities.
DISCRETE : (X : Set) -> Category
DISCRETE X = record
               { Obj         = X
               ; _~>_        = _==_
               ; id~>        = refl _
               ; _>~>_       = \ { {x} (refl .x) (refl .x) -> refl x }
               ; law-id~>>~> = \ _ -> eqUnique _ _
               ; law->~>id~> = \ _ -> eqUnique _ _
               ; law->~>>~>  = \ _ _ _ -> eqUnique _ _
               }

module FUNCTOR where
  open Category

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

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

open FUNCTOR public

postulate homework : {A : Set} -> A

VEC : Nat -> SET => SET
VEC n = record
  { F-Obj       = \ X -> Vec X n
  ; F-map       = homework
  ; F-map-id~>  = homework
  ; F-map->~>   = homework
  }

VTAKE : Set -> NAT->= => SET
VTAKE X = record
  { F-Obj      = Vec X
  ; F-map      = \ {m}{n} m>=n xs -> vTake m n m>=n xs
  ; F-map-id~> = \ {n} -> extensionality \ xs -> vTakeIdFact n xs
  ; F-map->~>  = \ {m}{n}{p} m>=n n>=p -> extensionality \ xs ->
     vTakeCpFact m n p m>=n n>=p xs
  }

ADD : Nat -> NAT->= => NAT->=
ADD d = record { F-Obj = (d +N_)
               ; F-map = \ {m}{n} -> help d m n
               ; F-map-id~> = \ {n} -> unique->= (d +N n) (d +N n) _ _
               ; F-map->~> = \ {m}{n}{p} x y ->
                  unique->= (d +N m) (d +N p) _ _
               } where
  help : (d m n : Nat) -> m >= n -> (d +N m) >= (d +N n)
  help zero m n m>=n = m>=n
  help (suc d) m n m>=n = help d m n m>=n

Thing : {C D : Category}(F G : C => D) -> Set
Thing {C}{D}
      (record { F-Obj = F-Obj ; F-map = F-map
              ; F-map-id~> = F-map-id~> ; F-map->~> = F-map->~> })
      (record { F-Obj = G-Obj ; F-map = G-map
              ; F-map-id~> = G-map-id~> ; F-map->~> = G-map->~> })
  = Sg (F-Obj == G-Obj)
     \ { (refl _) ->
    Sg (_==_ {forall {S T : Category.Obj C} →
          (C Category.~> S) T → (D Category.~> F-Obj S) (F-Obj T)}
          F-map  G-map)
     \ { (refl _) ->
       _==_ {forall {T : Category.Obj C} →
      F-map (Category.id~> C {T}) == Category.id~> D} F-map-id~> G-map-id~>
       *
       _==_ {forall {R S T : Category.Obj C} (f : (C Category.~> R) S)
      (g : (C Category.~> S) T) →
      F-map ((C Category.>~> f) g) == (D Category.>~> F-map f) (F-map g)}
        F-map->~>  G-map->~>
     }}

Lemma : {C D : Category}{F G : C => D} -> Thing F G -> F == G
Lemma (refl _ , (refl _ , (refl _ , refl _))) = refl _

CATEGORY : Category
CATEGORY = record
             { Obj = Category
             ; _~>_ = _=>_
             ; id~> = record { F-Obj = \ X -> X
                             ; F-map = \ a -> a
                             ; F-map-id~> = refl _
                             ; F-map->~> = \ _ _ -> refl _ }
             ; _>~>_ = \ {R}{S}{T} F G -> record
               { F-Obj = F-Obj F >> F-Obj G
               ; F-map = F-map F >> F-map G
               ; F-map-id~> = F-map G (F-map F (Category.id~> R))
                                =[ refl (F-map G) =$= F-map-id~> F >=
                              F-map G (Category.id~> S)
                                =[ F-map-id~> G >=
                              Category.id~> T
                                [QED]
               ; F-map->~> = \ f g ->
                   F-map G (F-map F (Category._>~>_ R f g))
                     =[ refl (F-map G) =$= F-map->~> F f g >=
                   F-map G (Category._>~>_ S (F-map F f) (F-map F g))
                     =[ F-map->~> G (F-map F f) (F-map F g) >=
                   Category._>~>_ T (F-map G (F-map F f))
                                    (F-map G (F-map F g))
                     [QED]
               }
             ; law-id~>>~> = \ F -> Lemma
                 ((refl _) , ((refl _) ,
                    (extensionality' (\ x -> eqUnique _ _) ,
                    extensionality' (\ x ->
                      extensionality' \ y -> extensionality' \ z ->
                        extensionality \ f -> extensionality \ g ->
                          eqUnique _ _))))
             ; law->~>id~> = \ F -> Lemma
                 ((refl _) , ((refl _) ,
                    (extensionality' (\ x -> eqUnique _ _) ,
                    extensionality' (\ x ->
                      extensionality' \ y -> extensionality' \ z ->
                        extensionality \ f -> extensionality \ g ->
                          eqUnique _ _))))
             ; law->~>>~> = \ F G H ->  Lemma
                 ((refl _) , ((refl _) ,
                    (extensionality' (\ x -> eqUnique _ _) ,
                    extensionality' (\ x ->
                      extensionality' \ y -> extensionality' \ z ->
                        extensionality \ f -> extensionality \ g ->
                          eqUnique _ _))))
             } where
             open _=>_


module PiFrac where

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

infixr 20 _◎_

data <_> {A : Set} : A → Set where
  singleton : (x : A) -> < x >

mutual

  data B : Set₁ where
    ZERO  : B
    ONE   : B
    PLUS  : B → B → B
    TIMES : B → B → B
    SING  : (b : B) → ⟦ b ⟧ → B
    RECIP : (b : B) → ⟦ b ⟧ → B
    DPAIR : (b : B) → (⟦ b ⟧ → B) → B

  ⟦_⟧ : B → Set
  ⟦ ZERO ⟧         = ⊥
  ⟦ ONE ⟧          = ⊤
  ⟦ PLUS b₁ b₂ ⟧   = ⟦ b₁ ⟧ ⊎ ⟦ b₂ ⟧
  ⟦ TIMES b₁ b₂ ⟧  = ⟦ b₁ ⟧ × ⟦ b₂ ⟧
  ⟦ SING b v ⟧  = <_> {⟦ b ⟧} v
  ⟦ RECIP b v ⟧      = <_> {⟦ b ⟧} v → ⊤
  ⟦ DPAIR b c ⟧ = Σ ⟦ b ⟧ (λ v → ⟦ c v ⟧)

data _⟷_ : B → B → Set₁ where
  unite₊ : {b : B} → PLUS ZERO b ⟷ b
  uniti₊ : {b : B} → b ⟷ PLUS ZERO b
  swap₊ : {b₁ b₂ : B} → PLUS b₁ b₂ ⟷ PLUS b₂ b₁
  assocl₊ : { b₁ b₂ b₃ : B } → PLUS b₁ (PLUS b₂ b₃) ⟷ PLUS (PLUS b₁ b₂) b₃
  assocr₊ : { b₁ b₂ b₃ : B } → PLUS (PLUS b₁ b₂) b₃ ⟷ PLUS b₁ (PLUS b₂ b₃)
  unite⋆  : { b : B } → TIMES ONE b ⟷ b
  uniti⋆  : { b : B } → b ⟷ TIMES ONE b
  swap⋆ : {b₁ b₂ : B} → TIMES b₁ b₂ ⟷ TIMES b₂ b₁
  assocl⋆ : { b₁ b₂ b₃ : B } → TIMES b₁ (TIMES b₂ b₃) ⟷ TIMES (TIMES b₁ b₂) b₃
  assocr⋆ : { b₁ b₂ b₃ : B } → TIMES (TIMES b₁ b₂) b₃ ⟷ TIMES b₁ (TIMES b₂ b₃)
  id⟷ : {b : B } → b ⟷ b
  sym    : { b₁ b₂ : B } → (b₁ ⟷ b₂) → (b₂ ⟷ b₁)
  _◎_    : { b₁ b₂ b₃ : B } → (b₁ ⟷ b₂) → (b₂ ⟷ b₃) → (b₁ ⟷ b₃)
  _⊕_    : { b₁ b₂ b₃ b₄ : B } →
           (b₁ ⟷ b₃) → (b₂ ⟷ b₄) → (PLUS b₁ b₂ ⟷ PLUS b₃ b₄)
  _⊗_    : { b₁ b₂ b₃ b₄ : B } →
           (b₁ ⟷ b₃) → (b₂ ⟷ b₄) → (TIMES b₁ b₂ ⟷ TIMES b₃ b₄)
  η : {b : B} → ONE ⟷ DPAIR b (λ v → RECIP b v)
  ε : {b : B} → DPAIR b (λ v → RECIP b v) ⟷ ONE

mutual
  eval : {b₁ b₂ : B} → (c : b₁ ⟷ b₂) → ⟦ b₁ ⟧ → ⟦ b₂ ⟧
  eval unite₊ (inj₁ ())
  eval unite₊ (inj₂ v) = v
  eval uniti₊ v = inj₂ v
  eval swap₊ (inj₁ x) = inj₂ x
  eval swap₊ (inj₂ y) = inj₁ y
  eval assocl₊ (inj₁ x) = inj₁ (inj₁ x)
  eval assocl₊ (inj₂ (inj₁ x)) = inj₁ (inj₂ x)
  eval assocl₊ (inj₂ (inj₂ x)) = inj₂ x
  eval assocr₊ (inj₁ (inj₁ x)) = inj₁ x
  eval assocr₊ (inj₁ (inj₂ y)) = inj₂ (inj₁ y)
  eval assocr₊ (inj₂ y) = inj₂ (inj₂ y)
  eval unite⋆ (tt , x) = x
  eval uniti⋆ v = ( tt , v)
  eval swap⋆ (v₁ , v₂) = (v₂ , v₁)
  eval assocl⋆ (x , (y , z)) = ((x , y), z)
  eval assocr⋆ ((x , y), z) = (x , (y , z))
  eval id⟷ v = v
  eval (sym c) v = evalB c v
  eval (c₁ ◎ c₂) v = eval c₂ (eval c₁ v)
  eval (c₁ ⊕ c₂) (inj₁ x) = inj₁ (eval c₁ x)
  eval (c₁ ⊕ c₂) (inj₂ y) = inj₂ (eval c₂ y)
  eval (c₁ ⊗ c₂) (x , y) = (eval c₁ x , eval c₂ y)
  eval (η {b}) tt = {!!}
  eval (ε {b}) (w , c) = c (singleton w)

  evalB :  {b₁ b₂ : B} → (c : b₁ ⟷ b₂) → ⟦ b₂ ⟧ → ⟦ b₁ ⟧
  evalB uniti₊ (inj₁ ())
  evalB uniti₊ (inj₂ v) = v
  evalB unite₊ v = inj₂ v
  evalB swap₊ (inj₁ x) = inj₂ x
  evalB swap₊ (inj₂ y) = inj₁ y
  evalB assocr₊ (inj₁ x) = inj₁ (inj₁ x)
  evalB assocr₊ (inj₂ (inj₁ x)) = inj₁ (inj₂ x)
  evalB assocr₊ (inj₂ (inj₂ x)) = inj₂ x
  evalB assocl₊ (inj₁ (inj₁ x)) = inj₁ x
  evalB assocl₊ (inj₁ (inj₂ y)) = inj₂ (inj₁ y)
  evalB assocl₊ (inj₂ y) = inj₂ (inj₂ y)
  evalB uniti⋆ (tt , x) = x
  evalB unite⋆ v = ( tt , v)
  evalB swap⋆ (v₁ , v₂) = (v₂ , v₁)
  evalB assocr⋆ (x , (y , z)) = ((x , y), z)
  evalB assocl⋆ ((x , y), z) = (x , (y , z))
  evalB id⟷ v = v
  evalB (sym c) v = eval c v
  evalB (c₁ ◎ c₂) v = evalB c₁ (evalB c₂ v)
  evalB (c₁ ⊕ c₂) (inj₁ x) = inj₁ (evalB c₁ x)
  evalB (c₁ ⊕ c₂) (inj₂ y) = inj₂ (evalB c₂ y)
  evalB (c₁ ⊗ c₂) (x , y) = (evalB c₁ x , evalB c₂ y)
  evalB (η {b}) (w , c) = c (singleton w)
  evalB (ε {b}) tt = {!!}

 -- this is better in that one can't use inj₂ tt on the rhs
-- the overly precise version doesn't type check, don't know why
test : ⊤
test = eval (ε {PLUS ONE ONE}) (inj₁ tt , f)
  where f : < inj₁ {B = ⊤} tt > → ⊤
--      f (singleton (inj₁ {B = ⊤} tt)) = tt
        f x = tt
{-
mutual
  data B : Set where
    ONE   : B
    PLUS  : B → B → B
    TIMES : B → B → B
    MATCH : {b : B} → Singleton b → B
    DPAIR : {b : B} → (Σ (BV b) Singleton) → B

mutual
  eval : {b₁ b₂ : B} → b₁ ⟷ b₂ → BV b₁ → BV b₂
  eval swap₊ (LEFT v) = RIGHT v
  eval swap₊ (RIGHT v) = LEFT v
  eval (η {b₁} {v}) UNIT = NCPROD b₁ v
  eval assocl₊ (LEFT v) = LEFT (LEFT v)
  eval assocl₊ (RIGHT (LEFT v)) = LEFT (RIGHT v)
  eval assocl₊ (RIGHT (RIGHT v)) = RIGHT v
  eval assocr₊ (LEFT (LEFT v)) = LEFT v
  eval assocr₊ (LEFT (RIGHT v)) = RIGHT (LEFT v)
  eval assocr₊ (RIGHT v) = RIGHT (RIGHT v)
  eval unite⋆ (PAIR UNIT v) = v
  eval uniti⋆ v = PAIR UNIT v
  eval (ε {b₁} {v}) (NCPROD .b₁ .v) = UNIT
  eval id⟷ v = v
  eval (c₁ ◎ c₂) v = eval c₂ (eval c₁ v)
  eval (c₁ ⊕ c₂) (LEFT v) = LEFT (eval c₁ v)
  eval (c₁ ⊕ c₂) (RIGHT v) = RIGHT (eval c₂ v)
  eval (c₁ ⊗ c₂) (PAIR v₁ v₂) = PAIR (eval c₁ v₁) (eval c₂ v₂)
  eval (lift {b₁} {b₂} {v} c) (NCPROD .b₁ .v) = NCPROD b₂ (eval c v)

-- name : {b₁ b₂ : B} {v : BV b₁} → (b₁ ⟷ b₂) → (ONE ⟷ DPAIR (v , singleton v))
-- name {b₁} {b₂} {v} c = η {b₁} {v} ◎ (lift (id⟷ ⊗ c))

--}

--------------------------------------------------------------------------------
-- Jacques's code

{--

data Singleton {A : Set} : A → Set where
  singleton : (x : A) -> Singleton x

mutual
  data B : Set where
    ONE   : B
    PLUS  : B → B → B
    TIMES : B → B → B
    MATCH : {b : B} → Singleton b → B
    DPAIR : {b : B} → (Σ (BV b) Singleton) → B

  data BV : B → Set where
    UNIT : BV ONE
    LEFT : {b₁ b₂ : B} → BV b₁ → BV (PLUS b₁ b₂)
    RIGHT : {b₁ b₂ : B} → BV b₂ → BV (PLUS b₁ b₂)
    PAIR : {b₁ b₂ : B} → BV b₁ → BV b₂ → BV (TIMES b₁ b₂)
    SINGLE : {b₁ : B} → BV b₁ → BV (MATCH {b₁} (singleton b₁))
    NCPROD : (b₁ : B) →  (v : BV b₁) → BV (DPAIR (v , (singleton v)))

mutual
  data _⟷_ : B → B → Set where
    swap₊ : {b₁ b₂ : B} → PLUS b₁ b₂ ⟷ PLUS b₂ b₁
    assocl₊ : { b₁ b₂ b₃ : B } → PLUS b₁ (PLUS b₂ b₃) ⟷ PLUS (PLUS b₁ b₂) b₃
    assocr₊ : { b₁ b₂ b₃ : B } → PLUS (PLUS b₁ b₂) b₃ ⟷ PLUS b₁ (PLUS b₂ b₃)
    unite⋆  : { b : B } → TIMES ONE b ⟷ b
    uniti⋆  : { b : B } → b ⟷ TIMES ONE b
    η : {b₁ : B} {v : BV b₁} → ONE ⟷ DPAIR (v , (singleton v))
    ε : {b₁ : B} {v : BV b₁} → DPAIR (v , (singleton v)) ⟷ ONE
    id⟷ : {b : B } → b ⟷ b
-- closure combinators
--   sym    : { b₁ b₂ : B } → (b₁ ⟷ b₂) → (b₂ ⟷ b₁)
    _◎_    : { b₁ b₂ b₃ : B } → (b₁ ⟷ b₂) → (b₂ ⟷ b₃) → (b₁ ⟷ b₃)
    _⊕_    : { b₁ b₂ b₃ b₄ : B } →
             (b₁ ⟷ b₃) → (b₂ ⟷ b₄) → (PLUS b₁ b₂ ⟷ PLUS b₃ b₄)
    _⊗_    : { b₁ b₂ b₃ b₄ : B } →
             (b₁ ⟷ b₃) → (b₂ ⟷ b₄) → (TIMES b₁ b₂ ⟷ TIMES b₃ b₄)
--   refe⋆  : { b : BF } → RECIP (RECIP b) ⟺ b
--   refi⋆  : { b : BF } → b ⟺ RECIP (RECIP b)
--   rile⋆  : { b : BF } → TIMESF b (TIMESF b (RECIP b)) ⟺ b
--   rili⋆  : { b : BF } → b ⟺ TIMESF b (TIMESF b (RECIP b))
    lift : {b₁ b₂ : B} {v : BV b₁} → (c : b₁ ⟷ b₂) →
      DPAIR (v , (singleton v)) ⟷ DPAIR (eval c v , (singleton (eval c v)))

  eval : {b₁ b₂ : B} → b₁ ⟷ b₂ → BV b₁ → BV b₂
  eval swap₊ (LEFT v) = RIGHT v
  eval swap₊ (RIGHT v) = LEFT v
  eval (η {b₁} {v}) UNIT = NCPROD b₁ v
  eval assocl₊ (LEFT v) = LEFT (LEFT v)
  eval assocl₊ (RIGHT (LEFT v)) = LEFT (RIGHT v)
  eval assocl₊ (RIGHT (RIGHT v)) = RIGHT v
  eval assocr₊ (LEFT (LEFT v)) = LEFT v
  eval assocr₊ (LEFT (RIGHT v)) = RIGHT (LEFT v)
  eval assocr₊ (RIGHT v) = RIGHT (RIGHT v)
  eval unite⋆ (PAIR UNIT v) = v
  eval uniti⋆ v = PAIR UNIT v
  eval (ε {b₁} {v}) (NCPROD .b₁ .v) = UNIT
  eval id⟷ v = v
  eval (c₁ ◎ c₂) v = eval c₂ (eval c₁ v)
  eval (c₁ ⊕ c₂) (LEFT v) = LEFT (eval c₁ v)
  eval (c₁ ⊕ c₂) (RIGHT v) = RIGHT (eval c₂ v)
  eval (c₁ ⊗ c₂) (PAIR v₁ v₂) = PAIR (eval c₁ v₁) (eval c₂ v₂)
  eval (lift {b₁} {b₂} {v} c) (NCPROD .b₁ .v) = NCPROD b₂ (eval c v)

-- name : {b₁ b₂ : B} {v : BV b₁} → (b₁ ⟷ b₂) → (ONE ⟷ DPAIR (v , singleton v))
-- name {b₁} {b₂} {v} c = η {b₁} {v} ◎ (lift (id⟷ ⊗ c))

--}


{-# OPTIONS --without-K --safe #-}
module Categories.Category.Construction.Functors where

-- the "Functor Category", often denoted [ C , D ]

open import Level
open import Data.Product using (_,_; proj₁; uncurry′)

open import Categories.Category using (Category; _[_∘_])
open import Categories.Category.Equivalence using (StrongEquivalence)
open import Categories.Category.Product using (_※ⁿ_) renaming (Product to _×_)
open import Categories.Functor using (Functor; _∘F_)
open import Categories.Functor.Bifunctor
open import Categories.Functor.Construction.Constant using (constNat)
open import Categories.NaturalTransformation
  using (NaturalTransformation; _∘ᵥ_; _∘ˡ_; _∘ₕ_) renaming (id to idN)
open import Categories.NaturalTransformation.Equivalence using (_≃_; ≃-isEquivalence)
open import Categories.NaturalTransformation.NaturalIsomorphism
  using (NaturalIsomorphism)
import Categories.Morphism.Reasoning as MR

private
  variable
    o ℓ e o′ ℓ′ e′ : Level
    C D C₁ C₂ : Category o ℓ e

-- The reason the proofs below are so easy is that _∘ᵥ_ 'computes' all the way down into
-- expressions in D, from which the properties follow.
Functors : Category o ℓ e → Category o′ ℓ′ e′ → Category (o ⊔ ℓ ⊔ e ⊔ o′ ⊔ ℓ′ ⊔ e′) (o ⊔ ℓ ⊔ ℓ′ ⊔ e′) (o ⊔ e′)
Functors C D = record
  { Obj       = Functor C D
  ; _⇒_       = NaturalTransformation
  ; _≈_       = _≃_
  ; id        = idN
  ; _∘_       = _∘ᵥ_
  ; assoc     = assoc
  ; sym-assoc = sym-assoc
  ; identityˡ = identityˡ
  ; identityʳ = identityʳ
  ; identity² = identity²
  ; equiv     = ≃-isEquivalence
  ; ∘-resp-≈  = λ eq eq′ → ∘-resp-≈ eq eq′
  }
  where module C = Category C
        module D = Category D
        open D

-- Part of the proof that Cats is a CCC:
eval : Bifunctor (Functors C D) C D
eval {C = C} {D = D} = record
  { F₀           = uncurry′ Functor.F₀
  ; F₁           = λ where
    {F , _} {_ , B} (α , f) →
      let open NaturalTransformation α
          open Functor F
      in η B ∘ F₁ f
  ; identity     = λ where
    {F , _} → elimʳ (Functor.identity F)
  ; homomorphism = λ where
    {F , _} {G , B} {_ , C} {α , f} {β , g} →
      let open NaturalTransformation
          open Functor
      in begin
        (η β C ∘ η α C) ∘ F₁ F (g C.∘ f)  ≈⟨ refl ⟩∘⟨ homomorphism F ⟩
        (η β C ∘ η α C) ∘ F₁ F g ∘ F₁ F f ≈⟨ assoc ⟩
        η β C ∘ η α C ∘ F₁ F g ∘ F₁ F f   ≈⟨ refl ⟩∘⟨ pullˡ (commute α g) ⟩
        η β C ∘ (F₁ G g ∘ η α B) ∘ F₁ F f ≈⟨ refl ⟩∘⟨ assoc ⟩
        η β C ∘ F₁ G g ∘ η α B ∘ F₁ F f   ≈˘⟨ assoc ⟩
        (η β C ∘ F₁ G g) ∘ η α B ∘ F₁ F f ∎
  ; F-resp-≈     = λ where
    {F , _} (comm , eq) → ∘-resp-≈ comm (Functor.F-resp-≈ F eq)
  }
  where module C = Category C
        module D = Category D
        open D
        open MR D
        open HomReasoning

-- Currying induces a functor between functor categories -- another
-- part of the proof that Cats is a catesian closed (bi)category.

curry : Functor (Functors (C₁ × C₂) D) (Functors C₁ (Functors C₂ D))
curry {C₁ = C₁} {C₂ = C₂} {D = D} = record
  { F₀ = curry₀
  ; F₁ = curry₁
  ; identity     = Equiv.refl D
  ; homomorphism = Equiv.refl D
  ; F-resp-≈     = λ F≈G {x₁} {x₂} → F≈G {x₁ , x₂}
  }
  where
    open Category

    curry₀ : Bifunctor C₁ C₂ D → Functor C₁ (Functors C₂ D)
    curry₀ F = record
      { F₀ = λ c → appˡ F c
      ; F₁ = λ f → F ∘ˡ (constNat f ※ⁿ idN)
      ; identity     = identity
      ; homomorphism = λ {_} {_} {_} {f} {g} → begin
          F₁ (C₁ [ g ∘ f ] , id C₂)
        ≈˘⟨ F-resp-≈ (Equiv.refl C₁ , identityˡ C₂) ⟩
          F₁ (C₁ [ g ∘ f ] , C₂ [ id C₂ ∘ id C₂ ])
        ≈⟨ homomorphism ⟩
          D [ F₁ (g , id C₂) ∘ F₁ (f , id C₂) ]
        ∎
      ; F-resp-≈ = λ f≈g → F-resp-≈ (f≈g , Equiv.refl C₂)
      }
      where
        open Functor F
        open HomReasoning D

    curry₁ : {F G : Bifunctor C₁ C₂ D} →
             NaturalTransformation F G →
             NaturalTransformation (curry₀ F) (curry₀ G)
    curry₁ α = record
      { η = λ c → record
        { η           = λ a → η α (c , a)
        ; commute     = λ f → commute α (id C₁  , f)
        ; sym-commute = λ f → sym-commute α (id C₁  , f)
        }
      ; commute       = λ f → commute α (f , id C₂)
      ; sym-commute   = λ f → sym-commute α (f , id C₂)
      }
      where open NaturalTransformation

module curry {o₁ e₁ ℓ₁} {C₁ : Category o₁ e₁ ℓ₁}
             {o₂ e₂ ℓ₂} {C₂ : Category o₂ e₂ ℓ₂}
             {o′ e′ ℓ′} {D  : Category o′ e′ ℓ′}
             where
  open Functor (curry {C₁ = C₁} {C₂ = C₂} {D = D}) public
  open Category
  open NaturalIsomorphism

  -- Currying preserves natural isos.
  -- This makes |curry.F₀| a map between the hom-setoids of Cats.

  resp-NI : {F G : Bifunctor C₁ C₂ D} →
            NaturalIsomorphism F G → NaturalIsomorphism (F₀ F) (F₀ G)
  resp-NI α = record
    { F⇒G = F₁ (F⇒G α)
    ; F⇐G = F₁ (F⇐G α)
    ; iso = λ x → record
      { isoˡ = iso.isoˡ α (x , _)
      ; isoʳ = iso.isoʳ α (x , _)
      }
    }

-- Godement product ?
product : {A B C : Category o ℓ e} → Bifunctor (Functors B C) (Functors A B) (Functors A C)
product {A = A} {B = B} {C = C} = record
  { F₀ = uncurry′ _∘F_
  ; F₁ = uncurry′ _∘ₕ_
  ; identity = λ {f} → identityʳ ○ identity {D = C} (proj₁ f)
  ; homomorphism = λ { {_ , F₂} {G₁ , G₂} {H₁ , _} {f₁ , f₂} {g₁ , g₂} {x} → begin
      F₁ H₁ (η g₂ x B.∘ η f₂ x) ∘ η g₁ (F₀ F₂ x) ∘ η f₁ (F₀ F₂ x)
          ≈⟨ ∘-resp-≈ˡ (homomorphism H₁) ○ assoc ○ ∘-resp-≈ʳ (⟺ assoc) ⟩
      F₁ H₁ (η g₂ x) ∘ (F₁ H₁ (η f₂ x) ∘ η g₁ (F₀ F₂ x)) ∘ η f₁ (F₀ F₂ x)
          ≈⟨  ⟺ ( refl⟩∘⟨ ( commute g₁ (η f₂ x) ⟩∘⟨refl) ) ⟩
      F₁ H₁ (η g₂ x) ∘ (η g₁ (F₀ G₂ x) ∘ F₁  G₁ (η f₂ x)) ∘ η f₁ (F₀ F₂ x)
          ≈⟨ ∘-resp-≈ʳ assoc ○ ⟺ assoc ⟩
      (F₁ H₁ (η g₂ x) ∘ η g₁ (F₀ G₂ x)) ∘ F₁ G₁ (η f₂ x) ∘ η f₁ (F₀ F₂ x) ∎ }
  ; F-resp-≈ = λ { {_} {g₁ , _} (≈₁ , ≈₂) → ∘-resp-≈ (F-resp-≈ g₁ ≈₂) ≈₁ }
  }
  where
    open Category C
    open HomReasoning
    open Functor
    module B = Category B
    open NaturalTransformation

-- op induces a Functor on the Functors category.
-- This is an instance where the proof-irrelevant version is simpler because (op op C) is
-- just C. Here we rather need to be more explicit.
opF⇒ : {A : Category o ℓ e} {B : Category o′ ℓ′ e′} →
      Functor (Category.op (Functors (Category.op A) (Category.op B))) (Functors A B)
opF⇒ {A = A} {B} = record
  { F₀ = Functor.op
  ; F₁ = NaturalTransformation.op
  ; identity = Equiv.refl
  ; homomorphism = Equiv.refl
  ; F-resp-≈ = λ eq → eq
  }
  where open Category B

opF⇐ : {A : Category o ℓ e} {B : Category o′ ℓ′ e′} →
      Functor (Functors A B) (Category.op (Functors (Category.op A) (Category.op B)))
opF⇐ {A = A} {B} = record
  { F₀           = Functor.op
  ; F₁           = NaturalTransformation.op
  ; identity     = Equiv.refl
  ; homomorphism = Equiv.refl
  ; F-resp-≈     = λ eq → eq
  }
  where open Category B

Functorsᵒᵖ-equiv : {A : Category o ℓ e} {B : Category o′ ℓ′ e′} →
                   StrongEquivalence (Category.op (Functors (Category.op A) (Category.op B))) (Functors A B)
Functorsᵒᵖ-equiv {B = B} = record
  { F            = opF⇒
  ; G            = opF⇐
  ; weak-inverse = record
    { F∘G≈id = record
      { F⇒G = record
        { η           = λ _ → idN
        ; commute     = λ _ → id-comm-sym
        ; sym-commute = λ _ → id-comm
        }
      ; F⇐G = record
        { η           = λ _ → idN
        ; commute     = λ _ → id-comm-sym
        ; sym-commute = λ _ → id-comm
        }
      ; iso = λ _ → record
        { isoˡ = identityˡ
        ; isoʳ = identityˡ
        }
      }
    ; G∘F≈id = record
      { F⇒G = record
        { η           = λ _ → idN
        ; commute     = λ _ → id-comm-sym
        ; sym-commute = λ _ → id-comm
        }
      ; F⇐G = record
        { η           = λ _ → idN
        ; commute     = λ _ → id-comm-sym
        ; sym-commute = λ _ → id-comm
        }
      ; iso = λ _ → record
        { isoˡ = identityˡ
        ; isoʳ = identityˡ
        }
      }
    }
  }
  where open Category B
        open HomReasoning
        open MR B


infixr 2 _×_
infixr 2 _,_
record Σ (A : Set) (B : A → Set) : Set where
  constructor _,_
  field
    fst : A
    snd : B fst

_×_ : Set → Set → Set
A × B = Σ A λ _ → B

syntax Σ A (λ x → B) = Σ[ x ∶ A ] B

data W (A : Set) (B : A → Set) : Set where
  sup : (x : A) → ((p : B x) → W A B) → W A B

-- Should be able to get φ (λ x → (k x) , rec φ (k x)))
-- just using refine.
rec : ∀ {S P}{X : Set} → (Σ[ s ∶ S ] (P s → W S P × X) → X) → W S P → X
rec φ (sup s k) = {!!}

postulate
  A : Set
  a : A

-- Refine hole as far as possible.
-- Should get: (? , ?) , (λ x → ?) , λ x → ?
test-refine : (A × A) × (A → A) × (A → A)
test-refine = {!!}


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

open import HoTT
open import homotopy.EilenbergMacLane
open import groups.ToOmega
open import cohomology.Theory
open import cohomology.SpectrumModel

module cohomology.EMModel where

module _ {i} (G : AbGroup i) where

  open EMExplicit G using (⊙EM; EM-level; EM-conn; spectrum)

  private
    E : (n : ℤ) → Ptd i
    E (pos m) = ⊙EM m
    E (negsucc m) = ⊙Lift ⊙Unit

    E-spectrum : (n : ℤ) → ⊙Ω (E (succ n)) ⊙≃ E n
    E-spectrum (pos n) = spectrum n
    E-spectrum (negsucc O) = ≃-to-⊙≃ {X = ⊙Ω (E 0)}
      (equiv (λ _ → _) (λ _ → idp)
             (λ _ → idp) (prop-has-all-paths _))
      idp
    E-spectrum (negsucc (S n)) = ≃-to-⊙≃ {X = ⊙Ω (E (negsucc n))}
      (equiv (λ _ → _) (λ _ → idp)
             (λ _ → idp) (prop-has-all-paths {{=-preserves-level ⟨⟩}} _))
      idp

  EM-Cohomology : CohomologyTheory i
  EM-Cohomology = spectrum-cohomology E E-spectrum

  open CohomologyTheory EM-Cohomology

  EM-dimension : {n : ℤ} → n ≠ 0 → is-trivialᴳ (C n (⊙Lift ⊙S⁰))
  EM-dimension {pos O} neq = ⊥-rec (neq idp)
  EM-dimension {pos (S n)} _ =
    contr-is-trivialᴳ (C (pos (S n)) (⊙Lift ⊙S⁰))
      {{connected-at-level-is-contr
        {{⟨⟩}}
        {{Trunc-preserves-conn $
           equiv-preserves-conn
            (pre⊙∘-equiv ⊙lower-equiv ∘e ⊙Bool→-equiv-idf _ ⁻¹)
            {{path-conn (connected-≤T (⟨⟩-monotone-≤ (≤-ap-S (O≤ n)))
                                     )}}}}}}
  EM-dimension {negsucc O} _ =
    contr-is-trivialᴳ (C (negsucc O) (⊙Lift ⊙S⁰))
      {{Trunc-preserves-level 0 (Σ-level (Π-level λ _ →
        inhab-prop-is-contr idp) ⟨⟩)}}

  EM-dimension {negsucc (S n)} _ =
    contr-is-trivialᴳ (C (negsucc (S n)) (⊙Lift ⊙S⁰))
      {{Trunc-preserves-level 0 (Σ-level (Π-level λ _ →
        =-preserves-level ⟨⟩) λ _ → =-preserves-level (=-preserves-level ⟨⟩))}}

  EM-Ordinary : OrdinaryTheory i
  EM-Ordinary = ordinary-theory EM-Cohomology EM-dimension


-- Martin-Löf identity type without the K axiom
-- (we do not assume uniqueness of identity proofs).

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

module Tools.PropositionalEquality where

-- We reexport Agda's builtin equality type.

open import Tools.Empty public
import Relation.Binary.PropositionalEquality as Eq
open Eq using (_≡_; _≢_; refl; sym; trans; cong; cong₂; subst; subst₂) public
open Eq.≡-Reasoning public

-- Non-dependent congruence rules.

cong₃ : ∀ {A B C D : Set} {a a′ b b′ c c′}
        (f : A → B → C → D) → a ≡ a′ → b ≡ b′ → c ≡ c′
      → f a b c ≡ f a′ b′ c′
cong₃ f refl refl refl = refl

cong₄ : ∀ {A B C D E : Set} {a a′ b b′ c c′ d d′}
        (f : A → B → C → D → E) → a ≡ a′ → b ≡ b′ → c ≡ c′ → d ≡ d′
      → f a b c d ≡ f a′ b′ c′ d′
cong₄ f refl refl refl refl = refl

-- Substitution (type-cast).

-- Three substitutions simultaneously.

subst₃ : ∀ {A B C : Set} {a a′ b b′ c c′} (F : A → B → C → Set)
       → a ≡ a′ → b ≡ b′ → c ≡ c′ → F a b c → F a′ b′ c′
subst₃ F refl refl refl f = f


{-# OPTIONS --cubical --safe #-}

module Data.Probability where

open import Prelude
import Data.Nat as ℕ
import Data.Nat.Properties as ℕ
open import Data.Bits renaming (Bits to ℚ⁺; [] to 1ℚ; 0∷_ to lℚ; 1∷_ to rℚ)
open import Data.Bits.Equatable

open import Data.Bits.Fold

euclidian : ℕ → ℕ → ℕ → ℚ⁺
euclidian n m zero    = 1ℚ
euclidian n m (suc s) =
  if n ℕ.≡ᴮ m
  then 1ℚ
  else if n ℕ.<ᴮ m
  then lℚ (euclidian n (m ℕ.∸ (1 ℕ.+ n)) s)
  else rℚ (euclidian (n ℕ.∸ (1 ℕ.+ m)) m s)

normalise-suc : ℕ → ℕ → ℚ⁺
normalise-suc n m = euclidian n m (n ℕ.+ m)

data ℙ : Type where
  ℙ⁰ : ℙ
  ℙ¹ : ℙ
  ℙ∙ : ℚ⁺ → ℙ

unEu : ℚ⁺ → ℙ
unEu 1ℚ = ℙ¹
unEu (lℚ x) = ℙ∙ x
unEu (rℚ x) = ℙ¹ -- won't happen

_/suc_ : ℕ → ℕ → ℙ
zero  /suc d = ℙ⁰
suc n /suc d = unEu (normalise-suc n d)

_/_ : ℕ → ℕ → ℙ
n / zero = ℙ⁰
n / suc d = n /suc d

zer : (ℕ × ℕ) → (ℕ × ℕ)
zer (p , q) = p , suc p ℕ.+ q

one : (ℕ × ℕ) → (ℕ × ℕ)
one (p , q) = suc p ℕ.+ q , q

norm : (ℕ × ℕ) → ℕ × ℕ
norm (x , y) = suc x , suc y

prob : ℙ → (ℕ × ℕ)
prob ℙ⁰ = 0 , 1
prob ℙ¹ = 1 , 1
prob (ℙ∙ x) = norm (zer (foldr-bits zer one (0 , 0) x))

mul″ : ℙ → ℙ → ℙ
mul″ x y =
  let xn , xd = prob x
      yn , yd = prob y
  in (xn ℕ.* yn) / (xd ℕ.* yd)

-- e = prob (5 /suc 22)

nextFrac : ℚ⁺ → ℚ⁺
nextFrac 1ℚ = lℚ 1ℚ
nextFrac (lℚ x) = rℚ x
nextFrac (rℚ x) = lℚ (nextFrac x)

open import Data.List

fracs : ℕ → List ℙ
fracs n = ℙ⁰ ∷ ℙ¹ ∷ go n 1ℚ
  where
  go : ℕ → ℚ⁺ → List ℙ
  go zero    x = []
  go (suc n) x = ℙ∙ x ∷ go n (nextFrac x)

open import Data.List.Sugar using (liftA2)

-- mul′ : ℚ⁺ → ℚ⁺ → ℚ⁺
-- mul′ 1ℚ 1ℚ = lℚ (lℚ 1ℚ)
-- mul′ 1ℚ (lℚ ys) = {!!}
-- mul′ 1ℚ (rℚ ys) = {!!}
-- mul′ (lℚ xs) 1ℚ = {!!}
-- mul′ (lℚ xs) (lℚ ys) = {!!}
-- mul′ (lℚ xs) (rℚ ys) = {!!}
-- mul′ (rℚ xs) 1ℚ = {!!}
-- mul′ (rℚ xs) (lℚ ys) = {!!}
-- mul′ (rℚ xs) (rℚ ys) = {!!}

-- mul : ℙ → ℙ → ℙ
-- mul ℙ⁰ ys = ℙ⁰
-- mul ℙ¹ ys = ys
-- mul (ℙ∙ x) ℙ⁰ = ℙ⁰
-- mul xs ℙ¹ = xs
-- mul (ℙ∙ x) (ℙ∙ y) = ℙ∙ (mul′ x y)

-- testMul : ℕ → Type
-- testMul n = liftA2 mul xs xs ≡ liftA2 mul″ xs xs
--   where
--     xs : List ℙ
--     xs = fracs n

-- _ : testMul 5
-- _ = refl


{- This file describes properties of computable relations. -}

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

module relations {ℓ ℓ' : level}{A : Set ℓ} (_≥A_ : A → A → Set ℓ') where

reflexive : Set (ℓ ⊔ ℓ')
reflexive = ∀ {a : A} → a ≥A a

transitive : Set (ℓ ⊔ ℓ')
transitive = ∀ {a b c : A} → a ≥A b → b ≥A c → a ≥A c

preorder : Set (ℓ ⊔ ℓ')
preorder = reflexive ∧ transitive

_iso_ : A → A → Set ℓ'
d iso d' = d ≥A d' ∧ d' ≥A d

iso-intro : ∀{x y : A} → x ≥A y → y ≥A x → x iso y
iso-intro p1 p2 = p1 , p2


module prime where

open import Data.Nat
open import Data.Nat.Properties
open import Data.Empty
open import Data.Unit using (⊤ ; tt)
open import Relation.Nullary
open import Relation.Binary.PropositionalEquality
open import Relation.Binary.Definitions

open import gcd
open import nat

record Prime (i : ℕ ) : Set where
   field
      p>1 : i > 1
      isPrime : ( j : ℕ ) → j < i → 0 < j → gcd i j ≡ 1

record NonPrime ( n : ℕ ) : Set where
   field
      factor : ℕ
      prime : Prime factor
      dividable : Dividable factor n

PrimeP : ( n : ℕ ) → Dec ( Prime n )
PrimeP 0 = no (λ p → ⊥-elim ( nat-<> (Prime.p>1 p) (s≤s z≤n)))
PrimeP 1 = no (λ p → ⊥-elim ( nat-≤> (Prime.p>1 p) (s≤s (≤-refl))))
PrimeP (suc (suc n)) = isPrime1 (suc (suc n)) (suc n) (s≤s (s≤s z≤n)) a<sa (λ i m<i i<n → isp0 (suc n) i (<to≤ m<i) i<n ) where
   isp0 : (n : ℕ) (i : ℕ) ( n<i : n ≤ i) ( i<n : i < suc n ) →  gcd (suc n) i ≡ 1
   isp0  n i n<i i<n with <-cmp i n
   ... | tri< a ¬b ¬c = ⊥-elim ( nat-≤> n<i a)
   ... | tri≈ ¬a refl ¬c = gcd203 i
   ... | tri> ¬a ¬b c = ⊥-elim ( nat-≤> c i<n )
   isPrime1 : ( n m : ℕ ) → n > 1 → m < n → ( (i : ℕ) → m < i → i < n  →  gcd n i ≡ 1 )  → Dec ( Prime n )
   isPrime1 n zero n>1 m<n lt = yes record { isPrime = λ j j<i 0<j → lt j 0<j j<i ; p>1 = n>1 }
   isPrime1 n (suc m) n>1 m<n lt with <-cmp (gcd n (suc m)) 1
   ... | tri< a ¬b ¬c = ⊥-elim ( nat-≤> ( gcd>0 n (suc m) (<-trans (s≤s z≤n) n>1) (s≤s z≤n)) a )
   ... | tri≈ ¬a b ¬c = isPrime1 n m n>1 (<-trans a<sa m<n) isp1 where
    --  lt : (i : ℕ) → suc m ≤ i → suc i ≤ n → gcd1 n n i i ≡ 1
        isp1 :  (i : ℕ) → m < i → i < n → gcd n i ≡ 1
        isp1 i m<i i<n with <-cmp (suc m) i
        ... | tri< a ¬b ¬c = lt i a i<n
        ... | tri≈ ¬a m=i  ¬c = subst (λ k → gcd n k ≡ 1) m=i b -- gcd n (suc m) ≡ 1 →  gcd n i ≡ 1
        ... | tri> ¬a ¬b c = ⊥-elim ( nat-≤> m<i c) -- suc i ≤ suc m →  i < m
   ... | tri> ¬a ¬b c = no ( λ p → nat-≡< (sym (Prime.isPrime p (suc m) m<n (s≤s z≤n) )) c )

open import logic
open _∧_

-- find prime factor

data Factoring (m : ℕ ) : (n : ℕ) → Set where
     findFactor : (n : ℕ) → m ≤ n → ( (j : ℕ ) → m ≤ j → j < n → gcd n j ≡ 1  ) → Factoring m n
     skipFactor : (n : ℕ) → n < m →  Factoring m n

nonPrime : { n : ℕ } → 1 < n → ¬ Prime n → NonPrime n
nonPrime {n} 1<n np = np1 n n np 1<n (findFactor n ≤-refl (λ j n≤j j<n → ⊥-elim (nat-≤>  n≤j j<n ) )) where
    mg1 :  (n m : ℕ )→ ( (j : ℕ ) → m < j → j < n → gcd n j ≡ 1  ) →  gcd n m ≡ 1 → (j : ℕ) → m ≤ j → j < n → gcd n j ≡ 1
    mg1 n m mg gcd j m<j j<n with <-cmp m j
    ... | tri< a ¬b ¬c = mg j a j<n
    ... | tri≈ ¬a refl ¬c = gcd
    ... | tri> ¬a ¬b c = ⊥-elim ( nat-≤> m<j c)
    np1 : ( n m : ℕ ) → ¬ Prime n → 1 < n → Factoring m n → NonPrime n
    np1 n zero np 1<n (findFactor n m≤n mg ) = ⊥-elim ( np record { isPrime = λ j lt _ → mg j z≤n lt ; p>1 = 1<n } ) -- zero < j , j < n
    np1 n (suc m) np 1<n (findFactor n m≤n mg) with <-cmp ( gcd n m ) 1
    ... | tri< a ¬b ¬c = ⊥-elim ( nat-≤> ( gcd>0 n m (<-trans (s≤s z≤n) 1<n) 0<m ) a ) where
         0<m : 0 < m
         0<m with <-cmp 0 m
         ... | tri< a ¬b ¬c = a
         ... | tri≈ ¬a refl ¬c = ⊥-elim ( nat-<> a (subst (λ k → 1 < k) (sym (gcd20 n)) 1<n ))
    ... | tri≈ ¬a b ¬c = np1 n m np 1<n (findFactor n (≤-trans refl-≤s m≤n) (mg1 n m mg b ) )
    ... | tri> ¬a ¬b c with PrimeP ( gcd n m )
    ... | yes y = record { factor = gcd n m ; prime = y ;  dividable = proj1 (gcd-dividable n m ) }
    ... | no ngcd = np2 where
         skip-case : NonPrime (gcd n m) → NonPrime n
         skip-case  cc = record { factor = NonPrime.factor cc ; prime = NonPrime.prime cc ; dividable =
                          record { factor = (Dividable.factor (proj1 (gcd-dividable n m))) * (Dividable.factor (NonPrime.dividable cc))
                              ; is-factor = begin
             Dividable.factor (proj1 (gcd-dividable n m)) * Dividable.factor (NonPrime.dividable cc) * NonPrime.factor cc + 0 ≡⟨ refl ⟩
             g * d * p + 0 ≡⟨ +-comm _ 0 ⟩
             g * d * p  ≡⟨ *-assoc g d p  ⟩
             g * (d * p)  ≡⟨ cong (λ k → g * k ) (+-comm 0 _) ⟩
             g * (d * p + 0)  ≡⟨ cong (λ k → g * k ) (Dividable.is-factor (NonPrime.dividable cc) ) ⟩
             g * gcd n m  ≡⟨ +-comm 0 _ ⟩
             g * gcd n m + 0 ≡⟨ Dividable.is-factor (proj1 (gcd-dividable n m)) ⟩
             n ∎  }} where
                open ≡-Reasoning
                g =  Dividable.factor (proj1 (gcd-dividable n m))
                d =  Dividable.factor (NonPrime.dividable cc)
                p = NonPrime.factor cc
         np2 :  NonPrime n
         np2 with <-cmp (gcd n m) m
         ... | tri< a ¬b ¬c = skip-case (np1 (gcd n m) m ngcd c (skipFactor (gcd n m) a ))
         ... | tri≈ ¬a b ¬c = skip-case (np1 (gcd n m) m ngcd c
              (subst (λ k → Factoring m k) (sym b) (findFactor m ≤-refl (λ j n≤j j<n → ⊥-elim (nat-≤>  n≤j j<n) ))))
         ... | tri> ¬a ¬b' c with <-cmp 0 m
         ... | tri< a ¬b ¬c = ⊥-elim ( nat-≤> (subst (λ k → k ≤ m) (gcdsym {m} {n}) (gcd-≤ a (<-trans a m≤n))) c )
         ... | tri≈ ¬a' b' ¬c = ⊥-elim ( np record { isPrime = λ j lt 0<j → mg j (subst (λ k → k < j) b' 0<j) lt ; p>1 = 1<n } ) -- suc m ≤ j
    np1 n zero np 1<n (skipFactor n ())
    np1 n (suc m) np 1<n  (skipFactor n n<m) with <-cmp m n
    ... | tri< a ¬b ¬c = ⊥-elim ( nat-≤> a n<m)
    ... | tri> ¬a ¬b c    = np1 n m np 1<n (skipFactor n c)
    ... | tri≈ ¬a refl ¬c = np1 n m np 1<n (findFactor m ≤-refl (λ j n≤j j<n → ⊥-elim (nat-≤>  n≤j j<n) ))

factorial : (n : ℕ) → ℕ
factorial zero = 1
factorial (suc n) = (suc n) * (factorial n)
factorial-mono : (n : ℕ) → factorial n ≤ factorial (suc n)
factorial-mono n = begin
     factorial n  ≤⟨ x≤x+y ⟩
     factorial n + n * factorial n ≡⟨ refl ⟩
     (suc n) * factorial n  ≡⟨ refl ⟩
     factorial (suc n)  ∎  where open ≤-Reasoning
factorial≥1 : {m : ℕ} → 1 ≤ factorial m
factorial≥1 {zero} = ≤-refl
factorial≥1 {suc m} = begin
     1 ≤⟨ s≤s z≤n ⟩
     (suc m) * 1 ≤⟨  *-monoʳ-≤ (suc m) (factorial≥1 {m}) ⟩
     (suc m) * factorial m ≡⟨ refl ⟩
     factorial (suc m)  ∎  where open ≤-Reasoning
m<factorial : (m : ℕ) → m ≤ factorial m
m<factorial zero = z≤n
m<factorial (suc m) = begin
     suc m ≡⟨ cong suc (+-comm 0 _) ⟩
     1 * suc m ≡⟨ *-comm 1 _ ⟩
     (suc m) * 1 ≤⟨  *-monoʳ-≤ (suc m) (factorial≥1 {m}) ⟩
     (suc m) * factorial m  ≡⟨ refl ⟩
     factorial (suc m)  ∎  where open ≤-Reasoning
-- *-monoˡ-≤ (suc m) {!!}
fact< : (m n : ℕ) → 0 < n → n < suc (suc m) → Dividable n ( factorial (suc m) )
fact< zero 1 0<n (s≤s (s≤s z≤n)) = record { factor = 1 ; is-factor = refl }
fact< (suc m) (suc zero) 0<n n<m = record { factor = factorial (suc (suc m)) ; is-factor = begin
     factorial (suc (suc m)) * 1 + 0  ≡⟨ +-comm _ 0 ⟩
     factorial (suc (suc m)) * 1   ≡⟨ m*1=m  ⟩
     (suc (suc m)) * factorial (suc m)  ≡⟨ refl ⟩
     factorial (suc (suc m))  ∎  } where open ≡-Reasoning
fact< (suc m) (suc (suc n)) 0<n n<m with <-cmp (suc (suc n)) (suc (suc m))
... | tri< a ¬b ¬c = record { factor = suc (suc m) * Dividable.factor fact1 ; is-factor = fact2 } where
      fact1 : Dividable (suc (suc n))  (factorial (suc m ))
      fact1 = fact< m (suc (suc n)) 0<n a
      d =  (fact< m (suc (suc n)) 0<n a)
      fact2 : suc (suc m) * Dividable.factor d * suc (suc n) + 0 ≡ factorial (suc (suc m))
      fact2 = begin
        suc (suc m) * Dividable.factor d * suc (suc n) + 0  ≡⟨ +-comm _ 0 ⟩
        suc (suc m) * Dividable.factor d * suc (suc n)   ≡⟨ *-assoc (suc (suc m)) (Dividable.factor d) ( suc (suc n)) ⟩
        suc (suc m) * (Dividable.factor d * suc (suc n))   ≡⟨ cong (λ k →  suc (suc m) * k ) ( +-comm 0 (Dividable.factor d * suc (suc n)) ) ⟩
        suc (suc m) * (Dividable.factor d * suc (suc n) + 0)   ≡⟨ cong (λ k → suc (suc m) * k ) (Dividable.is-factor d)  ⟩
        suc (suc m) * factorial (suc m)  ≡⟨ refl ⟩
        factorial (suc (suc m))  ∎   where open ≡-Reasoning
... | tri≈ ¬a b ¬c = record { factor = factorial (suc m)  ; is-factor = begin
     factorial (suc m) * suc (suc n) + 0 ≡⟨ +-comm _ 0 ⟩
     factorial (suc m) * suc (suc n)  ≡⟨ *-comm (factorial (suc m)) (suc (suc n))  ⟩
     (suc (suc n)) * factorial (suc m)  ≡⟨ cong (λ k → k * factorial (suc m) ) b ⟩
     (suc (suc m)) * factorial (suc m)  ≡⟨ refl ⟩
     factorial (suc (suc m))  ∎  } where open ≡-Reasoning
... | tri> ¬a ¬b c = ⊥-elim ( nat-≤> c n<m)

f>m :  {m : ℕ} → suc m < suc (factorial (suc m))
f>m {m} = begin
     suc (suc m)  ≡⟨ cong (λ k → 1 + suc k ) (+-comm _ m) ⟩
     suc (1 + 1 * m)  ≡⟨ cong (λ k → suc (1 + k )) (*-comm 1 m)  ⟩
     suc (1 + m * 1)  ≤⟨ s≤s (s≤s (*-monoʳ-≤ m  (factorial≥1 {m}) )) ⟩
     suc (1 + m * factorial m) ≤⟨ s≤s  (+-monoˡ-≤ _ (factorial≥1 {m})) ⟩
     suc (factorial m + m * factorial m)  ≡⟨ refl ⟩
     suc (factorial (suc m)) ∎  where open ≤-Reasoning

prime-is-infinite : (max-prime : ℕ ) → ¬ ( (j : ℕ) → max-prime < j → ¬ Prime j )
prime-is-infinite zero pmax = pmax 3 (s≤s z≤n) record { isPrime = λ n lt 0<j → pif3 n lt 0<j  ; p>1 = s≤s (s≤s z≤n) } where
  pif3 : (n : ℕ) →  n < 3 →  0 < n → gcd 3 n ≡ 1
  pif3 .1 (s≤s (s≤s z≤n)) _ = refl
  pif3 .2 (s≤s (s≤s (s≤s z≤n))) _ = refl
prime-is-infinite (suc m) pmax = newPrime where
  prime<max : (n : ℕ ) → Prime n → n < suc (suc m)
  prime<max n p with <-cmp n (suc m)
  ... | tri< a ¬b ¬c = ≤-trans a refl-≤s
  ... | tri≈ ¬a refl ¬c = ≤-refl
  ... | tri> ¬a ¬b c = ⊥-elim ( pmax n c p )
  fact : (n : ℕ) → Prime n → Dividable n ( factorial (suc m) )
  fact n p = fact< m n (<-trans (s≤s z≤n) (Prime.p>1 p)) ( prime<max n p )
  -- div+1 : { i k : ℕ } → k > 1 →  Dividable k i →  ¬ Dividable k (suc i)
  newPrime : ⊥
  newPrime with PrimeP ( suc (factorial (suc m)) )
  ... | yes p = pmax _ f>m p   -- yes, we found a prime not in list
  ... | no np = div+1 (Prime.p>1 (NonPrime.prime p1)) (fact (NonPrime.factor p1) (NonPrime.prime p1) ) (NonPrime.dividable p1) where
      -- n!+1 cannot be dividable, because n! is dividable
      -- the factor need not be a prime, but anyway we prove that there is a prime factor in NonPrime
      p1 : NonPrime  ( suc (factorial (suc m)) )
      p1 = nonPrime (begin
       2 ≤⟨ s≤s ( s≤s z≤n) ⟩
       suc (suc m) ≤⟨ s≤s (m<factorial (suc m))  ⟩
       suc (factorial (suc m)) ∎ ) np where open ≤-Reasoning


-- Andreas, 2011-10-02
module NotApplyingInDontCareTriggersInternalError where
import Common.Irrelevance

postulate
  Val : Set
  App : Val -> Val -> Val -> Set

Rel = Val -> Val -> Set

Transitive : Rel → Set
Transitive R = ∀ {t1 t2 t3} → R t1 t2 → R t2 t3 → R t1 t3

postulate
  LeftReflexive : Rel → Set
  RightReflexive : Rel → Set

record PP (R : Rel) : Set where
  constructor pp
  field
    .leftRefl  : LeftReflexive R
    .rightRefl : RightReflexive R
    .trans     : Transitive R
open PP public

record Those (P : Val → Set)(R : Rel)(P' : Val → Set) : Set where
  constructor those
  field
    B    : Val
    B'   : Val
    PB   : P B
    PB'  : P' B'
    RBB' : R B B'

Fam : Rel → Set1
Fam AA = ∀ {a a'} → .(AA a a') → Rel

FamTrans : {AA : Rel}.(TA : Transitive AA)(FF : Fam AA) → Set
FamTrans {AA = AA} TA FF = ∀ {a1 a2 a3}(a12 : AA a1 a2)(a23 : AA a2 a3) →
  ∀ {b1 b2 b3}(b12 : FF a12 b1 b2)(b23 : FF a23 b2 b3) →
  FF (TA a12 a23) b1 b3

Π : (AA : Rel) → Fam AA → Rel
Π AA FF g g' = ∀ {a a'} → .(a≼a' : AA a a') →
  Those (App g a) (FF a≼a') (App g' a')

ΠTrans : {AA : Rel}(PA : PP AA){FF : Fam AA}(TF : FamTrans {AA = AA} (trans PA) FF) →
  Transitive (Π AA FF)
ΠTrans (pp leftRefl rightRefl trans) TF f12 f23 a≼a' with (leftRefl a≼a')
... | a≼a with f12 a≼a | f23 a≼a'
ΠTrans (pp leftRefl rightRefl trans) TF f12 f23 a≼a' | a≼a | those b1 b2 app1 app2 b1≼b2 | those b2' b3 app2' app3 b2'≼b3 = those b1 b3 app1 app3 ?

-- This should not give the internal error:
--
--   An internal error has occurred. Please report this as a bug.
--   Location of the error: src/full/Agda/TypeChecking/Substitute.hs:50
--
-- Instead it should complain that
--
--   Variable leftRefl is declared irrelevant, so it cannot be used here
--   when checking that the expression leftRefl a≼a' has type
--   _141 leftRefl rightRefl trans TF f12 f23 a≼a'


module _ where

open import Prelude
open import Numeric.Nat.Prime

2^44+7 2^46+15 2^48+21 : Nat
2^44+7  =  17592186044423
2^46+15 =  70368744177679
2^48+21 = 281474976710677

main : IO ⊤
main = print $ isPrime! 2^48+21


------------------------------------------------------------------------
-- This module proves that the recognisers correspond exactly to
-- decidable predicates of type List Bool → Bool (when the alphabet is
-- Bool)
------------------------------------------------------------------------

-- This result could be generalised to other finite alphabets.

module TotalRecognisers.Simple.ExpressiveStrength where

open import Codata.Musical.Notation
open import Data.Bool
open import Data.Bool.Properties
open import Function.Base
open import Function.Equality using (_⟨$⟩_)
open import Function.Equivalence
  using (_⇔_; equivalence; module Equivalence)
open import Data.List
open import Data.Product
open import Relation.Binary.PropositionalEquality
open import Relation.Nullary.Decidable

import TotalRecognisers.Simple
open TotalRecognisers.Simple Bool _≟_

-- One direction of the correspondence has already been established:
-- For every grammar there is an equivalent decidable predicate.

grammar⇒pred : ∀ {n} (p : P n) →
               ∃ λ (f : List Bool → Bool) → ∀ s → s ∈ p ⇔ T (f s)
grammar⇒pred p =
  ((λ s → ⌊ s ∈? p ⌋) , λ _ → equivalence fromWitness toWitness)

-- For every decidable predicate there is a corresponding grammar.
-- Note that these grammars are all "infinite LL(1)".

pred⇒grammar : (f : List Bool → Bool) →
               ∃ λ (p : P (f [])) → ∀ s → s ∈ p ⇔ T (f s)
pred⇒grammar f =
  (grammar f , λ s → equivalence (sound f) (complete f s))
  where
  accept-if-true : ∀ b → P b
  accept-if-true true  = empty
  accept-if-true false = fail

  grammar : (f : List Bool → Bool) → P (f [])
  grammar f = tok true  · ♯ grammar (f ∘ _∷_ true )
            ∣ tok false · ♯ grammar (f ∘ _∷_ false)
            ∣ accept-if-true (f [])

  accept-if-true-sound :
    ∀ b {s} → s ∈ accept-if-true b → s ≡ [] × T b
  accept-if-true-sound true  empty = (refl , _)
  accept-if-true-sound false ()

  accept-if-true-complete : ∀ {b} → T b → [] ∈ accept-if-true b
  accept-if-true-complete ok with Equivalence.to T-≡ ⟨$⟩ ok
  ... | refl = empty

  sound : ∀ f {s} → s ∈ grammar f → T (f s)
  sound f (∣-right s∈) with accept-if-true-sound (f []) s∈
  ... | (refl , ok) = ok
  sound f (∣-left (∣-left  (tok · s∈))) = sound (f ∘ _∷_ true ) s∈
  sound f (∣-left (∣-right (tok · s∈))) = sound (f ∘ _∷_ false) s∈

  complete : ∀ f s → T (f s) → s ∈ grammar f
  complete f [] ok =
    ∣-right {n₁ = false} $ accept-if-true-complete ok
  complete f (true ∷ bs) ok =
    ∣-left {n₁ = false} $ ∣-left {n₁ = false} $
      tok · complete (f ∘ _∷_ true ) bs ok
  complete f (false ∷ bs) ok =
    ∣-left {n₁ = false} $ ∣-right {n₁ = false} $
      tok · complete (f ∘ _∷_ false) bs ok


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

open import Level

open import Relation.Binary using (Rel; IsEquivalence)
open import Data.Sum
open import Data.Product
open import Function using (flip)

open import Categories.Category
open import Categories.Functor hiding (equiv; assoc; identityˡ; identityʳ; ∘-resp-≡) renaming (id to idF; _≡_ to _≡F_; _∘_ to _∘F_)
open import Categories.NaturalTransformation hiding (equiv; setoid) renaming (id to idT; _≡_ to _≡T_)
open import Categories.Monad
open import Categories.Support.Equivalence

open import Categories.Adjunction
open import Categories.Adjunction.Composition

.assoc : ∀ {o₀ ℓ₀ e₀ o₁ ℓ₁ e₁ o₂ ℓ₂ e₂ o₃ ℓ₃ e₃}
           {C₀ : Category o₀ ℓ₀ e₀} {C₁ : Category o₁ ℓ₁ e₁} {C₂ : Category o₂ ℓ₂ e₂} {C₃ : Category o₃ ℓ₃ e₃}
           {F : Functor C₀ C₁} {G : Functor C₁ C₂} {H : Functor C₂ C₃}
           {F′ : Functor C₁ C₀} {G′ : Functor C₂ C₁} {H′ : Functor C₃ C₂}
           {T : F ⊣ F′} {U : G ⊣ G′} {V : H ⊣ H′}
       → (V ∘ U) ∘ T ≡ V ∘ (U ∘ T)
assoc {C₀ = C₀} {C₁} {C₂} {C₃} {F} {G} {H} {F′} {G′} {H′} {T} {U} {V} = (λ {x} → pf₀ {x}) , λ {x} → pf₁ {x}
  where
    module C₀ = Category C₀
    module C₁ = Category C₁
    module C₂ = Category C₂
    module C₃ = Category C₃
    module F = Functor F
    module G = Functor G renaming (F₀ to G₀; F₁ to G₁; F-resp-≡ to G-resp-≡)
    module H = Functor H renaming (F₀ to H₀; F₁ to H₁; F-resp-≡ to H-resp-≡)
    module F′ = Functor F′ renaming (F₀ to F′₀; F₁ to F′₁; F-resp-≡ to F′-resp-≡)
    module G′ = Functor G′ renaming (F₀ to G′₀; F₁ to G′₁; F-resp-≡ to G′-resp-≡)
    module H′ = Functor H′ renaming (F₀ to H′₀; F₁ to H′₁; F-resp-≡ to H′-resp-≡)
    module T = Adjunction T renaming (unit to Tη′; counit to Tε′)
    module U = Adjunction U renaming (unit to Uη′; counit to Uε′)
    module V = Adjunction V renaming (unit to Vη′; counit to Vε′)
    module Tη = NaturalTransformation (Adjunction.unit T)
    module Tε = NaturalTransformation (Adjunction.counit T)
    module Uη = NaturalTransformation (Adjunction.unit U)
    module Uε = NaturalTransformation (Adjunction.counit U)
    module Vη = NaturalTransformation (Adjunction.unit V)
    module Vε = NaturalTransformation (Adjunction.counit V)

    pf₀ : {x : C₀.Obj} → F′.F′₁ (G′.G′₁ (Vη.η (G.G₀ (F.F₀ x))) C₁.∘ Uη.η (F.F₀ x)) C₀.∘
                           Tη.η x
                           C₀.≡
                           F′.F′₁ (G′.G′₁ (Vη.η (G.G₀ (F.F₀ x)))) C₀.∘
                           F′.F′₁ (Uη.η (F.F₀ x)) C₀.∘ Tη.η x
    pf₀ {x} = begin
               F′.F′₁ (G′.G′₁ (Vη.η (G.G₀ (F.F₀ x))) C₁.∘ Uη.η (F.F₀ x)) C₀.∘
                 Tη.η x
             ↓⟨ C₀.∘-resp-≡ˡ F′.homomorphism ⟩
               C₀ [ F′.F′₁ (G′.G′₁ (Vη.η (G.G₀ (F.F₀ x)))) ∘
                 F′.F′₁ (Uη.η (F.F₀ x)) ]
                 C₀.∘ Tη.η x
             ↓⟨ C₀.assoc ⟩
               F′.F′₁ (G′.G′₁ (Vη.η (G.G₀ (F.F₀ x)))) C₀.∘
                 F′.F′₁ (Uη.η (F.F₀ x)) C₀.∘ Tη.η x
             ∎
        where open C₀.HomReasoning

    pf₁ : {x : C₃.Obj} → (Vε.η x C₃.∘ H.H₁ (Uε.η (H′.H′₀ x))) C₃.∘
                               H.H₁ (G.G₁ (Tε.η (G′.G′₀ (H′.H′₀ x))))
                         C₃.≡
                         Vε.η x C₃.∘
                           H.H₁ (Uε.η (H′.H′₀ x) C₂.∘ G.G₁ (Tε.η (G′.G′₀ (H′.H′₀ x))))
    pf₁ {x} = begin
               (Vε.η x C₃.∘ H.H₁ (Uε.η (H′.H′₀ x))) C₃.∘
                 H.H₁ (G.G₁ (Tε.η (G′.G′₀ (H′.H′₀ x))))
             ↓⟨ C₃.assoc ⟩
               Vε.η x C₃.∘
                 H.H₁ (Uε.η (H′.H′₀ x)) C₃.∘ H.H₁ (G.G₁ (Tε.η (G′.G′₀ (H′.H′₀ x))))
             ↑⟨ C₃.∘-resp-≡ʳ H.homomorphism ⟩
               Vε.η x C₃.∘
                 H.H₁ (C₂ [ Uε.η (H′.H′₀ x) ∘ G.G₁ (Tε.η (G′.G′₀ (H′.H′₀ x))) ])
             ∎
      where open C₃.HomReasoning

.identityˡ : ∀ {o ℓ e} {o′ ℓ′ e′} {C : Category o ℓ e} {D : Category o′ ℓ′ e′} {F : Functor C D} {G : Functor D C} {T : F ⊣ G}
             → id ∘ T ≡ T
identityˡ {C = C} {D} {F} {G} {T} = ( (λ {x} → pf₀ {x}) , D.identityˡ )
  where
    module C = Category C
    module D = Category D
    module F = Functor F
    module G = Functor G
    module T = Adjunction T renaming (unit to Tη′; counit to Tε′)
    module Tη = NaturalTransformation T.Tη′
    module Tε = NaturalTransformation T.Tε′

    pf₀ : {x : C.Obj} → G.F₁ D.id C.∘ NaturalTransformation.η Tη.op x C.≡
                             NaturalTransformation.η Tη.op x
    pf₀ {x} = begin
               G.F₁ D.id C.∘ NaturalTransformation.η Tη.op x
             ↓⟨ C.∘-resp-≡ˡ G.identity ⟩
               C.id C.∘ NaturalTransformation.η Tη.op x
             ↓⟨ C.identityˡ ⟩
               NaturalTransformation.η Tη.op x
             ∎
      where open C.HomReasoning

.identityʳ : ∀ {o ℓ e} {o′ ℓ′ e′} {C : Category o ℓ e} {D : Category o′ ℓ′ e′} {F : Functor C D} {G : Functor D C} {T : F ⊣ G}
             → T ∘ id ≡ T
identityʳ {C = C} {D} {F} {G} {T} = (λ {x} → C.identityʳ) , (λ {x} → pf₀ {x})
  where
    module C = Category C
    module D = Category D
    module F = Functor F
    module G = Functor G
    module T = Adjunction T renaming (unit to Tη′; counit to Tε′)
    module Tη = NaturalTransformation T.Tη′
    module Tε = NaturalTransformation T.Tε′

    pf₀ : {x : D.Obj} → NaturalTransformation.η Tε.op x D.∘ F.F₁ C.id D.≡
                        NaturalTransformation.η Tε.op x
    pf₀ {x} = begin
               NaturalTransformation.η Tε.op x D.∘ F.F₁ C.id
             ↓⟨ D.∘-resp-≡ʳ F.identity ⟩
               NaturalTransformation.η Tε.op x D.∘ D.id
             ↓⟨ D.identityʳ ⟩
               NaturalTransformation.η Tε.op x
             ∎
      where open D.HomReasoning

.∘-resp-≡  : ∀ {o₀ ℓ₀ e₀ o₁ ℓ₁ e₁ o₂ ℓ₂ e₂}
               {A : Category o₀ ℓ₀ e₀} {B : Category o₁ ℓ₁ e₁} {C : Category o₂ ℓ₂ e₂}
               {F : Functor B C} {G : Functor A B} {F′ : Functor C B} {G′ : Functor B A}
               {T T′ : G ⊣ G′} {U U′ : F ⊣ F′}
           → T ≡ T′ → U ≡ U′ → U ∘ T ≡ U′ ∘ T′
∘-resp-≡ {A = A} {B} {C} {F} {G} {F′} {G′} {T} {T′} {U} {U′} (Tη≡T′η , Tε≡T′ε) (Uη≡U′η , Uε≡U′ε) =
         (λ {x} → A.∘-resp-≡ (G′.F-resp-≡ Uη≡U′η) Tη≡T′η)
         ,
         (λ {x} → C.∘-resp-≡ Uε≡U′ε (F.F-resp-≡ Tε≡T′ε))
  where
    module A = Category A
    module B = Category B
    module C = Category C
    module F = Functor F
    module G = Functor G
    module F′ = Functor F′
    module G′ = Functor G′
    module T = Adjunction T renaming (unit to Tη′; counit to Tε′)
    module U = Adjunction U renaming (unit to Uη′; counit to Uε′)
    module T′ = Adjunction T′ renaming (unit to T′η′; counit to T′ε′)
    module U′ = Adjunction U′ renaming (unit to U′η′; counit to U′ε′)
    module Tη = NaturalTransformation (Adjunction.unit T)
    module Tε = NaturalTransformation (Adjunction.counit T)
    module Uη = NaturalTransformation (Adjunction.unit U)
    module Uε = NaturalTransformation (Adjunction.counit U)
    module T′η = NaturalTransformation (Adjunction.unit T′)
    module T′ε = NaturalTransformation (Adjunction.counit T′)
    module U′η = NaturalTransformation (Adjunction.unit U′)
    module U′ε = NaturalTransformation (Adjunction.counit U′)


module STLC.Semantics where

-- This file contains the definitional interpreter for STLC described
-- in Section 2 of the paper.

open import Data.Nat
open import Data.Integer
open import Data.List
open import Data.List.Membership.Propositional
open import Data.List.Relation.Unary.All as All
open import Data.Maybe.Base hiding (_>>=_)

------------
-- SYNTAX --
------------

-- These definitions correspond to Section 2.1, except we have
-- included numbers (integers) and integer operations in the language.

data Ty : Set where
  unit : Ty
  _⇒_  : (a b : Ty) → Ty
  int  : Ty

Ctx = List Ty

-- Below, `Expr` uses `_∈_` from `Relation.Binary` in the Agda
-- Standard Library to represent typed de Bruijn indices which witness
-- the existence of an entry in the type context.

data Expr (Γ : Ctx) : Ty → Set where
  unit  : Expr Γ unit
  var   : ∀ {t} → t ∈ Γ → Expr Γ t
  ƛ     : ∀ {a b} → Expr (a ∷ Γ) b → Expr Γ (a ⇒ b)
  _·_   : ∀ {a b} → Expr Γ (a ⇒ b) → Expr Γ a → Expr Γ b
  num   : ℤ → Expr Γ int
  iop   : (ℤ → ℤ → ℤ) → (l r : Expr Γ int) → Expr Γ int

-----------------------------
-- VALUES AND ENVIRONMENTS --
-----------------------------

-- These definitions correspond to Section 2.2.
--
-- Note that the `All` type described in Section 2.2 of the paper (for
-- simplicity) does not mention universe levels, whereas the `All`
-- definition below refers to `Data.List.All` in the Agda Standard
-- Library which is defined in a universe polymorphic manner.

mutual
  data Val : Ty → Set where
    unit  : Val unit
    ⟨_,_⟩ : ∀ {Γ a b} → Expr (a ∷ Γ) b → Env Γ → Val (a ⇒ b)
    num   : ℤ → Val int

  Env : Ctx → Set
  Env Γ = All Val Γ

-- The `lookup` function described in Section 2.2 of the paper is also
-- defined in `Data.List.All` in the Agda Standard Library.

-----------
-- MONAD --
-----------

-- These definitions correspond to Section 2.3.

M : Ctx → Set → Set
M Γ a  =  Env Γ → Maybe a

_>>=_    : ∀ {Γ a b} → M Γ a → (a → M Γ b) → M Γ b
(f >>= c) E with (f E)
... | just x = c x E
... | nothing = nothing

return   : ∀ {Γ a} → a → M Γ a
return x E = just x

getEnv   : ∀ {Γ} → M Γ (Env Γ)
getEnv E = return E E

usingEnv : ∀ {Γ Γ' a} → Env Γ → M Γ a → M Γ' a
usingEnv E f _ = f E

timeout  : ∀ {Γ a} → M Γ a
timeout = λ _ → nothing

-----------------
-- INTERPRETER --
-----------------

-- These definitions correspond to Section 2.4.

eval : ℕ → ∀ {Γ t} → Expr Γ t → M Γ (Val t)
eval zero     _           =
  timeout
eval (suc k)  unit        =
  return unit
eval (suc k)  (var x)     =
  getEnv >>= λ E →
  return (All.lookup E x)
eval (suc k)  (ƛ b)       =
  getEnv >>= λ E →
  return ⟨ b , E ⟩
eval (suc k)  (l · r)     =
  eval k l >>= λ{ ⟨ e , E ⟩ →
  eval k r >>= λ v →
  usingEnv (v ∷ E) (eval k e) }
eval (suc k)  (num x)     =
  return (num x)
eval (suc k)  (iop f l r) =
  eval k l >>= λ{ (num vₗ) →
  eval k r >>= λ{ (num vᵣ) →
  return (num (f vₗ vᵣ)) }}


{-# OPTIONS --safe --experimental-lossy-unification #-}
module Cubical.Algebra.Polynomials.UnivariateList.Polyn-nPoly where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Equiv

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

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

open import Cubical.Algebra.CommRing.Instances.Polynomials.UnivariatePolyList
open import Cubical.Algebra.CommRing.Instances.Polynomials.MultivariatePoly

open import Cubical.Algebra.Polynomials.Multivariate.EquivCarac.Poly0-A
open import Cubical.Algebra.Polynomials.Multivariate.EquivCarac.An[Am[X]]-Anm[X]
open import Cubical.Algebra.Polynomials.Multivariate.EquivCarac.AB-An[X]Bn[X]
open import Cubical.Algebra.Polynomials.UnivariateList.Poly1-1Poly

open CommRingEquivs renaming (compCommRingEquiv to _∘-ecr_ ; invCommRingEquiv to inv-ecr)

private variable
  ℓ : Level

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

Equiv-Polyn-nPoly : (A' : CommRing ℓ) → (n : ℕ) → CommRingEquiv (PolyCommRing A' n) (nUnivariatePolyList A' n)
Equiv-Polyn-nPoly A' zero = CRE-Poly0-A A'
Equiv-Polyn-nPoly A' (suc n) =       inv-ecr _ _ (CRE-PolyN∘M-PolyN+M A' 1 n)
                               ∘-ecr (lift-equiv-poly _ _ 1 (Equiv-Polyn-nPoly A' n)
                               ∘-ecr CRE-Poly1-Poly: (nUnivariatePolyList A' n))


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

module Data.Sum where

open import Level
open import Data.Bool.Base using (Bool; true; false)
open import Function using (const)

data _⊎_ (A : Type a) (B : Type b) : Type (a ℓ⊔ b) where
  inl : A → A ⊎ B
  inr : B → A ⊎ B

either : ∀ {ℓ} {C : A ⊎ B → Type ℓ} →  ((a : A) → C (inl a)) → ((b : B) → C (inr b))
        → (x : A ⊎ B) → C x
either f _ (inl x) = f x
either _ g (inr y) = g y

⟦l_,r_⟧ = either

either′ : (A → C) → (B → C) → (A ⊎ B) → C
either′ = either

_▿_ : (A → C) → (B → C) → A ⊎ B → C
_▿_ = either

is-l : A ⊎ B → Bool
is-l = either′ (const true) (const false)

map-⊎ : ∀ {a₁ a₂ b₁ b₂} {A₁ : Type a₁} {A₂ : Type a₂} {B₁ : Type b₁} {B₂ : Type b₂} →
      (A₁ → A₂) →
      (B₁ → B₂) →
      (A₁ ⊎ B₁) →
      (A₂ ⊎ B₂)
map-⊎ f g (inl x) = inl (f x)
map-⊎ f g (inr x) = inr (g x)

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

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


------------------------------------------------------------------------------
-- Natural numbers (PCF version)
------------------------------------------------------------------------------

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

module LTC-PCF.Data.Nat where

open import LTC-PCF.Base
open import LTC-PCF.Data.Nat.Rec
open import LTC-PCF.Data.Nat.Type public

infixl 7 _*_
infixl 6 _+_ _∸_

------------------------------------------------------------------------------
-- Addition with recursion on the first argument.
_+_ : D → D → D
m + n = rec m n (lam (λ _ → lam succ₁))

-- Subtraction with recursion on the second argument.
_∸_ : D → D → D
m ∸ n = rec n m (lam (λ _ → lam pred₁))

-- Multiplication with recursion on the first argument.
_*_ : D → D → D
m * n = rec m zero (lam (λ _ → lam (λ x → n + x)))


{-# OPTIONS --safe #-}
module Cubical.Algebra.CommAlgebra.FGIdeal where
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Structure
open import Cubical.Foundations.Powerset

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

open import Cubical.Algebra.CommRing
open import Cubical.Algebra.CommRing.FGIdeal using ()
            renaming (generatedIdeal to generatedIdealCommRing;
                      indInIdeal to ringIncInIdeal;
                      0FGIdeal to 0FGIdealCommRing)
open import Cubical.Algebra.CommAlgebra
open import Cubical.Algebra.CommAlgebra.Ideal

private
  variable
    ℓ : Level
    R : CommRing ℓ

generatedIdeal : {n : ℕ} (A : CommAlgebra R ℓ) → FinVec (fst A) n → IdealsIn A
generatedIdeal A = generatedIdealCommRing (CommAlgebra→CommRing A)

incInIdeal :   {n : ℕ} (A : CommAlgebra R ℓ)
              (U : FinVec ⟨ A ⟩ n) (i : Fin n) → U i ∈ fst (generatedIdeal A U)
incInIdeal A = ringIncInIdeal (CommAlgebra→CommRing A)

syntax generatedIdeal A V = ⟨ V ⟩[ A ]

module _ {R : CommRing ℓ} (A : CommAlgebra R ℓ) where
  open CommAlgebraStr (snd A)

  0FGIdeal : {n : ℕ} → ⟨ replicateFinVec n 0a ⟩[ A ] ≡ (0Ideal A)
  0FGIdeal = 0FGIdealCommRing (CommAlgebra→CommRing A)


module Data.Boolean where

import Lvl
open import Type

-- Boolean type
data Bool : Type{Lvl.𝟎} where
  𝑇 : Bool -- Represents truth
  𝐹 : Bool -- Represents falsity

{-# BUILTIN BOOL  Bool #-}
{-# BUILTIN TRUE  𝑇 #-}
{-# BUILTIN FALSE 𝐹 #-}

elim : ∀{ℓ}{T : Bool → Type{ℓ}} → T(𝑇) → T(𝐹) → ((b : Bool) → T(b))
elim t _ 𝑇 = t
elim _ f 𝐹 = f

not : Bool → Bool
not 𝑇 = 𝐹
not 𝐹 = 𝑇
{-# COMPILE GHC not = not #-}

-- Control-flow if-else expression
if_then_else_ : ∀{ℓ}{T : Type{ℓ}} → Bool → T → T → T
if b then t else f = elim t f b


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

module PiTracedLevel0 where

open import Data.Empty   using (⊥; ⊥-elim)
open import Data.Unit    using (⊤; tt)
open import Data.Sum     using (_⊎_; inj₁; inj₂)
open import Data.Product using (_×_; _,_; proj₁; proj₂)

------------------------------------------------------------------------------
-- Level 0 of Pi (with trace)
--
-- ZERO is a type with no elements
-- ONE is a type with one element 'tt'
-- PLUS ONE ONE is a type with elements 'false' and 'true'
-- and so on for all finite types built from ZERO, ONE, PLUS, and TIMES
--
-- We also have that U is a type with elements ZERO, ONE, PLUS ONE ONE,
--   TIMES BOOL BOOL, etc.

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

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

infix  30 _⟷_
infixr 50 _◎_

-- Combinators, permutations, or paths depending on the perspective

data _⟷_ : U → U → Set where
  unite₊  : {t : U} → PLUS ZERO t ⟷ t
  uniti₊  : {t : U} → t ⟷ PLUS ZERO t
  swap₊   : {t₁ t₂ : U} → PLUS t₁ t₂ ⟷ PLUS t₂ t₁
  assocl₊ : {t₁ t₂ t₃ : U} → PLUS t₁ (PLUS t₂ t₃) ⟷ PLUS (PLUS t₁ t₂) t₃
  assocr₊ : {t₁ t₂ t₃ : U} → PLUS (PLUS t₁ t₂) t₃ ⟷ PLUS t₁ (PLUS t₂ t₃)
  unite⋆  : {t : U} → TIMES ONE t ⟷ t
  uniti⋆  : {t : U} → t ⟷ TIMES ONE t
  swap⋆   : {t₁ t₂ : U} → TIMES t₁ t₂ ⟷ TIMES t₂ t₁
  assocl⋆ : {t₁ t₂ t₃ : U} → TIMES t₁ (TIMES t₂ t₃) ⟷ TIMES (TIMES t₁ t₂) t₃
  assocr⋆ : {t₁ t₂ t₃ : U} → TIMES (TIMES t₁ t₂) t₃ ⟷ TIMES t₁ (TIMES t₂ t₃)
  absorbr  : {t : U} → TIMES ZERO t ⟷ ZERO
  absorbl : {t : U} → TIMES t ZERO ⟷ ZERO
  factorzr : {t : U} → ZERO ⟷ TIMES t ZERO
  factorzl : {t : U} → ZERO ⟷ TIMES ZERO t
  dist    : {t₁ t₂ t₃ : U} →
            TIMES (PLUS t₁ t₂) t₃ ⟷ PLUS (TIMES t₁ t₃) (TIMES t₂ t₃)
  factor  : {t₁ t₂ t₃ : U} →
            PLUS (TIMES t₁ t₃) (TIMES t₂ t₃) ⟷ TIMES (PLUS t₁ t₂) t₃
  id⟷    : {t : U} → t ⟷ t
  _◎_     : {t₁ t₂ t₃ : U} → (t₁ ⟷ t₂) → (t₂ ⟷ t₃) → (t₁ ⟷ t₃)
  _⊕_     : {t₁ t₂ t₃ t₄ : U} →
            (t₁ ⟷ t₃) → (t₂ ⟷ t₄) → (PLUS t₁ t₂ ⟷ PLUS t₃ t₄)
  _⊗_     : {t₁ t₂ t₃ t₄ : U} →
            (t₁ ⟷ t₃) → (t₂ ⟷ t₄) → (TIMES t₁ t₂ ⟷ TIMES t₃ t₄)
  trace   : {t t₁ t₂ : U} → (PLUS t₁ t ⟷ PLUS t₂ t) → (t₁ ⟷ t₂)

! : {t₁ t₂ : U} → (t₁ ⟷ t₂) → (t₂ ⟷ t₁)
! unite₊    = uniti₊
! uniti₊    = unite₊
! swap₊     = swap₊
! assocl₊   = assocr₊
! assocr₊   = assocl₊
! unite⋆    = uniti⋆
! uniti⋆    = unite⋆
! swap⋆     = swap⋆
! assocl⋆   = assocr⋆
! assocr⋆   = assocl⋆
! absorbl     = factorzr
! absorbr     = factorzl
! factorzl  = absorbr
! factorzr = absorbl
! dist      = factor
! factor    = dist
! id⟷      = id⟷
! (c₁ ◎ c₂) = ! c₂ ◎ ! c₁
! (c₁ ⊕ c₂) = (! c₁) ⊕ (! c₂)
! (c₁ ⊗ c₂) = (! c₁) ⊗ (! c₂)
! (trace c) = trace (! c)

------------------------------------------------------------------------------
-- Cat

open import Level using () renaming (zero to lzero)
open import Data.Unit
open import Relation.Binary.Core using (IsEquivalence)
open import Data.Product using (_,_)

open import Categories.Category
open import Categories.Groupoid
open import Categories.Monoidal
open import Categories.Monoidal.Helpers
open import Categories.Bifunctor
open import Categories.NaturalIsomorphism
open import Categories.Monoidal.Braided
open import Categories.Monoidal.Symmetric
open import Categories.RigCategory
open import Categories.Monoidal.Traced

triv≡ : {t₁ t₂ : U} → (f g : t₁ ⟷ t₂) → Set
triv≡ _ _ = ⊤

triv≡Equiv : {t₁ t₂ : U} → IsEquivalence (triv≡ {t₁} {t₂})
triv≡Equiv = record
  { refl = tt
  ; sym = λ _ → tt
  ; trans = λ _ _ → tt
  }

PiCat : Category lzero lzero lzero
PiCat = record
  { Obj = U
  ; _⇒_ = _⟷_
  ; _≡_ = triv≡
  ; id = id⟷
  ; _∘_ = λ y⟷z x⟷y → x⟷y ◎ y⟷z
  ; assoc = tt
  ; identityˡ = tt
  ; identityʳ = tt
  ; equiv = triv≡Equiv
  ; ∘-resp-≡ = λ _ _ → tt
  }

-- and a groupoid

PiGroupoid : Groupoid PiCat
PiGroupoid = record
  { _⁻¹ = !
  ; iso = record { isoˡ = tt ; isoʳ = tt }
  }

-- additive bifunctor and monoidal structure
⊕-bifunctor : Bifunctor PiCat PiCat PiCat
⊕-bifunctor = record
  { F₀ = λ {(u , v) → PLUS u v}
  ; F₁ = λ {(x⟷y , z⟷w) → x⟷y ⊕ z⟷w }
  ; identity = tt
  ; homomorphism = tt
  ; F-resp-≡ = λ _ → tt
  }

module ⊎h = MonoidalHelperFunctors PiCat ⊕-bifunctor ZERO

0⊕x≡x : NaturalIsomorphism ⊎h.id⊗x ⊎h.x
0⊕x≡x = record
  { F⇒G = record
    { η = λ X → unite₊
    ; commute = λ _ → tt }
  ; F⇐G = record
    { η = λ X → uniti₊
    ; commute = λ _ → tt }
  ; iso = λ X → record { isoˡ = tt; isoʳ = tt }
  }

x⊕0≡x : NaturalIsomorphism ⊎h.x⊗id ⊎h.x
x⊕0≡x = record
  { F⇒G = record
    { η = λ X → swap₊ ◎ unite₊  -- !!!
    ; commute = λ _ → tt
    }
  ; F⇐G = record
    { η = λ X → uniti₊ ◎ swap₊
    ; commute = λ _ → tt
    }
  ; iso = λ X → record
    { isoˡ = tt
    ; isoʳ = tt
    }
  }

[x⊕y]⊕z≡x⊕[y⊕z] : NaturalIsomorphism ⊎h.[x⊗y]⊗z ⊎h.x⊗[y⊗z]
[x⊕y]⊕z≡x⊕[y⊕z] = record
  { F⇒G = record
    { η = λ X → assocr₊
    ; commute = λ f → tt
    }
  ; F⇐G = record
    { η = λ X → assocl₊
    ; commute = λ _ → tt
    }
  ; iso = λ X → record
    { isoˡ = tt
    ; isoʳ = tt
    }
  }

M⊕ : Monoidal PiCat
M⊕ = record
  { ⊗ = ⊕-bifunctor
  ; id = ZERO
  ; identityˡ = 0⊕x≡x
  ; identityʳ = x⊕0≡x
  ; assoc = [x⊕y]⊕z≡x⊕[y⊕z]
  ; triangle = tt
  ; pentagon = tt
  }

-- multiplicative bifunctor and monoidal structure
⊗-bifunctor : Bifunctor PiCat PiCat PiCat
⊗-bifunctor =  record
  { F₀ = λ {(u , v) → TIMES u v}
  ; F₁ = λ {(x⟷y , z⟷w) → x⟷y ⊗ z⟷w }
  ; identity = tt
  ; homomorphism = tt
  ; F-resp-≡ = λ _ → tt
  }

module ⊗h = MonoidalHelperFunctors PiCat ⊗-bifunctor ONE

1⊗x≡x : NaturalIsomorphism ⊗h.id⊗x ⊗h.x
1⊗x≡x = record
  { F⇒G = record
    { η = λ X → unite⋆
    ; commute = λ _ → tt }
  ; F⇐G = record
    { η = λ X → uniti⋆
    ; commute = λ _ → tt }
  ; iso = λ X → record { isoˡ = tt; isoʳ = tt }
  }

x⊗1≡x : NaturalIsomorphism ⊗h.x⊗id ⊗h.x
x⊗1≡x = record
  { F⇒G = record
    { η = λ X → swap⋆ ◎ unite⋆  -- !!!
    ; commute = λ _ → tt
    }
  ; F⇐G = record
    { η = λ X → uniti⋆ ◎ swap⋆
    ; commute = λ _ → tt
    }
  ; iso = λ X → record
    { isoˡ = tt
    ; isoʳ = tt
    }
  }

[x⊗y]⊗z≡x⊗[y⊗z] : NaturalIsomorphism ⊗h.[x⊗y]⊗z ⊗h.x⊗[y⊗z]
[x⊗y]⊗z≡x⊗[y⊗z] = record
  { F⇒G = record
    { η = λ X → assocr⋆
    ; commute = λ f → tt
    }
  ; F⇐G = record
    { η = λ X → assocl⋆
    ; commute = λ _ → tt
    }
  ; iso = λ X → record
    { isoˡ = tt
    ; isoʳ = tt
    }
  }

M⊗ : Monoidal PiCat
M⊗ = record
  { ⊗ = ⊗-bifunctor
  ; id = ONE
  ; identityˡ = 1⊗x≡x
  ; identityʳ = x⊗1≡x
  ; assoc = [x⊗y]⊗z≡x⊗[y⊗z]
  ; triangle = tt
  ; pentagon = tt
  }

x⊕y≡y⊕x : NaturalIsomorphism ⊎h.x⊗y ⊎h.y⊗x
x⊕y≡y⊕x = record
  { F⇒G = record { η = λ X → swap₊ ; commute = λ f → tt }
  ; F⇐G = record { η = λ X → swap₊ ; commute = λ f → tt }
  ; iso = λ X → record { isoˡ = tt ; isoʳ = tt } }

BM⊕ : Braided M⊕
BM⊕ = record { braid = x⊕y≡y⊕x ; hexagon₁ = tt ; hexagon₂ = tt }

x⊗y≡y⊗x : NaturalIsomorphism ⊗h.x⊗y ⊗h.y⊗x
x⊗y≡y⊗x = record
  { F⇒G = record { η = λ X → swap⋆ ; commute = λ f → tt }
  ; F⇐G = record { η = λ X → swap⋆ ; commute = λ f → tt }
  ; iso = λ X → record { isoˡ = tt ; isoʳ = tt } }

BM⊗ : Braided M⊗
BM⊗ = record { braid = x⊗y≡y⊗x ; hexagon₁ = tt ; hexagon₂ = tt }

SBM⊕ : Symmetric BM⊕
SBM⊕ = record { symmetry = tt }

-- trace

Pi0Traced : Traced SBM⊕
Pi0Traced = record {
    trace = trace
  ; vanish_id = tt
  ; vanish_⊗ = tt
  ; superpose = tt
  ; yank = tt
  }

--

SBM⊗ : Symmetric BM⊗
SBM⊗ = record { symmetry = tt }

module r = BimonoidalHelperFunctors SBM⊕ BM⊗

x⊗0≡0 : NaturalIsomorphism r.x⊗0 r.0↑
x⊗0≡0 = record
  { F⇒G = record { η = λ X → absorbl ; commute = λ f → tt }
  ; F⇐G = record { η = λ X → factorzr ; commute = λ f → tt }
  ; iso = λ X → record { isoˡ = tt ; isoʳ = tt }
  }

0⊗x≡0 : NaturalIsomorphism r.0⊗x r.0↑
0⊗x≡0 = record
  { F⇒G = record { η = λ X → absorbr ; commute = λ f → tt }
  ; F⇐G = record { η = λ X → factorzl ; commute = λ f → tt }
  ; iso = λ X → record { isoˡ = tt ; isoʳ = tt }
  }

x⊗[y⊕z]≡[x⊗y]⊕[x⊗z] : NaturalIsomorphism r.x⊗[y⊕z] r.[x⊗y]⊕[x⊗z]
x⊗[y⊕z]≡[x⊗y]⊕[x⊗z] = record -- this probably says we need distl/distr
  { F⇒G = record { η = λ X → swap⋆ ◎ dist ◎ (swap⋆ ⊕ swap⋆) ; commute = λ f → tt }
  ; F⇐G = record { η = λ X → (swap⋆ ⊕ swap⋆) ◎ factor ◎ swap⋆ ; commute = λ f → tt }
  ; iso = λ X → record { isoˡ = tt ; isoʳ = tt }
  }

[x⊕y]⊗z≡[x⊗z]⊕[y⊗z] : NaturalIsomorphism r.[x⊕y]⊗z r.[x⊗z]⊕[y⊗z]
[x⊕y]⊗z≡[x⊗z]⊕[y⊗z] = record
  { F⇒G = record { η = λ X → dist ; commute = λ f → tt }
  ; F⇐G = record { η = λ X → factor ; commute = λ f → tt }
  ; iso = λ X → record { isoˡ = tt ; isoʳ = tt }
  }

Pi0Rig : RigCategory SBM⊕ BM⊗
Pi0Rig = record
  { distribₗ = x⊗[y⊕z]≡[x⊗y]⊕[x⊗z]
  ; distribᵣ = [x⊕y]⊗z≡[x⊗z]⊕[y⊗z]
  ; annₗ = x⊗0≡0
  ; annᵣ = 0⊗x≡0
  }

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


module Data.Tuple.Proofs where

import      Lvl
open import Data using (Unit ; <>)
open import Data.Tuple as Tuple using (_⨯_ ; _,_)
open import Data.Tuple.Equiv
open import Function.Equals
open import Logic.Predicate
open import Logic.Propositional
open import Structure.Function.Domain
open import Structure.Function.Domain.Proofs
open import Structure.Function
open import Structure.Operator
open import Structure.Relator.Properties
open import Structure.Setoid
open import Type

private variable ℓ ℓₑ ℓₑ₁ ℓₑ₂ ℓₑ₃ ℓₑ₄ ℓₑ₅ ℓₑ₆ : Lvl.Level
private variable A B A₁ B₁ A₂ B₂ : Type{ℓ}
private variable f g : A → B
private variable p : A ⨯ B

module _
  ⦃ equiv-A : Equiv{ℓₑ₁}(A) ⦄
  ⦃ equiv-B : Equiv{ℓₑ₂}(B) ⦄
  ⦃ equiv-AB : Equiv{ℓₑ}(A ⨯ B) ⦄ ⦃ ext-AB : Extensionality(equiv-AB) ⦄
  where
  open Extensionality(ext-AB)

  [,]-left-right-inverses : ∀{x : A ⨯ B} → ((Tuple.left x , Tuple.right x) ≡ x)
  [,]-left-right-inverses {_ , _} = reflexivity(_≡_)

  left-right-[,]-inverses : ∀{a : A}{b : B} → ((Tuple.left(a , b) ≡ a) ∧ (Tuple.right(a , b) ≡ b))
  left-right-[,]-inverses = [∧]-intro (reflexivity(_≡_)) (reflexivity(_≡_))

  instance
    [,]ₗ-injective : ∀{r : B} → Injective(_, r)
    Injective.proof [,]ₗ-injective = congruence₁(Tuple.left)

  instance
    [,]ᵣ-injective : ∀{l : A} → Injective(l ,_)
    Injective.proof [,]ᵣ-injective = congruence₁(Tuple.right)

module _
  ⦃ equiv-A₁ : Equiv{ℓₑ₁}(A₁) ⦄
  ⦃ equiv-B₁ : Equiv{ℓₑ₂}(B₁) ⦄
  ⦃ equiv-A₂ : Equiv{ℓₑ₃}(A₂) ⦄
  ⦃ equiv-B₂ : Equiv{ℓₑ₄}(B₂) ⦄
  ⦃ equiv-AB₁ : Equiv{ℓₑ₅}(A₁ ⨯ B₁) ⦄ ⦃ equiv-AB₁ : Extensionality(equiv-AB₁) ⦄
  ⦃ equiv-AB₂ : Equiv{ℓₑ₆}(A₂ ⨯ B₂) ⦄ ⦃ equiv-AB₂ : Extensionality(equiv-AB₂) ⦄
  {f : A₁ → A₂}
  {g : B₁ → B₂}
  where

  instance
    map-function : BinaryOperator(Tuple.map {A₁ = A₁}{A₂ = A₂}{B₁ = B₁}{B₂ = B₂})
    _⊜_.proof (BinaryOperator.congruence map-function (intro eq₁) (intro eq₂)) {_ , _} = congruence₂(_,_) eq₁ eq₂

  instance
    map-injective : ⦃ inj-f : Injective(f) ⦄ → ⦃ inj-g : Injective(g) ⦄ → Injective(Tuple.map f g)
    Injective.proof map-injective {_ , _} {_ , _} p = congruence₂(_,_) (injective(f) (congruence₁(Tuple.left) p)) (injective(g) (congruence₁(Tuple.right) p))

  instance
    map-surjective : ⦃ surj-f : Surjective(f) ⦄ → ⦃ surj-g : Surjective(g) ⦄ → Surjective(Tuple.map f g)
    ∃.witness (Surjective.proof map-surjective {l , r}) = ([∃]-witness(surjective(f) {l}) , [∃]-witness(surjective(g) {r}))
    ∃.proof   (Surjective.proof map-surjective {l , r}) = congruence₂(_,_) ([∃]-proof(surjective(f) {l})) ([∃]-proof(surjective(g) {r}))

  instance
    map-bijective : ⦃ bij-f : Bijective(f) ⦄ → ⦃ bij-g : Bijective(g) ⦄ → Bijective(Tuple.map f g)
    map-bijective = injective-surjective-to-bijective(Tuple.map f g) where
      instance
        inj-f : Injective(f)
        inj-f = bijective-to-injective(f)
      instance
        inj-g : Injective(g)
        inj-g = bijective-to-injective(g)
      instance
        surj-f : Surjective(f)
        surj-f = bijective-to-surjective(f)
      instance
        surj-g : Surjective(g)
        surj-g = bijective-to-surjective(g)


------------------------------------------------------------------------------
-- The the power function
------------------------------------------------------------------------------

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

module FOT.FOTC.Data.Nat.Pow where

open import FOTC.Base
open import FOTC.Data.Nat
open import FOTC.Data.Nat.UnaryNumbers

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

infixr 11 _^_

postulate
  _^_ : D → D → D
  ^-0 : ∀ n → n ^ zero      ≡ 1'
  ^-S : ∀ m n → m ^ succ₁ n ≡ m * m ^ n
{-# ATP axioms ^-0 ^-S #-}


{-# OPTIONS --cubical --no-import-sorts --safe #-}
module Cubical.ZCohomology.Groups.Unit where

open import Cubical.ZCohomology.Base
open import Cubical.ZCohomology.Properties
open import Cubical.HITs.Sn
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Cubical.HITs.Susp
open import Cubical.HITs.SetTruncation renaming (rec to sRec ; elim to sElim ; elim2 to sElim2)
open import Cubical.HITs.PropositionalTruncation renaming (rec to pRec ; elim to pElim ; elim2 to pElim2 ; ∥_∥ to ∥_∥₋₁ ; ∣_∣ to ∣_∣₋₁)
open import Cubical.HITs.Nullification
open import Cubical.Data.Int hiding (_+_ ; +-comm)
open import Cubical.Data.Nat
open import Cubical.HITs.Truncation
open import Cubical.Homotopy.Connected
open import Cubical.Data.Unit
open import Cubical.Algebra.Group

-- H⁰(Unit)
open GroupHom
open GroupIso
H⁰-Unit≅ℤ : GroupIso (coHomGr 0 Unit) intGroup
fun (GroupIso.map H⁰-Unit≅ℤ) = sRec isSetInt (λ f → f tt)
isHom (GroupIso.map H⁰-Unit≅ℤ) = sElim2 (λ _ _ → isOfHLevelPath 2 isSetInt _ _) λ a b → addLemma (a tt) (b tt)
inv H⁰-Unit≅ℤ a = ∣ (λ _ → a) ∣₂
rightInv H⁰-Unit≅ℤ _ = refl
leftInv H⁰-Unit≅ℤ = sElim (λ _ → isOfHLevelPath 2 setTruncIsSet _ _) λ a → refl

{- Hⁿ(Unit) for n ≥ 1 -}
isContrHⁿ-Unit : (n : ℕ) → isContr (coHom (suc n) Unit)
isContrHⁿ-Unit n = subst isContr (λ i → ∥ UnitToTypePath (coHomK (suc n)) (~ i) ∥₂) (helper' n)
  where
  helper' : (n : ℕ) → isContr (∥ coHomK (suc n) ∥₂)
  helper' n =
    subst isContr
      ((isoToPath (truncOfTruncIso {A = S₊ (1 + n)} 2 (1 + n)))
         ∙∙ sym propTrunc≡Trunc2
         ∙∙ λ i → ∥ hLevelTrunc (suc (+-comm n 2 i)) (S₊ (1 + n)) ∥₂)
      (isConnectedSubtr 2 (helper2 n .fst)
        (subst (λ x → isConnected x (S₊ (suc n))) (sym (helper2 n .snd)) (sphereConnected (suc n))) )
    where
    helper2 : (n : ℕ) → Σ[ m ∈ ℕ ] m + 2  ≡ 2 + n
    helper2 zero = 0 , refl
    helper2 (suc n) = (suc n) , λ i → suc (+-comm n 2 i)

Hⁿ-Unit≅0 : (n : ℕ) → GroupIso (coHomGr (suc n) Unit) trivialGroup
GroupHom.fun (GroupIso.map (Hⁿ-Unit≅0 n)) _ = _
GroupHom.isHom (GroupIso.map (Hⁿ-Unit≅0 n)) _ _ = refl
GroupIso.inv (Hⁿ-Unit≅0 n) _ = 0ₕ (suc n)
GroupIso.rightInv (Hⁿ-Unit≅0 n) _ = refl
GroupIso.leftInv (Hⁿ-Unit≅0 n) _ = isOfHLevelSuc 0 (isContrHⁿ-Unit n) _ _

{- Hⁿ for arbitrary contractible types -}
private
  Hⁿ-contrTypeIso : ∀ {ℓ} {A : Type ℓ} (n : ℕ) → isContr A
                   → Iso (coHom (suc n) A) (coHom (suc n) Unit)
  Hⁿ-contrTypeIso n contr = compIso (setTruncIso (isContr→Iso2 contr))
                                    (setTruncIso (invIso (isContr→Iso2 isContrUnit)))

Hⁿ-contrType≅0 : ∀ {ℓ} {A : Type ℓ} (n : ℕ) → isContr A
              → GroupIso (coHomGr (suc n) A) trivialGroup
fun (GroupIso.map (Hⁿ-contrType≅0 _ _)) _ = _
isHom (GroupIso.map (Hⁿ-contrType≅0 _ _)) _ _ = refl
inv (Hⁿ-contrType≅0 n _) _ = 0ₕ (suc n)
rightInv (Hⁿ-contrType≅0 _ _) _ = refl
leftInv (Hⁿ-contrType≅0 {A = A} n contr) _ = isOfHLevelSuc 0 helper _ _
  where
  helper : isContr (coHom (suc n) A)
  helper = (Iso.inv (Hⁿ-contrTypeIso n contr) (0ₕ (suc n)))
          , λ y →  cong (Iso.inv (Hⁿ-contrTypeIso n contr))
                         (isOfHLevelSuc 0 (isContrHⁿ-Unit n) (0ₕ (suc n)) (Iso.fun (Hⁿ-contrTypeIso n contr) y))
                  ∙ Iso.leftInv (Hⁿ-contrTypeIso n contr) y


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

open import HoTT
open import cohomology.Theory

module cw.cohomology.TopGrid {i} (OT : OrdinaryTheory i)
  (n : ℤ) {X Y Z : Ptd i} (f : X ⊙→ Y) (g : Y ⊙→ Z) where

{-
  X --> Y ---> Z
  |     | this |
  v     v  one v
  1 -> Y/X -> Z/X
-}

  open OrdinaryTheory OT
  open import cohomology.PtdMapSequence cohomology-theory
  open import cw.cohomology.GridPtdMap f g using (Y/X-to-Z/X)

  top-grid-comm-sqr : CommSquare (fst g) (fst Y/X-to-Z/X) cfcod cfcod
  top-grid-comm-sqr = comm-sqr λ _ → idp

  C-top-grid-commutes : CommSquareᴳ
    (C-fmap n Y/X-to-Z/X) (C-fmap n g) (C-fmap n (⊙cfcod' (g ⊙∘ f))) (C-fmap n (⊙cfcod' f))
  C-top-grid-commutes = C-comm-square n top-grid-comm-sqr