{-# OPTIONS --safe #-}
module Data.Set where

open import Prelude

infixr 5 _∷_
data 𝒦 (A : Type a) : Type a where
  _∷_ : A  𝒦 A  𝒦 A
   : 𝒦 A
  dup :  x xs  x  x  xs  x  xs
  comm :  x y xs  x  y  xs  y  x  xs
  trunc : isSet (𝒦 A)

private variable 
  -- p : Level
  -- P : A → Type p
  x y z : A
  xs ys zs : 𝒦 A

infixr 5 _∷_⟨_⟩
data 𝔎 {p} (A : Type a) (P : 𝒦 A  Type p) : Type (a ℓ⊔ p) where
   : 𝔎 A P
  _∷_⟨_⟩ : A  (xs : 𝒦 A)  (P⟨xs⟩ : P xs)  𝔎 A P

embed : 𝔎 A P  𝒦 A
embed  = 
embed (x  xs  P⟨xs⟩ ) = x  xs

Alg : (P : 𝒦 A  Type p)  Type _
Alg P = (xs : 𝔎 _ P)  P (embed xs)

record Coherent {A : Type a} {P : 𝒦 A  Type p} (ϕ : Alg P) : Type (a ℓ⊔ p) where
  no-eta-equality
  field
    c-trunc :  xs  isSet (P xs)

    c-com :  x y xs P⟨xs⟩ 
            PathP
               i  P (comm x y xs i))
              (ϕ (x  (y  xs)  ϕ (y  xs  P⟨xs⟩ ) ))
              (ϕ (y  (x  xs)  ϕ (x  xs  P⟨xs⟩ )  ))

    c-dup :  x xs P⟨xs⟩ 
            PathP
               i  P (dup x xs i))
              (ϕ (x  (x  xs)  ϕ (x  xs  P⟨xs⟩ ) ))
              (ϕ (x  xs  P⟨xs⟩ ))
open Coherent public

𝒦-foldr : (A  B  B)  B  Alg (const B)
𝒦-foldr f b  = b
𝒦-foldr f b (x  xs  P⟨xs⟩ ) = f x P⟨xs⟩

Ψ : (𝒦 A  Type p)  Type _
Ψ P = Σ[ ϕ  Alg P ] × Coherent ϕ

infixr -1 Ψ-syntax
Ψ-syntax : (A : Type a)  (𝒦 A  Type p)  Type _
Ψ-syntax _ = Ψ

syntax Ψ-syntax A  x  e) = Ψ[ x  𝒦 A ]  e

ψ : Type a  Type b  Type _
ψ A B = Ψ {A = A} (const B)

⟦_⟧ : {P : 𝒦 A  Type p}  Ψ P  (xs : 𝒦 A)  P xs
 alg   = alg .fst 
 alg  (x  xs) = alg .fst (x  xs   alg  xs )
 alg  (comm x y xs i) = alg .snd .c-com x y xs ( alg  xs) i
 alg  (dup x xs i) = alg .snd .c-dup x xs ( alg  xs) i
 alg  (trunc xs ys p q i j) =
  isOfHLevel→isOfHLevelDep 2
    (alg .snd .c-trunc)
    ( alg  xs) ( alg  ys)
    (cong  alg  p) (cong  alg  q)
    (trunc xs ys p q)
    i j

opaque
  prop-coh : {A : Type a} {P : 𝒦 A  Type p} {alg : Alg P}  (∀ xs  isProp (P xs))  Coherent alg
  prop-coh P-isProp .c-trunc xs = isProp→isSet (P-isProp xs)
  prop-coh {P = P} {alg = alg} P-isProp .c-dup x xs ψ⟨xs⟩ =
    toPathP (P-isProp (x  xs) (transp  i  P (dup x xs i)) i0 (alg (x  (x  xs)  alg (x  xs  ψ⟨xs⟩ ) ))) (alg (x  xs  ψ⟨xs⟩ )))
  prop-coh {P = P} {alg = alg} P-isProp .c-com x y xs ψ⟨xs⟩ =
    toPathP (P-isProp (y  x  xs) (transp  i  P (comm x y xs i)) i0 (alg (x  (y  xs)  alg (y  xs  ψ⟨xs⟩ ) ))) (alg (y  (x  xs)  alg (x  xs  ψ⟨xs⟩ ) )))

infix 4 _⊜_
record AnEquality (A : Type a) : Type a where
  constructor _⊜_
  field lhs rhs : 𝒦 A
open AnEquality

EqualityProof-Alg : {B : Type b} (A : Type a) (P : 𝒦 A  AnEquality B)  Type (a ℓ⊔ b)
EqualityProof-Alg A P = Alg  xs  let Pxs = P xs in lhs Pxs  rhs Pxs)

opaque
  eq-coh : {A : Type a} {B : Type b} {P : 𝒦 A  AnEquality B} {alg : EqualityProof-Alg A P}  Coherent alg
  eq-coh {P = P} = prop-coh λ xs  let Pxs = P xs in trunc (lhs Pxs) (rhs Pxs)

