{-# OPTIONS --safe #-}
module Cubical.Relation.Binary.Order.Poset.Mappings where

open import Cubical.Foundations.Equiv
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Structure
open import Cubical.Foundations.Univalence

open import Cubical.Algebra.Semigroup

open import Cubical.Data.Sigma

open import Cubical.Functions.Embedding
open import Cubical.Functions.Image
open import Cubical.Functions.Logic using (_⊔′_)
open import Cubical.Functions.Preimage

open import Cubical.Reflection.RecordEquiv

open import Cubical.HITs.PropositionalTruncation as ∥₁
open import Cubical.HITs.SetQuotients

open import Cubical.Relation.Binary.Order.Poset.Base
open import Cubical.Relation.Binary.Order.Poset.Properties
open import Cubical.Relation.Binary.Order.Poset.Subset
open import Cubical.Relation.Binary.Order.Poset.Instances.Embedding
open import Cubical.Relation.Binary.Order.Proset
open import Cubical.Relation.Binary.Base


private
  variable
     ℓ' ℓ'' ℓ''' ℓ₀ ℓ₀' ℓ₁ ℓ₁' ℓ₂ ℓ₂' : Level

record IsIsotone {A : Type ℓ₀} {B : Type ℓ₁}
  (M : PosetStr ℓ₀' A) (f : A  B) (N : PosetStr ℓ₁' B)
  : Type (ℓ-max (ℓ-max ℓ₀ ℓ₀') ℓ₁')
  where
  -- Shorter qualified names
  private
    module M = PosetStr M
    module N = PosetStr N

  field
    pres≤ : (x y : A)  x M.≤ y  f x N.≤ f y

unquoteDecl IsIsotoneIsoΣ = declareRecordIsoΣ IsIsotoneIsoΣ (quote IsIsotone)

isPropIsIsotone : {A : Type ℓ₀} {B : Type ℓ₁}
                  (M : PosetStr ℓ₀' A) (f : A  B) (N : PosetStr ℓ₁' B)
                 isProp (IsIsotone M f N)
isPropIsIsotone M f N = isOfHLevelRetractFromIso 1 IsIsotoneIsoΣ
  (isPropΠ3 λ x y _  IsPoset.is-prop-valued (PosetStr.isPoset N) (f x) (f y))

IsIsotone-∘ : {A : Type ℓ₀} {B : Type ℓ₁} {C : Type ℓ₂}
             (M : PosetStr ℓ₀' A) (f : A  B)
              (N : PosetStr ℓ₁' B) (g : B  C) (O : PosetStr ℓ₂' C)
             IsIsotone M f N
             IsIsotone N g O
             IsIsotone M (g  f) O
IsIsotone.pres≤ (IsIsotone-∘ M f N g O isf isg) x y x≤y
  = IsIsotone.pres≤ isg (f x) (f y) (IsIsotone.pres≤ isf x y x≤y)

record IsAntitone {A : Type ℓ₀} {B : Type ℓ₁}
  (M : PosetStr ℓ₀' A) (f : A  B) (N : PosetStr ℓ₁' B)
  : Type (ℓ-max (ℓ-max ℓ₀ ℓ₀') ℓ₁')
  where
  constructor
    isantitone
  -- Shorter qualified names
  private
    module M = PosetStr M
    module N = PosetStr N

  field
    inv≤ : (x y : A)  x M.≤ y  f y N.≤ f x

unquoteDecl IsAntitoneIsoΣ = declareRecordIsoΣ IsAntitoneIsoΣ (quote IsAntitone)

isPropIsAntitone : {A : Type ℓ₀} {B : Type ℓ₁}
                   (M : PosetStr ℓ₀' A) (f : A  B) (N : PosetStr ℓ₁' B)
                  isProp (IsAntitone M f N)
isPropIsAntitone M f N = isOfHLevelRetractFromIso 1 IsAntitoneIsoΣ
  (isPropΠ3 λ x y _  IsPoset.is-prop-valued (PosetStr.isPoset N) (f y) (f x))

module _
  (A : Poset ℓ₀ ℓ₀')
  (B : Poset ℓ₁ ℓ₁')
  (f :  A    B )
  where

    DualIsotone : IsIsotone (snd A) f (snd B)
                 IsAntitone (snd (DualPoset A)) f (snd B)
    IsAntitone.inv≤ (DualIsotone is) x y = IsIsotone.pres≤ is y x

    DualIsotone' : IsIsotone (snd (DualPoset A)) f (snd B)
                  IsAntitone (snd A) f (snd B)
    IsAntitone.inv≤ (DualIsotone' is) x y = IsIsotone.pres≤ is y x

    IsotoneDual : IsIsotone (snd A) f (snd B)
                 IsAntitone (snd A) f (snd (DualPoset B))
    IsAntitone.inv≤ (IsotoneDual is) = IsIsotone.pres≤ is

    IsotoneDual' : IsIsotone (snd A) f (snd (DualPoset B))
                  IsAntitone (snd A) f (snd B)
    IsAntitone.inv≤ (IsotoneDual' is) = IsIsotone.pres≤ is

    DualAntitone : IsAntitone (snd A) f (snd B)
                  IsIsotone (snd (DualPoset A)) f (snd B)
    IsIsotone.pres≤ (DualAntitone is) x y = IsAntitone.inv≤ is y x

    DualAntitone' : IsAntitone (snd (DualPoset A)) f (snd B)
                   IsIsotone (snd A) f (snd B)
    IsIsotone.pres≤ (DualAntitone' is) x y = IsAntitone.inv≤ is y x

    AntitoneDual : IsAntitone (snd A) f (snd B)
                  IsIsotone (snd A) f (snd (DualPoset B))
    IsIsotone.pres≤ (AntitoneDual is) = IsAntitone.inv≤ is

    AntitoneDual' : IsAntitone (snd A) f (snd (DualPoset B))
                   IsIsotone (snd A) f (snd B)
    IsIsotone.pres≤ (AntitoneDual' is) = IsAntitone.inv≤ is

    DualIsotoneDual : IsIsotone (snd A) f (snd B)
                     IsIsotone (snd (DualPoset A)) f (snd (DualPoset B))
    IsIsotone.pres≤ (DualIsotoneDual is) x y = IsIsotone.pres≤ is y x

    DualIsotoneDual' : IsIsotone (snd (DualPoset A)) f (snd (DualPoset B))
                      IsIsotone (snd A) f (snd B)
    IsIsotone.pres≤ (DualIsotoneDual' is) x y = IsIsotone.pres≤ is y x

    DualAntitoneDual : IsAntitone (snd A) f (snd B)
                      IsAntitone (snd (DualPoset A)) f (snd (DualPoset B))
    IsAntitone.inv≤ (DualAntitoneDual is) x y = IsAntitone.inv≤ is y x

    DualAntitoneDual' : IsAntitone (snd (DualPoset A)) f (snd (DualPoset B))
                       IsAntitone (snd A) f (snd B)
    IsAntitone.inv≤ (DualAntitoneDual' is) x y = IsAntitone.inv≤ is y x

IsPosetEquiv→IsIsotone : (P S : Poset  ℓ')
                         (e :  P    S )
                        IsPosetEquiv (snd P) e (snd S)
                        IsIsotone (snd P) (equivFun e) (snd S)
IsIsotone.pres≤ (IsPosetEquiv→IsIsotone P S e eq) x y = equivFun (IsPosetEquiv.pres≤ eq x y)

-- Isotone maps are characterized by their actions on down-sets and up-sets
module _
  {P : Poset ℓ₀ ℓ₀'}
  {S : Poset ℓ₁ ℓ₁'}
  (f :  P    S )
  where
    private
      isP = PosetStr.isPoset (snd P)
      isS = PosetStr.isPoset (snd S)

      _≤P_ = PosetStr._≤_ (snd P)
      _≤S_ = PosetStr._≤_ (snd S)

      propS = IsPoset.is-prop-valued isS
      rflS = IsPoset.is-refl isS
      transS = IsPoset.is-trans isS

    IsIsotone→PreimagePrincipalDownsetIsDownset : IsIsotone (snd P) f (snd S)
                                                  y  isDownset P (f  (principalDownset S y))
    IsIsotone→PreimagePrincipalDownsetIsDownset is y (x , inPrex) z z≤x
      = ∥₁.rec (isEmbedding→hasPropFibers (preimageInclusion f (principalDownset S y) .snd) z)
                { ((b , b≤y) , fibb) 
                    (z ,  (f z , transS (f z) (f x) y
                                         (IsIsotone.pres≤ is z x z≤x)
                                         (subst (_≤S y) fibb b≤y)) ,
                                          refl ∣₁) ,
                     refl }) inPrex

    IsIsotone→PreimagePrincipalUpsetIsUpset : IsIsotone (snd P) f (snd S)
                                              y  isUpset P (f  (principalUpset S y))
    IsIsotone→PreimagePrincipalUpsetIsUpset is y (x , inPrex) z x≤z
      = ∥₁.rec (isEmbedding→hasPropFibers (preimageInclusion f (principalUpset S y) .snd) z)
                { ((b , y≤b) , fibb) 
                    (z ,  (f z , transS y (f x) (f z)
                                        (subst (y ≤S_) fibb y≤b)
                                        (IsIsotone.pres≤ is x z x≤z)) ,
                                         refl ∣₁) ,
                     refl }) inPrex

    PreimagePrincipalDownsetIsDownset→IsIsotone : (∀ x  isDownset P (f  (principalDownset S x)))
                                                 IsIsotone (snd P) f (snd S)
    IsIsotone.pres≤ (PreimagePrincipalDownsetIsDownset→IsIsotone down) y x y≤x
      = ∥₁.rec (propS _ _)  { ((b , b≤fx) , fibb)  subst (_≤S f x) (fibb  cong f fiba) b≤fx }) pre
        where fib = down (f x) (x ,  ((f x) , (rflS (f x))) , refl ∣₁) y y≤x

              pre = fib .fst .snd
              fiba = fib .snd

    PreimagePrincipalUpsetIsUpset→IsIsotone : (∀ x  isUpset P (f  (principalUpset S x)))
                                             IsIsotone (snd P) f (snd S)
    IsIsotone.pres≤ (PreimagePrincipalUpsetIsUpset→IsIsotone up) x y x≤y
      = ∥₁.rec (propS _ _)  { ((b , fx≤b) , fibb)  subst (f x ≤S_) (fibb  cong f fiba) fx≤b }) pre
        where fib = up (f x) (x ,  ((f x) , (rflS (f x))) , refl ∣₁) y x≤y

              pre = fib .fst .snd
              fiba = fib .snd

-- The next part requires our posets to operate over the same universes
module _
  (P S : Poset  ℓ')
  (f :  P    S )
  where
    private
      isP = PosetStr.isPoset (snd P)
      isS = PosetStr.isPoset (snd S)

      _≤P_ = PosetStr._≤_ (snd P)
      _≤S_ = PosetStr._≤_ (snd S)

      propP = IsPoset.is-prop-valued isP
      rflP = IsPoset.is-refl isP
      antiP = IsPoset.is-antisym isP
      transP = IsPoset.is-trans isP

      propS = IsPoset.is-prop-valued isS
      rflS = IsPoset.is-refl isS
      antiS = IsPoset.is-antisym isS
      transS = IsPoset.is-trans isS

    -- We can now define the type of residuated maps
    isResiduated : Type _
    isResiduated =  y  isPrincipalDownset P (f  (principalDownset S y))

    hasResidual : Type (ℓ-max  ℓ')
    hasResidual = (IsIsotone (snd P) f (snd S)) ×
                   (Σ[ g  ( S    P ) ] (IsIsotone (snd S) g (snd P) ×
                                             (∀ x  x ≤P (g  f) x) ×
                                             (∀ x  (f  g) x ≤S x)))

    isResiduated→hasResidual : isResiduated
                              hasResidual
    isResiduated→hasResidual down = isotonef , g , isotoneg , g∘f , f∘g
      where isotonef : IsIsotone (snd P) f (snd S)
            isotonef = PreimagePrincipalDownsetIsDownset→IsIsotone f
                       λ x  isPrincipalDownset→isDownset P (f  principalDownset S x) (down x)

            isotonef⃖ :  x y  x ≤S y  (f  (principalDownset S x)) ⊆ₑ (f  (principalDownset S y))
            isotonef⃖ x y x≤y z ((a , pre) , fiba)
              = ∥₁.rec (isProp∈ₑ z (f  principalDownset S y))
                        { ((b , b≤x) , fibb)  (a ,  (b , (transS b x y b≤x x≤y)) , fibb ∣₁) , fiba }) pre

            g :  S    P 
            g x = down x .fst

            isotoneg : IsIsotone (snd S) g (snd P)
            IsIsotone.pres≤ isotoneg x y x≤y
              = invEq
                  (principalDownsetMembership P (g x) (g y))
                  (subst
                    (g x ∈ₑ_)
                    (down y .snd)
                    (isotonef⃖ x y x≤y (g x)
                      (subst (g x ∈ₑ_)
                        (sym (down x .snd))
                        (equivFun (principalDownsetMembership P (g x) (g x)) (rflP (g x))))))

            g∘f :  x  x ≤P (g  f) x
            g∘f x = invEq (principalDownsetMembership P x (g (f x)))
                          (subst (x ∈ₑ_) (down (f x) .snd)
                                 ((x ,  ((f x) , (rflS (f x))) , refl ∣₁) , refl))

            f∘g :  y  (f  g) y ≤S y
            f∘g y = ∥₁.rec (propS _ _)
                            { ((a , a≤y) , fib) 
                                subst (_≤S y) (fib  cong f (gy∈pre .snd)) a≤y })
                           (gy∈pre .fst .snd)
              where gy∈pre : g y ∈ₑ (f  (principalDownset S y))
                    gy∈pre = subst (g y ∈ₑ_) (sym (down y .snd))
                                   (equivFun (principalDownsetMembership P (g y) (g y)) (rflP (g y)))

    hasResidual→isResiduated : hasResidual
                              isResiduated
    hasResidual→isResiduated (isf , g , isg , g∘f , f∘g) y
      = (g y) , (equivFun (EmbeddingIP _ _)
                ((λ x ((a , pre) , fiba) 
                  ∥₁.rec (isProp∈ₑ x (principalDownset P (g y)))
                                      { ((b , b≤y) , fibb) 
                                          equivFun (principalDownsetMembership P x (g y))
                                                   (transP x (g (f x)) (g y) (g∘f x)
                                                     (IsIsotone.pres≤ isg (f x) y
                                                       (subst (_≤S y)
                                                         (fibb  cong f fiba) b≤y))) }) pre) ,
                  λ x x∈g  (x ,  ((f x) ,
                                   (transS (f x) (f (g y)) y
                                     (IsIsotone.pres≤ isf x (g y)
                                       (invEq (principalDownsetMembership P x (g y)) x∈g))
                                     (f∘g y))) , refl ∣₁) , refl))

    isPropIsResiduated : isProp isResiduated
    isPropIsResiduated = isPropΠ λ _  isPropIsPrincipalDownset P _

    residualUnique : (p q : hasResidual)
                    p .snd .fst  q .snd .fst
    residualUnique (isf₀ , g  , isg  , g∘f  , f∘g)
                   (isf₁ , g* , isg* , g*∘f , f∘g*)
                   = funExt λ x  antiP (g x) (g* x) (g≤g* x) (g*≤g x)
                   where g≤g* :  x  g x ≤P g* x
                         g≤g* x = transP (g x) ((g*  f) (g x)) (g* x) (g*∘f (g x))
                                          (IsIsotone.pres≤ isg* (f (g x)) x (f∘g x))

                         g*≤g :  x  g* x ≤P g x
                         g*≤g x = transP (g* x) ((g  f) (g* x)) (g x) (g∘f (g* x))
                                         (IsIsotone.pres≤ isg (f (g* x)) x (f∘g* x))

    isPropHasResidual : isProp hasResidual
    isPropHasResidual p q = ≡-× (isPropIsIsotone _ f _ _ _)
                                 (Σ≡Prop  g  isProp× (isPropIsIsotone _ g _)
                                                (isProp× (isPropΠ  x  propP x (g (f x))))
                                                         (isPropΠ λ x  propS (f (g x)) x)))
                                          (residualUnique p q))

    residual : hasResidual   S    P 
    residual (_ , g , _) = g

    AbsorbResidual : (res : hasResidual)
                    f  (residual res)  f  f
    AbsorbResidual (isf , f⁺ , _ , f⁺∘f , f∘f⁺)
      = funExt λ x  antiS ((f  f⁺  f) x) (f x)
                           (f∘f⁺ (f x))
                           (IsIsotone.pres≤ isf x (f⁺ (f x)) (f⁺∘f x))

    ResidualAbsorb : (res : hasResidual)
                    (residual res)  f  (residual res)  (residual res)
    ResidualAbsorb (_ , f⁺ , isf⁺ , f⁺∘f , f∘f⁺)
      = funExt λ x  antiP ((f⁺  f  f⁺) x) (f⁺ x)
                           (IsIsotone.pres≤ isf⁺ (f (f⁺ x)) x (f∘f⁺ x))
                           (f⁺∘f (f⁺ x))

isResidual : (P S : Poset  ℓ')
            (f⁺ :  S    P )
            Type (ℓ-max  ℓ')
isResidual P S f⁺ = Σ[ f  ( P    S ) ] (Σ[ res  hasResidual P S f ] f⁺  residual P S f res)

isResidualOfUnique : (P S : Poset  ℓ')
                    (f⁺ :  S    P )
                    (p q : isResidual P S f⁺)
                    p .fst  q .fst
isResidualOfUnique P S h (f , (isf , f⁺ , isf⁺ , f⁺∘f , f∘f⁺) , h≡f⁺)
                         (g , (isg , g⁺ , isg⁺ , g⁺∘g , g∘g⁺) , h≡g⁺)
                   = funExt λ x  anti (f x) (g x)
                                 (trans (f x) (f (f⁺ (g x))) (g x)
                                        (IsIsotone.pres≤ isf x (f⁺ (g x))
                                          (subst (x ≤_) (sym (p (g x))) (g⁺∘g x)))
                                        (f∘f⁺ (g x)))
                                  (trans (g x) (g (g⁺ (f x))) (f x)
                                         (IsIsotone.pres≤ isg x (g⁺ (f x))
                                           (subst (x ≤_) (p (f x)) (f⁺∘f x)))
                                         (g∘g⁺ (f x)))
                   where _≤_ = PosetStr._≤_ (snd P)
                         anti = IsPoset.is-antisym (PosetStr.isPoset (snd S))
                         trans = IsPoset.is-trans (PosetStr.isPoset (snd S))
                         p = funExt⁻ ((sym h≡f⁺)  h≡g⁺)

isPropIsResidual : (P S : Poset  ℓ')
                  (f⁺ :  S    P )
                  isProp (isResidual P S f⁺)
isPropIsResidual P S f⁺ p q
  = Σ≡Prop  f  isPropΣ (isPropHasResidual P S f)
                            λ _  isSet→ (IsPoset.is-set (PosetStr.isPoset (snd P))) _ _)
                           (isResidualOfUnique P S f⁺ p q)

hasResidual-∘ : (E F G : Poset  ℓ')
               (f :  E    F )
               (g :  F    G )
               hasResidual E F f
               hasResidual F G g
               hasResidual E G (g  f)
hasResidual-∘ E F G f g (isf , f⁺ , isf⁺ , f⁺∘f , f∘f⁺) (isg , g⁺ , isg⁺ , g⁺∘g , g∘g⁺)
  = is , f⁺  g⁺ , is⁺ , ⁺∘ , ∘⁺
  where _≤E_ = PosetStr._≤_ (snd E)
        _≤G_ = PosetStr._≤_ (snd G)

        transE = IsPoset.is-trans (PosetStr.isPoset (snd E))
        transG = IsPoset.is-trans (PosetStr.isPoset (snd G))

        is : IsIsotone (snd E) (g  f) (snd G)
        is = IsIsotone-∘ (snd E) f (snd F) g (snd G) isf isg

        is⁺ : IsIsotone (snd G) (f⁺  g⁺) (snd E)
        is⁺ = IsIsotone-∘ (snd G) g⁺ (snd F) f⁺ (snd E) isg⁺ isf⁺

        ⁺∘ :  x  x ≤E ((f⁺  g⁺)  (g  f)) x
        ⁺∘ x = transE x ((f⁺  f) x) (((f⁺  g⁺)  g  f) x)
                      (f⁺∘f x)
                      (IsIsotone.pres≤ isf⁺ (f x) (g⁺ (g (f x))) (g⁺∘g (f x)))

        ∘⁺ :  x  ((g  f)  (f⁺  g⁺)) x ≤G x
        ∘⁺ x = transG (((g  f)  f⁺  g⁺) x) ((g  g⁺) x) x
                      (IsIsotone.pres≤ isg (f (f⁺ (g⁺ x))) (g⁺ x) (f∘f⁺ (g⁺ x)))
                      (g∘g⁺ x)

isResidual-∘ : (E F G : Poset  ℓ')
              (f⁺ :  F    E )
              (g⁺ :  G    F )
              isResidual E F f⁺
              isResidual F G g⁺
              isResidual E G (f⁺  g⁺)
isResidual-∘ E F G f⁺ g⁺ (f , resf , f⁺≡f*)
                         (g , resg , g⁺≡g*)
             = (g  f) ,
               (hasResidual-∘ E F G f g resf resg) ,
               (funExt  x  cong f⁺ (funExt⁻ g⁺≡g* x)  funExt⁻ f⁺≡f* _))

EqualResidual→Involution : (P : Poset  ℓ')
                          (f :  P    P )
                          (res : hasResidual P P f)
                          f  (residual P P f res)
                          f  f  idfun  P 
EqualResidual→Involution P f (isf , f⁺ , isf⁺ , f⁺∘f , f∘f⁺) f≡f⁺
  = funExt λ x  anti (f (f x)) x
                      (subst  g  (f  g) x  x) (sym f≡f⁺) (f∘f⁺ x))
                      (subst  g  x  (g  f) x) (sym f≡f⁺) (f⁺∘f x))
  where pos = PosetStr.isPoset (snd P)
        _≤_ = PosetStr._≤_ (snd P)
        anti = IsPoset.is-antisym pos

Involution→EqualResidual : (P : Poset  ℓ')
                          (f :  P    P )
                          (res : hasResidual P P f)
                          f  f  idfun  P 
                          f  (residual P P f res)
Involution→EqualResidual P f res inv
  = sym (cong₂  g h  g  f⁺  h) (sym inv) (sym inv) 
         cong  g  f  g  f) (AbsorbResidual P P f res) 
         cong (f ∘_) inv)
  where f⁺ = res .snd .fst

Res : Poset  ℓ'  Semigroup (ℓ-max  ℓ')
fst (Res E) = Σ[ f  ( E    E ) ] hasResidual E E f
SemigroupStr._·_ (snd (Res E)) (f , resf) (g , resg)
  = (g  f) , (hasResidual-∘ E E E f g resf resg)
IsSemigroup.is-set (SemigroupStr.isSemigroup (snd (Res E)))
  = isSetΣ (isSet→ (IsPoset.is-set (PosetStr.isPoset (snd E))))
      λ f  isProp→isSet (isPropHasResidual E E f)
IsSemigroup.·Assoc (SemigroupStr.isSemigroup (snd (Res E))) (f , _) (g , _) (h , _)
  = Σ≡Prop  f  isPropHasResidual E E f) refl

Res⁺ : Poset  ℓ'  Semigroup (ℓ-max  ℓ')
fst (Res⁺ E) = Σ[ f⁺  ( E    E ) ] isResidual E E f⁺
SemigroupStr._·_ (snd (Res⁺ E)) (f⁺ , isresf⁺) (g⁺ , isresg⁺)
  = (f⁺  g⁺) , isResidual-∘ E E E f⁺ g⁺ isresf⁺ isresg⁺
IsSemigroup.is-set (SemigroupStr.isSemigroup (snd (Res⁺ E)))
  = isSetΣ (isSet→ (IsPoset.is-set (PosetStr.isPoset (snd E))))
      λ f⁺  isProp→isSet (isPropIsResidual E E f⁺)
IsSemigroup.·Assoc (SemigroupStr.isSemigroup (snd (Res⁺ E))) (f⁺ , _) (g⁺ , _) (h⁺ , _)
  = Σ≡Prop  f⁺  isPropIsResidual E E f⁺) refl

isClosure : (E : Poset  ℓ')
            (f :  E    E )
           Type (ℓ-max  ℓ')
isClosure E f = IsIsotone (snd E) f (snd E) × (f  f  f) × (∀ x  x  f x)
  where _≤_ = PosetStr._≤_ (snd E)

isDualClosure : (E : Poset  ℓ')
                (f :  E    E )
               Type (ℓ-max  ℓ')
isDualClosure E f = IsIsotone (snd E) f (snd E) × (f  f  f) × (∀ x  f x  x)
  where _≤_ = PosetStr._≤_ (snd E)

-- This can be made more succinct
isClosure' : (E : Poset  ℓ')
             (f :  E    E )
            Type (ℓ-max  ℓ')
isClosure' E f =  x y  x  f y  f x  f y
  where _≤_ = PosetStr._≤_ (snd E)

isDualClosure' : (E : Poset  ℓ')
                 (f :  E    E )
                Type (ℓ-max  ℓ')
isDualClosure' E f =  x y  f x  y  f x  f y
  where _≤_ = PosetStr._≤_ (snd E)

isClosure→isClosure' : (E : Poset  ℓ')
                       f
                      isClosure E f
                      isClosure' E f
isClosure→isClosure' E f (isf , f≡f∘f , x≤fx) x y
  = propBiimpl→Equiv (prop _ _) (prop _ _)
                      x≤fy  subst (f x ≤_) (sym (funExt⁻ f≡f∘f y))
                                     (IsIsotone.pres≤ isf x (f y) x≤fy))
                     (trans x (f x) (f y) (x≤fx x))
  where _≤_ = PosetStr._≤_ (snd E)
        is = PosetStr.isPoset (snd E)

        prop = IsPoset.is-prop-valued is
        trans = IsPoset.is-trans is

isDualClosure→isDualClosure' : (E : Poset  ℓ')
                               f
                              isDualClosure E f
                              isDualClosure' E f
isDualClosure→isDualClosure' E f (isf , f≡f∘f , fx≤x) x y
  = propBiimpl→Equiv (prop _ _) (prop _ _)
                      fx≤y  subst (_≤ f y) (sym (funExt⁻ f≡f∘f x))
                                     (IsIsotone.pres≤ isf (f x) y fx≤y))
                      λ fx≤fy  trans (f x) (f y) y fx≤fy (fx≤x y)
  where _≤_ = PosetStr._≤_ (snd E)
        is = PosetStr.isPoset (snd E)

        prop = IsPoset.is-prop-valued is
        trans = IsPoset.is-trans is

isClosure'→isClosure : (E : Poset  ℓ')
                       f
                      isClosure' E f
                      isClosure E f
isClosure'→isClosure E f eq
  = isf ,
   (funExt λ x  anti (f x) (f (f x))
                      (IsIsotone.pres≤ isf x (f x) (x≤fx x))
                      (equivFun (eq (f x) x) (rfl (f x)))) ,
    x≤fx
  where _≤_ = PosetStr._≤_ (snd E)
        is = PosetStr.isPoset (snd E)

        rfl = IsPoset.is-refl is
        anti = IsPoset.is-antisym is
        trans = IsPoset.is-trans is

        x≤fx :  x  x  f x
        x≤fx x = invEq (eq x x) (rfl (f x))

        isf : IsIsotone (snd E) f (snd E)
        IsIsotone.pres≤ isf x y x≤y
          = equivFun (eq x y)
                     (trans x y (f y) x≤y (x≤fx y))

isDualClosure'→isDualClosure : (E : Poset  ℓ')
                               f
                              isDualClosure' E f
                              isDualClosure E f
isDualClosure'→isDualClosure E f eq
  = isf ,
    (funExt  x  anti (f x) (f (f x))
                        (equivFun (eq x (f x)) (rfl (f x)))
                        (IsIsotone.pres≤ isf (f x) x (fx≤x x)))) ,
    fx≤x
  where _≤_ = PosetStr._≤_ (snd E)
        is = PosetStr.isPoset (snd E)

        rfl = IsPoset.is-refl is
        anti = IsPoset.is-antisym is
        trans = IsPoset.is-trans is

        fx≤x :  x  f x  x
        fx≤x x = invEq (eq x x) (rfl (f x))

        isf : IsIsotone (snd E) f (snd E)
        IsIsotone.pres≤ isf x y x≤y
          = equivFun (eq x y)
                     (trans (f x) x y (fx≤x x) x≤y)

isClosure→ComposedResidual : {E : Poset  ℓ'}
                             {f :  E    E }
                            isClosure E f
                            Σ[ F  Poset  ℓ' ] (Σ[ g  ( E    F ) ] (Σ[ res  hasResidual E F g ] f  (residual E F g res)  g))
isClosure→ComposedResidual {} {ℓ'} {E = E} {f = f} (isf , f≡f∘f , x≤fx) = F ,  , (is♮ , ♮⁺ , is♮⁺ , x≤fx , ♮∘♮⁺) , refl
  where _≤_ = PosetStr._≤_ (snd E)
        is = PosetStr.isPoset (snd E)
        set = IsPoset.is-set is
        prop = IsPoset.is-prop-valued is
        rfl = IsPoset.is-refl is
        anti = IsPoset.is-antisym is
        trans = IsPoset.is-trans is

        kerf : Rel  E   E  
        kerf x y = f x  f y

        F' =  E  / kerf

        _⊑'_ : F'  F'  hProp _
        _⊑'_ = fun
          where
            fun₀ :  E   F'  hProp _
            fst (fun₀ x [ y ]) = f x  f y
            snd (fun₀ x [ y ]) = prop (f x) (f y)
            fun₀ x (eq/ a b fa≡fb i) = record
              { fst = cong (f x ≤_) fa≡fb i
              ; snd = isProp→PathP  i  isPropIsProp {A = cong (f x ≤_) fa≡fb i})
                                   (prop (f x) (f a)) (prop (f x) (f b)) i
              }
            fun₀ x (squash/ a b p q i j) = isSet→SquareP  _ _  isSetHProp)
               _  fun₀ x a)
               _  fun₀ x b)
               i  fun₀ x (p i))
               i  fun₀ x (q i)) j i

            toPath :  a b (p : kerf a b) (y : F')  fun₀ a y  fun₀ b y
            toPath a b fa≡fb = elimProp  _  isSetHProp _ _) λ c 
              Σ≡Prop  _  isPropIsProp) (cong (_≤ f c) fa≡fb)

            fun : F'  F'  hProp _
            fun [ a ] y = fun₀ a y
            fun (eq/ a b fa≡fb i) y = toPath a b fa≡fb y i
            fun (squash/ x y p q i j) z = isSet→SquareP  _ _  isSetHProp)
               _  fun x z)  _  fun y z)  i  fun (p i) z)  i  fun (q i) z) j i

        _⊑_ : Rel F' F' ℓ'
        a  b = (a ⊑' b) .fst

        open BinaryRelation _⊑_

        isProp⊑ : isPropValued
        isProp⊑ a b = (a ⊑' b) .snd

        isRefl⊑ : isRefl
        isRefl⊑ = elimProp  x  isProp⊑ x x)
                           (rfl  f)

        isAntisym⊑ : isAntisym
        isAntisym⊑ = elimProp2  x y  isPropΠ2 λ _ _  squash/ x y)
                                λ a b fa≤fb fb≤fa  eq/ a b (anti (f a) (f b) fa≤fb fb≤fa)

        isTrans⊑ : isTrans
        isTrans⊑ = elimProp3  x _ z  isPropΠ2 λ _ _  isProp⊑ x z)
                              λ a b c  trans (f a) (f b) (f c)

        poset⊑ : IsPoset _⊑_
        poset⊑ = isposet squash/ isProp⊑ isRefl⊑ isTrans⊑ isAntisym⊑

        F : Poset  ℓ'
        F = F' , (posetstr _⊑_ poset⊑)

         :  E    F 
         = [_]

        is♮ : IsIsotone (snd E)  (snd F)
        IsIsotone.pres≤ is♮ x y x≤y = IsIsotone.pres≤ isf x y x≤y

        ♮⁺ :  F    E 
        ♮⁺ [ x ] = f x
        ♮⁺ (eq/ a b fa≡fb i) = fa≡fb i
        ♮⁺ (squash/ x y p q i j) = isSet→SquareP  _ _  set)
           _  ♮⁺ x)
           _  ♮⁺ y)
           i  ♮⁺ (p i))
           i  ♮⁺ (q i)) j i

        is♮⁺ : IsIsotone (snd F) ♮⁺ (snd E)
        IsIsotone.pres≤ is♮⁺ = elimProp2  x y  isPropΠ λ _  prop (♮⁺ x) (♮⁺ y))
                                          λ x y fx≤fy  fx≤fy

        ♮∘♮⁺ :  x  (  ♮⁺) x  x
        ♮∘♮⁺ = elimProp  x  isProp⊑ ((  ♮⁺) x) x)
                        λ x  subst (_≤ f x) (funExt⁻ f≡f∘f x) (rfl (f x))

