{-# OPTIONS --safe #-}
module Prelude.Lists where

open import Prelude.Init hiding (mapMaybe); open L using (mapMaybe)
open import Prelude.FromNat
open import Prelude.Decidable
open import Prelude.DecEq
open import Prelude.InferenceRules
open import Prelude.Anyable
open import Prelude.Allable
open L.Any using (satisfied)

private variable
  A B : Type
  xs ys : List A
  x x′ y : A
  f : A  B

-- * Nats

open Nat hiding (_≟_)

module _ m {n o} (open ≤-Reasoning) where
  +-∸-assoc≤ : (m + n)  o  m + (n  o)
  +-∸-assoc≤
    with o ≤? n
  ... | yes o≤n = ≤-reflexive $ +-∸-assoc _ o≤n
  ... | no o≰n
    with o>n<⇒≤ (≰⇒> o≰n)
    with m≡m+n∸obegin-equality
      m           ≡˘⟨ +-identityʳ _ 
      m + 0       ≡˘⟨ cong (m +_) $ m≤n⇒m∸n≡0 o>n 
      m + (n  o) 
    with o ≥? m + n
  ... | yes o≥m+n =
    begin
      (m + n)  o
    ≡⟨ m≤n⇒m∸n≡0 o≥m+n 
      0
    ≤⟨ z≤n 
      m
    ≡⟨ m≡m+n∸o 
      m + (n  o)
    
  ... | no o≱m+n =
    let o<m+n = <⇒≤ (≰⇒> o≱m+n) in
    begin
      (m + n)  o
    ≤⟨ +-cancelʳ-≤ o _ m
    $ begin (m + n)  o + o ≡⟨ m∸n+n≡m o<m+n 
            m + n           ≤⟨ +-monoʳ-≤ m o>n 
            m + o           
    
      m
    ≡⟨ m≡m+n∸o 
      m + (n  o)
    

-- * Empty list

Null : Pred (List A) _
Null = _≡ []

Null-map⁻ : Null (map f xs)  Null xs
Null-map⁻ {xs = []} _ = refl

-- * Any/All

private variable P Q : Pred A 

∈-lookup :  {xs : List A} (i : Any P xs)  L.Any.lookup i  xs
∈-lookup = λ where
  (here px)  here refl
  (there i)  there (∈-lookup i)

anyAll :  {xs : List A}  All Q xs  (i : Any P xs)  Q (L.Any.lookup i)
anyAll q = L.All.lookup q  ∈-lookup

satisfied′ :  {A : Type} {P : A  Type} {xs : List A} 
  Any P xs
  ───────────────────
  ∃[ x ] x  xs × P x
satisfied′ = λ where
  (here px)    -, here refl , px
  (there pxs)  let x , x∈ , px = satisfied′ pxs
                in  x , there x∈ , px

∃⇔Any :  {xs : List A} 
  ( λ x  P x × x  xs)
  ══════════════════════
  Any P xs

module _ {P : A  Type} {xs : List A} where
  ∃⇒Any = ∃⇔Any {P = P}{xs = xs} .proj₁
  Any⇒∃ = ∃⇔Any {P = P}{xs = xs} .proj₂

∃⇔Any =  (_ , px , x∈)  L.Any.map  where refl  px) x∈)
      , λ where (here px)    -, px , here refl
                (there pxs)  map₂ (map₂ there) (Any⇒∃ pxs)

-- * indexing

Index : List A  Type
Index = Fin  length

mapWithIndex :  {B : Type} (xs : List A)  (Index xs  B)  List B
mapWithIndex [] _ = []
mapWithIndex (_  xs) f = f 0  mapWithIndex xs (f  fsuc)

filterByIndex : (xs : List A)  (Index xs  Bool)  List A
filterByIndex [] _ = []
filterByIndex (x  xs) f =
  let xs′ = filterByIndex xs (f  fsuc) in
  if f 0 then x  xs′ else xs′

∈-filterByIndex⁻ :  (xs : List A) (f : Index xs  Bool)  filterByIndex xs f ⊆ˢ xs
∈-filterByIndex⁻ (x  xs) f x∈
  with f 0
... | false = there $ ∈-filterByIndex⁻ xs (f  fsuc) x∈
... | true
  with x∈
... | here refl = here refl
... | there x∈  = there $ ∈-filterByIndex⁻ xs (f  fsuc) x∈

subsetIndices : xs ⊆ˢ ys  List (Index ys)
subsetIndices {xs = []} {ys} p = []
subsetIndices {xs = x  xs} {ys} p =
  L.Any.index (p $ here refl)  subsetIndices {xs = xs} (p  there)

removeAt∗ : (xs : List A)  List (Index xs)  List A
removeAt∗ xs is = filterByIndex xs (⌊_⌋  (_∉? is))

∈-removeAt∗⁻ :  {xs : List A} {is : List (Index xs)}  removeAt∗ xs is ⊆ˢ xs
∈-removeAt∗⁻ = ∈-filterByIndex⁻ _ _

