{-# OPTIONS --safe #-}

module Multiset.Functor where

open import Multiset.Prelude

open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Function
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Univalence using (ua)

record Functor {} (F : Type   Type ) : Type (ℓ-suc ) where
  field
    map :  {X Y : Type }  (X  Y)  (F X  F Y)
    map-id :  {X} x  map (idfun X) x  x
    map-comp :  {X Y Z}
       (g : Y  Z)
       (f : X  Y)
        x  map (g  f) x  map g (map f x)

  map-id-ext :  {X}  map (idfun X)  idfun (F X)
  map-id-ext = funExt map-id

  map-comp-ext :  {X Y Z}
     (g : Y  Z)  (f : X  Y)
     map (g  f)  map g  map f
  map-comp-ext g f = funExt (map-comp g f)

  cong-map-ext :  {X Y}  {f g : X  Y}
     f  g   x  map f x  map g x
  cong-map-ext p x = cong  h  map h x) p

  cong-map :  {X Y}  {f g : X  Y}
     (∀ x  f x  g x)   x  map f x  map g x
  cong-map p x = cong  h  map h x) (funExt p)

module _ where
  open Functor

  _∙F_ :  {} (F G : Type   Type )
      Functor F 
      Functor G 
     Functor (G  F)
  _∙F_ _ _  FF   FG  .Functor.map = FG .map  FF .map
  _∙F_ _ _  FF   FG  .Functor.map-id = funExt⁻ (cong (FG .map) (map-id-ext FF)  map-id-ext FG)
  _∙F_ _ _  FF   FG  .Functor.map-comp g f = funExt⁻ (cong (FG .map) (map-comp-ext FF g f)  map-comp-ext FG (map FF g) (map FF f))


module _ {} (F : Type   Type ) {{F' : Functor F}} where
  open Iso

  open Functor F' renaming (map to F[_])

  isoLift : {X Y : Type } (isom : Iso X Y)  Iso (F X) (F Y)
  isoLift isom .fun = F[ isom .fun ]
  isoLift isom .inv = F[ isom .inv ]
  isoLift isom .rightInv y = sym (map-comp (isom .fun) (isom .inv) y) ∙∙ cong-map (isom .rightInv) y ∙∙ map-id y
  isoLift isom .leftInv x = sym (map-comp (isom .inv) (isom .fun) x) ∙∙ cong-map (isom .leftInv) x ∙∙ map-id x

  equivLift : {X Y : Type }  X  Y  F X  F Y
  equivLift e = isoToEquiv (isoLift (equivToIso e))

  isEquivFunctorAction : {X Y : Type } {f : X  Y}  isEquiv f  isEquiv F[ f ]
  isEquivFunctorAction {f = f} is-equiv = equivIsEquiv (equivLift (f , is-equiv))

module _ {} (F G : Type   Type ) {{F' : Functor F}} {{G' : Functor G}} where
  open Functor F' using () renaming (map to F[_])
  open Functor G' using () renaming (map to G[_])

  isNatTrans : (∀ {X : Type }  F X  G X)  Type (ℓ-suc )
  isNatTrans η =  {X Y : Type } (f : X  Y)  η {Y}  F[ f ]  G[ f ]  η {X}

  record NatTrans : Type (ℓ-suc ) where
    constructor nattrans
    field
      mor :  {X}  F X  G X
      is-nat : isNatTrans mor

    component :  X  F X  G X
    component _ = mor

  _⇒_ : Type _
  _⇒_ = NatTrans

  open NatTrans public

  NatEquiv : Type (ℓ-suc )
  NatEquiv = Σ[ η  NatTrans ] (∀ {X}  isEquiv (component η X))

  NatEquiv→Equiv : NatEquiv   {X}  F X  G X
  NatEquiv→Equiv (η , is-equiv) {X} = component η X , is-equiv {X}

