l3lab/ntp-mathlib huggingface dataset:
theorem variance {n : ℕ} (h : 0 < (n : ℝ)) (x : I) :
    (∑ k : Fin (n + 1), (x - k/ₙ : ℝ) ^ 2 * bernstein n k x) = (x : ℝ) * (1 - x) / n := by
  have h' : (n : ℝ) ≠ 0 := ne_of_gt h
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  dsimp
  conv_lhs => simp only [Finset.sum_mul, z]
  conv_rhs => rw [div_mul_cancel _ h']
  have := bernsteinPolynomial.variance ℝ n
  apply_fun fun p => Polynomial.aeval (x : ℝ) p at this
  simp? [AlgHom.map_sum, Finset.sum_range, ← Polynomial.nat_cast_mul] at this says
    simp only [nsmul_eq_mul, Finset.sum_range, map_sum, map_mul, map_pow, map_sub, map_natCast,
      Polynomial.aeval_X, Polynomial.coe_aeval_eq_eval, map_one] at this
  convert this using 1
  · 

Proof state:
case h.e'_2
n : ℕ
h : 0 < ↑n
x : ↑I
h' : ↑n ≠ 0
this :
  ∑ x_1 : Fin (n + 1), (↑n * ↑x - ↑↑x_1) ^ 2 * Polynomial.eval (↑x) (bernsteinPolynomial ℝ n ↑x_1) = ↑n * ↑x * (1 - ↑x)
⊢ ∑ x_1 : Fin (n + 1), (↑x - ↑↑x_1 / ↑n) ^ 2 * (bernstein n ↑x_1) x * ↑n * ↑n =
    ∑ x_1 : Fin (n + 1), (↑n * ↑x - ↑↑x_1) ^ 2 * Polynomial.eval (↑x) (bernsteinPolynomial ℝ n ↑x_1)

Next tactic:
congr 1
theorem variance {n : ℕ} (h : 0 < (n : ℝ)) (x : I) :
    (∑ k : Fin (n + 1), (x - k/ₙ : ℝ) ^ 2 * bernstein n k x) = (x : ℝ) * (1 - x) / n := by
  have h' : (n : ℝ) ≠ 0 := ne_of_gt h
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  dsimp
  conv_lhs => simp only [Finset.sum_mul, z]
  conv_rhs => rw [div_mul_cancel _ h']
  have := bernsteinPolynomial.variance ℝ n
  apply_fun fun p => Polynomial.aeval (x : ℝ) p at this
  simp? [AlgHom.map_sum, Finset.sum_range, ← Polynomial.nat_cast_mul] at this says
    simp only [nsmul_eq_mul, Finset.sum_range, map_sum, map_mul, map_pow, map_sub, map_natCast,
      Polynomial.aeval_X, Polynomial.coe_aeval_eq_eval, map_one] at this
  convert this using 1
  · congr 1; 

Proof state:
case h.e'_2.e_f
n : ℕ
h : 0 < ↑n
x : ↑I
h' : ↑n ≠ 0
this :
  ∑ x_1 : Fin (n + 1), (↑n * ↑x - ↑↑x_1) ^ 2 * Polynomial.eval (↑x) (bernsteinPolynomial ℝ n ↑x_1) = ↑n * ↑x * (1 - ↑x)
⊢ (fun x_1 => (↑x - ↑↑x_1 / ↑n) ^ 2 * (bernstein n ↑x_1) x * ↑n * ↑n) = fun x_1 =>
    (↑n * ↑x - ↑↑x_1) ^ 2 * Polynomial.eval (↑x) (bernsteinPolynomial ℝ n ↑x_1)

Next tactic:
funext k
theorem variance {n : ℕ} (h : 0 < (n : ℝ)) (x : I) :
    (∑ k : Fin (n + 1), (x - k/ₙ : ℝ) ^ 2 * bernstein n k x) = (x : ℝ) * (1 - x) / n := by
  have h' : (n : ℝ) ≠ 0 := ne_of_gt h
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  dsimp
  conv_lhs => simp only [Finset.sum_mul, z]
  conv_rhs => rw [div_mul_cancel _ h']
  have := bernsteinPolynomial.variance ℝ n
  apply_fun fun p => Polynomial.aeval (x : ℝ) p at this
  simp? [AlgHom.map_sum, Finset.sum_range, ← Polynomial.nat_cast_mul] at this says
    simp only [nsmul_eq_mul, Finset.sum_range, map_sum, map_mul, map_pow, map_sub, map_natCast,
      Polynomial.aeval_X, Polynomial.coe_aeval_eq_eval, map_one] at this
  convert this using 1
  · congr 1; funext k
    

Proof state:
case h.e'_2.e_f.h
n : ℕ
h : 0 < ↑n
x : ↑I
h' : ↑n ≠ 0
this :
  ∑ x_1 : Fin (n + 1), (↑n * ↑x - ↑↑x_1) ^ 2 * Polynomial.eval (↑x) (bernsteinPolynomial ℝ n ↑x_1) = ↑n * ↑x * (1 - ↑x)
k : Fin (n + 1)
⊢ (↑x - ↑↑k / ↑n) ^ 2 * (bernstein n ↑k) x * ↑n * ↑n =
    (↑n * ↑x - ↑↑k) ^ 2 * Polynomial.eval (↑x) (bernsteinPolynomial ℝ n ↑k)

Next tactic:
rw [mul_comm _ (n : ℝ), mul_comm _ (n : ℝ), ← mul_assoc, ← mul_assoc]
theorem variance {n : ℕ} (h : 0 < (n : ℝ)) (x : I) :
    (∑ k : Fin (n + 1), (x - k/ₙ : ℝ) ^ 2 * bernstein n k x) = (x : ℝ) * (1 - x) / n := by
  have h' : (n : ℝ) ≠ 0 := ne_of_gt h
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  dsimp
  conv_lhs => simp only [Finset.sum_mul, z]
  conv_rhs => rw [div_mul_cancel _ h']
  have := bernsteinPolynomial.variance ℝ n
  apply_fun fun p => Polynomial.aeval (x : ℝ) p at this
  simp? [AlgHom.map_sum, Finset.sum_range, ← Polynomial.nat_cast_mul] at this says
    simp only [nsmul_eq_mul, Finset.sum_range, map_sum, map_mul, map_pow, map_sub, map_natCast,
      Polynomial.aeval_X, Polynomial.coe_aeval_eq_eval, map_one] at this
  convert this using 1
  · congr 1; funext k
    rw [mul_comm _ (n : ℝ), mul_comm _ (n : ℝ), ← mul_assoc, ← mul_assoc]
    

Proof state:
case h.e'_2.e_f.h
n : ℕ
h : 0 < ↑n
x : ↑I
h' : ↑n ≠ 0
this :
  ∑ x_1 : Fin (n + 1), (↑n * ↑x - ↑↑x_1) ^ 2 * Polynomial.eval (↑x) (bernsteinPolynomial ℝ n ↑x_1) = ↑n * ↑x * (1 - ↑x)
k : Fin (n + 1)
⊢ ↑n * ↑n * (↑x - ↑↑k / ↑n) ^ 2 * (bernstein n ↑k) x =
    (↑n * ↑x - ↑↑k) ^ 2 * Polynomial.eval (↑x) (bernsteinPolynomial ℝ n ↑k)

Next tactic:
congr 1
theorem variance {n : ℕ} (h : 0 < (n : ℝ)) (x : I) :
    (∑ k : Fin (n + 1), (x - k/ₙ : ℝ) ^ 2 * bernstein n k x) = (x : ℝ) * (1 - x) / n := by
  have h' : (n : ℝ) ≠ 0 := ne_of_gt h
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  dsimp
  conv_lhs => simp only [Finset.sum_mul, z]
  conv_rhs => rw [div_mul_cancel _ h']
  have := bernsteinPolynomial.variance ℝ n
  apply_fun fun p => Polynomial.aeval (x : ℝ) p at this
  simp? [AlgHom.map_sum, Finset.sum_range, ← Polynomial.nat_cast_mul] at this says
    simp only [nsmul_eq_mul, Finset.sum_range, map_sum, map_mul, map_pow, map_sub, map_natCast,
      Polynomial.aeval_X, Polynomial.coe_aeval_eq_eval, map_one] at this
  convert this using 1
  · congr 1; funext k
    rw [mul_comm _ (n : ℝ), mul_comm _ (n : ℝ), ← mul_assoc, ← mul_assoc]
    congr 1
    

Proof state:
case h.e'_2.e_f.h.e_a
n : ℕ
h : 0 < ↑n
x : ↑I
h' : ↑n ≠ 0
this :
  ∑ x_1 : Fin (n + 1), (↑n * ↑x - ↑↑x_1) ^ 2 * Polynomial.eval (↑x) (bernsteinPolynomial ℝ n ↑x_1) = ↑n * ↑x * (1 - ↑x)
k : Fin (n + 1)
⊢ ↑n * ↑n * (↑x - ↑↑k / ↑n) ^ 2 = (↑n * ↑x - ↑↑k) ^ 2

Next tactic:
field_simp [h]
theorem variance {n : ℕ} (h : 0 < (n : ℝ)) (x : I) :
    (∑ k : Fin (n + 1), (x - k/ₙ : ℝ) ^ 2 * bernstein n k x) = (x : ℝ) * (1 - x) / n := by
  have h' : (n : ℝ) ≠ 0 := ne_of_gt h
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  dsimp
  conv_lhs => simp only [Finset.sum_mul, z]
  conv_rhs => rw [div_mul_cancel _ h']
  have := bernsteinPolynomial.variance ℝ n
  apply_fun fun p => Polynomial.aeval (x : ℝ) p at this
  simp? [AlgHom.map_sum, Finset.sum_range, ← Polynomial.nat_cast_mul] at this says
    simp only [nsmul_eq_mul, Finset.sum_range, map_sum, map_mul, map_pow, map_sub, map_natCast,
      Polynomial.aeval_X, Polynomial.coe_aeval_eq_eval, map_one] at this
  convert this using 1
  · congr 1; funext k
    rw [mul_comm _ (n : ℝ), mul_comm _ (n : ℝ), ← mul_assoc, ← mul_assoc]
    congr 1
    field_simp [h]
    

Proof state:
case h.e'_2.e_f.h.e_a
n : ℕ
h : 0 < ↑n
x : ↑I
h' : ↑n ≠ 0
this :
  ∑ x_1 : Fin (n + 1), (↑n * ↑x - ↑↑x_1) ^ 2 * Polynomial.eval (↑x) (bernsteinPolynomial ℝ n ↑x_1) = ↑n * ↑x * (1 - ↑x)
k : Fin (n + 1)
⊢ ↑n * ↑n * (↑x * ↑n - ↑↑k) ^ 2 = (↑n * ↑x - ↑↑k) ^ 2 * ↑n ^ 2

Next tactic:
ring
theorem variance {n : ℕ} (h : 0 < (n : ℝ)) (x : I) :
    (∑ k : Fin (n + 1), (x - k/ₙ : ℝ) ^ 2 * bernstein n k x) = (x : ℝ) * (1 - x) / n := by
  have h' : (n : ℝ) ≠ 0 := ne_of_gt h
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  apply_fun fun x : ℝ => x * n using GroupWithZero.mul_right_injective h'
  dsimp
  conv_lhs => simp only [Finset.sum_mul, z]
  conv_rhs => rw [div_mul_cancel _ h']
  have := bernsteinPolynomial.variance ℝ n
  apply_fun fun p => Polynomial.aeval (x : ℝ) p at this
  simp? [AlgHom.map_sum, Finset.sum_range, ← Polynomial.nat_cast_mul] at this says
    simp only [nsmul_eq_mul, Finset.sum_range, map_sum, map_mul, map_pow, map_sub, map_natCast,
      Polynomial.aeval_X, Polynomial.coe_aeval_eq_eval, map_one] at this
  convert this using 1
  · congr 1; funext k
    rw [mul_comm _ (n : ℝ), mul_comm _ (n : ℝ), ← mul_assoc, ← mul_assoc]
    congr 1
    field_simp [h]
    ring
  · 

Proof state:
case h.e'_3
n : ℕ
h : 0 < ↑n
x : ↑I
h' : ↑n ≠ 0
this :
  ∑ x_1 : Fin (n + 1), (↑n * ↑x - ↑↑x_1) ^ 2 * Polynomial.eval (↑x) (bernsteinPolynomial ℝ n ↑x_1) = ↑n * ↑x * (1 - ↑x)
⊢ ↑x * (1 - ↑x) * ↑n = ↑n * ↑x * (1 - ↑x)

Next tactic:
ring
@[simp]
theorem apply (n : ℕ) (f : C(I, ℝ)) (x : I) :
    bernsteinApproximation n f x = ∑ k : Fin (n + 1), f k/ₙ * bernstein n k x := by
  

Proof state:
n : ℕ
f : C(↑I, ℝ)
x : ↑I
⊢ (bernsteinApproximation n f) x = ∑ k : Fin (n + 1), f k/ₙ * (bernstein n ↑k) x

Next tactic:
simp [bernsteinApproximation]
/-- If `k ∈ S`, then `f(k/n)` is close to `f x`.
-/
theorem lt_of_mem_S {f : C(I, ℝ)} {ε : ℝ} {h : 0 < ε} {n : ℕ} {x : I} {k : Fin (n + 1)}
    (m : k ∈ S f ε h n x) : |f k/ₙ - f x| < ε / 2 := by
  

Proof state:
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
n : ℕ
x : ↑I
k : Fin (n + 1)
m : k ∈ S f ε h n x
⊢ |f k/ₙ - f x| < ε / 2

Next tactic:
apply f.dist_lt_of_dist_lt_modulus (ε / 2) (half_pos h)
/-- If `k ∈ S`, then `f(k/n)` is close to `f x`.
-/
theorem lt_of_mem_S {f : C(I, ℝ)} {ε : ℝ} {h : 0 < ε} {n : ℕ} {x : I} {k : Fin (n + 1)}
    (m : k ∈ S f ε h n x) : |f k/ₙ - f x| < ε / 2 := by
  apply f.dist_lt_of_dist_lt_modulus (ε / 2) (half_pos h)
  -- Porting note: `simp` fails to apply `Set.mem_toFinset` on its own
  

Proof state:
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
n : ℕ
x : ↑I
k : Fin (n + 1)
m : k ∈ S f ε h n x
⊢ dist k/ₙ x < ContinuousMap.modulus f (ε / 2) (_ : 0 < ε / 2)

Next tactic:
simpa [S, (Set.mem_toFinset)] using m
/-- If `k ∉ S`, then as `δ ≤ |x - k/n|`, we have the inequality `1 ≤ δ^-2 * (x - k/n)^2`.
This particular formulation will be helpful later.
-/
theorem le_of_mem_S_compl {f : C(I, ℝ)} {ε : ℝ} {h : 0 < ε} {n : ℕ} {x : I} {k : Fin (n + 1)}
    (m : k ∈ (S f ε h n x)ᶜ) : (1 : ℝ) ≤ δ f ε h ^ (-2 : ℤ) * ((x : ℝ) - k/ₙ) ^ 2 := by
  -- Porting note: added parentheses to help `simp`
  

Proof state:
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
n : ℕ
x : ↑I
k : Fin (n + 1)
m : k ∈ (S f ε h n x)ᶜ
⊢ 1 ≤ δ f ε h ^ (-2) * (↑x - ↑k/ₙ) ^ 2

Next tactic:
simp only [Finset.mem_compl, not_lt, (Set.mem_toFinset), Set.mem_setOf_eq, S] at m
/-- If `k ∉ S`, then as `δ ≤ |x - k/n|`, we have the inequality `1 ≤ δ^-2 * (x - k/n)^2`.
This particular formulation will be helpful later.
-/
theorem le_of_mem_S_compl {f : C(I, ℝ)} {ε : ℝ} {h : 0 < ε} {n : ℕ} {x : I} {k : Fin (n + 1)}
    (m : k ∈ (S f ε h n x)ᶜ) : (1 : ℝ) ≤ δ f ε h ^ (-2 : ℤ) * ((x : ℝ) - k/ₙ) ^ 2 := by
  -- Porting note: added parentheses to help `simp`
  simp only [Finset.mem_compl, not_lt, (Set.mem_toFinset), Set.mem_setOf_eq, S] at m
  

Proof state:
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
n : ℕ
x : ↑I
k : Fin (n + 1)
m : δ f ε h ≤ dist k/ₙ x
⊢ 1 ≤ δ f ε h ^ (-2) * (↑x - ↑k/ₙ) ^ 2

Next tactic:
rw [zpow_neg, ← div_eq_inv_mul, zpow_two, ← pow_two, one_le_div (pow_pos δ_pos 2), sq_le_sq,
    abs_of_pos δ_pos]
/-- If `k ∉ S`, then as `δ ≤ |x - k/n|`, we have the inequality `1 ≤ δ^-2 * (x - k/n)^2`.
This particular formulation will be helpful later.
-/
theorem le_of_mem_S_compl {f : C(I, ℝ)} {ε : ℝ} {h : 0 < ε} {n : ℕ} {x : I} {k : Fin (n + 1)}
    (m : k ∈ (S f ε h n x)ᶜ) : (1 : ℝ) ≤ δ f ε h ^ (-2 : ℤ) * ((x : ℝ) - k/ₙ) ^ 2 := by
  -- Porting note: added parentheses to help `simp`
  simp only [Finset.mem_compl, not_lt, (Set.mem_toFinset), Set.mem_setOf_eq, S] at m
  rw [zpow_neg, ← div_eq_inv_mul, zpow_two, ← pow_two, one_le_div (pow_pos δ_pos 2), sq_le_sq,
    abs_of_pos δ_pos]
  

Proof state:
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
n : ℕ
x : ↑I
k : Fin (n + 1)
m : δ f ε h ≤ dist k/ₙ x
⊢ δ f ε h ≤ |↑x - ↑k/ₙ|

Next tactic:
rwa [dist_comm] at m
/-- The Bernstein approximations
```
∑ k : Fin (n+1), f (k/n : ℝ) * n.choose k * x^k * (1-x)^(n-k)
```
for a continuous function `f : C([0,1], ℝ)` converge uniformly to `f` as `n` tends to infinity.

This is the proof given in [Richard Beals' *Analysis, an introduction*][beals-analysis], §7D,
and reproduced on wikipedia.
-/
theorem bernsteinApproximation_uniform (f : C(I, ℝ)) :
    Tendsto (fun n : ℕ => bernsteinApproximation n f) atTop (𝓝 f) := by
  

Proof state:
f : C(↑I, ℝ)
⊢ Tendsto (fun n => bernsteinApproximation n f) atTop (𝓝 f)

Next tactic:
simp only [Metric.nhds_basis_ball.tendsto_right_iff, Metric.mem_ball, dist_eq_norm]
/-- The Bernstein approximations
```
∑ k : Fin (n+1), f (k/n : ℝ) * n.choose k * x^k * (1-x)^(n-k)
```
for a continuous function `f : C([0,1], ℝ)` converge uniformly to `f` as `n` tends to infinity.

This is the proof given in [Richard Beals' *Analysis, an introduction*][beals-analysis], §7D,
and reproduced on wikipedia.
-/
theorem bernsteinApproximation_uniform (f : C(I, ℝ)) :
    Tendsto (fun n : ℕ => bernsteinApproximation n f) atTop (𝓝 f) := by
  simp only [Metric.nhds_basis_ball.tendsto_right_iff, Metric.mem_ball, dist_eq_norm]
  

Proof state:
f : C(↑I, ℝ)
⊢ ∀ (i : ℝ), 0 < i → ∀ᶠ (x : ℕ) in atTop, ‖bernsteinApproximation x f - f‖ < i

Next tactic:
intro ε h
/-- The Bernstein approximations
```
∑ k : Fin (n+1), f (k/n : ℝ) * n.choose k * x^k * (1-x)^(n-k)
```
for a continuous function `f : C([0,1], ℝ)` converge uniformly to `f` as `n` tends to infinity.

This is the proof given in [Richard Beals' *Analysis, an introduction*][beals-analysis], §7D,
and reproduced on wikipedia.
-/
theorem bernsteinApproximation_uniform (f : C(I, ℝ)) :
    Tendsto (fun n : ℕ => bernsteinApproximation n f) atTop (𝓝 f) := by
  simp only [Metric.nhds_basis_ball.tendsto_right_iff, Metric.mem_ball, dist_eq_norm]
  intro ε h
  

Proof state:
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
⊢ ∀ᶠ (x : ℕ) in atTop, ‖bernsteinApproximation x f - f‖ < ε

Next tactic:
let δ := δ f ε h
/-- The Bernstein approximations
```
∑ k : Fin (n+1), f (k/n : ℝ) * n.choose k * x^k * (1-x)^(n-k)
```
for a continuous function `f : C([0,1], ℝ)` converge uniformly to `f` as `n` tends to infinity.

This is the proof given in [Richard Beals' *Analysis, an introduction*][beals-analysis], §7D,
and reproduced on wikipedia.
-/
theorem bernsteinApproximation_uniform (f : C(I, ℝ)) :
    Tendsto (fun n : ℕ => bernsteinApproximation n f) atTop (𝓝 f) := by
  simp only [Metric.nhds_basis_ball.tendsto_right_iff, Metric.mem_ball, dist_eq_norm]
  intro ε h
  let δ := δ f ε h
  

Proof state:
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
δ : ℝ := bernsteinApproximation.δ f ε h
⊢ ∀ᶠ (x : ℕ) in atTop, ‖bernsteinApproximation x f - f‖ < ε

Next tactic:
have nhds_zero := tendsto_const_div_atTop_nhds_0_nat (2 * ‖f‖ * δ ^ (-2 : ℤ))
/-- The Bernstein approximations
```
∑ k : Fin (n+1), f (k/n : ℝ) * n.choose k * x^k * (1-x)^(n-k)
```
for a continuous function `f : C([0,1], ℝ)` converge uniformly to `f` as `n` tends to infinity.

This is the proof given in [Richard Beals' *Analysis, an introduction*][beals-analysis], §7D,
and reproduced on wikipedia.
-/
theorem bernsteinApproximation_uniform (f : C(I, ℝ)) :
    Tendsto (fun n : ℕ => bernsteinApproximation n f) atTop (𝓝 f) := by
  simp only [Metric.nhds_basis_ball.tendsto_right_iff, Metric.mem_ball, dist_eq_norm]
  intro ε h
  let δ := δ f ε h
  have nhds_zero := tendsto_const_div_atTop_nhds_0_nat (2 * ‖f‖ * δ ^ (-2 : ℤ))
  

Proof state:
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
δ : ℝ := bernsteinApproximation.δ f ε h
nhds_zero : Tendsto (fun n => 2 * ‖f‖ * δ ^ (-2) / ↑n) atTop (𝓝 0)
⊢ ∀ᶠ (x : ℕ) in atTop, ‖bernsteinApproximation x f - f‖ < ε

Next tactic:
filter_upwards [nhds_zero.eventually (gt_mem_nhds (half_pos h)), eventually_gt_atTop 0] with n nh
    npos'
/-- The Bernstein approximations
```
∑ k : Fin (n+1), f (k/n : ℝ) * n.choose k * x^k * (1-x)^(n-k)
```
for a continuous function `f : C([0,1], ℝ)` converge uniformly to `f` as `n` tends to infinity.

This is the proof given in [Richard Beals' *Analysis, an introduction*][beals-analysis], §7D,
and reproduced on wikipedia.
-/
theorem bernsteinApproximation_uniform (f : C(I, ℝ)) :
    Tendsto (fun n : ℕ => bernsteinApproximation n f) atTop (𝓝 f) := by
  simp only [Metric.nhds_basis_ball.tendsto_right_iff, Metric.mem_ball, dist_eq_norm]
  intro ε h
  let δ := δ f ε h
  have nhds_zero := tendsto_const_div_atTop_nhds_0_nat (2 * ‖f‖ * δ ^ (-2 : ℤ))
  filter_upwards [nhds_zero.eventually (gt_mem_nhds (half_pos h)), eventually_gt_atTop 0] with n nh
    npos'
  

Proof state:
case h
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
δ : ℝ := bernsteinApproximation.δ f ε h
nhds_zero : Tendsto (fun n => 2 * ‖f‖ * δ ^ (-2) / ↑n) atTop (𝓝 0)
n : ℕ
nh : 2 * ‖f‖ * δ ^ (-2) / ↑n < ε / 2
npos' : 0 < n
⊢ ‖bernsteinApproximation n f - f‖ < ε

Next tactic:
have npos : 0 < (n : ℝ) := by positivity
/-- The Bernstein approximations
```
∑ k : Fin (n+1), f (k/n : ℝ) * n.choose k * x^k * (1-x)^(n-k)
```
for a continuous function `f : C([0,1], ℝ)` converge uniformly to `f` as `n` tends to infinity.

This is the proof given in [Richard Beals' *Analysis, an introduction*][beals-analysis], §7D,
and reproduced on wikipedia.
-/
theorem bernsteinApproximation_uniform (f : C(I, ℝ)) :
    Tendsto (fun n : ℕ => bernsteinApproximation n f) atTop (𝓝 f) := by
  simp only [Metric.nhds_basis_ball.tendsto_right_iff, Metric.mem_ball, dist_eq_norm]
  intro ε h
  let δ := δ f ε h
  have nhds_zero := tendsto_const_div_atTop_nhds_0_nat (2 * ‖f‖ * δ ^ (-2 : ℤ))
  filter_upwards [nhds_zero.eventually (gt_mem_nhds (half_pos h)), eventually_gt_atTop 0] with n nh
    npos'
  have npos : 0 < (n : ℝ) := by 

Proof state:
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
δ : ℝ := bernsteinApproximation.δ f ε h
nhds_zero : Tendsto (fun n => 2 * ‖f‖ * δ ^ (-2) / ↑n) atTop (𝓝 0)
n : ℕ
nh : 2 * ‖f‖ * δ ^ (-2) / ↑n < ε / 2
npos' : 0 < n
⊢ 0 < ↑n

Next tactic:
positivity
/-- The Bernstein approximations
```
∑ k : Fin (n+1), f (k/n : ℝ) * n.choose k * x^k * (1-x)^(n-k)
```
for a continuous function `f : C([0,1], ℝ)` converge uniformly to `f` as `n` tends to infinity.

This is the proof given in [Richard Beals' *Analysis, an introduction*][beals-analysis], §7D,
and reproduced on wikipedia.
-/
theorem bernsteinApproximation_uniform (f : C(I, ℝ)) :
    Tendsto (fun n : ℕ => bernsteinApproximation n f) atTop (𝓝 f) := by
  simp only [Metric.nhds_basis_ball.tendsto_right_iff, Metric.mem_ball, dist_eq_norm]
  intro ε h
  let δ := δ f ε h
  have nhds_zero := tendsto_const_div_atTop_nhds_0_nat (2 * ‖f‖ * δ ^ (-2 : ℤ))
  filter_upwards [nhds_zero.eventually (gt_mem_nhds (half_pos h)), eventually_gt_atTop 0] with n nh
    npos'
  have npos : 0 < (n : ℝ) := by positivity
  -- As `[0,1]` is compact, it suffices to check the inequality pointwise.
  

Proof state:
case h
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
δ : ℝ := bernsteinApproximation.δ f ε h
nhds_zero : Tendsto (fun n => 2 * ‖f‖ * δ ^ (-2) / ↑n) atTop (𝓝 0)
n : ℕ
nh : 2 * ‖f‖ * δ ^ (-2) / ↑n < ε / 2
npos' : 0 < n
npos : 0 < ↑n
⊢ ‖bernsteinApproximation n f - f‖ < ε

Next tactic:
rw [ContinuousMap.norm_lt_iff _ h]
/-- The Bernstein approximations
```
∑ k : Fin (n+1), f (k/n : ℝ) * n.choose k * x^k * (1-x)^(n-k)
```
for a continuous function `f : C([0,1], ℝ)` converge uniformly to `f` as `n` tends to infinity.

This is the proof given in [Richard Beals' *Analysis, an introduction*][beals-analysis], §7D,
and reproduced on wikipedia.
-/
theorem bernsteinApproximation_uniform (f : C(I, ℝ)) :
    Tendsto (fun n : ℕ => bernsteinApproximation n f) atTop (𝓝 f) := by
  simp only [Metric.nhds_basis_ball.tendsto_right_iff, Metric.mem_ball, dist_eq_norm]
  intro ε h
  let δ := δ f ε h
  have nhds_zero := tendsto_const_div_atTop_nhds_0_nat (2 * ‖f‖ * δ ^ (-2 : ℤ))
  filter_upwards [nhds_zero.eventually (gt_mem_nhds (half_pos h)), eventually_gt_atTop 0] with n nh
    npos'
  have npos : 0 < (n : ℝ) := by positivity
  -- As `[0,1]` is compact, it suffices to check the inequality pointwise.
  rw [ContinuousMap.norm_lt_iff _ h]
  

Proof state:
case h
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
δ : ℝ := bernsteinApproximation.δ f ε h
nhds_zero : Tendsto (fun n => 2 * ‖f‖ * δ ^ (-2) / ↑n) atTop (𝓝 0)
n : ℕ
nh : 2 * ‖f‖ * δ ^ (-2) / ↑n < ε / 2
npos' : 0 < n
npos : 0 < ↑n
⊢ ∀ (x : ↑I), ‖(bernsteinApproximation n f - f) x‖ < ε

Next tactic:
intro x
/-- The Bernstein approximations
```
∑ k : Fin (n+1), f (k/n : ℝ) * n.choose k * x^k * (1-x)^(n-k)
```
for a continuous function `f : C([0,1], ℝ)` converge uniformly to `f` as `n` tends to infinity.

This is the proof given in [Richard Beals' *Analysis, an introduction*][beals-analysis], §7D,
and reproduced on wikipedia.
-/
theorem bernsteinApproximation_uniform (f : C(I, ℝ)) :
    Tendsto (fun n : ℕ => bernsteinApproximation n f) atTop (𝓝 f) := by
  simp only [Metric.nhds_basis_ball.tendsto_right_iff, Metric.mem_ball, dist_eq_norm]
  intro ε h
  let δ := δ f ε h
  have nhds_zero := tendsto_const_div_atTop_nhds_0_nat (2 * ‖f‖ * δ ^ (-2 : ℤ))
  filter_upwards [nhds_zero.eventually (gt_mem_nhds (half_pos h)), eventually_gt_atTop 0] with n nh
    npos'
  have npos : 0 < (n : ℝ) := by positivity
  -- As `[0,1]` is compact, it suffices to check the inequality pointwise.
  rw [ContinuousMap.norm_lt_iff _ h]
  intro x
  -- The idea is to split up the sum over `k` into two sets,
  -- `S`, where `x - k/n < δ`, and its complement.
  

Proof state:
case h
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
δ : ℝ := bernsteinApproximation.δ f ε h
nhds_zero : Tendsto (fun n => 2 * ‖f‖ * δ ^ (-2) / ↑n) atTop (𝓝 0)
n : ℕ
nh : 2 * ‖f‖ * δ ^ (-2) / ↑n < ε / 2
npos' : 0 < n
npos : 0 < ↑n
x : ↑I
⊢ ‖(bernsteinApproximation n f - f) x‖ < ε

Next tactic:
let S := S f ε h n x
/-- The Bernstein approximations
```
∑ k : Fin (n+1), f (k/n : ℝ) * n.choose k * x^k * (1-x)^(n-k)
```
for a continuous function `f : C([0,1], ℝ)` converge uniformly to `f` as `n` tends to infinity.

This is the proof given in [Richard Beals' *Analysis, an introduction*][beals-analysis], §7D,
and reproduced on wikipedia.
-/
theorem bernsteinApproximation_uniform (f : C(I, ℝ)) :
    Tendsto (fun n : ℕ => bernsteinApproximation n f) atTop (𝓝 f) := by
  simp only [Metric.nhds_basis_ball.tendsto_right_iff, Metric.mem_ball, dist_eq_norm]
  intro ε h
  let δ := δ f ε h
  have nhds_zero := tendsto_const_div_atTop_nhds_0_nat (2 * ‖f‖ * δ ^ (-2 : ℤ))
  filter_upwards [nhds_zero.eventually (gt_mem_nhds (half_pos h)), eventually_gt_atTop 0] with n nh
    npos'
  have npos : 0 < (n : ℝ) := by positivity
  -- As `[0,1]` is compact, it suffices to check the inequality pointwise.
  rw [ContinuousMap.norm_lt_iff _ h]
  intro x
  -- The idea is to split up the sum over `k` into two sets,
  -- `S`, where `x - k/n < δ`, and its complement.
  let S := S f ε h n x
  

Proof state:
case h
f : C(↑I, ℝ)
ε : ℝ
h : 0 < ε
δ : ℝ := bernsteinApproximation.δ f ε h
nhds_zero : Tendsto (fun n => 2 * ‖f‖ * δ ^ (-2) / ↑n) atTop (𝓝 0)
n : ℕ
nh : 2 * ‖f‖ * δ ^ (-2) / ↑n < ε / 2
npos' : 0 < n
npos : 0 < ↑n
x : ↑I
S : Finset (Fin (n + 1)) := bernsteinApproximation.S f ε h n x
⊢ ‖(bernsteinApproximation n f - f) x‖ < ε

Next tactic:
calc
    |(bernsteinApproximation n f - f) x| = |bernsteinApproximation n f x - f x| := rfl
    _ = |bernsteinApproximation n f x - f x * 1| := by rw [mul_one]
    _ = |bernsteinApproximation n f x - f x * ∑ k : Fin (n + 1), bernstein n k x| := by
      rw [bernstein.probability]
    _ = |∑ k : Fin (n + 1), (f k/ₙ - f x) * bernstein n k x| := by
      simp [bernsteinApproximation, Finset.mul_sum, sub_mul]
    _ ≤ ∑ k : Fin (n + 1), |(f k/ₙ - f x) * bernstein n k x| := (Finset.abs_sum_le_sum_abs _ _)
    _ = ∑ k : Fin (n + 1), |f k/ₙ - f x| * bernstein n k x := by
      simp_rw [abs_mul, abs_eq_self.mpr bernstein_nonneg]
    _ = (∑ k in S, |f k/ₙ - f x| * bernstein n k x) + ∑ k in Sᶜ, |f k/ₙ - f x| * bernstein n k x :=
      (S.sum_add_sum_compl _).symm
    -- We'll now deal with the terms in `S` and the terms in `Sᶜ` in separate calc blocks.
    _ < ε / 2 + ε / 2 :=
      (add_lt_add_of_le_of_lt ?_ ?_)
    _ = ε := add_halves ε