l3lab/ntp-mathlib huggingface dataset:
/-- The coefficients of the monic polynomials of bounded degree with bounded roots are
uniformly bounded. -/
theorem coeff_bdd_of_roots_le {B : ℝ} {d : ℕ} (f : F →+* K) {p : F[X]} (h1 : p.Monic)
    (h2 : Splits f p) (h3 : p.natDegree ≤ d) (h4 : ∀ z ∈ (map f p).roots, ‖z‖ ≤ B) (i : ℕ) :
    ‖(map f p).coeff i‖ ≤ max B 1 ^ d * d.choose (d / 2) := by
  obtain hB | hB := le_or_lt 0 B
  · apply (coeff_le_of_roots_le i h1 h2 h4).trans
    calc
      _ ≤ max B 1 ^ (p.natDegree - i) * p.natDegree.choose i := by gcongr; apply le_max_left
      _ ≤ max B 1 ^ d * p.natDegree.choose i := by
        gcongr; apply le_max_right; exact le_trans (Nat.sub_le _ _) h3
      _ ≤ max B 1 ^ d * d.choose (d / 2) := by
        gcongr; exact (i.choose_mono h3).trans (i.choose_le_middle d)
  · rw [eq_one_of_roots_le hB h1 h2 h4, Polynomial.map_one, coeff_one]
    refine' _root_.trans _
      (one_le_mul_of_one_le_of_one_le (one_le_pow_of_one_le (le_max_right B 1) d) _)
    · split_ifs <;> norm_num
    · 

Proof state:
case inr.refine'_2
α : Type u_1
R : Type u_2
inst✝³ : NormedRing R
inst✝² : IsAbsoluteValue norm
F : Type u_3
K : Type u_4
inst✝¹ : CommRing F
inst✝ : NormedField K
B : ℝ
d : ℕ
f : F →+* K
p : F[X]
h1 : Monic p
h2 : Splits f p
h3 : natDegree p ≤ d
h4 : ∀ z ∈ roots (map f p), ‖z‖ ≤ B
i : ℕ
hB : B < 0
⊢ 1 ≤ ↑(Nat.choose d (d / 2))

