{-

Half adjoint equivalences ([HAEquiv])

- Iso to HAEquiv ([iso→HAEquiv])
- Equiv to HAEquiv ([equiv→HAEquiv])
- Cong is an equivalence ([congEquiv])

-}
{-# OPTIONS --safe #-}
module Cubical.Foundations.Equiv.HalfAdjoint where

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Isomorphism
open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Univalence
open import Cubical.Foundations.Function
open import Cubical.Foundations.GroupoidLaws
open import Cubical.Foundations.Path

private
  variable
     ℓ' : Level
    A : Type 
    B : Type ℓ'

record isHAEquiv { ℓ'} {A : Type } {B : Type ℓ'} (f : A  B) : Type (ℓ-max  ℓ') where
  field
    g : B  A
    linv :  a  g (f a)  a
    rinv :  b  f (g b)  b
    com :  a  cong f (linv a)  rinv (f a)

  com-op :  b  cong g (rinv b)  linv (g b)
  com-op b = subst  b  cong g (rinv b)  linv (g b)) (rinv b) (helper (g b))
    where
    helper :  a  cong g (rinv (f a))  linv (g (f a))
    helper a i j = hcomp  k  λ { (i = i0)  g (rinv (f a) (j  ~ k))
                                  ; (i = i1)  linv (linv a (~ k)) j
                                  ; (j = i0)  g (com a (~ i) (~ k))
                                  ; (j = i1)  linv a (i  ~ k) })
                         (linv a (i  j))

  isHAEquiv→Iso : Iso A B
  Iso.fun isHAEquiv→Iso = f
  Iso.inv isHAEquiv→Iso = g
  Iso.rightInv isHAEquiv→Iso = rinv
  Iso.leftInv isHAEquiv→Iso = linv

  isHAEquiv→isEquiv : isEquiv f
  isHAEquiv→isEquiv .equiv-proof y = (g y , rinv y) , isCenter where
    isCenter :  xp  (g y , rinv y)  xp
    isCenter (x , p) i = gy≡x i , ry≡p i where
      gy≡x : g y  x
      gy≡x = sym (cong g p) ∙∙ refl ∙∙ linv x

      lem0 : Square (cong f (linv x)) p (cong f (linv x)) p
      lem0 i j = invSides-filler p (sym (cong f (linv x))) (~ i) j

      ry≡p : Square (rinv y) p (cong f gy≡x) refl
      ry≡p i j = hcomp  k  λ { (i = i0)  cong rinv p k j
                                ; (i = i1)  lem0 k j
                                ; (j = i0)  f (doubleCompPath-filler (sym (cong g p)) refl (linv x) k i)
                                ; (j = i1)  p k })
                       (com x (~ i) j)

open isHAEquiv using (isHAEquiv→Iso; isHAEquiv→isEquiv) public

HAEquiv : (A : Type ) (B : Type ℓ')  Type (ℓ-max  ℓ')
HAEquiv A B = Σ[ f  (A  B) ] isHAEquiv f

-- vogt's lemma (https://ncatlab.org/nlab/show/homotopy+equivalence#vogts_lemma)
iso→HAEquiv : Iso A B  HAEquiv A B
iso→HAEquiv e = f , isHAEquivf
  where
    f = Iso.fun e
    g = Iso.inv e
    ε = Iso.rightInv e
    η = Iso.leftInv e

    Hfa≡fHa : (f : A  A)  (H :  a  f a  a)   a  H (f a)  cong f (H a)
    Hfa≡fHa f H = J  f p   a  funExt⁻ (sym p) (f a)  cong f (funExt⁻ (sym p) a))
                     a  refl)
                    (sym (funExt H))

    isHAEquivf : isHAEquiv f
    isHAEquiv.g isHAEquivf         = g
    isHAEquiv.linv isHAEquivf       = η
    isHAEquiv.rinv isHAEquivf b i   =
      hcomp  j  λ { (i = i0)  ε (f (g b)) j
                     ; (i = i1)  ε b j })
            (f (η (g b) i))
    isHAEquiv.com isHAEquivf a i j =
      hcomp  k  λ { (i = i0)  ε (f (η a j)) k
                     ; (j = i0)  ε (f (g (f a))) k
                     ; (j = i1)  ε (f a) k})
            (f (Hfa≡fHa  x  g (f x)) η a (~ i) j))

equiv→HAEquiv : A  B  HAEquiv A B
equiv→HAEquiv e = e .fst , λ where
  .isHAEquiv.g  invIsEq (snd e)
  .isHAEquiv.linv  retIsEq (snd e)
  .isHAEquiv.rinv  secIsEq (snd e)
  .isHAEquiv.com a  sym (commPathIsEq (snd e) a)

congIso : {x y : A} (e : Iso A B)  Iso (x  y) (Iso.fun e x  Iso.fun e y)
congIso {x = x} {y} e = goal
  where
  open isHAEquiv (iso→HAEquiv e .snd)
  open Iso

  goal : Iso (x  y) (Iso.fun e x  Iso.fun e y)
  fun goal   = cong (iso→HAEquiv e .fst)
  inv goal p = sym (linv x) ∙∙ cong g p ∙∙ linv y
  rightInv goal p i j =
    hcomp  k  λ { (i = i0)  iso→HAEquiv e .fst
                                  (doubleCompPath-filler (sym (linv x)) (cong g p) (linv y) k j)
                   ; (i = i1)  rinv (p j) k
                   ; (j = i0)  com x i k
                   ; (j = i1)  com y i k })
          (iso→HAEquiv e .fst (g (p j)))
  leftInv goal p i j =
    hcomp  k  λ { (i = i1)  p j
                   ; (j = i0)  Iso.leftInv e x (i  k)
                   ; (j = i1)  Iso.leftInv e y (i  k) })
          (Iso.leftInv e (p j) i)

invCongFunct : {x : A} (e : Iso A B) (p : Iso.fun e x  Iso.fun e x) (q : Iso.fun e x  Iso.fun e x)
              Iso.inv (congIso e) (p  q)  Iso.inv (congIso e) p  Iso.inv (congIso e) q
invCongFunct {x = x} e p q = helper (Iso.inv e) _ _ _
  where
  helper : {x : A} {y : B} (f : A  B) (r : f x  y) (p q : x  x)
          (sym r ∙∙ cong f (p  q) ∙∙ r)  (sym r ∙∙ cong f p ∙∙ r)  (sym r ∙∙ cong f q ∙∙ r)
  helper {x = x} f =
    J  y r  (p q : x  x)
     (sym r ∙∙ cong f (p  q) ∙∙ r)  (sym r ∙∙ cong f p ∙∙ r)  (sym r ∙∙ cong f q ∙∙ r))
      λ p q   i  rUnit (congFunct f p q i) (~ i))
              λ i  rUnit (cong f p) i  rUnit (cong f q) i

invCongRefl : {x : A} (e : Iso A B)  Iso.inv (congIso {x = x} {y = x} e) refl  refl
invCongRefl {x = x} e =  i   j  Iso.leftInv e x (i  ~ j)) ∙∙ refl ∙∙  j  Iso.leftInv e x (i  j)))  sym (rUnit refl)