l3lab/ntp-mathlib huggingface dataset:
@[simp]
theorem Multiset.toEnumFinset_subset_iff {m₁ m₂ : Multiset α} :
    m₁.toEnumFinset ⊆ m₂.toEnumFinset ↔ m₁ ≤ m₂ := by
  refine' ⟨fun h ↦ _, Multiset.toEnumFinset_mono⟩
  rw [Multiset.le_iff_count]
  intro x
  by_cases hx : x ∈ m₁
  · apply Nat.le_of_pred_lt
    

Proof state:
case pos.a
α : Type u_1
inst✝ : DecidableEq α
m m₁ m₂ : Multiset α
h : toEnumFinset m₁ ⊆ toEnumFinset m₂
x : α
hx : x ∈ m₁
⊢ Nat.pred (count x m₁) < count x m₂

Next tactic:
have : (x, m₁.count x - 1) ∈ m₁.toEnumFinset := by
      rw [Multiset.mem_toEnumFinset]
      exact Nat.pred_lt (ne_of_gt (Multiset.count_pos.mpr hx))
@[simp]
theorem Multiset.toEnumFinset_subset_iff {m₁ m₂ : Multiset α} :
    m₁.toEnumFinset ⊆ m₂.toEnumFinset ↔ m₁ ≤ m₂ := by
  refine' ⟨fun h ↦ _, Multiset.toEnumFinset_mono⟩
  rw [Multiset.le_iff_count]
  intro x
  by_cases hx : x ∈ m₁
  · apply Nat.le_of_pred_lt
    have : (x, m₁.count x - 1) ∈ m₁.toEnumFinset := by
      

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m m₁ m₂ : Multiset α
h : toEnumFinset m₁ ⊆ toEnumFinset m₂
x : α
hx : x ∈ m₁
⊢ (x, count x m₁ - 1) ∈ toEnumFinset m₁

Next tactic:
rw [Multiset.mem_toEnumFinset]
@[simp]
theorem Multiset.toEnumFinset_subset_iff {m₁ m₂ : Multiset α} :
    m₁.toEnumFinset ⊆ m₂.toEnumFinset ↔ m₁ ≤ m₂ := by
  refine' ⟨fun h ↦ _, Multiset.toEnumFinset_mono⟩
  rw [Multiset.le_iff_count]
  intro x
  by_cases hx : x ∈ m₁
  · apply Nat.le_of_pred_lt
    have : (x, m₁.count x - 1) ∈ m₁.toEnumFinset := by
      rw [Multiset.mem_toEnumFinset]
      

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m m₁ m₂ : Multiset α
h : toEnumFinset m₁ ⊆ toEnumFinset m₂
x : α
hx : x ∈ m₁
⊢ (x, count x m₁ - 1).2 < count (x, count x m₁ - 1).1 m₁

Next tactic:
exact Nat.pred_lt (ne_of_gt (Multiset.count_pos.mpr hx))
@[simp]
theorem Multiset.toEnumFinset_subset_iff {m₁ m₂ : Multiset α} :
    m₁.toEnumFinset ⊆ m₂.toEnumFinset ↔ m₁ ≤ m₂ := by
  refine' ⟨fun h ↦ _, Multiset.toEnumFinset_mono⟩
  rw [Multiset.le_iff_count]
  intro x
  by_cases hx : x ∈ m₁
  · apply Nat.le_of_pred_lt
    have : (x, m₁.count x - 1) ∈ m₁.toEnumFinset := by
      rw [Multiset.mem_toEnumFinset]
      exact Nat.pred_lt (ne_of_gt (Multiset.count_pos.mpr hx))
    

Proof state:
case pos.a
α : Type u_1
inst✝ : DecidableEq α
m m₁ m₂ : Multiset α
h : toEnumFinset m₁ ⊆ toEnumFinset m₂
x : α
hx : x ∈ m₁
this : (x, count x m₁ - 1) ∈ toEnumFinset m₁
⊢ Nat.pred (count x m₁) < count x m₂