remove⊆ˢ : {xs ys : List A}  xs ⊆ˢ ys  List A
remove⊆ˢ {ys = ys} p = removeAt∗ ys $ subsetIndices p

∈-remove⊆ˢ⁻ :  {xs ys : List A} (p : xs ⊆ˢ ys)  remove⊆ˢ p ⊆ˢ ys
∈-remove⊆ˢ⁻ _ = ∈-removeAt∗⁻

lookup≡proj₁∘satisfied :  {xs : List A} (p : Any P xs) 
  L.Any.lookup p  satisfied p .proj₁
lookup≡proj₁∘satisfied (here _)  = refl
lookup≡proj₁∘satisfied (there p) = lookup≡proj₁∘satisfied p

∈-removeAt⁻ :  (n : Index xs) {x}  x  L.removeAt xs n  x  xs
∈-removeAt⁻ {xs = _  _} fzero p = there p
∈-removeAt⁻ {xs = _  _} (fsuc n) = λ where
  (here refl)  here refl
  (there p)    there $′ ∈-removeAt⁻ n p

∈-∷ʳ⁻ :  {x y}  x  xs L.∷ʳ y  (x  xs)  (x  y)
∈-∷ʳ⁻ {xs = xs}{x}{y} x∈ = case L.Mem.∈-++⁻ xs x∈ of λ where
  (inj₁ x∈)         inj₁ x∈
  (inj₂ (here x≡))  inj₂ x≡

-- * constructing indexed functions

 : Index {A = A} []  B
 ()

[_∶_⋯_∶_] : (x : A)  B   xs  (Index xs  B)  (Index (x  xs)  B)
[ _  b  _  f ] = λ where
  fzero     b
  (fsuc i)  f i

-- * removing suffix

_─⋯_ : (xs : List A)  Index xs  List A
xs ─⋯ i = L.drop (suc $ Fi.toℕ i) xs

private
  open import Prelude.LiteralSequences
  ns = List   [ 0  1  2  3 ]
  _ = ns ─⋯ 0  [ 1  2  3 ]
     refl
  _ = ns ─⋯ 1  [ 2  3 ]
     refl
  _ = ns ─⋯ 2  [ 3 ]
     refl
  _ = ns ─⋯ 3  []
     refl

─⋯-⊆ : (i : Index xs)  (xs ─⋯ i) ⊆ˢ xs
─⋯-⊆ {xs = _  _} fzero    = there
─⋯-⊆ {xs = _  _} (fsuc i) = there ∘′ ─⋯-⊆ _

─⋯-∷ : (i : Index xs)   λ ys  xs  ys ++ L.lookup xs i  (xs ─⋯ i)
─⋯-∷ {xs = _  _}  fzero    = [] , refl
─⋯-∷ {xs = _  xs} (fsuc i) = let ys , xs≡ = ─⋯-∷ i in  _  ys , cong (_ ∷_) xs≡

─⋯-len : (i : Index xs)  length (xs ─⋯ i) < length xs
─⋯-len {xs = _  _}  fzero    = ≤-refl
─⋯-len {xs = _  xs} (fsuc i) = s≤s $ <⇒≤ $ ─⋯-len {xs = xs} i

-- * filter

module _ {P : A  Set} (P? : Decidable¹ P) where

  filter-length : length xs  length (filter P? xs) + length (filter (¬?  P?) xs)
  filter-length {[]} = refl
  filter-length {x  xs} with P? x
  ... | no ¬p
   rewrite +-suc (length (filter P? xs)) (length (filter (¬?  P?) xs))
   rewrite filter-length {xs = xs} = refl
  ... | yes p rewrite filter-length {xs = xs} = refl

  module _ {Q : A  Set} (Q? : Decidable¹ Q) (P⇔Q : P  Q) where
    filter≡ :  xs  filter P? xs  filter Q? xs
    filter≡ [] = refl
    filter≡ (x  xs)
      with IHfilter≡ xs
      with P? x
    ... | yes px rewrite dec-yes (Q? x) (P⇔Q .proj₁ px) .proj₂ = cong (x ∷_) IH
    ... | no ¬px rewrite dec-no  (Q? x) (¬px  P⇔Q .proj₂)     = IH

module _ {P : Pred A }  _ : P ⁇¹  (f : B  A) where
  filter-map :  xs  filter ¿ P ¿¹ (map f xs)  map f (filter (¿ P ¿¹  f) xs)
  filter-map [] = refl
  filter-map (x  xs)
    with IHfilter-map xs
    with ¿ P (f x) ¿
  ... | yes _ = cong (_ ∷_) IH
  ... | no  _ = IH

-- * membership

open L.Mem using (mapWith∈)
open L.All using (lookup; tabulate; ¬Any⇒All¬)
open L.Any using (index; _─_)

∈-─⁻ : (x∈′ : x′  xs)  x  (xs  x∈′)  x  xs
∈-─⁻ = λ where
  (here _)   x∈′          there x∈′
  (there x∈) (here refl)  here refl
  (there x∈) (there x∈′)  there $ ∈-─⁻ x∈ x∈′

