-- Convergent sequences of analytic functions

import analysis.analytic.basic
import analysis.calculus.fderiv_analytic
import analysis.complex.cauchy_integral
import analysis.complex.re_im_topology
import data.complex.basic
import data.real.basic
import data.real.ennreal
import data.real.nnreal
import data.real.pi.bounds
import data.set.basic
import measure_theory.integral.interval_integral
import order.bounded_order
import order.filter.at_top_bot
import topology.metric_space.basic
import topology.uniform_space.uniform_convergence

import analytic
import bounds
import simple
import tactics
import topology

open complex (abs I)
open filter (at_top)
open measure_theory.measure_space (volume)
open metric (ball closed_ball sphere)
open_locale real nnreal topological_space

noncomputable theory

def power_series := formal_multilinear_series ℂ ℂ ℂ

lemma cauchy_on_cball_radius {f : ℂ → ℂ} {z : ℂ} {r : ℝ≥0} (rp : r > 0)
    (h : analytic_on ℂ f (closed_ball z r))
    : has_fpower_series_on_ball f (cauchy_power_series f z r) z r := begin
  have hd : differentiable_on ℂ f (closed_ball z r), {
    intros x H, exact analytic_at.differentiable_within_at (h x H)
  },
  set p : power_series := cauchy_power_series f z r,
  exact differentiable_on.has_fpower_series_on_ball hd rp
end

lemma analytic_on_cball_radius {f : ℂ → ℂ} {z : ℂ} {r : ℝ≥0} (rp : r > 0) (h : analytic_on ℂ f (closed_ball z r))
    : ∃ p : power_series, has_fpower_series_on_ball f p z r :=
  ⟨cauchy_power_series f z r, cauchy_on_cball_radius rp h⟩

lemma analytic_on_small_cball {f : ℂ → ℂ} {z : ℂ} {r : ℝ≥0} (h : analytic_on ℂ f (ball z r))
    (s : ℝ≥0) (sr : s < r) : analytic_on ℂ f (closed_ball z s) := begin
  intros x hx,
  rw closed_ball at hx, simp at hx,
  have hb : x ∈ ball z r, {
    rw ball, simp,
    calc nndist x z ≤ s : hx
    ... < r : sr
  },
  exact h x hb
end

lemma analytic_on_ball_radius {f : ℂ → ℂ} {z : ℂ} {r : ℝ≥0} (rp : r > 0) (h : analytic_on ℂ f (ball z r))
    : ∃ p : power_series, has_fpower_series_on_ball f p z r := begin
  have h0 := analytic_on_small_cball h (r/2) (nnreal.half_lt_self $ ne_of_gt rp),
  rcases analytic_on_cball_radius (nnreal.half_pos rp) h0 with ⟨p, ph⟩,
  set R := formal_multilinear_series.radius p,
  refine ⟨p, { r_le := _, r_pos := ennreal.coe_pos.mpr rp, has_sum := _ }⟩, {
    apply ennreal.le_of_forall_pos_nnreal_lt,
    intros t tp tr,
    have ht := analytic_on_small_cball h t (ennreal.coe_lt_coe.elim_left tr),
    rcases analytic_on_cball_radius tp ht with ⟨p',hp'⟩,
    have pp : p = p' := has_fpower_series_at.eq_formal_multilinear_series ⟨↑(r/2),ph⟩ ⟨t,hp'⟩,
    rw ←pp at hp',
    refine hp'.r_le
  }, {
    intros y yr,
    rw [emetric.ball, set.mem_set_of] at yr,
    rcases exists_between yr with ⟨t,t0,t1⟩,
    have ht := analytic_on_small_cball h t.to_nnreal (simple.nnreal_ennreal_coe_lt t1),
    rcases analytic_on_cball_radius _ ht with ⟨p',hp'⟩,
    have pp : p = p' := has_fpower_series_at.eq_formal_multilinear_series ⟨↑(r/2),ph⟩ ⟨t.to_nnreal,hp'⟩,
    rw ←pp at hp',
    refine hp'.has_sum _,
    rw [emetric.ball, set.mem_set_of],
    calc edist y 0 < t : t0
    ... = ↑(t.to_nnreal) : (ennreal.coe_to_nnreal $ ne_top_of_lt t1).symm,
    exact simple.to_nnreal_pos t0 t1
  }
end

lemma cauchy_bound {f : ℂ → ℂ} {c : ℂ} {r : ℝ≥0} {d : ℝ≥0} {w : ℂ} {n : ℕ}
  (rp : r > 0) (h : ∀ w ∈ closed_ball c r, abs (f w) ≤ d)
  : abs (cauchy_power_series f c r n (λ _, w)) ≤ abs w ^ n * r⁻¹ ^ n * d :=
