{-# OPTIONS --without-K --safe #-}
module Cats.Util.SetoidMorphism.Iso where

open import Data.Product using (_,_ ; proj₁ ; proj₂)
open import Level using (_⊔_)
open import Relation.Binary using (Setoid ; IsEquivalence)

open import Cats.Util.SetoidMorphism as Mor using
  ( _⇒_ ; arr ; resp ; _≈_ ; ≈-intro ; ≈-elim ; ≈-elim′ ; _∘_ ; ∘-resp ; id
  ; IsInjective ; IsSurjective )
open import Cats.Util.SetoidReasoning


private
  module S = Setoid


record IsIso {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
  (f : A  B) : Set (l  l≈  l′  l≈′)
  where
  field
    back : B  A
    forth-back : f  back  id
    back-forth : back  f  id

open IsIso


record _≅_ {l l≈} (A : Setoid l l≈) {l′ l≈′} (B : Setoid l′ l≈′)
  : Set (l  l≈  l′  l≈′)
  where
  field
    forth : A  B
    back : B  A
    forth-back : forth  back  id
    back-forth : back  forth  id

open _≅_ public


IsIso→≅ :  {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′} (f : A  B)
   IsIso f  A  B
IsIso→≅ f i = record
    { forth = f
    ; back = back i
    ; forth-back = forth-back i
    ; back-forth = back-forth i
    }


≅→IsIso :  {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
   (i : A  B)  IsIso (forth i)
≅→IsIso i = record
    { back = back i
    ; forth-back = forth-back i
    ; back-forth = back-forth i
    }


IsIso-resp :  {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
   {f g : A  B}  f  g  IsIso f  IsIso g
IsIso-resp {A = A} {B = B} {f} {g} f≈g i = record
    { back = back i
    ; forth-back = ≈-intro λ {x} {y} x≈y 
        begin⟨ B 
          arr g (arr (back i) x)
        ≈˘⟨ ≈-elim f≈g (resp (back i) (B.sym x≈y)) 
          arr f (arr (back i) y)
        ≈⟨ ≈-elim′ (forth-back i) 
          y
        
    ; back-forth = ≈-intro λ {x} {y} x≈y 
        begin⟨ A 
          arr (back i) (arr g x)
        ≈⟨ resp (back i) (B.sym (≈-elim f≈g (A.sym x≈y))) 
          arr (back i) (arr f y)
        ≈⟨ ≈-elim′ (back-forth i) 
          y
        
    }
  where
    module A = Setoid A
    module B = Setoid B


refl :  {l l≈} {A : Setoid l l≈}  A  A
refl = record
    { forth = id
    ; back = id
    ; forth-back = ≈-intro λ eq  eq
    ; back-forth = ≈-intro λ eq  eq
    }


sym :  {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
   A  B  B  A
sym i = record
    { forth = back i
    ; back = forth i
    ; forth-back = back-forth i
    ; back-forth = forth-back i
    }


trans :  {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
    {l″ l≈″} {C : Setoid l″ l≈″}
   A  B  B  C  A  C
trans {A = A} {C = C} AB BC = record
    { forth = forth BC  forth AB
    ; back = back AB  back BC
    ; forth-back = ≈-intro λ x≈y
         S.trans C (resp (forth BC) (≈-elim (forth-back AB) (resp (back BC) x≈y)))
            (≈-elim′ (forth-back BC))
    ; back-forth = ≈-intro λ x≈y
         S.trans A (resp (back AB) (≈-elim (back-forth BC) (resp (forth AB) x≈y)))
            (≈-elim′ (back-forth AB))
    }


module _ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′} where

  Injective∧Surjective→Iso : {f : A  B}
     IsInjective f  IsSurjective f  IsIso f
  Injective∧Surjective→Iso {f} inj surj = record
      { back = record
        { arr = λ b  proj₁ (surj b)
        ; resp = λ {b} {c} b≈c  inj
            (S.trans B (S.sym B (proj₂ (surj b))) (S.trans B b≈c (proj₂ (surj c))))
        }
      ; forth-back = ≈-intro λ {x} {y} x≈y  S.trans B (S.sym B (proj₂ (surj x))) x≈y
      ; back-forth = ≈-intro λ {x} {y} x≈y  inj
          (S.trans B (S.sym B (proj₂ (surj _))) (resp f x≈y))
      }


  Injective∧Surjective→≅ : {f : A  B}  IsInjective f  IsSurjective f  A  B
  Injective∧Surjective→≅ {f} inj surj
      = IsIso→≅ f (Injective∧Surjective→Iso inj surj)


  Iso→Injective : {f : A  B}  IsIso f  IsInjective f
  Iso→Injective {f} i {a} {b} fa≈fb =
      begin⟨ A 
        a
      ≈˘⟨ ≈-elim′ (back-forth i) 
        arr (back i) (arr f a)
      ≈⟨ resp (back i) fa≈fb 
        arr (back i) (arr f b)
      ≈⟨ ≈-elim′ (back-forth i) 
        b
      


  ≅-Injective : (i : A  B)  IsInjective (forth i)
  ≅-Injective i = Iso→Injective (≅→IsIso i)


  Iso→Surjective : {f : A  B}  IsIso f  IsSurjective f
  Iso→Surjective i b = arr (back i) b  , S.sym B (≈-elim′ (forth-back i))


  ≅-Surjective : (i : A  B)  IsSurjective (forth i)
  ≅-Surjective i = Iso→Surjective (≅→IsIso i)