module Prelude.Distinct where

open import Data.List.Fresh as F public
  using ([] ; cons; fresh; _∷#_; map; map-#)
  renaming (List# to List##)

module A where
  open import Data.List.Fresh.Relation.Unary.All   public
  open import Data.List.Fresh.Relation.Unary.Any   public
    hiding (map)
open A public
  using ([]; _∷_; here; there)

open import Data.Unit
  using (; tt)
open import Data.Nat
  using (; zero; suc)
open import Data.Fin
  using (Fin; zero; suc)
open import Data.Empty
open import Data.Product
open import Data.Sum

open import Relation.Nullary
open import Relation.Binary
open import Relation.Binary.PropositionalEquality

open import Prelude.Level
open import Prelude.DecEq

List# : Set  Set
List# A = List## A _≢_

Fins# :   Set
Fins# n = List# (Fin n)

private variable
  A        : Set
  x y      : A
  xs ys zs : List# A

infix 5 _#_

_#_ : A  List# A  Set _
_#_ x xs = fresh _ _≢_ x xs

_##_ : List# A  List# A  Set
[]        ## ys = 
(x ∷# xs) ## ys = x # ys × (xs ## ys)

All : (P : A  Set)  List# A  Set _
All P xs = A.All P xs

Map : List# A  Set  Set
Map xs V = All  _  V) xs

infix 5 _#∈_ _#∉_ _#⊆_

_#∈_ _#∉_ : A  List# A  Set _
x #∈ xs = A.Any (x ≡_) xs

x #∉ xs = ¬ x #∈ xs

_#⊆_ : (xs y : List# A)  Set _
xs #⊆ ys =  {x}  x #∈ xs  x #∈ ys

≡→⊆ : xs  ys  xs #⊆ ys
≡→⊆ refl x = x

[]⊆xs : [] #⊆ xs
[]⊆xs ()
#⊆-refl : xs #⊆ xs
#⊆-refl x = x

⊆-trans : xs #⊆ ys  ys #⊆ zs  xs #⊆ zs
⊆-trans xs⊆ys ys⊆zs x∈ = ys⊆zs (xs⊆ys x∈)

∈→¬fresh : (xs : List# A)  x #∈ xs  x # xs  
∈→¬fresh (cons y xs _) (here eq)  (x≠y , _)  = x≠y eq
∈→¬fresh (cons y xs _) (there x∈) (_ , x#xs) = ∈→¬fresh xs x∈ x#xs

#∈-uniq : (x∈ x∈′ : x #∈ xs)  x∈  x∈′
#∈-uniq (here refl) (here refl) = refl
#∈-uniq {xs = cons x xs x#xs} (here refl) (there x∈′) =
  ⊥-elim (∈→¬fresh _ x∈′ x#xs)
#∈-uniq {xs = cons x xs x#xs} (there x∈)  (here refl) =
  ⊥-elim (∈→¬fresh _ x∈  x#xs)
#∈-uniq (there x∈) (there x∈′)  = cong there (#∈-uniq x∈ x∈′)

module Decidable {A : Set}  _ : DecEq A  where
  fresh? : (x : A) (xs : List# A)  x # xs  x #∈ xs
  fresh? x [] = inj₁ _
  fresh? x (cons y xs y#xs) with x  y
  ... | yes eq = inj₂ (here eq)
  ... | no neq with fresh? x xs
  ... | inj₁ x#xs = inj₁ (neq , x#xs)
  ... | inj₂ x∈xs = inj₂ (there x∈xs)

  _#∈?_ : (x : A) (xs : List# A)  Dec (x #∈ xs)
  _#∈?_ x []               = no λ ()
  _#∈?_ x (cons y ys _) with x  y
  ... | yes p = yes (here p)
  ... | no ¬p with x #∈? ys
  ... | yes q = yes (there q)
  ... | no ¬q = no λ where
    (here eq)   ¬p eq
    (there x∈)  ¬q x∈

  _#⊆?_ : (xs ys : List# A)  Dec (xs #⊆ ys)
  _#⊆?_ []            ys = yes λ ()
  _#⊆?_ (cons x xs _) ys with x #∈? ys
  ... | no ¬p = no λ ⊆ys  ¬p (⊆ys (here refl))
  ... | yes p with xs #⊆? ys
  ... | no ¬q = no λ ⊆ys  ¬q  x∈  ⊆ys (there x∈))
  ... | yes q = yes λ where
    (here refl)  p
    (there x∈)   q x∈

  infixr 8 _∪_

  _∪_     : (xs ys : List# A)  List# A
  fresh-∪ : x # xs  x # ys  x # (xs  ys)

  []            ys = ys
  cons x xs px  ys with fresh? x ys
  ... | inj₁ x#ys = cons x (xs  ys) (fresh-∪ px x#ys)
  ... | inj₂ x∈ys = xs  ys

  fresh-∪ {x} {[]}             {ys} _   pys = pys
  fresh-∪ {x} {cons y xs y#xs} {ys} (px , x#xs) x#ys with fresh? y ys
  ... | inj₁ y#ys = px , fresh-∪ x#xs x#ys
  ... | inj₂ y∈ys = fresh-∪ x#xs x#ys

  ∈-∪⁻
    :  {x : A} xs {ys : List# A}
     x #∈ xs  ys
     x #∈ xs  x #∈ ys
  ∈-∪⁻ []               x∈ = inj₂ x∈
  ∈-∪⁻ (cons y xs x#xs) {ys} x∈ with fresh? y ys
  ... | inj₂ ¬pys with ∈-∪⁻ xs x∈
  ... | inj₁ x∈xs = inj₁ (there x∈xs)
  ... | inj₂ x∈ys = inj₂ x∈ys
  ∈-∪⁻ (cons y xs x#xs) (here eq)  | inj₁ pys = inj₁ (here eq)
  ∈-∪⁻ (cons y xs x#xs) (there x∈) | inj₁ pys with ∈-∪⁻ xs x∈
  ... | inj₁ x∈xs = inj₁ (there x∈xs)
  ... | inj₂ x∈ys = inj₂ x∈ys

  ∪⁺ʳ
    : (xs {ys} : List# A)
     ys #⊆ xs  ys
  ∪⁺ʳ []                   x∈ = x∈
  ∪⁺ʳ (cons x xs pxs) {ys} x∈ with fresh? x ys
  ... | inj₁ x#ys = there (∪⁺ʳ xs x∈)
  ... | inj₂ _    = ∪⁺ʳ xs x∈

  ∪⁺ˡ
    : {xs ys : List# A}
     xs #⊆ xs  ys
  ∪⁺ˡ {y ∷# xs} {ys} x∈ with fresh? y ys
  ∪⁺ˡ {y ∷# xs} (here refl) | inj₂ y∈ys =
    ∪⁺ʳ xs y∈ys
  ∪⁺ˡ (there x∈) | inj₂ _  = ∪⁺ˡ x∈
  ∪⁺ˡ (here eq)  | inj₁ _ = here eq
  ∪⁺ˡ (there x∈) | inj₁ _ = there (∪⁺ˡ x∈)

  ∪-⊆⁺ : xs #⊆ zs  ys #⊆ zs  xs  ys #⊆ zs
  ∪-⊆⁺  {xs} xs⊆ ys⊆ x∈ with ∈-∪⁻ xs x∈
  ... | inj₁ ∈xs = xs⊆ ∈xs
  ... | inj₂ ∈ys = ys⊆ ∈ys

  ∪-⊆⁻ˡ : xs  ys #⊆ zs  xs #⊆ zs
  ∪-⊆⁻ˡ  xs∪ys⊆zs x∈xs = xs∪ys⊆zs (∪⁺ˡ x∈xs)

  ∪-⊆⁻ʳ :  xs  xs  ys #⊆ zs  ys #⊆ zs
  ∪-⊆⁻ʳ  xs xs∪ys⊆zs x∈xs = xs∪ys⊆zs (∪⁺ʳ xs x∈xs)

  ∪-monotoneʳ :  xs  ys #⊆ zs  xs  ys #⊆ xs  zs
  ∪-monotoneʳ xs ys⊆zs x∈ with ∈-∪⁻ xs x∈
  ... | inj₁ x∈xs = ∪⁺ˡ x∈xs
  ... | inj₂ x∈ys = ∪⁺ʳ xs (ys⊆zs x∈ys)
open Decidable public