union-alg : ψ A (𝒦 A  𝒦 A)
union-alg .fst                   ys = ys
union-alg .fst (x  xs  P⟨xs⟩ )  ys = x  P⟨xs⟩ ys
union-alg .snd .c-trunc _ = isSetΠ λ _  trunc
union-alg .snd .c-com x y xs P⟨xs⟩ i ys = comm x y (P⟨xs⟩ ys) i
union-alg .snd .c-dup x xs P⟨xs⟩ i ys = dup x (P⟨xs⟩ ys) i

infixr 5 _∪_
_∪_ : 𝒦 A  𝒦 A  𝒦 A
_∪_ =  union-alg 

∷-distrib :  (x : A) xs ys  x  (xs  ys)  xs  (x  ys)
∷-distrib x =  alg x 
  where
  alg :  x  Ψ[ xs  𝒦 A ]  (∀ ys  x  (xs  ys)  xs  (x  ys))
  alg x .fst  ys = refl
  alg x .fst (y  xs  P⟨xs⟩ ) ys = comm x y (xs  ys) ; cong (y ∷_) (P⟨xs⟩ ys)
  alg x .snd = prop-coh λ _  isPropΠ λ _  trunc _ _ 

open import Path.Reasoning

∪-idem : (xs : 𝒦 A)  xs  xs  xs
∪-idem =  alg 
  where
  alg : Ψ[ xs  𝒦 A ]  ((xs  xs)  xs)
  alg .fst  = refl
  alg .snd = eq-coh
  alg .fst (x  xs  P⟨xs⟩ ) =
    (x  xs)  (x  xs) ≡˘⟨ ∷-distrib x (x  xs) xs 
    x  x  xs  xs ≡⟨ dup x (xs  xs) 
    x  xs  xs ≡⟨ cong (x ∷_) P⟨xs⟩ 
    x  xs 

∪-idʳ : (xs : 𝒦 A)  (xs    xs)
∪-idʳ =  alg 
  where
  alg : Ψ[ xs  𝒦 A ]  (xs    xs)
  alg .fst  = refl
  alg .fst (x  xs  P⟨xs⟩ ) = cong (x ∷_) P⟨xs⟩
  alg .snd = eq-coh

∪-com : (xs ys : 𝒦 A)  (xs  ys  ys  xs)
∪-com =  alg 
  where
  alg : Ψ[ xs  𝒦 A ]  (∀ ys  xs  ys  ys  xs)
  alg .fst  ys = sym (∪-idʳ ys)
  alg .fst (x  xs  P⟨xs⟩ ) ys = cong (x ∷_) (P⟨xs⟩ ys) ; ∷-distrib x ys xs
  alg .snd = prop-coh λ _  isPropΠ λ _  trunc _ _

∪-assoc : (xs ys zs : 𝒦 A)  ((xs  ys)  zs  xs  (ys  zs))
∪-assoc =  alg 
  where
  alg : Ψ[ xs  𝒦 A ]  (∀ ys zs  (xs  ys)  zs  xs  (ys  zs))
  alg .fst  ys zs = refl
  alg .fst (x  xs  P⟨xs⟩ ) ys zs = cong (x ∷_) (P⟨xs⟩ ys zs)
  alg .snd = prop-coh λ _  isPropΠ λ _  isPropΠ λ _  trunc _ _

𝒦-map : (A  B)  𝒦 A  𝒦 B
𝒦-map f =  map-alg f 
  where
  map-alg : (A  B)  Ψ[ xs  𝒦 A ]  𝒦 B
  map-alg f .fst  = 
  map-alg f .fst (x  _  xs ) = f x  xs
  map-alg f .snd .c-trunc _ = trunc
  map-alg f .snd .c-com x y _ = comm (f x) (f y)
  map-alg f .snd .c-dup x _ = dup (f x)

map-∪ : (f : A  B) (xs ys : 𝒦 A)  𝒦-map f xs  𝒦-map f ys  𝒦-map f (xs  ys)
map-∪ f xs ys =  alg f ys  xs
  where
  alg : (f : A  B) (ys : 𝒦 A)  Ψ[ xs  𝒦 A ]  ((𝒦-map f xs  𝒦-map f ys)  𝒦-map f (xs  ys))
  alg f ys .fst  = refl
  alg f ys .fst (x  _  xs ) = cong (f x ∷_) xs
  alg f ys .snd = prop-coh λ _  trunc _ _

map-id : (xs : 𝒦 A)  𝒦-map id xs  xs
map-id =  alg 
  where
  alg : Ψ[ xs  𝒦 A ]  (𝒦-map id xs  xs)
  alg .fst  = refl
  alg .fst (x  xs  P⟨xs⟩ ) = cong (x ∷_) P⟨xs⟩
  alg .snd = prop-coh λ _  trunc _ _

