l3lab/ntp-mathlib huggingface dataset:
set_option linter.deprecated false in
@[deprecated not_nodup_of_get_eq_of_ne]
theorem nthLe_eq_of_ne_imp_not_nodup (xs : List α) (n m : ℕ) (hn : n < xs.length)
    (hm : m < xs.length) (h : xs.nthLe n hn = xs.nthLe m hm) (hne : n ≠ m) : ¬Nodup xs := by
  rw [nodup_iff_nthLe_inj]
  simp only [exists_prop, exists_and_right, not_forall]
  

Proof state:
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a b : α
xs : List α
n m : ℕ
hn : n < length xs
hm : m < length xs
h : nthLe xs n hn = nthLe xs m hm
hne : n ≠ m
⊢ ∃ x x_1,
    (∃ (x_2 : x < length xs) (x_3 : x_1 < length xs),
        nthLe xs x (_ : x < length xs) = nthLe xs x_1 (_ : x_1 < length xs)) ∧
      ¬x = x_1

Next tactic:
exact ⟨n, m, ⟨hn, hm, h⟩, hne⟩
theorem get_indexOf [DecidableEq α] {l : List α} (H : Nodup l) (i : Fin l.length) :
    indexOf (get l i) l = i :=
  suffices (⟨indexOf (get l i) l, indexOf_lt_length.2 (get_mem _ _ _)⟩ : Fin l.length) = i
    from Fin.veq_of_eq this
  nodup_iff_injective_get.1 H (by 

Proof state:
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a b : α
inst✝ : DecidableEq α
l : List α
H : Nodup l
i : Fin (length l)
⊢ get l { val := indexOf (get l i) l, isLt := (_ : indexOf (get l i) l < length l) } = get l i

Next tactic:
simp
theorem nodup_replicate (a : α) : ∀ {n : ℕ}, Nodup (replicate n a) ↔ n ≤ 1
  | 0 => by 

Proof state:
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a✝ b a : α
⊢ Nodup (replicate 0 a) ↔ 0 ≤ 1

Next tactic:
simp [Nat.zero_le]
theorem nodup_replicate (a : α) : ∀ {n : ℕ}, Nodup (replicate n a) ↔ n ≤ 1
  | 0 => by simp [Nat.zero_le]
  | 1 => by 

Proof state:
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a✝ b a : α
⊢ Nodup (replicate 1 a) ↔ 1 ≤ 1

Next tactic:
simp
theorem count_eq_of_nodup [DecidableEq α] {a : α} {l : List α} (d : Nodup l) :
    count a l = if a ∈ l then 1 else 0 := by
  

Proof state:
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a✝ b : α
inst✝ : DecidableEq α
a : α
l : List α
d : Nodup l
⊢ count a l = if a ∈ l then 1 else 0

Next tactic:
split_ifs with h
theorem count_eq_of_nodup [DecidableEq α] {a : α} {l : List α} (d : Nodup l) :
    count a l = if a ∈ l then 1 else 0 := by
  split_ifs with h
  · 

Proof state:
case pos
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a✝ b : α
inst✝ : DecidableEq α
a : α
l : List α
d : Nodup l
h : a ∈ l
⊢ count a l = 1

Next tactic:
exact count_eq_one_of_mem d h
theorem count_eq_of_nodup [DecidableEq α] {a : α} {l : List α} (d : Nodup l) :
    count a l = if a ∈ l then 1 else 0 := by
  split_ifs with h
  · exact count_eq_one_of_mem d h
  · 

Proof state:
case neg
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a✝ b : α
inst✝ : DecidableEq α
a : α
l : List α
d : Nodup l
h : a ∉ l
⊢ count a l = 0

Next tactic:
exact count_eq_zero_of_not_mem h
theorem nodup_append {l₁ l₂ : List α} : Nodup (l₁ ++ l₂) ↔ Nodup l₁ ∧ Nodup l₂ ∧ Disjoint l₁ l₂ :=
  by 

Proof state:
α : Type u
β : Type v
l l₁✝ l₂✝ : List α
r : α → α → Prop
a b : α
l₁ l₂ : List α
⊢ Nodup (l₁ ++ l₂) ↔ Nodup l₁ ∧ Nodup l₂ ∧ Disjoint l₁ l₂

Next tactic:
simp only [Nodup, pairwise_append, disjoint_iff_ne]
theorem nodup_append_comm {l₁ l₂ : List α} : Nodup (l₁ ++ l₂) ↔ Nodup (l₂ ++ l₁) := by
  

Proof state:
α : Type u
β : Type v
l l₁✝ l₂✝ : List α
r : α → α → Prop
a b : α
l₁ l₂ : List α
⊢ Nodup (l₁ ++ l₂) ↔ Nodup (l₂ ++ l₁)

Next tactic:
simp only [nodup_append, and_left_comm, disjoint_comm]
theorem nodup_middle {a : α} {l₁ l₂ : List α} :
    Nodup (l₁ ++ a :: l₂) ↔ Nodup (a :: (l₁ ++ l₂)) := by
  

Proof state:
α : Type u
β : Type v
l l₁✝ l₂✝ : List α
r : α → α → Prop
a✝ b a : α
l₁ l₂ : List α
⊢ Nodup (l₁ ++ a :: l₂) ↔ Nodup (a :: (l₁ ++ l₂))

Next tactic:
simp only [nodup_append, not_or, and_left_comm, and_assoc, nodup_cons, mem_append,
    disjoint_cons_right]
theorem inj_on_of_nodup_map {f : α → β} {l : List α} (d : Nodup (map f l)) :
    ∀ ⦃x⦄, x ∈ l → ∀ ⦃y⦄, y ∈ l → f x = f y → x = y := by
  

Proof state:
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a b : α
f : α → β
l : List α
d : Nodup (map f l)
⊢ ∀ ⦃x : α⦄, x ∈ l → ∀ ⦃y : α⦄, y ∈ l → f x = f y → x = y

Next tactic:
induction' l with hd tl ih
theorem inj_on_of_nodup_map {f : α → β} {l : List α} (d : Nodup (map f l)) :
    ∀ ⦃x⦄, x ∈ l → ∀ ⦃y⦄, y ∈ l → f x = f y → x = y := by
  induction' l with hd tl ih
  · 

Proof state:
case nil
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a b : α
f : α → β
d : Nodup (map f [])
⊢ ∀ ⦃x : α⦄, x ∈ [] → ∀ ⦃y : α⦄, y ∈ [] → f x = f y → x = y

Next tactic:
simp
theorem inj_on_of_nodup_map {f : α → β} {l : List α} (d : Nodup (map f l)) :
    ∀ ⦃x⦄, x ∈ l → ∀ ⦃y⦄, y ∈ l → f x = f y → x = y := by
  induction' l with hd tl ih
  · simp
  · 

Proof state:
case cons
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a b : α
f : α → β
hd : α
tl : List α
ih : Nodup (map f tl) → ∀ ⦃x : α⦄, x ∈ tl → ∀ ⦃y : α⦄, y ∈ tl → f x = f y → x = y
d : Nodup (map f (hd :: tl))
⊢ ∀ ⦃x : α⦄, x ∈ hd :: tl → ∀ ⦃y : α⦄, y ∈ hd :: tl → f x = f y → x = y

Next tactic:
simp only [map, nodup_cons, mem_map, not_exists, not_and, ← Ne.def] at d
theorem inj_on_of_nodup_map {f : α → β} {l : List α} (d : Nodup (map f l)) :
    ∀ ⦃x⦄, x ∈ l → ∀ ⦃y⦄, y ∈ l → f x = f y → x = y := by
  induction' l with hd tl ih
  · simp
  · simp only [map, nodup_cons, mem_map, not_exists, not_and, ← Ne.def] at d
    

Proof state:
case cons
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a b : α
f : α → β
hd : α
tl : List α
ih : Nodup (map f tl) → ∀ ⦃x : α⦄, x ∈ tl → ∀ ⦃y : α⦄, y ∈ tl → f x = f y → x = y
d : (∀ x ∈ tl, f x ≠ f hd) ∧ Nodup (map f tl)
⊢ ∀ ⦃x : α⦄, x ∈ hd :: tl → ∀ ⦃y : α⦄, y ∈ hd :: tl → f x = f y → x = y

Next tactic:
simp only [mem_cons]
theorem inj_on_of_nodup_map {f : α → β} {l : List α} (d : Nodup (map f l)) :
    ∀ ⦃x⦄, x ∈ l → ∀ ⦃y⦄, y ∈ l → f x = f y → x = y := by
  induction' l with hd tl ih
  · simp
  · simp only [map, nodup_cons, mem_map, not_exists, not_and, ← Ne.def] at d
    simp only [mem_cons]
    

Proof state:
case cons
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a b : α
f : α → β
hd : α
tl : List α
ih : Nodup (map f tl) → ∀ ⦃x : α⦄, x ∈ tl → ∀ ⦃y : α⦄, y ∈ tl → f x = f y → x = y
d : (∀ x ∈ tl, f x ≠ f hd) ∧ Nodup (map f tl)
⊢ ∀ ⦃x : α⦄, x = hd ∨ x ∈ tl → ∀ ⦃y : α⦄, y = hd ∨ y ∈ tl → f x = f y → x = y

Next tactic:
rintro _ (rfl | h₁) _ (rfl | h₂) h₃
theorem inj_on_of_nodup_map {f : α → β} {l : List α} (d : Nodup (map f l)) :
    ∀ ⦃x⦄, x ∈ l → ∀ ⦃y⦄, y ∈ l → f x = f y → x = y := by
  induction' l with hd tl ih
  · simp
  · simp only [map, nodup_cons, mem_map, not_exists, not_and, ← Ne.def] at d
    simp only [mem_cons]
    rintro _ (rfl | h₁) _ (rfl | h₂) h₃
    · 

Proof state:
case cons.inl.inl
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a b : α
f : α → β
tl : List α
ih : Nodup (map f tl) → ∀ ⦃x : α⦄, x ∈ tl → ∀ ⦃y : α⦄, y ∈ tl → f x = f y → x = y
y✝ : α
d : (∀ x ∈ tl, f x ≠ f y✝) ∧ Nodup (map f tl)
h₃ : f y✝ = f y✝
⊢ y✝ = y✝

Next tactic:
rfl
theorem inj_on_of_nodup_map {f : α → β} {l : List α} (d : Nodup (map f l)) :
    ∀ ⦃x⦄, x ∈ l → ∀ ⦃y⦄, y ∈ l → f x = f y → x = y := by
  induction' l with hd tl ih
  · simp
  · simp only [map, nodup_cons, mem_map, not_exists, not_and, ← Ne.def] at d
    simp only [mem_cons]
    rintro _ (rfl | h₁) _ (rfl | h₂) h₃
    · rfl
    · 

Proof state:
case cons.inl.inr
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a b : α
f : α → β
tl : List α
ih : Nodup (map f tl) → ∀ ⦃x : α⦄, x ∈ tl → ∀ ⦃y : α⦄, y ∈ tl → f x = f y → x = y
x✝ : α
d : (∀ x ∈ tl, f x ≠ f x✝) ∧ Nodup (map f tl)
y✝ : α
h₂ : y✝ ∈ tl
h₃ : f x✝ = f y✝
⊢ x✝ = y✝

Next tactic:
apply (d.1 _ h₂ h₃.symm).elim
theorem inj_on_of_nodup_map {f : α → β} {l : List α} (d : Nodup (map f l)) :
    ∀ ⦃x⦄, x ∈ l → ∀ ⦃y⦄, y ∈ l → f x = f y → x = y := by
  induction' l with hd tl ih
  · simp
  · simp only [map, nodup_cons, mem_map, not_exists, not_and, ← Ne.def] at d
    simp only [mem_cons]
    rintro _ (rfl | h₁) _ (rfl | h₂) h₃
    · rfl
    · apply (d.1 _ h₂ h₃.symm).elim
    · 

Proof state:
case cons.inr.inl
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a b : α
f : α → β
tl : List α
ih : Nodup (map f tl) → ∀ ⦃x : α⦄, x ∈ tl → ∀ ⦃y : α⦄, y ∈ tl → f x = f y → x = y
x✝ : α
h₁ : x✝ ∈ tl
y✝ : α
d : (∀ x ∈ tl, f x ≠ f y✝) ∧ Nodup (map f tl)
h₃ : f x✝ = f y✝
⊢ x✝ = y✝

Next tactic:
apply (d.1 _ h₁ h₃).elim
theorem inj_on_of_nodup_map {f : α → β} {l : List α} (d : Nodup (map f l)) :
    ∀ ⦃x⦄, x ∈ l → ∀ ⦃y⦄, y ∈ l → f x = f y → x = y := by
  induction' l with hd tl ih
  · simp
  · simp only [map, nodup_cons, mem_map, not_exists, not_and, ← Ne.def] at d
    simp only [mem_cons]
    rintro _ (rfl | h₁) _ (rfl | h₂) h₃
    · rfl
    · apply (d.1 _ h₂ h₃.symm).elim
    · apply (d.1 _ h₁ h₃).elim
    · 

Proof state:
case cons.inr.inr
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a b : α
f : α → β
hd : α
tl : List α
ih : Nodup (map f tl) → ∀ ⦃x : α⦄, x ∈ tl → ∀ ⦃y : α⦄, y ∈ tl → f x = f y → x = y
d : (∀ x ∈ tl, f x ≠ f hd) ∧ Nodup (map f tl)
x✝ : α
h₁ : x✝ ∈ tl
y✝ : α
h₂ : y✝ ∈ tl
h₃ : f x✝ = f y✝
⊢ x✝ = y✝

Next tactic:
apply ih d.2 h₁ h₂ h₃
theorem Nodup.pmap {p : α → Prop} {f : ∀ a, p a → β} {l : List α} {H}
    (hf : ∀ a ha b hb, f a ha = f b hb → a = b) (h : Nodup l) : Nodup (pmap f l H) := by
  

Proof state:
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a b : α
p : α → Prop
f : (a : α) → p a → β
l : List α
H : ∀ a ∈ l, p a
hf : ∀ (a : α) (ha : p a) (b : α) (hb : p b), f a ha = f b hb → a = b
h : Nodup l
⊢ Nodup (List.pmap f l H)

Next tactic:
rw [pmap_eq_map_attach]
theorem Nodup.pmap {p : α → Prop} {f : ∀ a, p a → β} {l : List α} {H}
    (hf : ∀ a ha b hb, f a ha = f b hb → a = b) (h : Nodup l) : Nodup (pmap f l H) := by
  rw [pmap_eq_map_attach]
  

Proof state:
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a b : α
p : α → Prop
f : (a : α) → p a → β
l : List α
H : ∀ a ∈ l, p a
hf : ∀ (a : α) (ha : p a) (b : α) (hb : p b), f a ha = f b hb → a = b
h : Nodup l
⊢ Nodup (map (fun x => f ↑x (_ : p ↑x)) (List.attach l))

Next tactic:
exact h.attach.map fun ⟨a, ha⟩ ⟨b, hb⟩ h => by congr; exact hf a (H _ ha) b (H _ hb) h
theorem Nodup.pmap {p : α → Prop} {f : ∀ a, p a → β} {l : List α} {H}
    (hf : ∀ a ha b hb, f a ha = f b hb → a = b) (h : Nodup l) : Nodup (pmap f l H) := by
  rw [pmap_eq_map_attach]
  exact h.attach.map fun ⟨a, ha⟩ ⟨b, hb⟩ h => by 

Proof state:
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a✝ b✝ : α
p : α → Prop
f : (a : α) → p a → β
l : List α
H : ∀ a ∈ l, p a
hf : ∀ (a : α) (ha : p a) (b : α) (hb : p b), f a ha = f b hb → a = b
h✝ : Nodup l
x✝¹ x✝ : { x // x ∈ l }
a : α
ha : a ∈ l
b : α
hb : b ∈ l
h :
  f ↑{ val := a, property := ha } (_ : p ↑{ val := a, property := ha }) =
    f ↑{ val := b, property := hb } (_ : p ↑{ val := b, property := hb })
⊢ { val := a, property := ha } = { val := b, property := hb }

Next tactic:
congr
theorem Nodup.pmap {p : α → Prop} {f : ∀ a, p a → β} {l : List α} {H}
    (hf : ∀ a ha b hb, f a ha = f b hb → a = b) (h : Nodup l) : Nodup (pmap f l H) := by
  rw [pmap_eq_map_attach]
  exact h.attach.map fun ⟨a, ha⟩ ⟨b, hb⟩ h => by congr; 

Proof state:
case e_val
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a✝ b✝ : α
p : α → Prop
f : (a : α) → p a → β
l : List α
H : ∀ a ∈ l, p a
hf : ∀ (a : α) (ha : p a) (b : α) (hb : p b), f a ha = f b hb → a = b
h✝ : Nodup l
x✝¹ x✝ : { x // x ∈ l }
a : α
ha : a ∈ l
b : α
hb : b ∈ l
h :
  f ↑{ val := a, property := ha } (_ : p ↑{ val := a, property := ha }) =
    f ↑{ val := b, property := hb } (_ : p ↑{ val := b, property := hb })
⊢ a = b

Next tactic:
exact hf a (H _ ha) b (H _ hb) h
theorem Nodup.filter (p : α → Bool) {l} : Nodup l → Nodup (filter p l) := by
  

Proof state:
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a b : α
p : α → Bool
l : List α
⊢ Nodup l → Nodup (List.filter p l)

Next tactic:
simpa using Pairwise.filter (fun a ↦ p a)
@[simp]
theorem nodup_reverse {l : List α} : Nodup (reverse l) ↔ Nodup l :=
  pairwise_reverse.trans <| by 

Proof state:
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a b : α
l : List α
⊢ Pairwise (fun a b => b ≠ a) l ↔ Nodup l

Next tactic:
simp only [Nodup, Ne.def, eq_comm]
theorem Nodup.erase_eq_filter [DecidableEq α] {l} (d : Nodup l) (a : α) :
    l.erase a = l.filter (· ≠ a) := by
  

Proof state:
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a✝ b : α
inst✝ : DecidableEq α
l : List α
d : Nodup l
a : α
⊢ List.erase l a = List.filter (fun x => decide (x ≠ a)) l

Next tactic:
induction' d with b l m _ IH
theorem Nodup.erase_eq_filter [DecidableEq α] {l} (d : Nodup l) (a : α) :
    l.erase a = l.filter (· ≠ a) := by
  induction' d with b l m _ IH; · 

Proof state:
case nil
α : Type u
β : Type v
l✝ l₁ l₂ : List α
r : α → α → Prop
a✝ b : α
inst✝ : DecidableEq α
l : List α
a : α
⊢ List.erase [] a = List.filter (fun x => decide (x ≠ a)) []

Next tactic:
rfl
theorem Nodup.erase_eq_filter [DecidableEq α] {l} (d : Nodup l) (a : α) :
    l.erase a = l.filter (· ≠ a) := by
  induction' d with b l m _ IH; · rfl
  

Proof state:
case cons
α : Type u
β : Type v
l✝¹ l₁ l₂ : List α
r : α → α → Prop
a✝¹ b✝ : α
inst✝ : DecidableEq α
l✝ : List α
a b : α
l : List α
m : ∀ a' ∈ l, b ≠ a'
a✝ : Pairwise (fun x x_1 => x ≠ x_1) l
IH : List.erase l a = List.filter (fun x => decide (x ≠ a)) l
⊢ List.erase (b :: l) a = List.filter (fun x => decide (x ≠ a)) (b :: l)

Next tactic:
by_cases h : b = a
theorem Nodup.erase_eq_filter [DecidableEq α] {l} (d : Nodup l) (a : α) :
    l.erase a = l.filter (· ≠ a) := by
  induction' d with b l m _ IH; · rfl
  by_cases h : b = a
  · 

Proof state:
case pos
α : Type u
β : Type v
l✝¹ l₁ l₂ : List α
r : α → α → Prop
a✝¹ b✝ : α
inst✝ : DecidableEq α
l✝ : List α
a b : α
l : List α
m : ∀ a' ∈ l, b ≠ a'
a✝ : Pairwise (fun x x_1 => x ≠ x_1) l
IH : List.erase l a = List.filter (fun x => decide (x ≠ a)) l
h : b = a
⊢ List.erase (b :: l) a = List.filter (fun x => decide (x ≠ a)) (b :: l)

Next tactic:
subst h
theorem Nodup.erase_eq_filter [DecidableEq α] {l} (d : Nodup l) (a : α) :
    l.erase a = l.filter (· ≠ a) := by
  induction' d with b l m _ IH; · rfl
  by_cases h : b = a
  · subst h
    

Proof state:
case pos
α : Type u
β : Type v
l✝¹ l₁ l₂ : List α
r : α → α → Prop
a b✝ : α
inst✝ : DecidableEq α
l✝ : List α
b : α
l : List α
m : ∀ a' ∈ l, b ≠ a'
a✝ : Pairwise (fun x x_1 => x ≠ x_1) l
IH : List.erase l b = List.filter (fun x => decide (x ≠ b)) l
⊢ List.erase (b :: l) b = List.filter (fun x => decide (x ≠ b)) (b :: l)

Next tactic:
rw [erase_cons_head, filter_cons_of_neg _ (by simp)]
theorem Nodup.erase_eq_filter [DecidableEq α] {l} (d : Nodup l) (a : α) :
    l.erase a = l.filter (· ≠ a) := by
  induction' d with b l m _ IH; · rfl
  by_cases h : b = a
  · subst h
    rw [erase_cons_head, filter_cons_of_neg _ (by 

Proof state:
α : Type u
β : Type v
l✝¹ l₁ l₂ : List α
r : α → α → Prop
a b✝ : α
inst✝ : DecidableEq α
l✝ : List α
b : α
l : List α
m : ∀ a' ∈ l, b ≠ a'
a✝ : Pairwise (fun x x_1 => x ≠ x_1) l
IH : List.erase l b = List.filter (fun x => decide (x ≠ b)) l
⊢ ¬decide (b ≠ b) = true

Next tactic:
simp
theorem Nodup.erase_eq_filter [DecidableEq α] {l} (d : Nodup l) (a : α) :
    l.erase a = l.filter (· ≠ a) := by
  induction' d with b l m _ IH; · rfl
  by_cases h : b = a
  · subst h
    rw [erase_cons_head, filter_cons_of_neg _ (by simp)]
    

Proof state:
case pos
α : Type u
β : Type v
l✝¹ l₁ l₂ : List α
r : α → α → Prop
a b✝ : α
inst✝ : DecidableEq α
l✝ : List α
b : α
l : List α
m : ∀ a' ∈ l, b ≠ a'
a✝ : Pairwise (fun x x_1 => x ≠ x_1) l
IH : List.erase l b = List.filter (fun x => decide (x ≠ b)) l
⊢ l = List.filter (fun x => decide (x ≠ b)) l

Next tactic:
symm
theorem Nodup.erase_eq_filter [DecidableEq α] {l} (d : Nodup l) (a : α) :
    l.erase a = l.filter (· ≠ a) := by
  induction' d with b l m _ IH; · rfl
  by_cases h : b = a
  · subst h
    rw [erase_cons_head, filter_cons_of_neg _ (by simp)]
    symm
    

Proof state:
case pos
α : Type u
β : Type v
l✝¹ l₁ l₂ : List α
r : α → α → Prop
a b✝ : α
inst✝ : DecidableEq α
l✝ : List α
b : α
l : List α
m : ∀ a' ∈ l, b ≠ a'
a✝ : Pairwise (fun x x_1 => x ≠ x_1) l
IH : List.erase l b = List.filter (fun x => decide (x ≠ b)) l
⊢ List.filter (fun x => decide (x ≠ b)) l = l

Next tactic:
rw [filter_eq_self]
theorem Nodup.erase_eq_filter [DecidableEq α] {l} (d : Nodup l) (a : α) :
    l.erase a = l.filter (· ≠ a) := by
  induction' d with b l m _ IH; · rfl
  by_cases h : b = a
  · subst h
    rw [erase_cons_head, filter_cons_of_neg _ (by simp)]
    symm
    rw [filter_eq_self]
    

Proof state:
case pos
α : Type u
β : Type v
l✝¹ l₁ l₂ : List α
r : α → α → Prop
a b✝ : α
inst✝ : DecidableEq α
l✝ : List α
b : α
l : List α
m : ∀ a' ∈ l, b ≠ a'
a✝ : Pairwise (fun x x_1 => x ≠ x_1) l
IH : List.erase l b = List.filter (fun x => decide (x ≠ b)) l
⊢ ∀ a ∈ l, decide (a ≠ b) = true

Next tactic:
simpa [@eq_comm α] using m
theorem Nodup.erase_eq_filter [DecidableEq α] {l} (d : Nodup l) (a : α) :
    l.erase a = l.filter (· ≠ a) := by
  induction' d with b l m _ IH; · rfl
  by_cases h : b = a
  · subst h
    rw [erase_cons_head, filter_cons_of_neg _ (by simp)]
    symm
    rw [filter_eq_self]
    simpa [@eq_comm α] using m
  · 

Proof state:
case neg
α : Type u
β : Type v
l✝¹ l₁ l₂ : List α
r : α → α → Prop
a✝¹ b✝ : α
inst✝ : DecidableEq α
l✝ : List α
a b : α
l : List α
m : ∀ a' ∈ l, b ≠ a'
a✝ : Pairwise (fun x x_1 => x ≠ x_1) l
IH : List.erase l a = List.filter (fun x => decide (x ≠ a)) l
h : ¬b = a
⊢ List.erase (b :: l) a = List.filter (fun x => decide (x ≠ a)) (b :: l)

Next tactic:
rw [erase_cons_tail _ h, filter_cons_of_pos, IH]
theorem Nodup.erase_eq_filter [DecidableEq α] {l} (d : Nodup l) (a : α) :
    l.erase a = l.filter (· ≠ a) := by
  induction' d with b l m _ IH; · rfl
  by_cases h : b = a
  · subst h
    rw [erase_cons_head, filter_cons_of_neg _ (by simp)]
    symm
    rw [filter_eq_self]
    simpa [@eq_comm α] using m
  · rw [erase_cons_tail _ h, filter_cons_of_pos, IH]
    

Proof state:
case neg.pa
α : Type u
β : Type v
l✝¹ l₁ l₂ : List α
r : α → α → Prop
a✝¹ b✝ : α
inst✝ : DecidableEq α
l✝ : List α
a b : α
l : List α
m : ∀ a' ∈ l, b ≠ a'
a✝ : Pairwise (fun x x_1 => x ≠ x_1) l
IH : List.erase l a = List.filter (fun x => decide (x ≠ a)) l
h : ¬b = a
⊢ decide (b ≠ a) = true

Next tactic:
simp [h]
theorem Nodup.mem_erase_iff [DecidableEq α] (d : Nodup l) : a ∈ l.erase b ↔ a ≠ b ∧ a ∈ l := by
  

Proof state:
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a b : α
inst✝ : DecidableEq α
d : Nodup l
⊢ a ∈ List.erase l b ↔ a ≠ b ∧ a ∈ l

Next tactic:
rw [d.erase_eq_filter, mem_filter, and_comm, decide_eq_true_iff]
theorem nodup_join {L : List (List α)} :
    Nodup (join L) ↔ (∀ l ∈ L, Nodup l) ∧ Pairwise Disjoint L := by
  

Proof state:
α : Type u
β : Type v
l l₁ l₂ : List α
r : α → α → Prop
a b : α
L : List (List α)
⊢ Nodup (join L) ↔ (∀ l ∈ L, Nodup l) ∧ Pairwise Disjoint L

Next tactic:
simp only [Nodup, pairwise_join, disjoint_left.symm, forall_mem_ne]
theorem nodup_bind {l₁ : List α} {f : α → List β} :
    Nodup (l₁.bind f) ↔
      (∀ x ∈ l₁, Nodup (f x)) ∧ Pairwise (fun a b : α => Disjoint (f a) (f b)) l₁ := by
  

Proof state:
α : Type u
β : Type v
l l₁✝ l₂ : List α
r : α → α → Prop
a b : α
l₁ : List α
f : α → List β
⊢ Nodup (List.bind l₁ f) ↔ (∀ x ∈ l₁, Nodup (f x)) ∧ Pairwise (fun a b => Disjoint (f a) (f b)) l₁

Next tactic:
simp only [List.bind, nodup_join, pairwise_map, and_comm, and_left_comm, mem_map, exists_imp,
      and_imp]
theorem nodup_bind {l₁ : List α} {f : α → List β} :
    Nodup (l₁.bind f) ↔
      (∀ x ∈ l₁, Nodup (f x)) ∧ Pairwise (fun a b : α => Disjoint (f a) (f b)) l₁ := by
  simp only [List.bind, nodup_join, pairwise_map, and_comm, and_left_comm, mem_map, exists_imp,
      and_imp]
  

Proof state:
α : Type u
β : Type v
l l₁✝ l₂ : List α
r : α → α → Prop
a b : α
l₁ : List α
f : α → List β
⊢ (Pairwise (fun a b => Disjoint (f a) (f b)) l₁ ∧ ∀ (l : List β) (x : α), f x = l → x ∈ l₁ → Nodup l) ↔
    Pairwise (fun a b => Disjoint (f a) (f b)) l₁ ∧ ∀ x ∈ l₁, Nodup (f x)

Next tactic:
rw [show (∀ (l : List β) (x : α), f x = l → x ∈ l₁ → Nodup l) ↔ ∀ x : α, x ∈ l₁ → Nodup (f x)
      from forall_swap.trans <| forall_congr' fun _ => forall_eq']
protected theorem Nodup.product {l₂ : List β} (d₁ : l₁.Nodup) (d₂ : l₂.Nodup) :
    (l₁ ×ˢ l₂).Nodup :=
  nodup_bind.2
    ⟨fun a _ => d₂.map <| LeftInverse.injective fun b => (rfl : (a, b).2 = b),
      d₁.imp fun {a₁ a₂} n x h₁ h₂ => by
        

Proof state:
α : Type u
β : Type v
l l₁ l₂✝ : List α
r : α → α → Prop
a b : α
l₂ : List β
d₁ : Nodup l₁
d₂ : Nodup l₂
a₁ a₂ : α
n : a₁ ≠ a₂
x : α × β
h₁ : x ∈ map (Prod.mk a₁) l₂
h₂ : x ∈ map (Prod.mk a₂) l₂
⊢ False

Next tactic:
rcases mem_map.1 h₁ with ⟨b₁, _, rfl⟩
protected theorem Nodup.product {l₂ : List β} (d₁ : l₁.Nodup) (d₂ : l₂.Nodup) :
    (l₁ ×ˢ l₂).Nodup :=
  nodup_bind.2
    ⟨fun a _ => d₂.map <| LeftInverse.injective fun b => (rfl : (a, b).2 = b),
      d₁.imp fun {a₁ a₂} n x h₁ h₂ => by
        rcases mem_map.1 h₁ with ⟨b₁, _, rfl⟩
        

Proof state:
case intro.intro
α : Type u
β : Type v
l l₁ l₂✝ : List α
r : α → α → Prop
a b : α
l₂ : List β
d₁ : Nodup l₁
d₂ : Nodup l₂
a₁ a₂ : α
n : a₁ ≠ a₂
b₁ : β
left✝ : b₁ ∈ l₂
h₁ : (a₁, b₁) ∈ map (Prod.mk a₁) l₂
h₂ : (a₁, b₁) ∈ map (Prod.mk a₂) l₂
⊢ False

Next tactic:
rcases mem_map.1 h₂ with ⟨b₂, mb₂, ⟨⟩⟩
protected theorem Nodup.product {l₂ : List β} (d₁ : l₁.Nodup) (d₂ : l₂.Nodup) :
    (l₁ ×ˢ l₂).Nodup :=
  nodup_bind.2
    ⟨fun a _ => d₂.map <| LeftInverse.injective fun b => (rfl : (a, b).2 = b),
      d₁.imp fun {a₁ a₂} n x h₁ h₂ => by
        rcases mem_map.1 h₁ with ⟨b₁, _, rfl⟩
        rcases mem_map.1 h₂ with ⟨b₂, mb₂, ⟨⟩⟩
        

Proof state:
case intro.intro.intro.intro.refl
α : Type u
β : Type v
l l₁ l₂✝ : List α
r : α → α → Prop
a b : α
l₂ : List β
d₁ : Nodup l₁
d₂ : Nodup l₂
a₁ : α
b₁ : β
left✝ : b₁ ∈ l₂
h₁ : (a₁, b₁) ∈ map (Prod.mk a₁) l₂
n : a₁ ≠ a₁
h₂ : (a₁, b₁) ∈ map (Prod.mk a₁) l₂
mb₂ : b₁ ∈ l₂
⊢ False

Next tactic:
exact n rfl
theorem Nodup.sigma {σ : α → Type*} {l₂ : ∀ a , List (σ a)} (d₁ : Nodup l₁)
    (d₂ : ∀ a , Nodup (l₂ a)) : (l₁.sigma l₂).Nodup :=
  nodup_bind.2
    ⟨fun a _ => (d₂ a).map fun b b' h => by 

Proof state:
α : Type u
β : Type v
l l₁ l₂✝ : List α
r : α → α → Prop
a✝ b✝ : α
σ : α → Type u_1
l₂ : (a : α) → List (σ a)
d₁ : Nodup l₁
d₂ : ∀ (a : α), Nodup (l₂ a)
a : α
x✝ : a ∈ l₁
b b' : (fun a => σ a) a
h : { fst := a, snd := b } = { fst := a, snd := b' }
⊢ b = b'

Next tactic:
injection h with _ h
theorem Nodup.sigma {σ : α → Type*} {l₂ : ∀ a , List (σ a)} (d₁ : Nodup l₁)
    (d₂ : ∀ a , Nodup (l₂ a)) : (l₁.sigma l₂).Nodup :=
  nodup_bind.2
    ⟨fun a _ => (d₂ a).map fun b b' h => by injection h with _ h,
      d₁.imp fun {a₁ a₂} n x h₁ h₂ => by
        

Proof state:
α : Type u
β : Type v
l l₁ l₂✝ : List α
r : α → α → Prop
a b : α
σ : α → Type u_1
l₂ : (a : α) → List (σ a)
d₁ : Nodup l₁
d₂ : ∀ (a : α), Nodup (l₂ a)
a₁ a₂ : α
n : a₁ ≠ a₂
x : (a : α) × σ a
h₁ : x ∈ map (Sigma.mk a₁) (l₂ a₁)
h₂ : x ∈ map (Sigma.mk a₂) (l₂ a₂)
⊢ False

Next tactic:
rcases mem_map.1 h₁ with ⟨b₁, _, rfl⟩
theorem Nodup.sigma {σ : α → Type*} {l₂ : ∀ a , List (σ a)} (d₁ : Nodup l₁)
    (d₂ : ∀ a , Nodup (l₂ a)) : (l₁.sigma l₂).Nodup :=
  nodup_bind.2
    ⟨fun a _ => (d₂ a).map fun b b' h => by injection h with _ h,
      d₁.imp fun {a₁ a₂} n x h₁ h₂ => by
        rcases mem_map.1 h₁ with ⟨b₁, _, rfl⟩
        

Proof state:
case intro.intro
α : Type u
β : Type v
l l₁ l₂✝ : List α
r : α → α → Prop
a b : α
σ : α → Type u_1
l₂ : (a : α) → List (σ a)
d₁ : Nodup l₁
d₂ : ∀ (a : α), Nodup (l₂ a)
a₁ a₂ : α
n : a₁ ≠ a₂
b₁ : σ a₁
left✝ : b₁ ∈ l₂ a₁
h₁ : { fst := a₁, snd := b₁ } ∈ map (Sigma.mk a₁) (l₂ a₁)
h₂ : { fst := a₁, snd := b₁ } ∈ map (Sigma.mk a₂) (l₂ a₂)
⊢ False

Next tactic:
rcases mem_map.1 h₂ with ⟨b₂, mb₂, ⟨⟩⟩