------------------------------------------------------------------------
-- The Agda standard library
--
-- Composition of functional properties
------------------------------------------------------------------------

{-# OPTIONS --without-K --safe #-}

module Function.Construct.Composition where

open import Data.Product using (_,_)
open import Function
open import Level using (Level)
open import Relation.Binary as B hiding (_⇔_; IsEquivalence)

private
  variable
    a b c ℓ₁ ℓ₂ ℓ₃ : Level
    A : Set a
    B : Set b
    C : Set c

------------------------------------------------------------------------
-- Properties

module _ (≈₁ : Rel A ℓ₁) (≈₂ : Rel B ℓ₂) (≈₃ : Rel C ℓ₃)
         {f : A  B} {g : B  C}
         where

  congruent : Congruent ≈₁ ≈₂ f  Congruent ≈₂ ≈₃ g 
              Congruent ≈₁ ≈₃ (g  f)
  congruent f-cong g-cong = g-cong  f-cong

  injective : Injective ≈₁ ≈₂ f  Injective ≈₂ ≈₃ g 
              Injective ≈₁ ≈₃ (g  f)
  injective f-inj g-inj = f-inj  g-inj

  surjective : Transitive ≈₃  Congruent ≈₂ ≈₃ g 
               Surjective ≈₁ ≈₂ f  Surjective ≈₂ ≈₃ g 
               Surjective ≈₁ ≈₃ (g  f)
  surjective trans g-cong f-sur g-sur x with g-sur x
  ... | y , fy≈x  with f-sur y
  ...   | z , fz≈y = z , trans (g-cong fz≈y) fy≈x

  bijective : Transitive ≈₃  Congruent ≈₂ ≈₃ g 
              Bijective ≈₁ ≈₂ f  Bijective ≈₂ ≈₃ g 
              Bijective ≈₁ ≈₃ (g  f)
  bijective trans g-cong (f-inj , f-sur) (g-inj , g-sur) =
    injective f-inj g-inj , surjective trans g-cong f-sur g-sur

module _ (≈₁ : Rel A ℓ₁) (≈₂ : Rel B ℓ₂) (≈₃ : Rel C ℓ₃)
         (f : A  B) {f⁻¹ : B  A} {g : B  C} (g⁻¹ : C  B)
         where

  inverseˡ : Transitive ≈₃  Congruent ≈₂ ≈₃ g 
             Inverseˡ ≈₁ ≈₂ f f⁻¹  Inverseˡ ≈₂ ≈₃ g g⁻¹ 
             Inverseˡ ≈₁ ≈₃ (g  f) (f⁻¹  g⁻¹)
  inverseˡ trn g-cong f-inv g-inv x = trn (g-cong (f-inv _)) (g-inv x)

  inverseʳ : Transitive ≈₁  Congruent ≈₂ ≈₁ f⁻¹ 
             Inverseʳ ≈₁ ≈₂ f f⁻¹  Inverseʳ ≈₂ ≈₃ g g⁻¹ 
             Inverseʳ ≈₁ ≈₃ (g  f) (f⁻¹  g⁻¹)
  inverseʳ trn f⁻¹-cong f-inv g-inv x = trn (f⁻¹-cong (g-inv _)) (f-inv x)

  inverseᵇ : Transitive ≈₁  Transitive ≈₃ 
             Congruent ≈₂ ≈₃ g  Congruent ≈₂ ≈₁ f⁻¹ 
             Inverseᵇ ≈₁ ≈₂ f f⁻¹  Inverseᵇ ≈₂ ≈₃ g g⁻¹ 
             Inverseᵇ ≈₁ ≈₃ (g  f) (f⁻¹  g⁻¹)
  inverseᵇ trn₁ trn₃ g-cong f⁻¹-cong (f-invˡ , f-invʳ) (g-invˡ , g-invʳ) =
    inverseˡ trn₃ g-cong f-invˡ g-invˡ , inverseʳ trn₁ f⁻¹-cong f-invʳ g-invʳ

------------------------------------------------------------------------
-- Structures

module _ {≈₁ : Rel A ℓ₁} {≈₂ : Rel B ℓ₂} {≈₃ : Rel C ℓ₃}
         {f : A  B} {g : B  C}
         where

  isCongruent : IsCongruent ≈₁ ≈₂ f  IsCongruent ≈₂ ≈₃ g 
                IsCongruent ≈₁ ≈₃ (g  f)
  isCongruent f-cong g-cong = record
    { cong           = G.cong  F.cong
    ; isEquivalence₁ = F.isEquivalence₁
    ; isEquivalence₂ = G.isEquivalence₂
    } where module F = IsCongruent f-cong; module G = IsCongruent g-cong

  isInjection : IsInjection ≈₁ ≈₂ f  IsInjection ≈₂ ≈₃ g 
                IsInjection ≈₁ ≈₃ (g  f)
  isInjection f-inj g-inj = record
    { isCongruent = isCongruent F.isCongruent G.isCongruent
    ; injective   = injective ≈₁ ≈₂ ≈₃ F.injective G.injective
    } where module F = IsInjection f-inj; module G = IsInjection g-inj

  isSurjection : IsSurjection ≈₁ ≈₂ f  IsSurjection ≈₂ ≈₃ g 
                 IsSurjection ≈₁ ≈₃ (g  f)
  isSurjection f-surj g-surj = record
    { isCongruent = isCongruent F.isCongruent G.isCongruent
    ; surjective   = surjective ≈₁ ≈₂ ≈₃ G.Eq₂.trans G.cong F.surjective G.surjective
    } where module F = IsSurjection f-surj; module G = IsSurjection g-surj

  isBijection : IsBijection ≈₁ ≈₂ f  IsBijection ≈₂ ≈₃ g 
                IsBijection ≈₁ ≈₃ (g  f)
  isBijection f-bij g-bij = record
    { isInjection = isInjection F.isInjection G.isInjection
    ; surjective  = surjective ≈₁ ≈₂ ≈₃ G.Eq₂.trans G.cong F.surjective G.surjective
    } where module F = IsBijection f-bij; module G = IsBijection g-bij

module _ {≈₁ : Rel A ℓ₁} {≈₂ : Rel B ℓ₂} {≈₃ : Rel C ℓ₃}
         {f : A  B} {g : B  C} {f⁻¹ : B  A} {g⁻¹ : C  B}
         where

  isLeftInverse : IsLeftInverse ≈₁ ≈₂ f f⁻¹  IsLeftInverse ≈₂ ≈₃ g g⁻¹ 
                  IsLeftInverse ≈₁ ≈₃ (g  f) (f⁻¹  g⁻¹)
  isLeftInverse f-invˡ g-invˡ = record
    { isCongruent = isCongruent F.isCongruent G.isCongruent
    ; cong₂       = congruent ≈₃ ≈₂ ≈₁ G.cong₂ F.cong₂
    ; inverseˡ    = inverseˡ ≈₁ ≈₂ ≈₃ f _ G.Eq₂.trans G.cong₁ F.inverseˡ G.inverseˡ
    } where module F = IsLeftInverse f-invˡ; module G = IsLeftInverse g-invˡ

  isRightInverse : IsRightInverse ≈₁ ≈₂ f f⁻¹  IsRightInverse ≈₂ ≈₃ g g⁻¹ 
                   IsRightInverse ≈₁ ≈₃ (g  f) (f⁻¹  g⁻¹)
  isRightInverse f-invʳ g-invʳ = record
    { isCongruent = isCongruent F.isCongruent G.isCongruent
    ; cong₂       = congruent ≈₃ ≈₂ ≈₁ G.cong₂ F.cong₂
    ; inverseʳ    = inverseʳ ≈₁ ≈₂ ≈₃ _ g⁻¹ F.Eq₁.trans F.cong₂ F.inverseʳ G.inverseʳ
    } where module F = IsRightInverse f-invʳ; module G = IsRightInverse g-invʳ

  isInverse : IsInverse ≈₁ ≈₂ f f⁻¹  IsInverse ≈₂ ≈₃ g g⁻¹ 
              IsInverse ≈₁ ≈₃ (g  f) (f⁻¹  g⁻¹)
  isInverse f-inv g-inv = record
    { isLeftInverse = isLeftInverse F.isLeftInverse G.isLeftInverse
    ; inverseʳ      = inverseʳ ≈₁ ≈₂ ≈₃ _ g⁻¹ F.Eq₁.trans F.cong₂ F.inverseʳ G.inverseʳ
    } where module F = IsInverse f-inv; module G = IsInverse g-inv

------------------------------------------------------------------------
-- Setoid bundles

module _ {R : Setoid a ℓ₁} {S : Setoid b ℓ₂} {T : Setoid c ℓ₃} where

  open Setoid renaming (_≈_ to )

  function : Func R S  Func S T  Func R T
  function f g = record
    { f    = G.f  F.f
    ; cong = congruent ( R) ( S) ( T) F.cong G.cong
    } where module F = Func f; module G = Func g

  injection : Injection R S  Injection S T  Injection R T
  injection inj₁ inj₂ = record
    { f         = G.f  F.f
    ; cong      = congruent ( R) ( S) ( T) F.cong G.cong
    ; injective = injective ( R) ( S) ( T) F.injective G.injective
    } where module F = Injection inj₁; module G = Injection inj₂

  surjection : Surjection R S  Surjection S T  Surjection R T
  surjection surj₁ surj₂ = record
    { f          = G.f  F.f
    ; cong       = congruent ( R) ( S) ( T) F.cong G.cong
    ; surjective = surjective ( R) ( S) ( T) G.Eq₂.trans G.cong F.surjective G.surjective
    } where module F = Surjection surj₁; module G = Surjection surj₂

  bijection : Bijection R S  Bijection S T  Bijection R T
  bijection bij₁ bij₂ = record
    { f         = G.f  F.f
    ; cong      = congruent ( R) ( S) ( T) F.cong G.cong
    ; bijective = bijective ( R) ( S) ( T) (trans T) G.cong F.bijective G.bijective
    } where module F = Bijection bij₁; module G = Bijection bij₂

  equivalence : Equivalence R S  Equivalence S T  Equivalence R T
  equivalence equiv₁ equiv₂ = record
    { f        = G.f  F.f
    ; g        = F.g  G.g
    ; cong₁    = congruent ( R) ( S) ( T) F.cong₁ G.cong₁
    ; cong₂    = congruent ( T) ( S) ( R) G.cong₂ F.cong₂
    } where module F = Equivalence equiv₁; module G = Equivalence equiv₂

  leftInverse : LeftInverse R S  LeftInverse S T  LeftInverse R T
  leftInverse invˡ₁ invˡ₂ = record
    { f        = G.f  F.f
    ; g        = F.g  G.g
    ; cong₁    = congruent ( R) ( S) ( T) F.cong₁ G.cong₁
    ; cong₂    = congruent ( T) ( S) ( R) G.cong₂ F.cong₂
    ; inverseˡ = inverseˡ ( R) ( S) ( T) F.f _ (trans T) G.cong₁ F.inverseˡ G.inverseˡ
    } where module F = LeftInverse invˡ₁; module G = LeftInverse invˡ₂

  rightInverse : RightInverse R S  RightInverse S T  RightInverse R T
  rightInverse invʳ₁ invʳ₂ = record
    { f        = G.f  F.f
    ; g        = F.g  G.g
    ; cong₁    = congruent ( R) ( S) ( T) F.cong₁ G.cong₁
    ; cong₂    = congruent ( T) ( S) ( R) G.cong₂ F.cong₂
    ; inverseʳ = inverseʳ ( R) ( S) ( T) _ G.g (trans R) F.cong₂ F.inverseʳ G.inverseʳ
    } where module F = RightInverse invʳ₁; module G = RightInverse invʳ₂

  inverse : Inverse R S  Inverse S T  Inverse R T
  inverse inv₁ inv₂ = record
    { f       = G.f  F.f
    ; f⁻¹     = F.f⁻¹  G.f⁻¹
    ; cong₁   = congruent ( R) ( S) ( T) F.cong₁ G.cong₁
    ; cong₂   = congruent ( T) ( S) ( R) G.cong₂ F.cong₂
    ; inverse = inverseᵇ ( R) ( S) ( T) _ G.f⁻¹ (trans R) (trans T) G.cong₁ F.cong₂ F.inverse G.inverse
    } where module F = Inverse inv₁; module G = Inverse inv₂

------------------------------------------------------------------------
-- Propositional bundles

infix 8 _∘-⟶_ _∘-↣_ _∘-↠_ _∘-⤖_ _∘-⇔_ _∘-↩_ _∘-↪_ _∘-↔_

_∘-⟶_ : (A  B)  (B  C)  (A  C)
_∘-⟶_ = function

_∘-↣_ : A  B  B  C  A  C
_∘-↣_ = injection

_∘-↠_ : A  B  B  C  A  C
_∘-↠_ = surjection

_∘-⤖_ : A  B  B  C  A  C
_∘-⤖_ = bijection

_∘-⇔_ : A  B  B  C  A  C
_∘-⇔_ = equivalence

_∘-↩_ : A  B  B  C  A  C
_∘-↩_ = leftInverse

_∘-↪_ : A  B  B  C  A  C
_∘-↪_ = rightInverse

_∘-↔_ : A  B  B  C  A  C
_∘-↔_ = inverse