data.multiset.nodupMathlib.Data.Multiset.Nodup

This file has been ported!

Changes since the initial port

The following section lists changes to this file in mathlib3 and mathlib4 that occured after the initial port. Most recent changes are shown first. Hovering over a commit will show all commits associated with the same mathlib3 commit.

Changes in mathlib3

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(last sync)

refactor(*): define list.replicate and migrate to it (#18127)

This definition differs from list.repeat by the order of arguments. The new order is in sync with the Lean 4 version.

Diff
@@ -45,7 +45,7 @@ theorem not_nodup_pair : ∀ a : α, ¬ nodup (a ::ₘ a ::ₘ 0) := not_nodup_p
 
 theorem nodup_iff_le {s : multiset α} : nodup s ↔ ∀ a : α, ¬ a ::ₘ a ::ₘ 0 ≤ s :=
 quot.induction_on s $ λ l, nodup_iff_sublist.trans $ forall_congr $ λ a,
-not_congr (@repeat_le_coe _ a 2 _).symm
+  (@replicate_le_coe _ a 2 _).symm.not
 
 lemma nodup_iff_ne_cons_cons {s : multiset α} : s.nodup ↔ ∀ a t, s ≠ a ::ₘ a ::ₘ t :=
 nodup_iff_le.trans

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(no changes)

(first ported)

Changes in mathlib3port

mathlib3
mathlib3port
Diff
@@ -242,7 +242,7 @@ theorem Nodup.erase [DecidableEq α] (a : α) {l} : Nodup l → Nodup (l.erase
 
 #print Multiset.Nodup.mem_erase_iff /-
 theorem Nodup.mem_erase_iff [DecidableEq α] {a b : α} {l} (d : Nodup l) :
-    a ∈ l.eraseₓ b ↔ a ≠ b ∧ a ∈ l := by rw [d.erase_eq_filter b, mem_filter, and_comm']
+    a ∈ l.eraseₓ b ↔ a ≠ b ∧ a ∈ l := by rw [d.erase_eq_filter b, mem_filter, and_comm]
 #align multiset.nodup.mem_erase_iff Multiset.Nodup.mem_erase_iff
 -/
 
Diff
@@ -102,7 +102,7 @@ theorem nodup_iff_ne_cons_cons {s : Multiset α} : s.Nodup ↔ ∀ a t, s ≠ a
   nodup_iff_le.trans
     ⟨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 =>
       let ⟨t, s_eq⟩ := le_iff_exists_add.mp le
-      h a t (by rwa [cons_add, cons_add, zero_add] at s_eq )⟩
+      h a t (by rwa [cons_add, cons_add, zero_add] at s_eq)⟩
 #align multiset.nodup_iff_ne_cons_cons Multiset.nodup_iff_ne_cons_cons
 -/
 
Diff
@@ -318,7 +318,7 @@ theorem nodup_bind {s : Multiset α} {t : α → Multiset β} :
 
 #print Multiset.Nodup.ext /-
 theorem Nodup.ext {s t : Multiset α} : Nodup s → Nodup t → (s = t ↔ ∀ a, a ∈ s ↔ a ∈ t) :=
-  Quotient.induction_on₂ s t fun l₁ l₂ d₁ d₂ => Quotient.eq'.trans <| perm_ext d₁ d₂
+  Quotient.induction_on₂ s t fun l₁ l₂ d₁ d₂ => Quotient.eq'.trans <| perm_ext_iff_of_nodup d₁ d₂
 #align multiset.nodup.ext Multiset.Nodup.ext
 -/
 
Diff
@@ -3,9 +3,9 @@ Copyright (c) 2015 Microsoft Corporation. All rights reserved.
 Released under Apache 2.0 license as described in the file LICENSE.
 Authors: Mario Carneiro
 -/
-import Mathbin.Data.List.Nodup
-import Mathbin.Data.Multiset.Bind
-import Mathbin.Data.Multiset.Range
+import Data.List.Nodup
+import Data.Multiset.Bind
+import Data.Multiset.Range
 
 #align_import data.multiset.nodup from "leanprover-community/mathlib"@"f2f413b9d4be3a02840d0663dace76e8fe3da053"
 
Diff
@@ -2,16 +2,13 @@
 Copyright (c) 2015 Microsoft Corporation. All rights reserved.
 Released under Apache 2.0 license as described in the file LICENSE.
 Authors: Mario Carneiro
-
-! This file was ported from Lean 3 source module data.multiset.nodup
-! leanprover-community/mathlib commit f2f413b9d4be3a02840d0663dace76e8fe3da053
-! Please do not edit these lines, except to modify the commit id
-! if you have ported upstream changes.
 -/
 import Mathbin.Data.List.Nodup
 import Mathbin.Data.Multiset.Bind
 import Mathbin.Data.Multiset.Range
 
+#align_import data.multiset.nodup from "leanprover-community/mathlib"@"f2f413b9d4be3a02840d0663dace76e8fe3da053"
+
 /-!
 # The `nodup` predicate for multisets without duplicate elements.
 
Diff
@@ -81,9 +81,11 @@ theorem Nodup.not_mem (h : Nodup (a ::ₘ s)) : a ∉ s :=
 #align multiset.nodup.not_mem Multiset.Nodup.not_mem
 -/
 
+#print Multiset.nodup_of_le /-
 theorem nodup_of_le {s t : Multiset α} (h : s ≤ t) : Nodup t → Nodup s :=
   leInductionOn h fun l₁ l₂ => Nodup.sublist
 #align multiset.nodup_of_le Multiset.nodup_of_le
+-/
 
 #print Multiset.not_nodup_pair /-
 theorem not_nodup_pair : ∀ a : α, ¬Nodup (a ::ₘ a ::ₘ 0) :=
@@ -91,10 +93,12 @@ theorem not_nodup_pair : ∀ a : α, ¬Nodup (a ::ₘ a ::ₘ 0) :=
 #align multiset.not_nodup_pair Multiset.not_nodup_pair
 -/
 
+#print Multiset.nodup_iff_le /-
 theorem nodup_iff_le {s : Multiset α} : Nodup s ↔ ∀ a : α, ¬a ::ₘ a ::ₘ 0 ≤ s :=
   Quot.inductionOn s fun l =>
     nodup_iff_sublist.trans <| forall_congr' fun a => (@replicate_le_coe _ a 2 _).symm.Not
 #align multiset.nodup_iff_le Multiset.nodup_iff_le
+-/
 
 #print Multiset.nodup_iff_ne_cons_cons /-
 theorem nodup_iff_ne_cons_cons {s : Multiset α} : s.Nodup ↔ ∀ a t, s ≠ a ::ₘ a ::ₘ t :=
@@ -173,24 +177,32 @@ theorem Nodup.of_map (f : α → β) : Nodup (map f s) → Nodup s :=
 #align multiset.nodup.of_map Multiset.Nodup.of_map
 -/
 
+#print Multiset.Nodup.map_on /-
 theorem Nodup.map_on {f : α → β} :
     (∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y) → Nodup s → Nodup (map f s) :=
   Quot.inductionOn s fun l => Nodup.map_on
 #align multiset.nodup.map_on Multiset.Nodup.map_on
+-/
 
+#print Multiset.Nodup.map /-
 theorem Nodup.map {f : α → β} {s : Multiset α} (hf : Injective f) : Nodup s → Nodup (map f s) :=
   Nodup.map_on fun x _ y _ h => hf h
 #align multiset.nodup.map Multiset.Nodup.map
+-/
 
+#print Multiset.inj_on_of_nodup_map /-
 theorem inj_on_of_nodup_map {f : α → β} {s : Multiset α} :
     Nodup (map f s) → ∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y :=
   Quot.inductionOn s fun l => inj_on_of_nodup_map
 #align multiset.inj_on_of_nodup_map Multiset.inj_on_of_nodup_map
+-/
 
+#print Multiset.nodup_map_iff_inj_on /-
 theorem nodup_map_iff_inj_on {f : α → β} {s : Multiset α} (d : Nodup s) :
     Nodup (map f s) ↔ ∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y :=
   ⟨inj_on_of_nodup_map, fun h => d.map_onₓ h⟩
 #align multiset.nodup_map_iff_inj_on Multiset.nodup_map_iff_inj_on
+-/
 
 #print Multiset.Nodup.filter /-
 theorem Nodup.filter (p : α → Prop) [DecidablePred p] {s} : Nodup s → Nodup (filter p s) :=
@@ -205,10 +217,12 @@ theorem nodup_attach {s : Multiset α} : Nodup (attach s) ↔ Nodup s :=
 #align multiset.nodup_attach Multiset.nodup_attach
 -/
 
+#print Multiset.Nodup.pmap /-
 theorem Nodup.pmap {p : α → Prop} {f : ∀ a, p a → β} {s : Multiset α} {H}
     (hf : ∀ a ha b hb, f a ha = f b hb → a = b) : Nodup s → Nodup (pmap f s H) :=
   Quot.inductionOn s (fun l H => Nodup.pmap hf) H
 #align multiset.nodup.pmap Multiset.Nodup.pmap
+-/
 
 #print Multiset.nodupDecidable /-
 instance nodupDecidable [DecidableEq α] (s : Multiset α) : Decidable (Nodup s) :=
@@ -293,6 +307,7 @@ theorem nodup_union [DecidableEq α] {s t : Multiset α} : Nodup (s ∪ t) ↔ N
 #align multiset.nodup_union Multiset.nodup_union
 -/
 
+#print Multiset.nodup_bind /-
 @[simp]
 theorem nodup_bind {s : Multiset α} {t : α → Multiset β} :
     Nodup (bind s t) ↔ (∀ a ∈ s, Nodup (t a)) ∧ s.Pairwise fun a b => Disjoint (t a) (t b) :=
@@ -302,6 +317,7 @@ theorem nodup_bind {s : Multiset α} {t : α → Multiset β} :
   have hd : Symmetric fun a b => List.Disjoint (t' a) (t' b) := fun a b h => h.symm
   Quot.inductionOn s <| by simp [this, List.nodup_bind, pairwise_coe_iff_pairwise hd]
 #align multiset.nodup_bind Multiset.nodup_bind
+-/
 
 #print Multiset.Nodup.ext /-
 theorem Nodup.ext {s t : Multiset α} : Nodup s → Nodup t → (s = t ↔ ∀ a, a ∈ s ↔ a ∈ t) :=
@@ -309,13 +325,17 @@ theorem Nodup.ext {s t : Multiset α} : Nodup s → Nodup t → (s = t ↔ ∀ a
 #align multiset.nodup.ext Multiset.Nodup.ext
 -/
 
+#print Multiset.le_iff_subset /-
 theorem le_iff_subset {s t : Multiset α} : Nodup s → (s ≤ t ↔ s ⊆ t) :=
   Quotient.induction_on₂ s t fun l₁ l₂ d => ⟨subset_of_le, d.Subperm⟩
 #align multiset.le_iff_subset Multiset.le_iff_subset
+-/
 
+#print Multiset.range_le /-
 theorem range_le {m n : ℕ} : range m ≤ range n ↔ m ≤ n :=
   (le_iff_subset (nodup_range _)).trans range_subset
 #align multiset.range_le Multiset.range_le
+-/
 
 #print Multiset.mem_sub_of_nodup /-
 theorem mem_sub_of_nodup [DecidableEq α] {a : α} {s t : Multiset α} (d : Nodup s) :
@@ -328,6 +348,7 @@ theorem mem_sub_of_nodup [DecidableEq α] {a : α} {s t : Multiset α} (d : Nodu
 #align multiset.mem_sub_of_nodup Multiset.mem_sub_of_nodup
 -/
 
+#print Multiset.map_eq_map_of_bij_of_nodup /-
 theorem map_eq_map_of_bij_of_nodup (f : α → γ) (g : β → γ) {s : Multiset α} {t : Multiset β}
     (hs : s.Nodup) (ht : t.Nodup) (i : ∀ a ∈ s, β) (hi : ∀ a ha, i a ha ∈ t)
     (h : ∀ a ha, f a = g (i a ha)) (i_inj : ∀ a₁ a₂ ha₁ ha₂, i a₁ ha₁ = i a₂ ha₂ → a₁ = a₂)
@@ -345,6 +366,7 @@ theorem map_eq_map_of_bij_of_nodup (f : α → γ) (g : β → γ) {s : Multiset
     _ = s.attach.map fun x => f x := by rw [pmap_eq_map_attach]
     _ = t.map g := by rw [this, Multiset.map_map] <;> exact map_congr rfl fun x _ => h _ _
 #align multiset.map_eq_map_of_bij_of_nodup Multiset.map_eq_map_of_bij_of_nodup
+-/
 
 end Multiset
 
Diff
@@ -344,7 +344,6 @@ theorem map_eq_map_of_bij_of_nodup (f : α → γ) (g : β → γ) {s : Multiset
     s.map f = s.pmap (fun x _ => f x) fun _ => id := by rw [pmap_eq_map]
     _ = s.attach.map fun x => f x := by rw [pmap_eq_map_attach]
     _ = t.map g := by rw [this, Multiset.map_map] <;> exact map_congr rfl fun x _ => h _ _
-    
 #align multiset.map_eq_map_of_bij_of_nodup Multiset.map_eq_map_of_bij_of_nodup
 
 end Multiset
Diff
@@ -101,7 +101,7 @@ theorem nodup_iff_ne_cons_cons {s : Multiset α} : s.Nodup ↔ ∀ a t, s ≠ a
   nodup_iff_le.trans
     ⟨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 =>
       let ⟨t, s_eq⟩ := le_iff_exists_add.mp le
-      h a t (by rwa [cons_add, cons_add, zero_add] at s_eq)⟩
+      h a t (by rwa [cons_add, cons_add, zero_add] at s_eq )⟩
 #align multiset.nodup_iff_ne_cons_cons Multiset.nodup_iff_ne_cons_cons
 -/
 
Diff
@@ -81,12 +81,6 @@ theorem Nodup.not_mem (h : Nodup (a ::ₘ s)) : a ∉ s :=
 #align multiset.nodup.not_mem Multiset.Nodup.not_mem
 -/
 
-/- warning: multiset.nodup_of_le -> Multiset.nodup_of_le is a dubious translation:
-lean 3 declaration is
-  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (LE.le.{u1} (Multiset.{u1} α) (Preorder.toHasLe.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t) -> (Multiset.Nodup.{u1} α t) -> (Multiset.Nodup.{u1} α s)
-but is expected to have type
-  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s t) -> (Multiset.Nodup.{u1} α t) -> (Multiset.Nodup.{u1} α s)
-Case conversion may be inaccurate. Consider using '#align multiset.nodup_of_le Multiset.nodup_of_leₓ'. -/
 theorem nodup_of_le {s t : Multiset α} (h : s ≤ t) : Nodup t → Nodup s :=
   leInductionOn h fun l₁ l₂ => Nodup.sublist
 #align multiset.nodup_of_le Multiset.nodup_of_le
@@ -97,12 +91,6 @@ theorem not_nodup_pair : ∀ a : α, ¬Nodup (a ::ₘ a ::ₘ 0) :=
 #align multiset.not_nodup_pair Multiset.not_nodup_pair
 -/
 
-/- warning: multiset.nodup_iff_le -> Multiset.nodup_iff_le is a dubious translation:
-lean 3 declaration is
-  forall {α : Type.{u1}} {s : Multiset.{u1} α}, Iff (Multiset.Nodup.{u1} α s) (forall (a : α), Not (LE.le.{u1} (Multiset.{u1} α) (Preorder.toHasLe.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) (Multiset.cons.{u1} α a (Multiset.cons.{u1} α a (OfNat.ofNat.{u1} (Multiset.{u1} α) 0 (OfNat.mk.{u1} (Multiset.{u1} α) 0 (Zero.zero.{u1} (Multiset.{u1} α) (Multiset.hasZero.{u1} α)))))) s))
-but is expected to have type
-  forall {α : Type.{u1}} {s : Multiset.{u1} α}, Iff (Multiset.Nodup.{u1} α s) (forall (a : α), Not (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) (Multiset.cons.{u1} α a (Multiset.cons.{u1} α a (OfNat.ofNat.{u1} (Multiset.{u1} α) 0 (Zero.toOfNat0.{u1} (Multiset.{u1} α) (Multiset.instZeroMultiset.{u1} α))))) s))
-Case conversion may be inaccurate. Consider using '#align multiset.nodup_iff_le Multiset.nodup_iff_leₓ'. -/
 theorem nodup_iff_le {s : Multiset α} : Nodup s ↔ ∀ a : α, ¬a ::ₘ a ::ₘ 0 ≤ s :=
   Quot.inductionOn s fun l =>
     nodup_iff_sublist.trans <| forall_congr' fun a => (@replicate_le_coe _ a 2 _).symm.Not
@@ -185,44 +173,20 @@ theorem Nodup.of_map (f : α → β) : Nodup (map f s) → Nodup s :=
 #align multiset.nodup.of_map Multiset.Nodup.of_map
 -/
 
-/- warning: multiset.nodup.map_on -> Multiset.Nodup.map_on is a dubious translation:
-lean 3 declaration is
-  forall {α : Type.{u1}} {β : Type.{u2}} {s : Multiset.{u1} α} {f : α -> β}, (forall (x : α), (Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) x s) -> (forall (y : α), (Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) y s) -> (Eq.{succ u2} β (f x) (f y)) -> (Eq.{succ u1} α x y))) -> (Multiset.Nodup.{u1} α s) -> (Multiset.Nodup.{u2} β (Multiset.map.{u1, u2} α β f s))
-but is expected to have type
-  forall {α : Type.{u2}} {β : Type.{u1}} {s : Multiset.{u2} α} {f : α -> β}, (forall (x : α), (Membership.mem.{u2, u2} α (Multiset.{u2} α) (Multiset.instMembershipMultiset.{u2} α) x s) -> (forall (y : α), (Membership.mem.{u2, u2} α (Multiset.{u2} α) (Multiset.instMembershipMultiset.{u2} α) y s) -> (Eq.{succ u1} β (f x) (f y)) -> (Eq.{succ u2} α x y))) -> (Multiset.Nodup.{u2} α s) -> (Multiset.Nodup.{u1} β (Multiset.map.{u2, u1} α β f s))
-Case conversion may be inaccurate. Consider using '#align multiset.nodup.map_on Multiset.Nodup.map_onₓ'. -/
 theorem Nodup.map_on {f : α → β} :
     (∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y) → Nodup s → Nodup (map f s) :=
   Quot.inductionOn s fun l => Nodup.map_on
 #align multiset.nodup.map_on Multiset.Nodup.map_on
 
-/- warning: multiset.nodup.map -> Multiset.Nodup.map is a dubious translation:
-lean 3 declaration is
-  forall {α : Type.{u1}} {β : Type.{u2}} {f : α -> β} {s : Multiset.{u1} α}, (Function.Injective.{succ u1, succ u2} α β f) -> (Multiset.Nodup.{u1} α s) -> (Multiset.Nodup.{u2} β (Multiset.map.{u1, u2} α β f s))
-but is expected to have type
-  forall {α : Type.{u2}} {β : Type.{u1}} {f : α -> β} {s : Multiset.{u2} α}, (Function.Injective.{succ u2, succ u1} α β f) -> (Multiset.Nodup.{u2} α s) -> (Multiset.Nodup.{u1} β (Multiset.map.{u2, u1} α β f s))
-Case conversion may be inaccurate. Consider using '#align multiset.nodup.map Multiset.Nodup.mapₓ'. -/
 theorem Nodup.map {f : α → β} {s : Multiset α} (hf : Injective f) : Nodup s → Nodup (map f s) :=
   Nodup.map_on fun x _ y _ h => hf h
 #align multiset.nodup.map Multiset.Nodup.map
 
-/- warning: multiset.inj_on_of_nodup_map -> Multiset.inj_on_of_nodup_map is a dubious translation:
-lean 3 declaration is
-  forall {α : Type.{u1}} {β : Type.{u2}} {f : α -> β} {s : Multiset.{u1} α}, (Multiset.Nodup.{u2} β (Multiset.map.{u1, u2} α β f s)) -> (forall (x : α), (Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) x s) -> (forall (y : α), (Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) y s) -> (Eq.{succ u2} β (f x) (f y)) -> (Eq.{succ u1} α x y)))
-but is expected to have type
-  forall {α : Type.{u2}} {β : Type.{u1}} {f : α -> β} {s : Multiset.{u2} α}, (Multiset.Nodup.{u1} β (Multiset.map.{u2, u1} α β f s)) -> (forall (x : α), (Membership.mem.{u2, u2} α (Multiset.{u2} α) (Multiset.instMembershipMultiset.{u2} α) x s) -> (forall (y : α), (Membership.mem.{u2, u2} α (Multiset.{u2} α) (Multiset.instMembershipMultiset.{u2} α) y s) -> (Eq.{succ u1} β (f x) (f y)) -> (Eq.{succ u2} α x y)))
-Case conversion may be inaccurate. Consider using '#align multiset.inj_on_of_nodup_map Multiset.inj_on_of_nodup_mapₓ'. -/
 theorem inj_on_of_nodup_map {f : α → β} {s : Multiset α} :
     Nodup (map f s) → ∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y :=
   Quot.inductionOn s fun l => inj_on_of_nodup_map
 #align multiset.inj_on_of_nodup_map Multiset.inj_on_of_nodup_map
 
-/- warning: multiset.nodup_map_iff_inj_on -> Multiset.nodup_map_iff_inj_on is a dubious translation:
-lean 3 declaration is
-  forall {α : Type.{u1}} {β : Type.{u2}} {f : α -> β} {s : Multiset.{u1} α}, (Multiset.Nodup.{u1} α s) -> (Iff (Multiset.Nodup.{u2} β (Multiset.map.{u1, u2} α β f s)) (forall (x : α), (Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) x s) -> (forall (y : α), (Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) y s) -> (Eq.{succ u2} β (f x) (f y)) -> (Eq.{succ u1} α x y))))
-but is expected to have type
-  forall {α : Type.{u2}} {β : Type.{u1}} {f : α -> β} {s : Multiset.{u2} α}, (Multiset.Nodup.{u2} α s) -> (Iff (Multiset.Nodup.{u1} β (Multiset.map.{u2, u1} α β f s)) (forall (x : α), (Membership.mem.{u2, u2} α (Multiset.{u2} α) (Multiset.instMembershipMultiset.{u2} α) x s) -> (forall (y : α), (Membership.mem.{u2, u2} α (Multiset.{u2} α) (Multiset.instMembershipMultiset.{u2} α) y s) -> (Eq.{succ u1} β (f x) (f y)) -> (Eq.{succ u2} α x y))))
-Case conversion may be inaccurate. Consider using '#align multiset.nodup_map_iff_inj_on Multiset.nodup_map_iff_inj_onₓ'. -/
 theorem nodup_map_iff_inj_on {f : α → β} {s : Multiset α} (d : Nodup s) :
     Nodup (map f s) ↔ ∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y :=
   ⟨inj_on_of_nodup_map, fun h => d.map_onₓ h⟩
