{-# OPTIONS --safe #-}

open import Prelude

module Data.Set.Discrete {a} {A : Type a} (disc : Discrete A) where

private
  infix 4 _≟_
  _≟_ : Discrete A
  _≟_ = disc

open import Data.Set
open import Truth
-- open import Data.List.Properties using () renaming (_∈_ to _∈L_; _∉_ to _∉L_; there to thereL)
open import HITs.PropositionalTruncation

∈?-alg :  x  Ψ[ xs  𝒦 A ]  Dec (x  xs)
∈?-alg x .snd = prop-coh λ xs  isPropDec (IsProp ( ∈-alg x  xs))
∈?-alg x .fst  = no λ ()
∈?-alg x .fst (y  xs  x∈?xs ) =
  map-dec
    (∣_∣  mapˡ ∣_∣)
     ¬e⊎x∈xs  ∥rec∥ isProp⊥ (∥rec∥ isProp⊥ (¬e⊎x∈xs  inl)  ¬e⊎x∈xs  inr))
    ((x  y) or x∈?xs)

opaque
  _∈?_ :  (x : A) xs  Dec (x  xs)
  x ∈? xs =  ∈?-alg x  xs

infixl 6 _\\_
_\\_ : 𝒦 A  A  𝒦 A
xs \\ x = filter-𝒦  y  neg (does (x  y))) xs

\\-com :  x y xs  xs \\ x \\ y  xs \\ y \\ x
\\-com x y =  alg x y 
  where
  alg :  x y  Ψ[ xs  𝒦 A ]  xs \\ x \\ y  xs \\ y \\ x
  alg x y .snd = eq-coh
  alg x y .fst  = refl
  alg x y .fst (z  xs  P⟨xs⟩ ) with x  z | y  z
  ... | no  x≢z | no  y≢z = cong (bool′ _ _  neg) (it-doesn't (y  z) y≢z) ; cong (z ∷_) P⟨xs⟩ ; sym (cong (bool′ _ _  neg) (it-doesn't (x  z) x≢z))
  ... | no  x≢z | yes y≡z = cong (bool′ _ _  neg) (it-does (y  z) y≡z) ; P⟨xs⟩
  ... | yes x≡z | no  y≢z = P⟨xs⟩ ; sym (cong (bool′ _ _  neg) (it-does (x  z) x≡z))
  ... | yes x≡z | yes y≡z = P⟨xs⟩