∈-─⁺ : x  y  (x∈ : x  xs)  y  xs  y  (xs  x∈)
∈-─⁺ {xs = x  xs} x≢ = λ where
  (here refl) (here refl)  ⊥-elim $ x≢ refl
  (there x∈)  (here refl)  here refl
  (here refl) (there y∈)   y∈
  (there x∈)  (there y∈)   there $ ∈-─⁺ x≢ x∈ y∈

All-─⁻ :  {A : Type} {P : A  Type} {x : A} xs 
   (x∈ : x  xs) 
   P x
   All P (xs  x∈)
    ─────────────────────
    All P xs
All-─⁻ (_  _)    (here refl) x∈ xs∈       = x∈  xs∈
All-─⁻ (_   xs) (there x∈xs) x∈ (p  xs∈) = p   All-─⁻ xs x∈xs x∈ xs∈

All∈-dropHead :  {A : Type} {x : A} {xs ys} 
   x  ys
   All (_∈ (x  xs)) ys
    ────────────────────
    All (_∈ xs) ys
All∈-dropHead x∉ p = L.All.tabulate λ y∈ 
  case L.All.lookup p y∈ of λ where
    (here refl)  ⊥-elim $ x∉ y∈
    (there p)  p

length-mapWith∈ : (f :  {x}  x  xs  B) 
  length (mapWith∈ xs f)  length xs
length-mapWith∈ {xs = []} _ = refl
length-mapWith∈ {xs = _  xs} f = cong suc $ length-mapWith∈ {xs = xs} (f  there)

mapWith∈-∀ :  {xs : List A}  {f :  {x : A}  x  xs  B} {P : B  Type}
   (∀ {x} x∈  P (f {x} x∈))
   (∀ {y}  y  mapWith∈ xs f  P y)
mapWith∈-∀ {xs = x  xs} ∀P {y} (here px)  rewrite px = ∀P (here refl)
mapWith∈-∀ {xs = x  xs} ∀P {y} (there y∈) = mapWith∈-∀ (∀P  there) y∈

Unique-mapWith∈ :  {A B : Type} {xs : List A} {f :  {x}  x  xs  B}
   (∀ {x x′} {x∈ : x  xs} {x∈′ : x′  xs}
         f x∈  f x∈′
         index x∈  index x∈′)
   Unique (mapWith∈ xs f)
Unique-mapWith∈ {xs = []}     {f = f} f≡ = []
Unique-mapWith∈ {xs = x  xs} {f = f} f≡
  = L.All.tabulate (mapWith∈-∀ {P = f (here refl) ≢_} λ _ eq  case f≡ eq of λ () )
   Unique-mapWith∈ {xs = xs} (Fi.suc-injective  f≡)

filterWith∈ : (xs : List A) {P :  {x}  x  xs  Set}
              (P? :  {x} (x∈ : x  xs)  Dec (P x∈))  List A
-- filterWith∈ xs P?
--   = map proj₁
--   $ filter (λ (x , x∈) → P? x∈)
--   $ mapWith∈ xs λ {x} x∈ → x , x∈
filterWith∈ [] P? = []
filterWith∈ (x  xs) P?
  with IHfilterWith∈ xs (P?  there)
  with P? (here refl)
... | yes px = x  IH
... | no ¬px = IH

filterWith∈≡ :
  (xs : List A)
  {P Q :  {x}  x  xs  Set}
  (P? :  {x} (x∈ : x  xs)  Dec (P x∈))
  (Q? :  {x} (x∈ : x  xs)  Dec (Q x∈))
  (P⇔Q :  {x}{x∈ : x  xs}  P x∈  Q x∈)
   filterWith∈ xs P?  filterWith∈ xs Q?
filterWith∈≡ [] P? Q? P⇔Q = refl
filterWith∈≡ (x  xs) P? Q? P⇔Q
  with IHfilterWith∈≡ xs (P?  there) (Q?  there) P⇔Q
  with P? (here refl)
... | yes px rewrite dec-yes (Q? (here refl)) (P⇔Q .proj₁ px) .proj₂
  = cong (x ∷_) IH
... | no ¬px rewrite dec-no (Q? (here refl)) (¬px  P⇔Q .proj₂)
  = IH

filter∘mapWith∈ :
   (xs : List A)
   (f :  {x}  x  xs  B)
   {P : B  Set}
   (P? : Decidable¹ P)
  -- → filter P? (mapWith∈ xs f) ≡ map f (filterWith∈ xs (P? ∘ f))
   length (filter P? (mapWith∈ xs f))  length (filterWith∈ xs (P?  f))
filter∘mapWith∈ [] f {P} P? = refl
filter∘mapWith∈ (x  xs) f {P} P?
  with IHfilter∘mapWith∈ xs (f  there) P?
  with P? (f $ here refl)
... | yes px = cong suc IH
... | no ¬px = IH