𝒦-map-comp : (g : B  C) (f : A  B) (xs : 𝒦 A)  𝒦-map g (𝒦-map f xs)  𝒦-map (g  f) xs
𝒦-map-comp g f =  alg g f 
  where
  alg : (g : B  C) (f : A  B)  Ψ[ xs  𝒦 A ]  𝒦-map g (𝒦-map f xs)  𝒦-map (g  f) xs
  alg g f .snd = eq-coh
  alg g f .fst  = refl
  alg g f .fst (x  xs  P⟨xs⟩ ) = cong (g (f x) ∷_) P⟨xs⟩

null : 𝒦 A  Bool
null =  null-alg 
  where
  null-alg : Ψ[ xs  𝒦 A ]  Bool
  null-alg .fst  = true
  null-alg .fst (_  _  _ ) = false
  null-alg .snd .c-trunc _ = isSetBool
  null-alg .snd .c-com x y xs P⟨xs⟩ = refl
  null-alg .snd .c-dup x xs P⟨xs⟩ = refl

𝒦-cat-maybes : (A  Maybe B)  𝒦 A  𝒦 B
𝒦-cat-maybes {A = A} {B = B} p =  alg p 
  where
  alg : (A  Maybe B)  Ψ[ xs  𝒦 A ]  𝒦 B
  alg p .fst  = 
  alg p .fst (x  xs  P⟨xs⟩ ) = maybe id _∷_ (p x) P⟨xs⟩
  alg p .snd .c-trunc _ = trunc
  alg p .snd .c-com x y xs P⟨xs⟩ with p x | p y
  ... | just x | just y = comm x y P⟨xs⟩
  ... | just x | nothing = refl
  ... | nothing | _ = refl
  alg p .snd .c-dup x xs P⟨xs⟩ with p x
  ... | just x = dup x P⟨xs⟩
  ... | nothing = refl

