/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import analysis.box_integral.basic
import measure_theory.measure.regular

/-!
# McShane integrability vs Bochner integrability

In this file we prove that any Bochner integrable function is McShane integrable (hence, it is
Henstock and `⊥` integrable) with the same integral. The proof is based on
[Russel A. Gordon, *The integrals of Lebesgue, Denjoy, Perron, and Henstock*][Gordon55].

## Tags

integral, McShane integral, Bochner integral
-/

open_locale classical nnreal ennreal topology big_operators

universes u v

variables {ι : Type u} {E : Type v} [fintype ι] [normed_add_comm_group E] [normed_space ℝ E]

open measure_theory metric set finset filter box_integral

namespace box_integral

/-- The indicator function of a measurable set is McShane integrable with respect to any
locally-finite measure. -/
lemma has_integral_indicator_const (l : integration_params) (hl : l.bRiemann = ff)
  {s : set (ι → ℝ)} (hs : measurable_set s) (I : box ι) (y : E)
  (μ : measure (ι → ℝ)) [is_locally_finite_measure μ] :
  has_integral.{u v v} I l (s.indicator (λ _, y)) μ.to_box_additive.to_smul
    ((μ (s ∩ I)).to_real • y) :=
begin
  refine has_integral_of_mul (‖y‖) (λ ε ε0, _),
  lift ε to ℝ≥0 using ε0.le, rw nnreal.coe_pos at ε0,
  /- First we choose a closed set `F ⊆ s ∩ I.Icc` and an open set `U ⊇ s` such that
  both `(s ∩ I.Icc) \ F` and `U \ s` have measuer less than `ε`. -/
  have A : μ (s ∩ I.Icc) ≠ ∞,
    from ((measure_mono $ set.inter_subset_right _ _).trans_lt (I.measure_Icc_lt_top μ)).ne,
  have B : μ (s ∩ I) ≠ ∞,
    from ((measure_mono $ set.inter_subset_right _ _).trans_lt (I.measure_coe_lt_top μ)).ne,
  obtain ⟨F, hFs, hFc, hμF⟩ : ∃ F ⊆ s ∩ I.Icc, is_closed F ∧ μ ((s ∩ I.Icc) \ F) < ε,
    from (hs.inter I.measurable_set_Icc).exists_is_closed_diff_lt A (ennreal.coe_pos.2 ε0).ne',
  obtain ⟨U, hsU, hUo, hUt, hμU⟩ : ∃ U ⊇ s ∩ I.Icc, is_open U ∧ μ U < ∞ ∧ μ (U \ (s ∩ I.Icc)) < ε,
    from (hs.inter I.measurable_set_Icc).exists_is_open_diff_lt A (ennreal.coe_pos.2 ε0).ne',
  /- Then we choose `r` so that `closed_ball x (r x) ⊆ U` whenever `x ∈ s ∩ I.Icc` and
  `closed_ball x (r x)` is disjoint with `F` otherwise. -/
  have : ∀ x ∈ s ∩ I.Icc, ∃ r : Ioi (0 : ℝ), closed_ball x r ⊆ U,
    from λ x hx, subtype.exists'.1 (nhds_basis_closed_ball.mem_iff.1 (hUo.mem_nhds $ hsU hx)),
  choose! rs hrsU,
  have : ∀ x ∈ I.Icc \ s, ∃ r : Ioi (0 : ℝ), closed_ball x r ⊆ Fᶜ,
    from λ x hx, subtype.exists'.1 (nhds_basis_closed_ball.mem_iff.1 (hFc.is_open_compl.mem_nhds $
      λ hx', hx.2 (hFs hx').1)),
  choose! rs' hrs'F,
  set r : (ι → ℝ) → Ioi (0 : ℝ) := s.piecewise rs rs',
  refine ⟨λ c, r, λ c, l.r_cond_of_bRiemann_eq_ff hl, λ c π hπ hπp, _⟩, rw mul_comm,
  /- Then the union of boxes `J ∈ π` such that `π.tag ∈ s` includes `F` and is included by `U`,
  hence its measure is `ε`-close to the measure of `s`. -/
  dsimp [integral_sum],
  simp only [mem_closed_ball, dist_eq_norm, ← indicator_const_smul_apply,
    sum_indicator_eq_sum_filter, ← sum_smul, ← sub_smul, norm_smul, real.norm_eq_abs,
    ← prepartition.filter_boxes, ← prepartition.measure_Union_to_real],
  refine mul_le_mul_of_nonneg_right _ (norm_nonneg y),
  set t := (π.to_prepartition.filter (λ J, π.tag J ∈ s)).Union,
  change abs ((μ t).to_real - (μ (s ∩ I)).to_real) ≤ ε,
  have htU : t ⊆ U ∩ I,
  { simp only [t, prepartition.Union_def, Union_subset_iff, prepartition.mem_filter, and_imp],
    refine λ J hJ hJs x hx, ⟨hrsU _ ⟨hJs, π.tag_mem_Icc J⟩  _, π.le_of_mem' J hJ hx⟩,
    simpa only [r, s.piecewise_eq_of_mem _ _ hJs] using hπ.1 J hJ (box.coe_subset_Icc hx) },
  refine abs_sub_le_iff.2 ⟨_, _⟩,
  { refine (ennreal.le_to_real_sub B).trans (ennreal.to_real_le_coe_of_le_coe _),
    refine (tsub_le_tsub (measure_mono htU) le_rfl).trans (le_measure_diff.trans _),
    refine (measure_mono $ λ x hx, _).trans hμU.le,
    exact ⟨hx.1.1, λ hx', hx.2 ⟨hx'.1, hx.1.2⟩⟩ },
  { have hμt : μ t ≠ ∞ :=
      ((measure_mono (htU.trans (inter_subset_left _ _))).trans_lt hUt).ne,
    refine (ennreal.le_to_real_sub hμt).trans (ennreal.to_real_le_coe_of_le_coe _),
    refine le_measure_diff.trans ((measure_mono _).trans hμF.le),
    rintro x ⟨⟨hxs, hxI⟩, hxt⟩,
    refine ⟨⟨hxs, box.coe_subset_Icc hxI⟩, λ hxF, hxt _⟩,
    simp only [t, prepartition.Union_def, prepartition.mem_filter, set.mem_Union, exists_prop],
    rcases hπp x hxI with ⟨J, hJπ, hxJ⟩,
    refine ⟨J, ⟨hJπ, _⟩, hxJ⟩,
    contrapose hxF,
    refine hrs'F _ ⟨π.tag_mem_Icc J, hxF⟩ _,
    simpa only [r, s.piecewise_eq_of_not_mem _ _ hxF] using hπ.1 J hJπ (box.coe_subset_Icc hxJ) }
end

/-- If `f` is a.e. equal to zero on a rectangular box, then it has McShane integral zero on this
box. -/
lemma has_integral_zero_of_ae_eq_zero {l : integration_params} {I : box ι} {f : (ι → ℝ) → E}
  {μ : measure (ι → ℝ)} [is_locally_finite_measure μ] (hf : f =ᵐ[μ.restrict I] 0)
  (hl : l.bRiemann = ff) :
  has_integral.{u v v} I l f μ.to_box_additive.to_smul 0 :=
