    ------------------------------------------------------------------------
    -- The Agda standard library
    --
    -- The basic code for equational reasoning with three relations:
    -- equality, strict ordering and non-strict ordering.
    ------------------------------------------------------------------------
    --
    -- See `Data.Nat.Properties` or `Relation.Binary.Reasoning.PartialOrder`
    -- for examples of how to instantiate this module.

    {-# OPTIONS --cubical-compatible --safe #-}

    open import Relation.Binary.Core using (Rel; _⇒_)
    open import Relation.Binary.Structures using (IsPreorder)
    open import Relation.Binary.Definitions
      using (Transitive; _Respects₂_; Reflexive; Trans; Irreflexive; Asymmetric)

    module Relation.Binary.Reasoning.Base.Triple {a ℓ₁ ℓ₂ ℓ₃} {A : Set a}
      {_≈_ : Rel A ℓ₁} {_≤_ : Rel A ℓ₂} {_<_ : Rel A ℓ₃}
      (isPreorder : IsPreorder _≈_ _≤_)
      (<-asym : Asymmetric _<_) (<-trans : Transitive _<_) (<-resp-≈ : _<_ Respects₂ _≈_)
      (<⇒≤ : _<_ ⇒ _≤_)
      (<-≤-trans : Trans _<_ _≤_ _<_) (≤-<-trans : Trans _≤_ _<_ _<_)
      where

    open import Data.Product.Base using (proj₁; proj₂)
    open import Level using (_⊔_)
    open import Function.Base using (case_of_)
    open import Relation.Nullary.Decidable.Core
      using (Dec; yes; no)
    open import Relation.Binary.PropositionalEquality.Core as ≡ using (_≡_)
    open import Relation.Binary.Reasoning.Syntax

    open IsPreorder isPreorder

    ------------------------------------------------------------------------
    -- A datatype to abstract over the current relation

    infix 4 _IsRelatedTo_

    data _IsRelatedTo_ (x y : A) : Set (a ⊔ ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃) where
      strict    : (x<y : x < y) → x IsRelatedTo y
      nonstrict : (x≤y : x ≤ y) → x IsRelatedTo y
      equals    : (x≈y : x ≈ y) → x IsRelatedTo y

    start : _IsRelatedTo_ ⇒ _≤_
    start (equals x≈y) = reflexive x≈y
    start (nonstrict x≤y) = x≤y
    start (strict x<y) = <⇒≤ x<y

    ≡-go : Trans _≡_ _IsRelatedTo_ _IsRelatedTo_
    ≡-go x≡y (equals y≈z) = equals (case x≡y of λ where ≡.refl → y≈z)
    ≡-go x≡y (nonstrict y≤z) = nonstrict (case x≡y of λ where ≡.refl → y≤z)
    ≡-go x≡y (strict y<z) = strict (case x≡y of λ where ≡.refl → y<z)

    ≈-go : Trans _≈_ _IsRelatedTo_ _IsRelatedTo_
    ≈-go x≈y (equals y≈z) = equals (Eq.trans x≈y y≈z)
    ≈-go x≈y (nonstrict y≤z) = nonstrict (∼-respˡ-≈ (Eq.sym x≈y) y≤z)
    ≈-go x≈y (strict y<z) = strict (proj₂ <-resp-≈ (Eq.sym x≈y) y<z)

    ≤-go : Trans _≤_ _IsRelatedTo_ _IsRelatedTo_
    ≤-go x≤y (equals y≈z) = nonstrict (∼-respʳ-≈ y≈z x≤y)
    ≤-go x≤y (nonstrict y≤z) = nonstrict (trans x≤y y≤z)
    ≤-go x≤y (strict y<z) = strict (≤-<-trans x≤y y<z)

    <-go : Trans _<_ _IsRelatedTo_ _IsRelatedTo_
    <-go x<y (equals y≈z) = strict (proj₁ <-resp-≈ y≈z x<y)
    <-go x<y (nonstrict y≤z) = strict (<-≤-trans x<y y≤z)
    <-go x<y (strict y<z) = strict (<-trans x<y y<z)

    stop : Reflexive _IsRelatedTo_
    stop = equals Eq.refl

    ------------------------------------------------------------------------
    -- Types that are used to ensure that the final relation proved by the
    -- chain of reasoning can be converted into the required relation.

    data IsStrict {x y} : x IsRelatedTo y → Set (a ⊔ ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃) where
      isStrict : ∀ x<y → IsStrict (strict x<y)

    IsStrict? : ∀ {x y} (x≲y : x IsRelatedTo y) → Dec (IsStrict x≲y)
    IsStrict? (strict    x<y) = yes (isStrict x<y)
    IsStrict? (nonstrict _)   = no λ()
    IsStrict? (equals    _)   = no λ()

    extractStrict : ∀ {x y} {x≲y : x IsRelatedTo y} → IsStrict x≲y → x < y
    extractStrict (isStrict x<y) = x<y

    strictRelation : SubRelation _IsRelatedTo_ _ _
    strictRelation = record
      { IsS = IsStrict
      ; IsS? = IsStrict?
      ; extract = extractStrict
      }

    ------------------------------------------------------------------------
    -- Equality sub-relation

    data IsEquality {x y} : x IsRelatedTo y → Set (a ⊔ ℓ₁ ⊔ ℓ₂ ⊔ ℓ₃) where
      isEquality : ∀ x≈y → IsEquality (equals x≈y)

    IsEquality? : ∀ {x y} (x≲y : x IsRelatedTo y) → Dec (IsEquality x≲y)
    IsEquality? (strict    _) = no λ()
    IsEquality? (nonstrict _) = no λ()
    IsEquality? (equals x≈y)  = yes (isEquality x≈y)

    extractEquality : ∀ {x y} {x≲y : x IsRelatedTo y} → IsEquality x≲y → x ≈ y
    extractEquality (isEquality x≈y) = x≈y

    eqRelation : SubRelation _IsRelatedTo_ _ _
    eqRelation = record
      { IsS = IsEquality
      ; IsS? = IsEquality?
      ; extract = extractEquality
      }

    ------------------------------------------------------------------------
    -- Reasoning combinators

    open begin-syntax _IsRelatedTo_ start public
    open begin-equality-syntax _IsRelatedTo_ eqRelation public
    open begin-strict-syntax _IsRelatedTo_ strictRelation public
    open begin-contradiction-syntax _IsRelatedTo_ strictRelation <-asym public
    open ≡-syntax _IsRelatedTo_ ≡-go public
    open ≈-syntax _IsRelatedTo_ _IsRelatedTo_ ≈-go Eq.sym public
    open ≤-syntax _IsRelatedTo_ _IsRelatedTo_ ≤-go public
    open <-syntax _IsRelatedTo_ _IsRelatedTo_ <-go public
    open end-syntax _IsRelatedTo_ stop public