begin
  set wr := abs w ^ n * r⁻¹ ^ n * d,
  rw [cauchy_power_series_apply f c r n w],
  rw [smul_eq_mul, complex.abs_mul],
  generalize hg : (λ z, (w / (z - c)) ^ n • (z - c)⁻¹ • f z) = g,
  have gs : ∀ z ∈ sphere c r, ∥g z∥ ≤ wr * r⁻¹, {
    intro, simp, intro, rw ←hg, simp, rw H,
    have zb : z ∈ closed_ball c r, simp, rw [←nnreal.coe_le_coe, coe_nndist, complex.dist_eq], linarith,
    have zs := h z zb,
    calc abs w ^ n / ↑r ^ n * (r⁻¹ * abs (f z)) = abs w ^ n * r⁻¹ ^ n * (r⁻¹ * abs (f z))
      : by rw simple.div_pow_inv (abs w ^ n) ↑r n
    ... ≤ abs w ^ n * r⁻¹ ^ n * (r⁻¹ * d) : by bound
    ... = abs w ^ n * r⁻¹ ^ n * d * r⁻¹ : by ring
    ... = wr * r⁻¹ : rfl
  },
  have cn := circle_integral.norm_integral_le_of_norm_le_const (nnreal.coe_nonneg r) gs,
  rw complex.norm_eq_abs at cn,
  simp at hg cn ⊢,
  have p3 : |π| = π := abs_eq_self.elim_right (by linarith [real.pi_gt_three]),
  calc |π|⁻¹ * 2⁻¹ * abs (circle_integral g c ↑r) ≤ |π|⁻¹ * 2⁻¹ * (2*π*r * (wr * r⁻¹)) : by bound
  ... = (π * |π|⁻¹) * (r * r⁻¹) * wr : by ring
  ... = (π * π⁻¹) * (r * r⁻¹) * wr : by rw p3
  ... = 1 * (r * r⁻¹) * wr : by rw mul_inv_cancel real.pi_ne_zero
  ... = 1 * 1 * wr : by rw mul_inv_cancel (nnreal.coe_ne_zero.elim_right $ ne_of_gt rp)
  ... = wr : by ring
end

lemma circle_integral_sub {f g : ℂ → ℂ} {c : ℂ} {r : ℝ}
    (fi : circle_integrable f c r) (gi : circle_integrable g c r)
    : circle_integral f c r - circle_integral g c r = circle_integral (f - g) c r := begin
  rw circle_integral, generalize hf : (λ θ : ℝ, deriv (circle_map c r) θ • f (circle_map c r θ)) = fc,
  rw circle_integral, generalize hg : (λ θ : ℝ, deriv (circle_map c r) θ • g (circle_map c r θ)) = gc,
  rw circle_integral, generalize hfg : (λ θ : ℝ, deriv (circle_map c r) θ • (f-g) (circle_map c r θ)) = fgc,
  have hs : fc - gc = fgc, {
    rw [←hf, ←hg, ←hfg], apply funext, simp, intro, rw mul_sub_left_distrib,
  },
  rw ←hs, clear hfg hs fgc, symmetry,
  have fci := circle_integrable.out fi, rw hf at fci,
  have gci := circle_integrable.out gi, rw hg at gci,
  exact interval_integral.integral_sub fci gci
end

lemma circle_map_nz {c : ℂ} {r : ℝ≥0} {θ : ℝ} (rp : r > 0) : circle_map c r θ - c ≠ 0 := begin
  simp, intro h, rw h at rp, simp at rp, exact rp
end

lemma cauchy_is_circle_integrable {f : ℂ → ℂ} {c : ℂ} {r : ℝ≥0}
    (n : ℕ) (w : ℂ) (rp : r > 0) (h : continuous_on f (closed_ball c r))
    : circle_integrable (λ z, w^n / (z - c)^n * ((z - c)⁻¹ * f z)) c r := begin
  refine continuous_on.interval_integrable _,
  refine continuous_on.mul _ _,
  refine continuous_on.mul continuous_on_const _,
  apply continuous.continuous_on,
  refine continuous.inv₀ (by continuity) (λ x, pow_ne_zero n (circle_map_nz rp)),
  refine continuous_on.mul _ _,
  apply continuous.continuous_on,
  refine continuous.inv₀ (by continuity) (λ x, circle_map_nz rp),
  refine continuous_on.comp h (continuous.continuous_on (by continuity)) _,
  intros θ hθ, exact circle_map_mem_closed_ball c (nnreal.coe_nonneg r) θ,
end