filterWith∈≗filter :  {P : A  Set} (P? : Decidable¹ P) xs
                    filterWith∈ xs  {x} _  P? x)  filter P? xs
filterWith∈≗filter P? []       = refl
filterWith∈≗filter P? (x  xs)
  with IHfilterWith∈≗filter P? xs
  with P? x
... | yes px = cong (x ∷_) IH
... | no ¬px = IH

index∘subst :  {x x′}{xs : List A}{x∈ : x  xs} (eq : x  x′)
   index (subst (_∈ _) eq x∈)  index x∈
index∘subst refl = refl

module _ (f : A  Maybe B) where
  mapMaybe-++ :  xs ys  mapMaybe f (xs ++ ys)  mapMaybe f xs ++ mapMaybe f ys
  mapMaybe-++ []       ys = refl
  mapMaybe-++ (x  xs) ys with f x
  ... | just _  = cong (_ ∷_) (mapMaybe-++ xs ys)
  ... | nothing = mapMaybe-++ xs ys

  open L.Mem

  ∈-mapMaybe-++⁻ :  xs {ys} {x : B}
     x  mapMaybe f (xs ++ ys)
     x  mapMaybe f xs
     x  mapMaybe f ys
  ∈-mapMaybe-++⁻ xs {ys} rewrite mapMaybe-++ xs ys = ∈-++⁻ _

  ∈-mapMaybe-++⁺ˡ :  {xs ys} {x : B}
     x  mapMaybe f xs
     x  mapMaybe f (xs ++ ys)
  ∈-mapMaybe-++⁺ˡ {xs}{ys} rewrite mapMaybe-++ xs ys = ∈-++⁺ˡ

  ∈-mapMaybe-++⁺ʳ :  xs {ys} {y : B}
     y  mapMaybe f ys
     y  mapMaybe f (xs ++ ys)
  ∈-mapMaybe-++⁺ʳ xs {ys} rewrite mapMaybe-++ xs ys = ∈-++⁺ʳ _

  --
  ∈-mapMaybe⁺ : x  xs  f x  just y  y  mapMaybe f xs
  ∈-mapMaybe⁺ {x = x} {xs = xs} {y = y} x∈ eq
    = L.Any.mapMaybe⁺ f xs
    $ L.Any.map  where refl  subst (Any _) (sym eq) (just refl))
                (∈-map⁺ f x∈)

  ∈-mapMaybe⁻ : y  mapMaybe f xs
                λ x  (x  xs) × (f x  just y)
  ∈-mapMaybe⁻ {y = y} {xs = x  xs} y∈
    with f x in fx≡
  ... | nothing = map₂ (map₁ there) (∈-mapMaybe⁻ y∈)
  ... | just y′
    with y∈
  ... | here refl = x , here refl , fx≡
  ... | there y∈′ = map₂ (map₁ there) (∈-mapMaybe⁻ y∈′)

  mapMaybe-⊆ : xs ⊆ˢ ys  mapMaybe f xs ⊆ˢ mapMaybe f ys
  mapMaybe-⊆ {xs = x  xs} {ys = ys} xs⊆ fx∈ =
    let x , x∈ , fx≡ = ∈-mapMaybe⁻ fx∈
    in  ∈-mapMaybe⁺ (xs⊆ x∈) fx≡

-- * subsets/sublists

⊆⇒⊆ˢ : xs  ys  xs ⊆ˢ ys
⊆⇒⊆ˢ (_    ∷ʳ p) = there  ⊆⇒⊆ˢ p
⊆⇒⊆ˢ (refl  p)  = λ where
  (here refl)  here refl
  (there x∈)   there (⊆⇒⊆ˢ p x∈)

⊆ˢ-tail :  {z}  z  xs  z  xs ⊆ˢ z  ys  xs ⊆ˢ ys
⊆ˢ-tail z∉ sub x∈ =
  L.Any.tail (lookup (¬Any⇒All¬ _ z∉) x∈  sym) (sub $ there x∈)

⊆ˢ-length : Unique xs  xs ⊆ˢ ys  length xs  length ys
⊆ˢ-length {xs = []} {ys} uniq xs⊆ = z≤n
⊆ˢ-length {xs = x  xs} {ys} (x∉xs  uniq) xs⊆ =
  begin-strict length xs  ≤⟨ ⊆ˢ-length uniq xs⊆ys′ 
               length ys′ <⟨ ≤-reflexive $ sym $ L.length-removeAt′ ys _ 
               length ys  
  where
  open Nat.≤-Reasoning

  x∈ys : x  ys
  x∈ys = xs⊆ (here refl)

  ys′ = ys  x∈ys

  xs⊆ys′ : xs ⊆ˢ ys′
  xs⊆ys′ y∈ = ∈-─⁺ (L.All.lookup x∉xs y∈) x∈ys (xs⊆ $ there y∈)

-- * Fin lists

Fins = List  Fin

