{-# OPTIONS --safe #-}

module Multiset.FCM.Logic where

open import Multiset.Prelude
open import Multiset.FCM.Base as M
open import Multiset.FCM.Properties as M
  -- hiding (isSingleton ; isSingleton-η ; isEmpty)

open import Cubical.Foundations.Equiv
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Structure
open import Cubical.Foundations.Function
open import Cubical.Foundations.Structure
open import Cubical.Functions.Logic as Logic
  renaming
    (¬_ to ¬ₚ_
    ; inl to ⊔-inl
    ; inr to ⊔-inr
    )
open import Cubical.HITs.PropositionalTruncation as PT
  using
    ( ∣_∣₁
    ; ∥_∥₁
    ; isPropPropTrunc
    )
open import Cubical.Data.Nat as 
  using
    ( 
    ; zero
    ; suc
    )
open import Cubical.Data.Unit as Unit
  using (Unit)
import Cubical.Data.Empty as Empty
open import Cubical.Data.Sum as Sum
  using
    ( _⊎_
    )
open import Cubical.Data.Sigma as Sigma
  using
    ( _×_
    )

open import Cubical.Relation.Nullary.Base
  using
  (¬_)

private
  variable
     ℓX ℓY : Level
    X : Type ℓX
    Y : Type ℓY

⊥* : hProp 
⊥* = Empty.⊥* , λ ()

⊔*-identityˡ : (P : hProp )  _⊔_ { = } ⊥* P  P
⊔*-identityˡ P =
  ⇒∶ (⊔-elim ⊥* P  _  P)  ()) λ p  p)
  ⇐∶ ⊔-inr

any : (p : X  hProp )  M X  hProp 
any p = M.rec isSetHProp ⊥* p _⊔_ ⊔*-identityˡ ⊔-assoc ⊔-comm

all : (p : X  hProp )  M X  hProp 
all p = M.rec isSetHProp  p _⊓_ ⊓-identityˡ ⊓-assoc ⊓-comm

both : (p q : M X  hProp )  M X  hProp 
both p q xs = p xs  q xs

module _ (p : X  hProp ) where
  ¬any-ε : ¬  any p ε 
  ¬any-ε ()

  any-⊕ˡ :  (xs ys : M X)
      any p xs 
      any p (xs  ys) 
  any-⊕ˡ _ _ p-xs = ⊔-inl p-xs

  any-⊕ʳ :  xs ys
      any p ys 
      any p (xs  ys) 
  any-⊕ʳ _ _ p-ys = ⊔-inr p-ys

  ¬any-⊕ :  {xs ys}
     ¬  any p xs 
     ¬  any p ys 
     ¬  any p (xs  ys) 
  ¬any-⊕ {xs = xs} {ys = ys} ¬p-xs ¬p-ys = ⊔-elim (any p xs) (any p ys)  _  ) ¬p-xs ¬p-ys

  all-ε :  all p ε 
  all-ε = Unit.tt*

  all-⊕ :  {xs ys}
      all p xs 
      all p ys 
      all p (xs  ys) 
  all-⊕ {xs = xs} {ys = ys} p-xs p-ys = p-xs , p-ys

  ¬all-⊕ˡ :  {xs ys}
     ¬  all p xs 
     ¬  all p (xs  ys) 
  ¬all-⊕ˡ ¬p-xs = ¬p-xs  fst

  ¬all-⊕ʳ :  {xs ys}
     ¬  all p ys 
     ¬  all p (xs  ys) 
  ¬all-⊕ʳ ¬p-ys = ¬p-ys  snd


-- syntax any (λ x → p) xs = ∃[ x ∈ xs ] p
-- syntax all (λ x → p) xs = ∀[ x ∈ xs ] p

-- Membership
module _ { : Level} {X : Type } where
  _∈ₚ_ : X  M X  hProp 
  x ∈ₚ xs = any (x ≡ₚ_) xs -- ∃[ z ∈ xs ] x ≡ₚ z

  _∈_ : X  M X  Type 
  x  xs =  x ∈ₚ xs 

  infix 80 _∈ₚ_ _∈_

  ∈-η :  x  x  η x
  ∈-η x =  refl ∣₁

  ∈-⊕ˡ :  (xs ys : M X) (x : X)
     x  xs
     x  (xs  ys)
  ∈-⊕ˡ xs ys x = any-⊕ˡ (x ≡ₚ_) xs ys

  ∈-⊕ʳ :  (xs ys : M X) (x : X)
     x  ys
     x  (xs  ys)
  ∈-⊕ʳ xs ys x = any-⊕ʳ (x ≡ₚ_) xs ys