\\-idem :  x xs  xs \\ x \\ x  xs \\ x
\\-idem x =  alg x 
  where
  alg :  x  Ψ[ xs  𝒦 A ]  xs \\ x \\ x  xs \\ x
  alg x .snd = eq-coh
  alg x .fst  = refl
  alg x .fst (y  xs  P⟨xs⟩ ) with x  y
  ... | no  x≢y = cong (bool′ _ _  neg) (it-doesn't (x  y) x≢y) ; cong (y ∷_) P⟨xs⟩
  ... | yes x≡y = P⟨xs⟩

open import Data.Empty.Properties

∉rem :  x xs  x  xs \\ x
∉rem x =  ∉rem-alg x 
  where
  ∉rem-alg : (x : A)  Ψ[ xs  𝒦 A ]  x  xs \\ x
  ∉rem-alg x .snd = prop-coh λ _  isProp¬
  ∉rem-alg x .fst  ()
  ∉rem-alg x .fst (y  xs  P⟨xs⟩ ) with x  y
  ... | no  x≢y = ∥rec∥ isProp⊥ (∥rec∥ isProp⊥ x≢y  P⟨xs⟩)
  ... | yes x≡y = P⟨xs⟩

rem-∉ :  x xs  x  xs  xs \\ x  xs
rem-∉ x =  rem-∉-alg x 
  where
  rem-∉-alg :  x  Ψ[ xs  𝒦 A ]  (x  xs  xs \\ x  xs)
  rem-∉-alg x .snd = prop-coh λ _  isProp→ (trunc _ _)
  rem-∉-alg x .fst  _ = refl
  rem-∉-alg x .fst (y  xs  P⟨xs⟩ ) p with x  y
  ... | yes x≡y = absurd (p  inl  x≡y  )
  ... | no  x≢y = cong (y ∷_) (P⟨xs⟩ (p  ∣_∣  inr))

rem-∈ :  x y xs  x  xs \\ y  x  xs
rem-∈  x y =  rem-∈-alg x y 
  where
  rem-∈-alg :  x y  Ψ[ xs  𝒦 A ]  (x  xs \\ y  x  xs)
  rem-∈-alg x y .snd = prop-coh λ xs  isProp→ (IsProp ( ∈-alg x  xs))
  rem-∈-alg x y .fst  ()
  rem-∈-alg x y .fst (z  xs  P⟨xs⟩ ) with y  z
  ... | yes y≡z = λ p   inr (P⟨xs⟩ p) 
  ... | no  y≢z = ∥rec∥ squash (∣_∣  mapʳ P⟨xs⟩)

≢-rem :  x y  x  y   xs  x  xs  x  xs \\ y
≢-rem x y x≢y =  ≢-rem-alg x y x≢y 
  where
  ≢-rem-alg :  x y  x  y  Ψ[ xs  𝒦 A ]  (x  xs  x  xs \\ y)
  ≢-rem-alg x y x≢y .snd = prop-coh λ xs  isProp→ (IsProp ( ∈-alg x  (xs \\ y)))
  ≢-rem-alg x y x≢y .fst  ()
  ≢-rem-alg x y x≢y .fst (z  xs  P⟨xs⟩ ) with y  z
  ... | yes y≡z = ∥rec∥ (isProp-∈ x (xs \\ y)) (absurd  ∥rec∥ isProp⊥  x≡z  x≢y (x≡z ; sym y≡z))  P⟨xs⟩)
  ... | no  y≢z = ∥rec∥ squash (∣_∣  mapʳ P⟨xs⟩)

-- rem-tail : ∀ x xs → x ∈ xs → xs ≡ x ∷ xs \\ x
-- rem-tail x = ⟦ rem-tail-alg x ⟧
--   where
--   rem-tail-alg : ∀ x → Ψ[ xs ⦂ 𝒦 A ] ↦ (x ∈ xs → xs ≡ x ∷ xs \\ x)
--   rem-tail-alg x .snd = prop-coh λ _ → isProp→ (trunc _ _)
--   rem-tail-alg x .fst ∅ ()
--   rem-tail-alg x .fst (y ∷ xs ⟨ P⟨xs⟩ ⟩) with x ≟ y
--   ... | no  x≢y = ∥rec∥ (trunc _ _) (∥rec∥ (trunc _ _) (absurd ∘ x≢y) ▿ (λ x∈xs → cong (y ∷_) (P⟨xs⟩ x∈xs) ; comm y x _))
--   ... | yes x≡y with x ∈? xs
--   ... | no  x∉xs = λ p → sym (cong₂ _∷_ x≡y (rem-∉ x xs x∉xs))
--   ... | yes x∈xs = λ _ → cong₂ _∷_ (sym x≡y) (P⟨xs⟩ x∈xs) ; dup x _

-- rem-cons : ∀ (x : A) (xs : 𝒦 A) → x ∷ xs ≡ x ∷ xs \\ x
-- rem-cons x xs = rem-tail x (x ∷ xs) ∣ inl ∣ refl ∣ ∣ ; cong (x ∷_) (cong (bool′ _ _ ∘ neg) (it-does (x ≟ x) refl))