module _ {n : } {x x′ : Fin (suc n)} (x≢ : x′  x) where
  ∈-punchOutʳ :  {ys : Fins (suc n)}
     (x∉ : x′  ys)
     x  ys
     Fi.punchOut {n}{x′}{x} x≢
         mapWith∈ ys  {y} y∈  Fi.punchOut {n}{x′}{y} λ where refl  x∉ y∈)
  ∈-punchOutʳ x∉ = λ where
    (here refl)  here refl
    (there p)    there $ ∈-punchOutʳ (x∉  there) p

  ∈-punchOutˡ :  {ys : Fins (suc n)}
     (x∉ : x′  ys)
     Fi.punchOut {n}{x′}{x} x≢
         mapWith∈ ys  {y} y∈  Fi.punchOut {n}{x′}{y} λ where refl  x∉ y∈)
     x  ys
  ∈-punchOutˡ {ys = _  _} x∉ = λ where
    (here px)  here  $ Fi.punchOut-injective x≢  where refl  x∉ $ here refl) px
    (there p)  there $ ∈-punchOutˡ (x∉  there) p

  ∈-punchOut :  {ys : Fins (suc n)}
     (x∉ : x′  ys)
     (Fi.punchOut {n}{x′}{x} x≢
         mapWith∈ ys  {y} y∈  Fi.punchOut {n}{x′}{y} λ where refl  x∉ y∈))
     (x  ys)
  ∈-punchOut x∉ = ∈-punchOutˡ x∉ , ∈-punchOutʳ x∉

punchOut≡⇒index≡ :  {n}{xs : Fins (suc n)}{x x′ x₀}{x∈ : x  xs}{x∈′ : x′  xs}
   Unique xs
   (x≢ : L.All.All (x₀ ≢_) xs)
   Fi.punchOut (lookup x≢ x∈)  Fi.punchOut (lookup x≢ x∈′)
   index x∈  index x∈′
punchOut≡⇒index≡ {n}{xs}{x}{x′}{x₀}{x∈}{x∈′} uxs x≢ eq = let open ≡-Reasoning in
  begin
    index x∈
  ≡⟨ cong index $ unique⇒irrelevant uxs x∈ _ 
    index (subst (_∈ _) x≡ x∈′)
  ≡⟨ index∘subst x≡ 
    index x∈′
  
  where
  open import Data.List.Membership.Propositional.Properties.WithK
    using (unique⇒irrelevant)

  x≡ : x′  x
  x≡ = sym $ Fi.punchOut-injective (lookup x≢ x∈) (lookup x≢ x∈′) eq

-- * prefix (up to propositional equality)

Prefix≡ : List A  List A  Type _
Prefix≡ = Prefix _≡_

≡⇒Prefix : xs  ys  Prefix≡ xs ys
≡⇒Prefix {xs = []}    {ys = []}    refl = []
≡⇒Prefix {xs = _  _} {ys = _  _} refl = refl  ≡⇒Prefix refl

instance
  Dec-Prefix≡ :  DecEq A   Prefix≡ {A = A} ⁇²
  Dec-Prefix≡ =  L.Pre.prefix? _≟_ _ _

Prefix⇒⊆ : Prefix≡ xs ys  xs ⊆ˢ ys
Prefix⇒⊆ (refl  pre) = λ where
  (here refl)  here refl
  (there p)    there (Prefix⇒⊆ pre p)

Prefix⇒All :  {P : Pred A }  Prefix≡ xs ys  L.All.All P ys  L.All.All P xs
Prefix⇒All = L.SubS.All-resp-⊇  Prefix⇒⊆

Prefix-take :  {n}  Prefix≡ (L.take n xs) xs
Prefix-take {xs = xs} {n = n} with n
... | zero  = []
... | suc _ with xs
... | []    = []
... | _  _ = refl  Prefix-take

-- * suffix (up to propositional equality)

Suffix≡ : List A  List A  Type _
Suffix≡ = Suffix _≡_

≡⇒Suffix : xs  ys  Suffix≡ xs ys
≡⇒Suffix = here  L.PW.≡⇒Pointwise-≡

instance
  Dec-Suffix≡ :  DecEq A   Suffix≡ {A = A} ⁇²
  Dec-Suffix≡ =  L.Suf.suffix? _≟_ _ _

Suffix⇒⊆ : Suffix≡ xs ys  xs ⊆ˢ ys
Suffix⇒⊆ (here eq) rewrite L.PW.Pointwise-≡⇒≡ eq = id
Suffix⇒⊆ (there p) = there  Suffix⇒⊆ p

Suffix⇒All :  {P : Pred A }  Suffix≡ xs ys  L.All.All P ys  L.All.All P xs
Suffix⇒All = L.SubS.All-resp-⊇  Suffix⇒⊆

Suffix-drop :  n  Suffix≡ (L.drop n xs) xs
Suffix-drop zero = ≡⇒Suffix refl
Suffix-drop {xs = xs} (suc n) with xs
... | []    = here []
... | _  _ = there $′ Suffix-drop n