∈-map-η :  {f : X  Y} {y} x
   y  map f (η x)
    y ≡ₚ f x 
∈-map-η x p = p

[_⁻¹_]∈_ :  {X Y : Type }  (f : X  Y)  (y : Y)  (xs : M X)  hProp 
[ f ⁻¹ y ]∈ xs = any  x  y ≡ₚ f x) xs


-- ∈-map-fiber : ∀ {f : X → Y} {y : Y} {xs : M X}
--   → y ∈ map f xs
--   → ⟨ ∃[ x ] x ∈ₚ xs ⊓ y ≡ₚ f x ⟩
-- ∈-map-fiber {X = X} {f = f} {y} {xs} = ind {P = λ xs → y ∈ map f xs → ⟨ (∃[ x ] x ∈ₚ xs ⊓ y ≡ₚ f x) ⟩}
--   (λ xs → isPropΠ (λ h → isProp⟨⟩ (∃[ x ] x ∈ₚ xs ⊓ y ≡ₚ f x)))
--   empty* singl* union* xs
--   where
--     empty* : y ∈ map f ε → ⟨ ∃[ x ] x ∈ₚ ε ⊓ y ≡ₚ f x ⟩
--     empty* ()

--     singl* : ∀ x → y ∈ map f (η x) → ⟨ ∃[ x′ ] x′ ∈ₚ η x ⊓ y ≡ₚ f x′ ⟩
--     singl* x p = ∣ x , ∣ refl {x = x} ∣₁ , p ∣₁ where
--       _ : ∥ y ≡ f x ∥₁
--       _ = p

--     union* : ∀ {xs₁ xs₂ : M X}
--       → (y ∈ map f xs₁ → ⟨ ∃[ x ] x ∈ₚ xs₁ ⊓ y ≡ₚ f x ⟩)
--       → (y ∈ map f xs₂ → ⟨ ∃[ x ] x ∈ₚ xs₂ ⊓ y ≡ₚ f x ⟩)
--       → (y ∈ map f (xs₁ ⊕ xs₂) → ⟨ ∃[ x ] x ∈ₚ (xs₁ ⊕ xs₂) ⊓ y ≡ₚ f x ⟩)
--     union* {xs₁ = xs₁} {xs₂} p-xs₁ p-xs₂ = PT.rec (isPropPropTrunc) (Sum.elim ∈ˡ ∈ʳ)
--       where

--       ∈ˡ : (y ∈ map f xs₁) → ⟨ ∃[ x ] x ∈ₚ (xs₁ ⊕ xs₂) ⊓ y ≡ₚ f x ⟩
--       ∈ˡ = PT.map (λ (x , h∈ , h≡) → x , ∈-⊕ˡ xs₁ xs₂ x h∈ , h≡) ∘ p-xs₁

--       ∈ʳ : (y ∈ map f xs₂) → ⟨ ∃[ x ] x ∈ₚ (xs₁ ⊕ xs₂) ⊓ y ≡ₚ f x ⟩
--       ∈ʳ = PT.map (λ (x , h∈ , h≡) → x , ∈-⊕ʳ xs₁ xs₂ x h∈ , h≡) ∘ p-xs₂

⊕≡ε→ε⊔ε :  {xs ys : M X}
   xs  ys  ε
    xs ≡ₚ ε  ys ≡ₚ ε 