lemma cauchy_sub {f g : ℂ → ℂ} {c : ℂ} {r : ℝ≥0}
    (n : ℕ) (w : ℂ) (rp : r > 0) (cf : continuous_on f (closed_ball c r)) (cg : continuous_on g (closed_ball c r))
    : cauchy_power_series f c r n (λ _, w) - cauchy_power_series g c r n (λ _, w)
      = cauchy_power_series (f - g) c r n (λ _, w) :=
begin
  rw [cauchy_power_series_apply f c r n w],
  rw [cauchy_power_series_apply g c r n w],
  rw [cauchy_power_series_apply (f-g) c r n w],
  set s : ℂ := (2 * π * I)⁻¹,
  simp,
  have hfg : (λ z, w^n / (z - c)^n * ((z - c)⁻¹ * (f - g) z))
           = (λ z, w^n / (z - c)^n * ((z - c)⁻¹ * f z))
           - (λ z, w^n / (z - c)^n * ((z - c)⁻¹ * g z)), {
    apply funext, simp, intro, ring,
  },
  have fi := cauchy_is_circle_integrable n w rp cf,
  have gi := cauchy_is_circle_integrable n w rp cg,
  have cia := circle_integral_sub fi gi,
  rw [←mul_sub_left_distrib, cia],
  clear cia fi gi hfg cf cg rp,
  have flip : (λ z, w^n/(z-c)^n * ((z-c)⁻¹ * f z)) - (λ z, w^n/(z-c)^n * ((z-c)⁻¹ * g z))
            = (λ z, w^n/(z-c)^n * ((z-c)⁻¹ * f z) - w^n/(z-c)^n * ((z-c)⁻¹ * g z)) := rfl,
  rw flip, clear flip, ring_nf
end

lemma cauchy_dist {f g : ℂ → ℂ} {c : ℂ} {r : ℝ≥0} {d : ℝ≥0}
  (n : ℕ) (w : ℂ) (rp : r > 0) (cf : continuous_on f (closed_ball c r)) (cg : continuous_on g (closed_ball c r))
  (h : ∀ z, z ∈ closed_ball c r → abs (f z - g z) ≤ d)
  : dist (cauchy_power_series f c r n (λ _, w)) (cauchy_power_series g c r n (λ _, w)) ≤ abs w ^ n * r⁻¹ ^ n * d :=
begin
  rw [complex.dist_eq, cauchy_sub n w rp cf cg],
  refine cauchy_bound rp _, intros z zr, simp at h zr, refine h z zr
end