@@ -241,12 +205,6 @@ theorem nodup_attach {s : Multiset α} : Nodup (attach s) ↔ Nodup s :=
 #align multiset.nodup_attach Multiset.nodup_attach
 -/
 
-/- warning: multiset.nodup.pmap -> Multiset.Nodup.pmap is a dubious translation:
-lean 3 declaration is
-  forall {α : Type.{u1}} {β : Type.{u2}} {p : α -> Prop} {f : forall (a : α), (p a) -> β} {s : Multiset.{u1} α} {H : forall (a : α), (Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) a s) -> (p a)}, (forall (a : α) (ha : p a) (b : α) (hb : p b), (Eq.{succ u2} β (f a ha) (f b hb)) -> (Eq.{succ u1} α a b)) -> (Multiset.Nodup.{u1} α s) -> (Multiset.Nodup.{u2} β (Multiset.pmap.{u1, u2} α β (fun (a : α) => p a) f s H))
-but is expected to have type
-  forall {α : Type.{u2}} {β : Type.{u1}} {p : α -> Prop} {f : forall (a : α), (p a) -> β} {s : Multiset.{u2} α} {H : forall (a : α), (Membership.mem.{u2, u2} α (Multiset.{u2} α) (Multiset.instMembershipMultiset.{u2} α) a s) -> (p a)}, (forall (a : α) (ha : p a) (b : α) (hb : p b), (Eq.{succ u1} β (f a ha) (f b hb)) -> (Eq.{succ u2} α a b)) -> (Multiset.Nodup.{u2} α s) -> (Multiset.Nodup.{u1} β (Multiset.pmap.{u2, u1} α β (fun (a : α) => p a) f s H))
-Case conversion may be inaccurate. Consider using '#align multiset.nodup.pmap Multiset.Nodup.pmapₓ'. -/
 theorem Nodup.pmap {p : α → Prop} {f : ∀ a, p a → β} {s : Multiset α} {H}
     (hf : ∀ a ha b hb, f a ha = f b hb → a = b) : Nodup s → Nodup (pmap f s H) :=
   Quot.inductionOn s (fun l H => Nodup.pmap hf) H