Next tactic:
exact mod_cast Nat.succ_le_iff.mpr (Nat.choose_pos (d.div_le_self 2))
instance addZeroClass [AddZeroClass α] : AddZeroClass αᵐᵒᵖ :=
  unop_injective.addZeroClass _ (by 

Proof state:
α : Type u
inst✝ : AddZeroClass α
⊢ unop 0 = 0

Next tactic:
exact rfl
instance addMonoid [AddMonoid α] : AddMonoid αᵐᵒᵖ :=
  unop_injective.addMonoid _ (by 

Proof state:
α : Type u
inst✝ : AddMonoid α
⊢ unop 0 = 0

Next tactic:
exact rfl
instance addMonoidWithOne [AddMonoidWithOne α] : AddMonoidWithOne αᵐᵒᵖ :=
  { MulOpposite.addMonoid α, MulOpposite.one α, MulOpposite.natCast _ with
    natCast_zero := show op ((0 : ℕ) : α) = 0 by 

Proof state:
α : Type u
inst✝ : AddMonoidWithOne α
src✝² : AddMonoid αᵐᵒᵖ := addMonoid α
src✝¹ : One αᵐᵒᵖ := one α
src✝ : NatCast αᵐᵒᵖ := natCast α
⊢ op ↑0 = 0

Next tactic:
rw [Nat.cast_zero, op_zero]
instance addMonoidWithOne [AddMonoidWithOne α] : AddMonoidWithOne αᵐᵒᵖ :=
  { MulOpposite.addMonoid α, MulOpposite.one α, MulOpposite.natCast _ with
    natCast_zero := show op ((0 : ℕ) : α) = 0 by rw [Nat.cast_zero, op_zero]
    natCast_succ := show ∀ n, op ((n + 1 : ℕ) : α) = op ↑(n : ℕ) + 1 by 

Proof state:
α : Type u
inst✝ : AddMonoidWithOne α
src✝² : AddMonoid αᵐᵒᵖ := addMonoid α
src✝¹ : One αᵐᵒᵖ := one α
src✝ : NatCast αᵐᵒᵖ := natCast α
⊢ ∀ (n : ℕ), op ↑(n + 1) = op ↑n + 1

Next tactic:
simp
instance subNegMonoid [SubNegMonoid α] : SubNegMonoid αᵐᵒᵖ :=
  unop_injective.subNegMonoid _ (by 

Proof state:
α : Type u
inst✝ : SubNegMonoid α
⊢ unop 0 = 0

Next tactic:
exact rfl
instance addGroup [AddGroup α] : AddGroup αᵐᵒᵖ :=
  unop_injective.addGroup _ (by 

Proof state:
α : Type u
inst✝ : AddGroup α
⊢ unop 0 = 0

Next tactic:
exact rfl
instance addGroupWithOne [AddGroupWithOne α] : AddGroupWithOne αᵐᵒᵖ :=
  { MulOpposite.addMonoidWithOne α, MulOpposite.addGroup α with
    intCast := fun n => op n,
    intCast_ofNat := fun n => show op ((n : ℤ) : α) = op (n : α) by 

Proof state:
α : Type u
inst✝ : AddGroupWithOne α
src✝¹ : AddMonoidWithOne αᵐᵒᵖ := addMonoidWithOne α
src✝ : AddGroup αᵐᵒᵖ := addGroup α
n : ℕ
⊢ op ↑↑n = op ↑n

Next tactic:
rw [Int.cast_ofNat]
instance addGroupWithOne [AddGroupWithOne α] : AddGroupWithOne αᵐᵒᵖ :=
  { MulOpposite.addMonoidWithOne α, MulOpposite.addGroup α with
    intCast := fun n => op n,
    intCast_ofNat := fun n => show op ((n : ℤ) : α) = op (n : α) by rw [Int.cast_ofNat],
    intCast_negSucc := fun n =>
      show op _ = op (-unop (op ((n + 1 : ℕ) : α))) by 

Proof state:
α : Type u
inst✝ : AddGroupWithOne α
src✝¹ : AddMonoidWithOne αᵐᵒᵖ := addMonoidWithOne α
src✝ : AddGroup αᵐᵒᵖ := addGroup α
n : ℕ
⊢ op ↑(Int.negSucc n) = op (-unop (op ↑(n + 1)))

Next tactic:
simp
@[to_additive AddOpposite.subNegMonoid]
instance divInvMonoid [DivInvMonoid α] : DivInvMonoid αᵐᵒᵖ :=
  { MulOpposite.monoid α, MulOpposite.inv α with
    zpow := fun n x => op <| x.unop ^ n,
    zpow_zero' := fun x => unop_injective <| DivInvMonoid.zpow_zero' x.unop,
    zpow_succ' := fun n x => unop_injective <| by
      

Proof state:
α : Type u
inst✝ : DivInvMonoid α
src✝¹ : Monoid αᵐᵒᵖ := monoid α
src✝ : Inv αᵐᵒᵖ := inv α
n : ℕ
x : αᵐᵒᵖ
⊢ unop ((fun n x => op (unop x ^ n)) (Int.ofNat (Nat.succ n)) x) =
    unop (x * (fun n x => op (unop x ^ n)) (Int.ofNat n) x)

Next tactic:
simp only [Int.ofNat_eq_coe]
@[to_additive AddOpposite.subNegMonoid]
instance divInvMonoid [DivInvMonoid α] : DivInvMonoid αᵐᵒᵖ :=
  { MulOpposite.monoid α, MulOpposite.inv α with
    zpow := fun n x => op <| x.unop ^ n,
    zpow_zero' := fun x => unop_injective <| DivInvMonoid.zpow_zero' x.unop,
    zpow_succ' := fun n x => unop_injective <| by
      simp only [Int.ofNat_eq_coe]
      

Proof state:
α : Type u
inst✝ : DivInvMonoid α
src✝¹ : Monoid αᵐᵒᵖ := monoid α
src✝ : Inv αᵐᵒᵖ := inv α
n : ℕ
x : αᵐᵒᵖ
⊢ unop (op (unop x ^ ↑(Nat.succ n))) = unop (x * op (unop x ^ ↑n))

Next tactic:
rw [unop_op, zpow_ofNat, pow_succ', unop_mul, unop_op, zpow_ofNat]
@[to_additive (attr := simp)]
theorem op_div [DivInvMonoid α] (x y : α) : op (x / y) = (op y)⁻¹ * op x := by 

Proof state:
α : Type u
inst✝ : DivInvMonoid α
x y : α
⊢ op (x / y) = (op y)⁻¹ * op x

Next tactic:
simp [div_eq_mul_inv]
@[to_additive (attr := simp)]
theorem semiconjBy_op [Mul α] {a x y : α} : SemiconjBy (op a) (op y) (op x) ↔ SemiconjBy a x y :=
  by 

Proof state:
α : Type u
inst✝ : Mul α
a x y : α
⊢ SemiconjBy (op a) (op y) (op x) ↔ SemiconjBy a x y

Next tactic:
simp only [SemiconjBy, ← op_mul, op_inj, eq_comm]
@[to_additive (attr := simp, nolint simpComm)]
theorem semiconjBy_unop [Mul α] {a x y : αᵐᵒᵖ} :
    SemiconjBy (unop a) (unop y) (unop x) ↔ SemiconjBy a x y := by
  

Proof state:
α : Type u
inst✝ : Mul α
a x y : αᵐᵒᵖ
⊢ SemiconjBy (unop a) (unop y) (unop x) ↔ SemiconjBy a x y

Next tactic:
conv_rhs => rw [← op_unop a, ← op_unop x, ← op_unop y, semiconjBy_op]
@[to_additive (attr := simp, nolint simpComm)]
theorem semiconjBy_unop [Mul α] {a x y : αᵐᵒᵖ} :
    SemiconjBy (unop a) (unop y) (unop x) ↔ SemiconjBy a x y := by
  conv_rhs => 

Proof state:
α : Type u
inst✝ : Mul α
a x y : αᵐᵒᵖ
| SemiconjBy a x y

Next tactic:
rw [← op_unop a, ← op_unop x, ← op_unop y, semiconjBy_op]
instance mulOneClass [MulOneClass α] : MulOneClass αᵃᵒᵖ :=
  unop_injective.mulOneClass _ (by 

Proof state:
α : Type u
inst✝ : MulOneClass α
⊢ unop 1 = 1

Next tactic:
exact rfl
instance monoid [Monoid α] : Monoid αᵃᵒᵖ :=
  unop_injective.monoid _ (by 

Proof state:
α : Type u
inst✝ : Monoid α
⊢ unop 1 = 1

Next tactic:
exact rfl
instance commMonoid [CommMonoid α] : CommMonoid αᵃᵒᵖ :=
  unop_injective.commMonoid _ (by 

Proof state:
α : Type u
inst✝ : CommMonoid α
⊢ unop 1 = 1

Next tactic:
exact rfl
instance divInvMonoid [DivInvMonoid α] : DivInvMonoid αᵃᵒᵖ :=
  unop_injective.divInvMonoid _ (by 

Proof state:
α : Type u
inst✝ : DivInvMonoid α
⊢ unop 1 = 1

Next tactic:
exact rfl
instance group [Group α] : Group αᵃᵒᵖ :=
  unop_injective.group _ (by 

Proof state:
α : Type u
inst✝ : Group α
⊢ unop 1 = 1

Next tactic:
exact rfl
instance commGroup [CommGroup α] : CommGroup αᵃᵒᵖ :=
  unop_injective.commGroup _ (by 

Proof state:
α : Type u
inst✝ : CommGroup α
⊢ unop 1 = 1

Next tactic:
exact rfl
instance addCommMonoidWithOne [AddCommMonoidWithOne α] : AddCommMonoidWithOne αᵃᵒᵖ :=
  { AddOpposite.addCommMonoid α, AddOpposite.one, AddOpposite.natCast α with
    natCast_zero := show op ((0 : ℕ) : α) = 0 by 

Proof state:
α : Type u
inst✝ : AddCommMonoidWithOne α
src✝² : AddCommMonoid αᵃᵒᵖ := addCommMonoid α
src✝¹ : One αᵃᵒᵖ := one
src✝ : NatCast αᵃᵒᵖ := natCast α
⊢ op ↑0 = 0

Next tactic:
rw [Nat.cast_zero, op_zero]
instance addCommMonoidWithOne [AddCommMonoidWithOne α] : AddCommMonoidWithOne αᵃᵒᵖ :=
  { AddOpposite.addCommMonoid α, AddOpposite.one, AddOpposite.natCast α with
    natCast_zero := show op ((0 : ℕ) : α) = 0 by rw [Nat.cast_zero, op_zero]
    natCast_succ := show ∀ n, op ((n + 1 : ℕ) : α) = op ↑(n : ℕ) + 1 by 

Proof state:
α : Type u
inst✝ : AddCommMonoidWithOne α
src✝² : AddCommMonoid αᵃᵒᵖ := addCommMonoid α
src✝¹ : One αᵃᵒᵖ := one
src✝ : NatCast αᵃᵒᵖ := natCast α
⊢ ∀ (n : ℕ), op ↑(n + 1) = op ↑n + 1

Next tactic:
simp [add_comm]
/-- A semigroup homomorphism `f : M →ₙ* N` such that `f x` commutes with `f y` for all `x, y`
defines a semigroup homomorphism to `Nᵐᵒᵖ`. -/
@[to_additive (attr := simps (config := .asFn))
      "An additive semigroup homomorphism `f : AddHom M N` such that `f x` additively
      commutes with `f y` for all `x, y` defines an additive semigroup homomorphism to `Sᵃᵒᵖ`."]
def MulHom.toOpposite {M N : Type*} [Mul M] [Mul N] (f : M →ₙ* N)
    (hf : ∀ x y, Commute (f x) (f y)) : M →ₙ* Nᵐᵒᵖ where
  toFun := op ∘ f
  map_mul' x y := by 

Proof state:
α : Type u
M : Type u_1
N : Type u_2
inst✝¹ : Mul M
inst✝ : Mul N
f : M →ₙ* N
hf : ∀ (x y : M), Commute (f x) (f y)
x y : M
⊢ (op ∘ ⇑f) (x * y) = (op ∘ ⇑f) x * (op ∘ ⇑f) y

Next tactic:
simp [(hf x y).eq]
/-- A monoid homomorphism `f : M →* N` such that `f x` commutes with `f y` for all `x, y` defines
a monoid homomorphism to `Nᵐᵒᵖ`. -/
@[to_additive (attr := simps (config := .asFn))
      "An additive monoid homomorphism `f : M →+ N` such that `f x` additively commutes
      with `f y` for all `x, y` defines an additive monoid homomorphism to `Sᵃᵒᵖ`."]
def MonoidHom.toOpposite {M N : Type*} [MulOneClass M] [MulOneClass N] (f : M →* N)
    (hf : ∀ x y, Commute (f x) (f y)) : M →* Nᵐᵒᵖ where
  toFun := op ∘ f
  map_one' := congrArg op f.map_one
  map_mul' x y := by 

Proof state:
α : Type u
M : Type u_1
N : Type u_2
inst✝¹ : MulOneClass M
inst✝ : MulOneClass N
f : M →* N
hf : ∀ (x y : M), Commute (f x) (f y)
x y : M
⊢ OneHom.toFun { toFun := op ∘ ⇑f, map_one' := (_ : op (f 1) = op 1) } (x * y) =
    OneHom.toFun { toFun := op ∘ ⇑f, map_one' := (_ : op (f 1) = op 1) } x *
      OneHom.toFun { toFun := op ∘ ⇑f, map_one' := (_ : op (f 1) = op 1) } y

Next tactic:
simp [(hf x y).eq]
/-- The units of the opposites are equivalent to the opposites of the units. -/
@[to_additive
      "The additive units of the additive opposites are equivalent to the additive opposites
      of the additive units."]
def Units.opEquiv {M} [Monoid M] : Mᵐᵒᵖˣ ≃* Mˣᵐᵒᵖ where
  toFun u := op ⟨unop u, unop ↑u⁻¹, op_injective u.4, op_injective u.3⟩
  invFun := MulOpposite.rec' fun u => ⟨op ↑u, op ↑u⁻¹, unop_injective <| u.4, unop_injective u.3⟩
  map_mul' x y := unop_injective <| Units.ext <| rfl
  left_inv x := Units.ext <| by 

Proof state:
α : Type u
M : Type ?u.35855
inst✝ : Monoid M
x : Mᵐᵒᵖˣ
⊢ ↑((fun X =>
          MulOpposite.rec'
            (fun u =>
              { val := op ↑u, inv := op ↑u⁻¹, val_inv := (_ : op ↑u * op ↑u⁻¹ = 1),
                inv_val := (_ : op ↑u⁻¹ * op ↑u = 1) })
            X)
        ((fun u =>
            op
              { val := unop ↑u, inv := unop ↑u⁻¹, val_inv := (_ : unop ↑u * unop ↑u⁻¹ = 1),
                inv_val := (_ : unop ↑u⁻¹ * unop ↑u = 1) })
          x)) =
    ↑x

Next tactic:
simp
/-- The units of the opposites are equivalent to the opposites of the units. -/
@[to_additive
      "The additive units of the additive opposites are equivalent to the additive opposites
      of the additive units."]
def Units.opEquiv {M} [Monoid M] : Mᵐᵒᵖˣ ≃* Mˣᵐᵒᵖ where
  toFun u := op ⟨unop u, unop ↑u⁻¹, op_injective u.4, op_injective u.3⟩
  invFun := MulOpposite.rec' fun u => ⟨op ↑u, op ↑u⁻¹, unop_injective <| u.4, unop_injective u.3⟩
  map_mul' x y := unop_injective <| Units.ext <| rfl
  left_inv x := Units.ext <| by simp
  right_inv x := unop_injective <| Units.ext <| by 

Proof state:
α : Type u
M : Type ?u.35855
inst✝ : Monoid M
x : Mˣᵐᵒᵖ
⊢ ↑(unop
        ((fun u =>
            op
              { val := unop ↑u, inv := unop ↑u⁻¹, val_inv := (_ : unop ↑u * unop ↑u⁻¹ = 1),
                inv_val := (_ : unop ↑u⁻¹ * unop ↑u = 1) })
          ((fun X =>
              MulOpposite.rec'
                (fun u =>
                  { val := op ↑u, inv := op ↑u⁻¹, val_inv := (_ : op ↑u * op ↑u⁻¹ = 1),
                    inv_val := (_ : op ↑u⁻¹ * op ↑u = 1) })
                X)
            x))) =
    ↑(unop x)

Next tactic:
rfl
/-- An iso `α ≃* β` can equivalently be viewed as an iso `αᵐᵒᵖ ≃* βᵐᵒᵖ`. -/
@[to_additive (attr := simps)
  "An iso `α ≃+ β` can equivalently be viewed as an iso `αᵃᵒᵖ ≃+ βᵃᵒᵖ`."]
def MulEquiv.op {α β} [Mul α] [Mul β] : α ≃* β ≃ (αᵐᵒᵖ ≃* βᵐᵒᵖ) where
  toFun f :=
    { toFun := MulOpposite.op ∘ f ∘ unop, invFun := MulOpposite.op ∘ f.symm ∘ unop,
      left_inv := fun x => unop_injective (f.symm_apply_apply x.unop),
      right_inv := fun x => unop_injective (f.apply_symm_apply x.unop),
      map_mul' := fun x y => unop_injective (f.map_mul y.unop x.unop) }
  invFun f :=
    { toFun := unop ∘ f ∘ MulOpposite.op, invFun := unop ∘ f.symm ∘ MulOpposite.op,
      left_inv := fun x => by 

Proof state:
α✝ : Type u
α : Type ?u.54628
β : Type ?u.54631
inst✝¹ : Mul α
inst✝ : Mul β
f : αᵐᵒᵖ ≃* βᵐᵒᵖ
x : α
⊢ (unop ∘ ⇑(symm f) ∘ MulOpposite.op) ((unop ∘ ⇑f ∘ MulOpposite.op) x) = x

Next tactic:
simp
/-- An iso `α ≃* β` can equivalently be viewed as an iso `αᵐᵒᵖ ≃* βᵐᵒᵖ`. -/
@[to_additive (attr := simps)
  "An iso `α ≃+ β` can equivalently be viewed as an iso `αᵃᵒᵖ ≃+ βᵃᵒᵖ`."]
def MulEquiv.op {α β} [Mul α] [Mul β] : α ≃* β ≃ (αᵐᵒᵖ ≃* βᵐᵒᵖ) where
  toFun f :=
    { toFun := MulOpposite.op ∘ f ∘ unop, invFun := MulOpposite.op ∘ f.symm ∘ unop,
      left_inv := fun x => unop_injective (f.symm_apply_apply x.unop),
      right_inv := fun x => unop_injective (f.apply_symm_apply x.unop),
      map_mul' := fun x y => unop_injective (f.map_mul y.unop x.unop) }
  invFun f :=
    { toFun := unop ∘ f ∘ MulOpposite.op, invFun := unop ∘ f.symm ∘ MulOpposite.op,
      left_inv := fun x => by simp,
      right_inv := fun x => by 

Proof state:
α✝ : Type u
α : Type ?u.54628
β : Type ?u.54631
inst✝¹ : Mul α
inst✝ : Mul β
f : αᵐᵒᵖ ≃* βᵐᵒᵖ
x : β
⊢ (unop ∘ ⇑f ∘ MulOpposite.op) ((unop ∘ ⇑(symm f) ∘ MulOpposite.op) x) = x

Next tactic:
simp
@[to_additive]
theorem prod_univ_def [CommMonoid β] {n : ℕ} (f : Fin n → β) :
    ∏ i, f i = ((List.finRange n).map f).prod := by 

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
n : ℕ
f : Fin n → β
⊢ ∏ i : Fin n, f i = List.prod (List.map f (List.finRange n))

Next tactic:
simp [univ_def]
@[to_additive]
theorem prod_ofFn [CommMonoid β] {n : ℕ} (f : Fin n → β) : (List.ofFn f).prod = ∏ i, f i := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
n : ℕ
f : Fin n → β
⊢ List.prod (List.ofFn f) = ∏ i : Fin n, f i

Next tactic:
rw [List.ofFn_eq_map, prod_univ_def]
/-- A product of a function `f : Fin (n + 1) → β` over all `Fin (n + 1)`
is the product of `f x`, for some `x : Fin (n + 1)` times the remaining product -/
@[to_additive "A sum of a function `f : Fin (n + 1) → β` over all `Fin (n + 1)` is the sum of
`f x`, for some `x : Fin (n + 1)` plus the remaining product"]
theorem prod_univ_succAbove [CommMonoid β] {n : ℕ} (f : Fin (n + 1) → β) (x : Fin (n + 1)) :
    ∏ i, f i = f x * ∏ i : Fin n, f (x.succAbove i) := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
n : ℕ
f : Fin (n + 1) → β
x : Fin (n + 1)
⊢ ∏ i : Fin (n + 1), f i = f x * ∏ i : Fin n, f (succAbove x i)

Next tactic:
rw [univ_succAbove, prod_cons, Finset.prod_map _ x.succAboveEmb.toEmbedding,
    RelEmbedding.coe_toEmbedding]
/-- A product of a function `f : Fin (n + 1) → β` over all `Fin (n + 1)`
is the product of `f x`, for some `x : Fin (n + 1)` times the remaining product -/
@[to_additive "A sum of a function `f : Fin (n + 1) → β` over all `Fin (n + 1)` is the sum of
`f x`, for some `x : Fin (n + 1)` plus the remaining product"]
theorem prod_univ_succAbove [CommMonoid β] {n : ℕ} (f : Fin (n + 1) → β) (x : Fin (n + 1)) :
    ∏ i, f i = f x * ∏ i : Fin n, f (x.succAbove i) := by
  rw [univ_succAbove, prod_cons, Finset.prod_map _ x.succAboveEmb.toEmbedding,
    RelEmbedding.coe_toEmbedding]
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
n : ℕ
f : Fin (n + 1) → β
x : Fin (n + 1)
⊢ f x * ∏ x_1 : Fin n, f ((succAboveEmb x) x_1) = f x * ∏ i : Fin n, f (succAbove x i)

Next tactic:
rfl
/-- A product of a function `f : Fin (n + 1) → β` over all `Fin (n + 1)`
is the product of `f (Fin.last n)` plus the remaining product -/
@[to_additive "A sum of a function `f : Fin (n + 1) → β` over all `Fin (n + 1)` is the sum of
`f (Fin.last n)` plus the remaining sum"]
theorem prod_univ_castSucc [CommMonoid β] {n : ℕ} (f : Fin (n + 1) → β) :
    ∏ i, f i = (∏ i : Fin n, f (Fin.castSucc i)) * f (last n) := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
n : ℕ
f : Fin (n + 1) → β
⊢ ∏ i : Fin (n + 1), f i = (∏ i : Fin n, f (castSucc i)) * f (last n)

Next tactic:
simpa [mul_comm] using prod_univ_succAbove f (last n)
@[to_additive]
theorem prod_cons [CommMonoid β] {n : ℕ} (x : β) (f : Fin n → β) :
    (∏ i : Fin n.succ, (cons x f : Fin n.succ → β) i) = x * ∏ i : Fin n, f i := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
n : ℕ
x : β
f : Fin n → β
⊢ ∏ i : Fin (Nat.succ n), cons x f i = x * ∏ i : Fin n, f i

Next tactic:
simp_rw [prod_univ_succ, cons_zero, cons_succ]
@[to_additive sum_univ_one]
theorem prod_univ_one [CommMonoid β] (f : Fin 1 → β) : ∏ i, f i = f 0 := by 

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 1 → β
⊢ ∏ i : Fin 1, f i = f 0

Next tactic:
simp
@[to_additive (attr := simp)]
theorem prod_univ_two [CommMonoid β] (f : Fin 2 → β) : ∏ i, f i = f 0 * f 1 := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 2 → β
⊢ ∏ i : Fin 2, f i = f 0 * f 1

Next tactic:
simp [prod_univ_succ]
@[to_additive]
theorem prod_univ_three [CommMonoid β] (f : Fin 3 → β) : ∏ i, f i = f 0 * f 1 * f 2 := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 3 → β
⊢ ∏ i : Fin 3, f i = f 0 * f 1 * f 2

Next tactic:
rw [prod_univ_castSucc, prod_univ_two]
@[to_additive]
theorem prod_univ_three [CommMonoid β] (f : Fin 3 → β) : ∏ i, f i = f 0 * f 1 * f 2 := by
  rw [prod_univ_castSucc, prod_univ_two]
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 3 → β
⊢ f (castSucc 0) * f (castSucc 1) * f (last 2) = f 0 * f 1 * f 2

Next tactic:
rfl
@[to_additive]
theorem prod_univ_four [CommMonoid β] (f : Fin 4 → β) : ∏ i, f i = f 0 * f 1 * f 2 * f 3 := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 4 → β
⊢ ∏ i : Fin 4, f i = f 0 * f 1 * f 2 * f 3

Next tactic:
rw [prod_univ_castSucc, prod_univ_three]
@[to_additive]
theorem prod_univ_four [CommMonoid β] (f : Fin 4 → β) : ∏ i, f i = f 0 * f 1 * f 2 * f 3 := by
  rw [prod_univ_castSucc, prod_univ_three]
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 4 → β
⊢ f (castSucc 0) * f (castSucc 1) * f (castSucc 2) * f (last 3) = f 0 * f 1 * f 2 * f 3

Next tactic:
rfl
@[to_additive]
theorem prod_univ_five [CommMonoid β] (f : Fin 5 → β) :
    ∏ i, f i = f 0 * f 1 * f 2 * f 3 * f 4 := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 5 → β
⊢ ∏ i : Fin 5, f i = f 0 * f 1 * f 2 * f 3 * f 4

Next tactic:
rw [prod_univ_castSucc, prod_univ_four]
@[to_additive]
theorem prod_univ_five [CommMonoid β] (f : Fin 5 → β) :
    ∏ i, f i = f 0 * f 1 * f 2 * f 3 * f 4 := by
  rw [prod_univ_castSucc, prod_univ_four]
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 5 → β
⊢ f (castSucc 0) * f (castSucc 1) * f (castSucc 2) * f (castSucc 3) * f (last 4) = f 0 * f 1 * f 2 * f 3 * f 4

Next tactic:
rfl
@[to_additive]
theorem prod_univ_six [CommMonoid β] (f : Fin 6 → β) :
    ∏ i, f i = f 0 * f 1 * f 2 * f 3 * f 4 * f 5 := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 6 → β
⊢ ∏ i : Fin 6, f i = f 0 * f 1 * f 2 * f 3 * f 4 * f 5

Next tactic:
rw [prod_univ_castSucc, prod_univ_five]
@[to_additive]
theorem prod_univ_six [CommMonoid β] (f : Fin 6 → β) :
    ∏ i, f i = f 0 * f 1 * f 2 * f 3 * f 4 * f 5 := by
  rw [prod_univ_castSucc, prod_univ_five]
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 6 → β
⊢ f (castSucc 0) * f (castSucc 1) * f (castSucc 2) * f (castSucc 3) * f (castSucc 4) * f (last 5) =
    f 0 * f 1 * f 2 * f 3 * f 4 * f 5

Next tactic:
rfl
@[to_additive]
theorem prod_univ_seven [CommMonoid β] (f : Fin 7 → β) :
    ∏ i, f i = f 0 * f 1 * f 2 * f 3 * f 4 * f 5 * f 6 := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 7 → β
⊢ ∏ i : Fin 7, f i = f 0 * f 1 * f 2 * f 3 * f 4 * f 5 * f 6

Next tactic:
rw [prod_univ_castSucc, prod_univ_six]
@[to_additive]
theorem prod_univ_seven [CommMonoid β] (f : Fin 7 → β) :
    ∏ i, f i = f 0 * f 1 * f 2 * f 3 * f 4 * f 5 * f 6 := by
  rw [prod_univ_castSucc, prod_univ_six]
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 7 → β
⊢ f (castSucc 0) * f (castSucc 1) * f (castSucc 2) * f (castSucc 3) * f (castSucc 4) * f (castSucc 5) * f (last 6) =
    f 0 * f 1 * f 2 * f 3 * f 4 * f 5 * f 6

Next tactic:
rfl
@[to_additive]
theorem prod_univ_eight [CommMonoid β] (f : Fin 8 → β) :
    ∏ i, f i = f 0 * f 1 * f 2 * f 3 * f 4 * f 5 * f 6 * f 7 := by
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 8 → β
⊢ ∏ i : Fin 8, f i = f 0 * f 1 * f 2 * f 3 * f 4 * f 5 * f 6 * f 7

Next tactic:
rw [prod_univ_castSucc, prod_univ_seven]
@[to_additive]
theorem prod_univ_eight [CommMonoid β] (f : Fin 8 → β) :
    ∏ i, f i = f 0 * f 1 * f 2 * f 3 * f 4 * f 5 * f 6 * f 7 := by
  rw [prod_univ_castSucc, prod_univ_seven]
  

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid β
f : Fin 8 → β
⊢ f (castSucc 0) * f (castSucc 1) * f (castSucc 2) * f (castSucc 3) * f (castSucc 4) * f (castSucc 5) * f (castSucc 6) *
      f (last 7) =
    f 0 * f 1 * f 2 * f 3 * f 4 * f 5 * f 6 * f 7

Next tactic:
rfl
theorem sum_pow_mul_eq_add_pow {n : ℕ} {R : Type*} [CommSemiring R] (a b : R) :
    (∑ s : Finset (Fin n), a ^ s.card * b ^ (n - s.card)) = (a + b) ^ n := by
  

Proof state:
α : Type u_1
β : Type u_2
n : ℕ
R : Type u_3
inst✝ : CommSemiring R
a b : R
⊢ ∑ s : Finset (Fin n), a ^ card s * b ^ (n - card s) = (a + b) ^ n

Next tactic:
simpa using Fintype.sum_pow_mul_eq_add_pow (Fin n) a b
theorem prod_const [CommMonoid α] (n : ℕ) (x : α) : ∏ _i : Fin n, x = x ^ n := by 

Proof state:
α : Type u_1
β : Type u_2
inst✝ : CommMonoid α
n : ℕ
x : α
⊢ ∏ _i : Fin n, x = x ^ n

Next tactic:
simp
theorem sum_const [AddCommMonoid α] (n : ℕ) (x : α) : ∑ _i : Fin n, x = n • x := by 

Proof state:
α : Type u_1
β : Type u_2
inst✝ : AddCommMonoid α
n : ℕ
x : α
⊢ ∑ _i : Fin n, x = n • x

Next tactic:
simp
@[to_additive]
theorem prod_Ioi_zero {M : Type*} [CommMonoid M] {n : ℕ} {v : Fin n.succ → M} :
    ∏ i in Ioi 0, v i = ∏ j : Fin n, v j.succ := by
  

Proof state:
α : Type u_1
β : Type u_2
M : Type u_3
inst✝ : CommMonoid M
n : ℕ
v : Fin (Nat.succ n) → M
⊢ ∏ i in Ioi 0, v i = ∏ j : Fin n, v (succ j)

Next tactic:
rw [Ioi_zero_eq_map, Finset.prod_map, RelEmbedding.coe_toEmbedding, val_succEmbedding]
@[to_additive]
theorem prod_Ioi_succ {M : Type*} [CommMonoid M] {n : ℕ} (i : Fin n) (v : Fin n.succ → M) :
    ∏ j in Ioi i.succ, v j = ∏ j in Ioi i, v j.succ := by
  

Proof state:
α : Type u_1
β : Type u_2
M : Type u_3
inst✝ : CommMonoid M
n : ℕ
i : Fin n
v : Fin (Nat.succ n) → M
⊢ ∏ j in Ioi (succ i), v j = ∏ j in Ioi i, v (succ j)

Next tactic:
rw [Ioi_succ, Finset.prod_map, RelEmbedding.coe_toEmbedding, val_succEmbedding]
@[to_additive]
theorem prod_congr' {M : Type*} [CommMonoid M] {a b : ℕ} (f : Fin b → M) (h : a = b) :
    (∏ i : Fin a, f (cast h i)) = ∏ i : Fin b, f i := by
  

Proof state:
α : Type u_1
β : Type u_2
M : Type u_3
inst✝ : CommMonoid M
a b : ℕ
f : Fin b → M
h : a = b
⊢ ∏ i : Fin a, f (cast h i) = ∏ i : Fin b, f i

Next tactic:
subst h
@[to_additive]
theorem prod_congr' {M : Type*} [CommMonoid M] {a b : ℕ} (f : Fin b → M) (h : a = b) :
    (∏ i : Fin a, f (cast h i)) = ∏ i : Fin b, f i := by
  subst h
  

Proof state:
α : Type u_1
β : Type u_2
M : Type u_3
inst✝ : CommMonoid M
a : ℕ
f : Fin a → M
⊢ ∏ i : Fin a, f (cast (_ : a = a) i) = ∏ i : Fin a, f i

Next tactic:
congr