l3lab/ntp-mathlib huggingface dataset:
@[to_additive (attr := simp)]
theorem prod_singleton (a : α) : prod {a} = a := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a✝ : α
m : Multiset ι
f g : ι → α
a : α
⊢ prod {a} = a

Next tactic:
simp only [mul_one, prod_cons, ← cons_zero, eq_self_iff_true, prod_zero]
@[to_additive]
theorem prod_pair (a b : α) : ({a, b} : Multiset α).prod = a * b := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a✝ : α
m : Multiset ι
f g : ι → α
a b : α
⊢ prod {a, b} = a * b

Next tactic:
rw [insert_eq_cons, prod_cons, prod_singleton]
@[to_additive (attr := simp)]
theorem prod_add (s t : Multiset α) : prod (s + t) = prod s * prod t :=
  Quotient.inductionOn₂ s t fun l₁ l₂ => by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s✝ t✝ : Multiset α
a : α
m : Multiset ι
f g : ι → α
s t : Multiset α
l₁ l₂ : List α
⊢ prod (⟦l₁⟧ + ⟦l₂⟧) = prod ⟦l₁⟧ * prod ⟦l₂⟧

Next tactic:
simp
@[to_additive]
theorem prod_nsmul (m : Multiset α) : ∀ n : ℕ, (n • m).prod = m.prod ^ n
  | 0 => by
    

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a : α
m✝ : Multiset ι
f g : ι → α
m : Multiset α
⊢ prod (0 • m) = prod m ^ 0

Next tactic:
rw [zero_nsmul, pow_zero]
@[to_additive]
theorem prod_nsmul (m : Multiset α) : ∀ n : ℕ, (n • m).prod = m.prod ^ n
  | 0 => by
    rw [zero_nsmul, pow_zero]
    

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a : α
m✝ : Multiset ι
f g : ι → α
m : Multiset α
⊢ prod 0 = 1

Next tactic:
rfl
@[to_additive]
theorem prod_nsmul (m : Multiset α) : ∀ n : ℕ, (n • m).prod = m.prod ^ n
  | 0 => by
    rw [zero_nsmul, pow_zero]
    rfl
  | n + 1 => by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a : α
m✝ : Multiset ι
f g : ι → α
m : Multiset α
n : ℕ
⊢ prod ((n + 1) • m) = prod m ^ (n + 1)

Next tactic:
rw [add_nsmul, one_nsmul, pow_add, pow_one, prod_add, prod_nsmul m n]
@[to_additive (attr := simp)]
theorem prod_replicate (n : ℕ) (a : α) : (replicate n a).prod = a ^ n := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a✝ : α
m : Multiset ι
f g : ι → α
n : ℕ
a : α
⊢ prod (replicate n a) = a ^ n