@@ -335,12 +293,6 @@ theorem nodup_union [DecidableEq α] {s t : Multiset α} : Nodup (s ∪ t) ↔ N
 #align multiset.nodup_union Multiset.nodup_union
 -/
 
-/- warning: multiset.nodup_bind -> Multiset.nodup_bind is a dubious translation:
-lean 3 declaration is
-  forall {α : Type.{u1}} {β : Type.{u2}} {s : Multiset.{u1} α} {t : α -> (Multiset.{u2} β)}, Iff (Multiset.Nodup.{u2} β (Multiset.bind.{u1, u2} α β s t)) (And (forall (a : α), (Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) a s) -> (Multiset.Nodup.{u2} β (t a))) (Multiset.Pairwise.{u1} α (fun (a : α) (b : α) => Multiset.Disjoint.{u2} β (t a) (t b)) s))
-but is expected to have type
-  forall {α : Type.{u2}} {β : Type.{u1}} {s : Multiset.{u2} α} {t : α -> (Multiset.{u1} β)}, Iff (Multiset.Nodup.{u1} β (Multiset.bind.{u2, u1} α β s t)) (And (forall (a : α), (Membership.mem.{u2, u2} α (Multiset.{u2} α) (Multiset.instMembershipMultiset.{u2} α) a s) -> (Multiset.Nodup.{u1} β (t a))) (Multiset.Pairwise.{u2} α (fun (a : α) (b : α) => Multiset.Disjoint.{u1} β (t a) (t b)) s))
-Case conversion may be inaccurate. Consider using '#align multiset.nodup_bind Multiset.nodup_bindₓ'. -/
 @[simp]
 theorem nodup_bind {s : Multiset α} {t : α → Multiset β} :
     Nodup (bind s t) ↔ (∀ a ∈ s, Nodup (t a)) ∧ s.Pairwise fun a b => Disjoint (t a) (t b) :=
@@ -357,22 +309,10 @@ theorem Nodup.ext {s t : Multiset α} : Nodup s → Nodup t → (s = t ↔ ∀ a
 #align multiset.nodup.ext Multiset.Nodup.ext
 -/
 
-/- warning: multiset.le_iff_subset -> Multiset.le_iff_subset is a dubious translation:
-lean 3 declaration is
-  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (Multiset.Nodup.{u1} α s) -> (Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toHasLe.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t) (HasSubset.Subset.{u1} (Multiset.{u1} α) (Multiset.hasSubset.{u1} α) s t))
-but is expected to have type
-  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (Multiset.Nodup.{u1} α s) -> (Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s t) (HasSubset.Subset.{u1} (Multiset.{u1} α) (Multiset.instHasSubsetMultiset.{u1} α) s t))
-Case conversion may be inaccurate. Consider using '#align multiset.le_iff_subset Multiset.le_iff_subsetₓ'. -/
 theorem le_iff_subset {s t : Multiset α} : Nodup s → (s ≤ t ↔ s ⊆ t) :=
   Quotient.induction_on₂ s t fun l₁ l₂ d => ⟨subset_of_le, d.Subperm⟩
 #align multiset.le_iff_subset Multiset.le_iff_subset
 
-/- warning: multiset.range_le -> Multiset.range_le is a dubious translation:
-lean 3 declaration is
-  forall {m : Nat} {n : Nat}, Iff (LE.le.{0} (Multiset.{0} Nat) (Preorder.toHasLe.{0} (Multiset.{0} Nat) (PartialOrder.toPreorder.{0} (Multiset.{0} Nat) (Multiset.partialOrder.{0} Nat))) (Multiset.range m) (Multiset.range n)) (LE.le.{0} Nat Nat.hasLe m n)
-but is expected to have type
-  forall {m : Nat} {n : Nat}, Iff (LE.le.{0} (Multiset.{0} Nat) (Preorder.toLE.{0} (Multiset.{0} Nat) (PartialOrder.toPreorder.{0} (Multiset.{0} Nat) (Multiset.instPartialOrderMultiset.{0} Nat))) (Multiset.range m) (Multiset.range n)) (LE.le.{0} Nat instLENat m n)
-Case conversion may be inaccurate. Consider using '#align multiset.range_le Multiset.range_leₓ'. -/
 theorem range_le {m n : ℕ} : range m ≤ range n ↔ m ≤ n :=
   (le_iff_subset (nodup_range _)).trans range_subset
 #align multiset.range_le Multiset.range_le
@@ -388,12 +328,6 @@ theorem mem_sub_of_nodup [DecidableEq α] {a : α} {s t : Multiset α} (d : Nodu
 #align multiset.mem_sub_of_nodup Multiset.mem_sub_of_nodup
 -/
 
-/- warning: multiset.map_eq_map_of_bij_of_nodup -> Multiset.map_eq_map_of_bij_of_nodup is a dubious translation:
-lean 3 declaration is
-  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : α -> γ) (g : β -> γ) {s : Multiset.{u1} α} {t : Multiset.{u2} β}, (Multiset.Nodup.{u1} α s) -> (Multiset.Nodup.{u2} β t) -> (forall (i : forall (a : α), (Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) a s) -> β), (forall (a : α) (ha : Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) a s), Membership.Mem.{u2, u2} β (Multiset.{u2} β) (Multiset.hasMem.{u2} β) (i a ha) t) -> (forall (a : α) (ha : Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) a s), Eq.{succ u3} γ (f a) (g (i a ha))) -> (forall (a₁ : α) (a₂ : α) (ha₁ : Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) a₁ s) (ha₂ : Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) a₂ s), (Eq.{succ u2} β (i a₁ ha₁) (i a₂ ha₂)) -> (Eq.{succ u1} α a₁ a₂)) -> (forall (b : β), (Membership.Mem.{u2, u2} β (Multiset.{u2} β) (Multiset.hasMem.{u2} β) b t) -> (Exists.{succ u1} α (fun (a : α) => Exists.{0} (Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) a s) (fun (ha : Membership.Mem.{u1, u1} α (Multiset.{u1} α) (Multiset.hasMem.{u1} α) a s) => Eq.{succ u2} β b (i a ha))))) -> (Eq.{succ u3} (Multiset.{u3} γ) (Multiset.map.{u1, u3} α γ f s) (Multiset.map.{u2, u3} β γ g t)))
-but is expected to have type
-  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} (f : α -> γ) (g : β -> γ) {s : Multiset.{u3} α} {t : Multiset.{u2} β}, (Multiset.Nodup.{u3} α s) -> (Multiset.Nodup.{u2} β t) -> (forall (i : forall (a : α), (Membership.mem.{u3, u3} α (Multiset.{u3} α) (Multiset.instMembershipMultiset.{u3} α) a s) -> β), (forall (a : α) (ha : Membership.mem.{u3, u3} α (Multiset.{u3} α) (Multiset.instMembershipMultiset.{u3} α) a s), Membership.mem.{u2, u2} β (Multiset.{u2} β) (Multiset.instMembershipMultiset.{u2} β) (i a ha) t) -> (forall (a : α) (ha : Membership.mem.{u3, u3} α (Multiset.{u3} α) (Multiset.instMembershipMultiset.{u3} α) a s), Eq.{succ u1} γ (f a) (g (i a ha))) -> (forall (a₁ : α) (a₂ : α) (ha₁ : Membership.mem.{u3, u3} α (Multiset.{u3} α) (Multiset.instMembershipMultiset.{u3} α) a₁ s) (ha₂ : Membership.mem.{u3, u3} α (Multiset.{u3} α) (Multiset.instMembershipMultiset.{u3} α) a₂ s), (Eq.{succ u2} β (i a₁ ha₁) (i a₂ ha₂)) -> (Eq.{succ u3} α a₁ a₂)) -> (forall (b : β), (Membership.mem.{u2, u2} β (Multiset.{u2} β) (Multiset.instMembershipMultiset.{u2} β) b t) -> (Exists.{succ u3} α (fun (a : α) => Exists.{0} (Membership.mem.{u3, u3} α (Multiset.{u3} α) (Multiset.instMembershipMultiset.{u3} α) a s) (fun (ha : Membership.mem.{u3, u3} α (Multiset.{u3} α) (Multiset.instMembershipMultiset.{u3} α) a s) => Eq.{succ u2} β b (i a ha))))) -> (Eq.{succ u1} (Multiset.{u1} γ) (Multiset.map.{u3, u1} α γ f s) (Multiset.map.{u2, u1} β γ g t)))
-Case conversion may be inaccurate. Consider using '#align multiset.map_eq_map_of_bij_of_nodup Multiset.map_eq_map_of_bij_of_nodupₓ'. -/
 theorem map_eq_map_of_bij_of_nodup (f : α → γ) (g : β → γ) {s : Multiset α} {t : Multiset β}
     (hs : s.Nodup) (ht : t.Nodup) (i : ∀ a ∈ s, β) (hi : ∀ a ha, i a ha ∈ t)
     (h : ∀ a ha, f a = g (i a ha)) (i_inj : ∀ a₁ a₂ ha₁ ha₂, i a₁ ha₁ = i a₂ ha₂ → a₁ = a₂)