-- Uniform limits of analytic functions are analytic.
theorem uniform_analytic_lim {I : Type} [lattice I] [nonempty I] {f : I → ℂ → ℂ} {g : ℂ → ℂ} {s : set ℂ}
    (o : is_open s) (h : ∀ n, analytic_on ℂ (f n) s) (u : tendsto_uniformly_on f g at_top s)
    : analytic_on ℂ g s := begin
  intros c hc,
  rcases open_has_cball o c hc with ⟨r, rp, cb⟩,
  have hb : ∀ n, analytic_on ℂ (f n) (closed_ball c r) := λ n, (h n).mono cb,
  set pr := λ n, cauchy_power_series (f n) c r,
  have hpf : ∀ n, has_fpower_series_on_ball (f n) (pr n) c r, {
    intro,
    have cs := cauchy_on_cball_radius rp (hb n),
    have pn : pr n = cauchy_power_series (f n) c r := rfl,
    rw ←pn at cs, exact cs
  },
  have cfs : ∀ n, continuous_on (f n) s := λ n, analytic_on.continuous_on (h n),
  have cf : ∀ n, continuous_on (f n) (closed_ball c r) := λ n, continuous_on.mono (cfs n) cb,
  have cg : continuous_on g (closed_ball c r)
    := continuous_on.mono (tendsto_uniformly_on.continuous_on u (filter.eventually_of_forall cfs)) cb,
  clear h hb hc o cfs,
  set p := cauchy_power_series g c r,
  exact has_fpower_series_on_ball.analytic_at {
    r_le := le_radius_cauchy_power_series g c r,
    r_pos := ennreal.coe_pos.mpr rp,
    has_sum := _
  },
  intros y yb,
  have yr := yb, simp at yr,
  set a := abs y / r,
  have a0 : a ≥ 0 := by bound,
  have a1 : a < 1 := (div_lt_one (nnreal.coe_pos.mpr rp)).mpr yr,
  have a1p : 1 - a > 0 := by bound,
  rw [has_sum, metric.tendsto_at_top],
  intros e ep,
  generalize d4 : (1-a)*(e/4) = d,
  have dp : d > 0, { rw ←d4, bound },
  rcases filter.eventually_at_top.mp (metric.tendsto_uniformly_on_iff.mp u d dp) with ⟨n,hn'⟩,
  set hn := hn' n, simp at hn, clear hn' u,
  have dfg : dist (f n (c + y)) (g (c + y)) ≤ d, {
    apply le_of_lt, rw dist_comm,
    refine hn (c + y) _,
    apply cb,
    simp, exact le_of_lt yr
  },
  set hs := (hpf n).has_sum yb, rw [has_sum, metric.tendsto_at_top] at hs,
  rcases hs d dp with ⟨N,NM⟩, clear hs,
  existsi N, intros M NlM,
  have dpf := le_of_lt (NM M NlM), clear NM NlM N yb,
  have dppr : dist (M.sum (λ (k : ℕ), p k (λ _, y))) (M.sum (λ (k : ℕ), pr n k (λ _, y))) ≤ e/4, {
    transitivity M.sum (λ (k : ℕ), dist (p k (λ _, y)) (pr n k (λ _, y))),
    apply dist_sum_sum_le M (λ (k : ℕ), p k (λ _, y)) (λ (k : ℕ), pr n k (λ _, y)),
    transitivity M.sum (λ k, a^k * d), {
      apply finset.sum_le_sum, intros k hk,
      have hak : a^k = abs y^k * r⁻¹^k, {
        calc (abs y / r)^k = (abs y * r⁻¹)^k : by ring
        ... = abs y^k * r⁻¹^k : mul_pow _ _ _
      },
      rw hak,
      generalize hd' : d.to_nnreal = d',
      have dd : (d' : ℝ) = d, { rw ←hd', exact real.coe_to_nnreal d (le_of_lt dp) },
      have hcb : ∀ z, z ∈ closed_ball c r → abs (g z - f n z) ≤ d', {
        intros z zb, exact trans (le_of_lt (hn z (cb zb))) (le_of_eq dd.symm)
      },
      exact trans (cauchy_dist k y rp cg (cf n) hcb) (mul_le_mul_of_nonneg_left (le_of_eq dd) (by bound))
    }, {
      have pgb : M.sum (λ k, a^k) ≤ (1-a)⁻¹ := partial_geometric_bound M a0 a1,
      calc M.sum (λ k, a^k * d) = M.sum (λ k, a^k) * d : by rw ←finset.sum_mul
      ... ≤ (1-a)⁻¹ * d : by bound
      ... = (1-a)⁻¹ * ((1-a) * (e/4)) : by rw ←d4
      ... = (1-a) * (1-a)⁻¹ * (e/4) : by ring
      ... = 1 * (e/4) : by rw mul_inv_cancel (ne_of_gt a1p)
      ... = e/4 : by ring
    }
  },
  generalize hMp : M.sum (λ k : ℕ, p k (λ _, y)) = Mp, rw hMp at dppr,
  generalize hMpr : M.sum (λ k, pr n k (λ _, y)) = Mpr, rw hMpr at dpf dppr,
  calc dist Mp (g (c + y)) ≤ dist Mp (f n (c + y)) + dist (f n (c + y)) (g (c + y)) : dist_triangle _ _ _
  ... ≤ dist Mp Mpr + dist Mpr (f n (c + y)) + d : by bound
  ... ≤ e/4 + d + d : by bound
  ... = e/4 + 2*(1-a)*(e/4) : by { rw ←d4, ring }
  ... ≤ e/4 + 2*(1-0)*(e/4) : by bound
  ... = (3/4)*e : by ring
  ... < 1*e : mul_lt_mul_of_pos_right (by norm_num) ep
  ... = e : by simp
end


/-
Copyright (c) 2022 Yaël Dillies. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies
-/
import analysis.convex.combination

/-!
# Convex join

This file defines the convex join of two sets. The convex join of `s` and `t` is the union of the
segments with one end in `s` and the other in `t`. This is notably a useful gadget to deal with
convex hulls of finite sets.
-/

open set
open_locale big_operators

variables {ι : Sort*} {𝕜 E : Type*}

section ordered_semiring
variables (𝕜) [ordered_semiring 𝕜] [add_comm_monoid E] [module 𝕜 E] {s t s₁ s₂ t₁ t₂ u : set E}
  {x y : E}

/-- The join of two sets is the union of the segments joining them. This can be interpreted as the
topological join, but within the original space. -/
def convex_join (s t : set E) : set E := ⋃ (x ∈ s) (y ∈ t), segment 𝕜 x y

variables {𝕜}

lemma mem_convex_join : x ∈ convex_join 𝕜 s t ↔ ∃ (a ∈ s) (b ∈ t), x ∈ segment 𝕜 a b :=
by simp [convex_join]

lemma convex_join_comm (s t : set E) : convex_join 𝕜 s t = convex_join 𝕜 t s :=
(Union₂_comm _).trans $ by simp_rw [convex_join, segment_symm]