-- ∈-eq-lemma : ∀ x xs ys → x ∉ xs → (∀ z → z ∈ x ∷ xs ⟷ z ∈ ys) → ∀ z → z ∈ xs ⟷ z ∈ ys \\ x
-- ∈-eq-lemma x xs ys x∉xs xxs↭ys z .fst z∈xs with z ≟ x
-- ... | yes z≡x = absurd (x∉xs (subst (_∈ xs) z≡x z∈xs) )
-- ... | no  z≢x =
--   ≢-rem z x z≢x ys (xxs↭ys z .fst ∣ inr z∈xs ∣)
-- ∈-eq-lemma x xs ys x∉xs xxs↭ys z .snd z∈rys with z ≟ x
-- ... | yes z≡x = absurd (∉rem x ys (subst (_∈ ys \\ x) z≡x z∈rys))
-- ... | no z≢x =
--   ∥rec∥
--     (IsProp (⟦ ∈-alg z ⟧ xs))
--     (absurd ∘ ∥rec∥ isProp⊥ z≢x ▿ id)
--     (xxs↭ys z .snd (rem-∈ z x ys z∈rys))

-- ⊆-antisym : (xs ys : 𝒦 A) → xs ⊆ ys → ys ⊆ xs → xs ≡ ys
-- ⊆-antisym = ⟦ ⊆-antisym-alg ⟧
--   where
--   ⊆-antisym-alg : Ψ[ xs ⦂ 𝒦 A ] ↦ (∀ ys → xs ⊆ ys → ys ⊆ xs → xs ≡ ys)
--   ⊆-antisym-alg .snd = prop-coh λ _ → isPropΠ λ _ → isProp→ (isProp→ (trunc _ _))
--   ⊆-antisym-alg .fst ∅ ys xs⊆ys ys⊆xs = sym (∀∉ ys λ y y∈ys → ∉∅ y (ys⊆xs y y∈ys))
--   ⊆-antisym-alg .fst (x ∷ xs ⟨ P⟨xs⟩ ⟩) ys xs⊆ys ys⊆xs with x ∈? xs
--   ... | yes x∈xs =
--    let h = ∈-cons x xs x∈xs
--    in sym h ; P⟨xs⟩ ys (λ z z∈xs → xs⊆ys z ∣ inr z∈xs ∣) (λ z z∈ys → subst (z ∈_) (sym h) (ys⊆xs z z∈ys))
--   ... | no  x∉xs =
--     let h = ∈-eq-lemma x xs ys x∉xs (λ z → xs⊆ys z , ys⊆xs z) ⦂ (∀ z → z ∈ xs ⟷ z ∈ ys \\ x)
--     in cong (x ∷_) (P⟨xs⟩ (ys \\ x) (fst ∘′ h) (snd ∘′ h)) ; sym (rem-tail x ys (xs⊆ys x ∣ inl ∣ refl ∣ ∣))

-- fromList-eq : (xs ys : List A) → (∀ x → x ∈L xs ⟷ x ∈L ys) → 𝒦-fromList xs ≡ 𝒦-fromList ys
-- fromList-eq xs ys p =
--   ⊆-antisym
--     (𝒦-fromList xs)
--     (𝒦-fromList ys)
--     (λ x → ∥rec∥ (isProp-∈ x (𝒦-fromList ys)) (∈-fromList x ys ∘ p x .fst) ∘ fromList-∈ x xs)
--     (λ x → ∥rec∥ (isProp-∈ x (𝒦-fromList xs)) (∈-fromList x xs ∘ p x .snd) ∘ fromList-∈ x ys)

-- infixr 5 _∩_
-- _∩_ : 𝒦 A → 𝒦 A → 𝒦 A
-- xs ∩ ys = filter-𝒦 (λ x → does (x ∈? ys)) xs