module _ {A : Type a} where
  open import Truth
  open import HITs.PropositionalTruncation

  ∈-alg : A  Ψ[ xs  𝒦 A ]  Ω a
  ∈-alg x .fst  = False
  ∈-alg x .fst (y  _  P⟨xs⟩ ) = (x |≡| y) |∨| P⟨xs⟩
  ∈-alg x .snd .c-trunc _ = isSetΩ
  ∈-alg x .snd .c-com y z xs x∈xs =
    (x |≡| y) |∨| ((x |≡| z) |∨| x∈xs) ≡˘⟨ ∨-assoc (x |≡| y) _ _ 
    ((x |≡| y) |∨| (x |≡| z)) |∨| x∈xs ≡⟨ cong (_|∨| x∈xs) (∨-com _ _) 
    ((x |≡| z) |∨| (x |≡| y)) |∨| x∈xs ≡⟨ ∨-assoc (x |≡| z) _ _ 
    (x |≡| z) |∨| ((x |≡| y) |∨| x∈xs) 
  ∈-alg x .snd .c-dup y xs x∈xs =
    (x |≡| y) |∨| ((x |≡| y) |∨| x∈xs) ≡˘⟨ ∨-assoc (x |≡| y) _ _ 
    ((x |≡| y) |∨| (x |≡| y)) |∨| x∈xs ≡⟨ cong (_|∨| x∈xs) (∨-idem (x |≡| y) ) 
    (x |≡| y) |∨| x∈xs 

  infixr 5 _∈_ _∉_
  _∈_ : A  𝒦 A  Type a
  x  xs = ProofOf ( ∈-alg x  xs)


  isProp-∈ :  x xs  isProp (x  xs)
  isProp-∈ x xs = IsProp ( ∈-alg x  xs)

  _∉_ : A  𝒦 A  Type a
  x  xs = ¬ (x  xs)

  ∀∉ :  xs  (∀ x  x  xs)  xs  
  ∀∉ =  ∀∉-alg 
    where
    ∀∉-alg : Ψ[ xs  𝒦 A ]  ((∀ x  x  xs)  xs  )
    ∀∉-alg .snd = prop-coh λ _  isProp→ (trunc _ _)
    ∀∉-alg .fst  x = refl
    ∀∉-alg .fst (y  _  _ ) x∉xs = absurd (x∉xs y  inl  refl  )

  ∉∅ :  x  x  
  ∉∅ x ()

  ∈-cons :  x xs  x  xs  xs  x  xs
  ∈-cons x =  ∈-cons-alg x 
    where
    ∈-cons-alg :  x  Ψ[ xs  𝒦 A ]  (x  xs  xs  x  xs)
    ∈-cons-alg x .snd = prop-coh λ _  isProp→ (trunc _ _)
    ∈-cons-alg x .fst (y  xs  P⟨xs⟩ ) =
      ∥elim∥
         _  trunc _ _)
        λ { (inl ∣x≡y∣)  ∥rec∥ (trunc _ _)  x≡y  sym (dup y xs) ; cong (_∷ y  xs) (sym x≡y)) ∣x≡y∣
          ; (inr x∈xs)  cong (y ∷_) (P⟨xs⟩ x∈xs) ; comm y x xs
          }

  Disjoint : 𝒦 A  𝒦 A  Type a
  Disjoint xs ys =  x  x  xs  x  ys

  infix 4 _⊆_
  _⊆_ : 𝒦 A  𝒦 A  Type _
  xs  ys =  x  x  xs  x  ys


  disj-com :  xs ys  Disjoint xs ys  Disjoint ys xs
  disj-com xs ys p x x∈xs x∈ys = p x x∈ys x∈xs

  module _ (p : A  Bool) where
    partition-𝒦 : 𝒦 A  𝒦 A × 𝒦 A
    partition-𝒦 =  partition-alg 
      where
      partition-alg : Ψ[ xs  𝒦 A ]  𝒦 A × 𝒦 A
      partition-alg .fst  =  , 
      partition-alg .fst (x  _  fls , trs ) = let px = p x in ((if px then fls else x  fls) , (if px then x  trs else trs))
      partition-alg .snd .c-trunc _ = isSet× trunc trunc
      partition-alg .snd .c-com x y _ (fls , trs) with p x | p y 
      ... | false | false = cong (_, trs) (comm x y fls)
      ... | false | true = refl
      ... | true | false = refl
      ... | true | true = cong (fls ,_) (comm x y trs)
      partition-alg .snd .c-dup x _ (fls , trs) with p x
      ... | false = cong (_, trs) (dup x fls)
      ... | true = cong (fls ,_) (dup x trs)

    partition-𝒦-∪ : (xs : 𝒦 A)  uncurry _∪_ (partition-𝒦 xs)  xs
    partition-𝒦-∪ =  alg 
      where
      alg : Ψ[ xs  𝒦 A ]  uncurry _∪_ (partition-𝒦 xs)  xs
      alg .snd = eq-coh
      alg .fst  = refl
      alg .fst (x  xs  P⟨xs⟩ ) with p x
      ... | false = cong (x ∷_) P⟨xs⟩
      ... | true  = sym (uncurry (∷-distrib x) (partition-𝒦 xs)) ; cong (x ∷_) P⟨xs⟩

    filter-𝒦 : 𝒦 A  𝒦 A
    filter-𝒦 = snd  partition-𝒦

    filter-idem :  xs  filter-𝒦 (filter-𝒦 xs)  filter-𝒦 xs
    filter-idem =  alg 
      where
      alg : Ψ[ xs  𝒦 A ]  filter-𝒦 (filter-𝒦 xs)  filter-𝒦 xs
      alg .fst  = refl
      alg .fst (x  xs  P⟨xs⟩ ) with p x | inspect p x
      ... | false | _ = P⟨xs⟩
      ... | true |  eq  = cong (bool′ _ _) eq ; cong (x ∷_) P⟨xs⟩
      alg .snd = eq-coh

    T→∈-filter :  xs x  T (p x)  x  xs  x  filter-𝒦 xs
    T→∈-filter xs x Tpx =  alg x Tpx  xs
      where
      alg :  x  T (p x)  Ψ[ xs  𝒦 A ]  (x  xs  x  filter-𝒦 xs)
      alg x Tpx .fst (y  xs  P⟨xs⟩ ) x∈xs with p y | inspect p y
      ... | true  |  eq  = ∥map∥ (mapʳ P⟨xs⟩) x∈xs
      ... | false |  eq  = P⟨xs⟩ (∥rec∥ (isProp-∈ x xs) (absurd  ∥rec∥ isProp⊥  e  subst T (cong p e ; eq) Tpx)  id) x∈xs)
      alg x Tpx .snd = prop-coh λ xs  isProp→ (isProp-∈ x (filter-𝒦 xs))

    ∈-filter→T :  xs x  x  filter-𝒦 xs  T (p x)
    ∈-filter→T xs x =  alg x  xs
      where
      open import Data.Bool.Properties

      alg :  x  Ψ[ xs  𝒦 A ]  (x  filter-𝒦 xs  T (p x))
      alg x .snd = prop-coh λ xs  isProp→ (isPropT (p x))
      alg x .fst (y  xs  P⟨xs⟩ ) x∈fpxs with p y | inspect p y
      ... | false |  eq  = P⟨xs⟩ x∈fpxs
      ... | true  |  eq  = ∥rec∥ (isPropT (p x)) (∥rec∥ (isPropT (p x))  x≡y  subst T (sym (cong p x≡y ; eq)) tt)  P⟨xs⟩) x∈fpxs

    filter-⊆ :  xs  filter-𝒦 xs  xs
    filter-⊆ xs x =  alg x  xs
      where
      alg :  x  Ψ[ xs  𝒦 A ]  (x  filter-𝒦 xs  x  xs)
      alg x .snd = prop-coh λ xs  isProp→ (isProp-∈ x xs)
      alg x .fst (y  xs  P⟨xs⟩ ) x∈fpxs with p y | inspect p y
      ... | false |  eq  =  inr (P⟨xs⟩ x∈fpxs) 
      ... | true  |  eq  = ∥map∥ (mapʳ P⟨xs⟩) x∈fpxs


  𝒦-fromList : List A  𝒦 A
  𝒦-fromList = foldr _∷_ 

  fromListed : List A  𝒦 A
  fromListed .fst = 𝒦-fromList
  fromListed .snd =  alg 
    where
    alg : Ψ[ xs  𝒦 A ]   Surjected 𝒦-fromList xs 
    alg .snd = prop-coh λ _  squash
    alg .fst  =  [] ,↠ refl 
    alg .fst (x  xs  P⟨xs⟩ ) = ∥map∥  s  (x  s .image) ,↠ cong (x ∷_) (s .reflects)) P⟨xs⟩

  opaque
    there :  {x} y xs  x  xs  x  y  xs
    there _ _ x∈xs =  inr x∈xs 

  ∈-∪ˡ :  {x} xs ys  x  xs  x  xs  ys
  ∈-∪ˡ xs ys =  alg ys  xs
    where
    alg :  {x } ys  Ψ[ xs  𝒦 A ]  (x  xs  x  xs  ys)
    alg ys .snd = prop-coh λ xs  isProp→ (isProp-∈ _ (xs  _))
    alg ys .fst (x  xs  k ) = ∥map∥ (mapʳ k)

  ∈-∪ʳ :  {x} xs ys  x  ys  x  xs  ys
  ∈-∪ʳ xs ys =  alg ys  xs
    where
    alg :  {x} ys  Ψ[ xs  𝒦 A ]  (x  ys  x  xs  ys)
    alg ys .snd = prop-coh λ xs  isProp→ (isProp-∈ _ (xs  _))
    alg ys .fst  = id
    alg ys .fst (x  xs  k ) x∈ys = there x (xs  _) (k x∈ys)

  ∈-⊎ :  x xs ys  x  xs  ys   x  xs  x  ys 
  ∈-⊎ x xs ys =  alg x ys  xs
    where
    alg :  x ys  Ψ[ xs  𝒦 A ]  (x  xs  ys   x  xs  x  ys )
    alg x ys .snd = prop-coh  _  isProp→ squash)
    alg x ys .fst  x∈xs∪ys =  inr x∈xs∪ys 
    alg x ys .fst (y  xs  P⟨xs⟩ ) = ∥rec∥ squash (∣_∣  inl  ∣_∣  inl  ∥map∥ (mapˡ (∣_∣  inr))  P⟨xs⟩)

  in-head :  {x y : A} {xs}  x  y  x  y  xs
  in-head x≡y =  inl  x≡y  

  open import Data.List.Properties using () renaming (_∈_ to _∈L_; _∉_ to _∉L_; there to thereL)
  open import Data.Fin

  ∈-fromList :  x xs  x ∈L xs  x  𝒦-fromList xs
  ∈-fromList x (y  xs) (fzero   , x∈xs) =  inl  sym x∈xs  
  ∈-fromList x (y  xs) (fsuc i , x∈xs) =  inr (∈-fromList x xs (i , x∈xs)) 

  fromList-∈ :  x xs  x  𝒦-fromList xs   x ∈L xs 
  fromList-∈ x (y  xs) = ∥rec∥ squash (∥map∥  x≡y  fzero , sym x≡y)  ∥map∥ (thereL y)  fromList-∈ x xs)

  All-𝒦 :  {p} (P : A  Ω p)  𝒦 A  Ω p
  All-𝒦 P =  alg P  where
    alg : (P : A  Ω p)  Ψ[ xs  𝒦 A ]  Ω p
    alg P .fst  = True
    alg P .fst (x  xs  P⟨xs⟩ ) = P x |∧| P⟨xs⟩
    alg P .snd .c-trunc _ = isSetΩ
    alg P .snd .c-com x y _ P⟨xs⟩ = sym (∧-assoc (P x) (P y) P⟨xs⟩) ; cong (_|∧| P⟨xs⟩) (∧-com (P x) (P y)) ; ∧-assoc (P y) (P x) P⟨xs⟩
    alg P .snd .c-dup x _ P⟨xs⟩ = sym (∧-assoc (P x) (P x) P⟨xs⟩) ; cong (_|∧| P⟨xs⟩) (∧-idem (P x))

  All-𝒦? :  {p} {P : A  Ω p} (P? :  x  Dec (P x .ProofOf))   xs  Dec (All-𝒦 P xs .ProofOf)
  All-𝒦? P? =  alg P? 
    where
    alg :  {p} {P : A  Ω p} (P? :  x  Dec (P x .ProofOf))  Ψ[  xs  𝒦 A ]  Dec (All-𝒦 P xs .ProofOf)
    alg P? .snd = prop-coh λ xs  isPropDec (All-𝒦 _ xs .IsProp)
    alg P? .fst  = yes Poly-tt
    alg P? .fst (x  _  P⟨xs⟩ ) with P? x
    alg P? .fst (x  _  P⟨xs⟩ ) | no ¬Px = no (¬Px  fst)
    alg P? .fst (x  _  no ¬P⟨xs⟩ ) | _ = no (¬P⟨xs⟩  snd)
    alg P? .fst (x  _  yes P⟨xs⟩ ) | yes Px = yes (Px , P⟨xs⟩)

  All-𝒦-∈ :  {p} (P : A  Ω p)   x xs  ProofOf (All-𝒦 P xs)  x  xs  ProofOf (P x)
  All-𝒦-∈ P x =  alg P x 
    where
    alg :  {p} (P : A  Ω p)   x  Ψ[ xs  𝒦 A ]  (ProofOf (All-𝒦 P xs)  x  xs  ProofOf (P x))
    alg P x .snd = prop-coh  xs  isProp→ (isProp→ (IsProp (P x))))
    alg P x .fst (y  xs  P⟨xs⟩ ) (px , apxs) =
      ∥rec∥ (IsProp (P x)) (∥rec∥ (IsProp (P x))  x≡y  subst (ProofOf  P) (sym x≡y) px )  P⟨xs⟩ apxs)

  ∈-All-𝒦 :  {p} (P : A  Ω p)   xs  (∀ x  x  xs  P x .ProofOf)  All-𝒦 P xs .ProofOf
  ∈-All-𝒦 P =  alg P 
    where
    alg :  {p} (P : A  Ω p)  Ψ[ xs  𝒦 A ]  ((∀ x  x  xs  P x .ProofOf)  All-𝒦 P xs .ProofOf)
    alg P .snd = prop-coh λ xs  isProp→ (IsProp (All-𝒦 P xs))
    alg P .fst  k = Poly-tt
    alg P .fst (x  xs  P⟨xs⟩ ) k = k x  inl  refl   , P⟨xs⟩ λ y y∈xs  k y  inr y∈xs 

  Any-𝒦 :  {p} (P : A  Ω p)  𝒦 A  Ω p
  Any-𝒦 P =  alg P  where
    alg : (P : A  Ω p)  Ψ[ xs  𝒦 A ]  Ω p
    alg P .fst  = False
    alg P .fst (x  xs  P⟨xs⟩ ) = P x |∨| P⟨xs⟩
    alg P .snd .c-trunc _ = isSetΩ
    alg P .snd .c-com x y _ P⟨xs⟩ = sym (∨-assoc (P x) (P y) P⟨xs⟩) ; cong (_|∨| P⟨xs⟩) (∨-com (P x) (P y)) ; ∨-assoc (P y) (P x) P⟨xs⟩
    alg P .snd .c-dup x _ P⟨xs⟩ = sym (∨-assoc (P x) (P x) P⟨xs⟩) ; cong (_|∨| P⟨xs⟩) (∨-idem (P x))

  Any-𝒦? :  {p} {P : A  Ω p} (P? :  x  Dec (P x .ProofOf))   xs  Dec (Any-𝒦 P xs .ProofOf)
  Any-𝒦? P? =  alg P? 
    where
    alg :  {p} {P : A  Ω p} (P? :  x  Dec (P x .ProofOf))  Ψ[  xs  𝒦 A ]  Dec (Any-𝒦 P xs .ProofOf)
    alg P? .snd = prop-coh λ xs  isPropDec (Any-𝒦 _ xs .IsProp)
    alg P? .fst  = no λ ()
    alg P? .fst (x  _  P⟨xs⟩ ) with P? x
    alg P? .fst (x  _  _ ) | yes Px = yes  inl Px 
    alg P? .fst (x  _  yes P⟨xs⟩ ) | _ = yes  inr P⟨xs⟩ 
    alg P? .fst (x  _  no ¬P⟨xs⟩ ) | no ¬Px = no (∥rec∥ isProp⊥ (¬Px  ¬P⟨xs⟩))

  Any-𝒦-∈ :  {p} (P : A  Ω p)   xs  ProofOf (Any-𝒦 P xs)    x × x  xs × ProofOf (P x) 
  Any-𝒦-∈ P =  alg P 
    where
    alg :  {p} (P : A  Ω p)  Ψ[ xs  𝒦 A ]  (ProofOf (Any-𝒦 P xs)    x × x  xs × ProofOf (P x) )
    alg P .snd = prop-coh  xs  isProp→ squash)
    alg P .fst (x  xs  P⟨xs⟩ ) = ∥rec∥ squash ((∣_∣  (x ,_)  ( inl  refl   ,_))  ∥map∥ (map₂ (map₁ (there x xs)) )  P⟨xs⟩)

  ∈-Any-𝒦 :  {p} (P : A  Ω p)   x  ProofOf (P x)   xs  x  xs  Any-𝒦 P xs .ProofOf
  ∈-Any-𝒦 P x Px =  alg P x Px 
    where
    alg :  {p} (P : A  Ω p)   x  ProofOf (P x)  Ψ[ xs  𝒦 A ]  (x  xs  Any-𝒦 P xs .ProofOf)
    alg P x Px .snd = prop-coh λ xs  isProp→ (IsProp (Any-𝒦 P xs))
    alg P x Px .fst (y  xs  P⟨xs⟩ ) =
      ∥rec∥ (IsProp (Any-𝒦 P (y  xs)))
        (∥rec∥ (IsProp (Any-𝒦 P (y  xs))) (∣_∣  inl  flip (subst (ProofOf  P)) Px )  ∣_∣  inr  P⟨xs⟩)

  isProp-⊆ : (xs ys : 𝒦 A)  isProp (xs  ys)
  isProp-⊆ xs ys = isPropΠ  x  isProp→ (isProp-∈ x ys))