Diff
@@ -83,7 +83,7 @@ theorem Nodup.not_mem (h : Nodup (a ::ₘ s)) : a ∉ s :=
 
 /- warning: multiset.nodup_of_le -> Multiset.nodup_of_le is a dubious translation:
 lean 3 declaration is
-  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t) -> (Multiset.Nodup.{u1} α t) -> (Multiset.Nodup.{u1} α s)
+  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (LE.le.{u1} (Multiset.{u1} α) (Preorder.toHasLe.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t) -> (Multiset.Nodup.{u1} α t) -> (Multiset.Nodup.{u1} α s)
 but is expected to have type
   forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s t) -> (Multiset.Nodup.{u1} α t) -> (Multiset.Nodup.{u1} α s)
 Case conversion may be inaccurate. Consider using '#align multiset.nodup_of_le Multiset.nodup_of_leₓ'. -/
@@ -99,7 +99,7 @@ theorem not_nodup_pair : ∀ a : α, ¬Nodup (a ::ₘ a ::ₘ 0) :=
 
 /- warning: multiset.nodup_iff_le -> Multiset.nodup_iff_le is a dubious translation:
 lean 3 declaration is
-  forall {α : Type.{u1}} {s : Multiset.{u1} α}, Iff (Multiset.Nodup.{u1} α s) (forall (a : α), Not (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) (Multiset.cons.{u1} α a (Multiset.cons.{u1} α a (OfNat.ofNat.{u1} (Multiset.{u1} α) 0 (OfNat.mk.{u1} (Multiset.{u1} α) 0 (Zero.zero.{u1} (Multiset.{u1} α) (Multiset.hasZero.{u1} α)))))) s))
+  forall {α : Type.{u1}} {s : Multiset.{u1} α}, Iff (Multiset.Nodup.{u1} α s) (forall (a : α), Not (LE.le.{u1} (Multiset.{u1} α) (Preorder.toHasLe.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) (Multiset.cons.{u1} α a (Multiset.cons.{u1} α a (OfNat.ofNat.{u1} (Multiset.{u1} α) 0 (OfNat.mk.{u1} (Multiset.{u1} α) 0 (Zero.zero.{u1} (Multiset.{u1} α) (Multiset.hasZero.{u1} α)))))) s))
 but is expected to have type
   forall {α : Type.{u1}} {s : Multiset.{u1} α}, Iff (Multiset.Nodup.{u1} α s) (forall (a : α), Not (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) (Multiset.cons.{u1} α a (Multiset.cons.{u1} α a (OfNat.ofNat.{u1} (Multiset.{u1} α) 0 (Zero.toOfNat0.{u1} (Multiset.{u1} α) (Multiset.instZeroMultiset.{u1} α))))) s))
 Case conversion may be inaccurate. Consider using '#align multiset.nodup_iff_le Multiset.nodup_iff_leₓ'. -/
@@ -359,7 +359,7 @@ theorem Nodup.ext {s t : Multiset α} : Nodup s → Nodup t → (s = t ↔ ∀ a
 
 /- warning: multiset.le_iff_subset -> Multiset.le_iff_subset is a dubious translation:
 lean 3 declaration is
-  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (Multiset.Nodup.{u1} α s) -> (Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t) (HasSubset.Subset.{u1} (Multiset.{u1} α) (Multiset.hasSubset.{u1} α) s t))
+  forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (Multiset.Nodup.{u1} α s) -> (Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toHasLe.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.partialOrder.{u1} α))) s t) (HasSubset.Subset.{u1} (Multiset.{u1} α) (Multiset.hasSubset.{u1} α) s t))
 but is expected to have type
   forall {α : Type.{u1}} {s : Multiset.{u1} α} {t : Multiset.{u1} α}, (Multiset.Nodup.{u1} α s) -> (Iff (LE.le.{u1} (Multiset.{u1} α) (Preorder.toLE.{u1} (Multiset.{u1} α) (PartialOrder.toPreorder.{u1} (Multiset.{u1} α) (Multiset.instPartialOrderMultiset.{u1} α))) s t) (HasSubset.Subset.{u1} (Multiset.{u1} α) (Multiset.instHasSubsetMultiset.{u1} α) s t))
 Case conversion may be inaccurate. Consider using '#align multiset.le_iff_subset Multiset.le_iff_subsetₓ'. -/
@@ -369,7 +369,7 @@ theorem le_iff_subset {s t : Multiset α} : Nodup s → (s ≤ t ↔ s ⊆ t) :=
 
 /- warning: multiset.range_le -> Multiset.range_le is a dubious translation:
 lean 3 declaration is
-  forall {m : Nat} {n : Nat}, Iff (LE.le.{0} (Multiset.{0} Nat) (Preorder.toLE.{0} (Multiset.{0} Nat) (PartialOrder.toPreorder.{0} (Multiset.{0} Nat) (Multiset.partialOrder.{0} Nat))) (Multiset.range m) (Multiset.range n)) (LE.le.{0} Nat Nat.hasLe m n)
+  forall {m : Nat} {n : Nat}, Iff (LE.le.{0} (Multiset.{0} Nat) (Preorder.toHasLe.{0} (Multiset.{0} Nat) (PartialOrder.toPreorder.{0} (Multiset.{0} Nat) (Multiset.partialOrder.{0} Nat))) (Multiset.range m) (Multiset.range n)) (LE.le.{0} Nat Nat.hasLe m n)
 but is expected to have type
   forall {m : Nat} {n : Nat}, Iff (LE.le.{0} (Multiset.{0} Nat) (Preorder.toLE.{0} (Multiset.{0} Nat) (PartialOrder.toPreorder.{0} (Multiset.{0} Nat) (Multiset.instPartialOrderMultiset.{0} Nat))) (Multiset.range m) (Multiset.range n)) (LE.le.{0} Nat instLENat m n)
 Case conversion may be inaccurate. Consider using '#align multiset.range_le Multiset.range_leₓ'. -/

Changes in mathlib4