⊕≡ε→ε⊔ε {X = X} {xs = xs} {ys = ys} = M.ind {P = λ xs   ys  xs  ys  ε   xs ≡ₚ ε  ys ≡ₚ ε }
   xs  isPropΠ2 λ ys h  isProp⟨⟩ (xs ≡ₚ ε  ys ≡ₚ ε))
  empty* singl* union* xs ys where
    empty* :  ys  ε  ys  ε   ε ≡ₚ ε  ys ≡ₚ ε 
    empty* ys indH = ⊔-inr  subst (_≡ ε) (unit ys) indH ∣₁

    singl* :  (x : X) (ys : M X)  η x  ys  ε   η x ≡ₚ ε  ys ≡ₚ ε 
    singl* x ys h = Empty.rec (η⊕≢ε h)

    union* :  {xs₁ xs₂}
       (∀ ys  xs₁  ys  ε   xs₁ ≡ₚ ε  ys ≡ₚ ε )
       (∀ ys  xs₂  ys  ε   xs₂ ≡ₚ ε  ys ≡ₚ ε )
        ys
       (xs₁  xs₂)  ys  ε
        (xs₁  xs₂) ≡ₚ ε  ys ≡ₚ ε 
    union* {xs₁} {xs₂} indH-xs₁ indH-xs₂ ys h = PT.map2 goal (indH-xs₁ ys h₁) (indH-xs₂ ys h₂) where
      h₁ : xs₁  ys  ε
      h₁ = no-zero-divisorsˡ (
          (xs₁  ys)  xs₂ ≡⟨ sym (assoc _ _ _) 
          xs₁  (ys  xs₂) ≡⟨ cong (xs₁ ⊕_) (comm ys xs₂) 
          xs₁  (xs₂  ys) ≡⟨ assoc _ _ _ 
          (xs₁  xs₂)  ys ≡⟨ h 
          ε 
        )

      h₂ : xs₂  ys  ε
      h₂ = no-zero-divisorsʳ (
          xs₁  (xs₂  ys) ≡⟨ assoc _ _ _ 
          (xs₁  xs₂)  ys ≡⟨ h 
          ε 
        )

      goal :
           xs₁ ≡ₚ ε    ys ≡ₚ ε 
          xs₂ ≡ₚ ε    ys ≡ₚ ε 
          (xs₁  xs₂) ≡ₚ ε    ys ≡ₚ ε 
      goal (Sum.inl xs₁≡ε) (Sum.inl xs₂≡ε) = Sum.inl (PT.map2 subst-ε⊕ε≡ε xs₁≡ε xs₂≡ε)
      goal (Sum.inl xs₁≡ε) (Sum.inr ys≡ε)  = Sum.inr ys≡ε
      goal (Sum.inr ys≡ε)  _               = Sum.inr ys≡ε

-- map-⊕-fiber : ∀ {f : X → Y}
--   → (ys₁ ys₂ : M Y)
--   → (xs : M X)
--   → map f xs ≡ ys₁ ⊕ ys₂
--   → ⟨ ∃[ xs₁ ] ∃[ xs₂ ]
--       (xs₁ ⊕ xs₂) ≡ₚ xs
--       ⊓ map f xs₁ ≡ₚ ys₁
--       ⊓ map f xs₂ ≡ₚ ys₂
--     ⟩
-- map-⊕-fiber {f = f} ys₁ ys₂ = M.ind {P = λ xs → map f xs ≡ ys₁ ⊕ ys₂ → ⟨ _ ⟩}
--   (λ xs → isPropΠ λ h → isProp⟨⟩ _)
--   empty* {! !} {! !} where
--     empty* : ε ≡ ys₁ ⊕ ys₂ → ⟨ ∃[ xs₁ ] ∃[ xs₂ ] _ ⟩
--     empty* h = ∣ xs₁ , ∣ xs₂ , ∣ xs₁⊕xs₂≡ε ∣₁ , ∣ sym ys₁≡ε ∣₁ , ∣ sym ys₂≡ε ∣₁ ∣₁ ∣₁ where
--       xs₁ = ε
--       xs₂ = ε

--       xs₁⊕xs₂≡ε : xs₁ ⊕ xs₁ ≡ ε
--       xs₁⊕xs₂≡ε = unit ε

--       ys₁≡ε : ys₁ ≡ ε
--       ys₁≡ε = no-zero-divisorsˡ (sym h)

--       ys₂≡ε : ys₂ ≡ ε
--       ys₂≡ε = no-zero-divisorsʳ (sym h)

-- TODO: Remove facts about xor
-- _xorₚ_ : (P Q : hProp ℓ) → hProp ℓ
-- P xorₚ Q = ⟨ P ⟩ xor ⟨ Q ⟩ , isPropXor (str P) (str Q)

-- xorₚ-identityˡ : (P : hProp ℓ) → ⊥* xorₚ P ≡ P
-- xorₚ-identityˡ P = hProp≡ $ xor-identityˡ ⟨ P ⟩

-- xorₚ-comm : (P Q : hProp ℓ) → P xorₚ Q ≡ Q xorₚ P
-- xorₚ-comm P Q = hProp≡ $ xor-comm ⟨ P ⟩ ⟨ Q ⟩

