{-# OPTIONS --cubical --safe #-}

module Data.List.Membership where

open import Data.List
open import Data.Fin
open import Data.Fin.Properties
open import Prelude
open import Relation.Nullary
open import Path.Reasoning
open import Data.List.Relation.Unary as Relation using (module Exists; module Congruence; ◇; ◇!)
import Data.Nat as ℕ
import Data.Nat.Properties as ℕ
open import Data.Maybe.Properties using (just-inj)

module _ {a} {A : Type a} {x : A} where
  open Exists (_≡ x)
    using (push; pull; push!; pull!; here!; _◇++_; _++◇_)
    public

infixr 5 _∈_ _∈!_ _∉_
_∈_ : A → List A → Type _
x ∈ xs = ◇ (_≡ x) xs

delete : (xs : List A) → Fin (length xs) → List A
delete (x ∷ xs) f0     = xs
delete (x ∷ xs) (fs i) = x ∷ delete xs i

∈-remove : ∀ {x : A} {xs} → (x∈xs : x ∈ xs) → List A
∈-remove {xs = xs} = delete xs ∘ fst

_∉_ : {A : Type a} → A → List A → Type a
x ∉ xs = ¬ (x ∈ xs)

_∈!_ : A → List A → Type _
x ∈! xs = ◇! (_≡ x) xs

cong-∈ : ∀ (f : A → B) {x : A} xs → x ∈ xs → f x ∈ map f xs
cong-∈ f {x} = Congruence.cong-◇ (_≡ x) (_≡ (f x)) (cong f)

fin∈tabulate :  ∀ {n}
                (f : Fin n → A)
                (i : Fin n) →
                f i ∈ tabulate n f
fin∈tabulate {n = suc _} f f0     = f0 , refl
fin∈tabulate {n = suc _} f (fs i)  =
  push (fin∈tabulate (f ∘ fs) i)

open import Function.Injective

at : ∀ {xs : List A} (n : Fin (length xs)) → (xs ! n) ∈ xs
at n = n , refl

module WithDecEq {a} {A : Type a} (_≟_ : Discrete A) where
  isSet⟨A⟩ : isSet A
  isSet⟨A⟩ = Discrete→isSet _≟_

  _∈?_ : ∀ (x : A) xs → Dec (x ∈ xs)
  _∈?_ x xs = Exists.◇? (_≡ x) (_≟ x) xs

  infixl 6 _\\_
  _\\_ : List A → A → List A
  xs \\ x = foldr f [] xs
    where
    f : A → List A → List A
    f y xs with x ≟ y
    ... | yes p = xs
    ... | no  p = y ∷ xs

  uniques : List A → List A
  uniques = foldr f []
    where
    f : A → List A → List A
    f x xs = x ∷ (xs \\ x)

  x∉xs\\x : ∀ x xs → x ∉ xs \\ x
  x∉xs\\x x (y ∷ xs) (n , x∈xs) with x ≟ y
  x∉xs\\x x (y ∷ xs) (n      , x∈xs)  | yes p = x∉xs\\x x xs (n , x∈xs)
  x∉xs\\x x (y ∷ xs) (f0   , y≡x)   | no ¬p = ¬p (sym y≡x)
  x∉xs\\x x (y ∷ xs) (fs n  , x∈xs)  | no ¬p = x∉xs\\x x xs (n , x∈xs)

  x∈!x∷xs\\x : ∀ x xs → x ∈! x ∷ xs \\ x
  x∈!x∷xs\\x x xs = here! (refl , isSet⟨A⟩ _ _ refl) (x∉xs\\x x xs)

  x∉xs⇒x∉xs\\y : ∀ (x : A) y xs → x ∉ xs → x ∉ xs \\ y
  x∉xs⇒x∉xs\\y x y (z ∷ xs) x∉xs x∈xs\\y with y ≟ z
  x∉xs⇒x∉xs\\y x y (z ∷ xs) x∉xs x∈xs\\y | yes p =
    x∉xs⇒x∉xs\\y x y xs (x∉xs ∘ push) x∈xs\\y
  x∉xs⇒x∉xs\\y x y (z ∷ xs) x∉xs (f0  , x∈xs\\y) | no ¬p =
    x∉xs (f0 , x∈xs\\y)
  x∉xs⇒x∉xs\\y x y (z ∷ xs) x∉xs (fs n , x∈xs\\y) | no ¬p =
    x∉xs⇒x∉xs\\y x y xs (x∉xs ∘ push) (n , x∈xs\\y)

  x∈!xs⇒x∈!xs\\y : ∀ (x : A) y xs → y ≢ x → x ∈! xs → x ∈! xs \\ y
  x∈!xs⇒x∈!xs\\y x y (z ∷ xs) y≢x x∈!xs with y ≟ z
  x∈!xs⇒x∈!xs\\y x y (z ∷ xs) y≢x x∈!xs | yes p =
    x∈!xs⇒x∈!xs\\y x y xs y≢x (pull! (y≢x ∘ p ;_) x∈!xs)
  x∈!xs⇒x∈!xs\\y x y (z ∷ xs) y≢x ((f0  , x∈xs) , uniq) | no ¬p =
    here! (x∈xs , isSet⟨A⟩ _ _ _) (x∉xs⇒x∉xs\\y x y xs (ℕ.znots ∘ cong FinToℕ ∘ cong fst ∘ uniq ∘ push))
  x∈!xs⇒x∈!xs\\y x y (z ∷ xs) y≢x ((fs n , x∈xs) , uniq) | no ¬p =
    push! z≢x (x∈!xs⇒x∈!xs\\y x y xs y≢x (pull! z≢x ((fs n , x∈xs) , uniq)))
    where z≢x = ℕ.snotz ∘ cong FinToℕ ∘ cong fst ∘ uniq ∘ (f0 ,_)

  ∈⇒∈! : (x : A) (xs : List A) → x ∈ xs → x ∈! uniques xs
  ∈⇒∈! x (y ∷ xs) (f0  , x∈xs) =
    subst (_∈! (y ∷ uniques xs \\ y)) x∈xs (x∈!x∷xs\\x y (uniques xs))
  ∈⇒∈! x (y ∷ xs) (fs n , x∈xs) with y ≟ x
  ... | yes p = subst (_∈! (y ∷ uniques xs \\ y)) p (x∈!x∷xs\\x y (uniques xs))
  ... | no ¬p = push! ¬p (x∈!xs⇒x∈!xs\\y x y (uniques xs) ¬p (∈⇒∈! x xs (n , x∈xs)))

infixr 5 _∈²_
_∈²_ : A → List (List A) → Type _
x ∈² xs = ◇ (x ∈_) xs

∈²→∈ : ∀ {x : A} xs → x ∈² xs → x ∈ concat xs
∈²→∈ = Relation.◇-concat (_≡ _)