doc-next-gen

Mathlib.Data.List.Dedup

Module docstring

{"# Erasure of duplicates in a list

This file proves basic results about List.dedup (definition in Data.List.Defs). dedup l returns l without its duplicates. It keeps the earliest (that is, rightmost) occurrence of each.

Tags

duplicate, multiplicity, nodup, nub "}

List.dedup_nil theorem
: dedup [] = ([] : List α)
Full source
@[simp]
theorem dedup_nil : dedup [] = ([] : List α) :=
  rfl
Deduplication of the empty list is empty
Informal description
The deduplication of the empty list is the empty list, i.e., $\mathrm{dedup}([]) = []$.
List.dedup_cons_of_mem' theorem
{a : α} {l : List α} (h : a ∈ dedup l) : dedup (a :: l) = dedup l
Full source
theorem dedup_cons_of_mem' {a : α} {l : List α} (h : a ∈ dedup l) : dedup (a :: l) = dedup l :=
  pwFilter_cons_of_neg <| by simpa only [forall_mem_ne, not_not] using h
Deduplication of a list when head element is already present in deduplicated tail
Informal description
For any element $a$ of type $\alpha$ and any list $l$ of elements of type $\alpha$, if $a$ appears in the deduplicated version of $l$, then the deduplicated version of the list $a :: l$ is equal to the deduplicated version of $l$.
List.dedup_cons_of_not_mem' theorem
{a : α} {l : List α} (h : a ∉ dedup l) : dedup (a :: l) = a :: dedup l
Full source
theorem dedup_cons_of_not_mem' {a : α} {l : List α} (h : a ∉ dedup l) :
    dedup (a :: l) = a :: dedup l :=
  pwFilter_cons_of_pos <| by simpa only [forall_mem_ne] using h
Deduplication preserves non-duplicate prepended elements
Informal description
For any element $a$ of type $\alpha$ and any list $l$ of elements of type $\alpha$, if $a$ does not appear in the deduplicated list $\mathrm{dedup}(l)$, then the deduplication of the list obtained by prepending $a$ to $l$ is equal to $a$ prepended to the deduplicated list $\mathrm{dedup}(l)$. In other words, if $a \notin \mathrm{dedup}(l)$, then $\mathrm{dedup}(a :: l) = a :: \mathrm{dedup}(l)$.
List.mem_dedup theorem
{a : α} {l : List α} : a ∈ dedup l ↔ a ∈ l
Full source
@[simp]
theorem mem_dedup {a : α} {l : List α} : a ∈ dedup la ∈ dedup l ↔ a ∈ l := by
  have := not_congr (@forall_mem_pwFilter α (· ≠ ·) _ ?_ a l)
  · simpa only [dedup, forall_mem_ne, not_not] using this
  · intros x y z xz
    exact not_and_or.1 <| mt (fun h ↦ h.1.trans h.2) xz
Membership in Deduplicated List Equals Membership in Original List
Informal description
For any element $a$ of type $\alpha$ and any list $l$ of elements of type $\alpha$, the element $a$ belongs to the deduplicated list $\mathrm{dedup}(l)$ if and only if $a$ belongs to the original list $l$. That is, $a \in \mathrm{dedup}(l) \leftrightarrow a \in l$.
List.dedup_cons_of_mem theorem
{a : α} {l : List α} (h : a ∈ l) : dedup (a :: l) = dedup l
Full source
@[simp]
theorem dedup_cons_of_mem {a : α} {l : List α} (h : a ∈ l) : dedup (a :: l) = dedup l :=
  dedup_cons_of_mem' <| mem_dedup.2 h
Deduplication of list with duplicate head element
Informal description
For any element $a$ of type $\alpha$ and any list $l$ of elements of type $\alpha$, if $a$ appears in $l$, then the deduplicated version of the list $a :: l$ is equal to the deduplicated version of $l$. That is, $\mathrm{dedup}(a :: l) = \mathrm{dedup}(l)$ when $a \in l$.
List.dedup_cons_of_not_mem theorem
{a : α} {l : List α} (h : a ∉ l) : dedup (a :: l) = a :: dedup l
Full source
@[simp]
theorem dedup_cons_of_not_mem {a : α} {l : List α} (h : a ∉ l) : dedup (a :: l) = a :: dedup l :=
  dedup_cons_of_not_mem' <| mt mem_dedup.1 h
Deduplication of list with non-duplicate head element
Informal description
For any element $a$ of type $\alpha$ and any list $l$ of elements of type $\alpha$, if $a$ does not appear in $l$, then the deduplication of the list obtained by prepending $a$ to $l$ is equal to $a$ prepended to the deduplicated list $\mathrm{dedup}(l)$. That is, if $a \notin l$, then $\mathrm{dedup}(a :: l) = a :: \mathrm{dedup}(l)$.
List.dedup_sublist theorem
: ∀ l : List α, dedup l <+ l
Full source
theorem dedup_sublist : ∀ l : List α, dedupdedup l <+ l :=
  pwFilter_sublist
Deduplicated List is a Sublist
Informal description
For any list $l$ with elements of type $\alpha$, the deduplicated list $\mathrm{dedup}(l)$ is a sublist of $l$. This means that $\mathrm{dedup}(l)$ can be obtained by removing some elements from $l$ while preserving the order of the remaining elements.
List.dedup_subset theorem
: ∀ l : List α, dedup l ⊆ l
Full source
theorem dedup_subset : ∀ l : List α, dedupdedup l ⊆ l :=
  pwFilter_subset
Deduplicated List is Subset of Original List
Informal description
For any list $l$ with elements of type $\alpha$, the deduplicated list $\operatorname{dedup}(l)$ is a subset of $l$, meaning every element in $\operatorname{dedup}(l)$ appears in $l$.
List.subset_dedup theorem
(l : List α) : l ⊆ dedup l
Full source
theorem subset_dedup (l : List α) : l ⊆ dedup l := fun _ => mem_dedup.2
Original List is Subset of Its Deduplicated Version
Informal description
For any list $l$ with elements of type $\alpha$, the original list $l$ is a subset of its deduplicated version $\mathrm{dedup}(l)$. In other words, every element of $l$ appears in $\mathrm{dedup}(l)$.
List.nodup_dedup theorem
: ∀ l : List α, Nodup (dedup l)
Full source
theorem nodup_dedup : ∀ l : List α, Nodup (dedup l) :=
  pairwise_pwFilter
Deduplication Yields a Duplicate-Free List
Informal description
For any list $l$ with elements of type $\alpha$, the deduplicated list $\mathrm{dedup}(l)$ has no duplicate elements (i.e., it satisfies the $\mathrm{Nodup}$ property).
List.headI_dedup theorem
[Inhabited α] (l : List α) : l.dedup.headI = if l.headI ∈ l.tail then l.tail.dedup.headI else l.headI
Full source
theorem headI_dedup [Inhabited α] (l : List α) :
    l.dedup.headI = if l.headI ∈ l.tail then l.tail.dedup.headI else l.headI :=
  match l with
  | [] => rfl
  | a :: l => by by_cases ha : a ∈ l <;> simp [ha, List.dedup_cons_of_mem]
Head of Deduplicated List Depends on Tail Membership
Informal description
For any list $l$ of elements of type $\alpha$ (where $\alpha$ has a default element), the head of the deduplicated list $\mathrm{dedup}(l)$ is equal to the head of the deduplicated tail $\mathrm{dedup}(l.\mathrm{tail})$ if the original head $l.\mathrm{head}$ appears in the tail $l.\mathrm{tail}$, and is equal to $l.\mathrm{head}$ otherwise. In other words: \[ \mathrm{head}(\mathrm{dedup}(l)) = \begin{cases} \mathrm{head}(\mathrm{dedup}(l.\mathrm{tail})) & \text{if } l.\mathrm{head} \in l.\mathrm{tail}, \\ l.\mathrm{head} & \text{otherwise.} \end{cases} \]
List.tail_dedup theorem
[Inhabited α] (l : List α) : l.dedup.tail = if l.headI ∈ l.tail then l.tail.dedup.tail else l.tail.dedup
Full source
theorem tail_dedup [Inhabited α] (l : List α) :
    l.dedup.tail = if l.headI ∈ l.tail then l.tail.dedup.tail else l.tail.dedup :=
  match l with
  | [] => rfl
  | a :: l => by by_cases ha : a ∈ l <;> simp [ha, List.dedup_cons_of_mem]
Tail of Deduplicated List Depends on Head-Tail Membership
Informal description
For any list $l$ of elements of type $\alpha$ (where $\alpha$ has a default element), the tail of the deduplicated list $\mathrm{dedup}(l)$ is equal to: - the tail of the deduplicated tail $\mathrm{dedup}(l.\mathrm{tail})$ if the head $l.\mathrm{head}$ appears in the tail $l.\mathrm{tail}$, - the deduplicated tail $\mathrm{dedup}(l.\mathrm{tail})$ otherwise. In other words: \[ \mathrm{tail}(\mathrm{dedup}(l)) = \begin{cases} \mathrm{tail}(\mathrm{dedup}(l.\mathrm{tail})) & \text{if } l.\mathrm{head} \in l.\mathrm{tail}, \\ \mathrm{dedup}(l.\mathrm{tail}) & \text{otherwise.} \end{cases} \]
List.dedup_eq_self theorem
{l : List α} : dedup l = l ↔ Nodup l
Full source
theorem dedup_eq_self {l : List α} : dedupdedup l = l ↔ Nodup l :=
  pwFilter_eq_self
Deduplication Preserves List Identity if and only if List Has No Duplicates
Informal description
For any list $l$ with elements of type $\alpha$, the deduplicated version of $l$ is equal to $l$ itself if and only if $l$ has no duplicate elements (i.e., $l$ is `Nodup`).
List.dedup_eq_cons theorem
(l : List α) (a : α) (l' : List α) : l.dedup = a :: l' ↔ a ∈ l ∧ a ∉ l' ∧ l.dedup.tail = l'
Full source
theorem dedup_eq_cons (l : List α) (a : α) (l' : List α) :
    l.dedup = a :: l' ↔ a ∈ l ∧ a ∉ l' ∧ l.dedup.tail = l' := by
  refine ⟨fun h => ?_, fun h => ?_⟩
  · refine ⟨mem_dedup.1 (h.symm ▸ mem_cons_self), fun ha => ?_, by rw [h, tail_cons]⟩
    have := count_pos_iff.2 ha
    have : count a l.dedup ≤ 1 := nodup_iff_count_le_one.1 (nodup_dedup l) a
    rw [h, count_cons_self] at this
    omega
  · have := @List.cons_head!_tail α ⟨a⟩ _ (ne_nil_of_mem (mem_dedup.2 h.1))
    have hal : a ∈ l.dedup := mem_dedup.2 h.1
    rw [← this, mem_cons, or_iff_not_imp_right] at hal
    exact this ▸ h.2.2.symmcons_eq_cons.2 ⟨(hal (h.2.2.symm ▸ h.2.1)).symm, rfl⟩
Characterization of Deduplicated List as Cons Construction
Informal description
For any list $l$ of elements of type $\alpha$, an element $a \in \alpha$, and another list $l'$, the deduplicated version of $l$ equals the cons of $a$ onto $l'$ if and only if the following three conditions hold: 1. $a$ is an element of $l$, 2. $a$ does not appear in $l'$, and 3. the tail of the deduplicated list equals $l'$. In other words, $\mathrm{dedup}(l) = a :: l' \leftrightarrow (a \in l) \land (a \notin l') \land (\mathrm{tail}(\mathrm{dedup}(l)) = l')$.
List.dedup_eq_nil theorem
(l : List α) : l.dedup = [] ↔ l = []
Full source
@[simp]
theorem dedup_eq_nil (l : List α) : l.dedup = [] ↔ l = [] := by
  induction l with
  | nil => exact Iff.rfl
  | cons a l hl =>
    by_cases h : a ∈ l
    · simp only [List.dedup_cons_of_mem h, hl, List.ne_nil_of_mem h, reduceCtorEq]
    · simp only [List.dedup_cons_of_not_mem h, List.cons_ne_nil]
Deduplicated List is Empty if and only if Original List is Empty
Informal description
For any list $l$ with elements of type $\alpha$, the deduplicated version of $l$ is the empty list if and only if $l$ itself is the empty list. That is, $\mathrm{dedup}(l) = [] \leftrightarrow l = []$.
List.Nodup.dedup theorem
{l : List α} (h : l.Nodup) : l.dedup = l
Full source
protected theorem Nodup.dedup {l : List α} (h : l.Nodup) : l.dedup = l :=
  List.dedup_eq_self.2 h
Deduplication Preserves Nodup Lists
Informal description
For any list $l$ with elements of type $\alpha$, if $l$ has no duplicate elements (i.e., $l$ is `Nodup`), then the deduplicated version of $l$ is equal to $l$ itself.
List.dedup_idem theorem
{l : List α} : dedup (dedup l) = dedup l
Full source
@[simp]
theorem dedup_idem {l : List α} : dedup (dedup l) = dedup l :=
  pwFilter_idem
Idempotence of List Deduplication
Informal description
For any list $l$ with elements of type $\alpha$, applying the deduplication function twice is equivalent to applying it once, i.e., $\mathrm{dedup}(\mathrm{dedup}(l)) = \mathrm{dedup}(l)$.
List.dedup_append theorem
(l₁ l₂ : List α) : dedup (l₁ ++ l₂) = l₁ ∪ dedup l₂
Full source
theorem dedup_append (l₁ l₂ : List α) : dedup (l₁ ++ l₂) = l₁ ∪ dedup l₂ := by
  induction l₁ with | nil => rfl | cons a l₁ IH => ?_
  simp only [cons_union] at *
  rw [← IH, cons_append]
  by_cases h : a ∈ dedup (l₁ ++ l₂)
  · rw [dedup_cons_of_mem' h, insert_of_mem h]
  · rw [dedup_cons_of_not_mem' h, insert_of_not_mem h]
Deduplication of Concatenated Lists Equals Union with Deduplicated Second List
Informal description
For any two lists $l₁$ and $l₂$ with elements of type $\alpha$, the deduplication of their concatenation $l₁ ++ l₂$ is equal to the union of $l₁$ with the deduplicated version of $l₂$, i.e., $\mathrm{dedup}(l₁ ++ l₂) = l₁ \cup \mathrm{dedup}(l₂)$.
List.dedup_map_of_injective theorem
[DecidableEq β] {f : α → β} (hf : Function.Injective f) (xs : List α) : (xs.map f).dedup = xs.dedup.map f
Full source
theorem dedup_map_of_injective [DecidableEq β] {f : α → β} (hf : Function.Injective f)
    (xs : List α) :
    (xs.map f).dedup = xs.dedup.map f := by
  induction xs with
  | nil => simp
  | cons x xs ih =>
    rw [map_cons]
    by_cases h : x ∈ xs
    · rw [dedup_cons_of_mem h, dedup_cons_of_mem (mem_map_of_mem h), ih]
    · rw [dedup_cons_of_not_mem h, dedup_cons_of_not_mem <| (mem_map_of_injective hf).not.mpr h, ih,
        map_cons]
Deduplication Commutes with Injective Mapping
Informal description
For any injective function $f : \alpha \to \beta$ and any list $xs$ of elements of type $\alpha$, the deduplication of the mapped list $f(xs)$ is equal to the mapped deduplicated list $f(\mathrm{dedup}(xs))$. That is, $\mathrm{dedup}(f(xs)) = f(\mathrm{dedup}(xs))$.
List.Subset.dedup_append_right theorem
{xs ys : List α} (h : xs ⊆ ys) : dedup (xs ++ ys) = dedup ys
Full source
/-- Note that the weaker `List.Subset.dedup_append_left` is proved later. -/
theorem Subset.dedup_append_right {xs ys : List α} (h : xs ⊆ ys) :
    dedup (xs ++ ys) = dedup ys := by
  rw [List.dedup_append, Subset.union_eq_right (List.Subset.trans h <| subset_dedup _)]
Deduplication of Concatenated Lists with Superset Equals Deduplication of Superset
Informal description
For any two lists $xs$ and $ys$ of elements of type $\alpha$, if $xs$ is a subset of $ys$ (i.e., every element of $xs$ appears in $ys$), then the deduplication of their concatenation $xs ++ ys$ is equal to the deduplication of $ys$, i.e., $\mathrm{dedup}(xs ++ ys) = \mathrm{dedup}(ys)$.
List.Disjoint.union_eq theorem
{xs ys : List α} (h : Disjoint xs ys) : xs ∪ ys = xs.dedup ++ ys
Full source
theorem Disjoint.union_eq {xs ys : List α} (h : Disjoint xs ys) :
    xs ∪ ys = xs.dedup ++ ys := by
  induction xs with
  | nil => simp
  | cons x xs ih =>
    rw [cons_union]
    rw [disjoint_cons_left] at h
    by_cases hx : x ∈ xs
    · rw [dedup_cons_of_mem hx, insert_of_mem (mem_union_left hx _), ih h.2]
    · rw [dedup_cons_of_not_mem hx, insert_of_not_mem, ih h.2, cons_append]
      rw [mem_union_iff, not_or]
      exact ⟨hx, h.1⟩
Union of Disjoint Lists Equals Deduplicated First List Concatenated with Second List
Informal description
For any two lists $xs$ and $ys$ of elements of type $\alpha$, if $xs$ and $ys$ are disjoint (i.e., they share no common elements), then the union of $xs$ and $ys$ is equal to the concatenation of the deduplicated version of $xs$ and the list $ys$. That is, $xs \cup ys = \mathrm{dedup}(xs) ++ ys$.
List.Disjoint.dedup_append theorem
{xs ys : List α} (h : Disjoint xs ys) : dedup (xs ++ ys) = dedup xs ++ dedup ys
Full source
theorem Disjoint.dedup_append {xs ys : List α} (h : Disjoint xs ys) :
    dedup (xs ++ ys) = dedup xs ++ dedup ys := by
  rw [List.dedup_append, Disjoint.union_eq]
  intro a hx hy
  exact h hx (mem_dedup.mp hy)
Deduplication Preserves Concatenation of Disjoint Lists
Informal description
For any two lists $xs$ and $ys$ of elements of type $\alpha$, if $xs$ and $ys$ are disjoint (i.e., they share no common elements), then the deduplication of their concatenation $xs ++ ys$ is equal to the concatenation of the deduplicated versions of $xs$ and $ys$, i.e., $\mathrm{dedup}(xs ++ ys) = \mathrm{dedup}(xs) ++ \mathrm{dedup}(ys)$.
List.replicate_dedup theorem
{x : α} : ∀ {k}, k ≠ 0 → (replicate k x).dedup = [x]
Full source
theorem replicate_dedup {x : α} : ∀ {k}, k ≠ 0 → (replicate k x).dedup = [x]
  | 0, h => (h rfl).elim
  | 1, _ => rfl
  | n + 2, _ => by
    rw [replicate_succ, dedup_cons_of_mem (mem_replicate.2 ⟨n.succ_ne_zero, rfl⟩),
      replicate_dedup n.succ_ne_zero]
Deduplication of Replicated List Yields Singleton
Informal description
For any element $x$ of type $\alpha$ and any natural number $k \neq 0$, the deduplicated version of the list consisting of $k$ copies of $x$ is the singleton list $[x]$. That is, $\mathrm{dedup}(\mathrm{replicate}\,k\,x) = [x]$ for $k \neq 0$.
List.count_dedup theorem
(l : List α) (a : α) : l.dedup.count a = if a ∈ l then 1 else 0
Full source
theorem count_dedup (l : List α) (a : α) : l.dedup.count a = if a ∈ l then 1 else 0 := by
  simp_rw [count_eq_of_nodup <| nodup_dedup l, mem_dedup]
Count of Elements in Deduplicated List
Informal description
For any list $l$ of elements of type $\alpha$ and any element $a \in \alpha$, the count of $a$ in the deduplicated list $\mathrm{dedup}(l)$ is $1$ if $a$ appears in $l$ and $0$ otherwise. In other words, $\mathrm{count}(a, \mathrm{dedup}(l)) = \begin{cases} 1 & \text{if } a \in l \\ 0 & \text{otherwise} \end{cases}$.
List.Perm.dedup theorem
{l₁ l₂ : List α} (p : l₁ ~ l₂) : dedup l₁ ~ dedup l₂
Full source
theorem Perm.dedup {l₁ l₂ : List α} (p : l₁ ~ l₂) : dedupdedup l₁ ~ dedup l₂ :=
  perm_iff_count.2 fun a =>
    if h : a ∈ l₁ then by
      simp [h, nodup_dedup, p.subset h]
    else by
      simp [h, count_eq_zero_of_not_mem, mt p.mem_iff.2]
Permutation Preservation under Deduplication
Informal description
For any two lists $l_1$ and $l_2$ of elements of type $\alpha$, if $l_1$ is a permutation of $l_2$, then the deduplicated version of $l_1$ is a permutation of the deduplicated version of $l_2$.