Next tactic:
simpa only [Multiset.mem_toEnumFinset] using h this
@[simp]
theorem Multiset.toEnumFinset_subset_iff {m₁ m₂ : Multiset α} :
    m₁.toEnumFinset ⊆ m₂.toEnumFinset ↔ m₁ ≤ m₂ := by
  refine' ⟨fun h ↦ _, Multiset.toEnumFinset_mono⟩
  rw [Multiset.le_iff_count]
  intro x
  by_cases hx : x ∈ m₁
  · apply Nat.le_of_pred_lt
    have : (x, m₁.count x - 1) ∈ m₁.toEnumFinset := by
      rw [Multiset.mem_toEnumFinset]
      exact Nat.pred_lt (ne_of_gt (Multiset.count_pos.mpr hx))
    simpa only [Multiset.mem_toEnumFinset] using h this
  · 

Proof state:
case neg
α : Type u_1
inst✝ : DecidableEq α
m m₁ m₂ : Multiset α
h : toEnumFinset m₁ ⊆ toEnumFinset m₂
x : α
hx : x ∉ m₁
⊢ count x m₁ ≤ count x m₂

Next tactic:
simp [hx]
/-- The embedding from a multiset into `α × ℕ` where the second coordinate enumerates repeats.
If you are looking for the function `m → α`, that would be plain `(↑)`. -/
@[simps]
def Multiset.coeEmbedding (m : Multiset α) : m ↪ α × ℕ
    where
  toFun x := (x, x.2)
  inj' := by
    

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ Function.Injective fun x => (x.fst, ↑x.snd)

Next tactic:
intro ⟨x, i, hi⟩ ⟨y, j, hj⟩
/-- The embedding from a multiset into `α × ℕ` where the second coordinate enumerates repeats.
If you are looking for the function `m → α`, that would be plain `(↑)`. -/
@[simps]
def Multiset.coeEmbedding (m : Multiset α) : m ↪ α × ℕ
    where
  toFun x := (x, x.2)
  inj' := by
    intro ⟨x, i, hi⟩ ⟨y, j, hj⟩
    

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : α
i : ℕ
hi : i < count x m
y : α
j : ℕ
hj : j < count y m
⊢ (fun x => (x.fst, ↑x.snd)) { fst := x, snd := { val := i, isLt := hi } } =
      (fun x => (x.fst, ↑x.snd)) { fst := y, snd := { val := j, isLt := hj } } →
    { fst := x, snd := { val := i, isLt := hi } } = { fst := y, snd := { val := j, isLt := hj } }

Next tactic:
rintro ⟨⟩
/-- The embedding from a multiset into `α × ℕ` where the second coordinate enumerates repeats.
If you are looking for the function `m → α`, that would be plain `(↑)`. -/
@[simps]
def Multiset.coeEmbedding (m : Multiset α) : m ↪ α × ℕ
    where
  toFun x := (x, x.2)
  inj' := by
    intro ⟨x, i, hi⟩ ⟨y, j, hj⟩
    rintro ⟨⟩
    

Proof state:
case refl
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : α
i : ℕ
hi hj : i < count x m
⊢ { fst := x, snd := { val := i, isLt := hi } } = { fst := x, snd := { val := i, isLt := hj } }

Next tactic:
rfl
/-- Another way to coerce a `Multiset` to a type is to go through `m.toEnumFinset` and coerce
that `Finset` to a type. -/
@[simps]
def Multiset.coeEquiv (m : Multiset α) : m ≃ m.toEnumFinset
    where
  toFun x :=
    ⟨m.coeEmbedding x, by
      

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : ToType m
⊢ (coeEmbedding m) x ∈ toEnumFinset m

Next tactic:
rw [Multiset.mem_toEnumFinset]
/-- Another way to coerce a `Multiset` to a type is to go through `m.toEnumFinset` and coerce
that `Finset` to a type. -/
@[simps]
def Multiset.coeEquiv (m : Multiset α) : m ≃ m.toEnumFinset
    where
  toFun x :=
    ⟨m.coeEmbedding x, by
      rw [Multiset.mem_toEnumFinset]
      

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : ToType m
⊢ ((coeEmbedding m) x).2 < count ((coeEmbedding m) x).1 m