-- ** non-empty lists

nonEmpty∈ : length xs > 0   (_∈ xs)
nonEmpty∈ {xs = _  _} _ = -, here refl

-- * intersection

module _  _ : DecEq A  where
  infixr 7 _∩_
  _∩_ : Op₂ (List A)
  xs  ys = filter (_∈? ys) xs

  ∈-∩⁻ :  x xs ys  x  (xs  ys)  (x  xs) × (x  ys)
  ∈-∩⁻ _ _ _ = L.Mem.∈-filter⁻ (_∈? _)

  Unique-∩ : Unique xs  Unique (xs  ys)
  Unique-∩ = L.Unique.filter⁺ (_∈? _)

  ∩-identityˡ : Null ([]  xs)
  ∩-identityˡ = refl

  ∩-identityʳ : Null (xs  [])
  ∩-identityʳ {xs} = L.filter-none _ {xs} $ L.All.tabulate λ where _ ()

length-∩ˡ :  {n}{xs ys : Fins n}
   length (xs  ys)  length xs
length-∩ˡ {xs = []} {ys} = z≤n
length-∩ˡ {xs = x  xs} {ys}
  with x ∈? ys
... | no _ = let open ≤-Reasoning in
  begin length (xs  ys) ≤⟨ length-∩ˡ {xs = xs} 
        length xs        ≤⟨ n≤1+n _ 
        1 + length xs    
... | yes _ = let open ≤-Reasoning in
  begin 1 + length (xs  ys) ≤⟨ +-mono-≤ ≤-refl $ length-∩ˡ {xs = xs} 
        1 + length xs        

length-unique-fin :  {n}{xs : Fins n}
   Unique xs
   length xs  n
length-unique-fin {xs = []} _ = z≤n
length-unique-fin {suc n} {x  xs} (x≢  uxs)
  = s≤s $ let open ≤-Reasoning in
  begin
    length xs
  ≡˘⟨ length-mapWith∈ (Fi.punchOut  lookup x≢) 
    length xs′
  ≤⟨ length-unique-fin {xs = xs′} uxs′ 
    n
  
  where
  xs′ : Fins n
  xs′ = mapWith∈ xs (Fi.punchOut  lookup x≢)

  uxs′ : Unique xs′
  uxs′ = Unique-mapWith∈ $ punchOut≡⇒index≡ uxs x≢

length-∩ :  {n}{xs ys : Fins n}
   Unique xs
   Unique ys
   length (xs  ys)  length xs + length ys  n
length-∩ {n} {[]} {ys} _ uys = let open ≤-Reasoning in
  begin
    length ys  n
  ≡⟨ m≤n⇒m∸n≡0 (length-unique-fin uys) 
    0
  
length-∩ {suc n} {x  xs} {ys} (x≢  uxs) uys
  with IHlength-∩ {suc n}{xs} uxs uys
  with x ∈? ys
... | yes x∈ = let open ≤-Reasoning in
  begin
    length xs + length ys  n
  ≡⟨⟩
    (1 + (length xs + length ys))  suc n
  ≤⟨ +-∸-assoc≤ 1 {o = suc n} 
    1 + (length xs + length ys  suc n)
  ≤⟨ +-monoʳ-≤ 1 IH 
    1 + length (xs  ys)
  
... | no x∉ = let open ≤-Reasoning in
  begin
    length xs + length ys  n
  ≡˘⟨ cong     + _  n) len≡-xs 
    length xs′ + length ys  n
  ≡˘⟨ cong    length xs′ +   n) len≡-ys 
    length xs′ + length ys′  n
  ≤⟨ IH′ 
    length (xs′  ys′)
  ≡⟨ len≡ 
    length (xs  ys)
  
  where
  x↓ : {x : Fin (suc n)}  x  xs  Fin n
  x↓ = Fi.punchOut  lookup x≢

  xs′ : Fins n
  xs′ = mapWith∈ xs x↓

  uxs′ : Unique xs′
  uxs′ = Unique-mapWith∈ $ punchOut≡⇒index≡ uxs x≢

  len-xs′ : length xs′  n
  len-xs′ = length-unique-fin uxs′

  len≡-xs : length xs′  length xs
  len≡-xs = length-mapWith∈ x↓

  y↓ : {y : Fin (suc n)}  y  ys  Fin n
  y↓ {y} y∈ = Fi.punchOut {i = x} {j = y} λ where refl  x∉ y∈

  ys′ : Fins n
  ys′ = mapWith∈ ys y↓

  uys′ : Unique ys′
  uys′ = Unique-mapWith∈ $ punchOut≡⇒index≡ uys (¬Any⇒All¬ ys x∉)

  len-ys′ : length ys′  n
  len-ys′ = length-unique-fin uys′

  len≡-ys : length ys′  length ys
  len≡-ys = length-mapWith∈ y↓

  IH′ : length (xs′  ys′)  length xs′ + length ys′  n
  IH′ = length-∩ {n = n} uxs′ uys′

  len≡ : length (xs′  ys′)  length (xs  ys)
  len≡ = let open ≡-Reasoning in
    begin
      length (xs′  ys′)
    ≡⟨⟩
      length (filter (_∈? ys′) xs′)
    ≡⟨⟩
      length (filter (_∈? mapWith∈ ys y↓) $ mapWith∈ xs x↓)
    ≡⟨ filter∘mapWith∈ xs x↓ (_∈? mapWith∈ ys y↓) 
      length (filterWith∈ xs ((_∈? mapWith∈ ys y↓)  x↓))
    ≡⟨⟩
      length (filterWith∈ xs λ {x} x∈  x↓ x∈ ∈? mapWith∈ ys y↓)
    ≡⟨ cong length
     $ filterWith∈≡
         xs
          {x} x∈  x↓ x∈ ∈? mapWith∈ ys y↓)
          {x} _  x ∈? ys)
          {x} {x∈}  ∈-punchOut (lookup x≢ x∈) x∉)
     
      length (filterWith∈ xs λ {x} _  x ∈? ys)
    ≡⟨ (cong length $ filterWith∈≗filter (_∈? ys) xs) 
      length (filter (_∈? ys) xs)
    ≡⟨⟩
      length (xs  ys)
    