  Equiv→NatEquiv : (η :  {X}  F X  G X)  isNatTrans (equivFun η)  NatEquiv
  Equiv→NatEquiv η is-nat = nattrans (equivFun η) is-nat , equivIsEquiv η

FunctorSIP₀ :  {} {F G : Type   Type } {{FunctorF : Functor F}} {{FunctorG : Functor G}}
   ({X : Type }  F X  G X)  F  G
FunctorSIP₀ equivs = funExt λ X  ua (equivs {X})

-- FunctorSIP : ∀ {ℓ} {F G : Type ℓ → Type ℓ} {{FunctorF : Functor F}} {{FunctorG : Functor G}}
--   → (α : NatEquiv F G) → PathP (λ i → Functor (FunctorSIP₀ (NatEquiv→Equiv F G α) i)) FunctorF FunctorG
-- FunctorSIP α = {! !}

idNatTrans :  {} (F : Type   Type ) {{F' : Functor F}}  F  F
idNatTrans F .mor = idfun _
idNatTrans F .is-nat = λ _  refl


private
  variable
     : Level
    F G H : Type   Type 

invNatEquiv :   F' : Functor F   G' : Functor G   NatEquiv F G  NatEquiv G F
invNatEquiv {} {F} {G} η@(nattrans η⁺ is-nat , is-equiv) = nattrans η⁻ is-nat-η⁻ , is-equiv-η⁻ where
  open Functor ⦃...⦄

  η⁻ :  {X}  G X  F X
  η⁻ = invIsEq is-equiv

  is-nat-η⁻ :  {X Y}  (f : X  Y)  η⁻  map f  map f  η⁻
  is-nat-η⁻ {X} {Y} f =
    η⁻  map f            ≡⟨ cong ((η⁻  map f) ∘_) (sym (funExt (secIsEq is-equiv))) 
    η⁻  map f  η⁺  η⁻  ≡⟨ cong  ·  η⁻  ·  η⁻) (sym (is-nat f)) 
    η⁻  η⁺  map f  η⁻  ≡⟨ cong  g  g  (map f  η⁻)) (funExt (retIsEq is-equiv)) 
              map f  η⁻  

  is-equiv-η⁻ :  {X}  isEquiv (η⁻ {X})
  is-equiv-η⁻ {X} = isoToIsEquiv (invIso (equivToIso (NatEquiv→Equiv F G η)))


compNatTrans :   F' : Functor F   G' : Functor G   H' : Functor H 
   NatTrans F G
   NatTrans G H
   NatTrans F H
compNatTrans {} {F} {G} {H} (nattrans η η-nat) (nattrans φ φ-nat) = nattrans (φ  η) isNatTransComp where
  open Functor ⦃...⦄

  isNatTransComp : isNatTrans F H (φ  η)
  isNatTransComp f =
    φ  η  map f ≡⟨ cong (φ ∘_) (η-nat f) 
    φ  map f  η ≡⟨ cong (_∘ η) (φ-nat f) 
    map f  φ  η 

_∙ₙₜ_ = compNatTrans

compNatEquiv :   F' : Functor F   G' : Functor G   H' : Functor H 
   NatEquiv F G
   NatEquiv G H
   NatEquiv F H
compNatEquiv (η , η-equiv) (φ , φ-equiv) = η ∙ₙₜ φ , equivIsEquiv ((_ , η-equiv) ∙ₑ (_ , φ-equiv))

_∙ₙₑ_ = compNatEquiv

infixr 30 _∙ₙₜ_ _∙ₙₑ_

module SetTruncation where
  open import Cubical.HITs.SetTruncation as ST using (∥_∥₂ ; ∣_∣₂)
  open import Multiset.Util.SetTruncation

  open Functor ⦃...⦄

  instance
    setTruncFunctor : Functor {} ∥_∥₂
    setTruncFunctor .Functor.map = ST.map
    setTruncFunctor .Functor.map-id = ST.elim  _  ST.isSetPathImplicit)  _  refl)
    setTruncFunctor .Functor.map-comp g f = ST.elim  _  ST.isSetPathImplicit)  _  refl)

    setTruncPostFunctor :  Functor F   Functor (∥_∥₂  F)
    setTruncPostFunctor {F = F}  F'  .Functor.map = ST.map  map
    setTruncPostFunctor {F = F}  F'  .Functor.map-id = ST.elim  _  ST.isSetPathImplicit) (cong ∣_∣₂  map-id)
    setTruncPostFunctor {F = F}  F'  .Functor.map-comp g f = ST.elim  _  ST.isSetPathImplicit) (cong ∣_∣₂  map-comp g f)

  setTruncPostNatTrans :  F' : Functor F    G' : Functor G   F  G  (∥_∥₂  F)  (∥_∥₂  G)
  setTruncPostNatTrans η .mor = ST.map (η .mor)
  setTruncPostNatTrans η .is-nat f = funExt $ ST.elim  _  ST.isSetPathImplicit) λ x  cong ∣_∣₂ (funExt⁻ (η .is-nat f) x)

  setTruncPostNatEquiv :  F' : Functor F    G' : Functor G   NatEquiv F G  NatEquiv (∥_∥₂  F) (∥_∥₂  G)
  setTruncPostNatEquiv (α , α-equiv) = setTruncPostNatTrans α , equivIsEquiv (setTruncEquiv (_ , α-equiv))