open import HITs.PropositionalTruncation

opaque
  here : (x : A) (xs : 𝒦 A)  x  x  xs
  here x xs =  inl  refl { x = x }  

module _ {A : Type a} {B : Type b} where
  map-with-∈ : (xs : 𝒦 A)  (∀ x  x  xs  B)  𝒦 B
  map-with-∈ =  alg 
      where
      alg : Ψ[ xs  𝒦 A ]  ((∀ x  x  xs  B)  𝒦 B)
      alg .fst  f = 
      alg .fst (x  xs  P⟨xs⟩ ) f = f x (here x xs)  P⟨xs⟩ λ y y∈xs  f y (there x xs y∈xs)
      alg .snd .c-trunc xs = isSetΠ λ _  trunc
      alg .snd .c-dup x xs P⟨xs⟩ = 
        J  ys x∷x∷xs≡ys  (x∈ys : x  ys) (y∈ys :  y  y  xs  y  ys) 
          PathP
             i  ((x : A)  x  x∷x∷xs≡ys i  B)  𝒦 B)
             f  f x (here x (x  xs))  f x (there x (x  xs) (here x xs))  P⟨xs⟩ λ y y∈xs  f y (there x (x  xs) (there x xs y∈xs)))
             f  f x x∈ys  P⟨xs⟩ λ y y∈xs  f y (y∈ys y y∈xs)))
             x∈ys y∈ys  funExt λ f   
              cong₃  a b c  f x a  f x b  P⟨xs⟩ λ y y∈xs  f y (c y y∈xs))
                {z′ = y∈ys}
                (isProp-∈ x (x  x  xs) (here x (x  xs)) x∈ys)
                (isProp-∈ x (x  x  xs) (there x (x  xs) (here x xs)) x∈ys)
                (funExt λ y  funExt λ y∈xs  isProp-∈ y (x  x  xs) (there x (x  xs) (there x xs y∈xs)) (y∈ys y y∈xs))
                ; dup (f x x∈ys) (P⟨xs⟩  y y∈xs  f y (y∈ys y y∈xs)))
                )
            (dup x xs)
            (here x xs)
             _  there x xs)
      alg .snd .c-com x y xs P⟨xs⟩ =
        J  ys x∷y∷xs≡ys  (y∈ys : y  ys) (x∈ys : x  ys) (z∈ys :  z  z  xs  z  ys) 
          PathP
             i  ((x : A)  x  x∷y∷xs≡ys i  B)  𝒦 B)
             f  f x (here x (y  xs))  f y (there x (y  xs) (here y xs))  P⟨xs⟩ λ z z∈xs  f z (there x (y  xs) (there y xs z∈xs)) )
             f  f y y∈ys  f x x∈ys  P⟨xs⟩ λ z z∈xs  f z (z∈ys z z∈xs))
          )
           y∈ys x∈ys z∈ys  funExt λ f 
            cong₃
               a b c  f x a  f y b  P⟨xs⟩  z z∈xs  f z (c z z∈xs)))
              {z′ = z∈ys}
              (isProp-∈ x (x  y  xs) (here x (y  xs)) x∈ys)
              (isProp-∈ y (x  y  xs) (there x (y  xs) (here y xs)) y∈ys)
              (funExt λ z  funExt λ z∈xs  isProp-∈ z (x  y  xs) (there x (y  xs) (there y xs z∈xs)) (z∈ys z z∈xs))
              ; comm (f x x∈ys) (f y y∈ys) (P⟨xs⟩  z z∈xs  f z (z∈ys z z∈xs)))
          )
          (comm x y xs)
          (here y (x  xs))
          (there y (x  xs) (here x xs))
           z  there y (x  xs)  there x xs)

  map-with-∈-map :  (f : A  B) xs  map-with-∈ xs  x x∈xs  f x)  𝒦-map f xs
  map-with-∈-map f =  alg f 
    where
    alg : (f : A  B)  Ψ[ xs  𝒦 A ]  (map-with-∈ xs  x x∈xs  f x)  𝒦-map f xs)
    alg f .snd = eq-coh
    alg f .fst  = refl
    alg f .fst (x  xs  P⟨xs⟩ ) = cong (f x ∷_) P⟨xs⟩
 