lemma convex_join_mono (hs : s₁ ⊆ s₂) (ht : t₁ ⊆ t₂) : convex_join 𝕜 s₁ t₁ ⊆ convex_join 𝕜 s₂ t₂ :=
bUnion_mono hs $ λ x hx, bUnion_mono ht $ λ y hy, subset.rfl

lemma convex_join_mono_left (hs : s₁ ⊆ s₂) : convex_join 𝕜 s₁ t ⊆ convex_join 𝕜 s₂ t :=
convex_join_mono hs subset.rfl

lemma convex_join_mono_right (ht : t₁ ⊆ t₂) : convex_join 𝕜 s t₁ ⊆ convex_join 𝕜 s t₂ :=
convex_join_mono subset.rfl ht

@[simp] lemma convex_join_empty_left (t : set E) : convex_join 𝕜 ∅ t = ∅ := by simp [convex_join]
@[simp] lemma convex_join_empty_right (s : set E) : convex_join 𝕜 s ∅ = ∅ := by simp [convex_join]

@[simp] lemma convex_join_singleton_left (t : set E) (x : E) :
  convex_join 𝕜 {x} t = ⋃ (y ∈ t), segment 𝕜 x y := by simp [convex_join]

@[simp] lemma convex_join_singleton_right (s : set E) (y : E) :
  convex_join 𝕜 s {y} = ⋃ (x ∈ s), segment 𝕜 x y := by simp [convex_join]

@[simp] lemma convex_join_singletons (x : E) : convex_join 𝕜 {x} {y} = segment 𝕜 x y :=
by simp [convex_join]

@[simp] lemma convex_join_union_left (s₁ s₂ t : set E) :
  convex_join 𝕜 (s₁ ∪ s₂) t = convex_join 𝕜 s₁ t ∪ convex_join 𝕜 s₂ t :=
by simp_rw [convex_join, mem_union, Union_or, Union_union_distrib]

@[simp] lemma convex_join_union_right (s t₁ t₂ : set E) :
  convex_join 𝕜 s (t₁ ∪ t₂) = convex_join 𝕜 s t₁ ∪ convex_join 𝕜 s t₂ :=
by simp_rw [convex_join, mem_union, Union_or, Union_union_distrib]

@[simp] lemma convex_join_Union_left (s : ι → set E) (t : set E) :
  convex_join 𝕜 (⋃ i, s i) t = ⋃ i, convex_join 𝕜 (s i) t :=
by { simp_rw [convex_join, mem_Union, Union_exists], exact Union_comm _ }

@[simp] lemma convex_join_Union_right (s : set E) (t : ι → set E) :
  convex_join 𝕜 s (⋃ i, t i) = ⋃ i, convex_join 𝕜 s (t i) :=
by simp_rw [convex_join_comm s, convex_join_Union_left]

lemma segment_subset_convex_join (hx : x ∈ s) (hy : y ∈ t) : segment 𝕜 x y ⊆ convex_join 𝕜 s t :=
(subset_Union₂ y hy).trans (subset_Union₂ x hx)

lemma subset_convex_join_left (h : t.nonempty) : s ⊆ convex_join 𝕜 s t :=
λ x hx, let ⟨y, hy⟩ := h in segment_subset_convex_join hx hy $ left_mem_segment _ _ _

lemma subset_convex_join_right (h : s.nonempty) : t ⊆ convex_join 𝕜 s t :=
λ y hy, let ⟨x, hx⟩ := h in segment_subset_convex_join hx hy $ right_mem_segment _ _ _

lemma convex_join_subset (hs : s ⊆ u) (ht : t ⊆ u) (hu : convex 𝕜 u) : convex_join 𝕜 s t ⊆ u :=
Union₂_subset $ λ x hx, Union₂_subset $ λ y hy, hu.segment_subset (hs hx) (ht hy)

lemma convex_join_subset_convex_hull (s t : set E) : convex_join 𝕜 s t ⊆ convex_hull 𝕜 (s ∪ t) :=
convex_join_subset ((subset_union_left _ _).trans $ subset_convex_hull _ _)
  ((subset_union_right _ _).trans $ subset_convex_hull _ _) $ convex_convex_hull _ _

end ordered_semiring

section linear_ordered_field
variables [linear_ordered_field 𝕜] [add_comm_group E] [module 𝕜 E] {s t u : set E} {x y : E}

lemma convex_join_assoc_aux (s t u : set E) :
  convex_join 𝕜 (convex_join 𝕜 s t) u ⊆ convex_join 𝕜 s (convex_join 𝕜 t u) :=
