{-# OPTIONS --safe #-}
module Cubical.Relation.Binary.Order.Proset.Properties where

open import Cubical.Data.Sigma
open import Cubical.Data.Sum as 

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

open import Cubical.Functions.Embedding

open import Cubical.HITs.PropositionalTruncation as ∥₁

open import Cubical.Relation.Binary.Base
open import Cubical.Relation.Binary.Order.Proset.Base
open import Cubical.Relation.Binary.Order.Quoset.Base

open import Cubical.Relation.Nullary

private
  variable
     ℓ' ℓ'' : Level

module _
  {A : Type }
  {R : Rel A A ℓ'}
  where

  open BinaryRelation

  isProset→isEquivRelSymKernel : IsProset R  isEquivRel (SymKernel R)
  isProset→isEquivRelSymKernel proset
    = equivRel  a  (IsProset.is-refl proset a)
                    , (IsProset.is-refl proset a))
               (isSymSymKernel R)
                a b c (Rab , Rba) (Rbc , Rcb)
                  IsProset.is-trans proset a b c Rab Rbc
                 , IsProset.is-trans proset c b a Rcb Rba)

  isProset→isQuosetAsymKernel : IsProset R  IsQuoset (AsymKernel R)
  isProset→isQuosetAsymKernel proset
    = isquoset (IsProset.is-set proset)
                     a b  isProp× (IsProset.is-prop-valued proset a b) (isProp¬ (R b a)))
                     a (Raa , ¬Raa)  ¬Raa (IsProset.is-refl proset a))
                     a b c (Rab , ¬Rba) (Rbc , ¬Rcb)
                       IsProset.is-trans proset a b c Rab Rbc
                      , λ Rca  ¬Rcb (IsProset.is-trans proset c a b Rca Rab))
                    (isAsymAsymKernel R)

  isProsetInduced : IsProset R  (B : Type ℓ'')  (f : B  A)  IsProset (InducedRelation R (B , f))
  isProsetInduced pre B (f , emb)
    = isproset (Embedding-into-isSet→isSet (f , emb) (IsProset.is-set pre))
                  a b  IsProset.is-prop-valued pre (f a) (f b))
                  a  IsProset.is-refl pre (f a))
                 λ a b c  IsProset.is-trans pre (f a) (f b) (f c)

  isProsetDual : IsProset R  IsProset (Dual R)
  isProsetDual pre
    = isproset (IsProset.is-set pre)
                a b  IsProset.is-prop-valued pre b a)
               (IsProset.is-refl pre)  a b c Rab Rbc  IsProset.is-trans pre c b a Rbc Rab)

Proset→Quoset : Proset  ℓ'  Quoset  ℓ'
Proset→Quoset (_ , pre)
  = quoset _ (BinaryRelation.AsymKernel (ProsetStr._≲_ pre))
             (isProset→isQuosetAsymKernel (ProsetStr.isProset pre))

DualProset : Proset  ℓ'  Proset  ℓ'
DualProset (_ , pre)
  = proset _ (BinaryRelation.Dual (ProsetStr._≲_ pre))
             (isProsetDual (ProsetStr.isProset pre))