mathlib3
mathlib4
chore(Data/List): Use Std lemmas (#11711)

Make use of Nat-specific lemmas from Std rather than the general ones provided by mathlib. Also reverse the dependency between Multiset.Nodup/Multiset.dedup and Multiset.sum since only the latter needs algebra. Also rename Algebra.BigOperators.Multiset.Abs to Algebra.BigOperators.Multiset.Order and move some lemmas from Algebra.BigOperators.Multiset.Basic to it.

The ultimate goal here is to carve out Data, Algebra and Order sublibraries.

Diff
@@ -3,11 +3,8 @@ Copyright (c) 2015 Microsoft Corporation. All rights reserved.
 Released under Apache 2.0 license as described in the file LICENSE.
 Authors: Mario Carneiro
 -/
-import Mathlib.Data.List.Nodup
-import Mathlib.Data.Multiset.Bind
-import Mathlib.Data.Multiset.Range
 import Mathlib.Data.List.Range
-import Mathlib.Init.Classical
+import Mathlib.Data.Multiset.Range
 
 #align_import data.multiset.nodup from "leanprover-community/mathlib"@"f694c7dead66f5d4c80f446c796a5aad14707f0e"
 
@@ -199,17 +196,6 @@ theorem Nodup.not_mem_erase [DecidableEq α] {a : α} {s} (h : Nodup s) : a ∉
   (h.mem_erase_iff.1 ha).1 rfl
 #align multiset.nodup.not_mem_erase Multiset.Nodup.not_mem_erase
 
-protected theorem Nodup.product {t : Multiset β} : Nodup s → Nodup t → Nodup (s ×ˢ t) :=
-  Quotient.inductionOn₂ s t fun l₁ l₂ d₁ d₂ => by simp [List.Nodup.product d₁ d₂]
-#align multiset.nodup.product Multiset.Nodup.product
-
-protected theorem Nodup.sigma {σ : α → Type*} {t : ∀ a, Multiset (σ a)} :
-    Nodup s → (∀ a, Nodup (t a)) → Nodup (s.sigma t) :=
-  Quot.induction_on s fun l₁ => by
-    choose f hf using fun a => Quotient.exists_rep (t a)
-    simpa [← funext hf] using List.Nodup.sigma
-#align multiset.nodup.sigma Multiset.Nodup.sigma
-
 protected theorem Nodup.filterMap (f : α → Option β) (H : ∀ a a' b, b ∈ f a → b ∈ f a' → a = a') :
     Nodup s → Nodup (filterMap f s) :=
   Quot.induction_on s fun _ => List.Nodup.filterMap H
@@ -235,16 +221,6 @@ theorem nodup_union [DecidableEq α] {s t : Multiset α} : Nodup (s ∪ t) ↔ N
       exact max_le (nodup_iff_count_le_one.1 h₁ a) (nodup_iff_count_le_one.1 h₂ a)⟩
 #align multiset.nodup_union Multiset.nodup_union
 
-@[simp]
-theorem nodup_bind {s : Multiset α} {t : α → Multiset β} :
-    Nodup (bind s t) ↔ (∀ a ∈ s, Nodup (t a)) ∧ s.Pairwise fun a b => Disjoint (t a) (t b) :=
-  have h₁ : ∀ a, ∃ l : List β, t a = l := fun a => Quot.induction_on (t a) fun l => ⟨l, rfl⟩
-  let ⟨t', h'⟩ := Classical.axiom_of_choice h₁
-  have : t = fun a => ofList (t' a) := funext h'
-  have hd : Symmetric fun a b => List.Disjoint (t' a) (t' b) := fun a b h => h.symm
-  Quot.induction_on s <| by simp [this, List.nodup_bind, pairwise_coe_iff_pairwise hd]
-#align multiset.nodup_bind Multiset.nodup_bind
-
 theorem Nodup.ext {s t : Multiset α} : Nodup s → Nodup t → (s = t ↔ ∀ a, a ∈ s ↔ a ∈ t) :=
   Quotient.inductionOn₂ s t fun _ _ d₁ d₂ => Quotient.eq.trans <| perm_ext_iff_of_nodup d₁ d₂
 #align multiset.nodup.ext Multiset.Nodup.ext
@@ -262,7 +238,7 @@ theorem mem_sub_of_nodup [DecidableEq α] {a : α} {s t : Multiset α} (d : Nodu
   ⟨fun h =>
     ⟨mem_of_le tsub_le_self h, fun h' => by
       refine' count_eq_zero.1 _ h
-      rw [count_sub a s t, tsub_eq_zero_iff_le]
+      rw [count_sub a s t, Nat.sub_eq_zero_iff_le]
       exact le_trans (nodup_iff_count_le_one.1 d _) (count_pos.2 h')⟩,
     fun ⟨h₁, h₂⟩ => Or.resolve_right (mem_add.1 <| mem_of_le le_tsub_add h₁) h₂⟩
 #align multiset.mem_sub_of_nodup Multiset.mem_sub_of_nodup
chore: remove stream-of-consciousness uses of have, replace and suffices (#10640)

No changes to tactic file, it's just boring fixes throughout the library.

This follows on from #6964.

Co-authored-by: sgouezel <sebastien.gouezel@univ-rennes1.fr> Co-authored-by: Eric Wieser <wieser.eric@gmail.com>

Diff
@@ -271,8 +271,8 @@ theorem map_eq_map_of_bij_of_nodup (f : α → γ) (g : β → γ) {s : Multiset
     (hs : s.Nodup) (ht : t.Nodup) (i : ∀ a ∈ s, β) (hi : ∀ a ha, i a ha ∈ t)
     (i_inj : ∀ a₁ ha₁ a₂ ha₂, i a₁ ha₁ = i a₂ ha₂ → a₁ = a₂)
     (i_surj : ∀ b ∈ t, ∃ a ha, i a ha = b) (h : ∀ a ha, f a = g (i a ha)) : s.map f = t.map g := by
-  have : t = s.attach.map fun x => i x.1 x.2
-  · rw [ht.ext]
+  have : t = s.attach.map fun x => i x.1 x.2 := by
+    rw [ht.ext]
     · aesop
     · exact hs.attach.map fun x y hxy ↦ Subtype.ext <| i_inj _ x.2 _ y.2 hxy
   calc
chore: bump Std (#10482)

Co-authored-by: Scott Morrison <scott.morrison@gmail.com>

Diff
@@ -7,6 +7,7 @@ import Mathlib.Data.List.Nodup
 import Mathlib.Data.Multiset.Bind
 import Mathlib.Data.Multiset.Range
 import Mathlib.Data.List.Range
+import Mathlib.Init.Classical
 
 #align_import data.multiset.nodup from "leanprover-community/mathlib"@"f694c7dead66f5d4c80f446c796a5aad14707f0e"
 
chore: reduce imports (#9830)

This uses the improved shake script from #9772 to reduce imports across mathlib. The corresponding noshake.json file has been added to #9772.

Co-authored-by: Mario Carneiro <di.gama@gmail.com>

Diff
@@ -6,6 +6,7 @@ Authors: Mario Carneiro
 import Mathlib.Data.List.Nodup
 import Mathlib.Data.Multiset.Bind
 import Mathlib.Data.Multiset.Range
+import Mathlib.Data.List.Range
 
 #align_import data.multiset.nodup from "leanprover-community/mathlib"@"f694c7dead66f5d4c80f446c796a5aad14707f0e"
 
chore(*): replace $ with <| (#9319)

See Zulip thread for the discussion.

Diff
@@ -272,7 +272,7 @@ theorem map_eq_map_of_bij_of_nodup (f : α → γ) (g : β → γ) {s : Multiset
   have : t = s.attach.map fun x => i x.1 x.2
   · rw [ht.ext]
     · aesop
-    · exact hs.attach.map fun x y hxy ↦ Subtype.ext $ i_inj _ x.2 _ y.2 hxy
+    · exact hs.attach.map fun x y hxy ↦ Subtype.ext <| i_inj _ x.2 _ y.2 hxy
   calc
     s.map f = s.pmap (fun x _ => f x) fun _ => id := by rw [pmap_eq_map]
     _ = s.attach.map fun x => f x.1 := by rw [pmap_eq_map_attach]
chore(*): drop $/<| before fun (#9361)

Subset of #9319

Diff
@@ -272,7 +272,7 @@ theorem map_eq_map_of_bij_of_nodup (f : α → γ) (g : β → γ) {s : Multiset
   have : t = s.attach.map fun x => i x.1 x.2
   · rw [ht.ext]
     · aesop
-    · exact hs.attach.map $ fun x y hxy ↦ Subtype.ext $ i_inj _ x.2 _ y.2 hxy
+    · exact hs.attach.map fun x y hxy ↦ Subtype.ext $ i_inj _ x.2 _ y.2 hxy
   calc
     s.map f = s.pmap (fun x _ => f x) fun _ => id := by rw [pmap_eq_map]
     _ = s.attach.map fun x => f x.1 := by rw [pmap_eq_map_attach]
feat: Better lemmas for transferring finite sums along equivalences (#9237)

Lemmas around this were a mess, throth in terms of names, statement and location. This PR standardises everything to be in Algebra.BigOperators.Basic and changes the lemmas to take in InjOn and SurjOn assumptions where possible (and where impossible make sure the hypotheses are taken in the correct order) and moves the equality of functions hypothesis last.

Also add a few lemmas that help fix downstream uses by golfing.

From LeanAPAP and LeanCamCombi

Diff
@@ -167,6 +167,8 @@ theorem nodup_attach {s : Multiset α} : Nodup (attach s) ↔ Nodup s :=
   Quot.induction_on s fun _ => List.nodup_attach
 #align multiset.nodup_attach Multiset.nodup_attach
 
+protected alias ⟨_, Nodup.attach⟩ := nodup_attach
+
 theorem Nodup.pmap {p : α → Prop} {f : ∀ a, p a → β} {s : Multiset α} {H}
     (hf : ∀ a ha b hb, f a ha = f b hb → a = b) : Nodup s → Nodup (pmap f s H) :=
   Quot.induction_on s (fun _ _ => List.Nodup.pmap hf) H
@@ -265,16 +267,12 @@ theorem mem_sub_of_nodup [DecidableEq α] {a : α} {s t : Multiset α} (d : Nodu
 
 theorem map_eq_map_of_bij_of_nodup (f : α → γ) (g : β → γ) {s : Multiset α} {t : Multiset β}
     (hs : s.Nodup) (ht : t.Nodup) (i : ∀ a ∈ s, β) (hi : ∀ a ha, i a ha ∈ t)
-    (h : ∀ a ha, f a = g (i a ha)) (i_inj : ∀ a₁ a₂ ha₁ ha₂, i a₁ ha₁ = i a₂ ha₂ → a₁ = a₂)
-    (i_surj : ∀ b ∈ t, ∃ a ha, b = i a ha) : s.map f = t.map g :=
-  have : t = s.attach.map fun x => i x.1 x.2 :=
-    (ht.ext <|
-          (nodup_attach.2 hs).map <|
-            show Injective fun x : { x // x ∈ s } => i x x.2 from fun x y hxy =>
-              Subtype.ext <| i_inj x y x.2 y.2 hxy).2
-      fun x => by
-        simp only [mem_map, true_and_iff, Subtype.exists, eq_comm, mem_attach]
-        exact ⟨i_surj _, fun ⟨y, hy⟩ => hy.snd.symm ▸ hi _ _⟩
+    (i_inj : ∀ a₁ ha₁ a₂ ha₂, i a₁ ha₁ = i a₂ ha₂ → a₁ = a₂)
+    (i_surj : ∀ b ∈ t, ∃ a ha, i a ha = b) (h : ∀ a ha, f a = g (i a ha)) : s.map f = t.map g := by
+  have : t = s.attach.map fun x => i x.1 x.2
+  · rw [ht.ext]
+    · aesop
+    · exact hs.attach.map $ fun x y hxy ↦ Subtype.ext $ i_inj _ x.2 _ y.2 hxy
   calc
     s.map f = s.pmap (fun x _ => f x) fun _ => id := by rw [pmap_eq_map]
     _ = s.attach.map fun x => f x.1 := by rw [pmap_eq_map_attach]
chore: patch std4#89 (#8566)

Co-authored-by: Mario Carneiro <di.gama@gmail.com> Co-authored-by: Tobias Grosser <tobias@grosser.es> Co-authored-by: Scott Morrison <scott.morrison@gmail.com> Co-authored-by: Scott Morrison <scott@tqft.net>

Diff
@@ -242,7 +242,7 @@ theorem nodup_bind {s : Multiset α} {t : α → Multiset β} :
 #align multiset.nodup_bind Multiset.nodup_bind
 
 theorem Nodup.ext {s t : Multiset α} : Nodup s → Nodup t → (s = t ↔ ∀ a, a ∈ s ↔ a ∈ t) :=
-  Quotient.inductionOn₂ s t fun _ _ d₁ d₂ => Quotient.eq.trans <| perm_ext d₁ d₂
+  Quotient.inductionOn₂ s t fun _ _ d₁ d₂ => Quotient.eq.trans <| perm_ext_iff_of_nodup d₁ d₂
 #align multiset.nodup.ext Multiset.Nodup.ext
 
 theorem le_iff_subset {s t : Multiset α} : Nodup s → (s ≤ t ↔ s ⊆ t) :=
chore: space after (#8178)

Co-authored-by: Moritz Firsching <firsching@google.com>

Diff
@@ -203,7 +203,7 @@ protected theorem Nodup.sigma {σ : α → Type*} {t : ∀ a, Multiset (σ a)} :
     Nodup s → (∀ a, Nodup (t a)) → Nodup (s.sigma t) :=
   Quot.induction_on s fun l₁ => by
     choose f hf using fun a => Quotient.exists_rep (t a)
-    simpa [←funext hf] using List.Nodup.sigma
+    simpa [← funext hf] using List.Nodup.sigma
 #align multiset.nodup.sigma Multiset.Nodup.sigma
 
 protected theorem Nodup.filterMap (f : α → Option β) (H : ∀ a a' b, b ∈ f a → b ∈ f a' → a = a') :
feat: add some Multiset.Nodup lemmas (#8464)

Based on results from flt-regular.

Co-authored-by: Andrew Yang <36414270+erdOne@users.noreply.github.com>

Diff
@@ -82,13 +82,16 @@ theorem nodup_iff_ne_cons_cons {s : Multiset α} : s.Nodup ↔ ∀ a t, s ≠ a
 theorem nodup_iff_count_le_one [DecidableEq α] {s : Multiset α} : Nodup s ↔ ∀ a, count a s ≤ 1 :=
   Quot.induction_on s fun _l => by
     simp only [quot_mk_to_coe'', coe_nodup, mem_coe, coe_count]
-    apply List.nodup_iff_count_le_one
+    exact List.nodup_iff_count_le_one
 #align multiset.nodup_iff_count_le_one Multiset.nodup_iff_count_le_one
 
+theorem nodup_iff_count_eq_one [DecidableEq α] : Nodup s ↔ ∀ a ∈ s, count a s = 1 :=
+  Quot.induction_on s fun _l => by simpa using List.nodup_iff_count_eq_one
+
 @[simp]
 theorem count_eq_one_of_mem [DecidableEq α] {a : α} {s : Multiset α} (d : Nodup s) (h : a ∈ s) :
     count a s = 1 :=
-  le_antisymm (nodup_iff_count_le_one.1 d a) (count_pos.2 h)
+  nodup_iff_count_eq_one.mp d a h
 #align multiset.count_eq_one_of_mem Multiset.count_eq_one_of_mem
 
 theorem count_eq_of_nodup [DecidableEq α] {a : α} {s : Multiset α} (d : Nodup s) :
@@ -137,6 +140,14 @@ theorem Nodup.map {f : α → β} {s : Multiset α} (hf : Injective f) : Nodup s
   Nodup.map_on fun _ _ _ _ h => hf h
 #align multiset.nodup.map Multiset.Nodup.map
 
+theorem nodup_map_iff_of_inj_on {f : α → β} (d : ∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y) :
+    Nodup (map f s) ↔ Nodup s :=
+  ⟨Nodup.of_map _, fun h => h.map_on d⟩
+
+theorem nodup_map_iff_of_injective {f : α → β} (d : Function.Injective f) :
+    Nodup (map f s) ↔ Nodup s :=
+  ⟨Nodup.of_map _, fun h => h.map d⟩
+
 theorem inj_on_of_nodup_map {f : α → β} {s : Multiset α} :
     Nodup (map f s) → ∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y :=
   Quot.induction_on s fun _ => List.inj_on_of_nodup_map
chore: banish Type _ and Sort _ (#6499)

We remove all possible occurences of Type _ and Sort _ in favor of Type* and Sort*.

This has nice performance benefits.

Diff
@@ -18,7 +18,7 @@ namespace Multiset
 
 open Function List
 
-variable {α β γ : Type _} {r : α → α → Prop} {s t : Multiset α} {a : α}
+variable {α β γ : Type*} {r : α → α → Prop} {s t : Multiset α} {a : α}
 
 -- nodup
 /-- `Nodup s` means that `s` has no duplicates, i.e. the multiplicity of
@@ -188,7 +188,7 @@ protected theorem Nodup.product {t : Multiset β} : Nodup s → Nodup t → Nodu
   Quotient.inductionOn₂ s t fun l₁ l₂ d₁ d₂ => by simp [List.Nodup.product d₁ d₂]
 #align multiset.nodup.product Multiset.Nodup.product
 
-protected theorem Nodup.sigma {σ : α → Type _} {t : ∀ a, Multiset (σ a)} :
+protected theorem Nodup.sigma {σ : α → Type*} {t : ∀ a, Multiset (σ a)} :
     Nodup s → (∀ a, Nodup (t a)) → Nodup (s.sigma t) :=
   Quot.induction_on s fun l₁ => by
     choose f hf using fun a => Quotient.exists_rep (t a)
chore: script to replace headers with #align_import statements (#5979)

Open in Gitpod

Co-authored-by: Eric Wieser <wieser.eric@gmail.com> Co-authored-by: Scott Morrison <scott.morrison@gmail.com>

Diff
@@ -2,16 +2,13 @@
 Copyright (c) 2015 Microsoft Corporation. All rights reserved.
 Released under Apache 2.0 license as described in the file LICENSE.
 Authors: Mario Carneiro
-
-! This file was ported from Lean 3 source module data.multiset.nodup
-! leanprover-community/mathlib commit f694c7dead66f5d4c80f446c796a5aad14707f0e
-! Please do not edit these lines, except to modify the commit id
-! if you have ported upstream changes.
 -/
 import Mathlib.Data.List.Nodup
 import Mathlib.Data.Multiset.Bind
 import Mathlib.Data.Multiset.Range
 
+#align_import data.multiset.nodup from "leanprover-community/mathlib"@"f694c7dead66f5d4c80f446c796a5aad14707f0e"
+
 /-!
 # The `Nodup` predicate for multisets without duplicate elements.
 -/
refactor: use the typeclass SProd to implement overloaded notation · ×ˢ · (#4200)

Currently, the following notations are changed from · ×ˢ · because Lean 4 can't deal with ambiguous notations. | Definition | Notation | | :

Co-authored-by: Jeremy Tan Jie Rui <reddeloostw@gmail.com> Co-authored-by: Kyle Miller <kmill31415@gmail.com> Co-authored-by: Chris Hughes <chrishughes24@gmail.com>

Diff
@@ -187,7 +187,7 @@ theorem Nodup.not_mem_erase [DecidableEq α] {a : α} {s} (h : Nodup s) : a ∉
   (h.mem_erase_iff.1 ha).1 rfl
 #align multiset.nodup.not_mem_erase Multiset.Nodup.not_mem_erase
 
-protected theorem Nodup.product {t : Multiset β} : Nodup s → Nodup t → Nodup (product s t) :=
+protected theorem Nodup.product {t : Multiset β} : Nodup s → Nodup t → Nodup (s ×ˢ t) :=
   Quotient.inductionOn₂ s t fun l₁ l₂ d₁ d₂ => by simp [List.Nodup.product d₁ d₂]
 #align multiset.nodup.product Multiset.Nodup.product
 
chore: bye-bye, solo bys! (#3825)

This PR puts, with one exception, every single remaining by that lies all by itself on its own line to the previous line, thus matching the current behaviour of start-port.sh. The exception is when the by begins the second or later argument to a tuple or anonymous constructor; see https://github.com/leanprover-community/mathlib4/pull/3825#discussion_r1186702599.

Essentially this is s/\n *by$/ by/g, but with manual editing to satisfy the linter's max-100-char-line requirement. The Python style linter is also modified to catch these "isolated bys".

Diff
@@ -95,8 +95,7 @@ theorem count_eq_one_of_mem [DecidableEq α] {a : α} {s : Multiset α} (d : Nod
 #align multiset.count_eq_one_of_mem Multiset.count_eq_one_of_mem
 
 theorem count_eq_of_nodup [DecidableEq α] {a : α} {s : Multiset α} (d : Nodup s) :
-    count a s = if a ∈ s then 1 else 0 :=
-  by
+    count a s = if a ∈ s then 1 else 0 := by
   split_ifs with h
   · exact count_eq_one_of_mem d h
   · exact count_eq_zero_of_not_mem h
chore: fix #align lines (#3640)

This PR fixes two things:

  • Most align statements for definitions and theorems and instances that are separated by two newlines from the relevant declaration (s/\n\n#align/\n#align). This is often seen in the mathport output after ending calc blocks.
  • All remaining more-than-one-line #align statements. (This was needed for a script I wrote for #3630.)
Diff
@@ -272,7 +272,6 @@ theorem map_eq_map_of_bij_of_nodup (f : α → γ) (g : β → γ) {s : Multiset
     s.map f = s.pmap (fun x _ => f x) fun _ => id := by rw [pmap_eq_map]
     _ = s.attach.map fun x => f x.1 := by rw [pmap_eq_map_attach]
     _ = t.map g := by rw [this, Multiset.map_map]; exact map_congr rfl fun x _ => h _ _
-
 #align multiset.map_eq_map_of_bij_of_nodup Multiset.map_eq_map_of_bij_of_nodup
 
 end Multiset
fix: restore mathlib3 definition of Multiset.count (#3088)

Previously discussed in https://leanprover.zulipchat.com/#narrow/stream/287929-mathlib4/topic/count_eq_card_filter_eq

Diff
@@ -83,7 +83,9 @@ theorem nodup_iff_ne_cons_cons {s : Multiset α} : s.Nodup ↔ ∀ a t, s ≠ a
 #align multiset.nodup_iff_ne_cons_cons Multiset.nodup_iff_ne_cons_cons
 
 theorem nodup_iff_count_le_one [DecidableEq α] {s : Multiset α} : Nodup s ↔ ∀ a, count a s ≤ 1 :=
-  Quot.induction_on s fun _l => List.nodup_iff_count_le_one
+  Quot.induction_on s fun _l => by
+    simp only [quot_mk_to_coe'', coe_nodup, mem_coe, coe_count]
+    apply List.nodup_iff_count_le_one
 #align multiset.nodup_iff_count_le_one Multiset.nodup_iff_count_le_one
 
 @[simp]
Diff
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
 Authors: Mario Carneiro
 
 ! This file was ported from Lean 3 source module data.multiset.nodup
-! leanprover-community/mathlib commit 9003f28797c0664a49e4179487267c494477d853
+! leanprover-community/mathlib commit f694c7dead66f5d4c80f446c796a5aad14707f0e
 ! Please do not edit these lines, except to modify the commit id
 ! if you have ported upstream changes.
 -/
chore: fix name of Multiset.Nodup.filterMap (#1736)
Diff
@@ -197,10 +197,10 @@ protected theorem Nodup.sigma {σ : α → Type _} {t : ∀ a, Multiset (σ a)}
     simpa [←funext hf] using List.Nodup.sigma
 #align multiset.nodup.sigma Multiset.Nodup.sigma
 
-protected theorem Nodup.filter_map (f : α → Option β) (H : ∀ a a' b, b ∈ f a → b ∈ f a' → a = a') :
+protected theorem Nodup.filterMap (f : α → Option β) (H : ∀ a a' b, b ∈ f a → b ∈ f a' → a = a') :
     Nodup s → Nodup (filterMap f s) :=
-  Quot.induction_on s fun _ => Nodup.filterMap H
-#align multiset.nodup.filter_map Multiset.Nodup.filter_map
+  Quot.induction_on s fun _ => List.Nodup.filterMap H
+#align multiset.nodup.filter_map Multiset.Nodup.filterMap
 
 theorem nodup_range (n : ℕ) : Nodup (range n) :=
   List.nodup_range _
chore: revert Multiset and Finset API to use Prop instead of Bool (#1652)

Co-authored-by: Reid Barton <rwbarton@gmail.com>

Diff
@@ -149,8 +149,8 @@ theorem nodup_map_iff_inj_on {f : α → β} {s : Multiset α} (d : Nodup s) :
   ⟨inj_on_of_nodup_map, fun h => d.map_on h⟩
 #align multiset.nodup_map_iff_inj_on Multiset.nodup_map_iff_inj_on
 
-theorem Nodup.filter (p : α → Bool) {s} : Nodup s → Nodup (filter p s) :=
-  Quot.induction_on s fun _ => List.Nodup.filter p
+theorem Nodup.filter (p : α → Prop) [DecidablePred p] {s} : Nodup s → Nodup (filter p s) :=
+  Quot.induction_on s fun _ => List.Nodup.filter (p ·)
 #align multiset.nodup.filter Multiset.Nodup.filter
 
 @[simp]
@@ -180,7 +180,6 @@ theorem Nodup.erase [DecidableEq α] (a : α) {l} : Nodup l → Nodup (l.erase a
 theorem Nodup.mem_erase_iff [DecidableEq α] {a b : α} {l} (d : Nodup l) :
     a ∈ l.erase b ↔ a ≠ b ∧ a ∈ l := by
   rw [d.erase_eq_filter b, mem_filter, and_comm]
-  simp
 #align multiset.nodup.mem_erase_iff Multiset.Nodup.mem_erase_iff
 
 theorem Nodup.not_mem_erase [DecidableEq α] {a : α} {s} (h : Nodup s) : a ∉ s.erase a := fun ha =>
Refactor: drop List.repeat (#1579)

Mathlib 3 migrated to list.replicate in leanprover-community/mathlib#18127 (merged) and leanprover-community/mathlib#18181 (awaiting review).

Diff
@@ -72,7 +72,7 @@ theorem not_nodup_pair : ∀ a : α, ¬Nodup (a ::ₘ a ::ₘ 0) :=
 
 theorem nodup_iff_le {s : Multiset α} : Nodup s ↔ ∀ a : α, ¬a ::ₘ a ::ₘ 0 ≤ s :=
   Quot.induction_on s fun _ =>
-    nodup_iff_sublist.trans <| forall_congr' fun a => not_congr (@repeat_le_coe _ a 2 _).symm
+    nodup_iff_sublist.trans <| forall_congr' fun a => not_congr (@replicate_le_coe _ a 2 _).symm
 #align multiset.nodup_iff_le Multiset.nodup_iff_le
 
 theorem nodup_iff_ne_cons_cons {s : Multiset α} : s.Nodup ↔ ∀ a t, s ≠ a ::ₘ a ::ₘ t :=
feat: port Data.Multiset.Nodup (#1539)
Diff
@@ -2,10 +2,18 @@
 Copyright (c) 2015 Microsoft Corporation. All rights reserved.
 Released under Apache 2.0 license as described in the file LICENSE.
 Authors: Mario Carneiro
+
+! This file was ported from Lean 3 source module data.multiset.nodup
+! leanprover-community/mathlib commit 9003f28797c0664a49e4179487267c494477d853
+! Please do not edit these lines, except to modify the commit id
+! if you have ported upstream changes.
 -/
-import Mathlib.Data.Multiset.Basic
+import Mathlib.Data.List.Nodup
+import Mathlib.Data.Multiset.Bind
+import Mathlib.Data.Multiset.Range
+
 /-!
-# The `nodup` predicate for multisets without duplicate elements.
+# The `Nodup` predicate for multisets without duplicate elements.
 -/
 
 
@@ -15,7 +23,255 @@ open Function List
 
 variable {α β γ : Type _} {r : α → α → Prop} {s t : Multiset α} {a : α}
 
-/-- `nodup s` means that `s` has no duplicates, i.e. the multiplicity of
+-- nodup
+/-- `Nodup s` means that `s` has no duplicates, i.e. the multiplicity of
   any element is at most 1. -/
 def Nodup (s : Multiset α) : Prop :=
-  Quot.liftOn s List.Nodup fun _ _ p ↦ propext p.nodup_iff
+  Quot.liftOn s List.Nodup fun _ _ p => propext p.nodup_iff
+#align multiset.nodup Multiset.Nodup
+
+@[simp]
+theorem coe_nodup {l : List α} : @Nodup α l ↔ l.Nodup :=
+  Iff.rfl
+#align multiset.coe_nodup Multiset.coe_nodup
+
+@[simp]
+theorem nodup_zero : @Nodup α 0 :=
+  Pairwise.nil
+#align multiset.nodup_zero Multiset.nodup_zero
+
+@[simp]
+theorem nodup_cons {a : α} {s : Multiset α} : Nodup (a ::ₘ s) ↔ a ∉ s ∧ Nodup s :=
+  Quot.induction_on s fun _ => List.nodup_cons
+#align multiset.nodup_cons Multiset.nodup_cons
+
+theorem Nodup.cons (m : a ∉ s) (n : Nodup s) : Nodup (a ::ₘ s) :=
+  nodup_cons.2 ⟨m, n⟩
+#align multiset.nodup.cons Multiset.Nodup.cons
+
+@[simp]
+theorem nodup_singleton : ∀ a : α, Nodup ({a} : Multiset α) :=
+  List.nodup_singleton
+#align multiset.nodup_singleton Multiset.nodup_singleton
+
+theorem Nodup.of_cons (h : Nodup (a ::ₘ s)) : Nodup s :=
+  (nodup_cons.1 h).2
+#align multiset.nodup.of_cons Multiset.Nodup.of_cons
+
+theorem Nodup.not_mem (h : Nodup (a ::ₘ s)) : a ∉ s :=
+  (nodup_cons.1 h).1
+#align multiset.nodup.not_mem Multiset.Nodup.not_mem
+
+theorem nodup_of_le {s t : Multiset α} (h : s ≤ t) : Nodup t → Nodup s :=
+  Multiset.leInductionOn h fun {_ _} => Nodup.sublist
+#align multiset.nodup_of_le Multiset.nodup_of_le
+
+theorem not_nodup_pair : ∀ a : α, ¬Nodup (a ::ₘ a ::ₘ 0) :=
+  List.not_nodup_pair
+#align multiset.not_nodup_pair Multiset.not_nodup_pair
+
+theorem nodup_iff_le {s : Multiset α} : Nodup s ↔ ∀ a : α, ¬a ::ₘ a ::ₘ 0 ≤ s :=
+  Quot.induction_on s fun _ =>
+    nodup_iff_sublist.trans <| forall_congr' fun a => not_congr (@repeat_le_coe _ a 2 _).symm
+#align multiset.nodup_iff_le Multiset.nodup_iff_le
+
+theorem nodup_iff_ne_cons_cons {s : Multiset α} : s.Nodup ↔ ∀ a t, s ≠ a ::ₘ a ::ₘ t :=
+  nodup_iff_le.trans
+    ⟨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 =>
+      let ⟨t, s_eq⟩ := le_iff_exists_add.mp le
+      h a t (by rwa [cons_add, cons_add, zero_add] at s_eq)⟩
+#align multiset.nodup_iff_ne_cons_cons Multiset.nodup_iff_ne_cons_cons
+
+theorem nodup_iff_count_le_one [DecidableEq α] {s : Multiset α} : Nodup s ↔ ∀ a, count a s ≤ 1 :=
+  Quot.induction_on s fun _l => List.nodup_iff_count_le_one
+#align multiset.nodup_iff_count_le_one Multiset.nodup_iff_count_le_one
+
+@[simp]
+theorem count_eq_one_of_mem [DecidableEq α] {a : α} {s : Multiset α} (d : Nodup s) (h : a ∈ s) :
+    count a s = 1 :=
+  le_antisymm (nodup_iff_count_le_one.1 d a) (count_pos.2 h)
+#align multiset.count_eq_one_of_mem Multiset.count_eq_one_of_mem
+
+theorem count_eq_of_nodup [DecidableEq α] {a : α} {s : Multiset α} (d : Nodup s) :
+    count a s = if a ∈ s then 1 else 0 :=
+  by
+  split_ifs with h
+  · exact count_eq_one_of_mem d h
+  · exact count_eq_zero_of_not_mem h
+#align multiset.count_eq_of_nodup Multiset.count_eq_of_nodup
+
+theorem nodup_iff_pairwise {α} {s : Multiset α} : Nodup s ↔ Pairwise (· ≠ ·) s :=
+  Quotient.inductionOn s fun _ => (pairwise_coe_iff_pairwise fun _ _ => Ne.symm).symm
+#align multiset.nodup_iff_pairwise Multiset.nodup_iff_pairwise
+
+protected theorem Nodup.pairwise : (∀ a ∈ s, ∀ b ∈ s, a ≠ b → r a b) → Nodup s → Pairwise r s :=
+  Quotient.inductionOn s fun l h hl => ⟨l, rfl, hl.imp_of_mem fun {a b} ha hb => h a ha b hb⟩
+#align multiset.nodup.pairwise Multiset.Nodup.pairwise
+
+theorem Pairwise.forall (H : Symmetric r) (hs : Pairwise r s) :
+    ∀ ⦃a⦄, a ∈ s → ∀ ⦃b⦄, b ∈ s → a ≠ b → r a b :=
+  let ⟨_, hl₁, hl₂⟩ := hs
+  hl₁.symm ▸ hl₂.forall H
+#align multiset.pairwise.forall Multiset.Pairwise.forall
+
+theorem nodup_add {s t : Multiset α} : Nodup (s + t) ↔ Nodup s ∧ Nodup t ∧ Disjoint s t :=
+  Quotient.inductionOn₂ s t fun _ _ => nodup_append
+#align multiset.nodup_add Multiset.nodup_add
+
+theorem disjoint_of_nodup_add {s t : Multiset α} (d : Nodup (s + t)) : Disjoint s t :=
+  (nodup_add.1 d).2.2
+#align multiset.disjoint_of_nodup_add Multiset.disjoint_of_nodup_add
+
+theorem Nodup.add_iff (d₁ : Nodup s) (d₂ : Nodup t) : Nodup (s + t) ↔ Disjoint s t := by
+  simp [nodup_add, d₁, d₂]
+#align multiset.nodup.add_iff Multiset.Nodup.add_iff
+
+theorem Nodup.of_map (f : α → β) : Nodup (map f s) → Nodup s :=
+  Quot.induction_on s fun _ => List.Nodup.of_map f
+#align multiset.nodup.of_map Multiset.Nodup.of_map
+
+theorem Nodup.map_on {f : α → β} :
+    (∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y) → Nodup s → Nodup (map f s) :=
+  Quot.induction_on s fun _ => List.Nodup.map_on
+#align multiset.nodup.map_on Multiset.Nodup.map_on
+
+theorem Nodup.map {f : α → β} {s : Multiset α} (hf : Injective f) : Nodup s → Nodup (map f s) :=
+  Nodup.map_on fun _ _ _ _ h => hf h
+#align multiset.nodup.map Multiset.Nodup.map
+
+theorem inj_on_of_nodup_map {f : α → β} {s : Multiset α} :
+    Nodup (map f s) → ∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y :=
+  Quot.induction_on s fun _ => List.inj_on_of_nodup_map
+#align multiset.inj_on_of_nodup_map Multiset.inj_on_of_nodup_map
+
+theorem nodup_map_iff_inj_on {f : α → β} {s : Multiset α} (d : Nodup s) :
+    Nodup (map f s) ↔ ∀ x ∈ s, ∀ y ∈ s, f x = f y → x = y :=
+  ⟨inj_on_of_nodup_map, fun h => d.map_on h⟩
+#align multiset.nodup_map_iff_inj_on Multiset.nodup_map_iff_inj_on
+
+theorem Nodup.filter (p : α → Bool) {s} : Nodup s → Nodup (filter p s) :=
+  Quot.induction_on s fun _ => List.Nodup.filter p
+#align multiset.nodup.filter Multiset.Nodup.filter
+
+@[simp]
+theorem nodup_attach {s : Multiset α} : Nodup (attach s) ↔ Nodup s :=
+  Quot.induction_on s fun _ => List.nodup_attach
+#align multiset.nodup_attach Multiset.nodup_attach
+
+theorem Nodup.pmap {p : α → Prop} {f : ∀ a, p a → β} {s : Multiset α} {H}
+    (hf : ∀ a ha b hb, f a ha = f b hb → a = b) : Nodup s → Nodup (pmap f s H) :=
+  Quot.induction_on s (fun _ _ => List.Nodup.pmap hf) H
+#align multiset.nodup.pmap Multiset.Nodup.pmap
+
+instance nodupDecidable [DecidableEq α] (s : Multiset α) : Decidable (Nodup s) :=
+  Quotient.recOnSubsingleton s fun l => l.nodupDecidable
+#align multiset.nodup_decidable Multiset.nodupDecidable
+
+theorem Nodup.erase_eq_filter [DecidableEq α] (a : α) {s} :
+    Nodup s → s.erase a = Multiset.filter (· ≠ a) s :=
+  Quot.induction_on s fun _ d =>
+    congr_arg ((↑) : List α → Multiset α) <| List.Nodup.erase_eq_filter d a
+#align multiset.nodup.erase_eq_filter Multiset.Nodup.erase_eq_filter
+
+theorem Nodup.erase [DecidableEq α] (a : α) {l} : Nodup l → Nodup (l.erase a) :=
+  nodup_of_le (erase_le _ _)
+#align multiset.nodup.erase Multiset.Nodup.erase
+
+theorem Nodup.mem_erase_iff [DecidableEq α] {a b : α} {l} (d : Nodup l) :
+    a ∈ l.erase b ↔ a ≠ b ∧ a ∈ l := by
+  rw [d.erase_eq_filter b, mem_filter, and_comm]
+  simp
+#align multiset.nodup.mem_erase_iff Multiset.Nodup.mem_erase_iff
+
+theorem Nodup.not_mem_erase [DecidableEq α] {a : α} {s} (h : Nodup s) : a ∉ s.erase a := fun ha =>
+  (h.mem_erase_iff.1 ha).1 rfl
+#align multiset.nodup.not_mem_erase Multiset.Nodup.not_mem_erase
+
+protected theorem Nodup.product {t : Multiset β} : Nodup s → Nodup t → Nodup (product s t) :=
+  Quotient.inductionOn₂ s t fun l₁ l₂ d₁ d₂ => by simp [List.Nodup.product d₁ d₂]
+#align multiset.nodup.product Multiset.Nodup.product
+
+protected theorem Nodup.sigma {σ : α → Type _} {t : ∀ a, Multiset (σ a)} :
+    Nodup s → (∀ a, Nodup (t a)) → Nodup (s.sigma t) :=
+  Quot.induction_on s fun l₁ => by
+    choose f hf using fun a => Quotient.exists_rep (t a)
+    simpa [←funext hf] using List.Nodup.sigma
+#align multiset.nodup.sigma Multiset.Nodup.sigma
+
+protected theorem Nodup.filter_map (f : α → Option β) (H : ∀ a a' b, b ∈ f a → b ∈ f a' → a = a') :
+    Nodup s → Nodup (filterMap f s) :=
+  Quot.induction_on s fun _ => Nodup.filterMap H
+#align multiset.nodup.filter_map Multiset.Nodup.filter_map
+
+theorem nodup_range (n : ℕ) : Nodup (range n) :=
+  List.nodup_range _
+#align multiset.nodup_range Multiset.nodup_range
+
+theorem Nodup.inter_left [DecidableEq α] (t) : Nodup s → Nodup (s ∩ t) :=
+  nodup_of_le <| inter_le_left _ _
+#align multiset.nodup.inter_left Multiset.Nodup.inter_left
+
+theorem Nodup.inter_right [DecidableEq α] (s) : Nodup t → Nodup (s ∩ t) :=
+  nodup_of_le <| inter_le_right _ _
+#align multiset.nodup.inter_right Multiset.Nodup.inter_right
+
+@[simp]
+theorem nodup_union [DecidableEq α] {s t : Multiset α} : Nodup (s ∪ t) ↔ Nodup s ∧ Nodup t :=
+  ⟨fun h => ⟨nodup_of_le (le_union_left _ _) h, nodup_of_le (le_union_right _ _) h⟩, fun ⟨h₁, h₂⟩ =>
+    nodup_iff_count_le_one.2 fun a => by
+      rw [count_union]
+      exact max_le (nodup_iff_count_le_one.1 h₁ a) (nodup_iff_count_le_one.1 h₂ a)⟩
+#align multiset.nodup_union Multiset.nodup_union
+
+@[simp]
+theorem nodup_bind {s : Multiset α} {t : α → Multiset β} :
+    Nodup (bind s t) ↔ (∀ a ∈ s, Nodup (t a)) ∧ s.Pairwise fun a b => Disjoint (t a) (t b) :=
+  have h₁ : ∀ a, ∃ l : List β, t a = l := fun a => Quot.induction_on (t a) fun l => ⟨l, rfl⟩
+  let ⟨t', h'⟩ := Classical.axiom_of_choice h₁
+  have : t = fun a => ofList (t' a) := funext h'
+  have hd : Symmetric fun a b => List.Disjoint (t' a) (t' b) := fun a b h => h.symm
+  Quot.induction_on s <| by simp [this, List.nodup_bind, pairwise_coe_iff_pairwise hd]
+#align multiset.nodup_bind Multiset.nodup_bind
+
+theorem Nodup.ext {s t : Multiset α} : Nodup s → Nodup t → (s = t ↔ ∀ a, a ∈ s ↔ a ∈ t) :=
+  Quotient.inductionOn₂ s t fun _ _ d₁ d₂ => Quotient.eq.trans <| perm_ext d₁ d₂
+#align multiset.nodup.ext Multiset.Nodup.ext
+
+theorem le_iff_subset {s t : Multiset α} : Nodup s → (s ≤ t ↔ s ⊆ t) :=
+  Quotient.inductionOn₂ s t fun _ _ d => ⟨subset_of_le, d.subperm⟩
+#align multiset.le_iff_subset Multiset.le_iff_subset
+
+theorem range_le {m n : ℕ} : range m ≤ range n ↔ m ≤ n :=
+  (le_iff_subset (nodup_range _)).trans range_subset
+#align multiset.range_le Multiset.range_le
+
+theorem mem_sub_of_nodup [DecidableEq α] {a : α} {s t : Multiset α} (d : Nodup s) :
+    a ∈ s - t ↔ a ∈ s ∧ a ∉ t :=
+  ⟨fun h =>
+    ⟨mem_of_le tsub_le_self h, fun h' => by
+      refine' count_eq_zero.1 _ h
+      rw [count_sub a s t, tsub_eq_zero_iff_le]
+      exact le_trans (nodup_iff_count_le_one.1 d _) (count_pos.2 h')⟩,
+    fun ⟨h₁, h₂⟩ => Or.resolve_right (mem_add.1 <| mem_of_le le_tsub_add h₁) h₂⟩
+#align multiset.mem_sub_of_nodup Multiset.mem_sub_of_nodup
+
+theorem map_eq_map_of_bij_of_nodup (f : α → γ) (g : β → γ) {s : Multiset α} {t : Multiset β}
+    (hs : s.Nodup) (ht : t.Nodup) (i : ∀ a ∈ s, β) (hi : ∀ a ha, i a ha ∈ t)
+    (h : ∀ a ha, f a = g (i a ha)) (i_inj : ∀ a₁ a₂ ha₁ ha₂, i a₁ ha₁ = i a₂ ha₂ → a₁ = a₂)
+    (i_surj : ∀ b ∈ t, ∃ a ha, b = i a ha) : s.map f = t.map g :=
+  have : t = s.attach.map fun x => i x.1 x.2 :=
+    (ht.ext <|
+          (nodup_attach.2 hs).map <|
+            show Injective fun x : { x // x ∈ s } => i x x.2 from fun x y hxy =>
+              Subtype.ext <| i_inj x y x.2 y.2 hxy).2
+      fun x => by
+        simp only [mem_map, true_and_iff, Subtype.exists, eq_comm, mem_attach]
+        exact ⟨i_surj _, fun ⟨y, hy⟩ => hy.snd.symm ▸ hi _ _⟩
+  calc
+    s.map f = s.pmap (fun x _ => f x) fun _ => id := by rw [pmap_eq_map]
+    _ = s.attach.map fun x => f x.1 := by rw [pmap_eq_map_attach]
+    _ = t.map g := by rw [this, Multiset.map_map]; exact map_congr rfl fun x _ => h _ _
+
+#align multiset.map_eq_map_of_bij_of_nodup Multiset.map_eq_map_of_bij_of_nodup
+
+end Multiset

Dependencies 2 + 147

148 files ported (98.7%)
67082 lines ported (99.8%)
Show graph

The unported dependencies are