isDualClosure→ComposedResidual : {E : Poset  ℓ'}
                                 {f :  E    E }
                                isDualClosure E f
                                Σ[ F  Poset  ℓ' ] (Σ[ g  ( F    E ) ] (Σ[ res  hasResidual F E g ] f  g  (residual F E g res)))
isDualClosure→ComposedResidual {} {ℓ'} {E = E} {f = f} (isf , f≡f∘f , fx≤x) = F ,  , (is♮ , ♮⁺ , is♮⁺ , ♮⁺∘♮ , fx≤x) , refl
  where _≤_ = PosetStr._≤_ (snd E)
        is = PosetStr.isPoset (snd E)
        set = IsPoset.is-set is
        prop = IsPoset.is-prop-valued is
        rfl = IsPoset.is-refl is
        anti = IsPoset.is-antisym is
        trans = IsPoset.is-trans is

        kerf : Rel  E   E  
        kerf x y = f x  f y

        F' =  E  / kerf

        _⊑'_ : F'  F'  hProp _
        _⊑'_ = fun
          where
            fun₀ :  E   F'  hProp _
            fst (fun₀ x [ y ]) = f x  f y
            snd (fun₀ x [ y ]) = prop (f x) (f y)
            fun₀ x (eq/ a b fa≡fb i) = record
              { fst = cong (f x ≤_) fa≡fb i
              ; snd = isProp→PathP  i  isPropIsProp {A = cong (f x ≤_) fa≡fb i})
                                   (prop (f x) (f a)) (prop (f x) (f b)) i
              }
            fun₀ x (squash/ a b p q i j) = isSet→SquareP  _ _  isSetHProp)
               _  fun₀ x a)
               _  fun₀ x b)
               i  fun₀ x (p i))
               i  fun₀ x (q i)) j i

            toPath :  a b (p : kerf a b) (y : F')  fun₀ a y  fun₀ b y
            toPath a b fa≡fb = elimProp  _  isSetHProp _ _) λ c 
              Σ≡Prop  _  isPropIsProp) (cong (_≤ f c) fa≡fb)

            fun : F'  F'  hProp _
            fun [ a ] y = fun₀ a y
            fun (eq/ a b fa≡fb i) y = toPath a b fa≡fb y i
            fun (squash/ x y p q i j) z = isSet→SquareP  _ _  isSetHProp)
               _  fun x z)  _  fun y z)  i  fun (p i) z)  i  fun (q i) z) j i

        _⊑_ : Rel F' F' ℓ'
        a  b = (a ⊑' b) .fst

        open BinaryRelation _⊑_

        isProp⊑ : isPropValued
        isProp⊑ a b = (a ⊑' b) .snd

        isRefl⊑ : isRefl
        isRefl⊑ = elimProp  x  isProp⊑ x x)
                           (rfl  f)

        isAntisym⊑ : isAntisym
        isAntisym⊑ = elimProp2  x y  isPropΠ2 λ _ _  squash/ x y)
                                λ a b fa≤fb fb≤fa  eq/ a b (anti (f a) (f b) fa≤fb fb≤fa)

        isTrans⊑ : isTrans
        isTrans⊑ = elimProp3  x _ z  isPropΠ2 λ _ _  isProp⊑ x z)
                              λ a b c  trans (f a) (f b) (f c)

        poset⊑ : IsPoset _⊑_
        poset⊑ = isposet squash/ isProp⊑ isRefl⊑ isTrans⊑ isAntisym⊑

        F : Poset  ℓ'
        F = F' , (posetstr _⊑_ poset⊑)

        ♮⁺ :  E    F 
        ♮⁺ = [_]

        is♮⁺ : IsIsotone (snd E) ♮⁺ (snd F)
        IsIsotone.pres≤ is♮⁺ x y x≤y = IsIsotone.pres≤ isf x y x≤y

         :  F    E 
         [ x ] = f x
         (eq/ a b fa≡fb i) = fa≡fb i
         (squash/ x y p q i j) = isSet→SquareP  _ _  set)
           _   x)
           _   y)
           i   (p i))
           i   (q i)) j i

        is♮ : IsIsotone (snd F)  (snd E)
        IsIsotone.pres≤ is♮ = elimProp2  x y  isPropΠ λ _  prop ( x) ( y))
                                         λ x y fx≤fy  fx≤fy

        ♮⁺∘♮ :  x  x  (♮⁺  ) x
        ♮⁺∘♮ = elimProp  x  isProp⊑ x ((♮⁺  ) x))
                        λ x  subst (f x ≤_) (funExt⁻ f≡f∘f x) (rfl (f x))