module _
  {A : Type }
  {_≲_ : Rel A A ℓ'}
  (pre : IsProset _≲_)
  where

  private
      prop = IsProset.is-prop-valued pre

      rfl = IsProset.is-refl pre

      trans = IsProset.is-trans pre

  module _
    (P : Embedding A ℓ'')
    where

    private
      subtype = fst P

      toA = fst (snd P)

    isMinimal : (n : subtype)  Type (ℓ-max ℓ' ℓ'')
    isMinimal n = (x : subtype)  toA x  toA n  toA n  toA x

    isPropIsMinimal :  n  isProp (isMinimal n)
    isPropIsMinimal n = isPropΠ2 λ x _  prop (toA n) (toA x)

    Minimal : Type (ℓ-max ℓ' ℓ'')
    Minimal = Σ[ n  subtype ] isMinimal n

    isMaximal : (n : subtype)  Type (ℓ-max ℓ' ℓ'')
    isMaximal n = (x : subtype)  toA n  toA x  toA x  toA n

    isPropIsMaximal :  n  isProp (isMaximal n)
    isPropIsMaximal n = isPropΠ2 λ x _  prop (toA x) (toA n)

    Maximal : Type (ℓ-max ℓ' ℓ'')
    Maximal = Σ[ n  subtype ] isMaximal n

    isLeast : (n : subtype)  Type (ℓ-max ℓ' ℓ'')
    isLeast n = (x : subtype)  toA n  toA x

    isPropIsLeast :  n  isProp (isLeast n)
    isPropIsLeast n = isPropΠ λ x  prop (toA n) (toA x)

    Least : Type (ℓ-max ℓ' ℓ'')
    Least = Σ[ n  subtype ] isLeast n

    isGreatest : (n : subtype)  Type (ℓ-max ℓ' ℓ'')
    isGreatest n = (x : subtype)  toA x  toA n

    isPropIsGreatest :  n  isProp (isGreatest n)
    isPropIsGreatest n = isPropΠ λ x  prop (toA x) (toA n)

    Greatest : Type (ℓ-max ℓ' ℓ'')
    Greatest = Σ[ n  subtype ] isGreatest n

  module _
    {B : Type ℓ''}
    (P : B  A)
    where

    isLowerBound : (n : A)  Type (ℓ-max ℓ' ℓ'')
    isLowerBound n = (x : B)  n  P x

    isPropIsLowerBound :  n  isProp (isLowerBound n)
    isPropIsLowerBound n = isPropΠ λ x  prop n (P x)

    LowerBound : Type (ℓ-max (ℓ-max  ℓ') ℓ'')
    LowerBound = Σ[ n  A ] isLowerBound n

    isUpperBound : (n : A)  Type (ℓ-max ℓ' ℓ'')
    isUpperBound n = (x : B)  P x  n

    isPropIsUpperBound :  n  isProp (isUpperBound n)
    isPropIsUpperBound n = isPropΠ λ x  prop (P x) n

    UpperBound : Type (ℓ-max (ℓ-max  ℓ') ℓ'')
    UpperBound = Σ[ n  A ] isUpperBound n

  module _
    {B : Type ℓ''}
    (P : B  A)
    where

    isMaximalLowerBound : (n : A)  Type (ℓ-max (ℓ-max  ℓ') ℓ'')
    isMaximalLowerBound n
      = Σ[ islb  isLowerBound P n ]
        isMaximal (LowerBound P
                  , (EmbeddingΣProp (isPropIsLowerBound P))) (n , islb)

    isPropIsMaximalLowerBound :  n  isProp (isMaximalLowerBound n)
    isPropIsMaximalLowerBound n
      = isPropΣ (isPropIsLowerBound P n)
                λ islb  isPropIsMaximal (LowerBound P
                       , EmbeddingΣProp (isPropIsLowerBound P)) (n , islb)

    MaximalLowerBound : Type (ℓ-max (ℓ-max  ℓ') ℓ'')
    MaximalLowerBound = Σ[ n  A ] isMaximalLowerBound n

    isMinimalUpperBound : (n : A)  Type (ℓ-max (ℓ-max  ℓ') ℓ'')
    isMinimalUpperBound n
      = Σ[ isub  isUpperBound P n ]
        isMinimal (UpperBound P
                  , EmbeddingΣProp (isPropIsUpperBound P)) (n , isub)

    isPropIsMinimalUpperBound :  n  isProp (isMinimalUpperBound n)
    isPropIsMinimalUpperBound n
      = isPropΣ (isPropIsUpperBound P n)
                λ isub  isPropIsMinimal (UpperBound P
                       , EmbeddingΣProp (isPropIsUpperBound P)) (n , isub)

    MinimalUpperBound : Type (ℓ-max (ℓ-max  ℓ') ℓ'')
    MinimalUpperBound = Σ[ n  A ] isMinimalUpperBound n

    isInfimum : (n : A)  Type (ℓ-max (ℓ-max  ℓ') ℓ'')
    isInfimum n
      = Σ[ islb  isLowerBound P n ]
        isGreatest (LowerBound P
                   , EmbeddingΣProp (isPropIsLowerBound P)) (n , islb)

    isPropIsInfimum :  n  isProp (isInfimum n)
    isPropIsInfimum n
      = isPropΣ (isPropIsLowerBound P n)
                λ islb  isPropIsGreatest (LowerBound P
                       , EmbeddingΣProp (isPropIsLowerBound P)) (n , islb)

    Infimum : Type (ℓ-max (ℓ-max  ℓ') ℓ'')
    Infimum = Σ[ n  A ] isInfimum n

    isSupremum : (n : A)  Type (ℓ-max (ℓ-max  ℓ') ℓ'')
    isSupremum n
      = Σ[ isub  isUpperBound P n ]
        isLeast (UpperBound P
                , EmbeddingΣProp (isPropIsUpperBound P)) (n , isub)

    isPropIsSupremum :  n  isProp (isSupremum n)
    isPropIsSupremum n
      = isPropΣ (isPropIsUpperBound P n)
                λ isub  isPropIsLeast (UpperBound P
                       , EmbeddingΣProp (isPropIsUpperBound P)) (n , isub)

    Supremum : Type (ℓ-max (ℓ-max  ℓ') ℓ'')
    Supremum = Σ[ n  A ] isSupremum n

  isUpperBoundOfSelf→isGreatestOfSelf : ∀{n}  isUpperBound {B = A}  x  x) n
                                       isGreatest (A , (id↪ A)) n
  isUpperBoundOfSelf→isGreatestOfSelf ub = ub

  isLowerBoundOfSelf→isLeastOfSelf : ∀{n}  isLowerBound {B = A}  x  x) n
                                    isLeast (A , (id↪ A)) n
  isLowerBoundOfSelf→isLeastOfSelf lb = lb

  isInfimumOfUpperBounds→isUpperBound : {B : Type ℓ''}
                                       (P : B  A)
                                        n
                                       isInfimum {B = UpperBound P} fst n
                                       isUpperBound P n
  isInfimumOfUpperBounds→isUpperBound P _ (_ , gt) x = gt ((P x) , λ { (_ , upy)  upy x })

  isInfimumOfUpperBounds→isSupremum : {B : Type ℓ''}
                                     (P : B  A)
                                      n
                                     isInfimum {B = UpperBound P} fst n
                                     isSupremum P n
  isInfimumOfUpperBounds→isSupremum P n (lt , gt)
    = isInfimumOfUpperBounds→isUpperBound P n (lt , gt) , lt

  isSupremumOfLowerBounds→isLowerBound : {B : Type ℓ''}
                                        (P : B  A)
                                         n
                                        isSupremum {B = LowerBound P} fst n
                                        isLowerBound P n
  isSupremumOfLowerBounds→isLowerBound P _ (_ , ls) x = ls ((P x) , λ { (_ , lwy)  lwy x })

  isSupremumOfLowerBounds→isInfimum : {B : Type ℓ''}
                                     (P : B  A)
                                      n
                                     isSupremum {B = LowerBound P} fst n
                                     isInfimum P n
  isSupremumOfLowerBounds→isInfimum P n (gs , ls)
    = isSupremumOfLowerBounds→isLowerBound P n (gs , ls) , gs

  isMeet : A  A  A  Type (ℓ-max  ℓ')
  isMeet a b a∧b =  x  x  a∧b  (x  a × x  b)

  isJoin : A  A  A  Type (ℓ-max  ℓ')
  isJoin a b a∨b =  x  a∨b  x  (a  x × b  x)

  Meet :  a b  Type (ℓ-max  ℓ')
  Meet a b = Σ[ a∧b  A ] isMeet a b a∧b

  Join :  a b  Type (ℓ-max  ℓ')
  Join a b = Σ[ a∨b  A ] isJoin a b a∨b

  isPropIsMeet :  a b a∧b  isProp (isMeet a b a∧b)
  isPropIsMeet a b c = isPropΠ λ x  isOfHLevel≃ 1 (prop x c)
                                                    (isProp× (prop x a)
                                                             (prop x b))

  isPropIsJoin :  a b a∨b  isProp (isJoin a b a∨b)
  isPropIsJoin a b c = isPropΠ λ x  isOfHLevel≃ 1 (prop c x)
                                                    (isProp× (prop a x)
                                                             (prop b x))

  isReflIsMeet :  a  isMeet a a a
  isReflIsMeet a x = propBiimpl→Equiv (prop x a)
                                      (isProp× (prop x a) (prop x a))
                                       a  a , a)
                                       fst

  isReflIsJoin :  a  isJoin a a a
  isReflIsJoin a x = propBiimpl→Equiv (prop a x)
                                      (isProp× (prop a x) (prop a x))
                                       a  a , a)
                                       fst

  isMeet≃isInfimum :  a b c
                    isMeet a b c
                    isInfimum {B = Σ[ x  A ] (x  a)  (x  b)} fst c
  isMeet≃isInfimum a b a∧b
    = propBiimpl→Equiv (isPropIsMeet a b a∧b) (isPropIsInfimum fst a∧b)
                        fun  ⊎.rec  x≡a  subst
                                               (a∧b ≲_)
                                               (sym x≡a)
                                               (fun a∧b .fst (rfl a∧b) .fst))
                                       x≡b  subst
                                               (a∧b ≲_)
                                               (sym x≡b)
                                               (fun a∧b .fst (rfl a∧b) .snd))
                                 snd ,
                        λ { (c , uprc)
                           invEq (fun c) ((uprc (a , (inl refl))) ,
                                           (uprc (b , (inr refl)))) })
                        λ { (lb , gt) x
                           propBiimpl→Equiv
                           (prop x a∧b)
                           (isProp× (prop x a) (prop x b))
                        x≤a∧b  (trans x a∧b a x≤a∧b
                                         (lb (a , inl refl))) ,
                                   (trans x a∧b b x≤a∧b
                                         (lb (b , inr refl))))
                        λ { (x≤a , x≤b)
                           gt (x ,
                               (⊎.rec  y≡a  subst
                                               (x ≲_)
                                               (sym y≡a)
                                                x≤a)
                                       y≡b  subst
                                               (x ≲_)
                                               (sym y≡b)
                                                x≤b)
                                        snd)) } }

  isJoin≃isSupremum :  a b c
                     isJoin a b c
                     isSupremum {B = Σ[ x  A ] (x  a)  (x  b)} fst c
  isJoin≃isSupremum a b a∨b
    = propBiimpl→Equiv (isPropIsJoin a b a∨b) (isPropIsSupremum fst a∨b)
                        fun  ⊎.rec  x≡a  subst
                                               (_≲ a∨b)
                                               (sym x≡a)
                                               (fun a∨b .fst (rfl a∨b) .fst))
                                       x≡b  subst
                                               (_≲ a∨b)
                                               (sym x≡b)
                                               (fun a∨b .fst (rfl a∨b) .snd))
                                 snd ,
                        λ { (c , lwrc)
                           invEq (fun c) ((lwrc (a , (inl refl))) ,
                                           (lwrc (b , (inr refl)))) })
                        λ { (ub , lt) x
                           propBiimpl→Equiv
                           (prop a∨b x)
                           (isProp× (prop a x) (prop b x))
                        a∨b≤x  trans a a∨b x
                                        (ub (a , inl refl))
                                         a∨b≤x ,
                                   trans b a∨b x
                                        (ub (b , inr refl))
                                         a∨b≤x)
                        λ { (a≤x , b≤x)
                           lt (x ,
                               (⊎.rec  y≡a  subst
                                               (_≲ x)
                                               (sym y≡a)
                                                a≤x)
                                       y≡b  subst
                                               (_≲ x)
                                               (sym y≡b)
                                                b≤x)
                                 snd)) } }

  isMeetComm :  a b a∧b  isMeet a b a∧b  isMeet b a a∧b
  isMeetComm _ _ _ fun x = compEquiv (fun x) Σ-swap-≃

  isJoinComm :  a b a∨b  isJoin a b a∨b  isJoin b a a∨b
  isJoinComm _ _ _ fun x = compEquiv (fun x) Σ-swap-≃

  isMeetAssoc :  a b c a∧b b∧c a∧b∧c
               isMeet a b a∧b
               isMeet b c b∧c
               isMeet a b∧c a∧b∧c
               isMeet a∧b c a∧b∧c
  isMeetAssoc a b c a∧b b∧c a∧b∧c funa∧b funb∧c funa∧b∧c x
    = compEquiv (funa∧b∧c x)
                (propBiimpl→Equiv (isProp× (prop x a) (prop x b∧c))
                                  (isProp× (prop x a∧b) (prop x c))
                                   { (x≤a , x≤b∧c)
                                      invEq (funa∧b x)
                                             (x≤a ,
                                             (funb∧c x .fst x≤b∧c .fst)) ,
                                              funb∧c x .fst x≤b∧c .snd })
                                   λ { (x≤a∧b , x≤c)
                                      funa∧b x .fst x≤a∧b .fst ,
                                       invEq (funb∧c x)
                                            ((funa∧b x .fst x≤a∧b .snd) ,
                                              x≤c) })

  isJoinAssoc :  a b c a∨b b∨c a∨b∨c
               isJoin a b a∨b
               isJoin b c b∨c
               isJoin a b∨c a∨b∨c
               isJoin a∨b c a∨b∨c
  isJoinAssoc a b c a∨b b∨c a∨b∨c funa∨b funb∨c funa∨b∨c x
    = compEquiv (funa∨b∨c x)
                (propBiimpl→Equiv (isProp× (prop a x) (prop b∨c x))
                                  (isProp× (prop a∨b x) (prop c x))
                                   { (a≤x , b∨c≤x)
                                      invEq (funa∨b x) (a≤x ,
                                      (funb∨c x .fst b∨c≤x .fst)) ,
                                       funb∨c x .fst b∨c≤x .snd })
                                   λ { (a∨b≤x , c≤x)
                                      funa∨b x .fst a∨b≤x .fst ,
                                       invEq (funb∨c x)
                                     ((funa∨b x .fst a∨b≤x .snd) ,
                                       c≤x) })

  isMeetIsotone :  a b c d a∧c b∧d
                 isMeet a c a∧c
                 isMeet b d b∧d
                 a  b
                 c  d
                 a∧c  b∧d
  isMeetIsotone a b c d a∧c b∧d meeta∧c meetb∧d a≲b c≲d
    = invEq (meetb∧d a∧c) (trans a∧c a b a∧c≲a a≲b , trans a∧c c d a∧c≲c c≲d)
      where a∧c≲a = equivFun (meeta∧c a∧c) (rfl a∧c) .fst
            a∧c≲c = equivFun (meeta∧c a∧c) (rfl a∧c) .snd

  isJoinIsotone :  a b c d a∨c b∨d
                 isJoin a c a∨c
                 isJoin b d b∨d
                 a  b
                 c  d
                 a∨c  b∨d
  isJoinIsotone a b c d a∨c b∨d joina∨c joinb∨d a≲b c≲d
    = invEq (joina∨c b∨d) (trans a b b∨d a≲b b≲b∨d , trans c d b∨d c≲d d≲b∨d)
    where b≲b∨d = equivFun (joinb∨d b∨d) (rfl b∨d) .fst
          d≲b∨d = equivFun (joinb∨d b∨d) (rfl b∨d) .snd