Next tactic:
simp [replicate, List.prod_replicate]
@[to_additive]
theorem prod_map_eq_pow_single [DecidableEq ι] (i : ι)
    (hf : ∀ i' ≠ i, i' ∈ m → f i' = 1) : (m.map f).prod = f i ^ m.count i := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝¹ : CommMonoid α
s t : Multiset α
a : α
m : Multiset ι
f g : ι → α
inst✝ : DecidableEq ι
i : ι
hf : ∀ (i' : ι), i' ≠ i → i' ∈ m → f i' = 1
⊢ prod (map f m) = f i ^ count i m

Next tactic:
induction' m using Quotient.inductionOn with l
@[to_additive]
theorem prod_map_eq_pow_single [DecidableEq ι] (i : ι)
    (hf : ∀ i' ≠ i, i' ∈ m → f i' = 1) : (m.map f).prod = f i ^ m.count i := by
  induction' m using Quotient.inductionOn with l
  

Proof state:
case h
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝¹ : CommMonoid α
s t : Multiset α
a : α
m : Multiset ι
f g : ι → α
inst✝ : DecidableEq ι
i : ι
l : List ι
hf : ∀ (i' : ι), i' ≠ i → i' ∈ ⟦l⟧ → f i' = 1
⊢ prod (map f ⟦l⟧) = f i ^ count i ⟦l⟧

Next tactic:
simp [List.prod_map_eq_pow_single i f hf]
@[to_additive]
theorem prod_eq_pow_single [DecidableEq α] (a : α) (h : ∀ a' ≠ a, a' ∈ s → a' = 1) :
    s.prod = a ^ s.count a := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝¹ : CommMonoid α
s t : Multiset α
a✝ : α
m : Multiset ι
f g : ι → α
inst✝ : DecidableEq α
a : α
h : ∀ (a' : α), a' ≠ a → a' ∈ s → a' = 1
⊢ prod s = a ^ count a s

Next tactic:
induction' s using Quotient.inductionOn with l
@[to_additive]
theorem prod_eq_pow_single [DecidableEq α] (a : α) (h : ∀ a' ≠ a, a' ∈ s → a' = 1) :
    s.prod = a ^ s.count a := by
  induction' s using Quotient.inductionOn with l
  

Proof state:
case h
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝¹ : CommMonoid α
s t : Multiset α
a✝ : α
m : Multiset ι
f g : ι → α
inst✝ : DecidableEq α
a : α
l : List α
h : ∀ (a' : α), a' ≠ a → a' ∈ ⟦l⟧ → a' = 1
⊢ prod ⟦l⟧ = a ^ count a ⟦l⟧

Next tactic:
simp [List.prod_eq_pow_single a h]
@[to_additive]
theorem pow_count [DecidableEq α] (a : α) : a ^ s.count a = (s.filter (Eq a)).prod := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝¹ : CommMonoid α
s t : Multiset α
a✝ : α
m : Multiset ι
f g : ι → α
inst✝ : DecidableEq α
a : α
⊢ a ^ count a s = prod (filter (Eq a) s)

Next tactic:
rw [filter_eq, prod_replicate]
@[to_additive]
theorem prod_hom [CommMonoid β] (s : Multiset α) {F : Type*} [MonoidHomClass F α β] (f : F) :
    (s.map f).prod = f s.prod :=
  Quotient.inductionOn s fun l => by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝² : CommMonoid α
s✝ t : Multiset α
a : α
m : Multiset ι
f✝ g : ι → α
inst✝¹ : CommMonoid β
s : Multiset α
F : Type u_5
inst✝ : MonoidHomClass F α β
f : F
l : List α
⊢ prod (map ⇑f ⟦l⟧) = f (prod ⟦l⟧)

Next tactic:
simp only [l.prod_hom f, quot_mk_to_coe, coe_map, coe_prod]
@[to_additive]
theorem prod_hom' [CommMonoid β] (s : Multiset ι) {F : Type*} [MonoidHomClass F α β] (f : F)
    (g : ι → α) : (s.map fun i => f <| g i).prod = f (s.map g).prod := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝² : CommMonoid α
s✝ t : Multiset α
a : α
m : Multiset ι
f✝ g✝ : ι → α
inst✝¹ : CommMonoid β
s : Multiset ι
F : Type u_5
inst✝ : MonoidHomClass F α β
f : F
g : ι → α
⊢ prod (map (fun i => f (g i)) s) = f (prod (map g s))

Next tactic:
convert (s.map g).prod_hom f
@[to_additive]
theorem prod_hom' [CommMonoid β] (s : Multiset ι) {F : Type*} [MonoidHomClass F α β] (f : F)
    (g : ι → α) : (s.map fun i => f <| g i).prod = f (s.map g).prod := by
  convert (s.map g).prod_hom f
  

Proof state:
case h.e'_2.h.e'_3
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝² : CommMonoid α
s✝ t : Multiset α
a : α
m : Multiset ι
f✝ g✝ : ι → α
inst✝¹ : CommMonoid β
s : Multiset ι
F : Type u_5
inst✝ : MonoidHomClass F α β
f : F
g : ι → α
⊢ map (fun i => f (g i)) s = map (⇑f) (map g s)

Next tactic:
exact (map_map _ _ _).symm
@[to_additive]
theorem prod_hom₂ [CommMonoid β] [CommMonoid γ] (s : Multiset ι) (f : α → β → γ)
    (hf : ∀ a b c d, f (a * b) (c * d) = f a c * f b d) (hf' : f 1 1 = 1) (f₁ : ι → α)
    (f₂ : ι → β) : (s.map fun i => f (f₁ i) (f₂ i)).prod = f (s.map f₁).prod (s.map f₂).prod :=
  Quotient.inductionOn s fun l => by
    

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝² : CommMonoid α
s✝ t : Multiset α
a : α
m : Multiset ι
f✝ g : ι → α
inst✝¹ : CommMonoid β
inst✝ : CommMonoid γ
s : Multiset ι
f : α → β → γ
hf : ∀ (a b : α) (c d : β), f (a * b) (c * d) = f a c * f b d
hf' : f 1 1 = 1
f₁ : ι → α
f₂ : ι → β
l : List ι
⊢ prod (map (fun i => f (f₁ i) (f₂ i)) ⟦l⟧) = f (prod (map f₁ ⟦l⟧)) (prod (map f₂ ⟦l⟧))

Next tactic:
simp only [l.prod_hom₂ f hf hf', quot_mk_to_coe, coe_map, coe_prod]
@[to_additive]
theorem prod_hom_rel [CommMonoid β] (s : Multiset ι) {r : α → β → Prop} {f : ι → α} {g : ι → β}
    (h₁ : r 1 1) (h₂ : ∀ ⦃a b c⦄, r b c → r (f a * b) (g a * c)) :
    r (s.map f).prod (s.map g).prod :=
  Quotient.inductionOn s fun l => by
    

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝¹ : CommMonoid α
s✝ t : Multiset α
a : α
m : Multiset ι
f✝ g✝ : ι → α
inst✝ : CommMonoid β
s : Multiset ι
r : α → β → Prop
f : ι → α
g : ι → β
h₁ : r 1 1
h₂ : ∀ ⦃a : ι⦄ ⦃b : α⦄ ⦃c : β⦄, r b c → r (f a * b) (g a * c)
l : List ι
⊢ r (prod (map f ⟦l⟧)) (prod (map g ⟦l⟧))

Next tactic:
simp only [l.prod_hom_rel h₁ h₂, quot_mk_to_coe, coe_map, coe_prod]
@[to_additive]
theorem prod_map_one : prod (m.map fun _ => (1 : α)) = 1 := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a : α
m : Multiset ι
f g : ι → α
⊢ prod (map (fun x => 1) m) = 1

Next tactic:
rw [map_const', prod_replicate, one_pow]
@[simp]
theorem prod_map_neg [HasDistribNeg α] (s : Multiset α) :
    (s.map Neg.neg).prod = (-1) ^ card s * s.prod :=
  Quotient.inductionOn s (by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝¹ : CommMonoid α
s✝ t : Multiset α
a : α
m : Multiset ι
f g : ι → α
inst✝ : HasDistribNeg α
s : Multiset α
⊢ ∀ (a : List α), prod (map Neg.neg ⟦a⟧) = (-1) ^ card ⟦a⟧ * prod ⟦a⟧

Next tactic:
simp
@[to_additive]
theorem prod_map_prod_map (m : Multiset β) (n : Multiset γ) {f : β → γ → α} :
    prod (m.map fun a => prod <| n.map fun b => f a b) =
      prod (n.map fun b => prod <| m.map fun a => f a b) :=
  Multiset.induction_on m (by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a : α
m✝ : Multiset ι
f✝ g : ι → α
m : Multiset β
n : Multiset γ
f : β → γ → α
⊢ prod (map (fun a => prod (map (fun b => f a b) n)) 0) = prod (map (fun b => prod (map (fun a => f a b) 0)) n)

Next tactic:
simp
@[to_additive]
theorem prod_map_prod_map (m : Multiset β) (n : Multiset γ) {f : β → γ → α} :
    prod (m.map fun a => prod <| n.map fun b => f a b) =
      prod (n.map fun b => prod <| m.map fun a => f a b) :=
  Multiset.induction_on m (by simp) fun a m ih => by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a✝ : α
m✝¹ : Multiset ι
f✝ g : ι → α
m✝ : Multiset β
n : Multiset γ
f : β → γ → α
a : β
m : Multiset β
ih : prod (map (fun a => prod (map (fun b => f a b) n)) m) = prod (map (fun b => prod (map (fun a => f a b) m)) n)
⊢ prod (map (fun a => prod (map (fun b => f a b) n)) (a ::ₘ m)) =
    prod (map (fun b => prod (map (fun a => f a b) (a ::ₘ m))) n)

Next tactic:
simp [ih]
@[to_additive]
theorem prod_induction (p : α → Prop) (s : Multiset α) (p_mul : ∀ a b, p a → p b → p (a * b))
    (p_one : p 1) (p_s : ∀ a ∈ s, p a) : p s.prod := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s✝ t : Multiset α
a : α
m : Multiset ι
f g : ι → α
p : α → Prop
s : Multiset α
p_mul : ∀ (a b : α), p a → p b → p (a * b)
p_one : p 1
p_s : ∀ a ∈ s, p a
⊢ p (prod s)

Next tactic:
rw [prod_eq_foldr]
@[to_additive]
theorem prod_induction (p : α → Prop) (s : Multiset α) (p_mul : ∀ a b, p a → p b → p (a * b))
    (p_one : p 1) (p_s : ∀ a ∈ s, p a) : p s.prod := by
  rw [prod_eq_foldr]
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s✝ t : Multiset α
a : α
m : Multiset ι
f g : ι → α
p : α → Prop
s : Multiset α
p_mul : ∀ (a b : α), p a → p b → p (a * b)
p_one : p 1
p_s : ∀ a ∈ s, p a
⊢ p (foldr (fun x x_1 => x * x_1) (_ : ∀ (x y z : α), x * (y * z) = y * (x * z)) 1 s)

Next tactic:
exact foldr_induction (· * ·) (fun x y z => by simp [mul_left_comm]) 1 p s p_mul p_one p_s
@[to_additive]
theorem prod_induction (p : α → Prop) (s : Multiset α) (p_mul : ∀ a b, p a → p b → p (a * b))
    (p_one : p 1) (p_s : ∀ a ∈ s, p a) : p s.prod := by
  rw [prod_eq_foldr]
  exact foldr_induction (· * ·) (fun x y z => by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s✝ t : Multiset α
a : α
m : Multiset ι
f g : ι → α
p : α → Prop
s : Multiset α
p_mul : ∀ (a b : α), p a → p b → p (a * b)
p_one : p 1
p_s : ∀ a ∈ s, p a
x y z : α
⊢ (fun x x_1 => x * x_1) x ((fun x x_1 => x * x_1) y z) = (fun x x_1 => x * x_1) y ((fun x x_1 => x * x_1) x z)

Next tactic:
simp [mul_left_comm]
@[to_additive]
theorem prod_induction_nonempty (p : α → Prop) (p_mul : ∀ a b, p a → p b → p (a * b)) (hs : s ≠ ∅)
    (p_s : ∀ a ∈ s, p a) : p s.prod := by
  -- Porting note: used `refine' Multiset.induction _ _`
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a : α
m : Multiset ι
f g : ι → α
p : α → Prop
p_mul : ∀ (a b : α), p a → p b → p (a * b)
hs : s ≠ ∅
p_s : ∀ a ∈ s, p a
⊢ p (prod s)

Next tactic:
induction' s using Multiset.induction_on with a s hsa
@[to_additive]
theorem prod_induction_nonempty (p : α → Prop) (p_mul : ∀ a b, p a → p b → p (a * b)) (hs : s ≠ ∅)
    (p_s : ∀ a ∈ s, p a) : p s.prod := by
  -- Porting note: used `refine' Multiset.induction _ _`
  induction' s using Multiset.induction_on with a s hsa
  · 

Proof state:
case empty
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a : α
m : Multiset ι
f g : ι → α
p : α → Prop
p_mul : ∀ (a b : α), p a → p b → p (a * b)
hs : 0 ≠ ∅
p_s : ∀ a ∈ 0, p a
⊢ p (prod 0)

Next tactic:
simp at hs
@[to_additive]
theorem prod_induction_nonempty (p : α → Prop) (p_mul : ∀ a b, p a → p b → p (a * b)) (hs : s ≠ ∅)
    (p_s : ∀ a ∈ s, p a) : p s.prod := by
  -- Porting note: used `refine' Multiset.induction _ _`
  induction' s using Multiset.induction_on with a s hsa
  · simp at hs
  

Proof state:
case cons
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s✝ t : Multiset α
a✝ : α
m : Multiset ι
f g : ι → α
p : α → Prop
p_mul : ∀ (a b : α), p a → p b → p (a * b)
a : α
s : Multiset α
hsa : s ≠ ∅ → (∀ a ∈ s, p a) → p (prod s)
hs : a ::ₘ s ≠ ∅
p_s : ∀ a_1 ∈ a ::ₘ s, p a_1
⊢ p (prod (a ::ₘ s))

Next tactic:
rw [prod_cons]
@[to_additive]
theorem prod_induction_nonempty (p : α → Prop) (p_mul : ∀ a b, p a → p b → p (a * b)) (hs : s ≠ ∅)
    (p_s : ∀ a ∈ s, p a) : p s.prod := by
  -- Porting note: used `refine' Multiset.induction _ _`
  induction' s using Multiset.induction_on with a s hsa
  · simp at hs
  rw [prod_cons]
  

Proof state:
case cons
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s✝ t : Multiset α
a✝ : α
m : Multiset ι
f g : ι → α
p : α → Prop
p_mul : ∀ (a b : α), p a → p b → p (a * b)
a : α
s : Multiset α
hsa : s ≠ ∅ → (∀ a ∈ s, p a) → p (prod s)
hs : a ::ₘ s ≠ ∅
p_s : ∀ a_1 ∈ a ::ₘ s, p a_1
⊢ p (a * prod s)

Next tactic:
by_cases hs_empty : s = ∅
@[to_additive]
theorem prod_induction_nonempty (p : α → Prop) (p_mul : ∀ a b, p a → p b → p (a * b)) (hs : s ≠ ∅)
    (p_s : ∀ a ∈ s, p a) : p s.prod := by
  -- Porting note: used `refine' Multiset.induction _ _`
  induction' s using Multiset.induction_on with a s hsa
  · simp at hs
  rw [prod_cons]
  by_cases hs_empty : s = ∅
  · 

Proof state:
case pos
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s✝ t : Multiset α
a✝ : α
m : Multiset ι
f g : ι → α
p : α → Prop
p_mul : ∀ (a b : α), p a → p b → p (a * b)
a : α
s : Multiset α
hsa : s ≠ ∅ → (∀ a ∈ s, p a) → p (prod s)
hs : a ::ₘ s ≠ ∅
p_s : ∀ a_1 ∈ a ::ₘ s, p a_1
hs_empty : s = ∅
⊢ p (a * prod s)

Next tactic:
simp [hs_empty, p_s a]
@[to_additive]
theorem prod_induction_nonempty (p : α → Prop) (p_mul : ∀ a b, p a → p b → p (a * b)) (hs : s ≠ ∅)
    (p_s : ∀ a ∈ s, p a) : p s.prod := by
  -- Porting note: used `refine' Multiset.induction _ _`
  induction' s using Multiset.induction_on with a s hsa
  · simp at hs
  rw [prod_cons]
  by_cases hs_empty : s = ∅
  · simp [hs_empty, p_s a]
  

Proof state:
case neg
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s✝ t : Multiset α
a✝ : α
m : Multiset ι
f g : ι → α
p : α → Prop
p_mul : ∀ (a b : α), p a → p b → p (a * b)
a : α
s : Multiset α
hsa : s ≠ ∅ → (∀ a ∈ s, p a) → p (prod s)
hs : a ::ₘ s ≠ ∅
p_s : ∀ a_1 ∈ a ::ₘ s, p a_1
hs_empty : ¬s = ∅
⊢ p (a * prod s)

Next tactic:
have hps : ∀ x, x ∈ s → p x := fun x hxs => p_s x (mem_cons_of_mem hxs)
@[to_additive]
theorem prod_induction_nonempty (p : α → Prop) (p_mul : ∀ a b, p a → p b → p (a * b)) (hs : s ≠ ∅)
    (p_s : ∀ a ∈ s, p a) : p s.prod := by
  -- Porting note: used `refine' Multiset.induction _ _`
  induction' s using Multiset.induction_on with a s hsa
  · simp at hs
  rw [prod_cons]
  by_cases hs_empty : s = ∅
  · simp [hs_empty, p_s a]
  have hps : ∀ x, x ∈ s → p x := fun x hxs => p_s x (mem_cons_of_mem hxs)
  

Proof state:
case neg
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s✝ t : Multiset α
a✝ : α
m : Multiset ι
f g : ι → α
p : α → Prop
p_mul : ∀ (a b : α), p a → p b → p (a * b)
a : α
s : Multiset α
hsa : s ≠ ∅ → (∀ a ∈ s, p a) → p (prod s)
hs : a ::ₘ s ≠ ∅
p_s : ∀ a_1 ∈ a ::ₘ s, p a_1
hs_empty : ¬s = ∅
hps : ∀ x ∈ s, p x
⊢ p (a * prod s)

Next tactic:
exact p_mul a s.prod (p_s a (mem_cons_self a s)) (hsa hs_empty hps)
theorem prod_dvd_prod_of_le (h : s ≤ t) : s.prod ∣ t.prod := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s t : Multiset α
a : α
m : Multiset ι
f g : ι → α
h : s ≤ t
⊢ prod s ∣ prod t

Next tactic:
obtain ⟨z, rfl⟩ := exists_add_of_le h
theorem prod_dvd_prod_of_le (h : s ≤ t) : s.prod ∣ t.prod := by
  obtain ⟨z, rfl⟩ := exists_add_of_le h
  

Proof state:
case intro
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid α
s : Multiset α
a : α
m : Multiset ι
f g : ι → α
z : Multiset α
h : s ≤ s + z
⊢ prod s ∣ prod (s + z)

Next tactic:
simp only [prod_add, dvd_mul_right]
theorem prod_dvd_prod_of_dvd [CommMonoid β] {S : Multiset α} (g1 g2 : α → β)
    (h : ∀ a ∈ S, g1 a ∣ g2 a) : (Multiset.map g1 S).prod ∣ (Multiset.map g2 S).prod := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid β
S : Multiset α
g1 g2 : α → β
h : ∀ a ∈ S, g1 a ∣ g2 a
⊢ prod (map g1 S) ∣ prod (map g2 S)

Next tactic:
apply Multiset.induction_on' S
theorem prod_dvd_prod_of_dvd [CommMonoid β] {S : Multiset α} (g1 g2 : α → β)
    (h : ∀ a ∈ S, g1 a ∣ g2 a) : (Multiset.map g1 S).prod ∣ (Multiset.map g2 S).prod := by
  apply Multiset.induction_on' S
  · 

Proof state:
case h₁
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid β
S : Multiset α
g1 g2 : α → β
h : ∀ a ∈ S, g1 a ∣ g2 a
⊢ prod (map g1 0) ∣ prod (map g2 0)

Next tactic:
simp
theorem prod_dvd_prod_of_dvd [CommMonoid β] {S : Multiset α} (g1 g2 : α → β)
    (h : ∀ a ∈ S, g1 a ∣ g2 a) : (Multiset.map g1 S).prod ∣ (Multiset.map g2 S).prod := by
  apply Multiset.induction_on' S
  · simp
  

Proof state:
case h₂
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid β
S : Multiset α
g1 g2 : α → β
h : ∀ a ∈ S, g1 a ∣ g2 a
⊢ ∀ {a : α} {s : Multiset α},
    a ∈ S → s ⊆ S → prod (map g1 s) ∣ prod (map g2 s) → prod (map g1 (insert a s)) ∣ prod (map g2 (insert a s))

Next tactic:
intro a T haS _ IH
theorem prod_dvd_prod_of_dvd [CommMonoid β] {S : Multiset α} (g1 g2 : α → β)
    (h : ∀ a ∈ S, g1 a ∣ g2 a) : (Multiset.map g1 S).prod ∣ (Multiset.map g2 S).prod := by
  apply Multiset.induction_on' S
  · simp
  intro a T haS _ IH
  

Proof state:
case h₂
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoid β
S : Multiset α
g1 g2 : α → β
h : ∀ a ∈ S, g1 a ∣ g2 a
a : α
T : Multiset α
haS : a ∈ S
a✝ : T ⊆ S
IH : prod (map g1 T) ∣ prod (map g2 T)
⊢ prod (map g1 (insert a T)) ∣ prod (map g2 (insert a T))

Next tactic:
simp [mul_dvd_mul (h a haS) IH]
theorem prod_eq_zero {s : Multiset α} (h : (0 : α) ∈ s) : s.prod = 0 := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoidWithZero α
s : Multiset α
h : 0 ∈ s
⊢ prod s = 0

Next tactic:
rcases Multiset.exists_cons_of_mem h with ⟨s', hs'⟩
theorem prod_eq_zero {s : Multiset α} (h : (0 : α) ∈ s) : s.prod = 0 := by
  rcases Multiset.exists_cons_of_mem h with ⟨s', hs'⟩
  

Proof state:
case intro
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : CommMonoidWithZero α
s : Multiset α
h : 0 ∈ s
s' : Multiset α
hs' : s = 0 ::ₘ s'
⊢ prod s = 0

Next tactic:
simp [hs', Multiset.prod_cons]
theorem prod_eq_zero_iff : s.prod = 0 ↔ (0 : α) ∈ s :=
  Quotient.inductionOn s fun l => by
    

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝² : CommMonoidWithZero α
inst✝¹ : NoZeroDivisors α
inst✝ : Nontrivial α
s : Multiset α
l : List α
⊢ prod ⟦l⟧ = 0 ↔ 0 ∈ ⟦l⟧

Next tactic:
rw [quot_mk_to_coe, coe_prod]
theorem prod_eq_zero_iff : s.prod = 0 ↔ (0 : α) ∈ s :=
  Quotient.inductionOn s fun l => by
    rw [quot_mk_to_coe, coe_prod]
    

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝² : CommMonoidWithZero α
inst✝¹ : NoZeroDivisors α
inst✝ : Nontrivial α
s : Multiset α
l : List α
⊢ List.prod l = 0 ↔ 0 ∈ ↑l

Next tactic:
exact List.prod_eq_zero_iff
@[to_additive (attr := simp)]
theorem prod_map_inv : (m.map fun i => (f i)⁻¹).prod = (m.map f).prod⁻¹ := by
  -- Porting note: used `convert`
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : DivisionCommMonoid α
m : Multiset ι
f g : ι → α
⊢ prod (map (fun i => (f i)⁻¹) m) = (prod (map f m))⁻¹

Next tactic:
simp_rw [← (m.map f).prod_map_inv', map_map, Function.comp_apply]
@[to_additive]
theorem prod_map_zpow {n : ℤ} : (m.map fun i => f i ^ n).prod = (m.map f).prod ^ n := by
  

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : DivisionCommMonoid α
m : Multiset ι
f g : ι → α
n : ℤ
⊢ prod (map (fun i => f i ^ n) m) = prod (map f m) ^ n

Next tactic:
convert (m.map f).prod_hom (zpowGroupHom n : α →* α)
@[to_additive]
theorem prod_map_zpow {n : ℤ} : (m.map fun i => f i ^ n).prod = (m.map f).prod ^ n := by
  convert (m.map f).prod_hom (zpowGroupHom n : α →* α)
  

Proof state:
case h.e'_2.h.e'_3
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : DivisionCommMonoid α
m : Multiset ι
f g : ι → α
n : ℤ
⊢ map (fun i => f i ^ n) m = map (⇑(zpowGroupHom n)) (map f m)

Next tactic:
simp only [map_map, Function.comp_apply, zpowGroupHom_apply]
theorem sum_map_mul_left : sum (s.map fun i => a * f i) = a * sum (s.map f) :=
  Multiset.induction_on s (by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : NonUnitalNonAssocSemiring α
a : α
s : Multiset ι
f : ι → α
⊢ sum (map (fun i => a * f i) 0) = a * sum (map f 0)

Next tactic:
simp
theorem sum_map_mul_left : sum (s.map fun i => a * f i) = a * sum (s.map f) :=
  Multiset.induction_on s (by simp) fun i s ih => by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : NonUnitalNonAssocSemiring α
a : α
s✝ : Multiset ι
f : ι → α
i : ι
s : Multiset ι
ih : sum (map (fun i => a * f i) s) = a * sum (map f s)
⊢ sum (map (fun i => a * f i) (i ::ₘ s)) = a * sum (map f (i ::ₘ s))

Next tactic:
simp [ih, mul_add]
theorem sum_map_mul_right : sum (s.map fun i => f i * a) = sum (s.map f) * a :=
  Multiset.induction_on s (by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : NonUnitalNonAssocSemiring α
a : α
s : Multiset ι
f : ι → α
⊢ sum (map (fun i => f i * a) 0) = sum (map f 0) * a

Next tactic:
simp
theorem sum_map_mul_right : sum (s.map fun i => f i * a) = sum (s.map f) * a :=
  Multiset.induction_on s (by simp) fun a s ih => by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : NonUnitalNonAssocSemiring α
a✝ : α
s✝ : Multiset ι
f : ι → α
a : ι
s : Multiset ι
ih : sum (map (fun i => f i * a✝) s) = sum (map f s) * a✝
⊢ sum (map (fun i => f i * a✝) (a ::ₘ s)) = sum (map f (a ::ₘ s)) * a✝

Next tactic:
simp [ih, add_mul]
theorem dvd_sum {a : α} {s : Multiset α} : (∀ x ∈ s, a ∣ x) → a ∣ s.sum :=
  Multiset.induction_on s (fun _ => dvd_zero _) fun x s ih h => by
    

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : NonUnitalSemiring α
a : α
s✝ : Multiset α
x : α
s : Multiset α
ih : (∀ x ∈ s, a ∣ x) → a ∣ sum s
h : ∀ x_1 ∈ x ::ₘ s, a ∣ x_1
⊢ a ∣ sum (x ::ₘ s)

Next tactic:
rw [sum_cons]
theorem dvd_sum {a : α} {s : Multiset α} : (∀ x ∈ s, a ∣ x) → a ∣ s.sum :=
  Multiset.induction_on s (fun _ => dvd_zero _) fun x s ih h => by
    rw [sum_cons]
    

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : NonUnitalSemiring α
a : α
s✝ : Multiset α
x : α
s : Multiset α
ih : (∀ x ∈ s, a ∣ x) → a ∣ sum s
h : ∀ x_1 ∈ x ::ₘ s, a ∣ x_1
⊢ a ∣ x + sum s

Next tactic:
exact dvd_add (h _ (mem_cons_self _ _)) (ih fun y hy => h _ <| mem_cons.2 <| Or.inr hy)
@[to_additive sum_nonneg]
theorem one_le_prod_of_one_le : (∀ x ∈ s, (1 : α) ≤ x) → 1 ≤ s.prod :=
  Quotient.inductionOn s fun l hl => by 

Proof state:
ι : Type u_1
α : Type u_2
β : Type u_3
γ : Type u_4
inst✝ : OrderedCommMonoid α
s t : Multiset α
a : α
l : List α
hl : ∀ x ∈ ⟦l⟧, 1 ≤ x
⊢ 1 ≤ prod ⟦l⟧

Next tactic:
simpa using List.one_le_prod_of_one_le hl