enumerate∈ :  {A : Type}  (xs : List A)  List $  (_∈ xs)
enumerate∈ xs = mapWith∈ xs λ {x} x∈  x , x∈

All-mapWith∈⁺ :  {P : B  Type} {f :  {x : A}  x  xs  B} 
  All  (x , x∈)  P (f {x} x∈)) (enumerate∈ xs) 
  All P (mapWith∈ xs f)
All-mapWith∈⁺ {xs = []} [] = []
All-mapWith∈⁺ {xs = _  xs} {P = P} {f = f} (p  ps)
  = p
   All-mapWith∈⁺ {xs = xs} (L.All.map⁻ ps′)
  where
  -- ps : All (λ (x , x∈) → P (f {x} x∈)) (mapWith∈ xs λ x → ? , there x)

  ps′ : L.All.All  (x , x∈)  P (f {x} x∈))
                  (map  (x , x∈)  x , there x∈) $ enumerate∈ xs)
  ps′ = subst
       L.All.All  (x , x∈)  P (f {x} x∈)) )
    (sym $ L.Mem.map-mapWith∈ xs  {x} x∈  x , x∈)  (x , x∈)  x , there x∈))
    ps

∈-mapWith∈⁻ :  {xs : List A} {f :  {x}  x  xs  B} {y : B}
   y  mapWith∈ xs f
    λ x  Σ (x  xs) λ x∈  y  f {x} x∈
∈-mapWith∈⁻ {xs = x  _}  (here refl) = x , here refl , refl
∈-mapWith∈⁻ {xs = x  xs} (there p)   = let x , x∈ , y≡ = ∈-mapWith∈⁻ p in x , there x∈ , y≡

Unique-mapWith∈⁺ :  {f :  {x : A}  x  xs  B} 
  (∀ {x y} (x∈ : x  xs) (y∈ : y  xs)  f x∈  f y∈  x  y) 
  Unique xs 
  Unique (mapWith∈ xs f)
Unique-mapWith∈⁺ {xs = []} f≡ uniq = []
Unique-mapWith∈⁺ {xs = x  xs} {f = f} f≡ (x∉  uniq)
  = All-mapWith∈⁺ QED
   Unique-mapWith∈⁺ {xs = xs}  x∈ y∈  f≡ (there x∈) (there y∈)) uniq
  where
  -- x∉ : All (x ≢_) xs

  QED : L.All.All  (y , y∈)  f {x} (here refl)  (f {y} $ there y∈)) (enumerate∈ xs)
  QED = tabulate λ x∈ f∘there≡  let _ , p , eq = ∈-mapWith∈⁻ x∈ in
    L.All.lookup x∉ (subst     .proj₁  xs) (sym eq) p) $
      f≡ (here refl) (there _) f∘there≡

-- ** UniqueBy

UniqueBy : (A  B)  List A  Type
UniqueBy f = Unique  map f

∉-─ :  {A B : Type} {x : A} {xs} (f : A  B) 
   (x∈ : x  xs)
   UniqueBy f xs
   x  xs L.Mem.─ x∈
∉-─ {xs = _  xs} _ = λ where
  (here refl) (x∉  _)   x∈           L.All.lookup x∉ (L.Mem.∈-map⁺ _ x∈) refl
  (there x∈)  (x∉  _)   (here refl)  L.All.lookup x∉ (L.Mem.∈-map⁺ _ x∈) refl
  (there x∈)  (_  uniq) (there p)    ∉-─ {xs = xs} _ x∈ uniq p

-- ** nub (ported from omelkonian/formal-prelude)