begin
  simp_rw [subset_def, mem_convex_join],
  rintro _ ⟨z, ⟨x, hx, y, hy, a₁, b₁, ha₁, hb₁, hab₁, rfl⟩, z, hz, a₂, b₂, ha₂, hb₂, hab₂, rfl⟩,
  obtain rfl | hb₂ := hb₂.eq_or_lt,
  { refine ⟨x, hx, y, ⟨y, hy, z, hz, left_mem_segment _ _ _⟩, a₁, b₁, ha₁, hb₁, hab₁, _⟩,
    rw add_zero at hab₂,
    rw [hab₂, one_smul, zero_smul, add_zero] },
  have ha₂b₁ : 0 ≤ a₂ * b₁ := mul_nonneg ha₂ hb₁,
  have hab : 0 < a₂ * b₁ + b₂ := add_pos_of_nonneg_of_pos ha₂b₁ hb₂,
  refine ⟨x, hx, ((a₂ * b₁) / (a₂ * b₁ + b₂)) • y + (b₂ / (a₂ * b₁ + b₂)) • z,
    ⟨y, hy, z, hz, _, _, _, _, _, rfl⟩, a₂ * a₁, a₂ * b₁ + b₂, mul_nonneg ha₂ ha₁, hab.le, _, _⟩,
  { exact div_nonneg ha₂b₁ hab.le },
  { exact div_nonneg hb₂.le hab.le },
  { rw [←add_div, div_self hab.ne'] },
  { rw [←add_assoc, ←mul_add, hab₁, mul_one, hab₂] },
  { simp_rw [smul_add, ←mul_smul, mul_div_cancel' _ hab.ne', add_assoc] }
end

lemma convex_join_assoc (s t u : set E) :
  convex_join 𝕜 (convex_join 𝕜 s t) u = convex_join 𝕜 s (convex_join 𝕜 t u) :=
begin
  refine (convex_join_assoc_aux _ _ _).antisymm _,
  simp_rw [convex_join_comm s, convex_join_comm _ u],
  exact convex_join_assoc_aux _ _ _,
end

lemma convex_join_left_comm (s t u : set E) :
  convex_join 𝕜 s (convex_join 𝕜 t u) = convex_join 𝕜 t (convex_join 𝕜 s u) :=
by simp_rw [←convex_join_assoc, convex_join_comm]

lemma convex_join_right_comm (s t u : set E) :
  convex_join 𝕜 (convex_join 𝕜 s t) u = convex_join 𝕜 (convex_join 𝕜 s u) t :=
by simp_rw [convex_join_assoc, convex_join_comm]

lemma convex_join_convex_join_convex_join_comm (s t u v : set E) :
  convex_join 𝕜 (convex_join 𝕜 s t) (convex_join 𝕜 u v) =
    convex_join 𝕜 (convex_join 𝕜 s u) (convex_join 𝕜 t v) :=
by simp_rw [←convex_join_assoc, convex_join_right_comm]

lemma convex_hull_insert (hs : s.nonempty) :
  convex_hull 𝕜 (insert x s) = convex_join 𝕜 {x} (convex_hull 𝕜 s) :=
begin
  classical,
  refine (convex_join_subset ((singleton_subset_iff.2 $ mem_insert _ _).trans $ subset_convex_hull
    _ _) (convex_hull_mono $ subset_insert _ _) $ convex_convex_hull _ _).antisymm' (λ x hx, _),
  rw convex_hull_eq at hx,
  obtain ⟨ι, t, w, z, hw₀, hw₁, hz, rfl⟩ := hx,
  have : (∑ i in t.filter (λ i, z i = x), w i) • x + ∑ i in t.filter (λ i, z i ≠ x), w i • z i =
    t.center_mass w z,
  { rw [finset.center_mass_eq_of_sum_1 _ _ hw₁, finset.sum_smul],
    convert finset.sum_filter_add_sum_filter_not _ _ (w • z) using 2,
    refine finset.sum_congr rfl (λ i hi, _),
    rw [pi.smul_apply', (finset.mem_filter.1 hi).2] },
  rw ←this,
  have hw₀' : ∀ i ∈ t.filter (λ i, z i ≠ x), 0 ≤ w i := λ i hi, hw₀ _ $ finset.filter_subset _ _ hi,
  obtain hw | hw := (finset.sum_nonneg hw₀').eq_or_gt,
  { rw [←finset.sum_filter_add_sum_filter_not _ (λ i, z i = x), hw, add_zero] at hw₁,
    rw [hw₁, one_smul, finset.sum_eq_zero, add_zero],
    { exact subset_convex_join_left hs.convex_hull (mem_singleton _) },
    simp_rw finset.sum_eq_zero_iff_of_nonneg hw₀' at hw,
    rintro i hi,
    rw [hw _ hi, zero_smul] },
  refine mem_convex_join.2 ⟨x, mem_singleton _, (t.filter $ λ i, z i ≠ x).center_mass w z,
    finset.center_mass_mem_convex_hull _ hw₀' hw (λ i hi, _),
    ∑ i in t.filter (λ i, z i = x), w i, ∑ i in t.filter (λ i, z i ≠ x), w i,
    finset.sum_nonneg (λ i hi, hw₀ _ $ finset.filter_subset _ _ hi), finset.sum_nonneg hw₀', _, _⟩,
  { rw finset.mem_filter at hi,
    exact mem_of_mem_insert_of_ne (hz _ hi.1) hi.2 },
  { rw [finset.sum_filter_add_sum_filter_not, hw₁] },
  { rw [finset.center_mass, smul_inv_smul₀ hw.ne', finset.sum_smul] }
end

lemma convex_join_segments (a b c d : E) :
  convex_join 𝕜 (segment 𝕜 a b) (segment 𝕜 c d) = convex_hull 𝕜 {a, b, c, d} :=
by simp only [convex_hull_insert, insert_nonempty, singleton_nonempty, convex_hull_pair,
    ←convex_join_assoc, convex_join_singletons]

lemma convex_join_segment_singleton (a b c : E) :
  convex_join 𝕜 (segment 𝕜 a b) {c} = convex_hull 𝕜 {a, b, c} :=
by rw [←pair_eq_singleton, ←convex_join_segments, segment_same, pair_eq_singleton]

lemma convex_join_singleton_segment (a b c : E) :
  convex_join 𝕜 {a} (segment 𝕜 b c) = convex_hull 𝕜 {a, b, c} :=
by rw [←segment_same 𝕜, convex_join_segments, insert_idem]

protected lemma convex.convex_join (hs : convex 𝕜 s) (ht : convex 𝕜 t) :
  convex 𝕜 (convex_join 𝕜 s t) :=
begin
  rw convex_iff_segment_subset at ⊢ ht hs,
  simp_rw mem_convex_join,
  rintro x ⟨xa, hxa, xb, hxb, hx⟩ y ⟨ya, hya, yb, hyb, hy⟩,
  refine (segment_subset_convex_join hx hy).trans _,
  have triv : ({xa, xb, ya, yb} : set E) = {xa, ya, xb, yb} := by simp only [set.insert_comm],
  rw [convex_join_segments, triv, ←convex_join_segments],
  exact convex_join_mono (hs hxa hya) (ht hxb hyb),
end

protected lemma convex.convex_hull_union (hs : convex 𝕜 s) (ht : convex 𝕜 t) (hs₀ : s.nonempty)
  (ht₀ : t.nonempty) :
  convex_hull 𝕜 (s ∪ t) = convex_join 𝕜 s t :=
(convex_hull_min (union_subset (subset_convex_join_left ht₀) $ subset_convex_join_right hs₀) $
  hs.convex_join ht).antisymm $ convex_join_subset_convex_hull _ _

lemma convex_hull_union (hs : s.nonempty) (ht : t.nonempty) :
  convex_hull 𝕜 (s ∪ t) = convex_join 𝕜 (convex_hull 𝕜 s) (convex_hull 𝕜 t) :=
begin
  rw [←convex_hull_convex_hull_union_left, ←convex_hull_convex_hull_union_right],
  exact (convex_convex_hull 𝕜 s).convex_hull_union (convex_convex_hull 𝕜 t)
    hs.convex_hull ht.convex_hull,
end

end linear_ordered_field


/-
Copyright (c) 2017 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Tim Baumann, Stephen Morgan, Scott Morrison

Defines natural transformations between functors.

Introduces notations
  `F ⟹ G` for the type of natural transformations between functors `F` and `G`,
  `τ X` (a coercion) for the components of natural transformations,
  `σ ⊟ τ` for vertical compositions, and
  `σ ◫ τ` for horizontal compositions.
-/

import category_theory.functor

namespace category_theory

universes u₁ v₁ u₂ v₂ u₃ v₃ u₄ v₄

variables {C : Type u₁} [𝒞 : category.{u₁ v₁} C] {D : Type u₂} [𝒟 : category.{u₂ v₂} D]
include 𝒞 𝒟

/--
`nat_trans F G` represents a natural transformation between functors `F` and `G`.

The field `app` provides the components of the natural transformation, and there is a
coercion available so you can write `α X` for the component of a transformation `α` at an object `X`.

Naturality is expressed by `α.naturality_lemma`.
-/
structure nat_trans (F G : C ⥤ D) : Type (max u₁ v₂) :=
(app : Π X : C, (F X) ⟶ (G X))
(naturality' : ∀ {X Y : C} (f : X ⟶ Y), (F.map f) ≫ (app Y) = (app X) ≫ (G.map f) . obviously)

infixr ` ⟹ `:50  := nat_trans             -- type as \==> or ⟹

namespace nat_trans

instance {F G : C ⥤ D} : has_coe_to_fun (F ⟹ G) :=
{ F   := λ α, Π X : C, (F X) ⟶ (G X),
  coe := λ α, α.app }

@[simp] lemma app_eq_coe {F G : C ⥤ D} (α : F ⟹ G) (X : C) : α.app X = α X := by unfold_coes
@[simp] lemma mk_app {F G : C ⥤ D} (app : Π X : C, (F X) ⟶ (G X)) (naturality) (X : C) :
  { nat_trans . app := app, naturality' := naturality } X = app X := rfl

lemma naturality {F G : C ⥤ D} (α : F ⟹ G) {X Y : C} (f : X ⟶ Y) :
  (F.map f) ≫ (α Y) = (α X) ≫ (G.map f) :=
begin
  /- `obviously'` says: -/
  erw nat_trans.naturality', refl
end

/-- `nat_trans.id F` is the identity natural transformation on a functor `F`. -/
protected def id (F : C ⥤ D) : F ⟹ F :=
{ app := λ X, 𝟙 (F X) }

@[simp] lemma id_app (F : C ⥤ D) (X : C) : (nat_trans.id F) X = 𝟙 (F X) := rfl

open category
open category_theory.functor

section
variables {F G H I : C ⥤ D}

-- We'll want to be able to prove that two natural transformations are equal if they are componentwise equal.
@[extensionality] lemma ext (α β : F ⟹ G) (w : ∀ X : C, α X = β X) : α = β :=
begin
  induction α with α_components α_naturality,
  induction β with β_components β_naturality,
  have hc : α_components = β_components := funext w,
  subst hc
end

/-- `vcomp α β` is the vertical compositions of natural transformations. -/
def vcomp (α : F ⟹ G) (β : G ⟹ H) : F ⟹ H :=
{ app         := λ X, (α X) ≫ (β X),
  naturality' := begin /- `obviously'` says: -/ intros, simp, rw [←assoc, naturality, assoc, ←naturality], end }

notation α `⊟` β:80 := vcomp α β

@[simp] lemma vcomp_app (α : F ⟹ G) (β : G ⟹ H) (X : C) : (α ⊟ β) X = (α X) ≫ (β X) := rfl
@[simp] lemma vcomp_assoc (α : F ⟹ G) (β : G ⟹ H) (γ : H ⟹ I) : (α ⊟ β) ⊟ γ = (α ⊟ (β ⊟ γ)) := by tidy
end

variables {E : Type u₃} [ℰ : category.{u₃ v₃} E]
include ℰ

/-- `hcomp α β` is the horizontal composition of natural transformations. -/
def hcomp {F G : C ⥤ D} {H I : D ⥤ E} (α : F ⟹ G) (β : H ⟹ I) : (F ⋙ H) ⟹ (G ⋙ I) :=
{ app         := λ X : C, (β (F X)) ≫ (I.map (α X)),
  naturality' := begin
                   /- `obviously'` says: -/
                   intros,
                   dsimp,
                   simp,
                   -- Actually, obviously doesn't use exactly this sequence of rewrites, but achieves the same result
                   rw [← assoc, naturality, assoc],
                   conv { to_rhs, rw [← map_comp, ← α.naturality, map_comp] }
                 end }

notation α `◫` β:80 := hcomp α β

@[simp] lemma hcomp_app {F G : C ⥤ D} {H I : D ⥤ E} (α : F ⟹ G) (β : H ⟹ I) (X : C) :
  (α ◫ β) X = (β (F X)) ≫ (I.map (α X)) := rfl

-- Note that we don't yet prove a `hcomp_assoc` lemma here: even stating it is painful, because we need to use associativity of functor composition

lemma exchange {F G H : C ⥤ D} {I J K : D ⥤ E} (α : F ⟹ G) (β : G ⟹ H) (γ : I ⟹ J) (δ : J ⟹ K) :
  ((α ⊟ β) ◫ (γ ⊟ δ)) = ((α ◫ γ) ⊟ (β ◫ δ)) :=
begin
  -- `obviously'` says:
  ext,
  intros,
  dsimp,
  simp,
  -- again, this isn't actually what obviously says, but it achieves the same effect.
  conv { to_lhs, congr, skip, rw [←assoc, ←naturality, assoc] }
end

end nat_trans

end category_theory


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_algebra_427
  (x y z : ℝ)
  (h₀ : 3 * x + y = 17)
  (h₁ : 5 * y + z = 14)
  (h₂ : 3 * x + 5 * z = 41) :
  x + y + z = 12 :=
begin
  have h₃ := congr (congr_arg has_add.add h₀) h₁,
  linarith,
end