{-# OPTIONS --without-K --safe #-}
module Categories.Functor.Equivalence where

-- A 'strict' equality relation for Functors.

open import Level
open import Data.Product using (Σ; curry) renaming (_,_ to _,,_)
open import Relation.Binary.PropositionalEquality as 
open import Relation.Binary using (IsEquivalence)
open import Function using (_$_) renaming (_∘_ to _⊚_)

open import Categories.Category using (Category; _[_,_]; _[_≈_]; module Definitions)
open import Categories.Category.Product
open import Categories.Functor using (Functor; _∘F_) renaming (id to idF)
open import Categories.Functor.Bifunctor
open import Categories.Functor.Properties using ([_]-resp-square)
open import Categories.NaturalTransformation using (ntHelper)
import Categories.Morphism as Morphism
import Categories.Morphism.HeterogeneousIdentity as HId
import Categories.Morphism.HeterogeneousIdentity.Properties as HIdProps
import Categories.Morphism.IsoEquiv as IsoEquiv
import Categories.Morphism.Reasoning as MorphismReasoning
import Categories.NaturalTransformation.NaturalIsomorphism as NatIso

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

-- "Strict" equality of Functors.
--
-- It's helpful to think of a functor equality (P : F ≡F G) as a
-- special natural isomorphism where every component (hid $ eq₀ P X)
-- is a 'heterogeneous' identity.

infix  4 _≡F_

record _≡F_ {C : Category o  e}
            {D : Category o′ ℓ′ e′}
            (F G : Functor C D) : Set (  o  o′  e′) where
  private
    module C = Category C
    module D = Category D
    module F = Functor F
    module G = Functor G
  open F using (F₀; F₁)
  open G using () renaming (F₀ to G₀; F₁ to G₁)
  open HId D
  open Definitions D

  field
    eq₀ :  X  F₀ X  G₀ X

    eq₁ :  {X Y} (f : C [ X , Y ]) 
          CommutativeSquare (F₁ f) (hid $ eq₀ X) (hid $ eq₀ Y) (G₁ f)

  eq₀⁻¹ :  X  G₀ X  F₀ X
  eq₀⁻¹ X = sym $ eq₀ X

  eq₁⁻¹ :  {X Y} (f : C [ X , Y ]) 
          CommutativeSquare (G₁ f) (hid $ eq₀⁻¹ X) (hid $ eq₀⁻¹ Y) (F₁ f)
  eq₁⁻¹ {X} {Y} f = begin
      τ⁻¹ Y    G₁ f                      ≈˘⟨ identityʳ 
     (τ⁻¹ Y    G₁ f)   id               ≈˘⟨ ∘-resp-≈ʳ $ hid-symʳ _ 
     (τ⁻¹ Y    G₁ f)  (τ X    τ⁻¹ X)   ≈⟨ assoc 
      τ⁻¹ Y   (G₁ f   (τ X    τ⁻¹ X))  ≈⟨ ∘-resp-≈ʳ sym-assoc 
      τ⁻¹ Y  ((G₁ f    τ X)   τ⁻¹ X)   ≈˘⟨ ∘-resp-≈ʳ $ ∘-resp-≈ˡ $ eq₁ f 
      τ⁻¹ Y  ((τ Y    F₁ f)   τ⁻¹ X)   ≈⟨ sym-assoc 
     (τ⁻¹ Y   (τ Y    F₁ f))  τ⁻¹ X    ≈⟨ ∘-resp-≈ˡ sym-assoc 
    ((τ⁻¹ Y    τ Y)   F₁ f)   τ⁻¹ X    ≈⟨ ∘-resp-≈ˡ $ ∘-resp-≈ˡ $ hid-symˡ _ 
    (id                F₁ f)   τ⁻¹ X    ≈⟨ ∘-resp-≈ˡ identityˡ 
    F₁ f                        τ⁻¹ X    
    where
      τ   = λ X  hid $ eq₀   X
      τ⁻¹ = λ X  hid $ eq₀⁻¹ X
      open D
      open HomReasoning

  open NatIso using (_≃_)

  -- the family (hid eq₀) is a natural isomorphism

  natIso : F  G
  natIso = record
    { F⇒G = ntHelper record { η = hid  eq₀   ; commute = eq₁   }
    ; F⇐G = ntHelper record { η = hid  eq₀⁻¹ ; commute = eq₁⁻¹ }
    ; iso = λ X  hid-iso $ eq₀ X
    }

module _ {C : Category o  e} {D : Category o′ ℓ′ e′} where

  open Functor
  open Category D
  open HomReasoning
  open MorphismReasoning D
  open HId D
  open _≡F_

  -- Strict functor equality is an equivalence.

  ≡F-equiv : IsEquivalence (_≡F_ {C = C} {D})
  ≡F-equiv = record
    { refl  = record { eq₀ = λ _  refl ; eq₁ = λ _  id-comm-sym }
    ; sym   = λ F≡G  record { eq₀ = eq₀⁻¹ F≡G ; eq₁ = eq₁⁻¹ F≡G }
    ; trans = λ {F G H} F≡G G≡H  record
      { eq₀ = λ X  trans (eq₀ F≡G X) (eq₀ G≡H X)
      ; eq₁ = λ {X} {Y} f  begin
          hid (trans (eq₀ F≡G Y) (eq₀ G≡H Y))  F₁ F f
        ≈˘⟨ ∘-resp-≈ˡ (hid-trans _ _) 
          (hid (eq₀ G≡H Y)  hid (eq₀ F≡G Y))  F₁ F f
        ≈⟨ glue (eq₁ G≡H f) (eq₁ F≡G f) 
          F₁ H f  (hid (eq₀ G≡H X)  hid (eq₀ F≡G X))
        ≈⟨ ∘-resp-≈ʳ (hid-trans _ _) 
          F₁ H f  hid (trans (eq₀ F≡G X) (eq₀ G≡H X))
        
      }
    }

  ≡F-identityˡ : {F : Functor C D}  idF ∘F F ≡F F
  ≡F-identityˡ = record { eq₀ = λ _  refl ; eq₁ = λ _  id-comm-sym }

  ≡F-identityʳ : {F : Functor C D}  F ∘F idF ≡F F
  ≡F-identityʳ = record { eq₀ = λ _  refl ; eq₁ = λ _  id-comm-sym }

module _ {C : Category o  e} where
  open MorphismReasoning C

  ≡F-identity² : idF ∘F idF ≡F idF {C = C}
  ≡F-identity² = record { eq₀ = λ _  refl ; eq₁ = λ _  id-comm-sym }

module _ {C : Category o  e} {D : Category o′ ℓ′ e′}
         {E : Category o″ ℓ″ e″}
         where

  open Functor
  open Category E
  open HomReasoning
  open MorphismReasoning E
  open HId
  open HIdProps
  open _≡F_

  ∘F-resp-≡F : {H I : Functor D E} {F G : Functor C D} 
               H ≡F I  F ≡F G  H ∘F F ≡F I ∘F G
  ∘F-resp-≡F {H} {I} {F} {G} H≡I F≡G = record
    { eq₀ = λ X  trans (HF≡HG X) (HG≡IG X)
    ; eq₁ = λ {X Y} f  begin
        hid E (trans (HF≡HG Y) (HG≡IG Y))  F₁ H (F₁ F f)
      ≈˘⟨ ∘-resp-≈ˡ (hid-trans E _ _) 
        (hid E (HG≡IG Y)  hid E (HF≡HG Y))  F₁ H (F₁ F f)
      ≈˘⟨ ∘-resp-≈ˡ $ ∘-resp-≈ʳ (F-hid H (eq₀ F≡G Y)) 
        (hid E (HG≡IG Y)  F₁ H (hid D (eq₀ F≡G Y)))  F₁ H (F₁ F f)
      ≈⟨ glue (eq₁ H≡I (F₁ G f)) ([ H ]-resp-square (eq₁ F≡G f)) 
        F₁ I (F₁ G f)  (hid E (HG≡IG X)  F₁ H (hid D (eq₀ F≡G X)))
      ≈⟨ ∘-resp-≈ʳ $ ∘-resp-≈ʳ (F-hid H (eq₀ F≡G X)) 
        F₁ I (F₁ G f)  (hid E (HG≡IG X)  hid E (HF≡HG X))
      ≈⟨ ∘-resp-≈ʳ (hid-trans E _ _) 
        F₁ I (F₁ G f)  hid E (trans (HF≡HG X) (HG≡IG X))
      
    }
    where
      HF≡HG = λ X  cong (F₀ H) (eq₀ F≡G X)
      HG≡IG = λ X  eq₀ H≡I (F₀ G X)

module _ {B : Category o  e} {C : Category o′ ℓ′ e′}
         {D : Category o″ ℓ″ e″} {E : Category o‴ ℓ‴ e‴}
         where

  open Functor
  open Category E
  open MorphismReasoning E
  open _≡F_

  ≡F-assoc : {F : Functor B C} {G : Functor C D} {H : Functor D E} 
             (H ∘F G) ∘F F  ≡F  H ∘F (G ∘F F)
  ≡F-assoc = record { eq₀ = λ _  refl ; eq₁ = λ _  id-comm-sym }

  ≡F-sym-assoc : {F : Functor B C} {G : Functor C D} {H : Functor D E} 
                 H ∘F (G ∘F F)  ≡F  (H ∘F G) ∘F F
  ≡F-sym-assoc = record { eq₀ = λ _  refl ; eq₁ = λ _  id-comm-sym }