------------------------------------------------------------------------
-- The Agda standard library
--
-- Sums of binary relations
------------------------------------------------------------------------

module Relation.Binary.Sum where

open import Data.Sum as Sum
open import Data.Product
open import Data.Unit using (⊤)
open import Data.Empty
open import Function
open import Function.Equality as F using (_⟶_; _⟨$⟩_)
open import Function.Equivalence as Eq
  using (Equivalence; _⇔_; module Equivalence)
open import Function.Injection as Inj
  using (Injection; _↣_; module Injection)
open import Function.Inverse as Inv
  using (Inverse; _↔_; module Inverse)
open import Function.LeftInverse as LeftInv
  using (LeftInverse; _↞_; module LeftInverse)
open import Function.Related
open import Function.Surjection as Surj
  using (Surjection; _↠_; module Surjection)
open import Level
open import Relation.Nullary
import Relation.Nullary.Decidable as Dec
open import Relation.Binary
open import Relation.Binary.PropositionalEquality as P using (_≡_)

module _ {a₁ a₂} {A₁ : Set a₁} {A₂ : Set a₂} where

  ----------------------------------------------------------------------
  -- Sums of relations

  infixr 1 _⊎-Rel_ _⊎-<_

  -- Generalised sum.

  data ⊎ʳ {ℓ₁ ℓ₂} (P : Set) (_∼₁_ : Rel A₁ ℓ₁) (_∼₂_ : Rel A₂ ℓ₂) :
          A₁ ⊎ A₂ → A₁ ⊎ A₂ → Set (a₁ ⊔ a₂ ⊔ ℓ₁ ⊔ ℓ₂) where
    ₁∼₂ : ∀ {x y} (p : P)         → ⊎ʳ P _∼₁_ _∼₂_ (inj₁ x) (inj₂ y)
    ₁∼₁ : ∀ {x y} (x∼₁y : x ∼₁ y) → ⊎ʳ P _∼₁_ _∼₂_ (inj₁ x) (inj₁ y)
    ₂∼₂ : ∀ {x y} (x∼₂y : x ∼₂ y) → ⊎ʳ P _∼₁_ _∼₂_ (inj₂ x) (inj₂ y)

  -- Pointwise sum.

  _⊎-Rel_ : ∀ {ℓ₁ ℓ₂} → Rel A₁ ℓ₁ → Rel A₂ ℓ₂ → Rel (A₁ ⊎ A₂) _
  _⊎-Rel_ = ⊎ʳ ⊥

  -- All things to the left are "smaller than" all things to the
  -- right.

  _⊎-<_ : ∀ {ℓ₁ ℓ₂} → Rel A₁ ℓ₁ → Rel A₂ ℓ₂ → Rel (A₁ ⊎ A₂) _
  _⊎-<_ = ⊎ʳ ⊤

  ----------------------------------------------------------------------
  -- Helpers

  private

    ₁≁₂ : ∀ {ℓ₁ ℓ₂} {∼₁ : Rel A₁ ℓ₁} {∼₂ : Rel A₂ ℓ₂} →
          ∀ {x y} → ¬ (inj₁ x ⟨ ∼₁ ⊎-Rel ∼₂ ⟩ inj₂ y)
    ₁≁₂ (₁∼₂ ())

    drop-inj₁ : ∀ {ℓ₁ ℓ₂} {∼₁ : Rel A₁ ℓ₁} {∼₂ : Rel A₂ ℓ₂} →
                ∀ {P x y} → inj₁ x ⟨ ⊎ʳ P ∼₁ ∼₂ ⟩ inj₁ y → ∼₁ x y
    drop-inj₁ (₁∼₁ x∼y) = x∼y

    drop-inj₂ : ∀ {ℓ₁ ℓ₂} {∼₁ : Rel A₁ ℓ₁} {∼₂ : Rel A₂ ℓ₂} →
                ∀ {P x y} → inj₂ x ⟨ ⊎ʳ P ∼₁ ∼₂ ⟩ inj₂ y → ∼₂ x y
    drop-inj₂ (₂∼₂ x∼y) = x∼y

  ----------------------------------------------------------------------
  -- Some properties which are preserved by the relation formers above

  _⊎-reflexive_ : ∀ {ℓ₁ ℓ₁′} {≈₁ : Rel A₁ ℓ₁} {∼₁ : Rel A₁ ℓ₁′}
                    {ℓ₂ ℓ₂′} {≈₂ : Rel A₂ ℓ₂} {∼₂ : Rel A₂ ℓ₂′} →
                  ≈₁ ⇒ ∼₁ → ≈₂ ⇒ ∼₂ →
                  ∀ {P} → (≈₁ ⊎-Rel ≈₂) ⇒ (⊎ʳ P ∼₁ ∼₂)
  refl₁ ⊎-reflexive refl₂ = refl
    where
    refl : (_ ⊎-Rel _) ⇒ (⊎ʳ _ _ _)
    refl (₁∼₁ x₁≈y₁) = ₁∼₁ (refl₁ x₁≈y₁)
    refl (₂∼₂ x₂≈y₂) = ₂∼₂ (refl₂ x₂≈y₂)
    refl (₁∼₂ ())

  _⊎-refl_ : ∀ {ℓ₁ ℓ₂} {∼₁ : Rel A₁ ℓ₁} {∼₂ : Rel A₂ ℓ₂} →
             Reflexive ∼₁ → Reflexive ∼₂ → Reflexive (∼₁ ⊎-Rel ∼₂)
  refl₁ ⊎-refl refl₂ = refl
    where
    refl : Reflexive (_ ⊎-Rel _)
    refl {x = inj₁ _} = ₁∼₁ refl₁
    refl {x = inj₂ _} = ₂∼₂ refl₂

  _⊎-irreflexive_ : ∀ {ℓ₁ ℓ₁′} {≈₁ : Rel A₁ ℓ₁} {<₁ : Rel A₁ ℓ₁′}
                      {ℓ₂ ℓ₂′} {≈₂ : Rel A₂ ℓ₂} {<₂ : Rel A₂ ℓ₂′} →
                    Irreflexive ≈₁ <₁ → Irreflexive ≈₂ <₂ →
                    ∀ {P} → Irreflexive (≈₁ ⊎-Rel ≈₂) (⊎ʳ P <₁ <₂)
  irrefl₁ ⊎-irreflexive irrefl₂ = irrefl
    where
    irrefl : Irreflexive (_ ⊎-Rel _) (⊎ʳ _ _ _)
    irrefl (₁∼₁ x₁≈y₁) (₁∼₁ x₁<y₁) = irrefl₁ x₁≈y₁ x₁<y₁
    irrefl (₂∼₂ x₂≈y₂) (₂∼₂ x₂<y₂) = irrefl₂ x₂≈y₂ x₂<y₂
    irrefl (₁∼₂ ())    _

  _⊎-symmetric_ : ∀ {ℓ₁ ℓ₂} {∼₁ : Rel A₁ ℓ₁} {∼₂ : Rel A₂ ℓ₂} →
                  Symmetric ∼₁ → Symmetric ∼₂ → Symmetric (∼₁ ⊎-Rel ∼₂)
  sym₁ ⊎-symmetric sym₂ = sym
    where
    sym : Symmetric (_ ⊎-Rel _)
    sym (₁∼₁ x₁∼y₁) = ₁∼₁ (sym₁ x₁∼y₁)
    sym (₂∼₂ x₂∼y₂) = ₂∼₂ (sym₂ x₂∼y₂)
    sym (₁∼₂ ())

  _⊎-transitive_ : ∀ {ℓ₁ ℓ₂} {∼₁ : Rel A₁ ℓ₁} {∼₂ : Rel A₂ ℓ₂} →
                   Transitive ∼₁ → Transitive ∼₂ →
                   ∀ {P} → Transitive (⊎ʳ P ∼₁ ∼₂)
  trans₁ ⊎-transitive trans₂ = trans
    where
    trans : Transitive (⊎ʳ _ _ _)
    trans (₁∼₁ x∼y) (₁∼₁ y∼z) = ₁∼₁ (trans₁ x∼y y∼z)
    trans (₂∼₂ x∼y) (₂∼₂ y∼z) = ₂∼₂ (trans₂ x∼y y∼z)
    trans (₁∼₂ p)   (₂∼₂ _)     = ₁∼₂ p
    trans (₁∼₁ _)   (₁∼₂ p)     = ₁∼₂ p

  _⊎-antisymmetric_ : ∀ {ℓ₁ ℓ₁′} {≈₁ : Rel A₁ ℓ₁} {≤₁ : Rel A₁ ℓ₁′}
                        {ℓ₂ ℓ₂′} {≈₂ : Rel A₂ ℓ₂} {≤₂ : Rel A₂ ℓ₂′} →
                      Antisymmetric ≈₁ ≤₁ → Antisymmetric ≈₂ ≤₂ →
                      ∀ {P} → Antisymmetric (≈₁ ⊎-Rel ≈₂) (⊎ʳ P ≤₁ ≤₂)
  antisym₁ ⊎-antisymmetric antisym₂ = antisym
    where
    antisym : Antisymmetric (_ ⊎-Rel _) (⊎ʳ _ _ _)
    antisym (₁∼₁ x≤y) (₁∼₁ y≤x) = ₁∼₁ (antisym₁ x≤y y≤x)
    antisym (₂∼₂ x≤y) (₂∼₂ y≤x) = ₂∼₂ (antisym₂ x≤y y≤x)
    antisym (₁∼₂ _)   ()

  _⊎-asymmetric_ : ∀ {ℓ₁ ℓ₂} {<₁ : Rel A₁ ℓ₁} {<₂ : Rel A₂ ℓ₂} →
                   Asymmetric <₁ → Asymmetric <₂ →
                   ∀ {P} → Asymmetric (⊎ʳ P <₁ <₂)
  asym₁ ⊎-asymmetric asym₂ = asym
    where
    asym : Asymmetric (⊎ʳ _ _ _)
    asym (₁∼₁ x<y) (₁∼₁ y<x) = asym₁ x<y y<x
    asym (₂∼₂ x<y) (₂∼₂ y<x) = asym₂ x<y y<x
    asym (₁∼₂ _)   ()

  _⊎-≈-respects₂_ : ∀ {ℓ₁ ℓ₁′} {≈₁ : Rel A₁ ℓ₁} {∼₁ : Rel A₁ ℓ₁′}
                      {ℓ₂ ℓ₂′} {≈₂ : Rel A₂ ℓ₂} {∼₂ : Rel A₂ ℓ₂′} →
                    ∼₁ Respects₂ ≈₁ → ∼₂ Respects₂ ≈₂ →
                    ∀ {P} → (⊎ʳ P ∼₁ ∼₂) Respects₂ (≈₁ ⊎-Rel ≈₂)
  _⊎-≈-respects₂_ {≈₁ = ≈₁} {∼₁ = ∼₁}{≈₂ = ≈₂} {∼₂ = ∼₂}
                  resp₁ resp₂ {P} =
    (λ {_ _ _} → resp¹) ,
    (λ {_ _ _} → resp²)
    where
    resp¹ : ∀ {x} → ((⊎ʳ P ∼₁ ∼₂) x) Respects (≈₁ ⊎-Rel ≈₂)
    resp¹ (₁∼₁ y≈y') (₁∼₁ x∼y) = ₁∼₁ (proj₁ resp₁ y≈y' x∼y)
    resp¹ (₂∼₂ y≈y') (₂∼₂ x∼y) = ₂∼₂ (proj₁ resp₂ y≈y' x∼y)
    resp¹ (₂∼₂ y≈y') (₁∼₂ p)   = (₁∼₂ p)
    resp¹ (₁∼₂ ())   _

    resp² :  ∀ {y}
          → (flip (⊎ʳ P ∼₁ ∼₂) y) Respects (≈₁ ⊎-Rel ≈₂)
    resp² (₁∼₁ x≈x') (₁∼₁ x∼y) = ₁∼₁ (proj₂ resp₁ x≈x' x∼y)
    resp² (₂∼₂ x≈x') (₂∼₂ x∼y) = ₂∼₂ (proj₂ resp₂ x≈x' x∼y)
    resp² (₁∼₁ x≈x') (₁∼₂ p)   = (₁∼₂ p)
    resp² (₁∼₂ ())   _

  _⊎-substitutive_ : ∀ {ℓ₁ ℓ₂ ℓ₃} {∼₁ : Rel A₁ ℓ₁} {∼₂ : Rel A₂ ℓ₂} →
                     Substitutive ∼₁ ℓ₃ → Substitutive ∼₂ ℓ₃ →
                     Substitutive (∼₁ ⊎-Rel ∼₂) ℓ₃
  subst₁ ⊎-substitutive subst₂ = subst
    where
    subst : Substitutive (_ ⊎-Rel _) _
    subst P (₁∼₁ x∼y) Px = subst₁ (λ z → P (inj₁ z)) x∼y Px
    subst P (₂∼₂ x∼y) Px = subst₂ (λ z → P (inj₂ z)) x∼y Px
    subst P (₁∼₂ ())  Px

  ⊎-decidable : ∀ {ℓ₁ ℓ₂} {∼₁ : Rel A₁ ℓ₁} {∼₂ : Rel A₂ ℓ₂} →
                Decidable ∼₁ → Decidable ∼₂ →
                ∀ {P} → (∀ {x y} → Dec (inj₁ x ⟨ ⊎ʳ P ∼₁ ∼₂ ⟩ inj₂ y)) →
                Decidable (⊎ʳ P ∼₁ ∼₂)
  ⊎-decidable {∼₁ = ∼₁} {∼₂ = ∼₂} dec₁ dec₂ {P} dec₁₂ = dec
    where
    dec : Decidable (⊎ʳ P ∼₁ ∼₂)
    dec (inj₁ x) (inj₁ y) with dec₁ x y
    ...                   | yes x∼y = yes (₁∼₁ x∼y)
    ...                   | no  x≁y = no (x≁y ∘ drop-inj₁)
    dec (inj₂ x) (inj₂ y) with dec₂ x y
    ...                   | yes x∼y = yes (₂∼₂ x∼y)
    ...                   | no  x≁y = no (x≁y ∘ drop-inj₂)
    dec (inj₁ x) (inj₂ y) = dec₁₂
    dec (inj₂ x) (inj₁ y) = no (λ())

  _⊎-<-total_ : ∀ {ℓ₁ ℓ₂} {≤₁ : Rel A₁ ℓ₁} {≤₂ : Rel A₂ ℓ₂} →
                Total ≤₁ → Total ≤₂ → Total (≤₁ ⊎-< ≤₂)
  total₁ ⊎-<-total total₂ = total
    where
    total : Total (_ ⊎-< _)
    total (inj₁ x) (inj₁ y) = Sum.map ₁∼₁ ₁∼₁ $ total₁ x y
    total (inj₂ x) (inj₂ y) = Sum.map ₂∼₂ ₂∼₂ $ total₂ x y
    total (inj₁ x) (inj₂ y) = inj₁ (₁∼₂ _)
    total (inj₂ x) (inj₁ y) = inj₂ (₁∼₂ _)

  _⊎-<-trichotomous_ : ∀ {ℓ₁ ℓ₁′} {≈₁ : Rel A₁ ℓ₁} {<₁ : Rel A₁ ℓ₁′}
                         {ℓ₂ ℓ₂′} {≈₂ : Rel A₂ ℓ₂} {<₂ : Rel A₂ ℓ₂′} →
                       Trichotomous ≈₁ <₁ → Trichotomous ≈₂ <₂ →
                       Trichotomous (≈₁ ⊎-Rel ≈₂) (<₁ ⊎-< <₂)
  _⊎-<-trichotomous_ {≈₁ = ≈₁} {<₁ = <₁} {≈₂ = ≈₂} {<₂ = <₂}
                     tri₁ tri₂ = tri
    where
    tri : Trichotomous (≈₁ ⊎-Rel ≈₂) (<₁ ⊎-< <₂)
    tri (inj₁ x) (inj₂ y) = tri< (₁∼₂ _) ₁≁₂ (λ())
    tri (inj₂ x) (inj₁ y) = tri> (λ()) (λ()) (₁∼₂ _)
    tri (inj₁ x) (inj₁ y) with tri₁ x y
    ...                   | tri< x<y x≉y x≯y =
      tri< (₁∼₁ x<y)        (x≉y ∘ drop-inj₁) (x≯y ∘ drop-inj₁)
    ...                   | tri≈ x≮y x≈y x≯y =
      tri≈ (x≮y ∘ drop-inj₁) (₁∼₁ x≈y)    (x≯y ∘ drop-inj₁)
    ...                   | tri> x≮y x≉y x>y =
      tri> (x≮y ∘ drop-inj₁) (x≉y ∘ drop-inj₁) (₁∼₁ x>y)
    tri (inj₂ x) (inj₂ y) with tri₂ x y
    ...                   | tri< x<y x≉y x≯y =
      tri< (₂∼₂ x<y)        (x≉y ∘ drop-inj₂) (x≯y ∘ drop-inj₂)
    ...                   | tri≈ x≮y x≈y x≯y =
      tri≈ (x≮y ∘ drop-inj₂) (₂∼₂ x≈y)    (x≯y ∘ drop-inj₂)
    ...                   | tri> x≮y x≉y x>y =
      tri> (x≮y ∘ drop-inj₂) (x≉y ∘ drop-inj₂) (₂∼₂ x>y)

  ----------------------------------------------------------------------
  -- Some collections of properties which are preserved

  _⊎-isEquivalence_ : ∀ {ℓ₁ ℓ₂} {≈₁ : Rel A₁ ℓ₁} {≈₂ : Rel A₂ ℓ₂} →
                      IsEquivalence ≈₁ → IsEquivalence ≈₂ →
                      IsEquivalence (≈₁ ⊎-Rel ≈₂)
  eq₁ ⊎-isEquivalence eq₂ = record
    { refl  = refl  eq₁ ⊎-refl        refl  eq₂
    ; sym   = sym   eq₁ ⊎-symmetric   sym   eq₂
    ; trans = trans eq₁ ⊎-transitive  trans eq₂
    }
    where open IsEquivalence

  _⊎-isPreorder_ : ∀ {ℓ₁ ℓ₁′} {≈₁ : Rel A₁ ℓ₁} {∼₁ : Rel A₁ ℓ₁′}
                     {ℓ₂ ℓ₂′} {≈₂ : Rel A₂ ℓ₂} {∼₂ : Rel A₂ ℓ₂′} →
                   IsPreorder ≈₁ ∼₁ → IsPreorder ≈₂ ∼₂ →
                   ∀ {P} → IsPreorder (≈₁ ⊎-Rel ≈₂) (⊎ʳ P ∼₁ ∼₂)
  pre₁ ⊎-isPreorder pre₂ = record
    { isEquivalence = isEquivalence pre₁ ⊎-isEquivalence
                      isEquivalence pre₂
    ; reflexive     = reflexive pre₁ ⊎-reflexive   reflexive pre₂
    ; trans         = trans     pre₁ ⊎-transitive  trans     pre₂
    }
    where open IsPreorder

  _⊎-isDecEquivalence_ : ∀ {ℓ₁ ℓ₂} {≈₁ : Rel A₁ ℓ₁} {≈₂ : Rel A₂ ℓ₂} →
                         IsDecEquivalence ≈₁ → IsDecEquivalence ≈₂ →
                         IsDecEquivalence (≈₁ ⊎-Rel ≈₂)
  eq₁ ⊎-isDecEquivalence eq₂ = record
    { isEquivalence = isEquivalence eq₁ ⊎-isEquivalence
                      isEquivalence eq₂
    ; _≟_           = ⊎-decidable (_≟_ eq₁) (_≟_ eq₂) (no ₁≁₂)
    }
    where open IsDecEquivalence

  _⊎-isPartialOrder_ : ∀ {ℓ₁ ℓ₁′} {≈₁ : Rel A₁ ℓ₁} {≤₁ : Rel A₁ ℓ₁′}
                         {ℓ₂ ℓ₂′} {≈₂ : Rel A₂ ℓ₂} {≤₂ : Rel A₂ ℓ₂′} →
                       IsPartialOrder ≈₁ ≤₁ → IsPartialOrder ≈₂ ≤₂ →
                       ∀ {P} → IsPartialOrder (≈₁ ⊎-Rel ≈₂) (⊎ʳ P ≤₁ ≤₂)
  po₁ ⊎-isPartialOrder po₂ = record
    { isPreorder = isPreorder po₁ ⊎-isPreorder    isPreorder po₂
    ; antisym    = antisym    po₁ ⊎-antisymmetric antisym    po₂
    }
    where open IsPartialOrder

  _⊎-isStrictPartialOrder_ :
    ∀ {ℓ₁ ℓ₁′} {≈₁ : Rel A₁ ℓ₁} {<₁ : Rel A₁ ℓ₁′}
      {ℓ₂ ℓ₂′} {≈₂ : Rel A₂ ℓ₂} {<₂ : Rel A₂ ℓ₂′} →
    IsStrictPartialOrder ≈₁ <₁ → IsStrictPartialOrder ≈₂ <₂ →
    ∀ {P} → IsStrictPartialOrder (≈₁ ⊎-Rel ≈₂) (⊎ʳ P <₁ <₂)
  spo₁ ⊎-isStrictPartialOrder spo₂ = record
    { isEquivalence = isEquivalence spo₁ ⊎-isEquivalence
                      isEquivalence spo₂
    ; irrefl        = irrefl   spo₁ ⊎-irreflexive irrefl   spo₂
    ; trans         = trans    spo₁ ⊎-transitive  trans    spo₂
    ; <-resp-≈      = <-resp-≈ spo₁ ⊎-≈-respects₂ <-resp-≈ spo₂
    }
    where open IsStrictPartialOrder

  _⊎-<-isTotalOrder_ : ∀ {ℓ₁ ℓ₁′} {≈₁ : Rel A₁ ℓ₁} {≤₁ : Rel A₁ ℓ₁′}
                         {ℓ₂ ℓ₂′} {≈₂ : Rel A₂ ℓ₂} {≤₂ : Rel A₂ ℓ₂′} →
                       IsTotalOrder ≈₁ ≤₁ → IsTotalOrder ≈₂ ≤₂ →
                       IsTotalOrder (≈₁ ⊎-Rel ≈₂) (≤₁ ⊎-< ≤₂)
  to₁ ⊎-<-isTotalOrder to₂ = record
    { isPartialOrder = isPartialOrder to₁ ⊎-isPartialOrder
                       isPartialOrder to₂
    ; total          = total to₁ ⊎-<-total total to₂
    }
    where open IsTotalOrder

  _⊎-<-isDecTotalOrder_ :
    ∀ {ℓ₁ ℓ₁′} {≈₁ : Rel A₁ ℓ₁} {≤₁ : Rel A₁ ℓ₁′}
      {ℓ₂ ℓ₂′} {≈₂ : Rel A₂ ℓ₂} {≤₂ : Rel A₂ ℓ₂′} →
    IsDecTotalOrder ≈₁ ≤₁ → IsDecTotalOrder ≈₂ ≤₂ →
    IsDecTotalOrder (≈₁ ⊎-Rel ≈₂) (≤₁ ⊎-< ≤₂)
  to₁ ⊎-<-isDecTotalOrder to₂ = record
    { isTotalOrder = isTotalOrder to₁ ⊎-<-isTotalOrder isTotalOrder to₂
    ; _≟_          = ⊎-decidable (_≟_  to₁) (_≟_  to₂) (no ₁≁₂)
    ; _≤?_         = ⊎-decidable (_≤?_ to₁) (_≤?_ to₂) (yes (₁∼₂ _))
    }
    where open IsDecTotalOrder

  _⊎-<-isStrictTotalOrder_ :
    ∀ {ℓ₁ ℓ₁′} {≈₁ : Rel A₁ ℓ₁} {<₁ : Rel A₁ ℓ₁′}
      {ℓ₂ ℓ₂′} {≈₂ : Rel A₂ ℓ₂} {<₂ : Rel A₂ ℓ₂′} →
    IsStrictTotalOrder ≈₁ <₁ → IsStrictTotalOrder ≈₂ <₂ →
    IsStrictTotalOrder (≈₁ ⊎-Rel ≈₂) (<₁ ⊎-< <₂)
  sto₁ ⊎-<-isStrictTotalOrder sto₂ = record
    { isEquivalence = isEquivalence sto₁ ⊎-isEquivalence
                      isEquivalence sto₂
    ; trans         = trans    sto₁ ⊎-transitive     trans    sto₂
    ; compare       = compare  sto₁ ⊎-<-trichotomous compare  sto₂
    ; <-resp-≈      = <-resp-≈ sto₁ ⊎-≈-respects₂    <-resp-≈ sto₂
    }
    where open IsStrictTotalOrder

------------------------------------------------------------------------
-- The game can be taken even further...

_⊎-setoid_ : ∀ {s₁ s₂ s₃ s₄} →
             Setoid s₁ s₂ → Setoid s₃ s₄ → Setoid _ _
s₁ ⊎-setoid s₂ = record
  { isEquivalence = isEquivalence s₁ ⊎-isEquivalence isEquivalence s₂
  } where open Setoid

_⊎-preorder_ : ∀ {p₁ p₂ p₃ p₄ p₅ p₆} →
               Preorder p₁ p₂ p₃ → Preorder p₄ p₅ p₆ → Preorder _ _ _
p₁ ⊎-preorder p₂ = record
  { _∼_          = _∼_        p₁ ⊎-Rel        _∼_        p₂
  ; isPreorder   = isPreorder p₁ ⊎-isPreorder isPreorder p₂
  } where open Preorder

_⊎-decSetoid_ : ∀ {s₁ s₂ s₃ s₄} →
                DecSetoid s₁ s₂ → DecSetoid s₃ s₄ → DecSetoid _ _
ds₁ ⊎-decSetoid ds₂ = record
  { isDecEquivalence = isDecEquivalence ds₁ ⊎-isDecEquivalence
                       isDecEquivalence ds₂
  } where open DecSetoid

_⊎-poset_ : ∀ {p₁ p₂ p₃ p₄ p₅ p₆} →
            Poset p₁ p₂ p₃ → Poset p₄ p₅ p₆ → Poset _ _ _
po₁ ⊎-poset po₂ = record
  { _≤_            = _≤_ po₁ ⊎-Rel _≤_ po₂
  ; isPartialOrder = isPartialOrder po₁ ⊎-isPartialOrder
                     isPartialOrder po₂
  } where open Poset

_⊎-<-poset_ : ∀ {p₁ p₂ p₃ p₄ p₅ p₆} →
              Poset p₁ p₂ p₃ → Poset p₄ p₅ p₆ → Poset _ _ _
po₁ ⊎-<-poset po₂ = record
  { _≤_            = _≤_ po₁ ⊎-< _≤_ po₂
  ; isPartialOrder = isPartialOrder po₁ ⊎-isPartialOrder
                     isPartialOrder po₂
  } where open Poset

_⊎-<-strictPartialOrder_ :
  ∀ {p₁ p₂ p₃ p₄ p₅ p₆} →
  StrictPartialOrder p₁ p₂ p₃ → StrictPartialOrder p₄ p₅ p₆ →
  StrictPartialOrder _ _ _
spo₁ ⊎-<-strictPartialOrder spo₂ = record
  { _<_                  = _<_ spo₁ ⊎-< _<_ spo₂
  ; isStrictPartialOrder = isStrictPartialOrder spo₁
                             ⊎-isStrictPartialOrder
                           isStrictPartialOrder spo₂
  } where open StrictPartialOrder

_⊎-<-totalOrder_ :
  ∀ {t₁ t₂ t₃ t₄ t₅ t₆} →
  TotalOrder t₁ t₂ t₃ → TotalOrder t₄ t₅ t₆ → TotalOrder _ _ _
to₁ ⊎-<-totalOrder to₂ = record
  { isTotalOrder = isTotalOrder to₁ ⊎-<-isTotalOrder isTotalOrder to₂
  } where open TotalOrder

_⊎-<-decTotalOrder_ :
  ∀ {t₁ t₂ t₃ t₄ t₅ t₆} →
  DecTotalOrder t₁ t₂ t₃ → DecTotalOrder t₄ t₅ t₆ → DecTotalOrder _ _ _
to₁ ⊎-<-decTotalOrder to₂ = record
  { isDecTotalOrder = isDecTotalOrder to₁ ⊎-<-isDecTotalOrder
                      isDecTotalOrder to₂
  } where open DecTotalOrder

_⊎-<-strictTotalOrder_ :
  ∀ {p₁ p₂ p₃ p₄ p₅ p₆} →
  StrictTotalOrder p₁ p₂ p₃ → StrictTotalOrder p₄ p₅ p₆ →
  StrictTotalOrder _ _ _
sto₁ ⊎-<-strictTotalOrder sto₂ = record
  { _<_                = _<_ sto₁ ⊎-< _<_ sto₂
  ; isStrictTotalOrder = isStrictTotalOrder sto₁
                           ⊎-<-isStrictTotalOrder
                         isStrictTotalOrder sto₂
  } where open StrictTotalOrder

------------------------------------------------------------------------
-- Some properties related to "relatedness"

private

  to-cong : ∀ {a b} {A : Set a} {B : Set b} →
            (_≡_ ⊎-Rel _≡_) ⇒ _≡_ {A = A ⊎ B}
  to-cong (₁∼₂ ())
  to-cong (₁∼₁ P.refl) = P.refl
  to-cong (₂∼₂ P.refl) = P.refl

  from-cong : ∀ {a b} {A : Set a} {B : Set b} →
              _≡_ {A = A ⊎ B} ⇒ (_≡_ ⊎-Rel _≡_)
  from-cong P.refl = P.refl ⊎-refl P.refl

⊎-Rel↔≡ : ∀ {a b} (A : Set a) (B : Set b) →
          Inverse (P.setoid A ⊎-setoid P.setoid B) (P.setoid (A ⊎ B))
⊎-Rel↔≡ _ _ = record
  { to         = record { _⟨$⟩_ = id; cong = to-cong   }
  ; from       = record { _⟨$⟩_ = id; cong = from-cong }
  ; inverse-of = record
    { left-inverse-of  = λ _ → P.refl ⊎-refl P.refl
    ; right-inverse-of = λ _ → P.refl
    }
  }

_⊎-≟_ : ∀ {a b} {A : Set a} {B : Set b} →
        Decidable {A = A} _≡_ → Decidable {A = B} _≡_ →
        Decidable {A = A ⊎ B} _≡_
(dec₁ ⊎-≟ dec₂) s₁ s₂ = Dec.map′ to-cong from-cong (s₁ ≟ s₂)
  where
  open DecSetoid (P.decSetoid dec₁ ⊎-decSetoid P.decSetoid dec₂)

_⊎-⟶_ :
  ∀ {s₁ s₂ s₃ s₄ s₅ s₆ s₇ s₈}
    {A : Setoid s₁ s₂} {B : Setoid s₃ s₄}
    {C : Setoid s₅ s₆} {D : Setoid s₇ s₈} →
  A ⟶ B → C ⟶ D → (A ⊎-setoid C) ⟶ (B ⊎-setoid D)
_⊎-⟶_ {A = A} {B} {C} {D} f g = record
  { _⟨$⟩_ = fg
  ; cong  = fg-cong
  }
  where
  open Setoid (A ⊎-setoid C) using () renaming (_≈_ to _≈AC_)
  open Setoid (B ⊎-setoid D) using () renaming (_≈_ to _≈BD_)

  fg = Sum.map (_⟨$⟩_ f) (_⟨$⟩_ g)

  fg-cong : _≈AC_ =[ fg ]⇒ _≈BD_
  fg-cong (₁∼₂ ())
  fg-cong (₁∼₁ x∼₁y) = ₁∼₁ $ F.cong f x∼₁y
  fg-cong (₂∼₂ x∼₂y) = ₂∼₂ $ F.cong g x∼₂y

_⊎-equivalence_ :
  ∀ {s₁ s₂ s₃ s₄ s₅ s₆ s₇ s₈}
    {A : Setoid s₁ s₂} {B : Setoid s₃ s₄}
    {C : Setoid s₅ s₆} {D : Setoid s₇ s₈} →
  Equivalence A B → Equivalence C D →
  Equivalence (A ⊎-setoid C) (B ⊎-setoid D)
A⇔B ⊎-equivalence C⇔D = record
  { to   = to   A⇔B ⊎-⟶ to   C⇔D
  ; from = from A⇔B ⊎-⟶ from C⇔D
  } where open Equivalence

_⊎-⇔_ : ∀ {a b c d} {A : Set a} {B : Set b} {C : Set c} {D : Set d} →
        A ⇔ B → C ⇔ D → (A ⊎ C) ⇔ (B ⊎ D)
_⊎-⇔_ {A = A} {B} {C} {D} A⇔B C⇔D =
  Inverse.equivalence (⊎-Rel↔≡ B D) ⟨∘⟩
  A⇔B ⊎-equivalence C⇔D ⟨∘⟩
  Eq.sym (Inverse.equivalence (⊎-Rel↔≡ A C))
  where open Eq using () renaming (_∘_ to _⟨∘⟩_)

_⊎-injection_ :
  ∀ {s₁ s₂ s₃ s₄ s₅ s₆ s₇ s₈}
    {A : Setoid s₁ s₂} {B : Setoid s₃ s₄}
    {C : Setoid s₅ s₆} {D : Setoid s₇ s₈} →
  Injection A B → Injection C D →
  Injection (A ⊎-setoid C) (B ⊎-setoid D)
_⊎-injection_ {A = A} {B} {C} {D} A↣B C↣D = record
  { to        = to A↣B ⊎-⟶ to C↣D
  ; injective = inj _ _
  }
  where
  open Injection
  open Setoid (A ⊎-setoid C) using () renaming (_≈_ to _≈AC_)
  open Setoid (B ⊎-setoid D) using () renaming (_≈_ to _≈BD_)

  inj : ∀ x y →
        (to A↣B ⊎-⟶ to C↣D) ⟨$⟩ x ≈BD (to A↣B ⊎-⟶ to C↣D) ⟨$⟩ y →
        x ≈AC y
  inj (inj₁ x) (inj₁ y) (₁∼₁ x∼₁y) = ₁∼₁ (injective A↣B x∼₁y)
  inj (inj₂ x) (inj₂ y) (₂∼₂ x∼₂y) = ₂∼₂ (injective C↣D x∼₂y)
  inj (inj₁ x) (inj₂ y) (₁∼₂ ())
  inj (inj₂ x) (inj₁ y) ()

_⊎-↣_ : ∀ {a b c d} {A : Set a} {B : Set b} {C : Set c} {D : Set d} →
        A ↣ B → C ↣ D → (A ⊎ C) ↣ (B ⊎ D)
_⊎-↣_ {A = A} {B} {C} {D} A↣B C↣D =
  Inverse.injection (⊎-Rel↔≡ B D) ⟨∘⟩
  A↣B ⊎-injection C↣D ⟨∘⟩
  Inverse.injection (Inv.sym (⊎-Rel↔≡ A C))
  where open Inj using () renaming (_∘_ to _⟨∘⟩_)

_⊎-left-inverse_ :
  ∀ {s₁ s₂ s₃ s₄ s₅ s₆ s₇ s₈}
    {A : Setoid s₁ s₂} {B : Setoid s₃ s₄}
    {C : Setoid s₅ s₆} {D : Setoid s₇ s₈} →
  LeftInverse A B → LeftInverse C D →
  LeftInverse (A ⊎-setoid C) (B ⊎-setoid D)
A↞B ⊎-left-inverse C↞D = record
  { to              = Equivalence.to eq
  ; from            = Equivalence.from eq
  ; left-inverse-of = [ ₁∼₁ ∘ left-inverse-of A↞B
                      , ₂∼₂ ∘ left-inverse-of C↞D
                      ]
  }
  where
  open LeftInverse
  eq = LeftInverse.equivalence A↞B ⊎-equivalence
       LeftInverse.equivalence C↞D

_⊎-↞_ : ∀ {a b c d} {A : Set a} {B : Set b} {C : Set c} {D : Set d} →
        A ↞ B → C ↞ D → (A ⊎ C) ↞ (B ⊎ D)
_⊎-↞_ {A = A} {B} {C} {D} A↞B C↞D =
  Inverse.left-inverse (⊎-Rel↔≡ B D) ⟨∘⟩
  A↞B ⊎-left-inverse C↞D ⟨∘⟩
  Inverse.left-inverse (Inv.sym (⊎-Rel↔≡ A C))
  where open LeftInv using () renaming (_∘_ to _⟨∘⟩_)

_⊎-surjection_ :
  ∀ {s₁ s₂ s₃ s₄ s₅ s₆ s₇ s₈}
    {A : Setoid s₁ s₂} {B : Setoid s₃ s₄}
    {C : Setoid s₅ s₆} {D : Setoid s₇ s₈} →
  Surjection A B → Surjection C D →
  Surjection (A ⊎-setoid C) (B ⊎-setoid D)
A↠B ⊎-surjection C↠D = record
  { to         = LeftInverse.from inv
  ; surjective = record
    { from             = LeftInverse.to inv
    ; right-inverse-of = LeftInverse.left-inverse-of inv
    }
  }
  where
  open Surjection
  inv = right-inverse A↠B ⊎-left-inverse right-inverse C↠D

_⊎-↠_ : ∀ {a b c d} {A : Set a} {B : Set b} {C : Set c} {D : Set d} →
        A ↠ B → C ↠ D → (A ⊎ C) ↠ (B ⊎ D)
_⊎-↠_ {A = A} {B} {C} {D} A↠B C↠D =
  Inverse.surjection (⊎-Rel↔≡ B D) ⟨∘⟩
  A↠B ⊎-surjection C↠D ⟨∘⟩
  Inverse.surjection (Inv.sym (⊎-Rel↔≡ A C))
  where open Surj using () renaming (_∘_ to _⟨∘⟩_)

_⊎-inverse_ :
  ∀ {s₁ s₂ s₃ s₄ s₅ s₆ s₇ s₈}
    {A : Setoid s₁ s₂} {B : Setoid s₃ s₄}
    {C : Setoid s₅ s₆} {D : Setoid s₇ s₈} →
  Inverse A B → Inverse C D → Inverse (A ⊎-setoid C) (B ⊎-setoid D)
A↔B ⊎-inverse C↔D = record
  { to         = Surjection.to   surj
  ; from       = Surjection.from surj
  ; inverse-of = record
    { left-inverse-of  = LeftInverse.left-inverse-of inv
    ; right-inverse-of = Surjection.right-inverse-of surj
    }
  }
  where
  open Inverse
  surj = Inverse.surjection   A↔B ⊎-surjection
         Inverse.surjection   C↔D
  inv  = Inverse.left-inverse A↔B ⊎-left-inverse
         Inverse.left-inverse C↔D

_⊎-↔_ : ∀ {a b c d} {A : Set a} {B : Set b} {C : Set c} {D : Set d} →
        A ↔ B → C ↔ D → (A ⊎ C) ↔ (B ⊎ D)
_⊎-↔_ {A = A} {B} {C} {D} A↔B C↔D =
  ⊎-Rel↔≡ B D ⟨∘⟩ A↔B ⊎-inverse C↔D ⟨∘⟩ Inv.sym (⊎-Rel↔≡ A C)
  where open Inv using () renaming (_∘_ to _⟨∘⟩_)

_⊎-cong_ : ∀ {k a b c d} {A : Set a} {B : Set b} {C : Set c} {D : Set d} →
           A ∼[ k ] B → C ∼[ k ] D → (A ⊎ C) ∼[ k ] (B ⊎ D)
_⊎-cong_ {implication}         = Sum.map
_⊎-cong_ {reverse-implication} = λ f g → lam (Sum.map (app-← f) (app-← g))
_⊎-cong_ {equivalence}         = _⊎-⇔_
_⊎-cong_ {injection}           = _⊎-↣_
_⊎-cong_ {reverse-injection}   = λ f g → lam (app-↢ f ⊎-↣ app-↢ g)
_⊎-cong_ {left-inverse}        = _⊎-↞_
_⊎-cong_ {surjection}          = _⊎-↠_
_⊎-cong_ {bijection}           = _⊎-↔_


{-# OPTIONS --cubical --no-import-sorts #-}

module MoreLogic.Definitions where -- hProp logic

open import Cubical.Foundations.Everything renaming (_⁻¹ to _⁻¹ᵖ; assoc to ∙-assoc)
open import Cubical.Data.Sum.Base renaming (_⊎_ to infixr 4 _⊎_)
open import Cubical.Relation.Nullary.Base renaming (¬_ to ¬ᵗ_)
open import Cubical.Foundations.Logic renaming (inl to inlᵖ; inr to inrᵖ)
open import Cubical.Data.Empty renaming (elim to ⊥-elim) renaming (⊥ to ⊥⊥) -- `⊥` and `elim`

import Cubical.Data.Empty as Empty
open import Cubical.Data.Unit.Base

open import MoreLogic.Reasoning
open import Utils

-- lifted versions of ⊥ and ⊤

hPropRel : ∀ {ℓ} (A B : Type ℓ) (ℓ' : Level) → Type (ℓ-max ℓ (ℓ-suc ℓ'))
hPropRel A B ℓ' = A → B → hProp ℓ'

isSetIsProp : ∀{ℓ} {A : Type ℓ} → isProp (isSet A)
isSetIsProp is-set₀ is-set₁ = funExt (λ x → funExt λ y → isPropIsProp (is-set₀ x y) (is-set₁ x y))

isSetᵖ : ∀{ℓ} (A : Type ℓ) → hProp ℓ
isSetᵖ A = isSet A , isSetIsProp

-- hProp-syntax for Σ types of hProps to omit propositional truncation
-- this should be equivalent to ∃! from the standard library but ∃! is not an hProp
-- proof sketch:
--   `∃! A B = isContr (Σ A B) = Σ[ x ∈ Σ A B ] ∀(  y : Σ A B) → x ≡ y`
--   `Σᵖ[]-syntax A B          ≈ Σ[ c ∈ Σ A B ] ∀(x y : Σ A B) → x ≡ y`
-- NOTE: we also have isProp→Iso in `Cubical.Foundations.Isomorphism`

Σᵖ[]-syntax : ∀{ℓ ℓ'} → {A : hProp ℓ'} → ([ A ] → hProp ℓ) → hProp _
Σᵖ[]-syntax {A = A} P = Σ [ A ] ([_] ∘ P) , isPropΣ (isProp[] A) (isProp[] ∘ P)

syntax  Σᵖ[]-syntax (λ x → P) = Σᵖ[ x ] P
infix 2 Σᵖ[]-syntax

Σᵖ[∶]-syntax : ∀{ℓ ℓ'} → {A : hProp ℓ'} → ([ A ] → hProp ℓ) → hProp _
Σᵖ[∶]-syntax {A = A} P = Σ [ A ] ([_] ∘ P) , isPropΣ (isProp[] A) (isProp[] ∘ P)

syntax  Σᵖ[∶]-syntax {A = A} (λ x → P) = Σᵖ[ x ∶ A ] P
infix 2 Σᵖ[∶]-syntax

isProp⊎ˡ : ∀{ℓ ℓ'} {A : Type ℓ} {B : Type ℓ'} → isProp A → isProp B → (A → ¬ᵗ B) → isProp (A ⊎ B)
isProp⊎ˡ pA pB A⇒¬B (inl x) (inl y) = cong inl (pA x y)
isProp⊎ˡ pA pB A⇒¬B (inr x) (inr y) = cong inr (pB x y)
isProp⊎ˡ pA pB A⇒¬B (inl x) (inr y) = ⊥-elim (A⇒¬B x y)
isProp⊎ˡ pA pB A⇒¬B (inr x) (inl y) = ⊥-elim (A⇒¬B y x)

-- hProp-syntax for disjoint unions to omit propositional truncation
⊎ᵖ-syntax : ∀{ℓ ℓ'} (P : hProp ℓ) (Q : hProp ℓ') → {[ P ] → ¬ᵗ [ Q ]} → hProp _
⊎ᵖ-syntax P Q {P⇒¬Q} = ([ P ] ⊎ [ Q ]) , isProp⊎ˡ (isProp[] P) (isProp[] Q) P⇒¬Q

syntax ⊎ᵖ-syntax P Q {P⇒¬Q} = [ P⇒¬Q ] P ⊎ᵖ Q

{-# DISPLAY ⊎ᵖ-syntax a b = a ⊎ b #-}

-- hProp-syntax for equality on sets to omit propositional truncation
≡ˢ-syntax : {ℓ : Level} {A : Type ℓ} → A → A → {isset : isSet A} → hProp ℓ
≡ˢ-syntax a b {isset} = (a ≡ b) , isset a b

syntax ≡ˢ-syntax a b {isset} = [ isset ] a ≡ˢ b

infix 1 ⊎ᵖ-syntax
infix 1 ≡ˢ-syntax

{-# DISPLAY ≡ˢ-syntax a b = a ≡ b #-}

-- pretty print ∀-syntax from cubical standard library
{-# DISPLAY ∀[]-syntax {A = A} P = P #-}
-- {-# DISPLAY ∀[]-syntax {A = A} P = ∃ P #-}
-- {-# DISPLAY ∀[]-syntax (λ a → P) = ∀[ a ] P #-}
-- {-# DISPLAY ∃[]-syntax (λ x → P) = ∃[ x ] P #-}

-- ∀-syntax to quantify over hProps (without needing to `[_]` them)
∀ᵖ[∶]-syntax : ∀{ℓ ℓ'} {A : hProp ℓ'} → ([ A ] → hProp ℓ) → hProp (ℓ-max ℓ ℓ')
∀ᵖ[∶]-syntax {A = A} P = (∀ x → [ P x ]) , isPropΠ (isProp[] ∘ P)

syntax ∀ᵖ[∶]-syntax {A = A} (λ a → P) = ∀ᵖ[ a ∶ A ] P
infix 2 ∀ᵖ[∶]-syntax

{-# DISPLAY ∀ᵖ[∶]-syntax {A = A} P = P #-}

-- isPropΠ for a function with an instance argument
isPropΠⁱ : ∀{ℓ ℓ'} {A : Type ℓ} {B : {{p : A}} → Type ℓ'} (h : (x : A) → isProp (B {{x}})) → isProp ({{x : A}} → B {{x}})
isPropΠⁱ h f g i {{x}} = (h x) (f {{x}}) (g {{x}}) i

-- ∀-syntax to quantify over hProps (without needing to `[_]` them) which produces an intance argument
∀ᵖ〚∶〛-syntax : ∀{ℓ ℓ'} {A : hProp ℓ'} → ([ A ] → hProp ℓ) → hProp (ℓ-max ℓ ℓ')
∀ᵖ〚∶〛-syntax {A = A} P = (∀ {{x}} → [ P x ]) , isPropΠⁱ (isProp[] ∘ P)

syntax ∀ᵖ〚∶〛-syntax {A = A} (λ a → P) = ∀ᵖ〚 a ∶ A 〛 P
infix 2 ∀ᵖ〚∶〛-syntax

!isProp : ∀{ℓ} {P : Type ℓ} → isProp P → isProp (! P)
!isProp is-prop (!! x) (!! y) = subst (λ z → (!! x) ≡ (!! z)) (is-prop x y) refl

∀ᵖ!〚∶〛-syntax : ∀{ℓ ℓ'} {A : hProp ℓ'} → (! [ A ] → hProp ℓ) → hProp (ℓ-max ℓ ℓ')
∀ᵖ!〚∶〛-syntax {A = A} P = (∀ {{x}} → [ P x ]) , isPropΠⁱ (λ p → isProp[] (P (p))) -- isPropΠⁱ (isProp[] ∘ P) -- isPropΠⁱ (!isProp ∘ isProp[] ∘ P) --  --

syntax ∀ᵖ!〚∶〛-syntax {A = A} (λ a → P) = ∀ᵖ!〚 a ∶ A 〛 P
infix 2 ∀ᵖ!〚∶〛-syntax

{-# DISPLAY ∀ᵖ[∶]-syntax {A = A} P = P #-}

-- ∀-syntax which produces an intance argument
∀〚∶〛-syntax : ∀{ℓ ℓ'} {A : Type ℓ'} → (A → hProp ℓ) → hProp _
∀〚∶〛-syntax {A = A} P = (∀ {{x}} → [ P x ]) , isPropΠⁱ (isProp[] ∘ P)

syntax ∀〚∶〛-syntax {A = A} (λ a → P) = ∀〚 a ∶ A 〛 P
infix 2 ∀〚∶〛-syntax

{-# DISPLAY ∀〚∶〛-syntax {A = A} P = P #-}

⊎⇒⊔ : ∀ {ℓ ℓ'} (P : hProp ℓ) (Q : hProp ℓ') → [ P ] ⊎ [ Q ] → [ P ⊔ Q ]
⊎⇒⊔ P Q (inl x) = inlᵖ x
⊎⇒⊔ P Q (inr x) = inrᵖ x

-- pretty `⊔-elim`
case[⊔]-syntaxᵈ : ∀ {ℓ ℓ' ℓ''} (P : hProp ℓ) (Q : hProp ℓ')
                  → (z : [ P ⊔ Q ])
                  → (R : [ P ⊔ Q ] → hProp ℓ'')
                  → (S : (x : [ P ] ⊎ [ Q ]) → [ R (⊎⇒⊔ P Q x) ] )
                  → [ R z ]
case[⊔]-syntaxᵈ P Q z R S = ⊔-elim P Q R (λ p → S (inl p)) (λ q → S (inr q)) z

syntax case[⊔]-syntaxᵈ P Q z (λ x → R) S = case z as x ∶ P ⊔ Q ⇒ R of S

case[⊔]-syntax : ∀ {ℓ ℓ' ℓ''} (P : hProp ℓ) (Q : hProp ℓ')
                  → (z : [ P ⊔ Q ])
                  → (R : hProp ℓ'')
                  → (S : (x : [ P ] ⊎ [ Q ]) → [ R ] )
                  → [ R ]
case[⊔]-syntax P Q z R S = ⊔-elim P Q (λ _ → R) (λ p → S (inl p)) (λ q → S (inr q)) z

syntax case[⊔]-syntax P Q z R S = case z as P ⊔ Q ⇒ R of S

-- {-# DISPLAY case[⊔]-syntax P Q z R S = case z of S #-}

-- for a function, to be an hProp, it suffices that the result is an hProp
-- so in principle we might inject any non-hProps as arguments with `_ᵗ⇒_`
_ᵗ⇒_ : ∀{ℓ ℓ'} (A : Type ℓ) → (B : hProp ℓ') → hProp _
A ᵗ⇒ B = (A → [ B ]) , isPropΠ λ _ → isProp[] B

infixr 6 _ᵗ⇒_

-- lifting of hProps to create "universe-homogeneous" pathes
--   this is not necessary when using _⇔_ which is universe-inhomogeneous
--   because `_⇔_` can "cross" universes, where `PathP` needs to stay in the same universe

Liftᵖ : ∀{i j : Level} → hProp i → hProp (ℓ-max i j)
Liftᵖ {i} {j} P = Lift {i} {j} [ P ] , λ{ (lift p) (lift q) → λ i → lift (isProp[] P p q i) }

liftᵖ : ∀{i j : Level} → (P : hProp i) → [ P ] → [ Liftᵖ {i} {j} P ]
liftᵖ P p = lift p

unliftᵖ : ∀{i j : Level} → (P : hProp i) → [ Liftᵖ {i} {j} P ] → [ P ]
unliftᵖ P (lift p) = p

⊥↑ : ∀{ℓ} → hProp ℓ
⊥↑ = Lift Empty.⊥ , λ ()

⊤↑ : ∀{ℓ} → hProp ℓ
⊤↑ = Lift Unit , isOfHLevelLift 1 (λ _ _ _ → tt)

infix 10 ¬↑_
¬↑_ : ∀{ℓ} → hProp ℓ → hProp ℓ
¬↑_ {ℓ} A = ([ A ] → Lift {j = ℓ} Empty.⊥) , isPropΠ λ _ → isOfHLevelLift 1 Empty.isProp⊥

-- negation with an instance argument
¬ⁱ_ : ∀{ℓ} → hProp ℓ → hProp ℓ
¬ⁱ A = ({{p : [ A ]}} → ⊥⊥) , isPropΠⁱ {A = [ A ]} λ _ → isProp⊥

¬'_ : ∀{ℓ} → Type ℓ → hProp ℓ
¬' A = (A → ⊥⊥) , isPropΠ λ _ → isProp⊥

¬-≡-¬ⁱ : ∀{ℓ} (P : hProp ℓ) → ¬ P ≡ ¬ⁱ P
¬-≡-¬ⁱ P =
  ⇒∶ (λ f {{p}} → f   p  )
  ⇐∶ (λ f   p   → f {{p}})


open import Mockingbird.Forest using (Forest)
import Mockingbird.Forest.Birds as Birds

-- The Master Forest
module Mockingbird.Problems.Chapter18 {b ℓ} (forest : Forest {b} {ℓ})
                                      ⦃ _ : Birds.HasStarling forest ⦄
                                      ⦃ _ : Birds.HasKestrel forest ⦄ where

open import Data.Maybe using (Maybe; nothing; just)
open import Data.Product using (_×_; _,_; proj₁; ∃-syntax)
open import Data.Vec using (Vec; []; _∷_; _++_)
open import Data.Vec.Relation.Unary.Any.Properties using (++⁺ʳ)
open import Function using (_$_)
open import Level using (_⊔_)
open import Mockingbird.Forest.Combination.Vec forest using (⟨_⟩; here; there; [_]; _⟨∙⟩_∣_; _⟨∙⟩_; [#_])
open import Mockingbird.Forest.Combination.Vec.Properties forest using (subst′; weaken-++ˡ; weaken-++ʳ; ++-comm)
open import Relation.Unary using (_∈_)

open Forest forest
open import Mockingbird.Forest.Birds forest

problem₁ : HasIdentity
problem₁ = record
  { I = S ∙ K ∙ K
  ; isIdentity = λ x → begin
      S ∙ K ∙ K ∙ x    ≈⟨ isStarling K K x ⟩
      K ∙ x ∙ (K ∙ x)  ≈⟨ isKestrel x (K ∙ x) ⟩
      x                ∎
  }

private instance hasIdentity = problem₁

problem₂ : HasMockingbird
problem₂ = record
  { M = S ∙ I ∙ I
  ; isMockingbird = λ x → begin
      S ∙ I ∙ I ∙ x    ≈⟨ isStarling I I x ⟩
      I ∙ x ∙ (I ∙ x)  ≈⟨ congʳ $ isIdentity x ⟩
      x ∙ (I ∙ x)      ≈⟨ congˡ $ isIdentity x ⟩
      x ∙ x            ∎
  }

private instance hasMockingbird = problem₂

problem₃ : HasThrush
problem₃ = record
  { T = S ∙ (K ∙ (S ∙ I)) ∙ K
  ; isThrush = λ x y → begin
      S ∙ (K ∙ (S ∙ I)) ∙ K ∙ x ∙ y  ≈⟨ congʳ $ isStarling (K ∙ (S ∙ I)) K x ⟩
      K ∙ (S ∙ I) ∙ x ∙ (K ∙ x) ∙ y  ≈⟨ (congʳ $ congʳ $ isKestrel (S ∙ I) x) ⟩
      S ∙ I ∙ (K ∙ x) ∙ y            ≈⟨ isStarling I (K ∙ x) y ⟩
      I ∙ y ∙ (K ∙ x ∙ y)            ≈⟨ congʳ $ isIdentity y ⟩
      y ∙ (K ∙ x ∙ y)                ≈⟨ congˡ $ isKestrel x y ⟩
      y ∙ x                          ∎
  }

-- TODO: Problem 4.

I∈⟨S,K⟩ : I ∈ ⟨ S ∷ K ∷ [] ⟩
I∈⟨S,K⟩ = subst′ refl $ [# 0 ] ⟨∙⟩ [# 1 ] ⟨∙⟩ [# 1 ]

-- Try to strengthen a proof of X ∈ ⟨ y ∷ xs ⟩ to X ∈ ⟨ xs ⟩, which can be done
-- if y does not occur in X.
strengthen : ∀ {n X y} {xs : Vec Bird n} → X ∈ ⟨ y ∷ xs ⟩ → Maybe (X ∈ ⟨ xs ⟩)
-- NOTE: it could be the case that y ∈ xs, but checking that requires decidable
-- equality.
strengthen [ here X≈y ] = nothing
strengthen [ there X∈xs ] = just [ X∈xs ]
strengthen (Y∈⟨y,xs⟩ ⟨∙⟩ Z∈⟨y,xs⟩ ∣ YZ≈X) = do
  Y∈⟨xs⟩ ← strengthen Y∈⟨y,xs⟩
  Z∈⟨xs⟩ ← strengthen Z∈⟨y,xs⟩
  just $ Y∈⟨xs⟩ ⟨∙⟩ Z∈⟨xs⟩ ∣ YZ≈X
  where
    open import Data.Maybe.Categorical using (monad)
    open import Category.Monad using (RawMonad)
    open RawMonad (monad {b ⊔ ℓ})

eliminate : ∀ {n X y} {xs : Vec Bird n} → X ∈ ⟨ y ∷ xs ⟩ → ∃[ X′ ] (X′ ∈ ⟨ S ∷ K ∷ xs ⟩ × X′ ∙ y ≈ X)
eliminate {X = X} {y} [ here X≈y ] = (I , weaken-++ˡ I∈⟨S,K⟩ , trans (isIdentity y) (sym X≈y))
eliminate {X = X} {y} [ there X∈xs ] = (K ∙ X , [# 1 ] ⟨∙⟩ [ ++⁺ʳ (S ∷ K ∷ []) X∈xs ] , isKestrel X y)
eliminate {X = X} {y} (_⟨∙⟩_∣_ {Y} {Z} Y∈⟨y,xs⟩ [ here Z≈y ] YZ≈X) with strengthen Y∈⟨y,xs⟩
... | just Y∈⟨xs⟩ = (Y , weaken-++ʳ (S ∷ K ∷ []) Y∈⟨xs⟩ , trans (congˡ (sym Z≈y)) YZ≈X)
... | nothing =
  let (Y′ , Y′∈⟨S,K,xs⟩ , Y′y≈Y) = eliminate Y∈⟨y,xs⟩

      SY′Iy≈X : S ∙ Y′ ∙ I ∙ y ≈ X
      SY′Iy≈X = begin
        S ∙ Y′ ∙ I ∙ y    ≈⟨ isStarling Y′ I y ⟩
        Y′ ∙ y ∙ (I ∙ y)  ≈⟨ congʳ $ Y′y≈Y ⟩
        Y ∙ (I ∙ y)       ≈⟨ congˡ $ isIdentity y ⟩
        Y ∙ y             ≈˘⟨ congˡ Z≈y ⟩
        Y ∙ Z             ≈⟨ YZ≈X ⟩
        X                 ∎
  in (S ∙ Y′ ∙ I , [# 0 ] ⟨∙⟩ Y′∈⟨S,K,xs⟩ ⟨∙⟩ weaken-++ˡ I∈⟨S,K⟩ , SY′Iy≈X)
eliminate {X = X} {y} (_⟨∙⟩_∣_ {Y} {Z} Y∈⟨y,xs⟩ Z∈⟨y,xs⟩ YZ≈X) =
  let (Y′ , Y′∈⟨S,K,xs⟩ , Y′y≈Y) = eliminate Y∈⟨y,xs⟩
      (Z′ , Z′∈⟨S,K,xs⟩ , Z′y≈Z) = eliminate Z∈⟨y,xs⟩

      SY′Z′y≈X : S ∙ Y′ ∙ Z′ ∙ y ≈ X
      SY′Z′y≈X = begin
        S ∙ Y′ ∙ Z′ ∙ y    ≈⟨ isStarling Y′ Z′ y ⟩
        Y′ ∙ y ∙ (Z′ ∙ y)  ≈⟨ congʳ Y′y≈Y ⟩
        Y ∙ (Z′ ∙ y)       ≈⟨ congˡ Z′y≈Z ⟩
        Y ∙ Z              ≈⟨ YZ≈X ⟩
        X                  ∎
  in (S ∙ Y′ ∙ Z′ , [# 0 ] ⟨∙⟩ Y′∈⟨S,K,xs⟩ ⟨∙⟩ Z′∈⟨S,K,xs⟩ , SY′Z′y≈X)

module _ (x y : Bird) where
  -- Example: y-eliminating the expression y should give I.
  _ : proj₁ (eliminate {y = y} {xs = x ∷ []} $ [# 0 ]) ≈ I
  _ = refl

  -- Example: y-eliminating the expression x should give Kx.
  _ : proj₁ (eliminate {y = y} {xs = x ∷ []} $ [# 1 ]) ≈ K ∙ x
  _ = refl

  -- Example: y-eliminating the expression xy should give x (Principle 3).
  _ : proj₁ (eliminate {y = y} {xs = x ∷ []} $ [# 1 ] ⟨∙⟩ [# 0 ]) ≈ x
  _ = refl

  -- Example: y-eliminating the expression yx should give SI(Kx).
  _ : proj₁ (eliminate {y = y} {xs = x ∷ []} $ [# 0 ] ⟨∙⟩ [# 1 ]) ≈ S ∙ I ∙ (K ∙ x)
  _ = refl

  -- Example: y-eliminating the expression yy should give SII.
  _ : proj₁ (eliminate {y = y} {xs = x ∷ []} $ [# 0 ] ⟨∙⟩ [# 0 ]) ≈ S ∙ I ∙ I
  _ = refl

strengthen-SK : ∀ {n X y} {xs : Vec Bird n} → X ∈ ⟨ S ∷ K ∷ y ∷ xs ⟩ → Maybe (X ∈ ⟨ S ∷ K ∷ xs ⟩)
strengthen-SK {y = y} {xs} X∈⟨S,K,y,xs⟩ = do
  let X∈⟨y,xs,S,K⟩ = ++-comm (S ∷ K ∷ []) (y ∷ xs) X∈⟨S,K,y,xs⟩
  X∈⟨xs,S,K⟩ ← strengthen X∈⟨y,xs,S,K⟩
  let X∈⟨S,K,xs⟩ = ++-comm xs (S ∷ K ∷ []) X∈⟨xs,S,K⟩
  just X∈⟨S,K,xs⟩
  where
    open import Data.Maybe.Categorical using (monad)
    open import Category.Monad using (RawMonad)
    open RawMonad (monad {b ⊔ ℓ})

-- TODO: formulate eliminate or eliminate-SK in terms of the other.
eliminate-SK : ∀ {n X y} {xs : Vec Bird n} → X ∈ ⟨ S ∷ K ∷ y ∷ xs ⟩ → ∃[ X′ ] (X′ ∈ ⟨ S ∷ K ∷ xs ⟩ × X′ ∙ y ≈ X)
eliminate-SK {X = X} {y} [ here X≈S ] = (K ∙ S , [# 1 ] ⟨∙⟩ [# 0 ] , trans (isKestrel S y) (sym X≈S))
eliminate-SK {X = X} {y} [ there (here X≈K) ] = (K ∙ K , [# 1 ] ⟨∙⟩ [# 1 ] , trans (isKestrel K y) (sym X≈K))
eliminate-SK {X = X} {y} [ there (there (here X≈y)) ] = (I , weaken-++ˡ I∈⟨S,K⟩ , trans (isIdentity y) (sym X≈y))
eliminate-SK {X = X} {y} [ there (there (there X∈xs)) ] = (K ∙ X , ([# 1 ] ⟨∙⟩ [ (++⁺ʳ (S ∷ K ∷ []) X∈xs) ]) , isKestrel X y)
eliminate-SK {X = X} {y} (_⟨∙⟩_∣_ {Y} {Z} Y∈⟨S,K,y,xs⟩ [ there (there (here Z≈y)) ] YZ≈X) with strengthen-SK Y∈⟨S,K,y,xs⟩
... | just Y∈⟨S,K,xs⟩ = (Y , Y∈⟨S,K,xs⟩ , trans (congˡ (sym Z≈y)) YZ≈X)
... | nothing =
  let (Y′ , Y′∈⟨S,K,xs⟩ , Y′y≈Y) = eliminate-SK Y∈⟨S,K,y,xs⟩

      SY′Iy≈X : S ∙ Y′ ∙ I ∙ y ≈ X
      SY′Iy≈X = begin
        S ∙ Y′ ∙ I ∙ y    ≈⟨ isStarling Y′ I y ⟩
        Y′ ∙ y ∙ (I ∙ y)  ≈⟨ congʳ $ Y′y≈Y ⟩
        Y ∙ (I ∙ y)       ≈⟨ congˡ $ isIdentity y ⟩
        Y ∙ y             ≈˘⟨ congˡ Z≈y ⟩
        Y ∙ Z             ≈⟨ YZ≈X ⟩
        X                 ∎
  in (S ∙ Y′ ∙ I , [# 0 ] ⟨∙⟩ Y′∈⟨S,K,xs⟩ ⟨∙⟩ weaken-++ˡ I∈⟨S,K⟩ , SY′Iy≈X)
eliminate-SK {X = X} {y} (_⟨∙⟩_∣_ {Y} {Z} Y∈⟨S,K,y,xs⟩ Z∈⟨S,K,y,xs⟩ YZ≈X) =
  let (Y′ , Y′∈⟨S,K,xs⟩ , Y′y≈Y) = eliminate-SK Y∈⟨S,K,y,xs⟩
      (Z′ , Z′∈⟨S,K,xs⟩ , Z′y≈Z) = eliminate-SK Z∈⟨S,K,y,xs⟩

      SY′Z′y≈X : S ∙ Y′ ∙ Z′ ∙ y ≈ X
      SY′Z′y≈X = begin
        S ∙ Y′ ∙ Z′ ∙ y    ≈⟨ isStarling Y′ Z′ y ⟩
        Y′ ∙ y ∙ (Z′ ∙ y)  ≈⟨ congʳ Y′y≈Y ⟩
        Y ∙ (Z′ ∙ y)       ≈⟨ congˡ Z′y≈Z ⟩
        Y ∙ Z              ≈⟨ YZ≈X ⟩
        X                  ∎
  in (S ∙ Y′ ∙ Z′ , [# 0 ] ⟨∙⟩ Y′∈⟨S,K,xs⟩ ⟨∙⟩ Z′∈⟨S,K,xs⟩ , SY′Z′y≈X)

module _ (x y : Bird) where
  -- Example: y-eliminating the expression y should give I.
  _ : proj₁ (eliminate-SK {y = y} {xs = x ∷ []} $ [# 2 ]) ≈ I
  _ = refl

  -- Example: y-eliminating the expression x should give Kx.
  _ : proj₁ (eliminate-SK {y = y} {xs = x ∷ []} $ [# 3 ]) ≈ K ∙ x
  _ = refl

  -- Example: y-eliminating the expression xy should give x (Principle 3).
  _ : proj₁ (eliminate-SK {y = y} {xs = x ∷ []} $ [# 3 ] ⟨∙⟩ [# 2 ]) ≈ x
  _ = refl

  -- Example: y-eliminating the expression yx should give SI(Kx).
  _ : proj₁ (eliminate-SK {y = y} {xs = x ∷ []} $ [# 2 ] ⟨∙⟩ [# 3 ]) ≈ S ∙ I ∙ (K ∙ x)
  _ = refl

  -- Example: y-eliminating the expression yy should give SII.
  _ : proj₁ (eliminate-SK {y = y} {xs = x ∷ []} $ [# 2 ] ⟨∙⟩ [# 2 ]) ≈ S ∙ I ∙ I
  _ = refl

infixl 6 _∙ⁿ_
_∙ⁿ_ : ∀ {n} → (A : Bird) (xs : Vec Bird n) → Bird
A ∙ⁿ [] = A
A ∙ⁿ (x ∷ xs) = A ∙ⁿ xs ∙ x

eliminateAll′ : ∀ {n X} {xs : Vec Bird n} → X ∈ ⟨ S ∷ K ∷ xs ⟩ → ∃[ X′ ] (X′ ∈ ⟨ S ∷ K ∷ [] ⟩ × X′ ∙ⁿ xs ≈ X)
eliminateAll′ {X = X} {[]} X∈⟨S,K⟩ = (X , X∈⟨S,K⟩ , refl)
eliminateAll′ {X = X} {x ∷ xs} X∈⟨S,K,x,xs⟩ =
  let (X′ , X′∈⟨S,K,xs⟩ , X′x≈X) = eliminate-SK X∈⟨S,K,x,xs⟩
      (X″ , X″∈⟨S,K⟩ , X″xs≈X′) = eliminateAll′ X′∈⟨S,K,xs⟩

      X″∙ⁿxs∙x≈X : X″ ∙ⁿ xs ∙ x ≈ X
      X″∙ⁿxs∙x≈X = begin
        X″ ∙ⁿ xs ∙ x  ≈⟨ congʳ X″xs≈X′ ⟩
        X′ ∙ x           ≈⟨ X′x≈X ⟩
        X                ∎
  in (X″ , X″∈⟨S,K⟩ , X″∙ⁿxs∙x≈X)

-- TOOD: can we do this in some way without depending on xs : Vec Bird n?
eliminateAll : ∀ {n X} {xs : Vec Bird n} → X ∈ ⟨ xs ⟩ → ∃[ X′ ] (X′ ∈ ⟨ S ∷ K ∷ [] ⟩ × X′ ∙ⁿ xs ≈ X)
eliminateAll X∈⟨xs⟩ = eliminateAll′ $ weaken-++ʳ (S ∷ K ∷ []) X∈⟨xs⟩


{-# OPTIONS --without-K --rewriting #-}

open import HoTT
open import homotopy.Bouquet

{-
Various lemmas that will be used in cohomology.DisjointlyPointedSet.
Many of them, for example the choice lemma about coproducts, should be
put into core/.
-}

module homotopy.DisjointlyPointedSet where

module _ {i} where

  is-separable : (X : Ptd i) → Type i
  is-separable X = has-dec-onesided-eq (pt X)

  abstract
    is-separable-is-prop : {X : Ptd i}
      → is-prop (is-separable X)
    is-separable-is-prop = has-dec-onesided-eq-is-prop

  MinusPoint : (X : Ptd i) → Type i
  MinusPoint X = Σ (de⊙ X) (pt X ≠_)

  MinusPoint-prop : (X : Ptd i) → SubtypeProp (de⊙ X) i
  MinusPoint-prop X = (pt X ≠_) , ⟨⟩

  abstract
    MinusPoint-has-dec-eq : {X : Ptd i}
      → has-dec-eq (de⊙ X)
      → has-dec-eq (MinusPoint X)
    MinusPoint-has-dec-eq {X} X-dec =
      Subtype-has-dec-eq (MinusPoint-prop X) X-dec

  unite-pt : (X : Ptd i) → (⊤ ⊔ MinusPoint X → de⊙ X)
  unite-pt X (inl _) = pt X
  unite-pt X (inr (x , _)) = x

  separate-pt : {X : Ptd i}
    → is-separable X
    → (de⊙ X → ⊤ ⊔ (Σ (de⊙ X) (pt X ≠_)))
  separate-pt dec x with dec x
  separate-pt dec x | inl _  = inl unit
  separate-pt dec x | inr ¬p = inr (x , ¬p)

  has-disjoint-pt : (X : Ptd i) → Type i
  has-disjoint-pt X = is-equiv (unite-pt X)

  separable-has-disjoint-pt : {X : Ptd i}
    → is-separable X → has-disjoint-pt X
  separable-has-disjoint-pt {X} dec =
    is-eq _ (separate-pt dec) unite-sep sep-unite where
      abstract
        sep-unite : ∀ x → separate-pt dec (unite-pt X x) == x
        sep-unite (inl _) with dec (pt X)
        sep-unite (inl _) | inl _  = idp
        sep-unite (inl _) | inr ¬p = ⊥-rec (¬p idp)
        sep-unite (inr (x , ¬p)) with dec x
        sep-unite (inr (x , ¬p)) | inl p   = ⊥-rec (¬p p)
        sep-unite (inr (x , ¬p)) | inr ¬p' = ap inr $ pair= idp (prop-has-all-paths ¬p' ¬p)

        unite-sep : ∀ x → unite-pt X (separate-pt dec x) == x
        unite-sep x with dec x
        unite-sep x | inl p = p
        unite-sep x | inr ¬p = idp

  disjoint-pt-is-separable : {X : Ptd i}
    → has-disjoint-pt X → is-separable X
  disjoint-pt-is-separable unite-ise x with unite.g x | unite.f-g x
    where module unite = is-equiv unite-ise
  disjoint-pt-is-separable unite-ise x | inl unit       | p   = inl p
  disjoint-pt-is-separable unite-ise x | inr (y , pt≠y) | y=x = inr λ pt=x → pt≠y (pt=x ∙ ! y=x)

  separable-unite-equiv : ∀ {X}
    → is-separable X
    → (⊤ ⊔ MinusPoint X ≃ de⊙ X)
  separable-unite-equiv dX = _ , separable-has-disjoint-pt dX

module _ {i j k} n (A : Type i) (B : Type j) where
  {- Hmm. Where should we put this lemma? -}
  abstract
    ⊔-has-choice-implies-inr-has-choice : has-choice n (A ⊔ B) k → has-choice n B k
    ⊔-has-choice-implies-inr-has-choice ⊔-ac W =
      transport is-equiv (λ= lemma₃)
        (snd lemma₂ ∘ise ⊔-ac W' ∘ise is-equiv-inverse (snd (Trunc-emap lemma₁))) where
          W' : A ⊔ B → Type k
          W' (inl _) = Lift {j = k} ⊤
          W' (inr b) = W b

          lemma₁ : Π (A ⊔ B) W' ≃ Π B W
          lemma₁ = equiv to from to-from from-to where
            to : Π (A ⊔ B) W' → Π B W
            to f b = f (inr b)

            from : Π B W → Π (A ⊔ B) W'
            from f (inl a) = lift tt
            from f (inr b) = f b

            abstract
              to-from : ∀ f → to (from f) == f
              to-from f = λ= λ b → idp

              from-to : ∀ f → from (to f) == f
              from-to f = λ= λ{(inl a) → idp; (inr b) → idp}

          lemma₂ : Π (A ⊔ B) (Trunc n ∘ W') ≃ Π B (Trunc n ∘ W)
          lemma₂ = equiv to from to-from from-to where
            to : Π (A ⊔ B) (Trunc n ∘ W') → Π B (Trunc n ∘ W)
            to f b = f (inr b)

            from : Π B (Trunc n ∘ W) → Π (A ⊔ B) (Trunc n ∘ W')
            from f (inl a) = [ lift tt ]
            from f (inr b) = f b

            abstract
              to-from : ∀ f → to (from f) == f
              to-from f = λ= λ b → idp

              from-to : ∀ f → from (to f) == f
              from-to f = λ= λ{
                (inl a) → Trunc-elim
                  {P = λ t → [ lift tt ] == t}
                  (λ _ → idp) (f (inl a));
                (inr b) → idp}

          lemma₃ : ∀ f → –> lemma₂ (unchoose (<– (Trunc-emap lemma₁) f)) == unchoose f
          lemma₃ = Trunc-elim
            {P = λ f → –> lemma₂ (unchoose (<– (Trunc-emap lemma₁) f)) == unchoose f}
            (λ f → λ= λ b → idp)

module _ {i j} {n} {X : Ptd i} (X-sep : is-separable X) where
  abstract
    MinusPoint-has-choice : has-choice n (de⊙ X) j → has-choice n (MinusPoint X) j
    MinusPoint-has-choice X-ac =
      ⊔-has-choice-implies-inr-has-choice n ⊤ (MinusPoint X) $
        transport! (λ A → has-choice n A j) (ua (_ , separable-has-disjoint-pt X-sep)) X-ac

module _ {i} {X : Ptd i} (X-sep : is-separable X) where

  Bouquet-equiv-X : Bouquet (MinusPoint X) 0 ≃ de⊙ X
  Bouquet-equiv-X = equiv to from to-from from-to where
    from : de⊙ X → BigWedge {A = MinusPoint X} (λ _ → ⊙Bool)
    from x with X-sep x
    from x | inl p  = bwbase
    from x | inr ¬p = bwin (x , ¬p) false

    module To = BigWedgeRec {A = MinusPoint X} {X = λ _ → ⊙Bool}
      (pt X) (λ{_ true → pt X; (x , _) false → x}) (λ _ → idp)
    to = To.f

    abstract
      from-to : ∀ x → from (to x) == x
      from-to = BigWedge-elim base* in* glue* where
        base* : from (pt X) == bwbase
        base* with X-sep (pt X)
        base* | inl _  = idp
        base* | inr ¬p = ⊥-rec (¬p idp)

        in* : (wp : MinusPoint X) (b : Bool)
          → from (to (bwin wp b)) == bwin wp b
        in* wp true with X-sep (pt X)
        in* wp true | inl _ = bwglue wp
        in* wp true | inr pt≠pt = ⊥-rec (pt≠pt idp)
        in* (x , pt≠x) false with X-sep x
        in* (x , pt≠x) false | inl pt=x = ⊥-rec (pt≠x pt=x)
        in* (x , pt≠x) false | inr pt≠'x =
          ap (λ ¬p → bwin (x , ¬p) false) $ prop-has-all-paths pt≠'x pt≠x

        glue* : (wp : MinusPoint X)
          → base* == in* wp true [ (λ x → from (to x) == x) ↓ bwglue wp ]
        glue* wp = ↓-∘=idf-from-square from to $ ap (ap from) (To.glue-β wp) ∙v⊡ square where
          square : Square base* idp (bwglue wp) (in* wp true)
          square with X-sep (pt X)
          square | inl _ = br-square (bwglue wp)
          square | inr ¬p = ⊥-rec (¬p idp)

      to-from : ∀ x → to (from x) == x
      to-from x with X-sep x
      to-from x | inl pt=x = pt=x
      to-from x | inr pt≠x = idp

  Bouquet-⊙equiv-X : ⊙Bouquet (MinusPoint X) 0 ⊙≃ X
  Bouquet-⊙equiv-X = ≃-to-⊙≃ Bouquet-equiv-X idp


{-# OPTIONS --copatterns #-}
{-# OPTIONS --allow-unsolved-metas #-}
-- {-# OPTIONS --show-implicit #-}

-- One-place functors (decorations) on Set

module Control.Decoration where

open import Data.Product using (_×_; _,_; proj₁; proj₂; uncurry)
open import Function using (id; _∘_; const)
open import Relation.Binary.PropositionalEquality
open ≡-Reasoning

open import Axiom.FunctionExtensionality
open import Control.Functor hiding (Id; Const) renaming (idIsFunctor to Id; compIsFunctor to _·_; constIsFunctor to Const)
open import Control.Functor.NaturalTransformation using (IsNatTrans; KKNat)
open import Control.Comonad

open IsFunctor

record IsDecoration (D : Set → Set) : Set₁ where
  field
    traverse    : ∀ {F} (F! : IsFunctor F) {A B} →

        (A → F B) → D A → F (D B)

    -- The free theorem for traverse:
    -- A natural transformation after a traversal can be combined with the traversal.

    traverse-free : ∀ {F} (F! : IsFunctor F) {G} (G! : IsFunctor G) →
      ∀ (η : ∀ {A} → F A → G A) (η! : IsNatTrans (functor F F!) (functor G G!) η) →
      ∀ {A B} (f : A → F B) →

        η ∘ traverse F! f ≡ traverse G! (η ∘ f)

    -- Identity traversal.

    traverse-id : ∀ {A} →

        traverse Id {A = A} id ≡ id

    -- Distributing a traversal.

    traverse-∘  :
      ∀ {F G} (F! : IsFunctor F) (G! : IsFunctor G) →
      ∀ {A B C} {f : A → F B} {g : B → G C} →

        traverse (F! · G!) ((map F! g) ∘ f) ≡ map F! (traverse G! g) ∘ traverse F! f

  isFunctor : IsFunctor D
  isFunctor = record
    { ops   = record { map    = traverse Id }
    ; laws  = record { map-id = traverse-id
                     ; map-∘  = traverse-∘ Id Id
                     }
    }

  open IsFunctor isFunctor using () renaming (map to dmap; map-∘ to dmap-∘)

  -- Lens operations.

  private

    gets : ∀ {A B} → (A → B) → D A → B
    gets {B = B} = traverse (Const B) {B = B}

    get : ∀ {A} → D A → A
    get = gets id

    modify : ∀ {A B} → (A → B) → D A → D B
    modify = dmap

    set : ∀ {A B} → B → D A → D B
    set b = dmap (const b)

  -- Comonad structure.

  isComonad : IsComonad D
  isComonad = record
    { extract  = extract
    ; extend   = extend
    ; extend-β = extend-β _
    ; extend-η = extend-η
    ; extend-∘ = {!!}
    }
    where
      extract : ∀ {A} → D A → A
      extract {A = A} = get

      extend : ∀ {A B} → (D A → B) → (D A → D B)
      extend {A = A}{B = B} f x = set (f x) x
--      extend {A = A}{B = B} f = uncurry set ∘ (λ x → f x , x)

      -- The get-set law.

      extend-β : ∀ {A B} (f : D A → B) → extract ∘ extend f ≡ f
      extend-β {A = A}{B = B} f = begin
        extract ∘ extend f                                     ≡⟨⟩
        get ∘ (λ x → set (f x) x)                              ≡⟨⟩
        traverse (Const B) id  ∘ (λ x → dmap (const (f x)) x)  ≡⟨⟩
        (λ x → traverse (Const B) id (dmap (const (f x)) x))   ≡⟨⟩
        (λ x → (traverse (Const B) id ∘ traverse Id (const (f x))) x)  ≡⟨ {! traverse-∘!} ⟩
        (λ x → traverse (Const B) (const (f x)) x)                     ≡⟨ {!gets=∘get!} ⟩
        (λ x → (const (f x) ∘ traverse (Const A) id) x)        ≡⟨⟩
        (λ x → f x)                                            ≡⟨⟩
        f                                                      ∎

      -- The set-get law.

      extend-η : ∀ {A} → extend {A = A} extract ≡ id
      extend-η = begin
         extend extract         ≡⟨⟩
         (λ x → set (get x) x)  ≡⟨ {!!} ⟩
         id ∎

      -- The set-set law.

   -- Lens structure.

  -- Law: gets in terms of get.
  gets=∘get : ∀ {A B} (f : A → B) →

      gets f ≡ f ∘ get

  gets=∘get {A = A}{B = B} f =
    begin
      gets f
        ≡⟨⟩
      traverse (Const B) {B = B} f
        ≡⟨ traverse-∘ (Const B) Id {g = f} ⟩
      traverse (Const B) {B = A} f
        ≡⟨ sym (traverse-free (Const A) (Const B) f (KKNat f) id) ⟩
      f ∘ traverse (Const A) {B = A} id
        ≡⟨⟩
      f ∘ get
    ∎

  -- Constant traversal.

  traverse-c :  ∀ {A B} (b : B) (l : D A) → B
  traverse-c {B = B} b = traverse (Const B) {B = B} (const b)

  -- Holds by parametricity (since type B is arbitrary and there is just one b : B given).

  traverse-const : ∀ {A B} {b : B} (l : D A) →

      traverse (Const B) (const b) l ≡ b

  traverse-const {A = A} {B = B} {b = b} l =
    begin
      traverse (Const B) (const b) l               ≡⟨ cong (λ z → z l) (gets=∘get (const b)) ⟩
      (const b ∘ traverse (Const A) {B = B} id) l  ≡⟨⟩
      b
    ∎

  -- Lens laws.

  -- 1. Get what you set.

  get-set : ∀ {A} {a : A} →

      get ∘ set {A = A} a ≡ const a

  get-set {A = A} {a = a} =
    begin
      get ∘ set a                                             ≡⟨⟩
      get ∘ dmap (const a)                                    ≡⟨⟩
      traverse (Const A) id ∘ traverse Id (const a)           ≡⟨⟩
      map Id (traverse (Const A) id) ∘ traverse Id (const a)  ≡⟨ sym (traverse-∘ Id (Const A)) ⟩
      traverse (Id · Const A) (map Id id ∘ const a)           ≡⟨⟩
      traverse (Const A) (const a)                            ≡⟨⟩
      gets (const a)                                          ≡⟨ gets=∘get (const a) ⟩
      const a ∘ get                                           ≡⟨⟩
      const a
    ∎

  -- 2. Set what you got.

  set-get : ∀ {A} (l : D A) →

      set (get l) l ≡ l

  set-get {A = A} l =
    begin

      set (get l) l               ≡⟨⟩
      dmap (λ _ → get l) l        ≡⟨⟩
      traverse Id (λ _ → get l) l ≡⟨ {!!} ⟩
      l
    ∎

  -- 3. Set twice is set once.

  set-set : ∀ {A} (a b : A) →

    set a ∘ set {A = A} b  ≡  set a

  set-set a b =
    begin
      set a ∘ set b                    ≡⟨⟩
      dmap (const a) ∘ dmap (const b)  ≡⟨ sym dmap-∘ ⟩
      dmap (const a ∘ const b)         ≡⟨⟩
      dmap (const a)                   ≡⟨⟩
      set a
    ∎

open IsDecoration

-- Identity decoration.

idIsDecoration : IsDecoration (λ A → A)
traverse      idIsDecoration F! f         = f
traverse-free idIsDecoration F! G! η η! f = refl
traverse-id   idIsDecoration              = refl
traverse-∘    idIsDecoration F! G!        = refl

-- Decoration composition.

_•_ : ∀ {D E} → IsDecoration D → IsDecoration E → IsDecoration (λ A → D (E A))

traverse    (d • e) F f = traverse d F (traverse e F f)

traverse-free (d • e) F G η η! f =
  begin
    η ∘ traverse (d • e) F f    ≡⟨⟩
    η ∘ traverse d F (traverse e F f)    ≡⟨  traverse-free d F G η η! _ ⟩
    traverse d G (η ∘ traverse e F f)    ≡⟨  cong (traverse d G) (traverse-free e F G η η! f) ⟩
    traverse d G (traverse e G (η ∘ f))  ≡⟨⟩
    traverse (d • e) G (η ∘ f)
  ∎

traverse-id (d • e) =
  begin
    traverse d Id (traverse e Id id)  ≡⟨ cong (traverse d Id) (traverse-id e) ⟩
    traverse d Id id                  ≡⟨ traverse-id d ⟩
    id
  ∎

traverse-∘ (d • e) F G {f = f} {g = g} =
  begin
    traverse (d • e) FG (map F g ∘ f)                                       ≡⟨⟩
    traverse d FG (traverse e FG (map F g ∘ f))                             ≡⟨ cong (traverse d FG) (traverse-∘ e F G) ⟩
    traverse d FG (map F (traverse e G g) ∘ traverse e F f)                 ≡⟨ traverse-∘ d F G ⟩
    map F (traverse d G (traverse e G g)) ∘ traverse d F (traverse e F f)   ≡⟨⟩
    map F (traverse (d • e) G g) ∘ traverse (d • e) F f
  ∎
  where FG = F · G

-- The instance.

decoration : ∀ {A} → IsDecoration (_×_ A)
-- traverse decoration F f (a , x) = map F (_,_ a) (f x)           -- FAILS BUG WITH record pattern trans!!
traverse      decoration F f ax              = map F (_,_ (proj₁ ax)) (f (proj₂ ax)) -- WORKS

traverse-free decoration F G η η! f          =  fun-ext λ ax → let (a , x) = ax in
  begin
    (η ∘ traverse decoration F f) (a , x)  ≡⟨⟩
    (η ∘ map F (_,_ a)) (f x)              ≡⟨  cong (λ z → z (f x)) (η! (_,_ a))  ⟩
    (map G (_,_ a) ∘ η) (f x)              ≡⟨⟩
    map G (_,_ a) (η (f x))                ≡⟨⟩
    traverse decoration G (η ∘ f) (a , x)
  ∎

traverse-id   decoration                     = refl

traverse-∘    decoration F G {f = f} {g = g} = fun-ext λ ax → let (a , x) = ax in
  begin
    traverse decoration FG (map F g ∘ f) (a , x)                        ≡⟨⟩
    map FG (_,_ a) (map F g (f x))                                      ≡⟨⟩
    map F (map G (_,_ a)) (map F g (f x))                               ≡⟨ cong (λ z → z _) (sym (map-∘ F))  ⟩
    map F (map G (_,_ a) ∘ g) (f x)                                     ≡⟨⟩
    map F (λ y → map G (_,_ a) (g y)) (f x)                             ≡⟨⟩
    map F (λ y → traverse decoration G g (a , y)) (f x)                 ≡⟨⟩
    map F (traverse decoration G g ∘ (_,_ a)) (f x)                     ≡⟨ cong (λ z → z _) (map-∘ F) ⟩
    map F (traverse decoration G g) (map F (_,_ a) (f x))               ≡⟨⟩
    map F (traverse decoration G g) (traverse decoration F f (a , x))   ≡⟨⟩
    (map F (traverse decoration G g) ∘ traverse decoration F f) (a , x)
  ∎
  where FG = F · G


------------------------------------------------------------------------
-- "Reasoning" combinators
------------------------------------------------------------------------

open import Atom

module Reasoning (atoms : χ-atoms) where

open import Equality.Propositional

open import Chi    atoms
open import Values atoms

infix  -1 finally-⇓ _■⟨_⟩ _■⟨_⟩⋆
infixr -2 step-≡⇓ step-⇓ _⟶⟨⟩_ modus-ponens-⇓

step-≡⇓ : ∀ e₁ {e₂ e₃} → e₂ ⇓ e₃ → e₁ ≡ e₂ → e₁ ⇓ e₃
step-≡⇓ _ e₂⇓e₃ refl = e₂⇓e₃

syntax step-≡⇓ e₁ e₂⇓e₃ e₁≡e₂ = e₁ ≡⟨ e₁≡e₂ ⟩⟶ e₂⇓e₃

_⟶⟨⟩_ : ∀ e₁ {e₂} → e₁ ⇓ e₂ → e₁ ⇓ e₂
_ ⟶⟨⟩ e₁⇓e₂ = e₁⇓e₂

mutual

  _■⟨_⟩ : ∀ e → Value e → e ⇓ e
  _ ■⟨ lambda x e ⟩ = lambda
  _ ■⟨ const c vs ⟩ = const (_ ■⟨ vs ⟩⋆)

  _■⟨_⟩⋆ : ∀ es → Values es → es ⇓⋆ es
  _ ■⟨ []     ⟩⋆ = []
  _ ■⟨ v ∷ vs ⟩⋆ = (_ ■⟨ v ⟩) ∷ (_ ■⟨ vs ⟩⋆)

finally-⇓ : (e₁ e₂ : Exp) → e₁ ⇓ e₂ → e₁ ⇓ e₂
finally-⇓ _ _ e₁⇓e₂ = e₁⇓e₂

syntax finally-⇓ e₁ e₂ e₁⇓e₂ = e₁ ⇓⟨ e₁⇓e₂ ⟩■ e₂

modus-ponens-⇓ : ∀ e {e′ v} → e′ ⇓ v → (e′ ⇓ v → e ⇓ v) → e ⇓ v
modus-ponens-⇓ _ x f = f x

syntax modus-ponens-⇓ e x f = e ⟶⟨ f ⟩ x

mutual

  trans-⇓ : ∀ {e₁ e₂ e₃} → e₁ ⇓ e₂ → e₂ ⇓ e₃ → e₁ ⇓ e₃
  trans-⇓ (apply p q r)  s          = apply p q (trans-⇓ r s)
  trans-⇓ (case p q r s) t          = case p q r (trans-⇓ s t)
  trans-⇓ (rec p)        q          = rec (trans-⇓ p q)
  trans-⇓ lambda         lambda     = lambda
  trans-⇓ (const ps)     (const qs) = const (trans-⇓⋆ ps qs)

  trans-⇓⋆ : ∀ {es₁ es₂ es₃} → es₁ ⇓⋆ es₂ → es₂ ⇓⋆ es₃ → es₁ ⇓⋆ es₃
  trans-⇓⋆ []       []       = []
  trans-⇓⋆ (p ∷ ps) (q ∷ qs) = trans-⇓ p q ∷ trans-⇓⋆ ps qs

step-⇓ : ∀ e₁ {e₂ e₃} → e₂ ⇓ e₃ → e₁ ⇓ e₂ → e₁ ⇓ e₃
step-⇓ _ e₂⇓e₃ e₁⇓e₂ = trans-⇓ e₁⇓e₂ e₂⇓e₃

syntax step-⇓ e₁ e₂⇓e₃ e₁⇓e₂ = e₁ ⇓⟨ e₁⇓e₂ ⟩ e₂⇓e₃


------------------------------------------------------------------------
-- The Agda standard library
--
-- Patterns used in the reflection machinery
------------------------------------------------------------------------

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

module Reflection.Pattern where

open import Data.List.Base hiding (_++_)
open import Data.List.Properties
open import Data.Product
open import Data.String as String using (String; braces; parens; _++_; _<+>_)
import Reflection.Literal as Literal
import Reflection.Name as Name
open import Relation.Nullary
open import Relation.Nullary.Decidable as Dec
open import Relation.Nullary.Product using (_×-dec_)
open import Relation.Binary
open import Relation.Binary.PropositionalEquality

open import Reflection.Argument
open import Reflection.Argument.Visibility using (Visibility); open Visibility
open import Reflection.Argument.Relevance using (Relevance); open Relevance
open import Reflection.Argument.Information using (ArgInfo); open ArgInfo

------------------------------------------------------------------------
-- Re-exporting the builtin type and constructors

open import Agda.Builtin.Reflection public using (Pattern)
open Pattern public

------------------------------------------------------------------------
-- Decidable equality

con-injective₁ : ∀ {c c′ args args′} → con c args ≡ con c′ args′ → c ≡ c′
con-injective₁ refl = refl

con-injective₂ : ∀ {c c′ args args′} → con c args ≡ con c′ args′ → args ≡ args′
con-injective₂ refl = refl

con-injective : ∀ {c c′ args args′} → con c args ≡ con c′ args′ → c ≡ c′ × args ≡ args′
con-injective = < con-injective₁ , con-injective₂ >

var-injective : ∀ {x y} → var x ≡ var y → x ≡ y
var-injective refl = refl

lit-injective : ∀ {x y} → Pattern.lit x ≡ lit y → x ≡ y
lit-injective refl = refl

proj-injective : ∀ {x y} → proj x ≡ proj y → x ≡ y
proj-injective refl = refl

_≟s_ : Decidable (_≡_ {A = Args Pattern})
_≟_  : Decidable (_≡_ {A = Pattern})

con c ps ≟ con c′ ps′ = Dec.map′ (uncurry (cong₂ con)) con-injective (c Name.≟ c′ ×-dec ps ≟s ps′)
var s    ≟ var s′     = Dec.map′ (cong var) var-injective (s String.≟ s′)
lit l    ≟ lit l′     = Dec.map′ (cong lit) lit-injective (l Literal.≟ l′)
proj a   ≟ proj a′    = Dec.map′ (cong proj) proj-injective (a Name.≟ a′)

con x x₁ ≟ dot = no (λ ())
con x x₁ ≟ var x₂ = no (λ ())
con x x₁ ≟ lit x₂ = no (λ ())
con x x₁ ≟ proj x₂ = no (λ ())
con x x₁ ≟ absurd = no (λ ())
dot ≟ con x x₁ = no (λ ())
dot ≟ dot = yes refl
dot ≟ var x = no (λ ())
dot ≟ lit x = no (λ ())
dot ≟ proj x = no (λ ())
dot ≟ absurd = no (λ ())
var s ≟ con x x₁ = no (λ ())
var s ≟ dot = no (λ ())
var s ≟ lit x = no (λ ())
var s ≟ proj x = no (λ ())
var s ≟ absurd = no (λ ())
lit x ≟ con x₁ x₂ = no (λ ())
lit x ≟ dot = no (λ ())
lit x ≟ var _ = no (λ ())
lit x ≟ proj x₁ = no (λ ())
lit x ≟ absurd = no (λ ())
proj x ≟ con x₁ x₂ = no (λ ())
proj x ≟ dot = no (λ ())
proj x ≟ var _ = no (λ ())
proj x ≟ lit x₁ = no (λ ())
proj x ≟ absurd = no (λ ())
absurd ≟ con x x₁ = no (λ ())
absurd ≟ dot = no (λ ())
absurd ≟ var _ = no (λ ())
absurd ≟ lit x = no (λ ())
absurd ≟ proj x = no (λ ())
absurd ≟ absurd = yes refl

[]             ≟s []             = yes refl
(arg i p ∷ xs) ≟s (arg j q ∷ ys) = ∷-dec (unArg-dec (p ≟ q)) (xs ≟s ys)

[]      ≟s (_ ∷ _) = no λ()
(_ ∷ _) ≟s []      = no λ()


------------------------------------------------------------------------
-- The Agda standard library
--
-- The identity function
------------------------------------------------------------------------

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

module Function.Construct.Composition where

open import Data.Product using (_,_)
open import Function
open import Level
open import Relation.Binary

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 ≈)

  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
_∘-↣_ = 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


open import Coinduction using ( ♭ ; ♯_ )
open import Data.Empty using ( ⊥-elim )
open import System.IO.Transducers.Session using ( Session ; I ; Σ ; IsΣ ; Γ ; _/_ ; _∼_ )
open import System.IO.Transducers.Trace using ( Trace ; [] ; _∷_ ; _⊨_✓ ; _✓ ; _⊑_ )
open import System.IO.Transducers.Lazy using ( _⇒_ ; inp ; out ; done ; ⟦_⟧ ; _≃_ ; equiv )
open import System.IO.Transducers.Reflective using ( Reflective ; inp ; out ; done )
open import System.IO.Transducers.Strict using ( Strict ; inp ; done )
open import Relation.Binary.PropositionalEquality using ( _≡_ ; refl ; sym ; trans ; cong )
open import Relation.Nullary using ( Dec ; ¬_ ; yes ; no )

module System.IO.Transducers.Properties.Lemmas where

open Relation.Binary.PropositionalEquality.≡-Reasoning

-- ≃ is an equivalence

≃-refl : ∀ {S T} {f : Trace S → Trace T} → (f ≃ f)
≃-refl as = refl

≃-sym : ∀ {S T} {f g : Trace S → Trace T} →
  (f ≃ g) → (g ≃ f)
≃-sym f≃g as = sym (f≃g as)

≃-trans : ∀ {S T} {f g h : Trace S → Trace T} →
  (f ≃ g) → (g ≃ h) → (f ≃ h)
≃-trans f≃g g≃h as = trans (f≃g as) (g≃h as)

-- Completion is decidable

✓-tl : ∀ {S a as} → (S ⊨ a ∷ as ✓) → (S / a ⊨ as ✓)
✓-tl (a ∷ as✓) = as✓

✓? : ∀ {S} as → (Dec (S ⊨ as ✓))
✓? {I}     [] = yes []
✓? {Σ V F} [] = no (λ ())
✓?         (a ∷ as) with ✓? as
✓?         (a ∷ as) | yes as✓ = yes (a ∷ as✓)
✓?         (a ∷ as) | no ¬as✓ = no (λ a∷as✓ → ¬as✓ (✓-tl a∷as✓))

-- Eta conversion for traces of type I

I-η : ∀ (as : Trace I) → (as ≡ [])
I-η [] = refl
I-η (() ∷ as)

-- All traces at type I are complete

I-✓ : ∀ (as : Trace I) → (as ✓)
I-✓ [] = []
I-✓ (() ∷ as)

-- Cons is invertable

∷-refl-≡₁ : ∀ {S a b} {as bs : Trace S} {cs ds} →
  (as ≡ a ∷ cs) → (bs ≡ b ∷ ds) → (as ≡ bs) → (a ≡ b)
∷-refl-≡₁ refl refl refl = refl

∷-refl-≡₂ : ∀ {S a} {as bs : Trace S} {cs ds} →
  (as ≡ a ∷ cs) → (bs ≡ a ∷ ds) → (as ≡ bs) → (cs ≡ ds)
∷-refl-≡₂ refl refl refl = refl

-- Make a function reflective

liat : ∀ {S} → (Trace S) → (Trace S)
liat []           = []
liat (a ∷ [])     = []
liat (a ∷ b ∷ bs) = a ∷ liat (b ∷ bs)

incomplete : ∀ {S} → (Trace S) → (Trace S)
incomplete as with ✓? as
incomplete as | yes ✓as = liat as
incomplete as | no ¬✓as = as

reflective : ∀ {S T} → (Trace S → Trace T) → (Trace S → Trace T)
reflective f as with ✓? as
reflective f as | yes ✓as = f as
reflective f as | no ¬✓as = incomplete (f as)

liat-¬✓ : ∀ {S} a as → ¬ (S ⊨ liat (a ∷ as) ✓)
liat-¬✓         a  (b ∷ bs) (.a ∷ ✓cs) = liat-¬✓ b bs ✓cs
liat-¬✓ {I}     () []       []✓
liat-¬✓ {Σ V F} a  []       ()

incomplete-¬✓ : ∀ {S} {isΣ : IsΣ S} (as : Trace S) → ¬ (incomplete as ✓)
incomplete-¬✓ as with ✓? as
incomplete-¬✓ {I}  {} [] | yes []
incomplete-¬✓ {Σ V F} [] | yes ()
incomplete-¬✓ (a ∷ as)   | yes ✓a∷as = liat-¬✓ a as
incomplete-¬✓ as         | no ¬✓as = ¬✓as

reflective-refl-✓ : ∀ {S T} {isΣ : IsΣ T} (f : Trace S → Trace T) as → (reflective f as ✓) → (as ✓)
reflective-refl-✓             f as ✓bs with ✓? as
reflective-refl-✓             f as ✓bs | yes ✓as = ✓as
reflective-refl-✓ {S} {Σ V F} f as ✓bs | no ¬✓as = ⊥-elim (incomplete-¬✓ (f as) ✓bs)
reflective-refl-✓ {S} {I}  {} f as ✓bs | no ¬✓as

reflective-≡-✓ : ∀ {S T} (f : Trace S → Trace T) {as} → (as ✓) → (reflective f as ≡ f as)
reflective-≡-✓ f {as} ✓as with ✓? as
reflective-≡-✓ f {as} ✓as | yes _   = refl
reflective-≡-✓ f {as} ✓as | no ¬✓as = ⊥-elim (¬✓as ✓as)

-- All transducers are monotone

⟦⟧-mono :  ∀ {S T} (P : S ⇒ T) as bs → (as ⊑ bs) → (⟦ P ⟧ as ⊑ ⟦ P ⟧ bs)
⟦⟧-mono (inp P)   .[]       bs        []          = []
⟦⟧-mono (inp P)   (.a ∷ as) (.a ∷ bs) (a ∷ as⊑bs) = ⟦⟧-mono (♭ P a) as bs as⊑bs
⟦⟧-mono (out b P) as        bs        as⊑bs       = b ∷ (⟦⟧-mono P as bs as⊑bs)
⟦⟧-mono done      as        bs        as⊑bs       = as⊑bs

-- All transducers respect completion

⟦⟧-resp-✓ : ∀ {S T} (P : S ⇒ T) as → (as ✓) → (⟦ P ⟧ as ✓)
⟦⟧-resp-✓ (inp P)    (a ∷ as) (.a ∷ ✓as) = ⟦⟧-resp-✓ (♭ P a) as ✓as
⟦⟧-resp-✓ (out b P)  as       ✓as        = b ∷ ⟦⟧-resp-✓ P as ✓as
⟦⟧-resp-✓ done       as       ✓as        = ✓as
⟦⟧-resp-✓ (inp P)    []       ()

-- Reflective transducers reflect completion

⟦⟧-refl-✓ : ∀ {S T} {P : S ⇒ T} → (Reflective P) → ∀ as → (⟦ P ⟧ as ✓) → (as ✓)
⟦⟧-refl-✓ (inp ⟳P)   (a ∷ as) bs✓ = a ∷ ⟦⟧-refl-✓ (♭ ⟳P a) as bs✓
⟦⟧-refl-✓ (out b ⟳P) as       bs✓ = ⟦⟧-refl-✓ ⟳P as (✓-tl bs✓)
⟦⟧-refl-✓ done       as       bs✓ = bs✓
⟦⟧-refl-✓ (inp ⟳P)   []       ()

-- Any transducer which reflects completion is reflective

⟦⟧-refl-✓⁻¹ : ∀ {S T} (P : S ⇒ T) → (∀ as → (⟦ P ⟧ as ✓) → (as ✓)) → (Reflective P)
⟦⟧-refl-✓⁻¹ {Σ V F} {Σ W G} (inp P)   H = inp (♯ λ a → ⟦⟧-refl-✓⁻¹ (♭ P a) (λ as → λ bs✓ → ✓-tl (H (a ∷ as) bs✓)))
⟦⟧-refl-✓⁻¹                 (out b P) H = out b (⟦⟧-refl-✓⁻¹ P (λ as bs✓ → H as (b ∷ bs✓)))
⟦⟧-refl-✓⁻¹                 done      H = done
⟦⟧-refl-✓⁻¹ {Σ V F} {I}     (inp P)   H with H [] []
⟦⟧-refl-✓⁻¹ {Σ V F} {I}     (inp P)   H | ()

-- Strict transducers respect emptiness.

⟦⟧-resp-[] : ∀ {S T} {P : S ⇒ T} → (Strict P) → (⟦ P ⟧ [] ≡ [])
⟦⟧-resp-[] (inp P) = refl
⟦⟧-resp-[] done    = refl

-- Any transducer which respects emptiness is strict.

⟦⟧-resp-[]⁻¹ : ∀ {S T} (P : S ⇒ T) → (∀ {as} → (as ≡ []) → (⟦ P ⟧ as ≡ [])) → (Strict P)
⟦⟧-resp-[]⁻¹ (inp P)   H = inp P
⟦⟧-resp-[]⁻¹ (out b P) H with H refl
⟦⟧-resp-[]⁻¹ (out b P) H | ()
⟦⟧-resp-[]⁻¹ done      H = done

-- Coherence wrt ∼

⟦⟧-resp-∼ : ∀ {S T} (eq₁ eq₂ : S ∼ T) → ⟦ equiv eq₁ ⟧ ≃ ⟦ equiv eq₂ ⟧
⟦⟧-resp-∼ I       I        as       = refl
⟦⟧-resp-∼ (Σ V F) (Σ .V G) []       = refl
⟦⟧-resp-∼ (Σ V F) (Σ .V G) (a ∷ as) = cong (_∷_ a) (⟦⟧-resp-∼ (♭ F a) (♭ G a) as)

-- IsEquiv P is inhabited whenever P is equivalent to an equivalence

data IsEquiv {S T : Session} (P : S ⇒ T) : Set₁ where
  isEquiv : (S∼T : S ∼ T) → (⟦ P ⟧ ≃ ⟦ equiv S∼T ⟧) → (IsEquiv P)

-- Equivalences are equivalent

≃-equiv : ∀ {S T} {P Q : S ⇒ T} → (IsEquiv P) → (IsEquiv Q) → (⟦ P ⟧ ≃ ⟦ Q ⟧)
≃-equiv {S} {T} {P} {Q} (isEquiv eq₁ P≃eq₁) (isEquiv eq₂ Q≃eq₂) as =
  begin
    ⟦ P ⟧ as
  ≡⟨ P≃eq₁ as ⟩
    ⟦ equiv eq₁ ⟧ as
  ≡⟨ ⟦⟧-resp-∼ eq₁ eq₂ as ⟩
    ⟦ equiv eq₂ ⟧ as
  ≡⟨ sym (Q≃eq₂ as) ⟩
    ⟦ Q ⟧ as
  ∎


open import Agda.Builtin.Reflection
open import Agda.Builtin.List
open import Agda.Builtin.Unit

data World : Set where
  ww : World

test : Term → TC ⊤
test hole
  = unify (con (quote ww) []) hole

HVet : Set₁
HVet = {@(tactic test) w : World} → Set

a : {HQ : HVet} → HQ → ⊤
a = _


{-# OPTIONS --sized-types #-}

module CoSig where

open import Data.Product as Prod
open import Data.Fin
open import Data.Unit
open import Data.Empty
open import Data.Sum
open import Data.Nat
open import Size
open import Function
open import Relation.Binary.PropositionalEquality using (_≡_; refl; subst)

---- We don't use dependencies for now.

-- record CoSigDep (I : Set) : Set₁ where
--   field
--     ∥_∥ : I → Set
--     coar : {i : I} → ∥_∥ i → Set
--     sel : {i : I} (d : ∥_∥ i) → coar d → I
-- open CoSigDep public

-- Fam : Set → Set₁
-- Fam I = I → Set

-- -- | Extension of co-signature Δ, on objects
-- ⟪_⟫ : ∀{I} → CoSigDep I → Fam I → Fam I
-- ⟪ Δ ⟫ X i = (d : ∥ Δ ∥ i) → Σ[ u ∈ coar Δ d ] (X (sel Δ d u))

record CoSig : Set₁ where
  field
    ∥_∥ : Set
    coar : ∥_∥ → Set
open CoSig public

-- | Extension of co-signature Δ, on objects
⟪_⟫ : CoSig → Set → Set
--⟪ Δ ⟫ X = (d : ∥ Δ ∥) → Σ[ u ∈ coar Δ d ] X
⟪ Δ ⟫ X = (d : ∥ Δ ∥) → (coar Δ d) × X

-- | Extension of signature Σ, on morphisms
⟪_⟫₁ : (Δ : CoSig) {A B : Set} → (A → B) → ⟪ Δ ⟫ A → ⟪ Δ ⟫ B
⟪ Δ ⟫₁ f o d = Prod.map id f (o d)

-- | Lift ⟪ Δ ⟫ to relations
cosig-lift : {Δ : CoSig} {X : Set} (R : X → X → Set) (o₁ o₂ : ⟪ Δ ⟫ X) → Set
cosig-lift {Δ} _R_ o₁ o₂ =
  ∀ d → proj₁ (o₁ d) ≡ proj₁ (o₂ d) ×
        proj₂ (o₁ d) R proj₂ (o₂ d)

record CoSig-FP (Δ : CoSig) : Set where
  coinductive
  field destr : ⟪ Δ ⟫ (CoSig-FP Δ)
open CoSig-FP public

record Str {i : Size} (A : Set) : Set where
  coinductive
  constructor _∷_
  field
    hd : A
    tl : ∀ {j : Size< i} → Str {j} A
open Str public

--- Example: Streams
Str-CoSig : Set → CoSig
Str-CoSig A = record
  { ∥_∥ = ⊤
  ; coar = λ _ → A
  }

Str' : Set → Set
Str' A = CoSig-FP (Str-CoSig A)

-- We can indeed extract a stream from stream implemented through cosignatures.
Str'-Str : ∀{A} → Str' A → Str A
hd (Str'-Str s) = proj₁ (destr s tt)
tl (Str'-Str s) = Str'-Str (proj₂ (destr s tt))

-- Example: Mealy machines
MM-CoSig : Set → Set → CoSig
MM-CoSig I O = record
  { ∥_∥ = I
  ; coar = λ _ → O
  }

MM : Set → Set → Set
MM I O = CoSig-FP (MM-CoSig I O)

-- Such a Mealy machine gives rise to a causal stream transformation.
MM-Causal : ∀{I O} → MM I O → ∀{i} → Str {i} I → Str {i} O
hd (MM-Causal m s) = proj₁ (destr m (hd s))
tl (MM-Causal m s) = MM-Causal (proj₂ (destr m (hd s))) (tl s)

--- Not quite Example: Streams with stop button
BStr-CoSig : Set → CoSig
BStr-CoSig A = record
  { ∥_∥ = ⊥ ⊎ ⊤
  ; coar = BStr-CoSig-coar
  }
  where
    BStr-CoSig-coar : ⊥ ⊎ ⊤ → Set
    BStr-CoSig-coar (inj₁ x) = ⊥-elim x
    BStr-CoSig-coar (inj₂ y) = A

BStr : Set → Set
BStr A = CoSig-FP (BStr-CoSig A)

st-ones : BStr ℕ
destr st-ones (inj₁ x) = ⊥-elim x
destr st-ones (inj₂ x) = (1 , st-ones)

-- Claim: This way of definining streams with a "stop button" renders
-- the button unusable. Thus, we only get streams back.
BStr-Str : ∀{A} → BStr A → Str A
hd (BStr-Str s) = proj₁ (destr s (inj₂ tt))
tl (BStr-Str s) = BStr-Str (proj₂ (destr s (inj₂ tt)))

Str-BStr : ∀{A} → Str A → BStr A
destr (Str-BStr s) (inj₁ x) = ⊥-elim x
destr (Str-BStr s) (inj₂ y) = (hd s , Str-BStr (tl s))

--- Example: Streams with stop button
BStr'-CoSig : Set → CoSig
BStr'-CoSig A = record
  { ∥_∥ = ⊤ ⊎ ⊤
  ; coar = BStr-CoSig-coar
  }
  where
    BStr-CoSig-coar : ⊤ ⊎ ⊤ → Set
    BStr-CoSig-coar (inj₁ x) = ⊥
    BStr-CoSig-coar (inj₂ y) = A

BStr' : Set → Set
BStr' A = CoSig-FP (BStr'-CoSig A)

-- Claim: This way of defining streams with a stop button makes any
-- such stream undefinable. Thus, the fixed point is trivial.
BStr'-triv : ∀{A} → BStr' A → ⊥
BStr'-triv s = proj₁ (destr s (inj₁ tt))

-- An extension of cosignatures that allow us to give a continuation
-- for the following states. This enables us to specify systems with,
-- for example, termination.
record CoSig₂ : Set₁ where
  field
    ∥_∥₂ : Set
    coar₂ : ∥_∥₂ → Set
    cont :  ∥_∥₂ → Set
open CoSig₂ public

-- | Extension of co-signature Δ, on objects
⟪_⟫₂ : CoSig₂ → Set → Set
⟪ Δ ⟫₂ X = (d : ∥ Δ ∥₂) → (coar₂ Δ d) × (cont Δ d → X)

record CoSig₂-FP (Δ : CoSig₂) : Set where
  coinductive
  field destr₂ : ⟪ Δ ⟫₂ (CoSig₂-FP Δ)
open CoSig₂-FP public

--- Example: Streams with stop button
BStr-CoSig₂ : Set → CoSig₂
BStr-CoSig₂ A = record
  { ∥_∥₂ = ⊤ ⊎ ⊤
  ; coar₂ = BStr-CoSig-coar
  ; cont = BStr-CoSig-cont
  }
  where
    BStr-CoSig-coar : ⊤ ⊎ ⊤ → Set
    BStr-CoSig-coar (inj₁ x) = ⊤
    BStr-CoSig-coar (inj₂ y) = A

    BStr-CoSig-cont : ⊤ ⊎ ⊤ → Set
    BStr-CoSig-cont (inj₁ x) = ⊥
    BStr-CoSig-cont (inj₂ y) = ⊤

BStr₂ : Set → Set
BStr₂ A = CoSig₂-FP (BStr-CoSig₂ A)

st-ones₂ : BStr₂ ℕ
destr₂ st-ones₂ (inj₁ tt) = (tt , ⊥-elim)
destr₂ st-ones₂ (inj₂ tt) = (1 , (λ _ → st-ones₂))


{-# OPTIONS --allow-unsolved-metas #-}
{- Diamond operator. -}
module TemporalOps.Diamond where

open import CategoryTheory.Categories
open import CategoryTheory.Instances.Reactive
open import CategoryTheory.Functor
open import CategoryTheory.NatTrans
open import CategoryTheory.Monad
open import TemporalOps.Common
open import TemporalOps.Next
open import TemporalOps.Delay
open import TemporalOps.Diamond.Functor public
open import TemporalOps.Diamond.Join
open import TemporalOps.Diamond.JoinLemmas

import Relation.Binary.PropositionalEquality as ≡
open import Data.Product
open import Relation.Binary.HeterogeneousEquality as ≅
            using (_≅_ ; ≅-to-≡ ; ≡-to-≅ ; cong₂)
open import Data.Nat.Properties
            using (+-identityʳ ; +-comm ; +-suc ; +-assoc)

open import Holes.Term using (⌞_⌟)
open import Holes.Cong.Propositional

M-◇ : Monad ℝeactive
M-◇ = record
    { T = F-◇
    ; η = η-◇
    ; μ = μ-◇
    ; η-unit1 = refl
    ; η-unit2 = η-unit2-◇
    ; μ-assoc = λ{A}{n}{k} -> μ-assoc-◇ {A} {n} {k}
    }
    where
    η-◇ : I ⟹ F-◇
    η-◇ = record
        { at = λ A n x -> zero , x
        ; nat-cond = λ {A} {B} {f} {n} {a} → refl }

    private module μ = _⟹_ μ-◇
    private module η = _⟹_ η-◇
    private module F-◇ = Functor F-◇
    open ≡.≡-Reasoning

    η-unit2-◇ : {A : τ} {n : ℕ} {a : ◇ A at n} → (μ.at A n (F-◇.fmap (η.at A) n a)) ≡ a
    η-unit2-◇ {A} {n} {k , v} with inspect (compareLeq k n)
    -- n = k + l
    η-unit2-◇ {A} {.(k + l)} {k , v} | snd==[ .k + l ] with≡ pf =
        begin
            μ.at A (k + l) (F-◇.fmap (η.at A) (k + l) (k , v))
        ≡⟨⟩
            μ.at A (k + l) (k , (Functor.fmap (F-delay k) (η.at A) at (k + l)) v)
        ≡⟨⟩
            μ-compare A (k + l) k ((Functor.fmap (F-delay k) (η.at A) at (k + l)) v) (compareLeq k (k + l))
        ≡⟨ cong (λ x → μ-compare A (k + l) k ((Functor.fmap (F-delay k) (η.at A) at (k + l)) v) x) pf ⟩
            μ-compare A (k + l) k ((Functor.fmap (F-delay k) (η.at A) at (k + l)) v) snd==[ k + l ]
        ≡⟨⟩
            μ-shift k l ⌞ (rew (delay-+-left0 k l) ((Functor.fmap (F-delay k) (η.at A) at (k + l)) v)) ⌟
        ≡⟨ cong!
            (delay-+-left0-eq k l ((Functor.fmap (F-delay k) (η.at A) at (k + l)) v)
                                          ((Functor.fmap (F-delay (k + 0)) (η.at A) at (k + l)) v′) pr)
         ⟩
            μ-shift k l (rew (delay-+ k 0 l) ((Functor.fmap (F-delay (k + 0)) (η.at A) at (k + l)) v′))
        ≡⟨ cong (λ x → μ-shift k l x) (fmap-delay-+-n+k k 0 l v′) ⟩
            μ-shift k l ((Functor.fmap (F-delay 0) (η.at A) at l) (rew (delay-+ k 0 l) v′))
        ≡⟨⟩ -- Def. of Functor.fmap (F-delay 0)
            μ-shift k l ((η.at A) l (rew (delay-+ k 0 l) v′))
        ≡⟨⟩ -- Def. of η.at
            μ-shift k l (0 , rew (delay-+ k 0 l) v′)
        ≡⟨⟩
            k + 0 , rew (sym (delay-+ k 0 l)) (rew (delay-+ k 0 l) v′)
        ≡⟨ cong (λ x → k + 0 , x) (rew-cancel v′ (delay-+ k 0 l)) ⟩
            k + 0 , rew (delay-+0-left k (k + l)) v
        ≡⟨ ≅-to-≡ {_} {◇ A at (k + l)}
            (cong₂ {A = ℕ} {_} {λ n v → ◇ A at (k + l)}
                (λ x y → x , y) (≡-to-≅ (+-identityʳ k)) (≅.sym v≅v′)) ⟩
            k , v
        ∎
        where
        v′ : delay A by (k + 0) at (k + l)
        v′ = rew (delay-+0-left k (k + l)) v
        v≅v′ : v ≅ v′
        v≅v′ = rew-to-≅ (delay-+0-left k (k + l))
        pr : (Functor.fmap (F-delay k) (η.at A) at (k + l)) v
           ≅ (Functor.fmap (F-delay (k + 0)) (η.at A) at (k + l)) v′
        pr = cong₂ (λ x y → (Functor.fmap (F-delay x) (η.at A) at (k + l)) y)
                        (≡-to-≅ (sym (+-identityʳ k))) v≅v′
        v-no-delay : A at l
        v-no-delay = rew (delay-+-left0 k l) v

    -- k = suc n + l
    η-unit2-◇ {A} {n} {.(n + suc l) , v} | fst==suc[ .n + l ] with≡ pf =
        begin
            μ.at A n (F-◇.fmap (η.at A) n (n + suc l , v))
        ≡⟨⟩
            μ.at A n (n + suc l , (Functor.fmap (F-delay (n + suc l)) (η.at A) at n) v)
        ≡⟨⟩
            μ-compare A n (n + suc l) ((Functor.fmap (F-delay (n + suc l)) (η.at A) at n) v) (compareLeq (n + suc l) n)
        ≡⟨ cong (λ x → μ-compare A n (n + suc l) ((Functor.fmap (F-delay (n + suc l)) (η.at A) at n) v) x) pf ⟩
            μ-compare A n (n + suc l) ((Functor.fmap (F-delay (n + suc l)) (η.at A) at n) v) fst==suc[ n + l ]
        ≡⟨⟩
            n + suc l , rew (delay-⊤ n l) top.tt
        ≡⟨ cong (λ x → n + suc l , x) (eq n l v) ⟩
            n + suc l , v
        ∎
        where
        eq : ∀ (n l : ℕ) -> (v : delay A by (n + suc l) at n )
          -> rew (delay-⊤ n l) top.tt
           ≡ v
        eq zero l v = refl
        eq (suc n) l v = eq n l v

    μ-assoc-◇ : {A : τ} {n : ℕ} {a : ◇ ◇ ◇ A at n}
             -> (μ.at A n (μ.at (F-◇.omap A) n a))
              ≡ (μ.at A n (F-◇.fmap (μ.at A) n a))
    μ-assoc-◇ {A} {n} {k , v} with inspect (compareLeq k n)
    -- n = k + l
    μ-assoc-◇ {A} {.(k + l)} {k , v} | snd==[ .k + l ] with≡ pf =
        begin
            μ.at A (k + l) (μ.at (F-◇.omap A) (k + l) (k , v))
        ≡⟨⟩
            μ.at A (k + l) (μ-compare (F-◇.omap A) (k + l) k v ⌞ compareLeq k (k + l) ⌟)
        ≡⟨ cong! pf ⟩
            μ.at A (k + l) (μ-compare (F-◇.omap A) (k + l) k v (snd==[ k + l ]))
        ≡⟨⟩
            μ.at A (k + l) (μ-shift k l (rew (delay-+-left0 k l) v))
        ≡⟨ ≅-to-≡ (μ-interchange {A} {l} {k} {rew (delay-+-left0 k l) v}) ⟩
            μ-shift k l (μ.at A l ⌞ rew (delay-+-left0 k l) v ⌟)
        ≡⟨ cong! (v≡v′-rew k l v v′ v≅v′) ⟩
            μ-shift k l (μ.at A l (rew (delay-+ k 0 l) v′))
        ≡⟨⟩ -- Def. of (F-delay 0).fmap
            μ-shift k l ((Functor.fmap (F-delay 0) (μ.at A) at l) (rew (delay-+ k 0 l) v′))
        ≡⟨ cong (λ x → μ-shift k l x) (sym (fmap-delay-+-n+k k 0 l v′)) ⟩
            μ-shift k l ⌞ rew (delay-+ k 0 l) ((Functor.fmap (F-delay (k + 0)) (μ.at A) at (k + l)) v′) ⌟
        ≡⟨ cong!
            (sym (delay-+-left0-eq k l ((Functor.fmap (F-delay k) (μ.at A) at (k + l)) v)
                                       ((Functor.fmap (F-delay (k + 0)) (μ.at A) at (k + l)) v′) pr))
         ⟩
            μ-shift k l (rew (delay-+-left0 k l) ((Functor.fmap (F-delay k) (μ.at A) at (k + l)) v))
        ≡⟨⟩
            μ-compare A (k + l) k ((Functor.fmap (F-delay k) (μ.at A) at (k + l)) v) (snd==[ k + l ])
        ≡⟨ cong (λ x → μ-compare A (k + l) k ((Functor.fmap (F-delay k) (μ.at A) at (k + l)) v) x) (sym pf) ⟩
            μ-compare A (k + l) k ((Functor.fmap (F-delay k) (μ.at A) at (k + l)) v) (compareLeq k (k + l))
        ≡⟨⟩
            μ.at A (k + l) (k , (Functor.fmap (F-delay k) (μ.at A) at (k + l)) v)
        ≡⟨⟩
            μ.at A (k + l) (F-◇.fmap (μ.at A) (k + l) (k , ?))
        ∎
        where
        v′ : delay (◇ ◇ A) by (k + 0) at (k + l)
        v′ = rew (delay-+0-left k (k + l)) v
        v≅v′ : v ≅ v′
        v≅v′ = rew-to-≅ (delay-+0-left k (k + l))
        pr : (Functor.fmap (F-delay k) (μ.at A) at (k + l)) v
           ≅ (Functor.fmap (F-delay (k + 0)) (μ.at A) at (k + l)) v′
        pr = ≅.cong₂ (λ x y → (Functor.fmap (F-delay x) (μ.at A) at (k + l)) y)
                (≡-to-≅ (sym (+-identityʳ k))) v≅v′
        v≡v′-rew : ∀ {A} (k l : ℕ) -> Proof-≡ (delay-+-left0 {A} k l)
                                              (delay-+ {A} k 0 l)
        v≡v′-rew zero l v v′ ≅.refl = refl
        v≡v′-rew (suc k) l = v≡v′-rew k l
    -- k = suc n + l
    μ-assoc-◇ {A} {.n} {.(n + suc l) , v} | fst==suc[ n + l ] with≡ pf =
        begin
            μ.at A n (μ.at (F-◇.omap A) n (n + suc l , v))
        ≡⟨⟩
            μ.at A n (μ-compare (F-◇.omap A) n (n + suc l) v ⌞ compareLeq (n + suc l) n ⌟)
        ≡⟨ cong! pf ⟩
            μ.at A n (μ-compare (F-◇.omap A) n (n + suc l) v (fst==suc[ n + l ]))
        ≡⟨⟩
            μ.at A n (n + suc l , rew (delay-⊤ n l) top.tt)
        ≡⟨⟩
            μ-compare A n (n + suc l) (rew (delay-⊤ n l) top.tt) ⌞ compareLeq (n + suc l) n ⌟
        ≡⟨ cong! pf ⟩
            μ-compare A n (n + suc l) (rew (delay-⊤ n l) top.tt) (fst==suc[ n + l ])
        ≡⟨⟩
            n + suc l , rew (delay-⊤ n l) top.tt
        ≡⟨⟩
            μ-compare A n (n + suc l) ((Functor.fmap (F-delay (n + suc l)) (μ.at A) at n) v) (fst==suc[ n + l ])
        ≡⟨ cong (λ x -> μ-compare A n (n + suc l) ((Functor.fmap (F-delay (n + suc l)) (μ.at A) at n) v) x) (sym pf) ⟩
            μ-compare A n (n + suc l) ((Functor.fmap (F-delay (n + suc l)) (μ.at A) at n) v) (compareLeq (n + suc l) n)
        ≡⟨⟩
            μ.at A n (n + suc l , (Functor.fmap (F-delay (n + suc l)) (μ.at A) at n) v)
        ≡⟨⟩
            μ.at A n (F-◇.fmap (μ.at A) n (n + suc l , v))
        ∎


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

record Erased (A : Set) : Set where
  constructor [_]
  field
    @0 erased : A

open Erased

data W (A : Set) (B : A → Set) : Set where
  sup : (x : A) → (B x → W A B) → W A B

lemma :
  {A : Set} {B : A → Set} →
  Erased (W A B) → W (Erased A) (λ x → Erased (B (erased x)))
lemma [ sup x f ] = sup [ x ] λ ([ y ]) → lemma [ f y ]

data ⊥ : Set where

data E : Set where
  c : E → E

magic : @0 E → ⊥
magic (c e) = magic e