Next tactic:
exact x.2.2
/-- Another way to coerce a `Multiset` to a type is to go through `m.toEnumFinset` and coerce
that `Finset` to a type. -/
@[simps]
def Multiset.coeEquiv (m : Multiset α) : m ≃ m.toEnumFinset
    where
  toFun x :=
    ⟨m.coeEmbedding x, by
      rw [Multiset.mem_toEnumFinset]
      exact x.2.2⟩
  invFun x :=
    ⟨x.1.1, x.1.2, by
      

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : { x // x ∈ toEnumFinset m }
⊢ (↑x).2 < count (↑x).1 m

Next tactic:
rw [← Multiset.mem_toEnumFinset]
/-- Another way to coerce a `Multiset` to a type is to go through `m.toEnumFinset` and coerce
that `Finset` to a type. -/
@[simps]
def Multiset.coeEquiv (m : Multiset α) : m ≃ m.toEnumFinset
    where
  toFun x :=
    ⟨m.coeEmbedding x, by
      rw [Multiset.mem_toEnumFinset]
      exact x.2.2⟩
  invFun x :=
    ⟨x.1.1, x.1.2, by
      rw [← Multiset.mem_toEnumFinset]
      

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : { x // x ∈ toEnumFinset m }
⊢ ↑x ∈ toEnumFinset m

Next tactic:
exact x.2
/-- Another way to coerce a `Multiset` to a type is to go through `m.toEnumFinset` and coerce
that `Finset` to a type. -/
@[simps]
def Multiset.coeEquiv (m : Multiset α) : m ≃ m.toEnumFinset
    where
  toFun x :=
    ⟨m.coeEmbedding x, by
      rw [Multiset.mem_toEnumFinset]
      exact x.2.2⟩
  invFun x :=
    ⟨x.1.1, x.1.2, by
      rw [← Multiset.mem_toEnumFinset]
      exact x.2⟩
  left_inv := by
    

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ Function.LeftInverse (fun x => { fst := (↑x).1, snd := { val := (↑x).2, isLt := (_ : (↑x).2 < count (↑x).1 m) } })
    fun x => { val := (coeEmbedding m) x, property := (_ : (coeEmbedding m) x ∈ toEnumFinset m) }

Next tactic:
rintro ⟨x, i, h⟩
/-- Another way to coerce a `Multiset` to a type is to go through `m.toEnumFinset` and coerce
that `Finset` to a type. -/
@[simps]
def Multiset.coeEquiv (m : Multiset α) : m ≃ m.toEnumFinset
    where
  toFun x :=
    ⟨m.coeEmbedding x, by
      rw [Multiset.mem_toEnumFinset]
      exact x.2.2⟩
  invFun x :=
    ⟨x.1.1, x.1.2, by
      rw [← Multiset.mem_toEnumFinset]
      exact x.2⟩
  left_inv := by
    rintro ⟨x, i, h⟩
    

Proof state:
case mk.mk
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : α
i : ℕ
h : i < count x m
⊢ (fun x => { fst := (↑x).1, snd := { val := (↑x).2, isLt := (_ : (↑x).2 < count (↑x).1 m) } })
      ((fun x => { val := (coeEmbedding m) x, property := (_ : (coeEmbedding m) x ∈ toEnumFinset m) })
        { fst := x, snd := { val := i, isLt := h } }) =
    { fst := x, snd := { val := i, isLt := h } }

Next tactic:
rfl
/-- Another way to coerce a `Multiset` to a type is to go through `m.toEnumFinset` and coerce
that `Finset` to a type. -/
@[simps]
def Multiset.coeEquiv (m : Multiset α) : m ≃ m.toEnumFinset
    where
  toFun x :=
    ⟨m.coeEmbedding x, by
      rw [Multiset.mem_toEnumFinset]
      exact x.2.2⟩
  invFun x :=
    ⟨x.1.1, x.1.2, by
      rw [← Multiset.mem_toEnumFinset]
      exact x.2⟩
  left_inv := by
    rintro ⟨x, i, h⟩
    rfl
  right_inv := by
    

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ Function.RightInverse (fun x => { fst := (↑x).1, snd := { val := (↑x).2, isLt := (_ : (↑x).2 < count (↑x).1 m) } })
    fun x => { val := (coeEmbedding m) x, property := (_ : (coeEmbedding m) x ∈ toEnumFinset m) }

Next tactic:
rintro ⟨⟨x, i⟩, h⟩
/-- Another way to coerce a `Multiset` to a type is to go through `m.toEnumFinset` and coerce
that `Finset` to a type. -/
@[simps]
def Multiset.coeEquiv (m : Multiset α) : m ≃ m.toEnumFinset
    where
  toFun x :=
    ⟨m.coeEmbedding x, by
      rw [Multiset.mem_toEnumFinset]
      exact x.2.2⟩
  invFun x :=
    ⟨x.1.1, x.1.2, by
      rw [← Multiset.mem_toEnumFinset]
      exact x.2⟩
  left_inv := by
    rintro ⟨x, i, h⟩
    rfl
  right_inv := by
    rintro ⟨⟨x, i⟩, h⟩
    

Proof state:
case mk.mk
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : α
i : ℕ
h : (x, i) ∈ toEnumFinset m
⊢ (fun x => { val := (coeEmbedding m) x, property := (_ : (coeEmbedding m) x ∈ toEnumFinset m) })
      ((fun x => { fst := (↑x).1, snd := { val := (↑x).2, isLt := (_ : (↑x).2 < count (↑x).1 m) } })
        { val := (x, i), property := h }) =
    { val := (x, i), property := h }

Next tactic:
rfl
@[simp]
theorem Multiset.toEmbedding_coeEquiv_trans (m : Multiset α) :
    m.coeEquiv.toEmbedding.trans (Function.Embedding.subtype _) = m.coeEmbedding := by 

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ Function.Embedding.trans (Equiv.toEmbedding (coeEquiv m)) (Function.Embedding.subtype fun x => x ∈ toEnumFinset m) =
    coeEmbedding m

Next tactic:
ext
@[simp]
theorem Multiset.toEmbedding_coeEquiv_trans (m : Multiset α) :
    m.coeEquiv.toEmbedding.trans (Function.Embedding.subtype _) = m.coeEmbedding := by ext <;> 

Proof state:
case h.a
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x✝ : ToType m
⊢ ((Function.Embedding.trans (Equiv.toEmbedding (coeEquiv m)) (Function.Embedding.subtype fun x => x ∈ toEnumFinset m))
        x✝).1 =
    ((coeEmbedding m) x✝).1

Next tactic:
rfl
@[simp]
theorem Multiset.toEmbedding_coeEquiv_trans (m : Multiset α) :
    m.coeEquiv.toEmbedding.trans (Function.Embedding.subtype _) = m.coeEmbedding := by ext <;> 

Proof state:
case h.a
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x✝ : ToType m
⊢ ((Function.Embedding.trans (Equiv.toEmbedding (coeEquiv m)) (Function.Embedding.subtype fun x => x ∈ toEnumFinset m))
        x✝).2 =
    ((coeEmbedding m) x✝).2

Next tactic:
rfl
theorem Multiset.map_univ_coeEmbedding (m : Multiset α) :
    (Finset.univ : Finset m).map m.coeEmbedding = m.toEnumFinset := by
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ Finset.map (coeEmbedding m) Finset.univ = toEnumFinset m

Next tactic:
ext ⟨x, i⟩
theorem Multiset.map_univ_coeEmbedding (m : Multiset α) :
    (Finset.univ : Finset m).map m.coeEmbedding = m.toEnumFinset := by
  ext ⟨x, i⟩
  

Proof state:
case a.mk
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : α
i : ℕ
⊢ (x, i) ∈ Finset.map (coeEmbedding m) Finset.univ ↔ (x, i) ∈ toEnumFinset m

Next tactic:
simp only [Fin.exists_iff, Finset.mem_map, Finset.mem_univ, Multiset.coeEmbedding_apply,
    Prod.mk.inj_iff, exists_true_left, Multiset.exists_coe, Multiset.coe_mk, Fin.val_mk,
    exists_prop, exists_eq_right_right, exists_eq_right, Multiset.mem_toEnumFinset, iff_self_iff,
    true_and_iff]
theorem Multiset.toEnumFinset_filter_eq (m : Multiset α) (x : α) :
    (m.toEnumFinset.filter fun p ↦ x = p.1) =
      (Finset.range (m.count x)).map ⟨Prod.mk x, Prod.mk.inj_left x⟩ := by
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : α
⊢ Finset.filter (fun p => x = p.1) (toEnumFinset m) =
    Finset.map { toFun := Prod.mk x, inj' := (_ : Function.Injective (Prod.mk x)) } (Finset.range (count x m))

Next tactic:
ext ⟨y, i⟩
theorem Multiset.toEnumFinset_filter_eq (m : Multiset α) (x : α) :
    (m.toEnumFinset.filter fun p ↦ x = p.1) =
      (Finset.range (m.count x)).map ⟨Prod.mk x, Prod.mk.inj_left x⟩ := by
  ext ⟨y, i⟩
  

Proof state:
case a.mk
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x y : α
i : ℕ
⊢ (y, i) ∈ Finset.filter (fun p => x = p.1) (toEnumFinset m) ↔
    (y, i) ∈ Finset.map { toFun := Prod.mk x, inj' := (_ : Function.Injective (Prod.mk x)) } (Finset.range (count x m))

Next tactic:
simp only [eq_comm, Finset.mem_filter, Multiset.mem_toEnumFinset, Finset.mem_map,
    Finset.mem_range, Function.Embedding.coeFn_mk, Prod.mk.inj_iff, exists_prop,
    exists_eq_right_right', and_congr_left_iff]
theorem Multiset.toEnumFinset_filter_eq (m : Multiset α) (x : α) :
    (m.toEnumFinset.filter fun p ↦ x = p.1) =
      (Finset.range (m.count x)).map ⟨Prod.mk x, Prod.mk.inj_left x⟩ := by
  ext ⟨y, i⟩
  simp only [eq_comm, Finset.mem_filter, Multiset.mem_toEnumFinset, Finset.mem_map,
    Finset.mem_range, Function.Embedding.coeFn_mk, Prod.mk.inj_iff, exists_prop,
    exists_eq_right_right', and_congr_left_iff]
  

Proof state:
case a.mk
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x y : α
i : ℕ
⊢ x = y → (i < count y m ↔ i < count x m)

Next tactic:
rintro rfl
theorem Multiset.toEnumFinset_filter_eq (m : Multiset α) (x : α) :
    (m.toEnumFinset.filter fun p ↦ x = p.1) =
      (Finset.range (m.count x)).map ⟨Prod.mk x, Prod.mk.inj_left x⟩ := by
  ext ⟨y, i⟩
  simp only [eq_comm, Finset.mem_filter, Multiset.mem_toEnumFinset, Finset.mem_map,
    Finset.mem_range, Function.Embedding.coeFn_mk, Prod.mk.inj_iff, exists_prop,
    exists_eq_right_right', and_congr_left_iff]
  rintro rfl
  

Proof state:
case a.mk
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : α
i : ℕ
⊢ i < count x m ↔ i < count x m

Next tactic:
rfl
@[simp]
theorem Multiset.map_toEnumFinset_fst (m : Multiset α) : m.toEnumFinset.val.map Prod.fst = m := by
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ map Prod.fst (toEnumFinset m).val = m

Next tactic:
ext x
@[simp]
theorem Multiset.map_toEnumFinset_fst (m : Multiset α) : m.toEnumFinset.val.map Prod.fst = m := by
  ext x
  

Proof state:
case a
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
x : α
⊢ count x (map Prod.fst (toEnumFinset m).val) = count x m

Next tactic:
simp only [Multiset.count_map, ← Finset.filter_val, Multiset.toEnumFinset_filter_eq,
    Finset.map_val, Finset.range_val, Multiset.card_map, Multiset.card_range]
@[simp]
theorem Multiset.image_toEnumFinset_fst (m : Multiset α) :
    m.toEnumFinset.image Prod.fst = m.toFinset := by
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ Finset.image Prod.fst (toEnumFinset m) = toFinset m

Next tactic:
rw [Finset.image, Multiset.map_toEnumFinset_fst]
@[simp]
theorem Multiset.map_univ_coe (m : Multiset α) :
    (Finset.univ : Finset m).val.map (fun x : m ↦ (x : α)) = m := by
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ map (fun x => x.fst) Finset.univ.val = m

Next tactic:
have := m.map_toEnumFinset_fst
@[simp]
theorem Multiset.map_univ_coe (m : Multiset α) :
    (Finset.univ : Finset m).val.map (fun x : m ↦ (x : α)) = m := by
  have := m.map_toEnumFinset_fst
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
this : map Prod.fst (toEnumFinset m).val = m
⊢ map (fun x => x.fst) Finset.univ.val = m

Next tactic:
rw [← m.map_univ_coeEmbedding] at this
@[simp]
theorem Multiset.map_univ_coe (m : Multiset α) :
    (Finset.univ : Finset m).val.map (fun x : m ↦ (x : α)) = m := by
  have := m.map_toEnumFinset_fst
  rw [← m.map_univ_coeEmbedding] at this
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
this : map Prod.fst (Finset.map (coeEmbedding m) Finset.univ).val = m
⊢ map (fun x => x.fst) Finset.univ.val = m

Next tactic:
simpa only [Finset.map_val, Multiset.coeEmbedding_apply, Multiset.map_map,
    Function.comp_apply] using this
@[simp]
theorem Multiset.map_univ {β : Type*} (m : Multiset α) (f : α → β) :
    ((Finset.univ : Finset m).val.map fun (x : m) ↦ f (x : α)) = m.map f := by
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ : Multiset α
β : Type u_2
m : Multiset α
f : α → β
⊢ map (fun x => f x.fst) Finset.univ.val = map f m

Next tactic:
erw [← Multiset.map_map]
@[simp]
theorem Multiset.map_univ {β : Type*} (m : Multiset α) (f : α → β) :
    ((Finset.univ : Finset m).val.map fun (x : m) ↦ f (x : α)) = m.map f := by
  erw [← Multiset.map_map]
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ : Multiset α
β : Type u_2
m : Multiset α
f : α → β
⊢ map f (map (fun x => x.fst) Finset.univ.val) = map f m

Next tactic:
rw [Multiset.map_univ_coe]
@[simp]
theorem Multiset.card_toEnumFinset (m : Multiset α) : m.toEnumFinset.card = Multiset.card m := by
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ Finset.card (toEnumFinset m) = card m

Next tactic:
rw [Finset.card, ← Multiset.card_map Prod.fst m.toEnumFinset.val]
@[simp]
theorem Multiset.card_toEnumFinset (m : Multiset α) : m.toEnumFinset.card = Multiset.card m := by
  rw [Finset.card, ← Multiset.card_map Prod.fst m.toEnumFinset.val]
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ card (map Prod.fst (toEnumFinset m).val) = card m

Next tactic:
congr
@[simp]
theorem Multiset.card_toEnumFinset (m : Multiset α) : m.toEnumFinset.card = Multiset.card m := by
  rw [Finset.card, ← Multiset.card_map Prod.fst m.toEnumFinset.val]
  congr
  

Proof state:
case h.e_6.h
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ map Prod.fst (toEnumFinset m).val = m

Next tactic:
exact m.map_toEnumFinset_fst
@[simp]
theorem Multiset.card_coe (m : Multiset α) : Fintype.card m = Multiset.card m := by
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ Fintype.card (ToType m) = card m

Next tactic:
rw [Fintype.card_congr m.coeEquiv]
@[simp]
theorem Multiset.card_coe (m : Multiset α) : Fintype.card m = Multiset.card m := by
  rw [Fintype.card_congr m.coeEquiv]
  

Proof state:
α : Type u_1
inst✝ : DecidableEq α
m✝ m : Multiset α
⊢ Fintype.card { x // x ∈ toEnumFinset m } = card m

Next tactic:
simp only [Fintype.card_coe, card_toEnumFinset]
@[to_additive]
theorem Multiset.prod_eq_prod_coe [CommMonoid α] (m : Multiset α) : m.prod = ∏ x : m, (x : α) := by
  

Proof state:
α : Type u_1
inst✝¹ : DecidableEq α
m✝ : Multiset α
inst✝ : CommMonoid α
m : Multiset α
⊢ prod m = ∏ x : ToType m, x.fst

Next tactic:
congr
@[to_additive]
theorem Multiset.prod_eq_prod_coe [CommMonoid α] (m : Multiset α) : m.prod = ∏ x : m, (x : α) := by
  congr
  -- Porting note: `simp` fails with "maximum recursion depth has been reached"
  

Proof state:
case e_a
α : Type u_1
inst✝¹ : DecidableEq α
m✝ : Multiset α
inst✝ : CommMonoid α
m : Multiset α
⊢ m = map (fun x => x.fst) Finset.univ.val

Next tactic:
erw [map_univ_coe]
@[to_additive]
theorem Multiset.prod_eq_prod_toEnumFinset [CommMonoid α] (m : Multiset α) :
    m.prod = ∏ x in m.toEnumFinset, x.1 := by
  

Proof state:
α : Type u_1
inst✝¹ : DecidableEq α
m✝ : Multiset α
inst✝ : CommMonoid α
m : Multiset α
⊢ prod m = ∏ x in toEnumFinset m, x.1

Next tactic:
congr
@[to_additive]
theorem Multiset.prod_eq_prod_toEnumFinset [CommMonoid α] (m : Multiset α) :
    m.prod = ∏ x in m.toEnumFinset, x.1 := by
  congr
  

Proof state:
case e_a
α : Type u_1
inst✝¹ : DecidableEq α
m✝ : Multiset α
inst✝ : CommMonoid α
m : Multiset α
⊢ m = map (fun x => x.1) (toEnumFinset m).val

Next tactic:
simp
@[to_additive]
theorem Multiset.prod_toEnumFinset {β : Type*} [CommMonoid β] (m : Multiset α) (f : α → ℕ → β) :
    ∏ x in m.toEnumFinset, f x.1 x.2 = ∏ x : m, f x x.2 := by
  

Proof state:
α : Type u_1
inst✝¹ : DecidableEq α
m✝ : Multiset α
β : Type u_2
inst✝ : CommMonoid β
m : Multiset α
f : α → ℕ → β
⊢ ∏ x in toEnumFinset m, f x.1 x.2 = ∏ x : ToType m, f x.fst ↑x.snd

Next tactic:
rw [Fintype.prod_equiv m.coeEquiv (fun x ↦ f x x.2) fun x ↦ f x.1.1 x.1.2]
@[to_additive]
theorem Multiset.prod_toEnumFinset {β : Type*} [CommMonoid β] (m : Multiset α) (f : α → ℕ → β) :
    ∏ x in m.toEnumFinset, f x.1 x.2 = ∏ x : m, f x x.2 := by
  rw [Fintype.prod_equiv m.coeEquiv (fun x ↦ f x x.2) fun x ↦ f x.1.1 x.1.2]
  · 

Proof state:
α : Type u_1
inst✝¹ : DecidableEq α
m✝ : Multiset α
β : Type u_2
inst✝ : CommMonoid β
m : Multiset α
f : α → ℕ → β
⊢ ∏ x in toEnumFinset m, f x.1 x.2 = ∏ x : { x // x ∈ toEnumFinset m }, f (↑x).1 (↑x).2

Next tactic:
rw [← m.toEnumFinset.prod_coe_sort fun x ↦ f x.1 x.2]
@[to_additive]
theorem Multiset.prod_toEnumFinset {β : Type*} [CommMonoid β] (m : Multiset α) (f : α → ℕ → β) :
    ∏ x in m.toEnumFinset, f x.1 x.2 = ∏ x : m, f x x.2 := by
  rw [Fintype.prod_equiv m.coeEquiv (fun x ↦ f x x.2) fun x ↦ f x.1.1 x.1.2]
  · rw [← m.toEnumFinset.prod_coe_sort fun x ↦ f x.1 x.2]
  · 

Proof state:
α : Type u_1
inst✝¹ : DecidableEq α
m✝ : Multiset α
β : Type u_2
inst✝ : CommMonoid β
m : Multiset α
f : α → ℕ → β
⊢ ∀ (x : ToType m), f x.fst ↑x.snd = f (↑((coeEquiv m) x)).1 (↑((coeEquiv m) x)).2

Next tactic:
intro x
@[to_additive]
theorem Multiset.prod_toEnumFinset {β : Type*} [CommMonoid β] (m : Multiset α) (f : α → ℕ → β) :
    ∏ x in m.toEnumFinset, f x.1 x.2 = ∏ x : m, f x x.2 := by
  rw [Fintype.prod_equiv m.coeEquiv (fun x ↦ f x x.2) fun x ↦ f x.1.1 x.1.2]
  · rw [← m.toEnumFinset.prod_coe_sort fun x ↦ f x.1 x.2]
  · intro x
    

Proof state:
α : Type u_1
inst✝¹ : DecidableEq α
m✝ : Multiset α
β : Type u_2
inst✝ : CommMonoid β
m : Multiset α
f : α → ℕ → β
x : ToType m
⊢ f x.fst ↑x.snd = f (↑((coeEquiv m) x)).1 (↑((coeEquiv m) x)).2

Next tactic:
rfl
instance [Precoherent C] [HasFiniteCoproducts C] : Preregular C where
  exists_fac {X Y Z} f g _ := by
    

Proof state:
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Precoherent C
inst✝ : HasFiniteCoproducts C
X Y Z : C
f : X ⟶ Y
g : Z ⟶ Y
x✝ : EffectiveEpi g
⊢ ∃ W h, ∃ (_ : EffectiveEpi h), ∃ i, i ≫ g = h ≫ f

Next tactic:
have hp := Precoherent.pullback f PUnit (fun () ↦ Z) (fun () ↦ g)
instance [Precoherent C] [HasFiniteCoproducts C] : Preregular C where
  exists_fac {X Y Z} f g _ := by
    have hp := Precoherent.pullback f PUnit (fun () ↦ Z) (fun () ↦ g)
    

Proof state:
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Precoherent C
inst✝ : HasFiniteCoproducts C
X Y Z : C
f : X ⟶ Y
g : Z ⟶ Y
x✝ : EffectiveEpi g
hp :
  (EffectiveEpiFamily (fun x => Z) fun x =>
      match x with
      | PUnit.unit => g) →
    ∃ β x X₂ π₂,
      EffectiveEpiFamily X₂ π₂ ∧
        ∃ i ι,
          ∀ (b : β),
            (ι b ≫
                match i b with
                | PUnit.unit => g) =
              π₂ b ≫ f
⊢ ∃ W h, ∃ (_ : EffectiveEpi h), ∃ i, i ≫ g = h ≫ f

Next tactic:
simp only [exists_const] at hp
instance [Precoherent C] [HasFiniteCoproducts C] : Preregular C where
  exists_fac {X Y Z} f g _ := by
    have hp := Precoherent.pullback f PUnit (fun () ↦ Z) (fun () ↦ g)
    simp only [exists_const] at hp
    

Proof state:
C : Type u
inst✝² : Category.{v, u} C
inst✝¹ : Precoherent C
inst✝ : HasFiniteCoproducts C
X Y Z : C
f : X ⟶ Y
g : Z ⟶ Y
x✝ : EffectiveEpi g
hp :
  (EffectiveEpiFamily (fun x => Z) fun x => g) →
    ∃ β x X₂ π₂, EffectiveEpiFamily X₂ π₂ ∧ ∃ ι, ∀ (b : β), ι b ≫ g = π₂ b ≫ f
⊢ ∃ W h, ∃ (_ : EffectiveEpi h), ∃ i, i ≫ g = h ≫ f

Next tactic:
rw [← effectiveEpi_iff_effectiveEpiFamily g] at hp