∈-map-𝒦 :  (f : A  B) x xs  x  xs  f x  𝒦-map f xs
∈-map-𝒦 f x =  alg f x 
  where
  alg :  (f : A  B) x  Ψ[ xs  𝒦 A ]  (x  xs  f x  𝒦-map f xs)
  alg f x .snd = prop-coh λ xs  isProp→ (isProp-∈ (f x) (𝒦-map f xs))
  alg f x .fst (y  xs  P⟨xs⟩ ) = ∥map∥ (map-⊎ (∥map∥ (cong f)) P⟨xs⟩)

∈-map-ind-𝒦 :  (xs : 𝒦 A) (f :  x  x  xs  B) y  (y∈xs : y  xs)  f y y∈xs  map-with-∈ xs f
∈-map-ind-𝒦 xs f y =  alg y  xs f
  where
  opaque
    unfolding there
    alg :  y  Ψ[ xs  𝒦 A ]  (∀ (f :  x  x  xs  B)  (y∈xs : y  xs)  f y y∈xs  map-with-∈ xs f)
    alg y .snd = prop-coh λ xs  isPropΠ λ f  isPropΠ λ y∈xs  isProp-∈ (f y y∈xs) (map-with-∈ xs f)
    alg y .fst (x  xs  P⟨xs⟩ ) f =
      ∥elim∥
         y∈xs  isProp-∈ (f y y∈xs) (map-with-∈ (x  xs) f))
        λ { (inl ∣y≡x∣)  ∥rec∥ (isProp-∈ (f y  inl ∣y≡x∣ ) (map-with-∈ (x  xs) f))  y≡x   inl  cong (uncurry f) (Σ≡Prop  y  isProp-∈ y (x  xs)) y≡x)  ) ∣y≡x∣
          ; (inr y∈xs)  there (f x (here x xs)) (map-with-∈ xs _) (P⟨xs⟩  z z∈xs  f z (there x xs z∈xs)) y∈xs)
          }