module _  _ : DecEq A  where

  nub : List A  List A
  nub [] = []
  nub (x  xs) with x ∈? xs
  ... | yes _ = nub xs
  ... | no  _ = x  nub xs

  nub-⊆⁺ : xs ⊆ˢ nub xs
  nub-⊆⁺ {xs = x  xs} (here refl)
    with x ∈? xs
  ... | yes x∈ = nub-⊆⁺ {xs = xs} x∈
  ... | no  _  = here refl
  nub-⊆⁺ {xs = x  xs} (there y∈)
    with x ∈? xs
  ... | yes _ = nub-⊆⁺ {xs = xs} y∈
  ... | no  _ = there $ nub-⊆⁺ {xs = xs} y∈

  nub-⊆⁻ : nub xs ⊆ˢ xs
  nub-⊆⁻ {xs = x  xs}
    with x ∈? xs
  ... | yes x∈ = there  nub-⊆⁻ {xs = xs}
  ... | no  _  = λ where
    (here refl)  here refl
    (there y∈)   there $ nub-⊆⁻ {xs = xs} y∈

  All-nub⁺ : All P xs  All P (nub xs)
  All-nub⁺ {xs = []}     []       = []
  All-nub⁺ {xs = x  xs} (p  ps) with x ∈? xs
  ... | yes _ = All-nub⁺ ps
  ... | no  _ = p  All-nub⁺ ps

  Unique-nub : Unique (nub xs)
  Unique-nub {[]} = []
  Unique-nub {x  xs} with x ∈? xs
  ... | yes _ = Unique-nub {xs}
  ... | no x∉ = All-nub⁺ (¬Any⇒All¬ xs x∉)  Unique-nub {xs}

  nub-filter : {P : A  Set} (P? : Decidable¹ P)  nub (filter P? xs)  filter P? (nub xs)
  nub-filter {xs = []} P? = refl
  nub-filter {xs = x  xs} P?
    with P? x | x ∈? xs
  ... | no _  | yes _ = nub-filter {xs = xs} P?
  ... | no ¬p | no _
    rewrite L.filter-reject P? {x}{nub xs} ¬p
    = nub-filter {xs = xs} P?
  ... | yes p | no x∉
    rewrite L.filter-accept P? {x}{nub xs} p
    with x ∈? filter P? xs
  ... | yes x∈ = ⊥-elim (x∉ (proj₁ $ L.Mem.∈-filter⁻ P? x∈ ))
  ... | no _ = cong (x ∷_) $ nub-filter {xs = xs} P?
  nub-filter {xs = x  xs} P? | yes p | yes x∈ with x ∈? filter P? xs
  ... | yes _ = nub-filter {xs = xs} P?
  ... | no x∉ = ⊥-elim $ x∉ (L.Mem.∈-filter⁺ P? x∈ p)

  nub-length : length (nub xs)  length (nub (x  xs))
  nub-length {xs}{x} with x ∈? xs
  ... | yes _ = ≤-refl
  ... | no _ = n≤1+n _

-- ** nubBy

  -- NB: right-biased, e.g. nubBy ∣_∣ [-1,0,1] = [0,1]
  nubBy : (B  A)  List B  List B
  nubBy f [] = []
  nubBy f (x  xs) with f x ∈? map f xs
  ... | yes _ = nubBy f xs
  ... | no  _ = x  nubBy f xs

  module _ (f : B  A) where
    nubBy-⊆⁻ : nubBy f xs ⊆ˢ xs
    nubBy-⊆⁻ {xs = y  xs} x∈ with f y ∈? map f xs
    ... | yes _ = there (nubBy-⊆⁻ {xs = xs} x∈)
    ... | no  _ with x∈
    ... | here refl = here refl
    ... | there x∈′ = there (nubBy-⊆⁻ {xs = xs} x∈′)

    nubBy-⊆⁺ : map f xs ⊆ˢ map f (nubBy f xs)
    nubBy-⊆⁺ {xs = y  xs} (here refl)
      with f y ∈? map f xs
    ... | yes fy∈ = nubBy-⊆⁺ {xs = xs} fy∈
    ... | no  _   = here refl
    nubBy-⊆⁺ {xs = y  xs} (there x∈)
      with f y ∈? map f xs
    ... | yes fy∈ = nubBy-⊆⁺ x∈
    ... | no  _   = there (nubBy-⊆⁺ x∈)

    UniqueBy-nubBy :  xs  UniqueBy f (nubBy f xs)
    UniqueBy-nubBy [] = []
    UniqueBy-nubBy (x  xs) with f x ∈? map f xs
    ... | yes _  = UniqueBy-nubBy xs
    ... | no fx∉ = ¬Any⇒All¬ (map f (nubBy f xs)) (fx∉  L.SubS.map⁺ _ nubBy-⊆⁻)
                  UniqueBy-nubBy xs

++-injectiveʳ :  {xs ys zs : List A} 
  xs ++ ys  xs ++ zs
  ────────────────────
  ys  zs
++-injectiveʳ {xs = []}    = id
++-injectiveʳ {xs = _  _} = ++-injectiveʳ  L.∷-injectiveʳ