ComposedResidual→isClosure : {E : Poset  ℓ'}
                             {f :  E    E }
                            Σ[ F  Poset  ℓ' ] (Σ[ g  ( E    F ) ] (Σ[ res  hasResidual E F g ] f  (residual E F g res)  g))
                            isClosure E f
ComposedResidual→isClosure {E = E} {f = f} (F , g , (isg , g⁺ , isg⁺ , g⁺∘g , g∘g⁺) , f≡g⁺∘g)
  = subst  x  IsIsotone (snd E) x (snd E)) (sym f≡g⁺∘g) (IsIsotone-∘ (snd E) g (snd F) g⁺ (snd E) isg isg⁺) ,
    f≡g⁺∘g 
    sym (cong (g⁺ ∘_)
              (AbsorbResidual E F g (isg , g⁺ , isg⁺ , g⁺∘g , g∘g⁺))) 
    cong (_∘ g⁺  g) (sym f≡g⁺∘g) 
    cong (f ∘_) (sym f≡g⁺∘g) ,
    λ x  subst (x ≤_) (sym (funExt⁻ f≡g⁺∘g x)) (g⁺∘g x)
    where _≤_ = PosetStr._≤_ (snd E)

ComposedResidual→isDualClosure : {E : Poset  ℓ'}
                                 {f :  E    E }
                                Σ[ F  Poset  ℓ' ] (Σ[ g  ( F    E ) ] (Σ[ res  hasResidual F E g ] f  g  (residual F E g res)))
                                isDualClosure E f
ComposedResidual→isDualClosure {E = E} {f = f} (F , g , (isg , g⁺ , isg⁺ , g⁺∘g , g∘g⁺) , f≡g∘g⁺)
  = subst  x  IsIsotone (snd E) x (snd E)) (sym f≡g∘g⁺) (IsIsotone-∘ (snd E) g⁺ (snd F) g (snd E) isg⁺ isg) ,
  f≡g∘g⁺ 
  sym (cong (g ∘_) (ResidualAbsorb F E g (isg , g⁺ , isg⁺ , g⁺∘g , g∘g⁺))) 
  cong (_∘ g  g⁺) (sym f≡g∘g⁺) 
  cong (f ∘_) (sym f≡g∘g⁺) ,
  λ x  subst (_≤ x) (sym (funExt⁻ f≡g∘g⁺ x)) (g∘g⁺ x)
  where _≤_ = PosetStr._≤_ (snd E)

isPropIsClosure : {E : Poset  ℓ'}
                  {f :  E    E }
                 isProp (isClosure E f)
isPropIsClosure {E = E} {f = f}
  = isProp× (isPropIsIsotone (snd E) f (snd E))
            (isProp× (isSet→ (IsPoset.is-set is) _ _)
                     (isPropΠ λ x  IsPoset.is-prop-valued is x (f x)))
  where is = PosetStr.isPoset (snd E)

isPropIsClosure' : {E : Poset  ℓ'}
                   {f :  E    E }
                  isProp (isClosure' E f)
isPropIsClosure' {E = E} {f = f}
  = isPropΠ2 λ x y  isOfHLevel≃ 1 (prop x (f y)) (prop (f x) (f y))
  where prop = IsPoset.is-prop-valued (PosetStr.isPoset (snd E))

isPropIsDualClosure : {E : Poset  ℓ'}
                      {f :  E    E }
                     isProp (isDualClosure E f)
isPropIsDualClosure {E = E} {f = f}
  = isProp× (isPropIsIsotone (snd E) f (snd E))
            (isProp× (isSet→ (IsPoset.is-set is) _ _)
                     (isPropΠ λ x  IsPoset.is-prop-valued is (f x) x))
  where is = PosetStr.isPoset (snd E)

isPropIsDualClosure' : {E : Poset  ℓ'}
                       {f :  E    E }
                      isProp (isDualClosure' E f)
isPropIsDualClosure' {E = E} {f = f}
  = isPropΠ2 λ x y  isOfHLevel≃ 1 (prop (f x) y) (prop (f x) (f y))
  where prop = IsPoset.is-prop-valued (PosetStr.isPoset (snd E))

isClosureSubset : (E : Poset  ℓ')
                 (F : Embedding  E  )
                 Type _
isClosureSubset E F = Σ[ f  ( E    E ) ] (isClosure E f × (F  (Image f , imageInclusion f)))

isDualClosureSubset : (E : Poset  ℓ')
                     (F : Embedding  E  )
                     Type _
isDualClosureSubset E F = Σ[ f  ( E    E ) ] (isDualClosure E f × (F  (Image f , imageInclusion f)))

ClosureSubsetOperatorUnique : {E : Poset  ℓ'}
                              {F : Embedding  E  }
                             (f g : isClosureSubset E F)
                             f .fst  g .fst
ClosureSubsetOperatorUnique {E = E} {F = F}
                            (f , (isf , f≡f∘f , x≤fx) , F≡Imf)
                            (g , (isg , g≡g∘g , x≤gx) , F≡Img)
  = funExt λ x  anti (f x) (g x) (fx≤gx x) (gx≤fx x)
  where _≤_ = PosetStr._≤_ (snd E)
        is = PosetStr.isPoset (snd E)

        prop = IsPoset.is-prop-valued is
        anti = IsPoset.is-antisym is

        Imf⊆Img : (Image f , imageInclusion f) ⊆ₑ (Image g , imageInclusion g)
        Imf⊆Img x = subst (x ∈ₑ_) (sym F≡Imf  F≡Img)

        Img⊆Imf : (Image g , imageInclusion g) ⊆ₑ (Image f , imageInclusion f)
        Img⊆Imf x = subst (x ∈ₑ_) (sym F≡Img  F≡Imf)

        fx≤gx :  x  f x  g x
        fx≤gx x = ∥₁.rec (prop (f x) (g x))
                           { (a , fa≡gx)  subst (f x ≤_)
                                               (sym (funExt⁻ f≡f∘f a) 
                                                fa≡gx 
                                                lemma .snd)
                                               (IsIsotone.pres≤ isf x (f a)
                                                (subst (x ≤_)
                                                  (sym (fa≡gx  lemma .snd))
                                                    (x≤gx x))) })
                          (lemma .fst .snd)
              where lemma = Img⊆Imf (g x) (((g x) ,  x , refl ∣₁) , refl)

        gx≤fx :  x  g x  f x
        gx≤fx x = ∥₁.rec (prop (g x) (f x))
                          { (a , ga≡fx)  subst (g x ≤_)
                                              (sym (funExt⁻ g≡g∘g a) 
                                                    ga≡fx 
                                                    lemma .snd)
                                              (IsIsotone.pres≤ isg x (g a)
                                               (subst (x ≤_)
                                                 (sym (ga≡fx  lemma .snd))
                                                   (x≤fx x))) })
                         (lemma .fst .snd)
              where lemma = Imf⊆Img (f x) (((f x) ,  x , refl ∣₁) , refl)

DualClosureSubsetOperatorUnique : {E : Poset  ℓ'}
                                  {F : Embedding  E  }
                                 (f g : isDualClosureSubset E F)
                                 f .fst  g .fst
DualClosureSubsetOperatorUnique {E = E} {F = F}
                                (f , (isf , f≡f∘f , fx≤x) , F≡Imf)
                                (g , (isg , g≡g∘g , gx≤x) , F≡Img)
  = funExt λ x  anti (f x) (g x) (fx≤gx x) (gx≤fx x)
  where _≤_ = PosetStr._≤_ (snd E)
        is = PosetStr.isPoset (snd E)

        prop = IsPoset.is-prop-valued is
        anti = IsPoset.is-antisym is

        Imf⊆Img : (Image f , imageInclusion f) ⊆ₑ (Image g , imageInclusion g)
        Imf⊆Img x = subst (x ∈ₑ_) (sym F≡Imf  F≡Img)

        Img⊆Imf : (Image g , imageInclusion g) ⊆ₑ (Image f , imageInclusion f)
        Img⊆Imf x = subst (x ∈ₑ_) (sym F≡Img  F≡Imf)

        gx≤fx :  x  g x  f x
        gx≤fx x = ∥₁.rec (prop (g x) (f x))
                          { (a , fa≡gx)  subst (_≤ f x) (sym (funExt⁻ f≡f∘f a) 
                                                                  fa≡gx 
                                                                  lemma .snd)
                                                            (IsIsotone.pres≤ isf (f a) x
                                                             (subst (_≤ x)
                                                               (sym (fa≡gx  lemma .snd))
                                                                 (gx≤x x))) })
                         (lemma .fst .snd)
              where lemma = Img⊆Imf (g x) (((g x) ,  x , refl ∣₁) , refl)

        fx≤gx :  x  f x  g x
        fx≤gx x = ∥₁.rec (prop (f x) (g x))
                          { (a , ga≡fx)  subst (_≤ g x)
                                              (sym (funExt⁻ g≡g∘g a) 
                                                    ga≡fx 
                                                    lemma .snd)
                                              (IsIsotone.pres≤ isg (g a) x
                                                (subst (_≤ x)
                                                  (sym (ga≡fx  lemma .snd))
                                                    (fx≤x x))) })
                         (lemma .fst .snd)
              where lemma = Imf⊆Img (f x) (((f x) ,  x , refl ∣₁) , refl)

isPropIsClosureSubset : {E : Poset  ℓ'}
                        {F : Embedding  E  }
                       isProp (isClosureSubset E F)
isPropIsClosureSubset p q = Σ≡Prop  f  isProp× isPropIsClosure (isSetEmbedding _ _))
                                    (ClosureSubsetOperatorUnique p q)

isPropIsDualClosureSubset : {E : Poset  ℓ'}
                            {F : Embedding  E  }
                           isProp (isDualClosureSubset E F)
isPropIsDualClosureSubset p q = Σ≡Prop  f  isProp× isPropIsDualClosure (isSetEmbedding _ _))
                                        (DualClosureSubsetOperatorUnique p q)

isClosureSubset→IntersectionBottom : (E : Poset  ℓ')
                                     (F : Embedding  E  )
                                    isClosureSubset E F
                                     x
                                    Least (isPoset→isProset (PosetStr.isPoset (snd E))) (principalUpset E x ∩ₑ F)
isClosureSubset→IntersectionBottom E F (f , (isf , f≡f∘f , x≤fx) , F≡Imf) x
  = (f x , fx∈x↑ , fx∈F ) , least
  where _≤_ = PosetStr._≤_ (snd E)
        is = PosetStr.isPoset (snd E)

        prop = IsPoset.is-prop-valued is

        fx∈x↑ : f x ∈ₑ principalUpset E x
        fx∈x↑ = equivFun (principalUpsetMembership E x (f x)) (x≤fx x)

        fx∈F : f x ∈ₑ F
        fx∈F = subst (f x ∈ₑ_) (sym F≡Imf) ((f x ,  x , refl ∣₁) , refl)

        least : isLeast (isPoset→isProset is) (principalUpset E x ∩ₑ F) (f x , fx∈x↑ , fx∈F)
        least (y , y∈x↑ , y∈F) = ∥₁.rec (prop _ _)
                                         { (a , fa≡fz)
                                            subst (f x ≤_)
                                            (sym (funExt⁻ f≡f∘f a 
                                                 cong f (fa≡fz 
                                                         lemma .snd)) 
                                                 fa≡fz 
                                                 lemma .snd)
                                            (IsIsotone.pres≤ isf x y
                                              (invEq (principalUpsetMembership E x y) y∈x↑)) })
                                         (lemma .fst .snd)
          where lemma = subst (y ∈ₑ_) F≡Imf y∈F

IntersectionBottom→isClosureSubset : (E : Poset  )
                                     (F : Embedding  E  )
                                    (∀ x  Least (isPoset→isProset (PosetStr.isPoset (snd E))) (principalUpset E x ∩ₑ F))
                                    isClosureSubset E F
IntersectionBottom→isClosureSubset E F least
  = f , (isf , f≡f∘f , x≤f) , F≡Imf
    where _≤_ = PosetStr._≤_ (snd E)
          is = PosetStr.isPoset (snd E)

          rfl = IsPoset.is-refl is
          anti = IsPoset.is-antisym is

          f :  E    E 
          f x = least x .fst .fst

          isf : IsIsotone (snd E) f (snd E)
          IsIsotone.pres≤ isf x y x≤y = least x .snd (f y , y↑∩F⊆x↑∩F (f y) ((least y .fst) , refl) .fst .snd)
            where x↑ = principalUpset E x
                  y↑ = principalUpset E y

                  y↑⊆x↑ = principalUpsetInclusion E x y x≤y
                  y↑∩F⊆x↑∩F = isMeetIsotone
                              (isPoset→isProset isPoset⊆ₑ) y↑ x↑ F F
                              (y↑ ∩ₑ F)
                              (x↑ ∩ₑ F)
                              (isMeet∩ₑ y↑ F)
                              (isMeet∩ₑ x↑ F)
                               y↑⊆x↑
                              (isRefl⊆ₑ F)

          x≤f :  x  x  f x
          x≤f x = invEq (principalUpsetMembership E x (f x)) (least x .fst .snd .fst)

          F≡fF :  y  y ∈ₑ F
                       y  f y
          F≡fF y y∈F = anti y (f y) (x≤f y)
                       (least y .snd (y , equivFun (principalUpsetMembership E y y) (rfl y) , y∈F))

          f≡f∘f : f  (f  f)
          f≡f∘f = funExt λ x  F≡fF (f x) (least x .fst .snd .snd)

          F⊆Imf : F ⊆ₑ (Image f , imageInclusion f)
          F⊆Imf x x∈F = (x ,  x , (sym (F≡fF x x∈F)) ∣₁) , refl

          Imf⊆F : (Image f , imageInclusion f) ⊆ₑ F
          Imf⊆F x ((a , ima) , fib)
            = ∥₁.rec (isProp∈ₑ x F)
                      { (b , fb≡a) 
                           subst (_∈ₑ F)
                                 (fb≡a  fib)
                                 (least b .fst .snd .snd) }) ima

          F≡Imf : F  (Image f , imageInclusion f)
          F≡Imf = isAntisym⊆ₑ F (Image f , imageInclusion f) F⊆Imf Imf⊆F

isBicomplete : (E : Poset  ℓ')
               (F : Embedding  E  )
              Type _
isBicomplete E F = isClosureSubset E F × isClosureSubset (DualPoset E) F

isBicompleteResiduatedClosureImage : (E : Poset  ℓ')
                                     (f :  E    E )
                                    hasResidual E E f
                                    isClosure E f
                                    isBicomplete E (Image f , imageInclusion f)
isBicompleteResiduatedClosureImage E f (isf , f⁺ , isf⁺ , f⁺∘f , f∘f⁺) (_ , f≡f∘f , x≤fx)
  = (f , clsf , refl) , f⁺ , clsf⁺ , Imf≡Imf⁺
  where _≤_ = PosetStr._≤_ (snd E)
        is = PosetStr.isPoset (snd E)

        anti = IsPoset.is-antisym is

        resf = (isf , f⁺ , isf⁺ , f⁺∘f , f∘f⁺)
        clsf = (isf , f≡f∘f , x≤fx)

        f≡f⁺∘f : f  f⁺  f
        f≡f⁺∘f = funExt λ x  anti (f x) ((f⁺  f) x) (f≤f⁺∘f x) (f⁺∘f≤f x)
          where f≤f⁺∘f :  x  f x  (f⁺  f) x
                f≤f⁺∘f x = subst (f x ≤_)
                                 (cong f⁺ (sym (funExt⁻ f≡f∘f x))) (f⁺∘f (f x))

                f⁺∘f≤f :  x  (f⁺  f) x  f x
                f⁺∘f≤f x = subst ((f⁺  f) x ≤_)
                                 (funExt⁻ (AbsorbResidual E E f resf) x)
                                 (x≤fx ((f⁺  f) x))

        f⁺≡f∘f⁺ : f⁺  f  f⁺
        f⁺≡f∘f⁺ = funExt λ x  sym (funExt⁻ (ResidualAbsorb E E f resf) x) 
                               funExt⁻ (sym f≡f⁺∘f) (f⁺ x)

        Imf⊆Imf⁺ : (Image f , imageInclusion f) ⊆ₑ (Image f⁺ , imageInclusion f⁺)
        Imf⊆Imf⁺ x ((a , ima) , fib) = ∥₁.rec (isProp∈ₑ x (Image f⁺ , imageInclusion f⁺))
                                               { (b , fb≡a)  (x ,  x , (cong f⁺ (sym (fb≡a  fib)) 
                                                                            funExt⁻ (sym f≡f⁺∘f) b 
                                                                            fb≡a 
                                                                            fib) ∣₁) , refl })
                                               ima

        Imf⁺⊆Imf : (Image f⁺ , imageInclusion f⁺) ⊆ₑ (Image f , imageInclusion f)
        Imf⁺⊆Imf x ((a , ima) , fib) = ∥₁.rec (isProp∈ₑ x (Image f , imageInclusion f))
                                               { (b , f⁺b≡a)  (x ,  x , (cong f (sym (f⁺b≡a  fib)) 
                                                                             funExt⁻ (sym f⁺≡f∘f⁺) b 
                                                                             f⁺b≡a 
                                                                             fib) ∣₁) , refl })
                                               ima

        Imf≡Imf⁺ : (Image f , imageInclusion f)  (Image f⁺ , imageInclusion f⁺)
        Imf≡Imf⁺ = isAntisym⊆ₑ _ _ Imf⊆Imf⁺ Imf⁺⊆Imf

        clsf⁺ : isClosure (DualPoset E) f⁺
        clsf⁺ = DualIsotoneDual E E f⁺ isf⁺ ,
                f⁺≡f∘f⁺  cong (_∘ f⁺) f≡f⁺∘f  cong (f⁺ ∘_) (sym f⁺≡f∘f⁺) ,
                λ x  subst (_≤ x) (funExt⁻ (sym f⁺≡f∘f⁺) x) (f∘f⁺ x)

isBicomplete→ClosureOperatorHasResidual : (E : Poset  ℓ')
                                          (F : Embedding  E  )
                                         (bi : isBicomplete E F)
                                         hasResidual E E (bi . fst .fst)
isBicomplete→ClosureOperatorHasResidual E F ((f , (isf , f≡f∘f , x≤fx) , F≡Imf) ,
                                              f⁺ , (isf⁺ , f⁺≡f⁺∘f⁺ , f⁺x≤x) , F≡Imf⁺)
  = isf , f⁺ , DualIsotoneDual' E E f⁺ isf⁺ , f⁺∘f , f∘f⁺
  where _≤_ = PosetStr._≤_ (snd E)
        is = PosetStr.isPoset (snd E)
        set = IsPoset.is-set is

        Imf⁺⊆Imf : (Image f⁺ , imageInclusion f⁺) ⊆ₑ (Image f , imageInclusion f)
        Imf⁺⊆Imf = subst (Imf⁺ ⊆ₑ_) ((sym F≡Imf⁺)  F≡Imf) (isRefl⊆ₑ Imf⁺)
          where Imf⁺ = (Image f⁺ , imageInclusion f⁺)

        Imf⊆Imf⁺ : (Image f , imageInclusion f) ⊆ₑ (Image f⁺ , imageInclusion f⁺)
        Imf⊆Imf⁺ = subst (Imf ⊆ₑ_) ((sym F≡Imf)  F≡Imf⁺) (isRefl⊆ₑ Imf)
          where Imf = (Image f , imageInclusion f)

        f≡f⁺∘f :  x  f x  (f⁺  f) x
        f≡f⁺∘f x = ∥₁.rec (set _ _)  { (b , f⁺b≡a)  (sym (f⁺b≡a  fib)  funExt⁻ f⁺≡f⁺∘f⁺ b)  cong f⁺ (f⁺b≡a  fib) }) ima
          where lemma = Imf⊆Imf⁺ (f x) (((f x) ,  x , refl ∣₁) , refl)

                ima = lemma .fst .snd
                fib = lemma .snd

        f⁺≡f∘f⁺ :  x  f⁺ x  (f  f⁺) x
        f⁺≡f∘f⁺ x = ∥₁.rec (set _ _)  { (b , fb≡a)  (sym (fb≡a  fib)  funExt⁻ f≡f∘f b)  cong f (fb≡a  fib) }) ima
          where lemma = Imf⁺⊆Imf (f⁺ x) (((f⁺ x) ,  x , refl ∣₁) , refl)

                ima = lemma .fst .snd
                fib = lemma .snd

        f⁺∘f :  x  x  (f⁺  f) x
        f⁺∘f x = subst (x ≤_) (f≡f⁺∘f x) (x≤fx x)

        f∘f⁺ :  x  (f  f⁺) x  x
        f∘f⁺ x = subst (_≤ x) (f⁺≡f∘f⁺ x) (f⁺x≤x x)

IsPosetEquiv→isResiduatedBijection : (P S : Poset  ℓ')
                                     (e :  P    S )
                                    IsPosetEquiv (snd P) e (snd S)
                                    hasResidual P S (equivFun e)
IsPosetEquiv→isResiduatedBijection P S e eq
  = IsPosetEquiv→IsIsotone P S e eq , invEq e , is⁻ ,
    x  subst (x ≤P_) (sym (retEq e x)) (rflP x)) ,
    λ x  subst (_≤S x) (sym (secEq e x)) (rflS x)
  where _≤P_ = PosetStr._≤_ (snd P)
        _≤S_ = PosetStr._≤_ (snd S)

        rflP = IsPoset.is-refl (PosetStr.isPoset (snd P))
        rflS = IsPoset.is-refl (PosetStr.isPoset (snd S))

        is⁻ : IsIsotone (snd S) (invEq e) (snd P)
        IsIsotone.pres≤ is⁻ x y = equivFun (IsPosetEquiv.pres≤⁻ eq x y)

isResiduatedBijection→IsPosetEquiv : (P S : Poset  ℓ')
                                     (e :  P    S )
                                    hasResidual P S (equivFun e)
                                    IsPosetEquiv (snd P) e (snd S)
IsPosetEquiv.pres≤ (isResiduatedBijection→IsPosetEquiv P S e
                    (ise , e⁻ , ise⁻ , e⁻∘e , e∘e⁻)) x y
  = propBiimpl→Equiv (propP _ _) (propS _ _) (IsIsotone.pres≤ ise x y) (subst2 _≤P_ (lemma x) (lemma y)  IsIsotone.pres≤ ise⁻ _ _)
  where _≤P_ = PosetStr._≤_ (snd P)
        isP = PosetStr.isPoset (snd P)
        propP = IsPoset.is-prop-valued isP
        rflP = IsPoset.is-refl isP

        _≤S_ = PosetStr._≤_ (snd S)
        isS = PosetStr.isPoset (snd S)
        propS = IsPoset.is-prop-valued isS
        antiS = IsPoset.is-antisym isS

        e∘e⁻x≡x :  x  equivFun e (e⁻ x)  x
        e∘e⁻x≡x x = antiS _ x (e∘e⁻ x)
                              (subst2 _≤S_ (secEq e x)
                                           (cong (equivFun e  e⁻) (secEq e x))
                                           (IsIsotone.pres≤ ise _ _ (e⁻∘e (invEq e x))))

        e⁻≡inv :  x  e⁻ x  invEq e x
        e⁻≡inv x = sym (retEq e (e⁻ x))  cong (invEq e) (e∘e⁻x≡x x)

        lemma :  x  e⁻ (equivFun e x)  x
        lemma x = e⁻≡inv (equivFun e x)  retEq e x

-- We can weaken the equivalence of a poset equivalence to a surjection
isOrderRecovering→isEmbedding : (P S : Poset  ℓ')
                                (f :  P    S )
                               (∀ x y  (PosetStr._≤_ (snd S) (f x) (f y))
                                        (PosetStr._≤_ (snd P) x y))
                               isEmbedding f
isOrderRecovering→isEmbedding P S f is = emb
  where _≤_ = PosetStr._≤_ (snd S)

        isP = PosetStr.isPoset (snd P)
        isS = PosetStr.isPoset (snd S)

        setS = IsPoset.is-set isS

        antiP = IsPoset.is-antisym isP
        rflS = IsPoset.is-refl isS

        emb : isEmbedding f
        emb = injEmbedding setS λ {w} {x} fw≡fx
             antiP w x (is w x (subst (f w ≤_) fw≡fx (rflS (f w))))
                        (is x w (subst (_≤ f w) fw≡fx (rflS (f w))))

-- Galois connections work similarly to residuals, but are antitone
module _
  (E F : Poset  ℓ')
  (f :  E    F )
  (g :  F    E )
  where
    private
      _≤E_ = PosetStr._≤_ (snd E)
      _≤F_ = PosetStr._≤_ (snd F)

      propE = IsPoset.is-prop-valued (PosetStr.isPoset (snd E))
      propF = IsPoset.is-prop-valued (PosetStr.isPoset (snd F))

    isGaloisConnection : Type (ℓ-max  ℓ')
    isGaloisConnection = IsAntitone (snd E) f (snd F) ×
                         IsAntitone (snd F) g (snd E) ×
                        (∀ x  x ≤F (f  g) x) ×
                        (∀ x  x ≤E (g  f) x)

    isPropIsGaloisConnection : isProp isGaloisConnection
    isPropIsGaloisConnection = isProp× (isPropIsAntitone _ _ _)
                              (isProp× (isPropIsAntitone _ _ _)
                              (isProp× (isPropΠ λ x  propF x _)
                                       (isPropΠ λ x  propE x _)))

    isGaloisConnection→hasResidualDual : isGaloisConnection
                                        hasResidual E (DualPoset F) f
    isGaloisConnection→hasResidualDual (antif , antig , f∘g , g∘f)
      = AntitoneDual E F f antif , g , DualAntitone F E g antig , g∘f , f∘g

    AbsorbGaloisConnection : isGaloisConnection
                            f  g  f  f
    AbsorbGaloisConnection conn
      = AbsorbResidual E (DualPoset F) f (isGaloisConnection→hasResidualDual conn)

    GaloisConnectionAbsorb : isGaloisConnection
                            g  f  g  g
    GaloisConnectionAbsorb conn
      = ResidualAbsorb E (DualPoset F) f (isGaloisConnection→hasResidualDual conn)

    GaloisConnectionClosure : isGaloisConnection
                             isClosure E (g  f)
    GaloisConnectionClosure conn
      = ComposedResidual→isClosure (DualPoset F , f , isGaloisConnection→hasResidualDual conn , refl)

    GaloisConnectionDualClosure : isGaloisConnection
                                 isDualClosure (DualPoset F) (f  g)
    GaloisConnectionDualClosure conn
      = ComposedResidual→isDualClosure (E , f , isGaloisConnection→hasResidualDual conn , refl)

hasResidual→isGaloisConnectionDual : (E F : Poset  ℓ')
                                     (f :  E    F )
                                    (res : hasResidual E F f)
                                    isGaloisConnection E (DualPoset F) f (residual E F f res)
hasResidual→isGaloisConnectionDual E F f (isf , f⁺ , isf⁺ , f⁺∘f , f∘f⁺)
  = (IsotoneDual E F f isf) , (DualIsotone F E f⁺ isf⁺) , f∘f⁺ , f⁺∘f