-- xorₚ-assoc : (P Q R : hProp ℓ) → P xorₚ (Q xorₚ R) ≡ (P xorₚ Q) xorₚ R
-- xorₚ-assoc P Q R = hProp≡ $ xor-assoc ⟨ P ⟩ ⟨ Q ⟩ ⟨ R ⟩

module _ {} {X : Type } where
  _≡ₘ_ : M X  M X  hProp 
  xs ≡ₘ ys = (xs  ys) , isSetM xs ys

  -- isEmptyₚ : M X → hProp ℓ
  -- isEmptyₚ xs = ε ≡ₘ xs

  isEmptyₚ : M X  hProp 
  isEmptyₚ = all  _  ⊥*)

  isEmpty : M X  Type 
  isEmpty xs =  isEmptyₚ xs 

  isEmpty-ε : isEmpty ε
  isEmpty-ε = Unit.tt*

  ¬isEmpty-η :  {x}  ¬ (isEmpty (η x))
  ¬isEmpty-η ()

  isEmpty→≡ε :  {xs}  isEmpty xs  xs  ε
  isEmpty→≡ε {xs = xs} = M.ind {P = λ xs  isEmpty xs  xs  ε}
     xs  isPropΠ λ h  isSetM xs ε)  _  refl)  x ()) union* xs
    where
    union* :  {xs ys}
       (isEmpty xs  xs  ε)
       (isEmpty ys  ys  ε)
        isEmptyₚ xs  isEmptyₚ ys   xs  ys  ε
    union* indH-xs indH-ys (isEmpty-xs , isEmpty-ys) = subst-ε⊕ε≡ε (indH-xs isEmpty-xs) (indH-ys isEmpty-ys)

  isEmpty-⊕ :  {xs ys : M X}
     isEmpty xs
     isEmpty ys
     isEmpty (xs  ys)
  isEmpty-⊕ {xs = xs} {ys = ys} empty-xs empty-ys = subst isEmpty ε=xs⊕ys isEmpty-ε where
    ε=xs⊕ys : ε  xs  ys
    ε=xs⊕ys = sym $ subst-ε⊕ε≡ε (isEmpty→≡ε empty-xs) (isEmpty→≡ε empty-ys)

  ≡ε→isEmpty :  {xs : M X}  xs  ε  isEmpty xs
  ≡ε→isEmpty {xs = xs} = M.ind {P = λ xs  xs  ε  isEmpty xs}
     xs  isPropΠ λ h  isProp⟨⟩ (isEmptyₚ xs))
     _  isEmpty-ε)
     _  Empty.rec  η≢ε)
     {xs} {ys} indH-xs indH-ys xs⊕ys≡ε
       isEmpty-⊕ {xs} {ys}
        (indH-xs (no-zero-divisorsˡ xs⊕ys≡ε))
        (indH-ys (no-zero-divisorsʳ xs⊕ys≡ε))
    )
    xs

  -- isSingleton : X → M X → Type ℓ
  -- isSingleton x ys = η x ≡ ys

  -- isSingletonₚ : X → M X → hProp ℓ
  -- isSingletonₚ x ys = η x ≡ₘ ys
  
  -- private
  --   TaggedHProp = M X × hProp ℓ

  -- isEitherExactUnionₚ : TaggedHProp → TaggedHProp → hProp ℓ
  -- isEitherExactUnionₚ (xs , Pxs) (ys , Pys) =
  --   (Pxs ⊓ isEmptyₚ ys) ⊔ (Pys ⊓ isEmptyₚ xs)

  -- _⊔ₑ_ : TaggedHProp → TaggedHProp → TaggedHProp
  -- P ⊔ₑ Q = P .fst ⊕ Q .fst , isEitherExactUnionₚ P Q

  -- isEitherExactUnionₚ-identityˡ : ∀ {xs : M X} → (P : hProp ℓ) → isEitherExactUnionₚ (ε , ⊥*) (xs , P) ≡ P
  -- isEitherExactUnionₚ-identityˡ {xs = xs} P =
  --     ⇒∶ ⊔-elim (⊥* ⊓ isEmptyₚ xs) (P ⊓ isEmptyₚ ε) (λ _ → P) (λ ()) fst
  --     ⇐∶ ⊔-inr ∘ (_, isEmpty-ε)

  -- isEitherExactUnionₚ-assoc : (P Q R : TaggedHProp) → (P ⊔ₑ (Q ⊔ₑ R)) .snd ≡ ((P ⊔ₑ Q) ⊔ₑ R) .snd
  -- isEitherExactUnionₚ-assoc P Q R =
  --   ⇒∶ {! !}
  --   ⇐∶ {! !}

  -- isEitherExactUnionₚ-comm : (P Q : TaggedHProp) → isEitherExactUnionₚ P Q ≡ isEitherExactUnionₚ Q P
  -- isEitherExactUnionₚ-comm (xs , P) (ys , Q) = ⊔-comm (P ⊓ isEmptyₚ ys) (Q ⊓ isEmptyₚ xs)

  -- isSingletonₚ : X → (xs : M X) → hProp ℓ
  -- isSingletonₚ x = M.elim (λ xs → isSetHProp) ⊥* (x ≡ₚ_)
  --   (λ {xs} {ys} singl-xs singl-ys → isEitherExactUnionₚ (xs , singl-xs) (ys , singl-ys))
  --   (λ {xs} singl-xs → isEitherExactUnionₚ-identityˡ {xs = xs} singl-xs)
  --   (λ {xs} {ys} {zs} singl-xs singl-ys singl-zs → isEitherExactUnionₚ-assoc (xs , singl-xs) (ys , singl-ys) (zs , singl-zs))
  --   (λ {xs} {ys} singl-xs singl-ys → isEitherExactUnionₚ-comm (xs , singl-xs) (ys , singl-ys))

  -- isSingleton : X → M X → Type ℓ
  -- isSingleton x xs = ⟨ isSingletonₚ x xs ⟩

  -- ¬isSingletonₚ-ε : ∀ {ys} → ¬ ⟨ isSingletonₚ ys ε ⟩
  -- ¬isSingletonₚ-ε {ys = ys} ()

  -- isSingletonSelf : ∀ x → isSingleton x (η x)
  -- isSingletonSelf x = ∣ refl {x = x} ∣₁

  -- isSingleton-η : ∀ x y → isSingleton x (η y) → η x ≡ η y
  -- isSingleton-η x y = PT.rec (isSetM (η x) (η y)) (cong η)

  -- isSingleton→η≡ : ∀ x {xs} → isSingleton x xs → η x ≡ xs
  -- isSingleton→η≡ x {xs = xs} = M.ind
  --   {P = λ xs → isSingleton x xs → η x ≡ xs}
  --   (λ xs → isPropΠ λ _ → isSetM (η x) xs)
  --   (Empty.rec ∘ ¬isSingletonₚ-ε)
  --   (isSingleton-η x)
  --   union*
  --   xs where

  --   union* : ∀ {xs ys}
  --     → (isSingleton x xs → η x ≡ xs)
  --     → (isSingleton x ys → η x ≡ ys)
  --     → (isSingleton x (xs ⊕ ys) → η x ≡ xs ⊕ ys)
  --   union* {xs} {ys} indH-xs indH-ys =
  --     ⊔-elim (isSingletonₚ x xs ⊓ isEmptyₚ ys) (isSingletonₚ x ys ⊓ isEmptyₚ xs) (λ _ → η x ≡ₘ (xs ⊕ ys)) left right
  --     where
  --       left : ⟨ isSingletonₚ x xs ⊓ isEmptyₚ ys ⟩ → η x ≡ (xs ⊕ ys)
  --       left (singl-xs , empty-ys) =
  --         η x     ≡⟨ sym $ unit' _ ⟩
  --         η x ⊕ ε ≡⟨ cong₂ _⊕_ (indH-xs singl-xs) (sym $ isEmpty→≡ε empty-ys) ⟩
  --         xs ⊕ ys ∎

  --       right : ⟨ isSingletonₚ x ys ⊓ isEmptyₚ xs ⟩ → η x ≡ (xs ⊕ ys)
  --       right (singl-ys , empty-xs) =
  --         η x     ≡⟨ sym $ unit _ ⟩
  --         ε ⊕ η x ≡⟨ cong₂ _⊕_ (sym $ isEmpty→≡ε empty-xs) (indH-ys singl-ys) ⟩
  --         xs ⊕ ys ∎

  -- η≡→isSingleton : (x : X) → ∀ {xs} → η x ≡ xs → isSingleton x xs
  -- η≡→isSingleton x {xs = xs} = M.ind {P = λ xs → η x ≡ xs → isSingleton x xs }
  --   (λ xs → isPropΠ λ _ → isProp⟨⟩ (isSingletonₚ x xs))
  --   (Empty.rec ∘ η≢ε)
  --   (λ y ηx≡ηy → {! !})
  --   {! !}
  --   xs

  -- hasSplitOfₚ : (p q : M X → hProp ℓ) → (M X → hProp ℓ)
  -- hasSplitOfₚ p q ys = ∃[ (ys₁ , ys₂) ∶ (M X × M X) ] (ys₁ ⊕ ys₂) ≡ₘ ys ⊓ (p ys₁) ⊓ (q ys₂)

  -- hasSplitOfₚ-⊕ : (p q : M X → hProp ℓ)
  --   → ∀ {ys₁ ys₂}
  --   → ⟨ p ys₁ ⟩
  --   → ⟨ q ys₂ ⟩
  --   → ⟨ hasSplitOfₚ p q (ys₁ ⊕ ys₂) ⟩
  -- hasSplitOfₚ-⊕ p q {ys₁} {ys₂} p-ys₁ q-ys₂ = ∣ (ys₁ , ys₂) , refl , p-ys₁ , q-ys₂ ∣₁

  -- hasSplitOfₚ-identityˡ : ∀ (p : M X → hProp ℓ) → hasSplitOfₚ isEmptyₚ p ≡ p
  -- hasSplitOfₚ-identityˡ p = funExt λ ys →
  --   ⇒∶ imp⇒ₚ ys
  --   ⇐∶ imp⇐ₚ ys where module _ (ys : _) where

  --   P : M X → Type ℓ
  --   P = ⟨_⟩ ∘ p

  --   imp⇒ : ∀ ys₁ ys₂
  --     → (ys₁ ⊕ ys₂) ≡ ys
  --     → isEmpty ys₁
  --     → P ys₂
  --     → P ys
  --   imp⇒ ys₁ ys₂ ys-split isEmpty-ys₁ p-ys₂ = goal where
  --     ys₂≡ys : ys₂ ≡ ys
  --     ys₂≡ys = (sym $ unit ys₂) ∙∙ cong (_⊕ ys₂) (sym $ isEmpty→≡ε isEmpty-ys₁) ∙∙ ys-split

  --     goal : ⟨ p ys ⟩
  --     goal = subst P ys₂≡ys p-ys₂

  --   imp⇒ₚ : ⟨ hasSplitOfₚ isEmptyₚ p ys ⟩ → ⟨ p ys ⟩
  --   imp⇒ₚ = PT.rec (isProp⟨⟩ (p ys))
  --       λ { ((ys₁ , ys₂) , (ys-split , isEmpty-ys₁ , p-ys₂))
  --         → imp⇒ ys₁ ys₂ ys-split isEmpty-ys₁ p-ys₂
  --         }

  --   imp⇐ₚ : ⟨ p ys ⟩ → ⟨ hasSplitOfₚ isEmptyₚ p ys ⟩
  --   imp⇐ₚ p-ys = ∣ (ε , ys) , unit ys , isEmpty-ε , p-ys ∣₁

  -- hasSplitOfₚ-comm : (p q : M X → hProp ℓ) → hasSplitOfₚ p q ≡ hasSplitOfₚ q p
  -- hasSplitOfₚ-comm p q = funExt λ ys →
  --   ⇒∶ commₚ ys p q
  --   ⇐∶ commₚ ys q p where module _ (ys : _) where

  --   commₚ : ∀ p q → ⟨ hasSplitOfₚ p q ys ⟩ → ⟨ hasSplitOfₚ q p ys ⟩
  --   commₚ p q = PT.map
  --     λ { ((ys₁ , ys₂) , (ys-split , p-ys₁ , q-ys₂))
  --     → (ys₂ , ys₁) , comm ys₂ ys₁ ∙ ys-split , q-ys₂ , p-ys₁
  --     }

  -- hasSplitOfₚ-assoc : (p q r : M X → hProp ℓ) → hasSplitOfₚ p (hasSplitOfₚ q r) ≡ hasSplitOfₚ (hasSplitOfₚ p q) r
  -- hasSplitOfₚ-assoc p q r = funExt λ ys →
  --   ⇒∶ imp⇒ₚ ys
  --   ⇐∶ imp⇐ₚ ys where module _ (ys : _) where

  --   reassoc-split⇒ : ∀ {ys zs ys₁ ys₂ ys₃ : M X}
  --     → ys₁ ⊕ zs ≡ ys
  --     → ys₂ ⊕ ys₃ ≡ zs
  --     → (ys₁ ⊕ ys₂) ⊕ ys₃ ≡ ys
  --   reassoc-split⇒ split-ys split-zs = (sym $ assoc _ _ _) ∙∙ cong (_ ⊕_) split-zs ∙∙ split-ys

  --   reassoc-split⇐ : ∀ {ys zs ys₁ ys₂ ys₃ : M X}
  --     → zs ⊕ ys₃ ≡ ys
  --     → ys₁ ⊕ ys₂ ≡ zs
  --     → ys₁ ⊕ ys₂ ⊕ ys₃ ≡ ys
  --   reassoc-split⇐ split-ys split-zs = assoc _ _ _ ∙∙ cong (_⊕ _) split-zs ∙∙ split-ys

  --   imp⇒ₚ : ⟨ hasSplitOfₚ p (hasSplitOfₚ q r) ys ⟩ → ⟨ hasSplitOfₚ (hasSplitOfₚ p q) r ys ⟩
  --   imp⇒ₚ = PT.rec (isProp⟨⟩ (hasSplitOfₚ (hasSplitOfₚ p q) r ys))
  --     λ { ((ys₁ , zs) , (ys-split , p-ys₁ , split-q-r-zs))
  --       → PT.map
  --         ( λ { ((ys₂ , ys₃) , (zs-split , q-ys₂ , r-ys₃))
  --           → (ys₁ ⊕ ys₂ , ys₃) , reassoc-split⇒ ys-split zs-split , hasSplitOfₚ-⊕ p q p-ys₁ q-ys₂ , r-ys₃
  --           }
  --         ) split-q-r-zs
  --       }

  --   imp⇐ₚ : ⟨ hasSplitOfₚ (hasSplitOfₚ p q) r ys ⟩ → ⟨ hasSplitOfₚ p (hasSplitOfₚ q r) ys ⟩
  --   imp⇐ₚ = PT.rec (isProp⟨⟩ (hasSplitOfₚ p (hasSplitOfₚ q r) ys))
  --     λ { ((zs , ys₃) , (ys-split , split-p-q-zs , r-ys₃))
  --       → PT.map
  --         ( λ { ((ys₁ , ys₂) , (zs-split , p-ys₁ , q-ys₂))
  --           → (ys₁ , ys₂ ⊕ ys₃) , (reassoc-split⇐ ys-split zs-split , p-ys₁ , hasSplitOfₚ-⊕ q r q-ys₂ r-ys₃)
  --           }
  --         ) split-p-q-zs
  --     }

  -- infix 4 _≈ₚ_ _≈_

  -- _≈ₚ_ : M X → M X → hProp ℓ
  -- _≈ₚ_ = M.rec (isSetΠ λ ys → isSetHProp)
  --   isEmptyₚ isSingletonₚ hasSplitOfₚ
  --   hasSplitOfₚ-identityˡ hasSplitOfₚ-assoc hasSplitOfₚ-comm

  -- _≈ₚ_ : M X → M X → hProp ℓ
  -- _≈ₚ_ = M.rec (isSetΠ λ ys → isSetHProp)
  --   isEmptyₚ isSingletonₚ hasSplitOfₚ
  --   hasSplitOfₚ-identityˡ hasSplitOfₚ-assoc hasSplitOfₚ-comm

  -- _≈_ : M X → M X → Type ℓ
  -- xs ≈ ys = ⟨ xs ≈ₚ ys ⟩

  -- isProp≈ : ∀ xs ys → isProp (xs ≈ ys)
  -- isProp≈ xs ys = isProp⟨⟩ (xs ≈ₚ ys)

  -- ≈-refl : {xs : M X} → xs ≈ xs
  -- ≈-refl {xs = xs} = M.ind (λ xs → isProp≈ xs xs) Unit.tt* isSingletonSelf union* xs where
  --   union* : ∀ {xs ys}
  --     → xs ≈ xs
  --     → ys ≈ ys
  --     → (xs ⊕ ys) ≈ (xs ⊕ ys)
  --   union* {xs} {ys} xs≈xs ys≈ys = ∣ (xs , ys) , refl , xs≈xs , ys≈ys ∣₁

  -- ≈-⊕-β : ∀ {xs ys zs} → ((xs ⊕ ys) ≈ zs) ≡ ⟨ hasSplitOfₚ (xs ≈ₚ_) (ys ≈ₚ_) zs ⟩
  -- ≈-⊕-β = refl

  -- decode : (xs ys : M X) → ⟨ xs ≈ₚ ys ⟩ → xs ≡ ys
  -- decode = M.ind {P = λ xs → ∀ ys → xs ≈ ys → xs ≡ ys} (λ xs → isPropΠ2 λ ys _ → isSetM xs ys) empty* singl* union* where
  --   empty* : ∀ ys → isEmpty ys → ε ≡ ys
  --   empty* ys = sym ∘ isEmpty→≡ε

  --   singl* : ∀ x ys → isSingleton x ys → η x ≡ ys
  --   singl* x ys = isSingleton→η≡ x

  --   union* : ∀ {xs ys}
  --      → (∀ zs → xs ≈ zs → xs ≡ zs)
  --      → (∀ zs → ys ≈ zs → ys ≡ zs)
  --      → (∀ zs → ⟨ hasSplitOfₚ (xs ≈ₚ_) (ys ≈ₚ_) zs ⟩ → (xs ⊕ ys) ≡ zs)
  --   union* {xs} {ys} indH-xs indH-ys zs = PT.rec (isSetM (xs ⊕ ys) zs) step where

  --     step :
  --       Σ[ (zs₁ , zs₂) ∈ (M X × M X) ] ⟨ (zs₁ ⊕ zs₂) ≡ₘ zs ⊓ (xs ≈ₚ zs₁) ⊓ (ys ≈ₚ zs₂) ⟩
  --       → xs ⊕ ys ≡ zs
  --     step ((zs₁ , zs₂) , zs-split , xs≈zs₁ , ys≈zs₂) = goal where
  --       xs≡zs₁ = indH-xs zs₁ xs≈zs₁
  --       ys≡zs₂ = indH-ys zs₂ ys≈zs₂

  --       goal : xs ⊕ ys ≡ zs
  --       goal = (cong₂ _⊕_ xs≡zs₁ ys≡zs₂) ∙ zs-split

  -- encode : (xs ys : M X) → xs ≡ ys → xs ≈ ys
  -- encode = M.ind {P = λ xs → ∀ ys → xs ≡ ys → xs ≈ ys}
  --   (λ xs → isPropΠ2 λ ys p → isProp≈ xs ys) empty* singl* union* where
  --   empty* : ∀ ys → ε ≡ ys → ε ≈ ys
  --   empty* ys p = ≡ε→isEmpty $ sym p

  --   singl* : ∀ x ys → η x ≡ ys → η x ≈ ys
  --   singl* x = M.ind {P = λ ys → η x ≡ ys → η x ≈ ys} (λ ys → isProp≈ _ ys) (lift ∘ η≢ε) {! !} {! !}

  --   union* : ∀ {xs₁ xs₂}
  --     → (∀ ys → xs₁ ≡ ys → xs₁ ≈ ys)
  --     → (∀ ys → xs₂ ≡ ys → xs₂ ≈ ys)
  --     → (∀ ys → (xs₁ ⊕ xs₂) ≡ ys → (xs₁ ⊕ xs₂) ≈ ys)
  --   union* {xs₁} {xs₂} _ _ _ ys-split = ∣ (xs₁ , xs₂) , ys-split , ≈-refl , ≈-refl {xs = xs₂} ∣₁

  -- MPath≃Code≈ : ∀ {xs ys} → (xs ≡ ys) ≃ (xs ≈ ys)
  -- MPath≃Code≈ {xs} {ys} = propBiimpl→Equiv (isSetM xs ys) (isProp≈ xs ys) (encode xs ys) (decode xs ys)

-- η-merely-inj : {x y : X} → η x ≡ η y → ∥ x ≡ y ∥₁
-- η-merely-inj {x = x} {y = y} ηx≡ηy = ∥x≡y∥ where
--   ηx≈ηy : η x ≈ η y
--   ηx≈ηy = encode (η x) (η y) ηx≡ηy

--   -- spiderman_neat.jpg
--   ∥x≡y∥ : ∥ x ≡ y ∥₁
--   ∥x≡y∥ = ηx≈ηy

-- η-inj : isSet X → {x y : X} → η x ≡ η y → x ≡ y
-- η-inj setX {x} {y} ηx≡ηy = equivFun (PT.propTruncIdempotent≃ (setX x y)) $ η-merely-inj ηx≡ηy