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

open import Data.Product using (∃-syntax ; _,_ ; proj₁ ; proj₂)
open import Level using (_⊔_ ; suc)
open import Relation.Binary using (Rel ; Setoid ; IsEquivalence ; _Preserves_⟶_)
open import Relation.Binary.SetoidReasoning

open import Cats.Util.Function using () renaming (_∘_ to _⊚_)

open Setoid renaming (_≈_ to eq)


infixr 9 _∘_


record _⇒_ {l l≈} (A : Setoid l l≈) {l′ l≈′} (B : Setoid l′ l≈′)
  : Set (l ⊔ l′ ⊔ l≈ ⊔ l≈′)
  where
  field
    arr : Carrier A → Carrier B
    resp : arr Preserves eq A ⟶ eq B

open _⇒_ public using (arr ; resp)


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

  infixr 4 _≈_


  record _≈_ (f g : A ⇒ B) : Set (l ⊔ l≈ ⊔ l≈′) where
    constructor ≈-intro
    field
       ≈-elim : ∀ {x y} → eq A x y → eq B (arr f x) (arr g y)

    ≈-elim′ : ∀ {x} → eq B (arr f x) (arr g x)
    ≈-elim′ = ≈-elim (refl A)

  open _≈_ public


  equiv : IsEquivalence _≈_
  equiv = record
      { refl = λ {f} → ≈-intro (resp f)
      ; sym = λ eq → ≈-intro λ x≈y → sym B (≈-elim eq (sym A x≈y))
      ; trans = λ eq₁ eq₂ → ≈-intro (λ x≈y → trans B (≈-elim eq₁ x≈y) (≈-elim′ eq₂))
      }


  setoid : Setoid (l ⊔ l≈ ⊔ l′ ⊔ l≈′) (l ⊔ l≈ ⊔ l≈′)
  setoid = record
      { Carrier = A ⇒ B
      ; _≈_ = _≈_
      ; isEquivalence = equiv
      }


id : ∀ {l l≈} {A : Setoid l l≈} → A ⇒ A
id = record { arr = λ x → x ; resp = λ x → x }


_∘_ : ∀ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
  → ∀ {l″ l≈″} {C : Setoid l″ l≈″}
  → B ⇒ C → A ⇒ B → A ⇒ C
_∘_ f g = record
    { arr = arr f ⊚ arr g
    ; resp = resp f ⊚ resp g
    }


∘-resp : ∀ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
  → ∀ {l″ l≈″} {C : Setoid l″ l≈″}
  → {f f′ : B ⇒ C} {g g′ : A ⇒ B}
  → f ≈ f′ → g ≈ g′ → f ∘ g ≈ f′ ∘ g′
∘-resp f≈f′ g≈g′ = ≈-intro (≈-elim f≈f′ ⊚ ≈-elim g≈g′)


id-l : ∀ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
   → {f : A ⇒ B}
   → id ∘ f ≈ f
id-l {f = f} = ≈-intro (resp f)


id-r : ∀ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
  → {f : A ⇒ B}
  → f ∘ id ≈ f
id-r {f = f} = ≈-intro (resp f)


assoc : ∀ {l l≈} {A : Setoid l l≈} {l′ l≈′} {B : Setoid l′ l≈′}
  → ∀ {l″ l≈″} {C : Setoid l″ l≈″} {l‴ l≈‴} {D : Setoid l‴ l≈‴}
  → {f : C ⇒ D} {g : B ⇒ C} {h : A ⇒ B}
  → (f ∘ g) ∘ h ≈ f ∘ (g ∘ h)
assoc {f = f} {g} {h} = ≈-intro (resp f ⊚ resp g ⊚ resp h)


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

  IsInjective : A ⇒ B → Set (l ⊔ l≈ ⊔ l≈′)
  IsInjective f = ∀ {a b} → eq B (arr f a) (arr f b) → eq A a b


  IsSurjective : A ⇒ B → Set (l ⊔ l′ ⊔ l≈′)
  IsSurjective f = ∀ b → ∃[ a ] (eq B b (arr f a))