∈-map-inv-𝒦 :  (f : A  B) x xs  fst f x  𝒦-map (fst f) xs  x  xs
∈-map-inv-𝒦 f x =  alg f x 
  where
  alg :  (f : A  B) x  Ψ[ xs  𝒦 A ]  (fst f x  𝒦-map (fst f) xs  x  xs)
  alg f x .snd = prop-coh λ xs  isProp→ (isProp-∈ x xs)
  alg f x .fst (y  xs  P⟨xs⟩ ) = ∥map∥ (map-⊎ (∥map∥ (f .snd)) P⟨xs⟩)

∈-map-surj :  (f : A  B) x xs  x  𝒦-map f xs   Surjected f x 
∈-map-surj f x =  alg f x 
  where
  alg :  (f : A  B) x  Ψ[ xs  𝒦 A ]  (x  𝒦-map f xs   Surjected f x )
  alg f x .fst (y  xs  P⟨xs⟩ ) = ∥rec∥ squash (∥map∥ ((y ,↠_)  sym)  P⟨xs⟩)
  alg f x .snd = prop-coh λ xs  isProp→ squash

_>>=_ : 𝒦 A  (A  𝒦 B)  𝒦 B
xs >>= k =  bind-alg k  xs
  where
  bind-alg : (A  𝒦 B)  Ψ[ xs  𝒦 A ]  𝒦 B
  bind-alg k .fst  = 
  bind-alg k .fst (x  _  xs ) = k x  xs
  bind-alg k .snd .c-trunc _ = trunc
  bind-alg k .snd .c-com x y _ xs =
    k x  k y  xs ≡˘⟨ ∪-assoc (k x) (k y) xs 
    (k x  k y)  xs ≡⟨ cong (_∪ xs) (∪-com (k x) (k y)) 
    (k y  k x)  xs ≡⟨ ∪-assoc (k y) (k x) xs 
    k y  k x  xs 
  bind-alg k .snd .c-dup x _ xs =
    k x  k x  xs ≡˘⟨ ∪-assoc (k x) (k x) xs 
    (k x  k x)  xs ≡⟨ cong (_∪ xs) (∪-idem (k x)) 
    k x  xs 

open import HITs.PropositionalTruncation

∈->>= :  (x : A) (y : B)
       (k : A  𝒦 B)
       (y  k x)
       (xs : 𝒦 A)
       x  xs
       y  (xs >>= k)
∈->>= x y k fx∈kx =  alg x y k fx∈kx 
  where
  alg :  (x : A) (y : B) (k : A  𝒦 B)  y  k x  Ψ[ xs  𝒦 A ]  (x  xs  y  (xs >>= k))
  alg x y k y∈kx .snd = prop-coh λ xs  isProp→ (isProp-∈ y (xs >>= k))
  alg x y k y∈kx .fst (z  xs  P⟨xs⟩ ) =
    ∥rec∥
      (isProp-∈ y ((z  xs) >>= k))
      (∥rec∥ (isProp-∈ y ((z  xs) >>= k))  x≡z  ∈-∪ˡ (k z) (xs >>= k) (subst  e  y  k e) x≡z y∈kx))
       ∈-∪ʳ (k z) (xs >>= k)  P⟨xs⟩)