{-# 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!; _◇++_; _++◇_)
    renaming (◇? to _∈?_)
    public

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

_∉_ : {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 _ {a} {A : Set a} (_≟_ : Discrete A) where
  isSet⟨A⟩ : isSet A
  isSet⟨A⟩ = Discrete→isSet _≟_

  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 (_≡ _)