begin
  /- Each set `{x | n < ‖f x‖ ≤ n + 1}`, `n : ℕ`, has measure zero. We cover it by an open set of
  measure less than `ε / 2 ^ n / (n + 1)`. Then the norm of the integral sum is less than `ε`. -/
  refine has_integral_iff.2 (λ ε ε0, _),
  lift ε to ℝ≥0 using ε0.lt.le, rw [gt_iff_lt, nnreal.coe_pos] at ε0,
  rcases nnreal.exists_pos_sum_of_countable ε0.ne' ℕ with ⟨δ, δ0, c, hδc, hcε⟩,
  haveI := fact.mk (I.measure_coe_lt_top μ),
  change μ.restrict I {x | f x ≠ 0} = 0 at hf,
  set N : (ι → ℝ) → ℕ := λ x, ⌈‖f x‖⌉₊,
  have N0 : ∀ {x}, N x = 0 ↔ f x = 0, by { intro x, simp [N] },
  have : ∀ n, ∃ U ⊇ N ⁻¹' {n}, is_open U ∧ μ.restrict I U < δ n / n,
  { refine λ n, (N ⁻¹' {n}).exists_is_open_lt_of_lt _ _,
    cases n,
    { simpa [ennreal.div_zero (ennreal.coe_pos.2 (δ0 _)).ne']
        using measure_lt_top (μ.restrict I) _ },
    { refine (measure_mono_null _ hf).le.trans_lt _,
      { exact λ x hxN hxf, n.succ_ne_zero ((eq.symm hxN).trans $ N0.2 hxf) },
      { simp [(δ0 _).ne'] } } },
  choose U hNU hUo hμU,
  have : ∀ x, ∃ r : Ioi (0 : ℝ), closed_ball x r ⊆ U (N x),
    from λ x, subtype.exists'.1 (nhds_basis_closed_ball.mem_iff.1 ((hUo _).mem_nhds (hNU _ rfl))),
  choose r hrU,
  refine ⟨λ _, r, λ c, l.r_cond_of_bRiemann_eq_ff hl, λ c π hπ hπp, _⟩,
  rw [dist_eq_norm, sub_zero, ← integral_sum_fiberwise (λ J, N (π.tag J))],
  refine le_trans _ (nnreal.coe_lt_coe.2 hcε).le,
  refine (norm_sum_le_of_le _ _).trans
    (sum_le_has_sum _ (λ n _, (δ n).2) (nnreal.has_sum_coe.2 hδc)),
  rintro n -,
  dsimp [integral_sum],
  have : ∀ J ∈ π.filter (λ J, N (π.tag J) = n),
    ‖(μ ↑J).to_real • f (π.tag J)‖ ≤ (μ J).to_real * n,
  { intros J hJ, rw tagged_prepartition.mem_filter at hJ,
    rw [norm_smul, real.norm_eq_abs, abs_of_nonneg ennreal.to_real_nonneg],
    exact mul_le_mul_of_nonneg_left (hJ.2 ▸ nat.le_ceil _) ennreal.to_real_nonneg },
  refine (norm_sum_le_of_le _ this).trans _, clear this,
  rw [← sum_mul, ← prepartition.measure_Union_to_real],
  generalize hm : μ (π.filter (λ J, N (π.tag J) = n)).Union = m,
  have : m < δ n / n,
  { simp only [measure.restrict_apply (hUo _).measurable_set] at hμU,
    refine hm ▸ (measure_mono _).trans_lt (hμU _),
    simp only [set.subset_def, tagged_prepartition.mem_Union, exists_prop,
      tagged_prepartition.mem_filter],
    rintro x ⟨J, ⟨hJ, rfl⟩, hx⟩,
    exact ⟨hrU _ (hπ.1 _ hJ (box.coe_subset_Icc hx)), π.le_of_mem' J hJ hx⟩ },
  lift m to ℝ≥0 using ne_top_of_lt this,
  rw [ennreal.coe_to_real, ← nnreal.coe_nat_cast, ← nnreal.coe_mul, nnreal.coe_le_coe,
    ← ennreal.coe_le_coe, ennreal.coe_mul, ennreal.coe_nat, mul_comm],
  exact (mul_le_mul_left' this.le _).trans ennreal.mul_div_le
end

/-- If `f` has integral `y` on a box `I` with respect to a locally finite measure `μ` and `g` is
a.e. equal to `f` on `I`, then `g` has the same integral on `I`.  -/
lemma has_integral.congr_ae {l : integration_params} {I : box ι} {y : E} {f g : (ι → ℝ) → E}
  {μ : measure (ι → ℝ)} [is_locally_finite_measure μ]
  (hf : has_integral.{u v v} I l f μ.to_box_additive.to_smul y)
  (hfg : f =ᵐ[μ.restrict I] g) (hl : l.bRiemann = ff) :
  has_integral.{u v v} I l g μ.to_box_additive.to_smul y :=
begin
  have : (g - f) =ᵐ[μ.restrict I] 0, from hfg.mono (λ x hx, sub_eq_zero.2 hx.symm),
  simpa using hf.add (has_integral_zero_of_ae_eq_zero this hl)
end

end box_integral

namespace measure_theory

namespace simple_func

/-- A simple function is McShane integrable w.r.t. any locally finite measure. -/
lemma has_box_integral (f : simple_func (ι → ℝ) E) (μ : measure (ι → ℝ))
  [is_locally_finite_measure μ] (I : box ι) (l : integration_params) (hl : l.bRiemann = ff) :
  has_integral.{u v v} I l f μ.to_box_additive.to_smul (f.integral (μ.restrict I)) :=
begin
  induction f using measure_theory.simple_func.induction with y s hs f g hd hfi hgi,
  { simpa only [measure.restrict_apply hs, const_zero, integral_piecewise_zero, integral_const,
      measure.restrict_apply, measurable_set.univ, set.univ_inter]
      using box_integral.has_integral_indicator_const l hl hs I y μ },
  { borelize E, haveI := fact.mk (I.measure_coe_lt_top μ),
    rw integral_add,
    exacts [hfi.add hgi, integrable_iff.2 $ λ _ _, measure_lt_top _ _,
      integrable_iff.2 $ λ _ _, measure_lt_top _ _] }
end

/-- For a simple function, its McShane (or Henstock, or `⊥`) box integral is equal to its
integral in the sense of `measure_theory.simple_func.integral`. -/
lemma box_integral_eq_integral (f : simple_func (ι → ℝ) E) (μ : measure (ι → ℝ))
  [is_locally_finite_measure μ] (I : box ι) (l : integration_params) (hl : l.bRiemann = ff) :
  box_integral.integral.{u v v} I l f μ.to_box_additive.to_smul = f.integral (μ.restrict I) :=
(f.has_box_integral μ I l hl).integral_eq

end simple_func

open topological_space

/-- If `f : ℝⁿ → E` is Bochner integrable w.r.t. a locally finite measure `μ` on a rectangular box
`I`, then it is McShane integrable on `I` with the same integral.  -/
lemma integrable_on.has_box_integral [complete_space E] {f : (ι → ℝ) → E} {μ : measure (ι → ℝ)}
  [is_locally_finite_measure μ] {I : box ι} (hf : integrable_on f I μ) (l : integration_params)
  (hl : l.bRiemann = ff) :
  has_integral.{u v v} I l f μ.to_box_additive.to_smul (∫ x in I, f x ∂ μ) :=
begin
  borelize E,
  /- First we replace an `ae_strongly_measurable` function by a measurable one. -/
  rcases hf.ae_strongly_measurable with ⟨g, hg, hfg⟩,
  haveI : separable_space (range g ∪ {0} : set E) := hg.separable_space_range_union_singleton,
  rw integral_congr_ae hfg, have hgi : integrable_on g I μ := (integrable_congr hfg).1 hf,
  refine box_integral.has_integral.congr_ae _ hfg.symm hl,
  clear_dependent f,
  /- Now consider the sequence of simple functions
  `simple_func.approx_on g hg.measurable (range g ∪ {0}) 0 (by simp)`
  approximating `g`. Recall some properties of this sequence. -/
  set f : ℕ → simple_func (ι → ℝ) E :=
    simple_func.approx_on g hg.measurable (range g ∪ {0}) 0 (by simp),
  have hfi : ∀ n, integrable_on (f n) I μ,
    from simple_func.integrable_approx_on_range hg.measurable hgi,
  have hfi' := λ n, ((f n).has_box_integral μ I l hl).integrable,
  have hfgi : tendsto (λ n, (f n).integral (μ.restrict I)) at_top (𝓝 $ ∫ x in I, g x ∂μ),
    from tendsto_integral_approx_on_of_measurable_of_range_subset hg.measurable hgi _ subset.rfl,
  have hfg_mono : ∀ x {m n}, m ≤ n → ‖f n x - g x‖ ≤ ‖f m x - g x‖,
  { intros x m n hmn,
    rw [← dist_eq_norm, ← dist_eq_norm, dist_nndist, dist_nndist, nnreal.coe_le_coe,
      ← ennreal.coe_le_coe, ← edist_nndist, ← edist_nndist],
    exact simple_func.edist_approx_on_mono hg.measurable _ x hmn },
  /- Now consider `ε > 0`. We need to find `r` such that for any tagged partition subordinate
  to `r`, the integral sum is `(μ I + 1 + 1) * ε`-close to the Bochner integral. -/
  refine has_integral_of_mul ((μ I).to_real + 1 + 1) (λ ε ε0, _),
  lift ε to ℝ≥0 using ε0.le, rw nnreal.coe_pos at ε0, have ε0' := ennreal.coe_pos.2 ε0,
  /- Choose `N` such that the integral of `‖f N x - g x‖` is less than or equal to `ε`. -/
  obtain ⟨N₀, hN₀⟩ : ∃ N : ℕ, ∫ x in I, ‖f N x - g x‖ ∂μ ≤ ε,
  { have : tendsto (λ n, ∫⁻ x in I, ‖f n x - g x‖₊ ∂μ) at_top (𝓝 0),
      from simple_func.tendsto_approx_on_range_L1_nnnorm hg.measurable hgi,
    refine (this.eventually (ge_mem_nhds ε0')).exists.imp (λ N hN, _),
    exact integral_coe_le_of_lintegral_coe_le hN },
  /- For each `x`, we choose `Nx x ≥ N₀` such that `dist (f Nx x) (g x) ≤ ε`. -/
  have : ∀ x, ∃ N₁, N₀ ≤ N₁ ∧ dist (f N₁ x) (g x) ≤ ε,
  { intro x,
    have : tendsto (λ n, f n x) at_top (𝓝 $ g x),
      from simple_func.tendsto_approx_on hg.measurable _ (subset_closure (by simp)),
    exact ((eventually_ge_at_top N₀).and $ this $ closed_ball_mem_nhds _ ε0).exists },
  choose Nx hNx hNxε,
  /- We also choose a convergent series with `∑' i : ℕ, δ i < ε`. -/
  rcases nnreal.exists_pos_sum_of_countable ε0.ne' ℕ with ⟨δ, δ0, c, hδc, hcε⟩,
  /- Since each simple function `fᵢ` is integrable, there exists `rᵢ : ℝⁿ → (0, ∞)` such that
  the integral sum of `f` over any tagged prepartition is `δᵢ`-close to the sum of integrals
  of `fᵢ` over the boxes of this prepartition. For each `x`, we choose `r (Nx x)` as the radius
  at `x`. -/
  set r : ℝ≥0 → (ι → ℝ) → Ioi (0 : ℝ) := λ c x, (hfi' $ Nx x).convergence_r (δ $ Nx x) c x,
  refine ⟨r, λ c, l.r_cond_of_bRiemann_eq_ff hl, λ c π hπ hπp, _⟩,
  /- Now we prove the estimate in 3 "jumps": first we replace `g x` in the formula for the
  integral sum by `f (Nx x)`; then we replace each `μ J • f (Nx (π.tag J)) (π.tag J)`
  by the Bochner integral of `f (Nx (π.tag J)) x` over `J`, then we jump to the Bochner
  integral of `g`. -/
  refine (dist_triangle4 _ (∑ J in π.boxes, (μ J).to_real • f (Nx $ π.tag J) (π.tag J))
    (∑ J in π.boxes, ∫ x in J, f (Nx $ π.tag J) x ∂μ) _).trans _,
  rw [add_mul, add_mul, one_mul],
  refine add_le_add_three _ _ _,
  { /- Since each `f (Nx $ π.tag J)` is `ε`-close to `g (π.tag J)`, replacing the latter with
    the former in the formula for the integral sum changes the sum at most by `μ I * ε`. -/
    rw [← hπp.Union_eq, π.to_prepartition.measure_Union_to_real, sum_mul, integral_sum],
    refine dist_sum_sum_le_of_le _ (λ J hJ, _), dsimp,
    rw [dist_eq_norm, ← smul_sub, norm_smul, real.norm_eq_abs,
      abs_of_nonneg ennreal.to_real_nonneg],
    refine mul_le_mul_of_nonneg_left _ ennreal.to_real_nonneg,
    rw [← dist_eq_norm'], exact hNxε _ },
  { /- We group the terms of both sums by the values of `Nx (π.tag J)`.
    For each `N`, the sum of Bochner integrals over the boxes is equal
    to the sum of box integrals, and the sum of box integrals is `δᵢ`-close
    to the corresponding integral sum due to the Henstock-Sacks inequality. -/
    rw [← π.to_prepartition.sum_fiberwise (λ J, Nx (π.tag J)),
      ← π.to_prepartition.sum_fiberwise (λ J, Nx (π.tag J))],
    refine le_trans _ (nnreal.coe_lt_coe.2 hcε).le,
    refine (dist_sum_sum_le_of_le _ (λ n hn, _)).trans
      (sum_le_has_sum _ (λ n _, (δ n).2) (nnreal.has_sum_coe.2 hδc)),
    have hNxn : ∀ J ∈ π.filter (λ J, Nx (π.tag J) = n), Nx (π.tag J) = n,
      from λ J hJ, (π.mem_filter.1 hJ).2,
    have hrn : ∀ J ∈ π.filter (λ J, Nx (π.tag J) = n),
      r c (π.tag J) = (hfi' n).convergence_r (δ n) c (π.tag J),
    { intros J hJ,
      obtain rfl := hNxn J hJ,
      refl },
    have : l.mem_base_set I c ((hfi' n).convergence_r (δ n) c) (π.filter (λ J, Nx (π.tag J) = n)),
      from (hπ.filter _).mono' _ le_rfl le_rfl (λ J hJ, (hrn J hJ).le),
    convert (hfi' n).dist_integral_sum_sum_integral_le_of_mem_base_set (δ0 _) this using 2,
    { refine sum_congr rfl (λ J hJ, _),
      simp [hNxn J hJ] },
    { refine sum_congr rfl (λ J hJ, _),
      rw [← simple_func.integral_eq_integral, simple_func.box_integral_eq_integral _ _ _ _ hl,
        hNxn J hJ],
      exact (hfi _).mono_set (prepartition.le_of_mem _ hJ) } },
  { /-  For the last jump, we use the fact that the distance between `f (Nx x) x` and `g x` is less
    than or equal to the distance between `f N₀ x` and `g x` and the integral of `‖f N₀ x - g x‖`
    is less than or equal to `ε`. -/
    refine le_trans _ hN₀,
    have hfi : ∀ n (J ∈ π), integrable_on (f n) ↑J  μ,
      from λ n J hJ, (hfi n).mono_set (π.le_of_mem' J hJ),
    have hgi : ∀ J ∈ π, integrable_on g ↑J μ, from λ J hJ, hgi.mono_set (π.le_of_mem' J hJ),
    have hfgi : ∀ n (J ∈ π), integrable_on (λ x, ‖f n x - g x‖) J μ,
      from λ n J hJ, ((hfi n J hJ).sub (hgi J hJ)).norm,
    rw [← hπp.Union_eq, prepartition.Union_def',
      integral_finset_bUnion π.boxes (λ J hJ, J.measurable_set_coe) π.pairwise_disjoint hgi,
      integral_finset_bUnion π.boxes (λ J hJ, J.measurable_set_coe) π.pairwise_disjoint (hfgi _)],
    refine dist_sum_sum_le_of_le _ (λ J hJ, _),
    rw [dist_eq_norm, ← integral_sub (hfi _ J hJ) (hgi J hJ)],
    refine norm_integral_le_of_norm_le (hfgi _ J hJ) (eventually_of_forall $ λ x, _),
    exact hfg_mono x (hNx (π.tag J)) }
end

end measure_theory


import .lang
import .ualg

namespace lang

universes v u
variables (L : lang.{v}) (S : Type u)

inductive free : Type (max v u)
| of : S → free
| op {n} : L n → (fin n → free) → free

namespace free
instance : has_app L (L.free S) :=
{ app := λ _, op }

def univ : S → L.free S := of

variable {S}
def lift {B : Type*} [has_app L B] (f : S → B) : L.free S →$[L] B :=
{ to_fn := λ t, free.rec_on t f (λ n t as bs, applyo t bs),
  applyo_map' := by tauto }

theorem univ_comp_lift {B : Type*} [has_app L B] (f : S → B) : (lift L f) ∘ (univ L S) = f := rfl

theorem lift_unique {B : Type*} [has_app L B] (f : S → B) (g : L.free S →$[L] B) (hyp : g ∘ (univ L S) = f) : g = lift L f :=
begin
  apply ralg_hom.ext,
  ext,
  induction x with _ _ t as ind,
  { change (g ∘ (univ _ _)) x = _,
    rw hyp, refl },
  { change g (applyo t as) = (lift L f) (applyo t as),
    simp_rw ←ralg_hom.applyo_map,
    apply congr_arg,
    ext,
    apply ind }
end

end free

end lang


lemma mul_add (t a b : mynat) : t * (a + b) = t * a + t * b :=
begin
induction b with k Pk,
rw mul_zero,
repeat { rw add_zero },
rw mul_succ,
rw ← add_assoc,
rw ← Pk,
rw ← mul_succ,
rw add_succ,
refl,
end


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro, Patrick Massot
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.order.filter.lift
import Mathlib.topology.separation
import Mathlib.PostPort

universes u_1 u u_2 l u_3 u_4 u_6

namespace Mathlib

/-!
# Uniform spaces

Uniform spaces are a generalization of metric spaces and topological groups. Many concepts directly
generalize to uniform spaces, e.g.

* uniform continuity (in this file)
* completeness (in `cauchy.lean`)
* extension of uniform continuous functions to complete spaces (in `uniform_embedding.lean`)
* totally bounded sets (in `cauchy.lean`)
* totally bounded complete sets are compact (in `cauchy.lean`)

A uniform structure on a type `X` is a filter `𝓤 X` on `X × X` satisfying some conditions
which makes it reasonable to say that `∀ᶠ (p : X × X) in 𝓤 X, ...` means
"for all p.1 and p.2 in X close enough, ...". Elements of this filter are called entourages
of `X`. The two main examples are:

* If `X` is a metric space, `V ∈ 𝓤 X ↔ ∃ ε > 0, { p | dist p.1 p.2 < ε } ⊆ V`
* If `G` is an additive topological group, `V ∈ 𝓤 G ↔ ∃ U ∈ 𝓝 (0 : G), {p | p.2 - p.1 ∈ U} ⊆ V`

Those examples are generalizations in two different directions of the elementary example where
`X = ℝ` and `V ∈ 𝓤 ℝ ↔ ∃ ε > 0, { p | |p.2 - p.1| < ε } ⊆ V` which features both the topological
group structure on `ℝ` and its metric space structure.

Each uniform structure on `X` induces a topology on `X` characterized by

> `nhds_eq_comap_uniformity : ∀ {x : X}, 𝓝 x = comap (prod.mk x) (𝓤 X)`

where `prod.mk x : X → X × X := (λ y, (x, y))` is the partial evaluation of the product
constructor.

The dictionary with metric spaces includes:
* an upper bound for `dist x y` translates into `(x, y) ∈ V` for some `V ∈ 𝓤 X`
* a ball `ball x r` roughly corresponds to `uniform_space.ball x V := {y | (x, y) ∈ V}`
  for some `V ∈ 𝓤 X`, but the later is more general (it includes in
  particular both open and closed balls for suitable `V`).
  In particular we have:
  `is_open_iff_ball_subset {s : set X} : is_open s ↔ ∀ x ∈ s, ∃ V ∈ 𝓤 X, ball x V ⊆ s`

The triangle inequality is abstracted to a statement involving the composition of relations in `X`.
First note that the triangle inequality in a metric space is equivalent to
`∀ (x y z : X) (r r' : ℝ), dist x y ≤ r → dist y z ≤ r' → dist x z ≤ r + r'`.
Then, for any `V` and `W` with type `set (X × X)`, the composition `V ○ W : set (X × X)` is
defined as `{ p : X × X | ∃ z, (p.1, z) ∈ V ∧ (z, p.2) ∈ W }`.
In the metric space case, if `V = { p | dist p.1 p.2 ≤ r }` and `W = { p | dist p.1 p.2 ≤ r' }`
then the triangle inequality, as reformulated above, says `V ○ W` is contained in
`{p | dist p.1 p.2 ≤ r + r'}` which is the entourage associated to the radius `r + r'`.
In general we have `mem_ball_comp (h : y ∈ ball x V) (h' : z ∈ ball y W) : z ∈ ball x (V ○ W)`.
Note that this discussion does not depend on any axiom imposed on the uniformity filter,
it is simply captured by the definition of composition.

The uniform space axioms ask the filter `𝓤 X` to satisfy the following:
* every `V ∈ 𝓤 X` contains the diagonal `id_rel = { p | p.1 = p.2 }`. This abstracts the fact
  that `dist x x ≤ r` for every non-negative radius `r` in the metric space case and also that
  `x - x` belongs to every neighborhood of zero in the topological group case.
* `V ∈ 𝓤 X → prod.swap '' V ∈ 𝓤 X`. This is tightly related the fact that `dist x y = dist y x`
  in a metric space, and to continuity of negation in the topological group case.
* `∀ V ∈ 𝓤 X, ∃ W ∈ 𝓤 X, W ○ W ⊆ V`. In the metric space case, it corresponds
  to cutting the radius of a ball in half and applying the triangle inequality.
  In the topological group case, it comes from continuity of addition at `(0, 0)`.

These three axioms are stated more abstractly in the definition below, in terms of
operations on filters, without directly manipulating entourages.

## Main definitions

* `uniform_space X` is a uniform space structure on a type `X`
* `uniform_continuous f` is a predicate saying a function `f : α → β` between uniform spaces
  is uniformly continuous : `∀ r ∈ 𝓤 β, ∀ᶠ (x : α × α) in 𝓤 α, (f x.1, f x.2) ∈ r`

In this file we also define a complete lattice structure on the type `uniform_space X`
of uniform structures on `X`, as well as the pullback (`uniform_space.comap`) of uniform structures
coming from the pullback of filters.
Like distance functions, uniform structures cannot be pushed forward in general.

## Notations

Localized in `uniformity`, we have the notation `𝓤 X` for the uniformity on a uniform space `X`,
and `○` for composition of relations, seen as terms with type `set (X × X)`.

## Implementation notes

There is already a theory of relations in `data/rel.lean` where the main definition is
`def rel (α β : Type*) := α → β → Prop`.
The relations used in the current file involve only one type, but this is not the reason why
we don't reuse `data/rel.lean`. We use `set (α × α)`
instead of `rel α α` because we really need sets to use the filter library, and elements
of filters on `α × α` have type `set (α × α)`.

The structure `uniform_space X` bundles a uniform structure on `X`, a topology on `X` and
an assumption saying those are compatible. This may not seem mathematically reasonable at first,
but is in fact an instance of the forgetful inheritance pattern. See Note [forgetful inheritance]
below.

## References

The formalization uses the books:

* [N. Bourbaki, *General Topology*][bourbaki1966]
* [I. M. James, *Topologies and Uniformities*][james1999]

But it makes a more systematic use of the filter library.
-/

/-!
### Relations, seen as `set (α × α)`
-/

/-- The identity relation, or the graph of the identity function -/
def id_rel {α : Type u_1} : set (α × α) := set_of fun (p : α × α) => prod.fst p = prod.snd p

@[simp] theorem mem_id_rel {α : Type u_1} {a : α} {b : α} : (a, b) ∈ id_rel ↔ a = b := iff.rfl

@[simp] theorem id_rel_subset {α : Type u_1} {s : set (α × α)} :
    id_rel ⊆ s ↔ ∀ (a : α), (a, a) ∈ s :=
  sorry

/-- The composition of relations -/
def comp_rel {α : Type u} (r₁ : set (α × α)) (r₂ : set (α × α)) : set (α × α) :=
  set_of fun (p : α × α) => ∃ (z : α), (prod.fst p, z) ∈ r₁ ∧ (z, prod.snd p) ∈ r₂

@[simp] theorem mem_comp_rel {α : Type u_1} {r₁ : set (α × α)} {r₂ : set (α × α)} {x : α} {y : α} :
    (x, y) ∈ comp_rel r₁ r₂ ↔ ∃ (z : α), (x, z) ∈ r₁ ∧ (z, y) ∈ r₂ :=
  iff.rfl

@[simp] theorem swap_id_rel {α : Type u_1} : prod.swap '' id_rel = id_rel := sorry

theorem monotone_comp_rel {α : Type u_1} {β : Type u_2} [preorder β] {f : β → set (α × α)}
    {g : β → set (α × α)} (hf : monotone f) (hg : monotone g) :
    monotone fun (x : β) => comp_rel (f x) (g x) :=
  sorry

theorem comp_rel_mono {α : Type u_1} {f : set (α × α)} {g : set (α × α)} {h : set (α × α)}
    {k : set (α × α)} (h₁ : f ⊆ h) (h₂ : g ⊆ k) : comp_rel f g ⊆ comp_rel h k :=
  sorry

theorem prod_mk_mem_comp_rel {α : Type u_1} {a : α} {b : α} {c : α} {s : set (α × α)}
    {t : set (α × α)} (h₁ : (a, c) ∈ s) (h₂ : (c, b) ∈ t) : (a, b) ∈ comp_rel s t :=
  Exists.intro c { left := h₁, right := h₂ }

@[simp] theorem id_comp_rel {α : Type u_1} {r : set (α × α)} : comp_rel id_rel r = r := sorry

theorem comp_rel_assoc {α : Type u_1} {r : set (α × α)} {s : set (α × α)} {t : set (α × α)} :
    comp_rel (comp_rel r s) t = comp_rel r (comp_rel s t) :=
  sorry

theorem subset_comp_self {α : Type u_1} {s : set (α × α)} (h : id_rel ⊆ s) : s ⊆ comp_rel s s :=
  sorry

/-- The relation is invariant under swapping factors. -/
def symmetric_rel {α : Type u_1} (V : set (α × α)) := prod.swap ⁻¹' V = V

/-- The maximal symmetric relation contained in a given relation. -/
def symmetrize_rel {α : Type u_1} (V : set (α × α)) : set (α × α) := V ∩ prod.swap ⁻¹' V

theorem symmetric_symmetrize_rel {α : Type u_1} (V : set (α × α)) :
    symmetric_rel (symmetrize_rel V) :=
  sorry

theorem symmetrize_rel_subset_self {α : Type u_1} (V : set (α × α)) : symmetrize_rel V ⊆ V :=
  set.sep_subset V fun (a : α × α) => a ∈ prod.swap ⁻¹' V

theorem symmetrize_mono {α : Type u_1} {V : set (α × α)} {W : set (α × α)} (h : V ⊆ W) :
    symmetrize_rel V ⊆ symmetrize_rel W :=
  set.inter_subset_inter h (set.preimage_mono h)

theorem symmetric_rel_inter {α : Type u_1} {U : set (α × α)} {V : set (α × α)}
    (hU : symmetric_rel U) (hV : symmetric_rel V) : symmetric_rel (U ∩ V) :=
  sorry

/-- This core description of a uniform space is outside of the type class hierarchy. It is useful
  for constructions of uniform spaces, when the topology is derived from the uniform space. -/
structure uniform_space.core (α : Type u) where
  uniformity : filter (α × α)
  refl : filter.principal id_rel ≤ uniformity
  symm : filter.tendsto prod.swap uniformity uniformity
  comp : (filter.lift' uniformity fun (s : set (α × α)) => comp_rel s s) ≤ uniformity

/-- An alternative constructor for `uniform_space.core`. This version unfolds various
`filter`-related definitions. -/
def uniform_space.core.mk' {α : Type u} (U : filter (α × α))
    (refl : ∀ (r : set (α × α)), r ∈ U → ∀ (x : α), (x, x) ∈ r)
    (symm : ∀ (r : set (α × α)), r ∈ U → prod.swap ⁻¹' r ∈ U)
    (comp : ∀ (r : set (α × α)) (H : r ∈ U), ∃ (t : set (α × α)), ∃ (H : t ∈ U), comp_rel t t ⊆ r) :
    uniform_space.core α :=
  uniform_space.core.mk U sorry symm sorry

/-- A uniform space generates a topological space -/
def uniform_space.core.to_topological_space {α : Type u} (u : uniform_space.core α) :
    topological_space α :=
  topological_space.mk
    (fun (s : set α) =>
      ∀ (x : α),
        x ∈ s →
          (set_of fun (p : α × α) => prod.fst p = x → prod.snd p ∈ s) ∈
            uniform_space.core.uniformity u)
    sorry sorry sorry

theorem uniform_space.core_eq {α : Type u_1} {u₁ : uniform_space.core α}
    {u₂ : uniform_space.core α} :
    uniform_space.core.uniformity u₁ = uniform_space.core.uniformity u₂ → u₁ = u₂ :=
  sorry

/-- Suppose that one can put two mathematical structures on a type, a rich one `R` and a poor one
`P`, and that one can deduce the poor structure from the rich structure through a map `F` (called a
forgetful functor) (think `R = metric_space` and `P = topological_space`). A possible
implementation would be to have a type class `rich` containing a field `R`, a type class `poor`
containing a field `P`, and an instance from `rich` to `poor`. However, this creates diamond
problems, and a better approach is to let `rich` extend `poor` and have a field saying that
`F R = P`.

To illustrate this, consider the pair `metric_space` / `topological_space`. Consider the topology
on a product of two metric spaces. With the first approach, it could be obtained by going first from
each metric space to its topology, and then taking the product topology. But it could also be
obtained by considering the product metric space (with its sup distance) and then the topology
coming from this distance. These would be the same topology, but not definitionally, which means
that from the point of view of Lean's kernel, there would be two different `topological_space`
instances on the product. This is not compatible with the way instances are designed and used:
there should be at most one instance of a kind on each type. This approach has created an instance
diamond that does not commute definitionally.

The second approach solves this issue. Now, a metric space contains both a distance, a topology, and
a proof that the topology coincides with the one coming from the distance. When one defines the
product of two metric spaces, one uses the sup distance and the product topology, and one has to
give the proof that the sup distance induces the product topology. Following both sides of the
instance diamond then gives rise (definitionally) to the product topology on the product space.

Another approach would be to have the rich type class take the poor type class as an instance
parameter. It would solve the diamond problem, but it would lead to a blow up of the number
of type classes one would need to declare to work with complicated classes, say a real inner
product space, and would create exponential complexity when working with products of
such complicated spaces, that are avoided by bundling things carefully as above.

Note that this description of this specific case of the product of metric spaces is oversimplified
compared to mathlib, as there is an intermediate typeclass between `metric_space` and
`topological_space` called `uniform_space`. The above scheme is used at both levels, embedding a
topology in the uniform space structure, and a uniform structure in the metric space structure.

Note also that, when `P` is a proposition, there is no such issue as any two proofs of `P` are
definitionally equivalent in Lean.

To avoid boilerplate, there are some designs that can automatically fill the poor fields when
creating a rich structure if one doesn't want to do something special about them. For instance,
in the definition of metric spaces, default tactics fill the uniform space fields if they are
not given explicitly. One can also have a helper function creating the rich structure from a
structure with less fields, where the helper function fills the remaining fields. See for instance
`uniform_space.of_core` or `real_inner_product.of_core`.

For more details on this question, called the forgetful inheritance pattern, see [Competing
inheritance paths in dependent type theory: a case study in functional
analysis](https://hal.inria.fr/hal-02463336).
-/
/-- A uniform space is a generalization of the "uniform" topological aspects of a
  metric space. It consists of a filter on `α × α` called the "uniformity", which
  satisfies properties analogous to the reflexivity, symmetry, and triangle properties
  of a metric.

  A metric space has a natural uniformity, and a uniform space has a natural topology.
  A topological group also has a natural uniformity, even when it is not metrizable. -/
class uniform_space (α : Type u) extends uniform_space.core α, topological_space α where
  is_open_uniformity :
    ∀ (s : set α),
      topological_space.is_open _to_topological_space s ↔
        ∀ (x : α),
          x ∈ s →
            (set_of fun (p : α × α) => prod.fst p = x → prod.snd p ∈ s) ∈
              uniform_space.core.uniformity _to_core

/-- Alternative constructor for `uniform_space α` when a topology is already given. -/
def uniform_space.mk' {α : Type u_1} (t : topological_space α) (c : uniform_space.core α)
    (is_open_uniformity :
      ∀ (s : set α),
        topological_space.is_open t s ↔
          ∀ (x : α),
            x ∈ s →
              (set_of fun (p : α × α) => prod.fst p = x → prod.snd p ∈ s) ∈
                uniform_space.core.uniformity c) :
    uniform_space α :=
  uniform_space.mk c is_open_uniformity

/-- Construct a `uniform_space` from a `uniform_space.core`. -/
def uniform_space.of_core {α : Type u} (u : uniform_space.core α) : uniform_space α :=
  uniform_space.mk u sorry

/-- Construct a `uniform_space` from a `u : uniform_space.core` and a `topological_space` structure
that is equal to `u.to_topological_space`. -/
def uniform_space.of_core_eq {α : Type u} (u : uniform_space.core α) (t : topological_space α)
    (h : t = uniform_space.core.to_topological_space u) : uniform_space α :=
  uniform_space.mk u sorry

theorem uniform_space.to_core_to_topological_space {α : Type u_1} (u : uniform_space α) :
    uniform_space.core.to_topological_space uniform_space.to_core =
        uniform_space.to_topological_space :=
  sorry

theorem uniform_space_eq {α : Type u_1} {u₁ : uniform_space α} {u₂ : uniform_space α} :
    uniform_space.core.uniformity uniform_space.to_core =
          uniform_space.core.uniformity uniform_space.to_core →
        u₁ = u₂ :=
  sorry

theorem uniform_space.of_core_eq_to_core {α : Type u_1} (u : uniform_space α)
    (t : topological_space α)
    (h : t = uniform_space.core.to_topological_space uniform_space.to_core) :
    uniform_space.of_core_eq uniform_space.to_core t h = u :=
  uniform_space_eq rfl

/-- The uniformity is a filter on α × α (inferred from an ambient uniform space
  structure on α). -/
def uniformity (α : Type u) [uniform_space α] : filter (α × α) :=
  uniform_space.core.uniformity uniform_space.to_core

theorem is_open_uniformity {α : Type u_1} [uniform_space α] {s : set α} :
    is_open s ↔
        ∀ (x : α),
          x ∈ s → (set_of fun (p : α × α) => prod.fst p = x → prod.snd p ∈ s) ∈ uniformity α :=
  uniform_space.is_open_uniformity s

theorem refl_le_uniformity {α : Type u_1} [uniform_space α] :
    filter.principal id_rel ≤ uniformity α :=
  uniform_space.core.refl uniform_space.to_core

theorem refl_mem_uniformity {α : Type u_1} [uniform_space α] {x : α} {s : set (α × α)}
    (h : s ∈ uniformity α) : (x, x) ∈ s :=
  refl_le_uniformity h rfl

theorem symm_le_uniformity {α : Type u_1} [uniform_space α] :
    filter.map prod.swap (uniformity α) ≤ uniformity α :=
  uniform_space.core.symm uniform_space.to_core

theorem comp_le_uniformity {α : Type u_1} [uniform_space α] :
    (filter.lift' (uniformity α) fun (s : set (α × α)) => comp_rel s s) ≤ uniformity α :=
  uniform_space.core.comp uniform_space.to_core

theorem tendsto_swap_uniformity {α : Type u_1} [uniform_space α] :
    filter.tendsto prod.swap (uniformity α) (uniformity α) :=
  symm_le_uniformity

theorem comp_mem_uniformity_sets {α : Type u_1} [uniform_space α] {s : set (α × α)}
    (hs : s ∈ uniformity α) : ∃ (t : set (α × α)), ∃ (H : t ∈ uniformity α), comp_rel t t ⊆ s :=
  (fun (this : s ∈ filter.lift' (uniformity α) fun (t : set (α × α)) => comp_rel t t) =>
      iff.mp (filter.mem_lift'_sets (monotone_comp_rel monotone_id monotone_id)) this)
    (comp_le_uniformity hs)

/-- Relation `λ f g, tendsto (λ x, (f x, g x)) l (𝓤 α)` is transitive. -/
theorem filter.tendsto.uniformity_trans {α : Type u_1} {β : Type u_2} [uniform_space α]
    {l : filter β} {f₁ : β → α} {f₂ : β → α} {f₃ : β → α}
    (h₁₂ : filter.tendsto (fun (x : β) => (f₁ x, f₂ x)) l (uniformity α))
    (h₂₃ : filter.tendsto (fun (x : β) => (f₂ x, f₃ x)) l (uniformity α)) :
    filter.tendsto (fun (x : β) => (f₁ x, f₃ x)) l (uniformity α) :=
  sorry

/-- Relation `λ f g, tendsto (λ x, (f x, g x)) l (𝓤 α)` is symmetric -/
theorem filter.tendsto.uniformity_symm {α : Type u_1} {β : Type u_2} [uniform_space α]
    {l : filter β} {f : β → α × α} (h : filter.tendsto f l (uniformity α)) :
    filter.tendsto (fun (x : β) => (prod.snd (f x), prod.fst (f x))) l (uniformity α) :=
  filter.tendsto.comp tendsto_swap_uniformity h

/-- Relation `λ f g, tendsto (λ x, (f x, g x)) l (𝓤 α)` is reflexive. -/
theorem tendsto_diag_uniformity {α : Type u_1} {β : Type u_2} [uniform_space α] (f : β → α)
    (l : filter β) : filter.tendsto (fun (x : β) => (f x, f x)) l (uniformity α) :=
  fun (s : set (α × α)) (hs : s ∈ uniformity α) =>
    iff.mpr filter.mem_map (filter.univ_mem_sets' fun (x : β) => refl_mem_uniformity hs)

theorem tendsto_const_uniformity {α : Type u_1} {β : Type u_2} [uniform_space α] {a : α}
    {f : filter β} : filter.tendsto (fun (_x : β) => (a, a)) f (uniformity α) :=
  tendsto_diag_uniformity (fun (_x : β) => a) f

theorem symm_of_uniformity {α : Type u_1} [uniform_space α] {s : set (α × α)}
    (hs : s ∈ uniformity α) :
    ∃ (t : set (α × α)), ∃ (H : t ∈ uniformity α), (∀ (a b : α), (a, b) ∈ t → (b, a) ∈ t) ∧ t ⊆ s :=
  sorry

theorem comp_symm_of_uniformity {α : Type u_1} [uniform_space α] {s : set (α × α)}
    (hs : s ∈ uniformity α) :
    ∃ (t : set (α × α)),
        ∃ (H : t ∈ uniformity α), (∀ {a b : α}, (a, b) ∈ t → (b, a) ∈ t) ∧ comp_rel t t ⊆ s :=
  sorry

theorem uniformity_le_symm {α : Type u_1} [uniform_space α] :
    uniformity α ≤ prod.swap <$> uniformity α :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (uniformity α ≤ prod.swap <$> uniformity α))
        filter.map_swap_eq_comap_swap))
    (iff.mp filter.map_le_iff_le_comap tendsto_swap_uniformity)

theorem uniformity_eq_symm {α : Type u_1} [uniform_space α] :
    uniformity α = prod.swap <$> uniformity α :=
  le_antisymm uniformity_le_symm symm_le_uniformity

theorem symmetrize_mem_uniformity {α : Type u_1} [uniform_space α] {V : set (α × α)}
    (h : V ∈ uniformity α) : symmetrize_rel V ∈ uniformity α :=
  sorry

theorem uniformity_lift_le_swap {α : Type u_1} {β : Type u_2} [uniform_space α]
    {g : set (α × α) → filter β} {f : filter β} (hg : monotone g)
    (h : (filter.lift (uniformity α) fun (s : set (α × α)) => g (prod.swap ⁻¹' s)) ≤ f) :
    filter.lift (uniformity α) g ≤ f :=
  sorry

theorem uniformity_lift_le_comp {α : Type u_1} {β : Type u_2} [uniform_space α]
    {f : set (α × α) → filter β} (h : monotone f) :
    (filter.lift (uniformity α) fun (s : set (α × α)) => f (comp_rel s s)) ≤
        filter.lift (uniformity α) f :=
  sorry

theorem comp_le_uniformity3 {α : Type u_1} [uniform_space α] :
    (filter.lift' (uniformity α) fun (s : set (α × α)) => comp_rel s (comp_rel s s)) ≤
        uniformity α :=
  sorry

theorem comp_symm_mem_uniformity_sets {α : Type u_1} [uniform_space α] {s : set (α × α)}
    (hs : s ∈ uniformity α) :
    ∃ (t : set (α × α)), ∃ (H : t ∈ uniformity α), symmetric_rel t ∧ comp_rel t t ⊆ s :=
  sorry

theorem subset_comp_self_of_mem_uniformity {α : Type u_1} [uniform_space α] {s : set (α × α)}
    (h : s ∈ uniformity α) : s ⊆ comp_rel s s :=
  subset_comp_self (refl_le_uniformity h)

theorem comp_comp_symm_mem_uniformity_sets {α : Type u_1} [uniform_space α] {s : set (α × α)}
    (hs : s ∈ uniformity α) :
    ∃ (t : set (α × α)),
        ∃ (H : t ∈ uniformity α), symmetric_rel t ∧ comp_rel (comp_rel t t) t ⊆ s :=
  sorry

/-!
### Balls in uniform spaces
-/

/-- The ball around `(x : β)` with respect to `(V : set (β × β))`. Intended to be
used for `V ∈ 𝓤 β`, but this is not needed for the definition. Recovers the
notions of metric space ball when `V = {p | dist p.1 p.2 < r }`.  -/
def uniform_space.ball {β : Type u_2} (x : β) (V : set (β × β)) : set β := Prod.mk x ⁻¹' V

theorem uniform_space.mem_ball_self {α : Type u_1} [uniform_space α] (x : α) {V : set (α × α)}
    (hV : V ∈ uniformity α) : x ∈ uniform_space.ball x V :=
  refl_mem_uniformity hV

/-- The triangle inequality for `uniform_space.ball` -/
theorem mem_ball_comp {β : Type u_2} {V : set (β × β)} {W : set (β × β)} {x : β} {y : β} {z : β}
    (h : y ∈ uniform_space.ball x V) (h' : z ∈ uniform_space.ball y W) :
    z ∈ uniform_space.ball x (comp_rel V W) :=
  prod_mk_mem_comp_rel h h'

theorem ball_subset_of_comp_subset {β : Type u_2} {V : set (β × β)} {W : set (β × β)} {x : β}
    {y : β} (h : x ∈ uniform_space.ball y W) (h' : comp_rel W W ⊆ V) :
    uniform_space.ball x W ⊆ uniform_space.ball y V :=
  fun (z : β) (z_in : z ∈ uniform_space.ball x W) => h' (mem_ball_comp h z_in)

theorem ball_mono {β : Type u_2} {V : set (β × β)} {W : set (β × β)} (h : V ⊆ W) (x : β) :
    uniform_space.ball x V ⊆ uniform_space.ball x W :=
  id fun (a : β) (ᾰ : a ∈ uniform_space.ball x V) => h ᾰ

theorem mem_ball_symmetry {β : Type u_2} {V : set (β × β)} (hV : symmetric_rel V) {x : β} {y : β} :
    x ∈ uniform_space.ball y V ↔ y ∈ uniform_space.ball x V :=
  sorry

theorem ball_eq_of_symmetry {β : Type u_2} {V : set (β × β)} (hV : symmetric_rel V) {x : β} :
    uniform_space.ball x V = set_of fun (y : β) => (y, x) ∈ V :=
  sorry

theorem mem_comp_of_mem_ball {β : Type u_2} {V : set (β × β)} {W : set (β × β)} {x : β} {y : β}
    {z : β} (hV : symmetric_rel V) (hx : x ∈ uniform_space.ball z V)
    (hy : y ∈ uniform_space.ball z W) : (x, y) ∈ comp_rel V W :=
  Exists.intro z
    { left :=
        eq.mp (Eq._oldrec (Eq.refl (x ∈ uniform_space.ball z V)) (propext (mem_ball_symmetry hV)))
          hx,
      right := hy }

theorem uniform_space.is_open_ball {α : Type u_1} [uniform_space α] (x : α) {V : set (α × α)}
    (hV : is_open V) : is_open (uniform_space.ball x V) :=
  is_open.preimage (continuous.prod_mk continuous_const continuous_id) hV

theorem mem_comp_comp {β : Type u_2} {V : set (β × β)} {W : set (β × β)} {M : set (β × β)}
    (hW' : symmetric_rel W) {p : β × β} :
    p ∈ comp_rel (comp_rel V M) W ↔
        set.nonempty
          (set.prod (uniform_space.ball (prod.fst p) V) (uniform_space.ball (prod.snd p) W) ∩ M) :=
  sorry

/-!
### Neighborhoods in uniform spaces
-/

theorem mem_nhds_uniformity_iff_right {α : Type u_1} [uniform_space α] {x : α} {s : set α} :
    s ∈ nhds x ↔ (set_of fun (p : α × α) => prod.fst p = x → prod.snd p ∈ s) ∈ uniformity α :=
  sorry

theorem mem_nhds_uniformity_iff_left {α : Type u_1} [uniform_space α] {x : α} {s : set α} :
    s ∈ nhds x ↔ (set_of fun (p : α × α) => prod.snd p = x → prod.fst p ∈ s) ∈ uniformity α :=
  sorry

theorem nhds_eq_comap_uniformity_aux {α : Type u} {x : α} {s : set α} {F : filter (α × α)} :
    (set_of fun (p : α × α) => prod.fst p = x → prod.snd p ∈ s) ∈ F ↔
        s ∈ filter.comap (Prod.mk x) F :=
  sorry

theorem nhds_eq_comap_uniformity {α : Type u_1} [uniform_space α] {x : α} :
    nhds x = filter.comap (Prod.mk x) (uniformity α) :=
  sorry

theorem is_open_iff_ball_subset {α : Type u_1} [uniform_space α] {s : set α} :
    is_open s ↔
        ∀ (x : α) (H : x ∈ s),
          ∃ (V : set (α × α)), ∃ (H : V ∈ uniformity α), uniform_space.ball x V ⊆ s :=
  sorry

theorem nhds_basis_uniformity' {α : Type u_1} {β : Type u_2} [uniform_space α] {p : β → Prop}
    {s : β → set (α × α)} (h : filter.has_basis (uniformity α) p s) {x : α} :
    filter.has_basis (nhds x) p fun (i : β) => uniform_space.ball x (s i) :=
  sorry

theorem nhds_basis_uniformity {α : Type u_1} {β : Type u_2} [uniform_space α] {p : β → Prop}
    {s : β → set (α × α)} (h : filter.has_basis (uniformity α) p s) {x : α} :
    filter.has_basis (nhds x) p fun (i : β) => set_of fun (y : α) => (y, x) ∈ s i :=
  sorry

theorem uniform_space.mem_nhds_iff {α : Type u_1} [uniform_space α] {x : α} {s : set α} :
    s ∈ nhds x ↔ ∃ (V : set (α × α)), ∃ (H : V ∈ uniformity α), uniform_space.ball x V ⊆ s :=
  sorry

theorem uniform_space.ball_mem_nhds {α : Type u_1} [uniform_space α] (x : α) {V : set (α × α)}
    (V_in : V ∈ uniformity α) : uniform_space.ball x V ∈ nhds x :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (uniform_space.ball x V ∈ nhds x)) (propext uniform_space.mem_nhds_iff)))
    (Exists.intro V (Exists.intro V_in (set.subset.refl (uniform_space.ball x V))))

theorem uniform_space.mem_nhds_iff_symm {α : Type u_1} [uniform_space α] {x : α} {s : set α} :
    s ∈ nhds x ↔
        ∃ (V : set (α × α)),
          ∃ (H : V ∈ uniformity α), symmetric_rel V ∧ uniform_space.ball x V ⊆ s :=
  sorry

theorem uniform_space.has_basis_nhds {α : Type u_1} [uniform_space α] (x : α) :
    filter.has_basis (nhds x) (fun (s : set (α × α)) => s ∈ uniformity α ∧ symmetric_rel s)
        fun (s : set (α × α)) => uniform_space.ball x s :=
  sorry

theorem uniform_space.has_basis_nhds_prod {α : Type u_1} [uniform_space α] (x : α) (y : α) :
    filter.has_basis (nhds (x, y)) (fun (s : set (α × α)) => s ∈ uniformity α ∧ symmetric_rel s)
        fun (s : set (α × α)) => set.prod (uniform_space.ball x s) (uniform_space.ball y s) :=
  sorry

theorem nhds_eq_uniformity {α : Type u_1} [uniform_space α] {x : α} :
    nhds x = filter.lift' (uniformity α) (uniform_space.ball x) :=
  filter.has_basis.eq_binfi (nhds_basis_uniformity' (filter.basis_sets (uniformity α)))

theorem mem_nhds_left {α : Type u_1} [uniform_space α] (x : α) {s : set (α × α)}
    (h : s ∈ uniformity α) : (set_of fun (y : α) => (x, y) ∈ s) ∈ nhds x :=
  uniform_space.ball_mem_nhds x h

theorem mem_nhds_right {α : Type u_1} [uniform_space α] (y : α) {s : set (α × α)}
    (h : s ∈ uniformity α) : (set_of fun (x : α) => (x, y) ∈ s) ∈ nhds y :=
  mem_nhds_left y (symm_le_uniformity h)

theorem tendsto_right_nhds_uniformity {α : Type u_1} [uniform_space α] {a : α} :
    filter.tendsto (fun (a' : α) => (a', a)) (nhds a) (uniformity α) :=
  fun (s : set (α × α)) => mem_nhds_right a

theorem tendsto_left_nhds_uniformity {α : Type u_1} [uniform_space α] {a : α} :
    filter.tendsto (fun (a' : α) => (a, a')) (nhds a) (uniformity α) :=
  fun (s : set (α × α)) => mem_nhds_left a

theorem lift_nhds_left {α : Type u_1} {β : Type u_2} [uniform_space α] {x : α}
    {g : set α → filter β} (hg : monotone g) :
    filter.lift (nhds x) g =
        filter.lift (uniformity α) fun (s : set (α × α)) => g (set_of fun (y : α) => (x, y) ∈ s) :=
  sorry

theorem lift_nhds_right {α : Type u_1} {β : Type u_2} [uniform_space α] {x : α}
    {g : set α → filter β} (hg : monotone g) :
    filter.lift (nhds x) g =
        filter.lift (uniformity α) fun (s : set (α × α)) => g (set_of fun (y : α) => (y, x) ∈ s) :=
  sorry

theorem nhds_nhds_eq_uniformity_uniformity_prod {α : Type u_1} [uniform_space α] {a : α} {b : α} :
    filter.prod (nhds a) (nhds b) =
        filter.lift (uniformity α)
          fun (s : set (α × α)) =>
            filter.lift' (uniformity α)
              fun (t : set (α × α)) =>
                set.prod (set_of fun (y : α) => (y, a) ∈ s) (set_of fun (y : α) => (b, y) ∈ t) :=
  sorry

theorem nhds_eq_uniformity_prod {α : Type u_1} [uniform_space α] {a : α} {b : α} :
    nhds (a, b) =
        filter.lift' (uniformity α)
          fun (s : set (α × α)) =>
            set.prod (set_of fun (y : α) => (y, a) ∈ s) (set_of fun (y : α) => (b, y) ∈ s) :=
  sorry

theorem nhdset_of_mem_uniformity {α : Type u_1} [uniform_space α] {d : set (α × α)}
    (s : set (α × α)) (hd : d ∈ uniformity α) :
    ∃ (t : set (α × α)),
        is_open t ∧
          s ⊆ t ∧
            t ⊆
              set_of
                fun (p : α × α) =>
                  ∃ (x : α), ∃ (y : α), (prod.fst p, x) ∈ d ∧ (x, y) ∈ s ∧ (y, prod.snd p) ∈ d :=
  sorry

/-- Entourages are neighborhoods of the diagonal. -/
theorem nhds_le_uniformity {α : Type u_1} [uniform_space α] (x : α) : nhds (x, x) ≤ uniformity α :=
  sorry

/-- Entourages are neighborhoods of the diagonal. -/
theorem supr_nhds_le_uniformity {α : Type u_1} [uniform_space α] :
    (supr fun (x : α) => nhds (x, x)) ≤ uniformity α :=
  supr_le nhds_le_uniformity

/-!
### Closure and interior in uniform spaces
-/

theorem closure_eq_uniformity {α : Type u_1} [uniform_space α] (s : set (α × α)) :
    closure s =
        set.Inter
          fun (V : set (α × α)) =>
            set.Inter
              fun (H : V ∈ set_of fun (V : set (α × α)) => V ∈ uniformity α ∧ symmetric_rel V) =>
                comp_rel (comp_rel V s) V :=
  sorry

theorem uniformity_has_basis_closed {α : Type u_1} [uniform_space α] :
    filter.has_basis (uniformity α) (fun (V : set (α × α)) => V ∈ uniformity α ∧ is_closed V) id :=
  sorry

/-- Closed entourages form a basis of the uniformity filter. -/
theorem uniformity_has_basis_closure {α : Type u_1} [uniform_space α] :
    filter.has_basis (uniformity α) (fun (V : set (α × α)) => V ∈ uniformity α) closure :=
  sorry

theorem closure_eq_inter_uniformity {α : Type u_1} [uniform_space α] {t : set (α × α)} :
    closure t =
        set.Inter
          fun (d : set (α × α)) =>
            set.Inter fun (H : d ∈ uniformity α) => comp_rel d (comp_rel t d) :=
  sorry

theorem uniformity_eq_uniformity_closure {α : Type u_1} [uniform_space α] :
    uniformity α = filter.lift' (uniformity α) closure :=
  sorry

theorem uniformity_eq_uniformity_interior {α : Type u_1} [uniform_space α] :
    uniformity α = filter.lift' (uniformity α) interior :=
  sorry

theorem interior_mem_uniformity {α : Type u_1} [uniform_space α] {s : set (α × α)}
    (hs : s ∈ uniformity α) : interior s ∈ uniformity α :=
  eq.mpr (id (Eq._oldrec (Eq.refl (interior s ∈ uniformity α)) uniformity_eq_uniformity_interior))
    (filter.mem_lift' hs)

theorem mem_uniformity_is_closed {α : Type u_1} [uniform_space α] {s : set (α × α)}
    (h : s ∈ uniformity α) : ∃ (t : set (α × α)), ∃ (H : t ∈ uniformity α), is_closed t ∧ t ⊆ s :=
  sorry

/-- The uniform neighborhoods of all points of a dense set cover the whole space. -/
theorem dense.bUnion_uniformity_ball {α : Type u_1} [uniform_space α] {s : set α} {U : set (α × α)}
    (hs : dense s) (hU : U ∈ uniformity α) :
    (set.Union fun (x : α) => set.Union fun (H : x ∈ s) => uniform_space.ball x U) = set.univ :=
  sorry

/-!
### Uniformity bases
-/

/-- Open elements of `𝓤 α` form a basis of `𝓤 α`. -/
theorem uniformity_has_basis_open {α : Type u_1} [uniform_space α] :
    filter.has_basis (uniformity α) (fun (V : set (α × α)) => V ∈ uniformity α ∧ is_open V) id :=
  sorry

theorem filter.has_basis.mem_uniformity_iff {α : Type u_1} {β : Type u_2} [uniform_space α]
    {p : β → Prop} {s : β → set (α × α)} (h : filter.has_basis (uniformity α) p s)
    {t : set (α × α)} :
    t ∈ uniformity α ↔ ∃ (i : β), ∃ (hi : p i), ∀ (a b : α), (a, b) ∈ s i → (a, b) ∈ t :=
  sorry

/-- Symmetric entourages form a basis of `𝓤 α` -/
theorem uniform_space.has_basis_symmetric {α : Type u_1} [uniform_space α] :
    filter.has_basis (uniformity α) (fun (s : set (α × α)) => s ∈ uniformity α ∧ symmetric_rel s)
        id :=
  sorry

/-- Open elements `s : set (α × α)` of `𝓤 α` such that `(x, y) ∈ s ↔ (y, x) ∈ s` form a basis
of `𝓤 α`. -/
theorem uniformity_has_basis_open_symmetric {α : Type u_1} [uniform_space α] :
    filter.has_basis (uniformity α)
        (fun (V : set (α × α)) => V ∈ uniformity α ∧ is_open V ∧ symmetric_rel V) id :=
  sorry

theorem uniform_space.has_seq_basis {α : Type u_1} [uniform_space α]
    (h : filter.is_countably_generated (uniformity α)) :
    ∃ (V : ℕ → set (α × α)),
        filter.has_antimono_basis (uniformity α) (fun (_x : ℕ) => True) V ∧
          ∀ (n : ℕ), symmetric_rel (V n) :=
  sorry

/-! ### Uniform continuity -/

/-- A function `f : α → β` is *uniformly continuous* if `(f x, f y)` tends to the diagonal
as `(x, y)` tends to the diagonal. In other words, if `x` is sufficiently close to `y`, then
`f x` is close to `f y` no matter where `x` and `y` are located in `α`. -/
def uniform_continuous {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β]
    (f : α → β) :=
  filter.tendsto (fun (x : α × α) => (f (prod.fst x), f (prod.snd x))) (uniformity α) (uniformity β)

/-- A function `f : α → β` is *uniformly continuous* on `s : set α` if `(f x, f y)` tends to
the diagonal as `(x, y)` tends to the diagonal while remaining in `s.prod s`.
In other words, if `x` is sufficiently close to `y`, then `f x` is close to
`f y` no matter where `x` and `y` are located in `s`.-/
def uniform_continuous_on {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β]
    (f : α → β) (s : set α) :=
  filter.tendsto (fun (x : α × α) => (f (prod.fst x), f (prod.snd x)))
    (uniformity α ⊓ filter.principal (set.prod s s)) (uniformity β)

theorem uniform_continuous_def {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β]
    {f : α → β} :
    uniform_continuous f ↔
        ∀ (r : set (β × β)),
          r ∈ uniformity β →
            (set_of fun (x : α × α) => (f (prod.fst x), f (prod.snd x)) ∈ r) ∈ uniformity α :=
  iff.rfl

theorem uniform_continuous_iff_eventually {α : Type u_1} {β : Type u_2} [uniform_space α]
    [uniform_space β] {f : α → β} :
    uniform_continuous f ↔
        ∀ (r : set (β × β)),
          r ∈ uniformity β →
            filter.eventually (fun (x : α × α) => (f (prod.fst x), f (prod.snd x)) ∈ r)
              (uniformity α) :=
  iff.rfl

theorem uniform_continuous_of_const {α : Type u_1} {β : Type u_2} [uniform_space α]
    [uniform_space β] {c : α → β} (h : ∀ (a b : α), c a = c b) : uniform_continuous c :=
  sorry

theorem uniform_continuous_id {α : Type u_1} [uniform_space α] : uniform_continuous id := sorry

theorem uniform_continuous_const {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β]
    {b : β} : uniform_continuous fun (a : α) => b :=
  uniform_continuous_of_const fun (_x _x : α) => rfl

theorem uniform_continuous.comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} [uniform_space α]
    [uniform_space β] [uniform_space γ] {g : β → γ} {f : α → β} (hg : uniform_continuous g)
    (hf : uniform_continuous f) : uniform_continuous (g ∘ f) :=
  filter.tendsto.comp hg hf

theorem filter.has_basis.uniform_continuous_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    {δ : Type u_4} [uniform_space α] [uniform_space β] {p : γ → Prop} {s : γ → set (α × α)}
    (ha : filter.has_basis (uniformity α) p s) {q : δ → Prop} {t : δ → set (β × β)}
    (hb : filter.has_basis (uniformity β) q t) {f : α → β} :
    uniform_continuous f ↔
        ∀ (i : δ), q i → ∃ (j : γ), ∃ (hj : p j), ∀ (x y : α), (x, y) ∈ s j → (f x, f y) ∈ t i :=
  sorry

theorem filter.has_basis.uniform_continuous_on_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    {δ : Type u_4} [uniform_space α] [uniform_space β] {p : γ → Prop} {s : γ → set (α × α)}
    (ha : filter.has_basis (uniformity α) p s) {q : δ → Prop} {t : δ → set (β × β)}
    (hb : filter.has_basis (uniformity β) q t) {f : α → β} {S : set α} :
    uniform_continuous_on f S ↔
        ∀ (i : δ),
          q i →
            ∃ (j : γ), ∃ (hj : p j), ∀ (x y : α), x ∈ S → y ∈ S → (x, y) ∈ s j → (f x, f y) ∈ t i :=
  sorry

protected instance uniform_space.partial_order {α : Type u_1} : partial_order (uniform_space α) :=
  partial_order.mk
    (fun (t s : uniform_space α) =>
      uniform_space.core.uniformity uniform_space.to_core ≤
        uniform_space.core.uniformity uniform_space.to_core)
    (preorder.lt._default
      fun (t s : uniform_space α) =>
        uniform_space.core.uniformity uniform_space.to_core ≤
          uniform_space.core.uniformity uniform_space.to_core)
    sorry sorry sorry

protected instance uniform_space.has_Inf {α : Type u_1} : has_Inf (uniform_space α) :=
  has_Inf.mk
    fun (s : set (uniform_space α)) =>
      uniform_space.of_core
        (uniform_space.core.mk
          (infi fun (u : uniform_space α) => infi fun (H : u ∈ s) => uniformity α) sorry sorry
          sorry)

protected instance uniform_space.has_top {α : Type u_1} : has_top (uniform_space α) :=
  has_top.mk (uniform_space.of_core (uniform_space.core.mk ⊤ sorry sorry sorry))

protected instance uniform_space.has_bot {α : Type u_1} : has_bot (uniform_space α) :=
  has_bot.mk
    (uniform_space.mk (uniform_space.core.mk (filter.principal id_rel) sorry sorry sorry) sorry)

protected instance uniform_space.complete_lattice {α : Type u_1} :
    complete_lattice (uniform_space α) :=
  complete_lattice.mk
    (fun (a b : uniform_space α) => Inf (set_of fun (x : uniform_space α) => a ≤ x ∧ b ≤ x))
    partial_order.le partial_order.lt sorry sorry sorry sorry sorry sorry
    (fun (a b : uniform_space α) => Inf (insert a (singleton b))) sorry sorry sorry ⊤ sorry ⊥ sorry
    (fun (tt : set (uniform_space α)) =>
      Inf (set_of fun (t : uniform_space α) => ∀ (t' : uniform_space α), t' ∈ tt → t' ≤ t))
    Inf sorry sorry sorry sorry

theorem infi_uniformity {α : Type u_1} {ι : Sort u_2} {u : ι → uniform_space α} :
    uniform_space.core.uniformity uniform_space.to_core =
        infi fun (i : ι) => uniform_space.core.uniformity uniform_space.to_core :=
  sorry

theorem inf_uniformity {α : Type u_1} {u : uniform_space α} {v : uniform_space α} :
    uniform_space.core.uniformity uniform_space.to_core =
        uniform_space.core.uniformity uniform_space.to_core ⊓
          uniform_space.core.uniformity uniform_space.to_core :=
  sorry

protected instance inhabited_uniform_space {α : Type u_1} : Inhabited (uniform_space α) :=
  { default := ⊥ }

protected instance inhabited_uniform_space_core {α : Type u_1} : Inhabited (uniform_space.core α) :=
  { default := uniform_space.to_core }

/-- Given `f : α → β` and a uniformity `u` on `β`, the inverse image of `u` under `f`
  is the inverse image in the filter sense of the induced function `α × α → β × β`. -/
def uniform_space.comap {α : Type u_1} {β : Type u_2} (f : α → β) (u : uniform_space β) :
    uniform_space α :=
  uniform_space.mk
    (uniform_space.core.mk
      (filter.comap (fun (p : α × α) => (f (prod.fst p), f (prod.snd p)))
        (uniform_space.core.uniformity uniform_space.to_core))
      sorry sorry sorry)
    sorry

theorem uniformity_comap {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β]
    {f : α → β} (h : _inst_1 = uniform_space.comap f _inst_2) :
    uniformity α = filter.comap (prod.map f f) (uniformity β) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (uniformity α = filter.comap (prod.map f f) (uniformity β))) h))
    (Eq.refl (uniformity α))

theorem uniform_space_comap_id {α : Type u_1} : uniform_space.comap id = id := sorry

theorem uniform_space.comap_comap {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [uγ : uniform_space γ] {f : α → β} {g : β → γ} :
    uniform_space.comap (g ∘ f) uγ = uniform_space.comap f (uniform_space.comap g uγ) :=
  sorry

theorem uniform_continuous_iff {α : Type u_1} {β : Type u_2} [uα : uniform_space α]
    [uβ : uniform_space β] {f : α → β} : uniform_continuous f ↔ uα ≤ uniform_space.comap f uβ :=
  filter.map_le_iff_le_comap

theorem uniform_continuous_comap {α : Type u_1} {β : Type u_2} {f : α → β} [u : uniform_space β] :
    uniform_continuous f :=
  filter.tendsto_comap

theorem to_topological_space_comap {α : Type u_1} {β : Type u_2} {f : α → β} {u : uniform_space β} :
    uniform_space.to_topological_space =
        topological_space.induced f uniform_space.to_topological_space :=
  rfl

theorem uniform_continuous_comap' {α : Type u_1} {β : Type u_2} {γ : Type u_3} {f : γ → β}
    {g : α → γ} [v : uniform_space β] [u : uniform_space α] (h : uniform_continuous (f ∘ g)) :
    uniform_continuous g :=
  iff.mpr filter.tendsto_comap_iff h

theorem to_topological_space_mono {α : Type u_1} {u₁ : uniform_space α} {u₂ : uniform_space α}
    (h : u₁ ≤ u₂) : uniform_space.to_topological_space ≤ uniform_space.to_topological_space :=
  sorry

theorem uniform_continuous.continuous {α : Type u_1} {β : Type u_2} [uniform_space α]
    [uniform_space β] {f : α → β} (hf : uniform_continuous f) : continuous f :=
  iff.mpr continuous_iff_le_induced (to_topological_space_mono (iff.mp uniform_continuous_iff hf))

theorem to_topological_space_bot {α : Type u_1} : uniform_space.to_topological_space = ⊥ := rfl

theorem to_topological_space_top {α : Type u_1} : uniform_space.to_topological_space = ⊤ := sorry

theorem to_topological_space_infi {α : Type u_1} {ι : Sort u_2} {u : ι → uniform_space α} :
    uniform_space.to_topological_space = infi fun (i : ι) => uniform_space.to_topological_space :=
  sorry

theorem to_topological_space_Inf {α : Type u_1} {s : set (uniform_space α)} :
    uniform_space.to_topological_space =
        infi
          fun (i : uniform_space α) => infi fun (H : i ∈ s) => uniform_space.to_topological_space :=
  sorry

theorem to_topological_space_inf {α : Type u_1} {u : uniform_space α} {v : uniform_space α} :
    uniform_space.to_topological_space =
        uniform_space.to_topological_space ⊓ uniform_space.to_topological_space :=
  sorry

protected instance empty.uniform_space : uniform_space empty := ⊥

protected instance unit.uniform_space : uniform_space Unit := ⊥

protected instance bool.uniform_space : uniform_space Bool := ⊥

protected instance nat.uniform_space : uniform_space ℕ := ⊥

protected instance int.uniform_space : uniform_space ℤ := ⊥

protected instance subtype.uniform_space {α : Type u_1} {p : α → Prop} [t : uniform_space α] :
    uniform_space (Subtype p) :=
  uniform_space.comap subtype.val t

theorem uniformity_subtype {α : Type u_1} {p : α → Prop} [t : uniform_space α] :
    uniformity (Subtype p) =
        filter.comap
          (fun (q : Subtype p × Subtype p) => (subtype.val (prod.fst q), subtype.val (prod.snd q)))
          (uniformity α) :=
  rfl

theorem uniform_continuous_subtype_val {α : Type u_1} {p : α → Prop} [uniform_space α] :
    uniform_continuous subtype.val :=
  uniform_continuous_comap

theorem uniform_continuous_subtype_mk {α : Type u_1} {β : Type u_2} {p : α → Prop} [uniform_space α]
    [uniform_space β] {f : β → α} (hf : uniform_continuous f) (h : ∀ (x : β), p (f x)) :
    uniform_continuous fun (x : β) => { val := f x, property := h x } :=
  uniform_continuous_comap' hf

theorem uniform_continuous_on_iff_restrict {α : Type u_1} {β : Type u_2} [uniform_space α]
    [uniform_space β] {f : α → β} {s : set α} :
    uniform_continuous_on f s ↔ uniform_continuous (set.restrict f s) :=
  sorry

theorem tendsto_of_uniform_continuous_subtype {α : Type u_1} {β : Type u_2} [uniform_space α]
    [uniform_space β] {f : α → β} {s : set α} {a : α}
    (hf : uniform_continuous fun (x : ↥s) => f (subtype.val x)) (ha : s ∈ nhds a) :
    filter.tendsto f (nhds a) (nhds (f a)) :=
  sorry

theorem uniform_continuous_on.continuous_on {α : Type u_1} {β : Type u_2} [uniform_space α]
    [uniform_space β] {f : α → β} {s : set α} (h : uniform_continuous_on f s) : continuous_on f s :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (continuous_on f s)) (propext continuous_on_iff_continuous_restrict)))
    (uniform_continuous.continuous
      (eq.mp
        (Eq._oldrec (Eq.refl (uniform_continuous_on f s))
          (propext uniform_continuous_on_iff_restrict))
        h))

/- a similar product space is possible on the function space (uniformity of pointwise convergence),
  but we want to have the uniformity of uniform convergence on function spaces -/

protected instance prod.uniform_space {α : Type u_1} {β : Type u_2} [u₁ : uniform_space α]
    [u₂ : uniform_space β] : uniform_space (α × β) :=
  uniform_space.of_core_eq uniform_space.to_core prod.topological_space sorry

theorem uniformity_prod {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β] :
    uniformity (α × β) =
        filter.comap (fun (p : (α × β) × α × β) => (prod.fst (prod.fst p), prod.fst (prod.snd p)))
            (uniformity α) ⊓
          filter.comap (fun (p : (α × β) × α × β) => (prod.snd (prod.fst p), prod.snd (prod.snd p)))
            (uniformity β) :=
  inf_uniformity

theorem uniformity_prod_eq_prod {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β] :
    uniformity (α × β) =
        filter.map
          (fun (p : (α × α) × β × β) =>
            ((prod.fst (prod.fst p), prod.fst (prod.snd p)), prod.snd (prod.fst p),
            prod.snd (prod.snd p)))
          (filter.prod (uniformity α) (uniformity β)) :=
  sorry

theorem mem_map_sets_iff' {α : Type u_1} {β : Type u_2} {f : filter α} {m : α → β} {t : set β} :
    t ∈ filter.sets (filter.map m f) ↔ ∃ (s : set α), ∃ (H : s ∈ f), m '' s ⊆ t :=
  filter.mem_map_sets_iff

theorem mem_uniformity_of_uniform_continuous_invariant {α : Type u_1} [uniform_space α]
    {s : set (α × α)} {f : α → α → α}
    (hf : uniform_continuous fun (p : α × α) => f (prod.fst p) (prod.snd p))
    (hs : s ∈ uniformity α) :
    ∃ (u : set (α × α)), ∃ (H : u ∈ uniformity α), ∀ (a b c : α), (a, b) ∈ u → (f a c, f b c) ∈ s :=
  sorry

theorem mem_uniform_prod {α : Type u_1} {β : Type u_2} [t₁ : uniform_space α] [t₂ : uniform_space β]
    {a : set (α × α)} {b : set (β × β)} (ha : a ∈ uniformity α) (hb : b ∈ uniformity β) :
    (set_of
          fun (p : (α × β) × α × β) =>
            (prod.fst (prod.fst p), prod.fst (prod.snd p)) ∈ a ∧
              (prod.snd (prod.fst p), prod.snd (prod.snd p)) ∈ b) ∈
        uniformity (α × β) :=
  sorry

theorem tendsto_prod_uniformity_fst {α : Type u_1} {β : Type u_2} [uniform_space α]
    [uniform_space β] :
    filter.tendsto (fun (p : (α × β) × α × β) => (prod.fst (prod.fst p), prod.fst (prod.snd p)))
        (uniformity (α × β)) (uniformity α) :=
  le_trans (filter.map_mono inf_le_left) filter.map_comap_le

theorem tendsto_prod_uniformity_snd {α : Type u_1} {β : Type u_2} [uniform_space α]
    [uniform_space β] :
    filter.tendsto (fun (p : (α × β) × α × β) => (prod.snd (prod.fst p), prod.snd (prod.snd p)))
        (uniformity (α × β)) (uniformity β) :=
  le_trans (filter.map_mono inf_le_right) filter.map_comap_le

theorem uniform_continuous_fst {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β] :
    uniform_continuous fun (p : α × β) => prod.fst p :=
  tendsto_prod_uniformity_fst

theorem uniform_continuous_snd {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β] :
    uniform_continuous fun (p : α × β) => prod.snd p :=
  tendsto_prod_uniformity_snd

theorem uniform_continuous.prod_mk {α : Type u_1} {β : Type u_2} {γ : Type u_3} [uniform_space α]
    [uniform_space β] [uniform_space γ] {f₁ : α → β} {f₂ : α → γ} (h₁ : uniform_continuous f₁)
    (h₂ : uniform_continuous f₂) : uniform_continuous fun (a : α) => (f₁ a, f₂ a) :=
  sorry

theorem uniform_continuous.prod_mk_left {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [uniform_space α] [uniform_space β] [uniform_space γ] {f : α × β → γ} (h : uniform_continuous f)
    (b : β) : uniform_continuous fun (a : α) => f (a, b) :=
  uniform_continuous.comp h
    (uniform_continuous.prod_mk uniform_continuous_id uniform_continuous_const)

theorem uniform_continuous.prod_mk_right {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [uniform_space α] [uniform_space β] [uniform_space γ] {f : α × β → γ} (h : uniform_continuous f)
    (a : α) : uniform_continuous fun (b : β) => f (a, b) :=
  uniform_continuous.comp h
    (uniform_continuous.prod_mk uniform_continuous_const uniform_continuous_id)

theorem uniform_continuous.prod_map {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4}
    [uniform_space α] [uniform_space β] [uniform_space γ] [uniform_space δ] {f : α → γ} {g : β → δ}
    (hf : uniform_continuous f) (hg : uniform_continuous g) : uniform_continuous (prod.map f g) :=
  uniform_continuous.prod_mk (uniform_continuous.comp hf uniform_continuous_fst)
    (uniform_continuous.comp hg uniform_continuous_snd)

theorem to_topological_space_prod {α : Type u_1} {β : Type u_2} [u : uniform_space α]
    [v : uniform_space β] : uniform_space.to_topological_space = prod.topological_space :=
  rfl

/-- Uniform continuity for functions of two variables. -/
def uniform_continuous₂ {α : Type u_1} {β : Type u_2} {γ : Type u_3} [uniform_space α]
    [uniform_space β] [uniform_space γ] (f : α → β → γ) :=
  uniform_continuous (function.uncurry f)

theorem uniform_continuous₂_def {α : Type u_1} {β : Type u_2} {γ : Type u_3} [uniform_space α]
    [uniform_space β] [uniform_space γ] (f : α → β → γ) :
    uniform_continuous₂ f ↔ uniform_continuous (function.uncurry f) :=
  iff.rfl

theorem uniform_continuous₂.uniform_continuous {α : Type u_1} {β : Type u_2} {γ : Type u_3}
    [uniform_space α] [uniform_space β] [uniform_space γ] {f : α → β → γ}
    (h : uniform_continuous₂ f) : uniform_continuous (function.uncurry f) :=
  h

theorem uniform_continuous₂_curry {α : Type u_1} {β : Type u_2} {γ : Type u_3} [uniform_space α]
    [uniform_space β] [uniform_space γ] (f : α × β → γ) :
    uniform_continuous₂ (function.curry f) ↔ uniform_continuous f :=
  sorry

theorem uniform_continuous₂.comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4}
    [uniform_space α] [uniform_space β] [uniform_space γ] [uniform_space δ] {f : α → β → γ}
    {g : γ → δ} (hg : uniform_continuous g) (hf : uniform_continuous₂ f) :
    uniform_continuous₂ (function.bicompr g f) :=
  uniform_continuous.comp hg hf

theorem uniform_continuous₂.bicompl {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4}
    {δ' : Type u_6} [uniform_space α] [uniform_space β] [uniform_space γ] [uniform_space δ]
    [uniform_space δ'] {f : α → β → γ} {ga : δ → α} {gb : δ' → β} (hf : uniform_continuous₂ f)
    (hga : uniform_continuous ga) (hgb : uniform_continuous gb) :
    uniform_continuous₂ (function.bicompl f ga gb) :=
  uniform_continuous.comp (uniform_continuous₂.uniform_continuous hf)
    (uniform_continuous.prod_map hga hgb)

theorem to_topological_space_subtype {α : Type u_1} [u : uniform_space α] {p : α → Prop} :
    uniform_space.to_topological_space = subtype.topological_space :=
  rfl

/-- Uniformity on a disjoint union. Entourages of the diagonal in the union are obtained
by taking independently an entourage of the diagonal in the first part, and an entourage of
the diagonal in the second part. -/
def uniform_space.core.sum {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β] :
    uniform_space.core (α ⊕ β) :=
  uniform_space.core.mk'
    (filter.map (fun (p : α × α) => (sum.inl (prod.fst p), sum.inl (prod.snd p))) (uniformity α) ⊔
      filter.map (fun (p : β × β) => (sum.inr (prod.fst p), sum.inr (prod.snd p))) (uniformity β))
    sorry sorry sorry

/-- The union of an entourage of the diagonal in each set of a disjoint union is again an entourage
of the diagonal. -/
theorem union_mem_uniformity_sum {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β]
    {a : set (α × α)} (ha : a ∈ uniformity α) {b : set (β × β)} (hb : b ∈ uniformity β) :
    (fun (p : α × α) => (sum.inl (prod.fst p), sum.inl (prod.snd p))) '' a ∪
          (fun (p : β × β) => (sum.inr (prod.fst p), sum.inr (prod.snd p))) '' b ∈
        uniform_space.core.uniformity uniform_space.core.sum :=
  sorry

/- To prove that the topology defined by the uniform structure on the disjoint union coincides with
the disjoint union topology, we need two lemmas saying that open sets can be characterized by
the uniform structure -/

theorem uniformity_sum_of_open_aux {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β]
    {s : set (α ⊕ β)} (hs : is_open s) {x : α ⊕ β} (xs : x ∈ s) :
    (set_of fun (p : (α ⊕ β) × (α ⊕ β)) => prod.fst p = x → prod.snd p ∈ s) ∈
        uniform_space.core.uniformity uniform_space.core.sum :=
  sorry

theorem open_of_uniformity_sum_aux {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β]
    {s : set (α ⊕ β)}
    (hs :
      ∀ (x : α ⊕ β),
        x ∈ s →
          (set_of fun (p : (α ⊕ β) × (α ⊕ β)) => prod.fst p = x → prod.snd p ∈ s) ∈
            uniform_space.core.uniformity uniform_space.core.sum) :
    is_open s :=
  sorry

/- We can now define the uniform structure on the disjoint union -/

protected instance sum.uniform_space {α : Type u_1} {β : Type u_2} [uniform_space α]
    [uniform_space β] : uniform_space (α ⊕ β) :=
  uniform_space.mk uniform_space.core.sum sorry

theorem sum.uniformity {α : Type u_1} {β : Type u_2} [uniform_space α] [uniform_space β] :
    uniformity (α ⊕ β) =
        filter.map (fun (p : α × α) => (sum.inl (prod.fst p), sum.inl (prod.snd p)))
            (uniformity α) ⊔
          filter.map (fun (p : β × β) => (sum.inr (prod.fst p), sum.inr (prod.snd p)))
            (uniformity β) :=
  rfl

-- For a version of the Lebesgue number lemma assuming only a sequentially compact space,

-- see topology/sequences.lean

/-- Let `c : ι → set α` be an open cover of a compact set `s`. Then there exists an entourage
`n` such that for each `x ∈ s` its `n`-neighborhood is contained in some `c i`. -/
theorem lebesgue_number_lemma {α : Type u} [uniform_space α] {s : set α} {ι : Sort u_1}
    {c : ι → set α} (hs : is_compact s) (hc₁ : ∀ (i : ι), is_open (c i))
    (hc₂ : s ⊆ set.Union fun (i : ι) => c i) :
    ∃ (n : set (α × α)),
        ∃ (H : n ∈ uniformity α),
          ∀ (x : α), x ∈ s → ∃ (i : ι), (set_of fun (y : α) => (x, y) ∈ n) ⊆ c i :=
  sorry

/-- Let `c : set (set α)` be an open cover of a compact set `s`. Then there exists an entourage
`n` such that for each `x ∈ s` its `n`-neighborhood is contained in some `t ∈ c`. -/
theorem lebesgue_number_lemma_sUnion {α : Type u} [uniform_space α] {s : set α} {c : set (set α)}
    (hs : is_compact s) (hc₁ : ∀ (t : set α), t ∈ c → is_open t) (hc₂ : s ⊆ ⋃₀c) :
    ∃ (n : set (α × α)),
        ∃ (H : n ∈ uniformity α),
          ∀ (x : α) (H : x ∈ s), ∃ (t : set α), ∃ (H : t ∈ c), ∀ (y : α), (x, y) ∈ n → y ∈ t :=
  sorry

/-!
### Expressing continuity properties in uniform spaces

We reformulate the various continuity properties of functions taking values in a uniform space
in terms of the uniformity in the target. Since the same lemmas (essentially with the same names)
also exist for metric spaces and emetric spaces (reformulating things in terms of the distance or
the edistance in the target), we put them in a namespace `uniform` here.

In the metric and emetric space setting, there are also similar lemmas where one assumes that
both the source and the target are metric spaces, reformulating things in terms of the distance
on both sides. These lemmas are generally written without primes, and the versions where only
the target is a metric space is primed. We follow the same convention here, thus giving lemmas
with primes.
-/

namespace uniform

theorem tendsto_nhds_right {α : Type u_1} {β : Type u_2} [uniform_space α] {f : filter β}
    {u : β → α} {a : α} :
    filter.tendsto u f (nhds a) ↔ filter.tendsto (fun (x : β) => (a, u x)) f (uniformity α) :=
  sorry

theorem tendsto_nhds_left {α : Type u_1} {β : Type u_2} [uniform_space α] {f : filter β} {u : β → α}
    {a : α} :
    filter.tendsto u f (nhds a) ↔ filter.tendsto (fun (x : β) => (u x, a)) f (uniformity α) :=
  sorry

theorem continuous_at_iff'_right {α : Type u_1} {β : Type u_2} [uniform_space α]
    [topological_space β] {f : β → α} {b : β} :
    continuous_at f b ↔ filter.tendsto (fun (x : β) => (f b, f x)) (nhds b) (uniformity α) :=
  sorry

theorem continuous_at_iff'_left {α : Type u_1} {β : Type u_2} [uniform_space α]
    [topological_space β] {f : β → α} {b : β} :
    continuous_at f b ↔ filter.tendsto (fun (x : β) => (f x, f b)) (nhds b) (uniformity α) :=
  sorry

theorem continuous_at_iff_prod {α : Type u_1} {β : Type u_2} [uniform_space α] [topological_space β]
    {f : β → α} {b : β} :
    continuous_at f b ↔
        filter.tendsto (fun (x : β × β) => (f (prod.fst x), f (prod.snd x))) (nhds (b, b))
          (uniformity α) :=
  sorry

theorem continuous_within_at_iff'_right {α : Type u_1} {β : Type u_2} [uniform_space α]
    [topological_space β] {f : β → α} {b : β} {s : set β} :
    continuous_within_at f s b ↔
        filter.tendsto (fun (x : β) => (f b, f x)) (nhds_within b s) (uniformity α) :=
  sorry

theorem continuous_within_at_iff'_left {α : Type u_1} {β : Type u_2} [uniform_space α]
    [topological_space β] {f : β → α} {b : β} {s : set β} :
    continuous_within_at f s b ↔
        filter.tendsto (fun (x : β) => (f x, f b)) (nhds_within b s) (uniformity α) :=
  sorry

theorem continuous_on_iff'_right {α : Type u_1} {β : Type u_2} [uniform_space α]
    [topological_space β] {f : β → α} {s : set β} :
    continuous_on f s ↔
        ∀ (b : β),
          b ∈ s → filter.tendsto (fun (x : β) => (f b, f x)) (nhds_within b s) (uniformity α) :=
  sorry

theorem continuous_on_iff'_left {α : Type u_1} {β : Type u_2} [uniform_space α]
    [topological_space β] {f : β → α} {s : set β} :
    continuous_on f s ↔
        ∀ (b : β),
          b ∈ s → filter.tendsto (fun (x : β) => (f x, f b)) (nhds_within b s) (uniformity α) :=
  sorry

theorem continuous_iff'_right {α : Type u_1} {β : Type u_2} [uniform_space α] [topological_space β]
    {f : β → α} :
    continuous f ↔ ∀ (b : β), filter.tendsto (fun (x : β) => (f b, f x)) (nhds b) (uniformity α) :=
  iff.trans continuous_iff_continuous_at (forall_congr fun (b : β) => tendsto_nhds_right)

theorem continuous_iff'_left {α : Type u_1} {β : Type u_2} [uniform_space α] [topological_space β]
    {f : β → α} :
    continuous f ↔ ∀ (b : β), filter.tendsto (fun (x : β) => (f x, f b)) (nhds b) (uniformity α) :=
  iff.trans continuous_iff_continuous_at (forall_congr fun (b : β) => tendsto_nhds_left)

end uniform

theorem filter.tendsto.congr_uniformity {α : Type u_1} {β : Type u_2} [uniform_space β] {f : α → β}
    {g : α → β} {l : filter α} {b : β} (hf : filter.tendsto f l (nhds b))
    (hg : filter.tendsto (fun (x : α) => (f x, g x)) l (uniformity β)) :
    filter.tendsto g l (nhds b) :=
  iff.mpr uniform.tendsto_nhds_right
    (filter.tendsto.uniformity_trans (iff.mp uniform.tendsto_nhds_right hf) hg)

theorem uniform.tendsto_congr {α : Type u_1} {β : Type u_2} [uniform_space β] {f : α → β}
    {g : α → β} {l : filter α} {b : β}
    (hfg : filter.tendsto (fun (x : α) => (f x, g x)) l (uniformity β)) :
    filter.tendsto f l (nhds b) ↔ filter.tendsto g l (nhds b) :=
  { mp := fun (h : filter.tendsto f l (nhds b)) => filter.tendsto.congr_uniformity h hfg,
    mpr :=
      fun (h : filter.tendsto g l (nhds b)) =>
        filter.tendsto.congr_uniformity h (filter.tendsto.uniformity_symm hfg) }

end Mathlib


/-
Copyright (c) 2020 Joseph Myers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Joseph Myers.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.linear_algebra.affine_space.independent
import Mathlib.linear_algebra.finite_dimensional
import Mathlib.PostPort

universes u_1 u_2 u_3 u_4

namespace Mathlib

/-!
# Finite-dimensional subspaces of affine spaces.

This file provides a few results relating to finite-dimensional
subspaces of affine spaces.

## Main definitions

* `collinear` defines collinear sets of points as those that span a
  subspace of dimension at most 1.

-/

/-- The `vector_span` of a finite set is finite-dimensional. -/
theorem finite_dimensional_vector_span_of_finite (k : Type u_1) {V : Type u_2} {P : Type u_3}
    [field k] [add_comm_group V] [module k V] [add_torsor V P] {s : set P} (h : set.finite s) :
    finite_dimensional k ↥(vector_span k s) :=
  finite_dimensional.span_of_finite k (set.finite.vsub h h)

/-- The `vector_span` of a family indexed by a `fintype` is
finite-dimensional. -/
protected instance finite_dimensional_vector_span_of_fintype (k : Type u_1) {V : Type u_2}
    {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4}
    [fintype ι] (p : ι → P) : finite_dimensional k ↥(vector_span k (set.range p)) :=
  finite_dimensional_vector_span_of_finite k (set.finite_range p)

/-- The `vector_span` of a subset of a family indexed by a `fintype`
is finite-dimensional. -/
protected instance finite_dimensional_vector_span_image_of_fintype (k : Type u_1) {V : Type u_2}
    {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4}
    [fintype ι] (p : ι → P) (s : set ι) : finite_dimensional k ↥(vector_span k (p '' s)) :=
  finite_dimensional_vector_span_of_finite k (set.finite.image p (set.finite.of_fintype s))

/-- The direction of the affine span of a finite set is
finite-dimensional. -/
theorem finite_dimensional_direction_affine_span_of_finite (k : Type u_1) {V : Type u_2}
    {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P] {s : set P}
    (h : set.finite s) : finite_dimensional k ↥(affine_subspace.direction (affine_span k s)) :=
  Eq.symm (direction_affine_span k s) ▸ finite_dimensional_vector_span_of_finite k h

/-- The direction of the affine span of a family indexed by a
`fintype` is finite-dimensional. -/
protected instance finite_dimensional_direction_affine_span_of_fintype (k : Type u_1) {V : Type u_2}
    {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4}
    [fintype ι] (p : ι → P) :
    finite_dimensional k ↥(affine_subspace.direction (affine_span k (set.range p))) :=
  finite_dimensional_direction_affine_span_of_finite k (set.finite_range p)

/-- The direction of the affine span of a subset of a family indexed
by a `fintype` is finite-dimensional. -/
protected instance finite_dimensional_direction_affine_span_image_of_fintype (k : Type u_1)
    {V : Type u_2} {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P]
    {ι : Type u_4} [fintype ι] (p : ι → P) (s : set ι) :
    finite_dimensional k ↥(affine_subspace.direction (affine_span k (p '' s))) :=
  finite_dimensional_direction_affine_span_of_finite k
    (set.finite.image p (set.finite.of_fintype s))

/-- The `vector_span` of a finite subset of an affinely independent
family has dimension one less than its cardinality. -/
theorem findim_vector_span_image_finset_of_affine_independent {k : Type u_1} {V : Type u_2}
    {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4}
    {p : ι → P} (hi : affine_independent k p) {s : finset ι} {n : ℕ} (hc : finset.card s = n + 1) :
    finite_dimensional.findim k ↥(vector_span k (p '' ↑s)) = n :=
  sorry

/-- The `vector_span` of a finite affinely independent family has
dimension one less than its cardinality. -/
theorem findim_vector_span_of_affine_independent {k : Type u_1} {V : Type u_2} {P : Type u_3}
    [field k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4} [fintype ι]
    {p : ι → P} (hi : affine_independent k p) {n : ℕ} (hc : fintype.card ι = n + 1) :
    finite_dimensional.findim k ↥(vector_span k (set.range p)) = n :=
  sorry

/-- If the `vector_span` of a finite subset of an affinely independent
family lies in a submodule with dimension one less than its
cardinality, it equals that submodule. -/
theorem vector_span_image_finset_eq_of_le_of_affine_independent_of_card_eq_findim_add_one
    {k : Type u_1} {V : Type u_2} {P : Type u_3} [field k] [add_comm_group V] [module k V]
    [add_torsor V P] {ι : Type u_4} {p : ι → P} (hi : affine_independent k p) {s : finset ι}
    {sm : submodule k V} [finite_dimensional k ↥sm] (hle : vector_span k (p '' ↑s) ≤ sm)
    (hc : finset.card s = finite_dimensional.findim k ↥sm + 1) : vector_span k (p '' ↑s) = sm :=
  finite_dimensional.eq_of_le_of_findim_eq hle
    (findim_vector_span_image_finset_of_affine_independent hi hc)

/-- If the `vector_span` of a finite affinely independent
family lies in a submodule with dimension one less than its
cardinality, it equals that submodule. -/
theorem vector_span_eq_of_le_of_affine_independent_of_card_eq_findim_add_one {k : Type u_1}
    {V : Type u_2} {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P]
    {ι : Type u_4} [fintype ι] {p : ι → P} (hi : affine_independent k p) {sm : submodule k V}
    [finite_dimensional k ↥sm] (hle : vector_span k (set.range p) ≤ sm)
    (hc : fintype.card ι = finite_dimensional.findim k ↥sm + 1) :
    vector_span k (set.range p) = sm :=
  finite_dimensional.eq_of_le_of_findim_eq hle (findim_vector_span_of_affine_independent hi hc)

/-- If the `affine_span` of a finite subset of an affinely independent
family lies in an affine subspace whose direction has dimension one
less than its cardinality, it equals that subspace. -/
theorem affine_span_image_finset_eq_of_le_of_affine_independent_of_card_eq_findim_add_one
    {k : Type u_1} {V : Type u_2} {P : Type u_3} [field k] [add_comm_group V] [module k V]
    [add_torsor V P] {ι : Type u_4} {p : ι → P} (hi : affine_independent k p) {s : finset ι}
    {sp : affine_subspace k P} [finite_dimensional k ↥(affine_subspace.direction sp)]
    (hle : affine_span k (p '' ↑s) ≤ sp)
    (hc : finset.card s = finite_dimensional.findim k ↥(affine_subspace.direction sp) + 1) :
    affine_span k (p '' ↑s) = sp :=
  sorry

/-- If the `affine_span` of a finite affinely independent family lies
in an affine subspace whose direction has dimension one less than its
cardinality, it equals that subspace. -/
theorem affine_span_eq_of_le_of_affine_independent_of_card_eq_findim_add_one {k : Type u_1}
    {V : Type u_2} {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P]
    {ι : Type u_4} [fintype ι] {p : ι → P} (hi : affine_independent k p) {sp : affine_subspace k P}
    [finite_dimensional k ↥(affine_subspace.direction sp)] (hle : affine_span k (set.range p) ≤ sp)
    (hc : fintype.card ι = finite_dimensional.findim k ↥(affine_subspace.direction sp) + 1) :
    affine_span k (set.range p) = sp :=
  sorry

/-- The `vector_span` of a finite affinely independent family whose
cardinality is one more than that of the finite-dimensional space is
`⊤`. -/
theorem vector_span_eq_top_of_affine_independent_of_card_eq_findim_add_one {k : Type u_1}
    {V : Type u_2} {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P]
    {ι : Type u_4} [finite_dimensional k V] [fintype ι] {p : ι → P} (hi : affine_independent k p)
    (hc : fintype.card ι = finite_dimensional.findim k V + 1) : vector_span k (set.range p) = ⊤ :=
  finite_dimensional.eq_top_of_findim_eq (findim_vector_span_of_affine_independent hi hc)

/-- The `affine_span` of a finite affinely independent family whose
cardinality is one more than that of the finite-dimensional space is
`⊤`. -/
theorem affine_span_eq_top_of_affine_independent_of_card_eq_findim_add_one {k : Type u_1}
    {V : Type u_2} {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P]
    {ι : Type u_4} [finite_dimensional k V] [fintype ι] {p : ι → P} (hi : affine_independent k p)
    (hc : fintype.card ι = finite_dimensional.findim k V + 1) : affine_span k (set.range p) = ⊤ :=
  sorry

/-- The `vector_span` of `n + 1` points in an indexed family has
dimension at most `n`. -/
theorem findim_vector_span_image_finset_le (k : Type u_1) {V : Type u_2} {P : Type u_3} [field k]
    [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4} (p : ι → P) (s : finset ι)
    {n : ℕ} (hc : finset.card s = n + 1) :
    finite_dimensional.findim k ↥(vector_span k (p '' ↑s)) ≤ n :=
  sorry

/-- The `vector_span` of an indexed family of `n + 1` points has
dimension at most `n`. -/
theorem findim_vector_span_range_le (k : Type u_1) {V : Type u_2} {P : Type u_3} [field k]
    [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4} [fintype ι] (p : ι → P) {n : ℕ}
    (hc : fintype.card ι = n + 1) :
    finite_dimensional.findim k ↥(vector_span k (set.range p)) ≤ n :=
  sorry

/-- `n + 1` points are affinely independent if and only if their
`vector_span` has dimension `n`. -/
theorem affine_independent_iff_findim_vector_span_eq (k : Type u_1) {V : Type u_2} {P : Type u_3}
    [field k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4} [fintype ι]
    (p : ι → P) {n : ℕ} (hc : fintype.card ι = n + 1) :
    affine_independent k p ↔ finite_dimensional.findim k ↥(vector_span k (set.range p)) = n :=
  sorry

/-- `n + 1` points are affinely independent if and only if their
`vector_span` has dimension at least `n`. -/
theorem affine_independent_iff_le_findim_vector_span (k : Type u_1) {V : Type u_2} {P : Type u_3}
    [field k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4} [fintype ι]
    (p : ι → P) {n : ℕ} (hc : fintype.card ι = n + 1) :
    affine_independent k p ↔ n ≤ finite_dimensional.findim k ↥(vector_span k (set.range p)) :=
  sorry

/-- `n + 2` points are affinely independent if and only if their
`vector_span` does not have dimension at most `n`. -/
theorem affine_independent_iff_not_findim_vector_span_le (k : Type u_1) {V : Type u_2}
    {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4}
    [fintype ι] (p : ι → P) {n : ℕ} (hc : fintype.card ι = n + bit0 1) :
    affine_independent k p ↔ ¬finite_dimensional.findim k ↥(vector_span k (set.range p)) ≤ n :=
  sorry

/-- `n + 2` points have a `vector_span` with dimension at most `n` if
and only if they are not affinely independent. -/
theorem findim_vector_span_le_iff_not_affine_independent (k : Type u_1) {V : Type u_2}
    {P : Type u_3} [field k] [add_comm_group V] [module k V] [add_torsor V P] {ι : Type u_4}
    [fintype ι] (p : ι → P) {n : ℕ} (hc : fintype.card ι = n + bit0 1) :
    finite_dimensional.findim k ↥(vector_span k (set.range p)) ≤ n ↔ ¬affine_independent k p :=
  iff.symm
    (iff.mp not_iff_comm (iff.symm (affine_independent_iff_not_findim_vector_span_le k p hc)))

/-- A set of points is collinear if their `vector_span` has dimension
at most `1`. -/
def collinear (k : Type u_1) {V : Type u_2} {P : Type u_3} [field k] [add_comm_group V] [module k V]
    [add_torsor V P] (s : set P) :=
  vector_space.dim k ↥(vector_span k s) ≤ 1

/-- The definition of `collinear`. -/
theorem collinear_iff_dim_le_one (k : Type u_1) {V : Type u_2} {P : Type u_3} [field k]
    [add_comm_group V] [module k V] [add_torsor V P] (s : set P) :
    collinear k s ↔ vector_space.dim k ↥(vector_span k s) ≤ 1 :=
  iff.rfl

/-- A set of points, whose `vector_span` is finite-dimensional, is
collinear if and only if their `vector_span` has dimension at most
`1`. -/
theorem collinear_iff_findim_le_one (k : Type u_1) {V : Type u_2} {P : Type u_3} [field k]
    [add_comm_group V] [module k V] [add_torsor V P] (s : set P)
    [finite_dimensional k ↥(vector_span k s)] :
    collinear k s ↔ finite_dimensional.findim k ↥(vector_span k s) ≤ 1 :=
  sorry

/-- The empty set is collinear. -/
theorem collinear_empty (k : Type u_1) {V : Type u_2} (P : Type u_3) [field k] [add_comm_group V]
    [module k V] [add_torsor V P] : collinear k ∅ :=
  sorry

/-- A single point is collinear. -/
theorem collinear_singleton (k : Type u_1) {V : Type u_2} {P : Type u_3} [field k]
    [add_comm_group V] [module k V] [add_torsor V P] (p : P) : collinear k (singleton p) :=
  sorry

/-- Given a point `p₀` in a set of points, that set is collinear if and
only if the points can all be expressed as multiples of the same
vector, added to `p₀`. -/
theorem collinear_iff_of_mem (k : Type u_1) {V : Type u_2} {P : Type u_3} [field k]
    [add_comm_group V] [module k V] [add_torsor V P] {s : set P} {p₀ : P} (h : p₀ ∈ s) :
    collinear k s ↔ ∃ (v : V), ∀ (p : P), p ∈ s → ∃ (r : k), p = r • v +ᵥ p₀ :=
  sorry

/-- A set of points is collinear if and only if they can all be
expressed as multiples of the same vector, added to the same base
point. -/
theorem collinear_iff_exists_forall_eq_smul_vadd (k : Type u_1) {V : Type u_2} {P : Type u_3}
    [field k] [add_comm_group V] [module k V] [add_torsor V P] (s : set P) :
    collinear k s ↔ ∃ (p₀ : P), ∃ (v : V), ∀ (p : P), p ∈ s → ∃ (r : k), p = r • v +ᵥ p₀ :=
  sorry

/-- Two points are collinear. -/
theorem collinear_insert_singleton (k : Type u_1) {V : Type u_2} {P : Type u_3} [field k]
    [add_comm_group V] [module k V] [add_torsor V P] (p₁ : P) (p₂ : P) :
    collinear k (insert p₁ (singleton p₂)) :=
  sorry

/-- Three points are affinely independent if and only if they are not
collinear. -/
theorem affine_independent_iff_not_collinear (k : Type u_1) {V : Type u_2} {P : Type u_3} [field k]
    [add_comm_group V] [module k V] [add_torsor V P] (p : fin (bit1 1) → P) :
    affine_independent k p ↔ ¬collinear k (set.range p) :=
  sorry

/-- Three points are collinear if and only if they are not affinely
independent. -/
theorem collinear_iff_not_affine_independent (k : Type u_1) {V : Type u_2} {P : Type u_3} [field k]
    [add_comm_group V] [module k V] [add_torsor V P] (p : fin (bit1 1) → P) :
    collinear k (set.range p) ↔ ¬affine_independent k p :=
  sorry

end Mathlib


import data.matrix data.rat.basic linear_algebra.basis data.fintype tactic.omega
import algebra.associated
import linear_algebra.determinant .misc

noncomputable theory
universes u v w

open matrix fintype finset function
variables {m n k : Type u} [fintype m] [fintype n] [fintype k]
variables [decidable_eq m] [decidable_eq n] [decidable_eq k]
variables {one : Type u} [unique one]
variables (A M : matrix m n ℚ)

variables (b : matrix m one ℚ) (c x : matrix n one ℚ)

local notation M `⬝` N := M.mul N
local postfix `ᵀ` : 1500 := transpose

def is_feasible : Prop := 0 ≤ x ∧ A ⬝ x = b

instance decidable_le : decidable_rel ((≤) : matrix m n ℚ → matrix m n ℚ → Prop) :=
λ M N, show decidable (∀ i j, M i j ≤ N i j), by apply_instance

instance : decidable (is_feasible A b x) :=
by dunfold is_feasible; apply_instance

def is_optimal : Prop :=
is_feasible A b x ∧ ∀ y, is_feasible A b y → cᵀ ⬝ y ≤ cᵀ ⬝ x

def basis (B : m → n) : matrix m m ℚ := minor A id B

def non_basis (B : m → n) : matrix m {b // b ∉ univ.image B}  ℚ :=
minor A id subtype.val

def c_basis (B : m → n) : matrix m one ℚ := minor c B id

def c_non_basis (B : m → n) : matrix {b // b ∉ univ.image B} one ℚ :=
minor c subtype.val id

--#print non_basis
def reduced_cost (B : m → n) : Prop :=
is_invertible (basis A B) ∧
(c_non_basis c B)ᵀ ⬝ non_basis _ B = 0

lemma is_optimal_of_check_reduced_cost (x : cvec n) (B : finset (fin n)) :
  check_reduced_cost A c x B ∧ is_feasible A b x → is_optimal A b c x := sorry

set_option profiler true

def ex.A : matrix (fin 2) (fin 4) ℚ :=
λ i j, (list.nth_le [[1,0,1,0], [1,2,0,1]] i sorry).nth_le j sorry

def ex.B : finset (fin 4) := {2,3}

def ex.c : cvec 4 := λ i _, (list.nth_le [1,1,0,0] i sorry)

def ex.b : cvec 2 := λ i _, (list.nth_le [2,4] i sorry)

def ex.x : cvec 4 := λ i _, (list.nth_le [0,0,2,4] i sorry)

--#reduce (is_feasible ex.A ex.b ex.x : bool)

--set_option trace.class_instances true
--#reduce (check_reduced_cost ex.A ex.c ex.x ex.B : bool)

def ex2.A : matrix (fin 2) (fin 4) ℚ :=
  λ i j, (list.nth_le [[1,0,1,0], [1,2,0,1]] i sorry).nth_le j sorry

def ex2.B : finset (fin 4) := {0,1}

def ex2.c : cvec 4 := λ i _, (list.nth_le [1,1,0,0] i sorry)

def ex2.b : cvec 2 := λ i _, (list.nth_le [2,4] i sorry)

def ex2.x : cvec 4 := λ i _, (list.nth_le [2,1,0,0] i sorry)

#eval (is_feasible ex2.A ex2.b ex2.x : bool)

#eval (check_reduced_cost ex2.A ex2.c ex2.x ex2.B : bool)