-- ∈-∩ : ∀ 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 λ xs → isProp× (isProp→ (isProp× (isProp-∈ x xs) (isProp-∈ x ys))) (isProp→ (isProp-∈ x (xs ∩ ys)))
--   ∈-∩-alg x ys .fst ∅ .fst ()
--   ∈-∩-alg x ys .fst ∅ .snd = fst
--   ∈-∩-alg x ys .fst (y ∷ xs ⟨ P⟨xs⟩ ⟩) .fst x∈yx∩ys with y ∈? ys
--   ... | no y∉ys = map₁ (∣_∣ ∘ inr) (P⟨xs⟩ .fst x∈yx∩ys)
--   ... | yes y∈ys =
--     ∥rec∥
--       (isProp× squash (isProp-∈ x ys))
--       ((λ x≡y → ∣ inl x≡y ∣ , subst (_∈ ys) (∥rec∥ (Discrete→isSet _≟_ _ _) sym x≡y) y∈ys) ▿ map₁ (∣_∣ ∘ inr) ∘ P⟨xs⟩ .fst) x∈yx∩ys
--   ∈-∩-alg x ys .fst (y ∷ xs ⟨ P⟨xs⟩ ⟩) .snd (x∈xs , x∈ys) with y ∈? ys
--   ... | no y∉ys = P⟨xs⟩ .snd (∥rec∥ (isProp-∈ x xs) (absurd ∘ ∥rec∥ isProp⊥ (λ x≡y → y∉ys (subst (_∈ ys) x≡y x∈ys)) ▿ id) x∈xs , x∈ys)
--   ... | yes y∈ys = ∥map∥ (mapʳ (P⟨xs⟩ .snd ∘ (_, x∈ys))) x∈xs

-- _⊆?_ : ∀ xs ys → Dec (xs ⊆ ys)
-- xs ⊆? ys = case All-𝒦? {P = λ x → ⟦ ∈-alg x ⟧ ys} (λ x → x ∈? ys) xs of
--   λ { (no ¬xs⊆) → no λ xs⊆ys → ¬xs⊆ (∈-All-𝒦 _ xs xs⊆ys)
--     ; (yes xs⊆) → yes λ x x∈xs → All-𝒦-∈ _ x xs xs⊆ x∈xs
--     }

-- discreteSet : Discrete (𝒦 A)
-- discreteSet xs ys =
--   map-dec
--     (uncurry (⊆-antisym xs ys))
--     (λ ¬p xs≡ys → ¬p ((λ x → subst (x ∈_) xs≡ys) , (λ x → subst (x ∈_) (sym xs≡ys))))
--     (case (xs ⊆? ys , ys ⊆? xs) of
--     λ { (no xs⊈ys , _) → no (xs⊈ys ∘ fst)
--       ; (_ , no ys⊈xs) → no (ys⊈xs ∘ snd)
--       ; (yes xs⊆ys , yes ys⊆xs) → yes (xs⊆ys , ys⊆xs)
--     })

-- import Data.List.Properties.Discrete disc as L

-- \\-hom : ∀ (x : A) xs → 𝒦-fromList xs \\ x ≡ 𝒦-fromList (xs L.\\ x)
-- \\-hom x [] = refl
-- \\-hom x (y ∷ xs) with x ≟ y
-- ... | yes x≡y = \\-hom x xs
-- ... | no  x≢y = cong (y ∷_) (\\-hom x xs)

-- fromList-nub : (xs : List A) → 𝒦-fromList xs ≡ 𝒦-fromList (L.nub xs)
-- fromList-nub [] = refl
-- fromList-nub (x ∷ xs) =
--   𝒦-fromList (x ∷ xs) ≡⟨⟩
--   x ∷ 𝒦-fromList xs ≡⟨ cong (x ∷_) (fromList-nub xs) ⟩
--   x ∷ 𝒦-fromList (L.nub xs) ≡⟨ rem-tail x (x ∷ 𝒦-fromList (L.nub xs)) ∣ inl ∣ refl ∣ ∣ ⟩
--   x ∷ (x ∷ 𝒦-fromList (L.nub xs)) \\ x ≡⟨ cong (x ∷_) (cong (bool′ _ _ ∘ neg) (it-does (x ≟ x) refl)) ⟩
--   x ∷ 𝒦-fromList (L.nub xs) \\ x ≡⟨ cong (x ∷_) (\\-hom x (L.nub xs)) ⟩
--   x ∷ 𝒦-fromList (L.nub xs L.\\ x) ≡⟨⟩
--   𝒦-fromList (x ∷ L.nub xs L.\\ x) ≡⟨⟩
--   𝒦-fromList (L.nub (x ∷ xs)) ∎