Skip to content

Commit 43502ce

Browse files
feat: port Data.Multiset.Nodup (#1539)
1 parent 852320c commit 43502ce

File tree

1 file changed

+260
-4
lines changed

1 file changed

+260
-4
lines changed

Mathlib/Data/Multiset/Nodup.lean

Lines changed: 260 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@
22
Copyright (c) 2015 Microsoft Corporation. All rights reserved.
33
Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Mario Carneiro
5+
6+
! This file was ported from Lean 3 source module data.multiset.nodup
7+
! leanprover-community/mathlib commit 9003f28797c0664a49e4179487267c494477d853
8+
! Please do not edit these lines, except to modify the commit id
9+
! if you have ported upstream changes.
510
-/
6-
import Mathlib.Data.Multiset.Basic
11+
import Mathlib.Data.List.Nodup
12+
import Mathlib.Data.Multiset.Bind
13+
import Mathlib.Data.Multiset.Range
14+
715
/-!
8-
# The `nodup` predicate for multisets without duplicate elements.
16+
# The `Nodup` predicate for multisets without duplicate elements.
917
-/
1018

1119

@@ -15,7 +23,255 @@ open Function List
1523

1624
variable {α β γ : Type _} {r : α → α → Prop} {s t : Multiset α} {a : α}
1725

18-
/-- `nodup s` means that `s` has no duplicates, i.e. the multiplicity of
26+
-- nodup
27+
/-- `Nodup s` means that `s` has no duplicates, i.e. the multiplicity of
1928
any element is at most 1. -/
2029
def Nodup (s : Multiset α) : Prop :=
21-
Quot.liftOn s List.Nodup fun _ _ p ↦ propext p.nodup_iff
30+
Quot.liftOn s List.Nodup fun _ _ p => propext p.nodup_iff
31+
#align multiset.nodup Multiset.Nodup
32+
33+
@[simp]
34+
theorem coe_nodup {l : List α} : @Nodup α l ↔ l.Nodup :=
35+
Iff.rfl
36+
#align multiset.coe_nodup Multiset.coe_nodup
37+
38+
@[simp]
39+
theorem nodup_zero : @Nodup α 0 :=
40+
Pairwise.nil
41+
#align multiset.nodup_zero Multiset.nodup_zero
42+
43+
@[simp]
44+
theorem nodup_cons {a : α} {s : Multiset α} : Nodup (a ::ₘ s) ↔ a ∉ s ∧ Nodup s :=
45+
Quot.induction_on s fun _ => List.nodup_cons
46+
#align multiset.nodup_cons Multiset.nodup_cons
47+
48+
theorem Nodup.cons (m : a ∉ s) (n : Nodup s) : Nodup (a ::ₘ s) :=
49+
nodup_cons.2 ⟨m, n⟩
50+
#align multiset.nodup.cons Multiset.Nodup.cons
51+
52+
@[simp]
53+
theorem nodup_singleton : ∀ a : α, Nodup ({a} : Multiset α) :=
54+
List.nodup_singleton
55+
#align multiset.nodup_singleton Multiset.nodup_singleton
56+
57+
theorem Nodup.of_cons (h : Nodup (a ::ₘ s)) : Nodup s :=
58+
(nodup_cons.1 h).2
59+
#align multiset.nodup.of_cons Multiset.Nodup.of_cons
60+
61+
theorem Nodup.not_mem (h : Nodup (a ::ₘ s)) : a ∉ s :=
62+
(nodup_cons.1 h).1
63+
#align multiset.nodup.not_mem Multiset.Nodup.not_mem
64+
65+
theorem nodup_of_le {s t : Multiset α} (h : s ≤ t) : Nodup t → Nodup s :=
66+
Multiset.leInductionOn h fun {_ _} => Nodup.sublist
67+
#align multiset.nodup_of_le Multiset.nodup_of_le
68+
69+
theorem not_nodup_pair : ∀ a : α, ¬Nodup (a ::ₘ a ::ₘ 0) :=
70+
List.not_nodup_pair
71+
#align multiset.not_nodup_pair Multiset.not_nodup_pair
72+
73+
theorem nodup_iff_le {s : Multiset α} : Nodup s ↔ ∀ a : α, ¬a ::ₘ a ::ₘ 0 ≤ s :=
74+
Quot.induction_on s fun _ =>
75+
nodup_iff_sublist.trans <| forall_congr' fun a => not_congr (@repeat_le_coe _ a 2 _).symm
76+
#align multiset.nodup_iff_le Multiset.nodup_iff_le
77+
78+
theorem nodup_iff_ne_cons_cons {s : Multiset α} : s.Nodup ↔ ∀ a t, s ≠ a ::ₘ a ::ₘ t :=
79+
nodup_iff_le.trans
80+
fun h a t s_eq => h a (s_eq.symm ▸ cons_le_cons a (cons_le_cons a (zero_le _))), fun h a le =>
81+
let ⟨t, s_eq⟩ := le_iff_exists_add.mp le
82+
h a t (by rwa [cons_add, cons_add, zero_add] at s_eq)⟩
83+
#align multiset.nodup_iff_ne_cons_cons Multiset.nodup_iff_ne_cons_cons
84+
85+
theorem nodup_iff_count_le_one [DecidableEq α] {s : Multiset α} : Nodup s ↔ ∀ a, count a s ≤ 1 :=
86+
Quot.induction_on s fun _l => List.nodup_iff_count_le_one
87+
#align multiset.nodup_iff_count_le_one Multiset.nodup_iff_count_le_one
88+
89+
@[simp]
90+
theorem count_eq_one_of_mem [DecidableEq α] {a : α} {s : Multiset α} (d : Nodup s) (h : a ∈ s) :
91+
count a s = 1 :=
92+
le_antisymm (nodup_iff_count_le_one.1 d a) (count_pos.2 h)
93+
#align multiset.count_eq_one_of_mem Multiset.count_eq_one_of_mem
94+
95+
theorem count_eq_of_nodup [DecidableEq α] {a : α} {s : Multiset α} (d : Nodup s) :
96+
count a s = if a ∈ s then 1 else 0 :=
97+
by
98+
split_ifs with h
99+
· exact count_eq_one_of_mem d h
100+
· exact count_eq_zero_of_not_mem h
101+
#align multiset.count_eq_of_nodup Multiset.count_eq_of_nodup
102+
103+
theorem nodup_iff_pairwise {α} {s : Multiset α} : Nodup s ↔ Pairwise (· ≠ ·) s :=
104+
Quotient.inductionOn s fun _ => (pairwise_coe_iff_pairwise fun _ _ => Ne.symm).symm
105+
#align multiset.nodup_iff_pairwise Multiset.nodup_iff_pairwise
106+
107+
protected theorem Nodup.pairwise : (∀ a ∈ s, ∀ b ∈ s, a ≠ b → r a b) → Nodup s → Pairwise r s :=
108+
Quotient.inductionOn s fun l h hl => ⟨l, rfl, hl.imp_of_mem fun {a b} ha hb => h a ha b hb⟩
109+
#align multiset.nodup.pairwise Multiset.Nodup.pairwise
110+
111+
theorem Pairwise.forall (H : Symmetric r) (hs : Pairwise r s) :
112+
∀ ⦃a⦄, a ∈ s → ∀ ⦃b⦄, b ∈ s → a ≠ b → r a b :=
113+
let ⟨_, hl₁, hl₂⟩ := hs
114+
hl₁.symm ▸ hl₂.forall H
115+
#align multiset.pairwise.forall Multiset.Pairwise.forall
116+
117+
theorem nodup_add {s t : Multiset α} : Nodup (s + t) ↔ Nodup s ∧ Nodup t ∧ Disjoint s t :=
118+
Quotient.inductionOn₂ s t fun _ _ => nodup_append
119+
#align multiset.nodup_add Multiset.nodup_add
120+
121+
theorem disjoint_of_nodup_add {s t : Multiset α} (d : Nodup (s + t)) : Disjoint s t :=
122+
(nodup_add.1 d).2.2
123+
#align multiset.disjoint_of_nodup_add Multiset.disjoint_of_nodup_add
124+
125+
theorem Nodup.add_iff (d₁ : Nodup s) (d₂ : Nodup t) : Nodup (s + t) ↔ Disjoint s t := by
126+
simp [nodup_add, d₁, d₂]
127+
#align multiset.nodup.add_iff Multiset.Nodup.add_iff
128+
129+
theorem Nodup.of_map (f : α → β) : Nodup (map f s) → Nodup s :=
130+
Quot.induction_on s fun _ => List.Nodup.of_map f
131+
#align multiset.nodup.of_map Multiset.Nodup.of_map
132+
133+
theorem Nodup.map_on {f : α → β} :
134+
(∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y) → Nodup s → Nodup (map f s) :=
135+
Quot.induction_on s fun _ => List.Nodup.map_on
136+
#align multiset.nodup.map_on Multiset.Nodup.map_on
137+
138+
theorem Nodup.map {f : α → β} {s : Multiset α} (hf : Injective f) : Nodup s → Nodup (map f s) :=
139+
Nodup.map_on fun _ _ _ _ h => hf h
140+
#align multiset.nodup.map Multiset.Nodup.map
141+
142+
theorem inj_on_of_nodup_map {f : α → β} {s : Multiset α} :
143+
Nodup (map f s) → ∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y :=
144+
Quot.induction_on s fun _ => List.inj_on_of_nodup_map
145+
#align multiset.inj_on_of_nodup_map Multiset.inj_on_of_nodup_map
146+
147+
theorem nodup_map_iff_inj_on {f : α → β} {s : Multiset α} (d : Nodup s) :
148+
Nodup (map f s) ↔ ∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y :=
149+
⟨inj_on_of_nodup_map, fun h => d.map_on h⟩
150+
#align multiset.nodup_map_iff_inj_on Multiset.nodup_map_iff_inj_on
151+
152+
theorem Nodup.filter (p : α → Bool) {s} : Nodup s → Nodup (filter p s) :=
153+
Quot.induction_on s fun _ => List.Nodup.filter p
154+
#align multiset.nodup.filter Multiset.Nodup.filter
155+
156+
@[simp]
157+
theorem nodup_attach {s : Multiset α} : Nodup (attach s) ↔ Nodup s :=
158+
Quot.induction_on s fun _ => List.nodup_attach
159+
#align multiset.nodup_attach Multiset.nodup_attach
160+
161+
theorem Nodup.pmap {p : α → Prop} {f : ∀ a, p a → β} {s : Multiset α} {H}
162+
(hf : ∀ a ha b hb, f a ha = f b hb → a = b) : Nodup s → Nodup (pmap f s H) :=
163+
Quot.induction_on s (fun _ _ => List.Nodup.pmap hf) H
164+
#align multiset.nodup.pmap Multiset.Nodup.pmap
165+
166+
instance nodupDecidable [DecidableEq α] (s : Multiset α) : Decidable (Nodup s) :=
167+
Quotient.recOnSubsingleton s fun l => l.nodupDecidable
168+
#align multiset.nodup_decidable Multiset.nodupDecidable
169+
170+
theorem Nodup.erase_eq_filter [DecidableEq α] (a : α) {s} :
171+
Nodup s → s.erase a = Multiset.filter (· ≠ a) s :=
172+
Quot.induction_on s fun _ d =>
173+
congr_arg ((↑) : List α → Multiset α) <| List.Nodup.erase_eq_filter d a
174+
#align multiset.nodup.erase_eq_filter Multiset.Nodup.erase_eq_filter
175+
176+
theorem Nodup.erase [DecidableEq α] (a : α) {l} : Nodup l → Nodup (l.erase a) :=
177+
nodup_of_le (erase_le _ _)
178+
#align multiset.nodup.erase Multiset.Nodup.erase
179+
180+
theorem Nodup.mem_erase_iff [DecidableEq α] {a b : α} {l} (d : Nodup l) :
181+
a ∈ l.erase b ↔ a ≠ b ∧ a ∈ l := by
182+
rw [d.erase_eq_filter b, mem_filter, and_comm]
183+
simp
184+
#align multiset.nodup.mem_erase_iff Multiset.Nodup.mem_erase_iff
185+
186+
theorem Nodup.not_mem_erase [DecidableEq α] {a : α} {s} (h : Nodup s) : a ∉ s.erase a := fun ha =>
187+
(h.mem_erase_iff.1 ha).1 rfl
188+
#align multiset.nodup.not_mem_erase Multiset.Nodup.not_mem_erase
189+
190+
protected theorem Nodup.product {t : Multiset β} : Nodup s → Nodup t → Nodup (product s t) :=
191+
Quotient.inductionOn₂ s t fun l₁ l₂ d₁ d₂ => by simp [List.Nodup.product d₁ d₂]
192+
#align multiset.nodup.product Multiset.Nodup.product
193+
194+
protected theorem Nodup.sigma {σ : α → Type _} {t : ∀ a, Multiset (σ a)} :
195+
Nodup s → (∀ a, Nodup (t a)) → Nodup (s.sigma t) :=
196+
Quot.induction_on s fun l₁ => by
197+
choose f hf using fun a => Quotient.exists_rep (t a)
198+
simpa [←funext hf] using List.Nodup.sigma
199+
#align multiset.nodup.sigma Multiset.Nodup.sigma
200+
201+
protected theorem Nodup.filter_map (f : α → Option β) (H : ∀ a a' b, b ∈ f a → b ∈ f a' → a = a') :
202+
Nodup s → Nodup (filterMap f s) :=
203+
Quot.induction_on s fun _ => Nodup.filterMap H
204+
#align multiset.nodup.filter_map Multiset.Nodup.filter_map
205+
206+
theorem nodup_range (n : ℕ) : Nodup (range n) :=
207+
List.nodup_range _
208+
#align multiset.nodup_range Multiset.nodup_range
209+
210+
theorem Nodup.inter_left [DecidableEq α] (t) : Nodup s → Nodup (s ∩ t) :=
211+
nodup_of_le <| inter_le_left _ _
212+
#align multiset.nodup.inter_left Multiset.Nodup.inter_left
213+
214+
theorem Nodup.inter_right [DecidableEq α] (s) : Nodup t → Nodup (s ∩ t) :=
215+
nodup_of_le <| inter_le_right _ _
216+
#align multiset.nodup.inter_right Multiset.Nodup.inter_right
217+
218+
@[simp]
219+
theorem nodup_union [DecidableEq α] {s t : Multiset α} : Nodup (s ∪ t) ↔ Nodup s ∧ Nodup t :=
220+
fun h => ⟨nodup_of_le (le_union_left _ _) h, nodup_of_le (le_union_right _ _) h⟩, fun ⟨h₁, h₂⟩ =>
221+
nodup_iff_count_le_one.2 fun a => by
222+
rw [count_union]
223+
exact max_le (nodup_iff_count_le_one.1 h₁ a) (nodup_iff_count_le_one.1 h₂ a)⟩
224+
#align multiset.nodup_union Multiset.nodup_union
225+
226+
@[simp]
227+
theorem nodup_bind {s : Multiset α} {t : α → Multiset β} :
228+
Nodup (bind s t) ↔ (∀ a ∈ s, Nodup (t a)) ∧ s.Pairwise fun a b => Disjoint (t a) (t b) :=
229+
have h₁ : ∀ a, ∃ l : List β, t a = l := fun a => Quot.induction_on (t a) fun l => ⟨l, rfl⟩
230+
let ⟨t', h'⟩ := Classical.axiom_of_choice h₁
231+
have : t = fun a => ofList (t' a) := funext h'
232+
have hd : Symmetric fun a b => List.Disjoint (t' a) (t' b) := fun a b h => h.symm
233+
Quot.induction_on s <| by simp [this, List.nodup_bind, pairwise_coe_iff_pairwise hd]
234+
#align multiset.nodup_bind Multiset.nodup_bind
235+
236+
theorem Nodup.ext {s t : Multiset α} : Nodup s → Nodup t → (s = t ↔ ∀ a, a ∈ s ↔ a ∈ t) :=
237+
Quotient.inductionOn₂ s t fun _ _ d₁ d₂ => Quotient.eq.trans <| perm_ext d₁ d₂
238+
#align multiset.nodup.ext Multiset.Nodup.ext
239+
240+
theorem le_iff_subset {s t : Multiset α} : Nodup s → (s ≤ t ↔ s ⊆ t) :=
241+
Quotient.inductionOn₂ s t fun _ _ d => ⟨subset_of_le, d.subperm⟩
242+
#align multiset.le_iff_subset Multiset.le_iff_subset
243+
244+
theorem range_le {m n : ℕ} : range m ≤ range n ↔ m ≤ n :=
245+
(le_iff_subset (nodup_range _)).trans range_subset
246+
#align multiset.range_le Multiset.range_le
247+
248+
theorem mem_sub_of_nodup [DecidableEq α] {a : α} {s t : Multiset α} (d : Nodup s) :
249+
a ∈ s - t ↔ a ∈ s ∧ a ∉ t :=
250+
fun h =>
251+
⟨mem_of_le tsub_le_self h, fun h' => by
252+
refine' count_eq_zero.1 _ h
253+
rw [count_sub a s t, tsub_eq_zero_iff_le]
254+
exact le_trans (nodup_iff_count_le_one.1 d _) (count_pos.2 h')⟩,
255+
fun ⟨h₁, h₂⟩ => Or.resolve_right (mem_add.1 <| mem_of_le le_tsub_add h₁) h₂⟩
256+
#align multiset.mem_sub_of_nodup Multiset.mem_sub_of_nodup
257+
258+
theorem map_eq_map_of_bij_of_nodup (f : α → γ) (g : β → γ) {s : Multiset α} {t : Multiset β}
259+
(hs : s.Nodup) (ht : t.Nodup) (i : ∀ a ∈ s, β) (hi : ∀ a ha, i a ha ∈ t)
260+
(h : ∀ a ha, f a = g (i a ha)) (i_inj : ∀ a₁ a₂ ha₁ ha₂, i a₁ ha₁ = i a₂ ha₂ → a₁ = a₂)
261+
(i_surj : ∀ b ∈ t, ∃ a ha, b = i a ha) : s.map f = t.map g :=
262+
have : t = s.attach.map fun x => i x.1 x.2 :=
263+
(ht.ext <|
264+
(nodup_attach.2 hs).map <|
265+
show Injective fun x : { x // x ∈ s } => i x x.2 from fun x y hxy =>
266+
Subtype.ext <| i_inj x y x.2 y.2 hxy).2
267+
fun x => by
268+
simp only [mem_map, true_and_iff, Subtype.exists, eq_comm, mem_attach]
269+
exact ⟨i_surj _, fun ⟨y, hy⟩ => hy.snd.symm ▸ hi _ _⟩
270+
calc
271+
s.map f = s.pmap (fun x _ => f x) fun _ => id := by rw [pmap_eq_map]
272+
_ = s.attach.map fun x => f x.1 := by rw [pmap_eq_map_attach]
273+
_ = t.map g := by rw [this, Multiset.map_map]; exact map_congr rfl fun x _ => h _ _
274+
275+
#align multiset.map_eq_map_of_bij_of_nodup Multiset.map_eq_map_of_bij_of_nodup
276+
277+
end Multiset

0 commit comments

Comments
 (0)