/-
Copyright (c) 2022 Kexing Ying. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kexing Ying

! This file was ported from Lean 3 source module measure_theory.function.uniform_integrable
! leanprover-community/mathlib commit 57ac39bd365c2f80589a700f9fbb664d3a1a30c2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.MeasureTheory.Function.ConvergenceInMeasure
import Mathbin.MeasureTheory.Function.L1Space

/-!
# Uniform integrability

This file contains the definitions for uniform integrability (both in the measure theory sense
as well as the probability theory sense). This file also contains the Vitali convergence theorem
which estabishes a relation between uniform integrability, convergence in measure and
Lp convergence.

Uniform integrability plays a vital role in the theory of martingales most notably is used to
fomulate the martingale convergence theorem.

## Main definitions

* `measure_theory.unif_integrable`: uniform integrability in the measure theory sense.
  In particular, a sequence of functions `f` is uniformly integrable if for all `ε > 0`, there
  exists some `δ > 0` such that for all sets `s` of smaller measure than `δ`, the Lp-norm of
  `f i` restricted `s` is smaller than `ε` for all `i`.
* `measure_theory.uniform_integrable`: uniform integrability in the probability theory sense.
  In particular, a sequence of measurable functions `f` is uniformly integrable in the
  probability theory sense if it is uniformly integrable in the measure theory sense and
  has uniformly bounded Lp-norm.

# Main results

* `measure_theory.unif_integrable_fintype`: a finite sequence of Lp functions is uniformly
  integrable.
* `measure_theory.tendsto_Lp_of_tendsto_ae`: a sequence of Lp functions which is uniformly
  integrable converges in Lp if they converge almost everywhere.
* `measure_theory.tendsto_in_measure_iff_tendsto_Lp`: Vitali convergence theorem:
  a sequence of Lp functions converges in Lp if and only if it is uniformly integrable
  and converges in measure.

## Tags
uniform integrable, uniformly absolutely continuous integral, Vitali convergence theorem
-/

noncomputable section

open Classical MeasureTheory NNReal ENNReal Topology BigOperators

namespace MeasureTheory

open Set Filter TopologicalSpace

variable {α β ι : Type _} {m : MeasurableSpace α} {μ : Measure α} [NormedAddCommGroup β]

/-- Uniform integrability in the measure theory sense.

A sequence of functions `f` is said to be uniformly integrable if for all `ε > 0`, there exists
some `δ > 0` such that for all sets `s` with measure less than `δ`, the Lp-norm of `f i`
restricted on `s` is less than `ε`.

Uniform integrablility is also known as uniformly absolutely continuous integrals. -/
def UnifIntegrable {m : MeasurableSpace α} (f : ι → α → β) (p : ℝ≥0∞) (μ : Measure α) : Prop :=
  ∀ ⦃ε : ℝ⦄ (hε : 0 < ε),
    ∃ (δ : ℝ)(hδ : 0 < δ),
      ∀ i s,
        MeasurableSet s → μ s ≤ ENNReal.ofReal δ → snorm (s.indicator (f i)) p μ ≤ ENNReal.ofReal ε
#align measure_theory.unif_integrable MeasureTheory.UnifIntegrable

/-- In probability theory, a family of measurable functions is uniformly integrable if it is
uniformly integrable in the measure theory sense and is uniformly bounded. -/
def UniformIntegrable {m : MeasurableSpace α} (f : ι → α → β) (p : ℝ≥0∞) (μ : Measure α) : Prop :=
  (∀ i, AeStronglyMeasurable (f i) μ) ∧ UnifIntegrable f p μ ∧ ∃ C : ℝ≥0, ∀ i, snorm (f i) p μ ≤ C
#align measure_theory.uniform_integrable MeasureTheory.UniformIntegrable

namespace UniformIntegrable

protected theorem aeStronglyMeasurable {f : ι → α → β} {p : ℝ≥0∞} (hf : UniformIntegrable f p μ)
    (i : ι) : AeStronglyMeasurable (f i) μ :=
  hf.1 i
#align measure_theory.uniform_integrable.ae_strongly_measurable MeasureTheory.UniformIntegrable.aeStronglyMeasurable

protected theorem unifIntegrable {f : ι → α → β} {p : ℝ≥0∞} (hf : UniformIntegrable f p μ) :
    UnifIntegrable f p μ :=
  hf.2.1
#align measure_theory.uniform_integrable.unif_integrable MeasureTheory.UniformIntegrable.unifIntegrable

protected theorem memℒp {f : ι → α → β} {p : ℝ≥0∞} (hf : UniformIntegrable f p μ) (i : ι) :
    Memℒp (f i) p μ :=
  ⟨hf.1 i,
    let ⟨_, _, hC⟩ := hf.2
    lt_of_le_of_lt (hC i) ENNReal.coe_lt_top⟩
#align measure_theory.uniform_integrable.mem_ℒp MeasureTheory.UniformIntegrable.memℒp

end UniformIntegrable

section UnifIntegrable

/-! ### `unif_integrable`

This section deals with uniform integrability in the measure theory sense. -/

namespace UnifIntegrable

variable {f g : ι → α → β} {p : ℝ≥0∞}

protected theorem add (hf : UnifIntegrable f p μ) (hg : UnifIntegrable g p μ) (hp : 1 ≤ p)
    (hf_meas : ∀ i, AeStronglyMeasurable (f i) μ) (hg_meas : ∀ i, AeStronglyMeasurable (g i) μ) :
    UnifIntegrable (f + g) p μ := by
  intro ε hε
  have hε2 : 0 < ε / 2 := half_pos hε
  obtain ⟨δ₁, hδ₁_pos, hfδ₁⟩ := hf hε2
  obtain ⟨δ₂, hδ₂_pos, hgδ₂⟩ := hg hε2
  refine' ⟨min δ₁ δ₂, lt_min hδ₁_pos hδ₂_pos, fun i s hs hμs => _⟩
  simp_rw [Pi.add_apply, indicator_add']
  refine' (snorm_add_le ((hf_meas i).indicator hs) ((hg_meas i).indicator hs) hp).trans _
  have hε_halves : ENNReal.ofReal ε = ENNReal.ofReal (ε / 2) + ENNReal.ofReal (ε / 2) := by
    rw [← ENNReal.ofReal_add hε2.le hε2.le, add_halves]
  rw [hε_halves]
  exact
    add_le_add (hfδ₁ i s hs (hμs.trans (ENNReal.ofReal_le_ofReal (min_le_left _ _))))
      (hgδ₂ i s hs (hμs.trans (ENNReal.ofReal_le_ofReal (min_le_right _ _))))
#align measure_theory.unif_integrable.add MeasureTheory.UnifIntegrable.add

protected theorem neg (hf : UnifIntegrable f p μ) : UnifIntegrable (-f) p μ :=
  by
  simp_rw [unif_integrable, Pi.neg_apply, indicator_neg', snorm_neg]
  exact hf
#align measure_theory.unif_integrable.neg MeasureTheory.UnifIntegrable.neg

protected theorem sub (hf : UnifIntegrable f p μ) (hg : UnifIntegrable g p μ) (hp : 1 ≤ p)
    (hf_meas : ∀ i, AeStronglyMeasurable (f i) μ) (hg_meas : ∀ i, AeStronglyMeasurable (g i) μ) :
    UnifIntegrable (f - g) p μ := by
  rw [sub_eq_add_neg]
  exact hf.add hg.neg hp hf_meas fun i => (hg_meas i).neg
#align measure_theory.unif_integrable.sub MeasureTheory.UnifIntegrable.sub

protected theorem aeEq (hf : UnifIntegrable f p μ) (hfg : ∀ n, f n =ᵐ[μ] g n) :
    UnifIntegrable g p μ := by
  intro ε hε
  obtain ⟨δ, hδ_pos, hfδ⟩ := hf hε
  refine' ⟨δ, hδ_pos, fun n s hs hμs => (le_of_eq <| snorm_congr_ae _).trans (hfδ n s hs hμs)⟩
  filter_upwards [hfg n]with x hx
  simp_rw [indicator_apply, hx]
#align measure_theory.unif_integrable.ae_eq MeasureTheory.UnifIntegrable.aeEq

end UnifIntegrable

theorem unifIntegrableZeroMeas [MeasurableSpace α] {p : ℝ≥0∞} {f : ι → α → β} :
    UnifIntegrable f p (0 : Measure α) := fun ε hε => ⟨1, one_pos, fun i s hs hμs => by simp⟩
#align measure_theory.unif_integrable_zero_meas MeasureTheory.unifIntegrableZeroMeas

theorem unifIntegrable_congr_ae {p : ℝ≥0∞} {f g : ι → α → β} (hfg : ∀ n, f n =ᵐ[μ] g n) :
    UnifIntegrable f p μ ↔ UnifIntegrable g p μ :=
  ⟨fun hf => hf.ae_eq hfg, fun hg => hg.ae_eq fun n => (hfg n).symm⟩
#align measure_theory.unif_integrable_congr_ae MeasureTheory.unifIntegrable_congr_ae

theorem tendsto_indicator_ge (f : α → β) (x : α) :
    Tendsto (fun M : ℕ => { x | (M : ℝ) ≤ ‖f x‖₊ }.indicator f x) atTop (𝓝 0) :=
  by
  refine' @tendsto_atTop_of_eventually_const _ _ _ _ _ _ _ (Nat.ceil (‖f x‖₊ : ℝ) + 1) fun n hn => _
  rw [indicator_of_not_mem]
  simp only [not_le, mem_set_of_eq]
  refine' lt_of_le_of_lt (Nat.le_ceil _) _
  refine' lt_of_lt_of_le (lt_add_one _) _
  norm_cast
  rwa [ge_iff_le, coe_nnnorm] at hn
#align measure_theory.tendsto_indicator_ge MeasureTheory.tendsto_indicator_ge

variable (μ) {p : ℝ≥0∞}

section

variable {f : α → β}

/-- This lemma is weaker than `measure_theory.mem_ℒp.integral_indicator_norm_ge_nonneg_le`
as the latter provides `0 ≤ M` and does not require the measurability of `f`. -/
theorem Memℒp.integral_indicator_norm_ge_le (hf : Memℒp f 1 μ) (hmeas : StronglyMeasurable f)
    {ε : ℝ} (hε : 0 < ε) :
    ∃ M : ℝ, (∫⁻ x, ‖{ x | M ≤ ‖f x‖₊ }.indicator f x‖₊ ∂μ) ≤ ENNReal.ofReal ε :=
  by
  have htendsto :
    ∀ᵐ x ∂μ, tendsto (fun M : ℕ => { x | (M : ℝ) ≤ ‖f x‖₊ }.indicator f x) at_top (𝓝 0) :=
    univ_mem' (id fun x => tendsto_indicator_ge f x)
  have hmeas : ∀ M : ℕ, ae_strongly_measurable ({ x | (M : ℝ) ≤ ‖f x‖₊ }.indicator f) μ :=
    by
    intro M
    apply hf.1.indicator
    apply
      strongly_measurable.measurable_set_le strongly_measurable_const
        hmeas.nnnorm.measurable.coe_nnreal_real.strongly_measurable
  have hbound : has_finite_integral (fun x => ‖f x‖) μ :=
    by
    rw [mem_ℒp_one_iff_integrable] at hf
    exact hf.norm.2
  have := tendsto_lintegral_norm_of_dominated_convergence hmeas hbound _ htendsto
  · rw [ENNReal.tendsto_atTop_zero] at this
    obtain ⟨M, hM⟩ := this (ENNReal.ofReal ε) (ENNReal.ofReal_pos.2 hε)
    simp only [true_and_iff, ge_iff_le, zero_tsub, zero_le, sub_zero, zero_add, coe_nnnorm,
      mem_Icc] at hM
    refine' ⟨M, _⟩
    convert hM M le_rfl
    ext1 x
    simp only [coe_nnnorm, ENNReal.ofReal_eq_coe_nnreal (norm_nonneg _)]
    rfl
  · refine' fun n => univ_mem' (id fun x => _)
    by_cases hx : (n : ℝ) ≤ ‖f x‖
    · dsimp
      rwa [indicator_of_mem]
    · dsimp
      rw [indicator_of_not_mem, norm_zero]
      · exact norm_nonneg _
      · assumption
#align measure_theory.mem_ℒp.integral_indicator_norm_ge_le MeasureTheory.Memℒp.integral_indicator_norm_ge_le

/-- This lemma is superceded by `measure_theory.mem_ℒp.integral_indicator_norm_ge_nonneg_le`
which does not require measurability. -/
theorem Memℒp.integral_indicator_norm_ge_nonneg_le_of_meas (hf : Memℒp f 1 μ)
    (hmeas : StronglyMeasurable f) {ε : ℝ} (hε : 0 < ε) :
    ∃ M : ℝ, 0 ≤ M ∧ (∫⁻ x, ‖{ x | M ≤ ‖f x‖₊ }.indicator f x‖₊ ∂μ) ≤ ENNReal.ofReal ε :=
  let ⟨M, hM⟩ := hf.integral_indicator_norm_ge_le μ hmeas hε
  ⟨max M 0, le_max_right _ _, by simpa⟩
#align measure_theory.mem_ℒp.integral_indicator_norm_ge_nonneg_le_of_meas MeasureTheory.Memℒp.integral_indicator_norm_ge_nonneg_le_of_meas

theorem Memℒp.integral_indicator_norm_ge_nonneg_le (hf : Memℒp f 1 μ) {ε : ℝ} (hε : 0 < ε) :
    ∃ M : ℝ, 0 ≤ M ∧ (∫⁻ x, ‖{ x | M ≤ ‖f x‖₊ }.indicator f x‖₊ ∂μ) ≤ ENNReal.ofReal ε :=
  by
  have hf_mk : mem_ℒp (hf.1.mk f) 1 μ := (mem_ℒp_congr_ae hf.1.ae_eq_mk).mp hf
  obtain ⟨M, hM_pos, hfM⟩ :=
    hf_mk.integral_indicator_norm_ge_nonneg_le_of_meas μ hf.1.stronglyMeasurable_mk hε
  refine' ⟨M, hM_pos, (le_of_eq _).trans hfM⟩
  refine' lintegral_congr_ae _
  filter_upwards [hf.1.ae_eq_mk]with x hx
  simp only [indicator_apply, coe_nnnorm, mem_set_of_eq, ENNReal.coe_eq_coe, hx.symm]
#align measure_theory.mem_ℒp.integral_indicator_norm_ge_nonneg_le MeasureTheory.Memℒp.integral_indicator_norm_ge_nonneg_le

theorem Memℒp.snormEssSup_indicator_norm_ge_eq_zero (hf : Memℒp f ∞ μ)
    (hmeas : StronglyMeasurable f) : ∃ M : ℝ, snormEssSup ({ x | M ≤ ‖f x‖₊ }.indicator f) μ = 0 :=
  by
  have hbdd : snorm_ess_sup f μ < ∞ := hf.snorm_lt_top
  refine' ⟨(snorm f ∞ μ + 1).toReal, _⟩
  rw [snorm_ess_sup_indicator_eq_snorm_ess_sup_restrict]
  have : μ.restrict { x : α | (snorm f ⊤ μ + 1).toReal ≤ ‖f x‖₊ } = 0 :=
    by
    simp only [coe_nnnorm, snorm_exponent_top, measure.restrict_eq_zero]
    have :
      { x : α | (snorm_ess_sup f μ + 1).toReal ≤ ‖f x‖ } ⊆ { x : α | snorm_ess_sup f μ < ‖f x‖₊ } :=
      by
      intro x hx
      rw [mem_set_of_eq, ← ENNReal.toReal_lt_toReal hbdd.ne ennreal.coe_lt_top.ne,
        ENNReal.coe_toReal, coe_nnnorm]
      refine' lt_of_lt_of_le _ hx
      rw [ENNReal.toReal_lt_toReal hbdd.ne]
      · exact ENNReal.lt_add_right hbdd.ne one_ne_zero
      · exact (ENNReal.add_lt_top.2 ⟨hbdd, ENNReal.one_lt_top⟩).Ne
    rw [← nonpos_iff_eq_zero]
    refine' (measure_mono this).trans _
    have hle := coe_nnnorm_ae_le_snorm_ess_sup f μ
    simp_rw [ae_iff, not_le] at hle
    exact nonpos_iff_eq_zero.2 hle
  rw [this, snorm_ess_sup_measure_zero]
  exact measurableSet_le measurable_const hmeas.nnnorm.measurable.subtype_coe
#align measure_theory.mem_ℒp.snorm_ess_sup_indicator_norm_ge_eq_zero MeasureTheory.Memℒp.snormEssSup_indicator_norm_ge_eq_zero

/- This lemma is slightly weaker than `measure_theory.mem_ℒp.snorm_indicator_norm_ge_pos_le` as the
latter provides `0 < M`. -/
theorem Memℒp.snorm_indicator_norm_ge_le (hf : Memℒp f p μ) (hmeas : StronglyMeasurable f) {ε : ℝ}
    (hε : 0 < ε) : ∃ M : ℝ, snorm ({ x | M ≤ ‖f x‖₊ }.indicator f) p μ ≤ ENNReal.ofReal ε :=
  by
  by_cases hp_ne_zero : p = 0
  · refine' ⟨1, hp_ne_zero.symm ▸ _⟩
    simp [snorm_exponent_zero]
  by_cases hp_ne_top : p = ∞
  · subst hp_ne_top
    obtain ⟨M, hM⟩ := hf.snorm_ess_sup_indicator_norm_ge_eq_zero μ hmeas
    refine' ⟨M, _⟩
    simp only [snorm_exponent_top, hM, zero_le]
  obtain ⟨M, hM', hM⟩ :=
    @mem_ℒp.integral_indicator_norm_ge_nonneg_le _ _ _ μ _ (fun x => ‖f x‖ ^ p.to_real)
      (hf.norm_rpow hp_ne_zero hp_ne_top) _ (Real.rpow_pos_of_pos hε p.to_real)
  refine' ⟨M ^ (1 / p.to_real), _⟩
  rw [snorm_eq_lintegral_rpow_nnnorm hp_ne_zero hp_ne_top, ← ENNReal.rpow_one (ENNReal.ofReal ε)]
  conv_rhs => rw [← mul_one_div_cancel (ENNReal.toReal_pos hp_ne_zero hp_ne_top).Ne.symm]
  rw [ENNReal.rpow_mul,
    ENNReal.rpow_le_rpow_iff (one_div_pos.2 <| ENNReal.toReal_pos hp_ne_zero hp_ne_top),
    ENNReal.ofReal_rpow_of_pos hε]
  convert hM
  ext1 x
  rw [ENNReal.coe_rpow_of_nonneg _ ENNReal.toReal_nonneg, nnnorm_indicator_eq_indicator_nnnorm,
    nnnorm_indicator_eq_indicator_nnnorm]
  have hiff : M ^ (1 / p.to_real) ≤ ‖f x‖₊ ↔ M ≤ ‖‖f x‖ ^ p.to_real‖₊ := by
    rw [coe_nnnorm, coe_nnnorm, Real.norm_rpow_of_nonneg (norm_nonneg _), norm_norm, ←
      Real.rpow_le_rpow_iff hM' (Real.rpow_nonneg_of_nonneg (norm_nonneg _) _)
        (one_div_pos.2 <| ENNReal.toReal_pos hp_ne_zero hp_ne_top),
      ← Real.rpow_mul (norm_nonneg _),
      mul_one_div_cancel (ENNReal.toReal_pos hp_ne_zero hp_ne_top).Ne.symm, Real.rpow_one]
  by_cases hx : x ∈ { x : α | M ^ (1 / p.to_real) ≤ ‖f x‖₊ }
  · rw [Set.indicator_of_mem hx, Set.indicator_of_mem, Real.nnnorm_of_nonneg]
    rfl
    change _ ≤ _
    rwa [← hiff]
  · rw [Set.indicator_of_not_mem hx, Set.indicator_of_not_mem]
    · simp [(ENNReal.toReal_pos hp_ne_zero hp_ne_top).Ne.symm]
    · change ¬_ ≤ _
      rwa [← hiff]
#align measure_theory.mem_ℒp.snorm_indicator_norm_ge_le MeasureTheory.Memℒp.snorm_indicator_norm_ge_le

/-- This lemma implies that a single function is uniformly integrable (in the probability sense). -/
theorem Memℒp.snorm_indicator_norm_ge_pos_le (hf : Memℒp f p μ) (hmeas : StronglyMeasurable f)
    {ε : ℝ} (hε : 0 < ε) :
    ∃ M : ℝ, 0 < M ∧ snorm ({ x | M ≤ ‖f x‖₊ }.indicator f) p μ ≤ ENNReal.ofReal ε :=
  by
  obtain ⟨M, hM⟩ := hf.snorm_indicator_norm_ge_le μ hmeas hε
  refine'
    ⟨max M 1, lt_of_lt_of_le zero_lt_one (le_max_right _ _), le_trans (snorm_mono fun x => _) hM⟩
  rw [norm_indicator_eq_indicator_norm, norm_indicator_eq_indicator_norm]
  refine' indicator_le_indicator_of_subset (fun x hx => _) (fun x => norm_nonneg _) x
  change max _ _ ≤ _ at hx
  -- removing the `change` breaks the proof!
  exact (max_le_iff.1 hx).1
#align measure_theory.mem_ℒp.snorm_indicator_norm_ge_pos_le MeasureTheory.Memℒp.snorm_indicator_norm_ge_pos_le

end

/- ./././Mathport/Syntax/Translate/Tactic/Builtin.lean:72:38: in filter_upwards #[[], [], []]: ./././Mathport/Syntax/Translate/Basic.lean:349:22: unsupported: parse error @ arg 0: next failed, no more args -/
theorem snorm_indicator_le_of_bound {f : α → β} (hp_top : p ≠ ∞) {ε : ℝ} (hε : 0 < ε) {M : ℝ}
    (hf : ∀ x, ‖f x‖ < M) :
    ∃ (δ : ℝ)(hδ : 0 < δ),
      ∀ s,
        MeasurableSet s → μ s ≤ ENNReal.ofReal δ → snorm (s.indicator f) p μ ≤ ENNReal.ofReal ε :=
  by
  by_cases hM : M ≤ 0
  · refine' ⟨1, zero_lt_one, fun s hs hμ => _⟩
    rw [(_ : f = 0)]
    · simp [hε.le]
    · ext x
      rw [Pi.zero_apply, ← norm_le_zero_iff]
      exact (lt_of_lt_of_le (hf x) hM).le
  rw [not_le] at hM
  refine' ⟨(ε / M) ^ p.to_real, Real.rpow_pos_of_pos (div_pos hε hM) _, fun s hs hμ => _⟩
  by_cases hp : p = 0
  · simp [hp]
  rw [snorm_indicator_eq_snorm_restrict hs]
  have haebdd : ∀ᵐ x ∂μ.restrict s, ‖f x‖ ≤ M :=
    by
    trace
      "./././Mathport/Syntax/Translate/Tactic/Builtin.lean:72:38: in filter_upwards #[[], [], []]: ./././Mathport/Syntax/Translate/Basic.lean:349:22: unsupported: parse error @ arg 0: next failed, no more args"
    exact fun x => (hf x).le
  refine' le_trans (snorm_le_of_ae_bound haebdd) _
  rw [measure.restrict_apply MeasurableSet.univ, univ_inter, ←
    ENNReal.le_div_iff_mul_le (Or.inl _) (Or.inl ENNReal.ofReal_ne_top)]
  · rw [← one_div, ENNReal.rpow_one_div_le_iff (ENNReal.toReal_pos hp hp_top)]
    refine' le_trans hμ _
    rw [← ENNReal.ofReal_rpow_of_pos (div_pos hε hM),
      ENNReal.rpow_le_rpow_iff (ENNReal.toReal_pos hp hp_top), ENNReal.ofReal_div_of_pos hM]
    exact le_rfl
  · simpa only [ENNReal.ofReal_eq_zero, not_le, Ne.def]
#align measure_theory.snorm_indicator_le_of_bound MeasureTheory.snorm_indicator_le_of_bound

section

variable {f : α → β}

/-- Auxiliary lemma for `measure_theory.mem_ℒp.snorm_indicator_le`. -/
theorem Memℒp.snorm_indicator_le' (hp_one : 1 ≤ p) (hp_top : p ≠ ∞) (hf : Memℒp f p μ)
    (hmeas : StronglyMeasurable f) {ε : ℝ} (hε : 0 < ε) :
    ∃ (δ : ℝ)(hδ : 0 < δ),
      ∀ s,
        MeasurableSet s →
          μ s ≤ ENNReal.ofReal δ → snorm (s.indicator f) p μ ≤ 2 * ENNReal.ofReal ε :=
  by
  obtain ⟨M, hMpos, hM⟩ := hf.snorm_indicator_norm_ge_pos_le μ hmeas hε
  obtain ⟨δ, hδpos, hδ⟩ :=
    @snorm_indicator_le_of_bound _ _ _ μ _ _ ({ x | ‖f x‖ < M }.indicator f) hp_top _ hε M _
  · refine' ⟨δ, hδpos, fun s hs hμs => _⟩
    rw [(_ : f = { x : α | M ≤ ‖f x‖₊ }.indicator f + { x : α | ‖f x‖ < M }.indicator f)]
    · rw [snorm_indicator_eq_snorm_restrict hs]
      refine' le_trans (snorm_add_le _ _ hp_one) _
      ·
        exact
          strongly_measurable.ae_strongly_measurable
            (hmeas.indicator
              (measurableSet_le measurable_const hmeas.nnnorm.measurable.subtype_coe))
      ·
        exact
          strongly_measurable.ae_strongly_measurable
            (hmeas.indicator
              (measurableSet_lt hmeas.nnnorm.measurable.subtype_coe measurable_const))
      · rw [two_mul]
        refine' add_le_add (le_trans (snorm_mono_measure _ measure.restrict_le_self) hM) _
        rw [← snorm_indicator_eq_snorm_restrict hs]
        exact hδ s hs hμs
    · ext x
      by_cases hx : M ≤ ‖f x‖
      · rw [Pi.add_apply, indicator_of_mem, indicator_of_not_mem, add_zero] <;> simpa
      · rw [Pi.add_apply, indicator_of_not_mem, indicator_of_mem, zero_add] <;> simpa using hx
  · intro x
    rw [norm_indicator_eq_indicator_norm, indicator_apply]
    split_ifs
    exacts[h, hMpos]
#align measure_theory.mem_ℒp.snorm_indicator_le' MeasureTheory.Memℒp.snorm_indicator_le'

/-- This lemma is superceded by `measure_theory.mem_ℒp.snorm_indicator_le` which does not require
measurability on `f`. -/
theorem Memℒp.snorm_indicator_le_of_meas (hp_one : 1 ≤ p) (hp_top : p ≠ ∞) (hf : Memℒp f p μ)
    (hmeas : StronglyMeasurable f) {ε : ℝ} (hε : 0 < ε) :
    ∃ (δ : ℝ)(hδ : 0 < δ),
      ∀ s,
        MeasurableSet s → μ s ≤ ENNReal.ofReal δ → snorm (s.indicator f) p μ ≤ ENNReal.ofReal ε :=
  by
  obtain ⟨δ, hδpos, hδ⟩ := hf.snorm_indicator_le' μ hp_one hp_top hmeas (half_pos hε)
  refine' ⟨δ, hδpos, fun s hs hμs => le_trans (hδ s hs hμs) _⟩
  rw [ENNReal.ofReal_div_of_pos zero_lt_two, (by norm_num : ENNReal.ofReal 2 = 2),
      ENNReal.mul_div_cancel'] <;>
    norm_num
#align measure_theory.mem_ℒp.snorm_indicator_le_of_meas MeasureTheory.Memℒp.snorm_indicator_le_of_meas

theorem Memℒp.snorm_indicator_le (hp_one : 1 ≤ p) (hp_top : p ≠ ∞) (hf : Memℒp f p μ) {ε : ℝ}
    (hε : 0 < ε) :
    ∃ (δ : ℝ)(hδ : 0 < δ),
      ∀ s,
        MeasurableSet s → μ s ≤ ENNReal.ofReal δ → snorm (s.indicator f) p μ ≤ ENNReal.ofReal ε :=
  by
  have hℒp := hf
  obtain ⟨⟨f', hf', heq⟩, hnorm⟩ := hf
  obtain ⟨δ, hδpos, hδ⟩ := (hℒp.ae_eq HEq).snorm_indicator_le_of_meas μ hp_one hp_top hf' hε
  refine' ⟨δ, hδpos, fun s hs hμs => _⟩
  convert hδ s hs hμs using 1
  rw [snorm_indicator_eq_snorm_restrict hs, snorm_indicator_eq_snorm_restrict hs]
  refine' snorm_congr_ae heq.restrict
#align measure_theory.mem_ℒp.snorm_indicator_le MeasureTheory.Memℒp.snorm_indicator_le

/-- A constant function is uniformly integrable. -/
theorem unifIntegrableConst {g : α → β} (hp : 1 ≤ p) (hp_ne_top : p ≠ ∞) (hg : Memℒp g p μ) :
    UnifIntegrable (fun n : ι => g) p μ := by
  intro ε hε
  obtain ⟨δ, hδ_pos, hgδ⟩ := hg.snorm_indicator_le μ hp hp_ne_top hε
  exact ⟨δ, hδ_pos, fun i => hgδ⟩
#align measure_theory.unif_integrable_const MeasureTheory.unifIntegrableConst

/-- A single function is uniformly integrable. -/
theorem unifIntegrableSubsingleton [Subsingleton ι] (hp_one : 1 ≤ p) (hp_top : p ≠ ∞)
    {f : ι → α → β} (hf : ∀ i, Memℒp (f i) p μ) : UnifIntegrable f p μ :=
  by
  intro ε hε
  by_cases hι : Nonempty ι
  · cases' hι with i
    obtain ⟨δ, hδpos, hδ⟩ := (hf i).snorm_indicator_le μ hp_one hp_top hε
    refine' ⟨δ, hδpos, fun j s hs hμs => _⟩
    convert hδ s hs hμs
  · exact ⟨1, zero_lt_one, fun i => False.elim <| hι <| Nonempty.intro i⟩
#align measure_theory.unif_integrable_subsingleton MeasureTheory.unifIntegrableSubsingleton

/-- This lemma is less general than `measure_theory.unif_integrable_fintype` which applies to
all sequences indexed by a finite type. -/
theorem unifIntegrableFin (hp_one : 1 ≤ p) (hp_top : p ≠ ∞) {n : ℕ} {f : Fin n → α → β}
    (hf : ∀ i, Memℒp (f i) p μ) : UnifIntegrable f p μ :=
  by
  revert f
  induction' n with n h
  · exact fun f hf => unif_integrable_subsingleton μ hp_one hp_top hf
  intro f hfLp ε hε
  set g : Fin n → α → β := fun k => f k with hg
  have hgLp : ∀ i, mem_ℒp (g i) p μ := fun i => hfLp i
  obtain ⟨δ₁, hδ₁pos, hδ₁⟩ := h hgLp hε
  obtain ⟨δ₂, hδ₂pos, hδ₂⟩ := (hfLp n).snorm_indicator_le μ hp_one hp_top hε
  refine' ⟨min δ₁ δ₂, lt_min hδ₁pos hδ₂pos, fun i s hs hμs => _⟩
  by_cases hi : i.val < n
  · rw [(_ : f i = g ⟨i.val, hi⟩)]
    · exact hδ₁ _ s hs (le_trans hμs <| ENNReal.ofReal_le_ofReal <| min_le_left _ _)
    · rw [hg]
      simp
  · rw [(_ : i = n)]
    · exact hδ₂ _ hs (le_trans hμs <| ENNReal.ofReal_le_ofReal <| min_le_right _ _)
    · have hi' := Fin.is_lt i
      rw [Nat.lt_succ_iff] at hi'
      rw [not_lt] at hi
      simp [← le_antisymm hi' hi]
#align measure_theory.unif_integrable_fin MeasureTheory.unifIntegrableFin

/-- A finite sequence of Lp functions is uniformly integrable. -/
theorem unifIntegrableFinite [Finite ι] (hp_one : 1 ≤ p) (hp_top : p ≠ ∞) {f : ι → α → β}
    (hf : ∀ i, Memℒp (f i) p μ) : UnifIntegrable f p μ :=
  by
  obtain ⟨n, hn⟩ := Finite.exists_equiv_fin ι
  intro ε hε
  set g : Fin n → α → β := f ∘ hn.some.symm with hgeq
  have hg : ∀ i, mem_ℒp (g i) p μ := fun _ => hf _
  obtain ⟨δ, hδpos, hδ⟩ := unif_integrable_fin μ hp_one hp_top hg hε
  refine' ⟨δ, hδpos, fun i s hs hμs => _⟩
  specialize hδ (hn.some i) s hs hμs
  simp_rw [hgeq, Function.comp_apply, Equiv.symm_apply_apply] at hδ
  assumption
#align measure_theory.unif_integrable_finite MeasureTheory.unifIntegrableFinite

end

theorem snorm_sub_le_of_dist_bdd {p : ℝ≥0∞} (hp' : p ≠ ∞) {s : Set α} (hs : measurable_set[m] s)
    {f g : α → β} {c : ℝ} (hc : 0 ≤ c) (hf : ∀ x ∈ s, dist (f x) (g x) ≤ c) :
    snorm (s.indicator (f - g)) p μ ≤ ENNReal.ofReal c * μ s ^ (1 / p.toReal) :=
  by
  by_cases hp : p = 0
  · simp [hp]
  have : ∀ x, ‖s.indicator (f - g) x‖ ≤ ‖s.indicator (fun x => c) x‖ :=
    by
    intro x
    by_cases hx : x ∈ s
    · rw [indicator_of_mem hx, indicator_of_mem hx, Pi.sub_apply, ← dist_eq_norm, Real.norm_eq_abs,
        abs_of_nonneg hc]
      exact hf x hx
    · simp [indicator_of_not_mem hx]
  refine' le_trans (snorm_mono this) _
  rw [snorm_indicator_const hs hp hp']
  refine' mul_le_mul_right' (le_of_eq _) _
  rw [← ofReal_norm_eq_coe_nnnorm, Real.norm_eq_abs, abs_of_nonneg hc]
#align measure_theory.snorm_sub_le_of_dist_bdd MeasureTheory.snorm_sub_le_of_dist_bdd

/-- A sequence of uniformly integrable functions which converges μ-a.e. converges in Lp. -/
theorem tendsto_Lp_of_tendsto_ae_of_meas [IsFiniteMeasure μ] (hp : 1 ≤ p) (hp' : p ≠ ∞)
    {f : ℕ → α → β} {g : α → β} (hf : ∀ n, StronglyMeasurable (f n)) (hg : StronglyMeasurable g)
    (hg' : Memℒp g p μ) (hui : UnifIntegrable f p μ)
    (hfg : ∀ᵐ x ∂μ, Tendsto (fun n => f n x) atTop (𝓝 (g x))) :
    Tendsto (fun n => snorm (f n - g) p μ) atTop (𝓝 0) :=
  by
  rw [ENNReal.tendsto_atTop_zero]
  intro ε hε
  by_cases ε < ∞
  swap
  · rw [not_lt, top_le_iff] at h
    exact ⟨0, fun n hn => by simp [h]⟩
  by_cases hμ : μ = 0
  · exact ⟨0, fun n hn => by simp [hμ]⟩
  have hε' : 0 < ε.to_real / 3 :=
    div_pos (ENNReal.toReal_pos (gt_iff_lt.1 hε).Ne.symm h.ne) (by norm_num)
  have hdivp : 0 ≤ 1 / p.to_real := by
    refine' one_div_nonneg.2 _
    rw [← ENNReal.zero_toReal, ENNReal.toReal_le_toReal ENNReal.zero_ne_top hp']
    exact le_trans (zero_le _) hp
  have hpow : 0 < measure_univ_nnreal μ ^ (1 / p.to_real) :=
    Real.rpow_pos_of_pos (measure_univ_nnreal_pos hμ) _
  obtain ⟨δ₁, hδ₁, hsnorm₁⟩ := hui hε'
  obtain ⟨δ₂, hδ₂, hsnorm₂⟩ := hg'.snorm_indicator_le μ hp hp' hε'
  obtain ⟨t, htm, ht₁, ht₂⟩ := tendsto_uniformly_on_of_ae_tendsto' hf hg hfg (lt_min hδ₁ hδ₂)
  rw [Metric.tendstoUniformlyOn_iff] at ht₂
  specialize
    ht₂ (ε.to_real / (3 * measure_univ_nnreal μ ^ (1 / p.to_real)))
      (div_pos (ENNReal.toReal_pos (gt_iff_lt.1 hε).Ne.symm h.ne) (mul_pos (by norm_num) hpow))
  obtain ⟨N, hN⟩ := eventually_at_top.1 ht₂
  clear ht₂
  refine' ⟨N, fun n hn => _⟩
  rw [← t.indicator_self_add_compl (f n - g)]
  refine'
    le_trans
      (snorm_add_le (((hf n).sub hg).indicator htm).AeStronglyMeasurable
        (((hf n).sub hg).indicator htm.compl).AeStronglyMeasurable hp)
      _
  rw [sub_eq_add_neg, indicator_add' t, indicator_neg']
  refine'
    le_trans
      (add_le_add_right
        (snorm_add_le ((hf n).indicator htm).AeStronglyMeasurable
          (hg.indicator htm).neg.AeStronglyMeasurable hp)
        _)
      _
  have hnf : snorm (t.indicator (f n)) p μ ≤ ENNReal.ofReal (ε.to_real / 3) :=
    by
    refine' hsnorm₁ n t htm (le_trans ht₁ _)
    rw [ENNReal.ofReal_le_ofReal_iff hδ₁.le]
    exact min_le_left _ _
  have hng : snorm (t.indicator g) p μ ≤ ENNReal.ofReal (ε.to_real / 3) :=
    by
    refine' hsnorm₂ t htm (le_trans ht₁ _)
    rw [ENNReal.ofReal_le_ofReal_iff hδ₂.le]
    exact min_le_right _ _
  have hlt : snorm (tᶜ.indicator (f n - g)) p μ ≤ ENNReal.ofReal (ε.to_real / 3) :=
    by
    specialize hN n hn
    have :=
      snorm_sub_le_of_dist_bdd μ hp' htm.compl _ fun x hx =>
        (dist_comm (g x) (f n x) ▸ (hN x hx).le :
          dist (f n x) (g x) ≤ ε.to_real / (3 * measure_univ_nnreal μ ^ (1 / p.to_real)))
    refine' le_trans this _
    rw [div_mul_eq_div_mul_one_div, ← ENNReal.ofReal_toReal (measure_lt_top μ (tᶜ)).Ne,
      ENNReal.ofReal_rpow_of_nonneg ENNReal.toReal_nonneg hdivp, ← ENNReal.ofReal_mul, mul_assoc]
    · refine' ENNReal.ofReal_le_ofReal (mul_le_of_le_one_right hε'.le _)
      rw [mul_comm, mul_one_div, div_le_one]
      · refine'
          Real.rpow_le_rpow ENNReal.toReal_nonneg
            (ENNReal.toReal_le_of_le_ofReal (measure_univ_nnreal_pos hμ).le _) hdivp
        rw [ENNReal.ofReal_coe_nnreal, coe_measure_univ_nnreal]
        exact measure_mono (subset_univ _)
      · exact Real.rpow_pos_of_pos (measure_univ_nnreal_pos hμ) _
    · refine' mul_nonneg hε'.le (one_div_nonneg.2 hpow.le)
    · rw [div_mul_eq_div_mul_one_div]
      exact mul_nonneg hε'.le (one_div_nonneg.2 hpow.le)
  have : ENNReal.ofReal (ε.to_real / 3) = ε / 3 :=
    by
    rw [ENNReal.ofReal_div_of_pos (show (0 : ℝ) < 3 by norm_num), ENNReal.ofReal_toReal h.ne]
    simp
  rw [this] at hnf hng hlt
  rw [snorm_neg, ← ENNReal.add_thirds ε, ← sub_eq_add_neg]
  exact add_le_add_three hnf hng hlt
#align measure_theory.tendsto_Lp_of_tendsto_ae_of_meas MeasureTheory.tendsto_Lp_of_tendsto_ae_of_meas

/-- A sequence of uniformly integrable functions which converges μ-a.e. converges in Lp. -/
theorem tendsto_Lp_of_tendsto_ae [IsFiniteMeasure μ] (hp : 1 ≤ p) (hp' : p ≠ ∞) {f : ℕ → α → β}
    {g : α → β} (hf : ∀ n, AeStronglyMeasurable (f n) μ) (hg : Memℒp g p μ)
    (hui : UnifIntegrable f p μ) (hfg : ∀ᵐ x ∂μ, Tendsto (fun n => f n x) atTop (𝓝 (g x))) :
    Tendsto (fun n => snorm (f n - g) p μ) atTop (𝓝 0) :=
  by
  suffices tendsto (fun n : ℕ => snorm ((hf n).mk (f n) - hg.1.mk g) p μ) at_top (𝓝 0)
    by
    convert this
    exact funext fun n => snorm_congr_ae ((hf n).ae_eq_mk.sub hg.1.ae_eq_mk)
  refine'
    tendsto_Lp_of_tendsto_ae_of_meas μ hp hp' (fun n => (hf n).stronglyMeasurable_mk)
      hg.1.stronglyMeasurable_mk (hg.ae_eq hg.1.ae_eq_mk) (hui.ae_eq fun n => (hf n).ae_eq_mk) _
  have h_ae_forall_eq : ∀ᵐ x ∂μ, ∀ n, f n x = (hf n).mk (f n) x :=
    by
    rw [ae_all_iff]
    exact fun n => (hf n).ae_eq_mk
  filter_upwards [hfg, h_ae_forall_eq, hg.1.ae_eq_mk]with x hx_tendsto hxf_eq hxg_eq
  rw [← hxg_eq]
  convert hx_tendsto
  ext1 n
  exact (hxf_eq n).symm
#align measure_theory.tendsto_Lp_of_tendsto_ae MeasureTheory.tendsto_Lp_of_tendsto_ae

variable {f : ℕ → α → β} {g : α → β}

theorem unifIntegrableOfTendstoLpZero (hp : 1 ≤ p) (hp' : p ≠ ∞) (hf : ∀ n, Memℒp (f n) p μ)
    (hf_tendsto : Tendsto (fun n => snorm (f n) p μ) atTop (𝓝 0)) : UnifIntegrable f p μ :=
  by
  intro ε hε
  rw [ENNReal.tendsto_atTop_zero] at hf_tendsto
  obtain ⟨N, hN⟩ := hf_tendsto (ENNReal.ofReal ε) (by simpa)
  set F : Fin N → α → β := fun n => f n
  have hF : ∀ n, mem_ℒp (F n) p μ := fun n => hf n
  obtain ⟨δ₁, hδpos₁, hδ₁⟩ := unif_integrable_fin μ hp hp' hF hε
  refine' ⟨δ₁, hδpos₁, fun n s hs hμs => _⟩
  by_cases hn : n < N
  · exact hδ₁ ⟨n, hn⟩ s hs hμs
  · exact (snorm_indicator_le _).trans (hN n (not_lt.1 hn))
#align measure_theory.unif_integrable_of_tendsto_Lp_zero MeasureTheory.unifIntegrableOfTendstoLpZero

/-- Convergence in Lp implies uniform integrability. -/
theorem unifIntegrableOfTendstoLp (hp : 1 ≤ p) (hp' : p ≠ ∞) (hf : ∀ n, Memℒp (f n) p μ)
    (hg : Memℒp g p μ) (hfg : Tendsto (fun n => snorm (f n - g) p μ) atTop (𝓝 0)) :
    UnifIntegrable f p μ :=
  by
  have : f = (fun n => g) + fun n => f n - g :=
    by
    ext1 n
    simp
  rw [this]
  refine'
    unif_integrable.add _ _ hp (fun _ => hg.ae_strongly_measurable) fun n =>
      (hf n).1.sub hg.ae_strongly_measurable
  · exact unif_integrable_const μ hp hp' hg
  · exact unif_integrable_of_tendsto_Lp_zero μ hp hp' (fun n => (hf n).sub hg) hfg
#align measure_theory.unif_integrable_of_tendsto_Lp MeasureTheory.unifIntegrableOfTendstoLp

/-- Forward direction of Vitali's convergence theorem: if `f` is a sequence of uniformly integrable
functions that converge in measure to some function `g` in a finite measure space, then `f`
converge in Lp to `g`. -/
theorem tendsto_Lp_of_tendstoInMeasure [IsFiniteMeasure μ] (hp : 1 ≤ p) (hp' : p ≠ ∞)
    (hf : ∀ n, AeStronglyMeasurable (f n) μ) (hg : Memℒp g p μ) (hui : UnifIntegrable f p μ)
    (hfg : TendstoInMeasure μ f atTop g) : Tendsto (fun n => snorm (f n - g) p μ) atTop (𝓝 0) :=
  by
  refine' tendsto_of_subseq_tendsto fun ns hns => _
  obtain ⟨ms, hms, hms'⟩ := tendsto_in_measure.exists_seq_tendsto_ae fun ε hε => (hfg ε hε).comp hns
  exact
    ⟨ms,
      tendsto_Lp_of_tendsto_ae μ hp hp' (fun _ => hf _) hg
        (fun ε hε =>
          let ⟨δ, hδ, hδ'⟩ := hui hε
          ⟨δ, hδ, fun i s hs hμs => hδ' _ s hs hμs⟩)
        hms'⟩
#align measure_theory.tendsto_Lp_of_tendsto_in_measure MeasureTheory.tendsto_Lp_of_tendstoInMeasure

/-- **Vitali's convergence theorem**: A sequence of functions `f` converges to `g` in Lp if and
only if it is uniformly integrable and converges to `g` in measure. -/
theorem tendstoInMeasure_iff_tendsto_Lp [IsFiniteMeasure μ] (hp : 1 ≤ p) (hp' : p ≠ ∞)
    (hf : ∀ n, Memℒp (f n) p μ) (hg : Memℒp g p μ) :
    TendstoInMeasure μ f atTop g ∧ UnifIntegrable f p μ ↔
      Tendsto (fun n => snorm (f n - g) p μ) atTop (𝓝 0) :=
  ⟨fun h => tendsto_Lp_of_tendstoInMeasure μ hp hp' (fun n => (hf n).1) hg h.2 h.1, fun h =>
    ⟨tendstoInMeasureOfTendstoSnorm (lt_of_lt_of_le zero_lt_one hp).Ne.symm
        (fun n => (hf n).AeStronglyMeasurable) hg.AeStronglyMeasurable h,
      unifIntegrableOfTendstoLp μ hp hp' hf hg h⟩⟩
#align measure_theory.tendsto_in_measure_iff_tendsto_Lp MeasureTheory.tendstoInMeasure_iff_tendsto_Lp

/-- This lemma is superceded by `unif_integrable_of` which do not require `C` to be positive. -/
theorem unifIntegrableOf' (hp : 1 ≤ p) (hp' : p ≠ ∞) {f : ι → α → β}
    (hf : ∀ i, StronglyMeasurable (f i))
    (h :
      ∀ ε : ℝ,
        0 < ε →
          ∃ C : ℝ≥0,
            0 < C ∧ ∀ i, snorm ({ x | C ≤ ‖f i x‖₊ }.indicator (f i)) p μ ≤ ENNReal.ofReal ε) :
    UnifIntegrable f p μ :=
  by
  have hpzero := (lt_of_lt_of_le zero_lt_one hp).Ne.symm
  by_cases hμ : μ Set.univ = 0
  · rw [measure.measure_univ_eq_zero] at hμ
    exact hμ.symm ▸ unif_integrable_zero_meas
  intro ε hε
  obtain ⟨C, hCpos, hC⟩ := h (ε / 2) (half_pos hε)
  refine'
    ⟨(ε / (2 * C)) ^ ENNReal.toReal p,
      Real.rpow_pos_of_pos (div_pos hε (mul_pos two_pos (NNReal.coe_pos.2 hCpos))) _,
      fun i s hs hμs => _⟩
  by_cases hμs' : μ s = 0
  · rw [(snorm_eq_zero_iff ((hf i).indicator hs).AeStronglyMeasurable hpzero).2
        (indicator_meas_zero hμs')]
    norm_num
  calc
    snorm (indicator s (f i)) p μ ≤
        snorm (indicator (s ∩ { x | C ≤ ‖f i x‖₊ }) (f i)) p μ +
          snorm (indicator (s ∩ { x | ‖f i x‖₊ < C }) (f i)) p μ :=
      by
      refine'
        le_trans (Eq.le _)
          (snorm_add_le
            (strongly_measurable.ae_strongly_measurable
              ((hf i).indicator
                (hs.inter (strongly_measurable_const.measurable_set_le (hf i).nnnorm))))
            (strongly_measurable.ae_strongly_measurable
              ((hf i).indicator
                (hs.inter ((hf i).nnnorm.measurableSet_lt strongly_measurable_const))))
            hp)
      congr
      change
        _ = fun x =>
          (s ∩ { x : α | C ≤ ‖f i x‖₊ }).indicator (f i) x +
            (s ∩ { x : α | ‖f i x‖₊ < C }).indicator (f i) x
      rw [← Set.indicator_union_of_disjoint]
      · congr
        rw [← inter_union_distrib_left,
          (by
            ext
            simp [le_or_lt] : { x : α | C ≤ ‖f i x‖₊ } ∪ { x : α | ‖f i x‖₊ < C } = Set.univ),
          inter_univ]
      · refine' (Disjoint.inf_right' _ _).inf_left' _
        rw [disjoint_iff_inf_le]
        rintro x ⟨hx₁ : _ ≤ _, hx₂ : _ < _⟩
        exact False.elim (hx₂.ne (eq_of_le_of_not_lt hx₁ (not_lt.2 hx₂.le)).symm)
    _ ≤ snorm (indicator { x | C ≤ ‖f i x‖₊ } (f i)) p μ + C * μ s ^ (1 / ENNReal.toReal p) :=
      by
      refine'
        add_le_add (snorm_mono fun x => norm_indicator_le_of_subset (inter_subset_right _ _) _ _) _
      rw [← indicator_indicator]
      rw [snorm_indicator_eq_snorm_restrict]
      have : ∀ᵐ x ∂μ.restrict s, ‖{ x : α | ‖f i x‖₊ < C }.indicator (f i) x‖ ≤ C :=
        by
        refine' ae_of_all _ _
        simp_rw [norm_indicator_eq_indicator_norm]
        exact indicator_le' (fun x (hx : _ < _) => hx.le) fun _ _ => NNReal.coe_nonneg _
      refine' le_trans (snorm_le_of_ae_bound this) _
      rw [mul_comm, measure.restrict_apply' hs, univ_inter, ENNReal.ofReal_coe_nnreal, one_div]
      exacts[le_rfl, hs]
    _ ≤ ENNReal.ofReal (ε / 2) + C * ENNReal.ofReal (ε / (2 * C)) :=
      by
      refine' add_le_add (hC i) (mul_le_mul_left' _ _)
      rwa [ENNReal.rpow_one_div_le_iff (ENNReal.toReal_pos hpzero hp'),
        ENNReal.ofReal_rpow_of_pos (div_pos hε (mul_pos two_pos (NNReal.coe_pos.2 hCpos)))]
    _ ≤ ENNReal.ofReal (ε / 2) + ENNReal.ofReal (ε / 2) :=
      by
      refine' add_le_add_left _ _
      rw [← ENNReal.ofReal_coe_nnreal, ← ENNReal.ofReal_mul (NNReal.coe_nonneg _), ← div_div,
        mul_div_cancel' _ (NNReal.coe_pos.2 hCpos).Ne.symm]
      exact le_rfl
    _ ≤ ENNReal.ofReal ε :=
      by
      rw [← ENNReal.ofReal_add (half_pos hε).le (half_pos hε).le, add_halves]
      exact le_rfl

#align measure_theory.unif_integrable_of' MeasureTheory.unifIntegrableOf'

theorem unifIntegrableOf (hp : 1 ≤ p) (hp' : p ≠ ∞) {f : ι → α → β}
    (hf : ∀ i, AeStronglyMeasurable (f i) μ)
    (h :
      ∀ ε : ℝ,
        0 < ε →
          ∃ C : ℝ≥0, ∀ i, snorm ({ x | C ≤ ‖f i x‖₊ }.indicator (f i)) p μ ≤ ENNReal.ofReal ε) :
    UnifIntegrable f p μ := by
  set g : ι → α → β := fun i => (hf i).some
  refine'
    (unif_integrable_of' μ hp hp' (fun i => (Exists.choose_spec <| hf i).1) fun ε hε => _).ae_eq
      fun i => (Exists.choose_spec <| hf i).2.symm
  obtain ⟨C, hC⟩ := h ε hε
  have hCg : ∀ i, snorm ({ x | C ≤ ‖g i x‖₊ }.indicator (g i)) p μ ≤ ENNReal.ofReal ε :=
    by
    intro i
    refine' le_trans (le_of_eq <| snorm_congr_ae _) (hC i)
    filter_upwards [(Exists.choose_spec <| hf i).2]with x hx
    by_cases hfx : x ∈ { x | C ≤ ‖f i x‖₊ }
    · rw [indicator_of_mem hfx, indicator_of_mem, hx]
      rwa [mem_set_of, hx] at hfx
    · rw [indicator_of_not_mem hfx, indicator_of_not_mem]
      rwa [mem_set_of, hx] at hfx
  refine' ⟨max C 1, lt_max_of_lt_right one_pos, fun i => le_trans (snorm_mono fun x => _) (hCg i)⟩
  rw [norm_indicator_eq_indicator_norm, norm_indicator_eq_indicator_norm]
  exact
    indicator_le_indicator_of_subset (fun x hx => le_trans (le_max_left _ _) hx)
      (fun _ => norm_nonneg _) _
#align measure_theory.unif_integrable_of MeasureTheory.unifIntegrableOf

end UnifIntegrable

section UniformIntegrable

/-! `uniform_integrable`

In probability theory, uniform integrability normally refers to the condition that a sequence
of function `(fₙ)` satisfies for all `ε > 0`, there exists some `C ≥ 0` such that
`∫ x in {|fₙ| ≥ C}, fₙ x ∂μ ≤ ε` for all `n`.

In this section, we will develope some API for `uniform_integrable` and prove that
`uniform_integrable` is equivalent to this definition of uniform integrability.
-/

variable {p : ℝ≥0∞} {f : ι → α → β}

theorem uniformIntegrableZeroMeas [MeasurableSpace α] : UniformIntegrable f p (0 : Measure α) :=
  ⟨fun n => aeStronglyMeasurableZeroMeasure _, unifIntegrableZeroMeas, 0, fun i =>
    snorm_measure_zero.le⟩
#align measure_theory.uniform_integrable_zero_meas MeasureTheory.uniformIntegrableZeroMeas

theorem UniformIntegrable.aeEq {g : ι → α → β} (hf : UniformIntegrable f p μ)
    (hfg : ∀ n, f n =ᵐ[μ] g n) : UniformIntegrable g p μ :=
  by
  obtain ⟨hfm, hunif, C, hC⟩ := hf
  refine' ⟨fun i => (hfm i).congr (hfg i), (unif_integrable_congr_ae hfg).1 hunif, C, fun i => _⟩
  rw [← snorm_congr_ae (hfg i)]
  exact hC i
#align measure_theory.uniform_integrable.ae_eq MeasureTheory.UniformIntegrable.aeEq

theorem uniformIntegrable_congr_ae {g : ι → α → β} (hfg : ∀ n, f n =ᵐ[μ] g n) :
    UniformIntegrable f p μ ↔ UniformIntegrable g p μ :=
  ⟨fun h => h.ae_eq hfg, fun h => h.ae_eq fun i => (hfg i).symm⟩
#align measure_theory.uniform_integrable_congr_ae MeasureTheory.uniformIntegrable_congr_ae

/-- A finite sequence of Lp functions is uniformly integrable in the probability sense. -/
theorem uniformIntegrableFinite [Finite ι] (hp_one : 1 ≤ p) (hp_top : p ≠ ∞)
    (hf : ∀ i, Memℒp (f i) p μ) : UniformIntegrable f p μ :=
  by
  cases nonempty_fintype ι
  refine' ⟨fun n => (hf n).1, unif_integrable_finite μ hp_one hp_top hf, _⟩
  by_cases hι : Nonempty ι
  · choose ae_meas hf using hf
    set C :=
      (finset.univ.image fun i : ι => snorm (f i) p μ).max'
        ⟨snorm (f hι.some) p μ, Finset.mem_image.2 ⟨hι.some, Finset.mem_univ _, rfl⟩⟩
    refine' ⟨C.to_nnreal, fun i => _⟩
    rw [ENNReal.coe_toNNReal]
    · exact Finset.le_max' _ _ (Finset.mem_image.2 ⟨i, Finset.mem_univ _, rfl⟩)
    · refine' ne_of_lt ((Finset.max'_lt_iff _ _).2 fun y hy => _)
      rw [Finset.mem_image] at hy
      obtain ⟨i, -, rfl⟩ := hy
      exact hf i
  · exact ⟨0, fun i => False.elim <| hι <| Nonempty.intro i⟩
#align measure_theory.uniform_integrable_finite MeasureTheory.uniformIntegrableFinite

/-- A single function is uniformly integrable in the probability sense. -/
theorem uniformIntegrableSubsingleton [Subsingleton ι] (hp_one : 1 ≤ p) (hp_top : p ≠ ∞)
    (hf : ∀ i, Memℒp (f i) p μ) : UniformIntegrable f p μ :=
  uniformIntegrableFinite hp_one hp_top hf
#align measure_theory.uniform_integrable_subsingleton MeasureTheory.uniformIntegrableSubsingleton

/-- A constant sequence of functions is uniformly integrable in the probability sense. -/
theorem uniformIntegrableConst {g : α → β} (hp : 1 ≤ p) (hp_ne_top : p ≠ ∞) (hg : Memℒp g p μ) :
    UniformIntegrable (fun n : ι => g) p μ :=
  ⟨fun i => hg.1, unifIntegrableConst μ hp hp_ne_top hg,
    ⟨(snorm g p μ).toNNReal, fun i => le_of_eq (ENNReal.coe_toNNReal hg.2.Ne).symm⟩⟩
#align measure_theory.uniform_integrable_const MeasureTheory.uniformIntegrableConst

/-- This lemma is superceded by `uniform_integrable_of` which only requires
`ae_strongly_measurable`. -/
theorem uniformIntegrableOf' [IsFiniteMeasure μ] (hp : 1 ≤ p) (hp' : p ≠ ∞)
    (hf : ∀ i, StronglyMeasurable (f i))
    (h :
      ∀ ε : ℝ,
        0 < ε →
          ∃ C : ℝ≥0, ∀ i, snorm ({ x | C ≤ ‖f i x‖₊ }.indicator (f i)) p μ ≤ ENNReal.ofReal ε) :
    UniformIntegrable f p μ :=
  by
  refine'
    ⟨fun i => (hf i).AeStronglyMeasurable,
      unif_integrable_of μ hp hp' (fun i => (hf i).AeStronglyMeasurable) h, _⟩
  obtain ⟨C, hC⟩ := h 1 one_pos
  refine' ⟨(C * μ univ ^ p.to_real⁻¹ + 1 : ℝ≥0∞).toNNReal, fun i => _⟩
  calc
    snorm (f i) p μ ≤
        snorm ({ x : α | ‖f i x‖₊ < C }.indicator (f i)) p μ +
          snorm ({ x : α | C ≤ ‖f i x‖₊ }.indicator (f i)) p μ :=
      by
      refine'
        le_trans (snorm_mono fun x => _)
          (snorm_add_le
            (strongly_measurable.ae_strongly_measurable
              ((hf i).indicator ((hf i).nnnorm.measurableSet_lt strongly_measurable_const)))
            (strongly_measurable.ae_strongly_measurable
              ((hf i).indicator (strongly_measurable_const.measurable_set_le (hf i).nnnorm)))
            hp)
      · rw [Pi.add_apply, indicator_apply]
        split_ifs with hx
        · rw [indicator_of_not_mem, add_zero]
          simpa using hx
        · rw [indicator_of_mem, zero_add]
          simpa using hx
    _ ≤ C * μ univ ^ p.to_real⁻¹ + 1 :=
      by
      have : ∀ᵐ x ∂μ, ‖{ x : α | ‖f i x‖₊ < C }.indicator (f i) x‖₊ ≤ C :=
        by
        refine' eventually_of_forall _
        simp_rw [nnnorm_indicator_eq_indicator_nnnorm]
        exact indicator_le fun x (hx : _ < _) => hx.le
      refine' add_le_add (le_trans (snorm_le_of_ae_bound this) _) (ENNReal.ofReal_one ▸ hC i)
      rw [ENNReal.ofReal_coe_nnreal, mul_comm]
      exact le_rfl
    _ = (C * μ univ ^ p.to_real⁻¹ + 1 : ℝ≥0∞).toNNReal :=
      by
      rw [ENNReal.coe_toNNReal]
      exact
        ENNReal.add_ne_top.2
          ⟨ENNReal.mul_ne_top ENNReal.coe_ne_top
              (ENNReal.rpow_ne_top_of_nonneg (inv_nonneg.2 ENNReal.toReal_nonneg)
                (measure_lt_top _ _).Ne),
            ENNReal.one_ne_top⟩

#align measure_theory.uniform_integrable_of' MeasureTheory.uniformIntegrableOf'

/-- A sequene of functions `(fₙ)` is uniformly integrable in the probability sense if for all
`ε > 0`, there exists some `C` such that `∫ x in {|fₙ| ≥ C}, fₙ x ∂μ ≤ ε` for all `n`. -/
theorem uniformIntegrableOf [IsFiniteMeasure μ] (hp : 1 ≤ p) (hp' : p ≠ ∞)
    (hf : ∀ i, AeStronglyMeasurable (f i) μ)
    (h :
      ∀ ε : ℝ,
        0 < ε →
          ∃ C : ℝ≥0, ∀ i, snorm ({ x | C ≤ ‖f i x‖₊ }.indicator (f i)) p μ ≤ ENNReal.ofReal ε) :
    UniformIntegrable f p μ :=
  by
  set g : ι → α → β := fun i => (hf i).some
  have hgmeas : ∀ i, strongly_measurable (g i) := fun i => (Exists.choose_spec <| hf i).1
  have hgeq : ∀ i, g i =ᵐ[μ] f i := fun i => (Exists.choose_spec <| hf i).2.symm
  refine' (uniform_integrable_of' hp hp' hgmeas fun ε hε => _).ae_eq hgeq
  obtain ⟨C, hC⟩ := h ε hε
  refine' ⟨C, fun i => le_trans (le_of_eq <| snorm_congr_ae _) (hC i)⟩
  filter_upwards [(Exists.choose_spec <| hf i).2]with x hx
  by_cases hfx : x ∈ { x | C ≤ ‖f i x‖₊ }
  · rw [indicator_of_mem hfx, indicator_of_mem, hx]
    rwa [mem_set_of, hx] at hfx
  · rw [indicator_of_not_mem hfx, indicator_of_not_mem]
    rwa [mem_set_of, hx] at hfx
#align measure_theory.uniform_integrable_of MeasureTheory.uniformIntegrableOf

/-- This lemma is superceded by `uniform_integrable.spec` which does not require measurability. -/
theorem UniformIntegrable.spec' (hp : p ≠ 0) (hp' : p ≠ ∞) (hf : ∀ i, StronglyMeasurable (f i))
    (hfu : UniformIntegrable f p μ) {ε : ℝ} (hε : 0 < ε) :
    ∃ C : ℝ≥0, ∀ i, snorm ({ x | C ≤ ‖f i x‖₊ }.indicator (f i)) p μ ≤ ENNReal.ofReal ε :=
  by
  obtain ⟨-, hfu, M, hM⟩ := hfu
  obtain ⟨δ, hδpos, hδ⟩ := hfu hε
  obtain ⟨C, hC⟩ : ∃ C : ℝ≥0, ∀ i, μ { x | C ≤ ‖f i x‖₊ } ≤ ENNReal.ofReal δ :=
    by
    by_contra hcon
    push_neg  at hcon
    choose ℐ hℐ using hcon
    lift δ to ℝ≥0 using hδpos.le
    have : ∀ C : ℝ≥0, C • (δ : ℝ≥0∞) ^ (1 / p.to_real) ≤ snorm (f (ℐ C)) p μ :=
      by
      intro C
      calc
        C • (δ : ℝ≥0∞) ^ (1 / p.to_real) ≤ C • μ { x | C ≤ ‖f (ℐ C) x‖₊ } ^ (1 / p.to_real) :=
          by
          rw [ENNReal.smul_def, ENNReal.smul_def, smul_eq_mul, smul_eq_mul]
          simp_rw [ENNReal.ofReal_coe_nnreal] at hℐ
          refine'
            mul_le_mul' le_rfl
              (ENNReal.rpow_le_rpow (hℐ C).le (one_div_nonneg.2 ENNReal.toReal_nonneg))
        _ ≤ snorm ({ x | C ≤ ‖f (ℐ C) x‖₊ }.indicator (f (ℐ C))) p μ :=
          by
          refine'
            snorm_indicator_ge_of_bdd_below hp hp' _
              (measurableSet_le measurable_const (hf _).nnnorm.Measurable)
              (eventually_of_forall fun x hx => _)
          rwa [nnnorm_indicator_eq_indicator_nnnorm, indicator_of_mem hx]
        _ ≤ snorm (f (ℐ C)) p μ := snorm_indicator_le _

    specialize this (2 * max M 1 * δ⁻¹ ^ (1 / p.to_real))
    rw [ENNReal.coe_rpow_of_nonneg _ (one_div_nonneg.2 ENNReal.toReal_nonneg), ← ENNReal.coe_smul,
      smul_eq_mul, mul_assoc, NNReal.inv_rpow,
      inv_mul_cancel (NNReal.rpow_pos (NNReal.coe_pos.1 hδpos)).Ne.symm, mul_one, ENNReal.coe_mul, ←
      NNReal.inv_rpow] at this
    refine'
      (lt_of_le_of_lt (le_trans (hM <| ℐ <| 2 * max M 1 * δ⁻¹ ^ (1 / p.to_real)) (le_max_left M 1))
            (lt_of_lt_of_le _ this)).Ne
        rfl
    rw [← ENNReal.coe_one, ← WithTop.coe_max, ← ENNReal.coe_mul, ENNReal.coe_lt_coe]
    exact lt_two_mul_self (lt_max_of_lt_right one_pos)
  exact ⟨C, fun i => hδ i _ (measurableSet_le measurable_const (hf i).nnnorm.Measurable) (hC i)⟩
#align measure_theory.uniform_integrable.spec' MeasureTheory.UniformIntegrable.spec'

theorem UniformIntegrable.spec (hp : p ≠ 0) (hp' : p ≠ ∞) (hfu : UniformIntegrable f p μ) {ε : ℝ}
    (hε : 0 < ε) :
    ∃ C : ℝ≥0, ∀ i, snorm ({ x | C ≤ ‖f i x‖₊ }.indicator (f i)) p μ ≤ ENNReal.ofReal ε :=
  by
  set g : ι → α → β := fun i => (hfu.1 i).some
  have hgmeas : ∀ i, strongly_measurable (g i) := fun i => (Exists.choose_spec <| hfu.1 i).1
  have hgunif : uniform_integrable g p μ := hfu.ae_eq fun i => (Exists.choose_spec <| hfu.1 i).2
  obtain ⟨C, hC⟩ := hgunif.spec' hp hp' hgmeas hε
  refine' ⟨C, fun i => le_trans (le_of_eq <| snorm_congr_ae _) (hC i)⟩
  filter_upwards [(Exists.choose_spec <| hfu.1 i).2]with x hx
  by_cases hfx : x ∈ { x | C ≤ ‖f i x‖₊ }
  · rw [indicator_of_mem hfx, indicator_of_mem, hx]
    rwa [mem_set_of, hx] at hfx
  · rw [indicator_of_not_mem hfx, indicator_of_not_mem]
    rwa [mem_set_of, hx] at hfx
#align measure_theory.uniform_integrable.spec MeasureTheory.UniformIntegrable.spec

/-- The definition of uniform integrable in mathlib is equivalent to the definition commonly
found in literature. -/
theorem uniformIntegrable_iff [IsFiniteMeasure μ] (hp : 1 ≤ p) (hp' : p ≠ ∞) :
    UniformIntegrable f p μ ↔
      (∀ i, AeStronglyMeasurable (f i) μ) ∧
        ∀ ε : ℝ,
          0 < ε →
            ∃ C : ℝ≥0, ∀ i, snorm ({ x | C ≤ ‖f i x‖₊ }.indicator (f i)) p μ ≤ ENNReal.ofReal ε :=
  ⟨fun h => ⟨h.1, fun ε => h.spec (lt_of_lt_of_le zero_lt_one hp).Ne.symm hp'⟩, fun h =>
    uniformIntegrableOf hp hp' h.1 h.2⟩
#align measure_theory.uniform_integrable_iff MeasureTheory.uniformIntegrable_iff

/-- The averaging of a uniformly integrable sequence is also uniformly integrable. -/
theorem uniformIntegrableAverage (hp : 1 ≤ p) {f : ℕ → α → ℝ} (hf : UniformIntegrable f p μ) :
    UniformIntegrable (fun n => (∑ i in Finset.range n, f i) / n) p μ :=
  by
  obtain ⟨hf₁, hf₂, hf₃⟩ := hf
  refine' ⟨fun n => _, fun ε hε => _, _⟩
  · simp_rw [div_eq_mul_inv]
    exact
      (Finset.ae_strongly_measurable_sum' _ fun i _ => hf₁ i).mul
        (ae_strongly_measurable_const : ae_strongly_measurable (fun x => (↑n : ℝ)⁻¹) μ)
  · obtain ⟨δ, hδ₁, hδ₂⟩ := hf₂ hε
    refine' ⟨δ, hδ₁, fun n s hs hle => _⟩
    simp_rw [div_eq_mul_inv, Finset.sum_mul, Set.indicator_finset_sum]
    refine' le_trans (snorm_sum_le (fun i hi => ((hf₁ i).mul_const (↑n)⁻¹).indicator hs) hp) _
    have : ∀ i, s.indicator (f i * (↑n)⁻¹) = (↑n : ℝ)⁻¹ • s.indicator (f i) :=
      by
      intro i
      rw [mul_comm, (_ : (↑n)⁻¹ * f i = fun ω => (↑n : ℝ)⁻¹ • f i ω)]
      · rw [Set.indicator_const_smul s (↑n)⁻¹ (f i)]
        rfl
      · rfl
    simp_rw [this, snorm_const_smul, ← Finset.mul_sum, nnnorm_inv, Real.nnnorm_coe_nat]
    by_cases hn : (↑(↑n : ℝ≥0)⁻¹ : ℝ≥0∞) = 0
    · simp only [hn, MulZeroClass.zero_mul, zero_le]
    refine' le_trans _ (_ : ↑(↑n : ℝ≥0)⁻¹ * n • ENNReal.ofReal ε ≤ ENNReal.ofReal ε)
    · refine' (ENNReal.mul_le_mul_left hn ENNReal.coe_ne_top).2 _
      conv_rhs => rw [← Finset.card_range n]
      exact Finset.sum_le_card_nsmul _ _ _ fun i hi => hδ₂ _ _ hs hle
    · simp only [ENNReal.coe_eq_zero, inv_eq_zero, Nat.cast_eq_zero] at hn
      rw [nsmul_eq_mul, ← mul_assoc, ENNReal.coe_inv, ENNReal.coe_nat,
        ENNReal.inv_mul_cancel _ (ENNReal.nat_ne_top _), one_mul]
      · exact le_rfl
      all_goals simpa only [Ne.def, Nat.cast_eq_zero]
  · obtain ⟨C, hC⟩ := hf₃
    simp_rw [div_eq_mul_inv, Finset.sum_mul]
    refine' ⟨C, fun n => (snorm_sum_le (fun i hi => (hf₁ i).mul_const (↑n)⁻¹) hp).trans _⟩
    have : ∀ i, (fun ω => f i ω * (↑n)⁻¹) = (↑n : ℝ)⁻¹ • fun ω => f i ω :=
      by
      intro i
      ext ω
      simp only [mul_comm, Pi.smul_apply, Algebra.id.smul_eq_mul]
    simp_rw [this, snorm_const_smul, ← Finset.mul_sum, nnnorm_inv, Real.nnnorm_coe_nat]
    by_cases hn : (↑(↑n : ℝ≥0)⁻¹ : ℝ≥0∞) = 0
    · simp only [hn, MulZeroClass.zero_mul, zero_le]
    refine' le_trans _ (_ : ↑(↑n : ℝ≥0)⁻¹ * (n • C : ℝ≥0∞) ≤ C)
    · refine' (ENNReal.mul_le_mul_left hn ENNReal.coe_ne_top).2 _
      conv_rhs => rw [← Finset.card_range n]
      exact Finset.sum_le_card_nsmul _ _ _ fun i hi => hC i
    · simp only [ENNReal.coe_eq_zero, inv_eq_zero, Nat.cast_eq_zero] at hn
      rw [nsmul_eq_mul, ← mul_assoc, ENNReal.coe_inv, ENNReal.coe_nat,
        ENNReal.inv_mul_cancel _ (ENNReal.nat_ne_top _), one_mul]
      · exact le_rfl
      all_goals simpa only [Ne.def, Nat.cast_eq_zero]
#align measure_theory.uniform_integrable_average MeasureTheory.uniformIntegrableAverage

end UniformIntegrable

end MeasureTheory


import data.set.intervals.basic
import data.real.basic
import topology.instances.real
import tactic.suggest
import tactics

open set
open classical
--  Propriétés de base sur l'ensemble [0,1]

abbreviation I := Icc (0 : ℝ) (1 : ℝ)

instance : has_zero I := ⟨⟨ (0 : ℝ), and.intro (le_refl 0) (zero_le_one)⟩⟩
instance : has_one I := ⟨⟨1, and.intro (zero_le_one) (le_refl 1)⟩⟩
noncomputable def real_to_I (x : ℝ) : I := if h : x≥0 ∧ x≤1 then ⟨x, h⟩ else (0 : I)
instance : has_coe I ℝ := ⟨subtype.val⟩
instance : has_le I := ⟨λ r s, (r:ℝ) ≤ s⟩
noncomputable instance : has_sub I := ⟨λ a b, real_to_I (a-b)⟩

lemma oneminus (t : I) : 1-t.val ≥ 0 ∧ 1-t.val ≤ 1 :=
begin
  simp,
  split,
  exact t.property.2,
  ring,
  apply add_le_iff_nonpos_left.mpr,
  apply neg_le.mp,
  simp,
  exact t.property.1,
end

lemma twotimes (t:I) : t.val ≤ 0.5 -> 2*t.val ≥ 0 ∧ 2*t.val ≤ 1  :=
begin
    intro h,
    simp,
    sorry,
    --apply (le_div_iff zero_lt_two).mp,
end

lemma twotimesminus1 (t:I) : t.val > 0.5 -> 2*t.val-1 ≥ 0 ∧ 2*t.val-1 ≤ 1  :=
begin
    intro h,
    simp,
    split,
    linarith,
    have h2 : t.val ≤ 1, from t.property.2,
    linarith,
end

@[simp] lemma coe_of_0 : (0 : I).val = (0 : ℝ ) := rfl
@[simp] lemma coe_of_1 : (1 : I).val = (1 : ℝ ) := rfl
@[simp] lemma one_minus_one_coe : (1:ℝ) - (1 : I).val = (0 : ℝ ) := sub_self 1

lemma not_2_lt_0 : ¬((2:ℝ)<(0:ℝ)) := by {intro,  linarith}
lemma not_1_lt_half: ¬ ((1 : ℝ) ≤ 2⁻¹) :=  by sorry

lemma invtwo : (2:ℝ)*(1/2)=1 := by ring
lemma invtwo'  (x:ℝ) :((2:ℝ)*x)/2=x := by ring

@[simp] lemma oneisone : (⟨(1:ℝ),sorry⟩:I)=(1:I):=rfl
@[simp] lemma zeroiszero : (⟨(0:ℝ),sorry⟩:I)=(0:I):=rfl

lemma frontieronI' : frontier {a : ↥I | a.val ≤ 1 / 2} = {a : I | a.val=1/2}  := by sorry
lemma frontieronI : frontier  ({a : I × I | a.fst.val ≤ 2⁻¹} : set (I×I)) = {a : I×I | a.fst.val=1/2} :=
begin
  have  test : is_closed {a : I × I | a.fst.val ≤ 2⁻¹}, from sorry,
  have interior2 : interior {a : I × I | a.fst.val ≤ 2⁻¹} = {a : I × I | a.fst.val < 2⁻¹} , from sorry,
  simp [test, is_closed.frontier_eq, interior2],
  ext,
  split,
  intro h,
  cases h with h1 h2,
  simp [mem_def] at h1,
  simp [mem_def] at h2,
  simp *,
  linarith,

  intro h,
  simp *,
  simp [mem_def] at h,
  split,
  linarith,
  linarith,
end

lemma in_I_const_comp  (x : I×I) : 0≤(2-x.1.val)*x.2.val-1 + x.1.val ∧ (2-x.1.val)*x.2.val-1 + x.1.val≤1:=
begin
 sorry
end

lemma in_I_comp_const  (x : I×I) : 0≤(2-x.1.val)*x.2.val ∧ (2-x.1.val)*x.2.val ≤ 1 := by sorry

lemma one_half : (1 + 1) / (4:ℝ) = 1/2 := by ring
lemma one_quarter : (1 + 0) / (4:ℝ) = 1/4 := by ring


import category_theory.single_obj
       category_theory.action

open_locale classical
open category_theory function quotient_group

instance {C} [category C] (F : C ⥤ Type*) : faithful (category_of_elements.π F) :=
by tidy

def homset_equiv_of_mul_equiv {G H K} [group G] [group H] [group K] (h : G ≃* H) :
  (G →* K) ≃ (H →* K) :=
{ to_fun := λ g, g.comp h.symm.to_monoid_hom,
  inv_fun := λ g, g.comp h.to_monoid_hom,
  left_inv := by tidy,
  right_inv := by tidy }

lemma stabilizer_of_coset_action {G} [group G] {H : subgroup G} :
  mul_action.stabilizer G (default _ : quotient H) = H :=
by { ext, change _ = _ ↔ _, rw eq_comm, convert quotient_group.eq, simp }

instance action_category_inhabited {M X} [monoid M] [mul_action M X] [inhabited X] :
  inhabited (action_category M X) := ⟨⟨single_obj.star M, (default _ : X)⟩⟩

def End_mul_equiv_subgroup {G} [group G] (H : subgroup G) :
  End (default (action_category G (quotient H))) ≃* H :=
begin
  refine mul_equiv.trans _ _,
  { exact mul_action.stabilizer G (default _ : quotient H) },
  { apply_instance },
  { refl }, -- this is a heavy refl
  { rw stabilizer_of_coset_action },
end

noncomputable def compl_sum_set_equiv {A} (s : set A) : (set.compl s) ⊕ s ≃ A :=
{ to_fun := λ x, sum.rec_on x coe coe,
  inv_fun := λ a, if h : a ∈ s
                  then sum.inr ⟨a, h⟩
                  else sum.inl ⟨a, h⟩,
  left_inv := begin
    intro x,
    rcases x with ⟨x, h⟩ | ⟨x, h⟩,
    { dsimp, rw dif_neg },
    { dsimp, rw dif_pos }
  end,
  right_inv := begin
    intro y, by_cases y ∈ s,
    { dsimp, rw dif_pos, { refl }, exact h },
    { dsimp, rw dif_neg, { refl }, exact h },
  end }

instance (G A X : Type*) [group G] [mul_action G A] : mul_action G (A → X) :=
{ smul := λ g F a, F (g⁻¹ • a), -- the inverse is only here so we get a *left* action
  one_smul := by tidy,
  mul_smul := by simp [mul_smul] }

def my_mul_aut (G A X : Type*) [group G] [mul_action G A] [group X] :
  G →* mul_aut (A → X) :=
{ to_fun := λ g,
 { to_fun := λ F, g • F,
   inv_fun := λ F, g⁻¹ • F,
   left_inv := λ F, inv_smul_smul g F,
   right_inv := λ F, smul_inv_smul g F,
   map_mul' := by tidy },
  map_one' := by tidy,
  map_mul' := by { intros, ext, simp [mul_smul] }}


import data.complex.basic
import data.fintype.basic
import data.fin
import data.matrix.basic
import linear_algebra.basis
import linear_algebra.determinant
import linear_algebra.nonsingular_inverse
import .complex_transpose
import .complex_dot_product
import .matrix_extension
import tactic

noncomputable theory
open_locale classical
open_locale matrix

universes u u'
variables {m n l : Type*} [fintype m] [fintype n] [fintype l]
variables {k : ℕ}

section unitary

def matrix.unitary (A : matrix n n ℂ) :=
  A ⬝ (A.complex_transpose) = 1
open matrix

lemma unit_det_of_unitary {A : matrix n n ℂ} (hu : A.unitary) : is_unit A.det :=
begin
  have := matrix.det_mul A A.complex_transpose,
  unfold matrix.unitary at hu,
  rw [hu, matrix.det_one] at this,
  exact is_unit_of_mul_eq_one A.det A.complex_transpose.det (eq.symm this),
end

lemma unit_of_unitary {A : matrix n n ℂ} (hu : A.unitary) : is_unit A :=
  (matrix.is_unit_iff_is_unit_det A).mpr (unit_det_of_unitary hu)

lemma unitary_inv {A : matrix n n ℂ} (hu : A.unitary) : A.complex_transpose = A⁻¹ :=
begin
  unfold unitary at hu,
  rw [← mul_one A⁻¹, ← hu], rw [mul_eq_mul, ← matrix.mul_assoc],
  sorry
end

lemma unit_of_complex_transpose {A : matrix n n ℂ} (hu : A.unitary) : is_unit A.complex_transpose :=
begin
  have hunit := unit_of_unitary hu,
  refine unit_of_unitary _,
  unfold unitary,
  rw [complex_transpose_transpose, unitary_inv hu, ← mul_eq_mul],
  sorry
end

lemma unitary.has_mul {A B : matrix n n ℂ} (hA : A.unitary) (hB : B.unitary) :
  (A ⬝ B).unitary :=
begin
  unfold matrix.unitary at *,
  rw [complex_transpose_mul, matrix.mul_assoc, ← matrix.mul_assoc B], simp [hA, hB]
end

lemma unitary.has_one : (1 : matrix n n ℂ).unitary := by simp [matrix.unitary]
include n
instance unitary_group : group $ subtype $ unitary :=
{ mul := begin
  rintros ⟨A, hA⟩ ⟨B, hB⟩, refine ⟨A ⬝ B, _⟩, exact n, apply_instance, apply unitary.has_mul; assumption,
end,
  mul_assoc := sorry,
  one := ⟨1, unitary.has_one⟩,
  one_mul := sorry,
  mul_one := sorry,
  inv := sorry,
  mul_left_inv := sorry }

theorem rows_of_unitary {A : matrix n n ℂ} :
  A.unitary ↔
  ∀ i j : n,
  (vector.complex_dot_product (A i) (A i) = 1) ∧
  i ≠ j → (vector.complex_dot_product (A i) (A j) = 0)
:= sorry

lemma extension_unitary_of_unitary {k : ℕ} (A : matrix (fin k) (fin k) ℂ) (a : ℝ):
  A.unitary → (A.extension a).unitary :=
sorry

theorem unitary_of_unit_vector [linear_order n] [has_zero n] (v : n → ℂ) :
  vector.complex_norm v = 1 →
  ∃ A : matrix n n ℂ,
  A.unitary ∧
  (A 0) = v :=
sorry

end unitary


import ring_theory.tensor_product
import divided_powers.basic

namespace divided_powers

open_locale tensor_product

/- 3.7 Lemma. Suppose R is a ring, В and С are R-algebras, and
I ⊆ В and J ⊆ С are augmentation ideals (i.e. there is a section of В → B/I, etc.)
with P.D. structures γ and δ, respectively.
Then the ideal К = Ker (В ⊗ С → B/I ⊗ C/J) has a unique P.D. structure ε
such that (B,I,γ) → (В ⊗ С,К,ε) and
(C,J,δ) → (B ⊗ C,K,ε) are P.D. morphisms. -/

/- Lemma 3.7 of [BO] -> Change to 1.7.1 -/

def dpow_tensor_product (R B C : Type*) [comm_ring R] [comm_ring B] [comm_ring C]
  [algebra R B] [algebra R C] {I : ideal B} {J : ideal C} (hI : divided_powers I)
  (hJ : divided_powers J) (hIs : function.has_right_inverse (ideal.quotient.mk I))
  (hJs : function.has_right_inverse (ideal.quotient.mk J)) :
  ℕ → (B ⊗[R] C) → (B ⊗[R] C) := sorry

def divided_powers_tensor_product (R B C : Type*) [comm_ring R] [comm_ring B] [comm_ring C]
  [algebra R B] [algebra R C] {I : ideal B} {J : ideal C} (hI : divided_powers I)
  (hJ : divided_powers J) (hIs : function.has_right_inverse (ideal.quotient.mk I))
  (hJs : function.has_right_inverse (ideal.quotient.mk J)) :
  divided_powers (algebra.tensor_product.map (ideal.quotient.mkₐ R I)
    (ideal.quotient.mkₐ R J)).to_ring_hom.ker  :=
{ dpow := dpow_tensor_product R B C hI hJ hIs hJs,
  dpow_null := sorry,
  dpow_zero := sorry,
  dpow_one  := sorry,
  dpow_mem  := sorry,
  dpow_add  := sorry,
  dpow_smul := sorry,
  dpow_mul  := sorry,
  dpow_comp := sorry }

lemma divided_powers_tensor_product_unique (R B C : Type*) [comm_ring R] [comm_ring B] [comm_ring C]
  [algebra R B] [algebra R C] {I : ideal B} {J : ideal C} (hI : divided_powers I)
  (hJ : divided_powers J) (hIs : function.has_right_inverse (ideal.quotient.mk I))
  (hJs : function.has_right_inverse (ideal.quotient.mk J))
  (hK : divided_powers (algebra.tensor_product.map (ideal.quotient.mkₐ R I)
  (ideal.quotient.mkₐ R J)).to_ring_hom.ker) :
  hK = (divided_powers_tensor_product R B C hI hJ hIs hJs) :=
begin
  apply eq_of_eq_on_ideal,
  intros n x hx,
  sorry
end

end divided_powers


--import .affine_coordinate_space
import data.real.basic
import .affine_coordinate_frame

/-
Real affine coordinate 3 space.
-/

def real_affine_3_space := affine_coord_space_type ℝ (3 : ℕ)

def real_affine_3_space_1: real_affine_3_space := ⟨⟩
def real_affine_3_space_2: real_affine_3_space := ⟨⟩

example : real_affine_3_space_1 = real_affine_3_space_2 := rfl

noncomputable def real_affine_3_standard_frame : _ := aff_basis.aff_coord_space_std_frame ℝ 3

#check real_affine_3_standard_frame
/-
We've managed to define a real affine 3 space and a standard frame,
but the frame isn't yet "a part of" such a space.
-/


#check sub_self

example (x : ℤ) : x * 0 = 0 :=
calc
    x * 0 = x * (x - x)   : by rw sub_self
      ... = x * x - x * x : by rw mul_sub
      ... = 0             : by rw sub_self


/-
Copyright © 2019, Oracle and/or its affiliates. All rights reserved.
-/

import .setup_definition .setup_measurable
import .algorithm_definition .algorithm_measurable
import .sample_complexity
import .stump_pac_lemmas
import .to_epsilon

open set nat

local attribute [instance] classical.prop_decidable

namespace stump

variables (μ: probability_measure ℍ) (target: ℍ) (n: ℕ)

noncomputable
def denot: probability_measure ℍ :=
  let η := vec.prob_measure n μ  in
  let ν := map (label_sample target n) η in
  let γ := map (choose n) ν in
  γ

lemma pullback:
  ∀ P: nnreal → Prop,
  is_measurable {h: ℍ | P(@error μ target h)} →
  is_measurable {S: vec (ℍ × bool) n | P(error μ target (choose n S))} →
  (denot μ target n) {h: ℍ | P(@error μ target h)} = (vec.prob_measure n μ) {S: vec ℍ n | P(error μ target (choose n (label_sample target n S)))} :=
begin
  intros,
  dunfold denot,
  rw map_apply,
  rw map_apply,
  repeat {simp},
  repeat {assumption},
end

theorem choose_PAC:
  ∀ ε: nnreal, ∀ δ: nnreal, ∀ n: ℕ,
  ε > 0 → ε < 1 → δ > 0 → δ < 1 → (n: ℝ) > (complexity ε δ) →
  (denot μ target n) {h: ℍ | @error μ target h ≤ ε} ≥ 1 - δ :=
begin
  introv eps_0 eps_1 delta_0 delta_1 n_gt,
  by_cases((μ (Ioc 0 target)) > ε),
  {
    rw ← probability_measure.neq_prob_set,
    rw pullback μ target n (λ x, x > ε) _ _; try {simp},
    transitivity ((1 - ε)^(n+1)),
    {
      have EPS := extend_to_epsilon_1 μ target ε eps_0 h, cases EPS with θ θ_prop, cases θ_prop,
      have TZ: θ > 0,
      {
        by_contradiction h_contra,
        simp at h_contra, rw h_contra at *,
        have CONTRA: ¬ (μ (Ioc 0 target) ≤ ε), simp, assumption,
        contradiction,
      },
      transitivity ((vec.prob_measure n μ) {S: vec ℍ n |  ∀ (i: dfin (nat.succ n)), ∀ p = label target (kth_projn S i), (if p.snd then p.fst else 0) < θ}),
      {
        apply probability_measure.prob_mono,
        refine all_missed _ _ _ _ _ θ_prop_right,
      },
      {
        have INDEP := @prob_independence ℍ _ n _ (λ x, ∀ p = label target x, (if p.snd then p.fst else 0) < θ) μ (is_meas_one target θ TZ) (is_meas_forall target θ TZ),
        rw INDEP, clear INDEP,
        simp,
        apply pow_preserves_order (n+1),
        refine miss_prob _ _ _ _ TZ θ_prop_left,
      },
    },
    {
      apply complexity_enough ε δ n; assumption,
    },
    exact le_of_lt delta_1,
    simp,
  },
  {
    simp at h,
    rw pullback μ target n (λ x, x ≤ ε) _ _; try {simp},
    have DC: (vec.prob_measure n μ) {S: vec ℍ n | error μ target (choose n (label_sample target n S)) ≤ ε} = 1,
    {
      have AS := always_succeed μ target ε eps_0 n h,
      refine probability_measure.prob_trivial _ _ AS,
    },
    rw DC; try {assumption},
    apply delta_helper, assumption,
  },
end

end stump


/-
Copyright (c) 2018 Simon Hudon. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Simon Hudon, Patrick Massot

! This file was ported from Lean 3 source module algebra.order.pi
! leanprover-community/mathlib commit 422e70f7ce183d2900c586a8cda8381e788a0c62
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.Order.Ring.Defs
import Mathlib.Algebra.Ring.Pi
import Mathlib.Tactic.Positivity

/-!
# Pi instances for ordered groups and monoids

This file defines instances for ordered group, monoid, and related structures on Pi types.
-/

variable {ι α β : Type _}

variable {I : Type u}

-- The indexing type
variable {f : I → Type v}

-- The family of types already equipped with instances
variable (x y : ∀ i, f i) (i : I)

namespace Pi

/-- The product of a family of ordered commutative monoids is an ordered commutative monoid. -/
@[to_additive
      "The product of a family of ordered additive commutative monoids is
an ordered additive commutative monoid."]
instance orderedCommMonoid {ι : Type _} {Z : ι → Type _} [∀ i, OrderedCommMonoid (Z i)] :
    OrderedCommMonoid (∀ i, Z i) :=
  { Pi.partialOrder, Pi.commMonoid with
    mul_le_mul_left := fun _ _ w _ i => mul_le_mul_left' (w i) _ }
#align pi.ordered_comm_monoid Pi.orderedCommMonoid
#align pi.ordered_add_comm_monoid Pi.orderedAddCommMonoid

@[to_additive]
instance existsMulOfLe {ι : Type _} {α : ι → Type _} [∀ i, LE (α i)] [∀ i, Mul (α i)]
    [∀ i, ExistsMulOfLE (α i)] : ExistsMulOfLE (∀ i, α i) :=
  ⟨fun h =>
    ⟨fun i => (exists_mul_of_le <| h i).choose,
      funext fun i => (exists_mul_of_le <| h i).choose_spec⟩⟩
#align pi.has_exists_mul_of_le Pi.existsMulOfLe
#align pi.has_exists_add_of_le Pi.existsAddOfLe

/-- The product of a family of canonically ordered monoids is a canonically ordered monoid. -/
@[to_additive
      "The product of a family of canonically ordered additive monoids is
a canonically ordered additive monoid."]
instance {ι : Type _} {Z : ι → Type _} [∀ i, CanonicallyOrderedMonoid (Z i)] :
    CanonicallyOrderedMonoid (∀ i, Z i) :=
  { Pi.orderBot, Pi.orderedCommMonoid, Pi.existsMulOfLe with
    le_self_mul := fun _ _ _ => le_self_mul }

@[to_additive]
instance orderedCancelCommMonoid [∀ i, OrderedCancelCommMonoid <| f i] :
    OrderedCancelCommMonoid (∀ i : I, f i) :=
  { Pi.partialOrder, Pi.commMonoid with
    mul := (· * ·)
    one := (1 : ∀ i, f i)
    le := (· ≤ ·)
    lt := (· < ·)
    npow := Monoid.npow,
    le_of_mul_le_mul_left := fun _ _ _ h i =>
      OrderedCancelCommMonoid.le_of_mul_le_mul_left _ _ _ (h i)
    mul_le_mul_left := fun _ _ c h i =>
      OrderedCancelCommMonoid.mul_le_mul_left _ _ (c i) (h i) }
--Porting note: Old proof was
  -- refine_struct
  --     { Pi.partialOrder, Pi.monoid with
  --       mul := (· * ·)
  --       one := (1 : ∀ i, f i)
  --       le := (· ≤ ·)
  --       lt := (· < ·)
  --       npow := Monoid.npow } <;>
  --   pi_instance_derive_field
#align pi.ordered_cancel_comm_monoid Pi.orderedCancelCommMonoid
#align pi.ordered_cancel_add_comm_monoid Pi.orderedAddCancelCommMonoid

@[to_additive]
instance orderedCommGroup [∀ i, OrderedCommGroup <| f i] : OrderedCommGroup (∀ i : I, f i) :=
  { Pi.commGroup, Pi.orderedCommMonoid with
    mul := (· * ·)
    one := (1 : ∀ i, f i)
    le := (· ≤ ·)
    lt := (· < ·)
    npow := Monoid.npow }
#align pi.ordered_comm_group Pi.orderedCommGroup
#align pi.ordered_add_comm_group Pi.orderedAddCommGroup

instance orderedSemiring [∀ i, OrderedSemiring (f i)] : OrderedSemiring (∀ i, f i) :=
  { Pi.semiring,
    Pi.partialOrder with
    add_le_add_left := fun _ _ hab _ _ => add_le_add_left (hab _) _
    zero_le_one := fun i => zero_le_one (α := f i)
    mul_le_mul_of_nonneg_left := fun _ _ _ hab hc _ => mul_le_mul_of_nonneg_left (hab _) <| hc _
    mul_le_mul_of_nonneg_right := fun _ _ _ hab hc _ => mul_le_mul_of_nonneg_right (hab _) <| hc _ }
#align pi.ordered_semiring Pi.orderedSemiring

instance orderedCommSemiring [∀ i, OrderedCommSemiring (f i)] : OrderedCommSemiring (∀ i, f i) :=
  { Pi.commSemiring, Pi.orderedSemiring with }
#align pi.ordered_comm_semiring Pi.orderedCommSemiring

instance orderedRing [∀ i, OrderedRing (f i)] : OrderedRing (∀ i, f i) :=
  { Pi.ring, Pi.orderedSemiring with mul_nonneg := fun _ _ ha hb _ => mul_nonneg (ha _) (hb _) }
#align pi.ordered_ring Pi.orderedRing

instance orderedCommRing [∀ i, OrderedCommRing (f i)] : OrderedCommRing (∀ i, f i) :=
  { Pi.commRing, Pi.orderedRing with }
#align pi.ordered_comm_ring Pi.orderedCommRing

end Pi

namespace Function

variable (β) [One α] [Preorder α] {a : α}

@[to_additive const_nonneg_of_nonneg]
theorem one_le_const_of_one_le (ha : 1 ≤ a) : 1 ≤ const β a := fun _ => ha
#align function.one_le_const_of_one_le Function.one_le_const_of_one_le
#align function.const_nonneg_of_nonneg Function.const_nonneg_of_nonneg

@[to_additive]
theorem const_le_one_of_le_one (ha : a ≤ 1) : const β a ≤ 1 := fun _ => ha
#align function.const_le_one_of_le_one Function.const_le_one_of_le_one
#align function.const_nonpos_of_nonpos Function.const_nonpos_of_nonpos

variable {β} [Nonempty β]

@[to_additive (attr := simp) const_nonneg]
theorem one_le_const : 1 ≤ const β a ↔ 1 ≤ a :=
  @const_le_const _ _ _ _ 1 _
#align function.one_le_const Function.one_le_const
#align function.const_nonneg Function.const_nonneg

@[to_additive (attr := simp) const_pos]
theorem one_lt_const : 1 < const β a ↔ 1 < a :=
  @const_lt_const _ _ _ _ 1 a
#align function.one_lt_const Function.one_lt_const
#align function.const_pos Function.const_pos

@[to_additive (attr := simp)]
theorem const_le_one : const β a ≤ 1 ↔ a ≤ 1 :=
  @const_le_const _ _ _ _ _ 1
#align function.const_le_one Function.const_le_one
#align function.const_nonpos Function.const_nonpos

@[to_additive (attr := simp)]
theorem const_lt_one : const β a < 1 ↔ a < 1 :=
  @const_lt_const _ _ _ _ _ 1
#align function.const_lt_one Function.const_lt_one
#align function.const_neg Function.const_neg

end Function
--Porting note: Tactic code not ported yet
-- namespace Tactic

-- open Function

-- variable (ι) [Zero α] {a : α}

-- private theorem function_const_nonneg_of_pos [Preorder α] (ha : 0 < a) : 0 ≤ const ι a :=
--   const_nonneg_of_nonneg _ ha.le
-- #align tactic.function_const_nonneg_of_pos tactic.function_const_nonneg_of_pos

-- variable [Nonempty ι]

-- private theorem function_const_ne_zero : a ≠ 0 → const ι a ≠ 0 :=
--   const_ne_zero.2
-- #align tactic.function_const_ne_zero tactic.function_const_ne_zero

-- private theorem function_const_pos [Preorder α] : 0 < a → 0 < const ι a :=
--   const_pos.2
-- #align tactic.function_const_pos tactic.function_const_pos

-- /-- Extension for the `positivity` tactic: `function.const` is positive/nonnegative/nonzero if
-- its input is. -/
-- @[positivity]
-- unsafe def positivity_const : expr → tactic strictness
--   | q(Function.const $(ι) $(a)) => do
--     let strict_a ← core a
--     match strict_a with
--       | positive p =>
--         positive <$> to_expr ``(function_const_pos $(ι) $(p)) <|>
--           nonnegative <$> to_expr ``(function_const_nonneg_of_pos $(ι) $(p))
--       | nonnegative p => nonnegative <$> to_expr ``(const_nonneg_of_nonneg $(ι) $(p))
--       | nonzero p => nonzero <$> to_expr ``(function_const_ne_zero $(ι) $(p))
--   | e =>
--     pp e >>= fail ∘ format.bracket "The expression `" "` is not of the form `function.const ι a`"
-- #align tactic.positivity_const tactic.positivity_const

-- end Tactic


import Mathlib.Data.Nat.Basic
import Mathlib.Init.Data.Nat.Lemmas
import Mathlib.Algebra.Group.Defs
import Mathlib.Algebra.GroupWithZero.Defs
import Mathlib.Algebra.Ring.Basic

instance : Subsingleton (Fin 0) where
  allEq := fun.

instance : Subsingleton (Fin 1) where
  allEq := fun ⟨0, _⟩ ⟨0, _⟩ => rfl

/-- If you actually have an element of `Fin n`, then the `n` is always positive -/
lemma Fin.size_positive : ∀ (x : Fin n), 0 < n
| ⟨x, h⟩ =>
  match Nat.eq_or_lt_of_le (Nat.zero_le x) with
  | Or.inl h_eq => h_eq ▸ h
  | Or.inr h_lt => Nat.lt_trans h_lt h

lemma Fin.ext {a b : Fin n} : a.val = b.val → a = b := by
  cases a; cases b; intro h; cases h; rfl

lemma Fin.ext_iff {a b : Fin n} : a = b ↔ a.val = b.val :=
  ⟨congrArg _, ext⟩

lemma Fin.size_positive' [Nonempty (Fin n)] : 0 < n :=
  ‹Nonempty (Fin n)›.elim fun i => Fin.size_positive i

lemma zero_lt_of_lt {a : Nat} : ∀ {x : Nat}, x < a -> 0 < a
| 0, h   => h
| x+1, h => Nat.lt_trans (Nat.zero_lt_succ x) h

lemma Fin.val_eq_of_lt {n a : Nat} (h : a < n) : (Fin.ofNat' a (zero_lt_of_lt h)).val = a := by
  simp only [Fin.ofNat', Nat.mod_eq_of_lt h]

@[simp] lemma Fin.one_val : (1 : Fin (n + 2)).val = 1 := by
  simp only [OfNat.ofNat, Fin.ofNat]
  rw [Nat.mod_eq_of_lt]
  exact Nat.succ_lt_succ (Nat.zero_lt_succ _)

lemma Fin.modn_def : ∀ (a : Fin n) (m : Nat),
  a % m = Fin.mk ((a.val % m) % n) (Nat.mod_lt (a.val % m) (a.size_positive))
| ⟨a, pa⟩, m => rfl

lemma Fin.mod_def : ∀ (a m : Fin n),
  a % m = Fin.mk ((a.val % m.val) % n) (Nat.mod_lt (a.val % m.val) (a.size_positive))
| ⟨a, pa⟩, ⟨m, pm⟩ => rfl

lemma Fin.add_def : ∀ (a b : Fin n),
  a + b = (Fin.mk ((a.val + b.val) % n) (Nat.mod_lt _ (a.size_positive)))
| ⟨a, pa⟩, ⟨b, pb⟩ => rfl

lemma Fin.mul_def : ∀ (a b : Fin n),
  a * b = (Fin.mk ((a.val * b.val) % n) (Nat.mod_lt _ (a.size_positive)))
| ⟨a, pa⟩, ⟨b, pb⟩ => rfl

lemma Fin.sub_def : ∀ (a b : Fin n),
  a - b = (Fin.mk ((a + (n - b)) % n) (Nat.mod_lt _ (a.size_positive)))
| ⟨a, pa⟩, ⟨b, pb⟩ => rfl

@[simp] lemma Fin.mod_eq (a : Fin n) : a % n = a := by
  apply Fin.eq_of_val_eq
  simp only [Fin.modn_def, Nat.mod_mod]
  exact Nat.mod_eq_of_lt a.isLt

@[simp] lemma Fin.mod_eq_val (a : Fin n) : a.val % n = a.val := by
  simp only [Fin.modn_def, Nat.mod_mod]
  exact Nat.mod_eq_of_lt a.isLt

theorem Fin.mod_eq_of_lt {a b : Fin n} (h : a < b) : a % b = a := by
  apply Fin.eq_of_val_eq
  simp only [Fin.mod_def]
  rw [Nat.mod_eq_of_lt h, Nat.mod_eq_of_lt a.isLt]

/- The basic structures on `Fin` are predicated on `Fin n` being nonempty.
The Nonempty bound is there so that we can implement `Zero` in a way that satisfies
the requirements of the relevant typeclasses (for example, AddMonoid). If we were to
use `Fin n+1` for the `Zero` implementation, we would be shutting out some irreducible
definitions (notably USize.size) that are known to be inhabited, but not defined in terms
of `Nat.succ`. Since there's a blanket implementation of `∀ n, Inhabited (Fin n+1)` in
the prelude, this hopefully won't be a significant impediment. -/
section

variable {n : Nat} [Nonempty (Fin n)]

instance : OfNat (Fin n) a where
  ofNat := Fin.ofNat' a Fin.size_positive'

@[simp] lemma Fin.ofNat'_zero : (Fin.ofNat' 0 h : Fin n) = 0 := rfl
@[simp] lemma Fin.ofNat'_one : (Fin.ofNat' 1 h : Fin n) = 1 := rfl

lemma Fin.ofNat'_succ : (Fin.ofNat' i.succ Fin.size_positive' : Fin n) = (Fin.ofNat' i Fin.size_positive' : Fin n) + 1 := by
  revert n; exact fun
    | n + 2, h => ext (by simp [Fin.ofNat', Fin.add_def])
    | 1, h => Subsingleton.allEq _ _
    | 0, h => Subsingleton.allEq _ _

instance : AddCommSemigroup (Fin n) where
  add_assoc _ _ _ := by
    apply Fin.eq_of_val_eq
    simp only [Fin.add_def, Nat.mod_add_mod, Nat.add_mod_mod, Nat.add_assoc]
  add_comm _ _ := by
    apply Fin.eq_of_val_eq
    simp only [Fin.add_def, Nat.add_comm]

instance : CommSemigroup (Fin n) where
  mul_assoc a b c := by
    apply Fin.eq_of_val_eq
    simp only [Fin.mul_def]
    generalize lhs : ((a.val * b.val) % n * c.val) % n = l
    generalize rhs : a.val * (b.val * c.val % n) % n = r
    rw [← Nat.mod_eq_of_lt c.isLt, (Nat.mul_mod (a.val * b.val) c.val n).symm] at lhs
    rw [← Nat.mod_eq_of_lt a.isLt, (Nat.mul_mod a.val (b.val * c.val) n).symm,
        ← Nat.mul_assoc] at rhs
    rw [← lhs, ← rhs]
  mul_comm (a b : Fin n) : a * b = b * a := by
    apply Fin.eq_of_val_eq; simp only [Fin.mul_def, Nat.mul_comm]

@[simp] lemma Fin.zero_def : (0 : Fin n).val = (0 : Nat) :=
  show (Fin.ofNat' 0 Fin.size_positive').val = (0 : Nat) by simp only [Fin.ofNat', Nat.zero_mod]

theorem Fin.mod_lt : ∀ (i : Fin n) {m : Fin n}, (0 : Fin n) < m → (i % m) < m
| ⟨a, aLt⟩, ⟨m, mLt⟩, hp =>  by
    have zero_lt : (0 : Nat) < m := Fin.zero_def ▸ hp
    have a_mod_lt : a % m < m := Nat.mod_lt _ zero_lt
    simp only [Fin.mod_def, LT.lt]
    rw [(Nat.mod_eq_of_lt (Nat.lt_trans a_mod_lt mLt) : a % m % n = a % m)]
    exact Nat.mod_lt _ zero_lt

/- Aux lemma that makes nsmul_succ' easier -/
protected lemma Fin.nsmuls_eq (x : Nat) : ∀ (a : Fin n),
  ((Fin.ofNat' x Fin.size_positive') * a) = Fin.ofNat' (x * a.val) Fin.size_positive'
| ⟨a, isLt⟩ => by
  apply Fin.eq_of_val_eq
  simp only [Fin.ofNat', Fin.mul_def]
  generalize hy : x * a % n = y
  rw [← Nat.mod_eq_of_lt isLt, ← Nat.mul_mod, hy]

@[simp] lemma Fin.one_def : (1 : Fin n).val = (1 % n : Nat) :=
  show (Fin.ofNat' 1 Fin.size_positive').val = 1 % n by simp [Fin.ofNat']

def Fin.addOverflows? (a b : Fin n) : Bool := n <= a.val + b.val

def Fin.mulOverflows? (a b : Fin n) : Bool := n <= a.val * b.val

def Fin.subUnderflows? (a b : Fin n) : Bool := a.val < b.val

def Fin.overflowingAdd (a b : Fin n) : (Bool × Fin n) := (n <= a.val + b.val, a + b)

def Fin.overflowingMul (a b : Fin n) : (Bool × Fin n) := (n <= a.val * b.val, a * b)

def Fin.underflowingSub (a b : Fin n) : (Bool × Fin n) := (a.val < b.val, a - b)

def Fin.checkedAdd (a b : Fin n) : Option (Fin n) :=
  match Fin.overflowingAdd a b with
  | (true, _) => none
  | (false, sum) => some (sum)

def Fin.checkedMul (a b : Fin n) : Option (Fin n) :=
  match Fin.overflowingMul a b with
  | (true, _) => none
  | (false, prod) => some (prod)

def Fin.checkedSub (a b : Fin n) : Option (Fin n) :=
  match Fin.underflowingSub a b with
  | (true, _) => none
  | (false, diff) => some (diff)

lemma Fin.checked_add_spec (a b : Fin n) :
  (Fin.checkedAdd a b).isSome = true ↔ a.val + b.val < n := by
  by_cases n <= a.val + b.val <;>
    simp_all [checkedAdd, Option.isSome, overflowingAdd, decide_eq_true, decide_eq_false]

lemma Fin.checked_mul_spec (a b : Fin n) :
  (Fin.checkedMul a b).isSome = true ↔ a.val * b.val < n := by
  simp only [checkedMul, overflowingMul, Option.isSome]
  refine Iff.intro ?mp ?mpr <;> intro h
  case mp =>
    by_cases hx : n <= a.val * b.val
    case pos => simp only [(decide_eq_true_iff (n <= a.val * b.val)).mpr hx] at h
    case neg => exact Nat.lt_of_not_le hx
  case mpr => simp only [decide_eq_false (Nat.not_le_of_lt h : ¬n <= a.val * b.val)]

lemma Fin.checked_sub_spec (a b : Fin n) :
  (Fin.checkedSub a b).isSome = true ↔ b.val <= a.val := by
  simp only [checkedSub, underflowingSub, Option.isSome]
  refine Iff.intro ?mp ?mpr <;> intro h
  case mp =>
    by_cases hx : a.val < b.val
    case pos => simp only [(decide_eq_true_iff (a.val < b.val)).mpr hx] at h
    case neg => exact Nat.le_of_not_lt hx
  case mpr => simp only [decide_eq_false (Nat.not_lt_of_le h : ¬a.val < b.val)]

instance : AddCommMonoid (Fin n) where
  __ := inferInstanceAs (AddCommSemigroup (Fin n))

  add_zero (a : Fin n) : a + 0 = a := by
    apply Fin.eq_of_val_eq
    simp only [Fin.add_def, Fin.zero_def, Nat.add_zero]
    exact Nat.mod_eq_of_lt a.isLt
  zero_add (a : Fin n) : 0 + a = a := by
    apply Fin.eq_of_val_eq
    simp only [Fin.add_def, Fin.zero_def, Nat.zero_add]
    exact Nat.mod_eq_of_lt a.isLt

  nsmul := fun x a => (Fin.ofNat' x a.size_positive) * a
  nsmul_zero' := fun _ => by
    apply Fin.eq_of_val_eq
    simp [Fin.mul_def, Fin.ofNat', Fin.zero_def, Nat.zero_mul, Nat.zero_mod]
  nsmul_succ' := fun x a => by
    simp only [Fin.nsmuls_eq]
    simp [Fin.ofNat', Fin.add_def]
    exact congrArg (fun x => x % n) (Nat.add_comm (x * a.val) (a.val) ▸ Nat.succ_mul x a.val)

instance : AddMonoidWithOne (Fin n) where
  __ := inferInstanceAs (AddCommMonoid (Fin n))
  natCast n := Fin.ofNat' n Fin.size_positive'
  natCast_zero := rfl
  natCast_succ _ := Fin.ofNat'_succ

private theorem Fin.mul_one (a : Fin n) : a * 1 = a := by
  apply Fin.eq_of_val_eq
  simp only [Fin.mul_def, Fin.one_def]
  cases n with
  | zero => exact (False.elim a.elim0)
  | succ n =>
    match Nat.lt_or_eq_of_le (Nat.mod_le 1 n.succ) with
    | Or.inl h_lt =>
      have h_eq : 1 % n.succ = 0 := Nat.eq_zero_of_le_zero (Nat.le_of_lt_succ h_lt)
      have hnz : n = 0 := Nat.eq_zero_of_le_zero (Nat.le_of_succ_le_succ (Nat.le_of_mod_lt h_lt))
      have haz : a.val = 0 := Nat.eq_zero_of_le_zero (Nat.le_of_succ_le_succ (hnz ▸ a.isLt))
      rw [h_eq, haz]
      simp only [Nat.zero_mul, Nat.zero_mod]
    | Or.inr h_eq => simp only [h_eq, Nat.mul_one, Nat.mod_eq_of_lt (a.isLt)]

instance : MonoidWithZero (Fin n) where
  __ := inferInstanceAs (CommSemigroup (Fin n))
  mul_one := Fin.mul_one
  one_mul _ := by rw [mul_comm, Fin.mul_one]
  npow_zero' _ := rfl
  npow_succ' _ _ := rfl
  zero_mul x := by
    apply Fin.eq_of_val_eq
    simp only [Fin.mul_def, Fin.zero_def, Nat.zero_mul, Nat.zero_mod]
  mul_zero x := by
    apply Fin.eq_of_val_eq
    simp only [Fin.mul_def, Fin.zero_def, Nat.mul_zero, Nat.zero_mod]

private theorem Fin.mul_add (a b c : Fin n) : a * (b + c) = a * b + a * c := by
    apply Fin.eq_of_val_eq
    simp [Fin.mul_def, Fin.add_def]
    generalize lhs : a.val * ((b.val + c.val) % n) % n = l
    rw [(Nat.mod_eq_of_lt a.isLt).symm, ← Nat.mul_mod] at lhs
    rw [← lhs, left_distrib]

instance : CommSemiring (Fin n) where
  __ := inferInstanceAs (MonoidWithZero (Fin n))
  __ := inferInstanceAs (CommSemigroup (Fin n))
  __ := inferInstanceAs (AddCommMonoid (Fin n))
  __ := inferInstanceAs (AddMonoidWithOne (Fin n))
  left_distrib := Fin.mul_add
  right_distrib a b c := (by rw [mul_comm, Fin.mul_add, mul_comm c, mul_comm c])

instance : Neg (Fin n) where
  neg a := ⟨(n - a) % n, Nat.mod_lt _ (lt_of_le_of_lt (Nat.zero_le _) a.isLt)⟩

lemma Fin.neg_def : (-a : Fin n) = ⟨(n - a) % n, Nat.mod_lt _ (lt_of_le_of_lt (Nat.zero_le _) a.isLt)⟩ := rfl

protected def Fin.ofInt'' : Int → Fin n
  | Int.ofNat a => Fin.ofNat' a Fin.size_positive'
  | Int.negSucc a => -(Fin.ofNat' a.succ Fin.size_positive')

private theorem Fin.sub_eq_add_neg : ∀ (a b : Fin n), a - b = a + -b := by
  simp [Fin.add_def, Fin.sub_def, Neg.neg]

private theorem Fin.add_left_neg (a : Fin n) : -a + a = 0 := by
    rw [add_comm, ← Fin.sub_eq_add_neg]
    apply Fin.eq_of_val_eq
    simp [Fin.sub_def, (Nat.add_sub_cancel' (Nat.le_of_lt a.isLt)), Nat.mod_self]

def Fin.ofInt' : ℤ → Fin n
  | (i : ℕ) => i
  | (Int.negSucc i) => -↑(i + 1 : ℕ)

instance : AddGroupWithOne (Fin n) where
  __ := inferInstanceAs (AddMonoidWithOne (Fin n))
  gsmul_zero' := by simp [gsmul_rec, nsmul_rec]
  gsmul_succ' := by simp [gsmul_rec, nsmul_rec, -Int.ofNat_eq_cast]
  gsmul_neg' := by simp [gsmul_rec, nsmul_rec, -Int.ofNat_eq_cast]
  sub_eq_add_neg := Fin.sub_eq_add_neg
  add_left_neg := Fin.add_left_neg
  intCast := Fin.ofInt'
  intCast_ofNat _ := rfl
  intCast_negSucc _ := rfl

instance : CommRing (Fin n) where
  __ := inferInstanceAs (AddGroupWithOne (Fin n))
  __ := inferInstanceAs (CommSemiring (Fin n))

lemma Fin.gt_wf : WellFounded (fun a b : Fin n => b < a) :=
  Subrelation.wf (@fun a i h => ⟨h, i.2⟩) (invImage (fun i => i.1) (Nat.upRel n)).wf

/-- A well-ordered relation for "upwards" induction on `Fin n`. -/
def Fin.upRel (n : ℕ) : WellFoundedRelation (Fin n) := ⟨_, gt_wf⟩

lemma Fin.le_refl (f : Fin n) : f ≤ f := Nat.le_refl _
lemma Fin.le_trans (a b c : Fin n) : a ≤ b → b ≤ c → a ≤ c := Nat.le_trans
lemma Fin.lt_iff_le_not_le (a b : Fin n) : a < b ↔ a ≤ b ∧ ¬b ≤ a := Nat.lt_iff_le_not_le
lemma Fin.le_antisymm (a b : Fin n) : a ≤ b → b ≤ a → a = b := by
  intro h1 h2
  apply Fin.eq_of_val_eq
  exact Nat.le_antisymm h1 h2

lemma Fin.le_total (a b : Fin n) : a ≤ b ∨ b ≤ a := Nat.le_total _ _

instance : LinearOrder (Fin n) where
  le_refl := Fin.le_refl
  le_trans := Fin.le_trans
  lt_iff_le_not_le := Fin.lt_iff_le_not_le
  le_antisymm := Fin.le_antisymm
  le_total := Fin.le_total
  decidable_le := inferInstance

end


/-
Copyright (c) 2022 Joseph Myers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joseph Myers

! This file was ported from Lean 3 source module analysis.convex.strict_convex_between
! leanprover-community/mathlib commit e1730698f86560a342271c0471e4cb72d021aabf
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Convex.Between
import Mathbin.Analysis.Convex.StrictConvexSpace

/-!
# Betweenness in affine spaces for strictly convex spaces

This file proves results about betweenness for points in an affine space for a strictly convex
space.

-/

variable {V P : Type _} [NormedAddCommGroup V] [NormedSpace ℝ V] [PseudoMetricSpace P]

variable [NormedAddTorsor V P] [StrictConvexSpace ℝ V]

include V

theorem Sbtw.dist_lt_max_dist (p : P) {p₁ p₂ p₃ : P} (h : Sbtw ℝ p₁ p₂ p₃) :
    dist p₂ p < max (dist p₁ p) (dist p₃ p) :=
  by
  have hp₁p₃ : p₁ -ᵥ p ≠ p₃ -ᵥ p := by simpa using h.left_ne_right
  rw [Sbtw, ← wbtw_vsub_const_iff p, Wbtw, affineSegment_eq_segment, ← insert_endpoints_openSegment,
    Set.mem_insert_iff, Set.mem_insert_iff] at h
  rcases h with ⟨h | h | h, hp₂p₁, hp₂p₃⟩
  · rw [vsub_left_cancel_iff] at h
    exact False.elim (hp₂p₁ h)
  · rw [vsub_left_cancel_iff] at h
    exact False.elim (hp₂p₃ h)
  · rw [openSegment_eq_image, Set.mem_image] at h
    rcases h with ⟨r, ⟨hr0, hr1⟩, hr⟩
    simp_rw [@dist_eq_norm_vsub V, ← hr]
    exact
      norm_combo_lt_of_ne (le_max_left _ _) (le_max_right _ _) hp₁p₃ (sub_pos.2 hr1) hr0 (by abel)
#align sbtw.dist_lt_max_dist Sbtw.dist_lt_max_dist

theorem Wbtw.dist_le_max_dist (p : P) {p₁ p₂ p₃ : P} (h : Wbtw ℝ p₁ p₂ p₃) :
    dist p₂ p ≤ max (dist p₁ p) (dist p₃ p) :=
  by
  by_cases hp₁ : p₂ = p₁; · simp [hp₁]
  by_cases hp₃ : p₂ = p₃; · simp [hp₃]
  have hs : Sbtw ℝ p₁ p₂ p₃ := ⟨h, hp₁, hp₃⟩
  exact (hs.dist_lt_max_dist _).le
#align wbtw.dist_le_max_dist Wbtw.dist_le_max_dist

/-- Given three collinear points, two (not equal) with distance `r` from `p` and one with
distance at most `r` from `p`, the third point is weakly between the other two points. -/
theorem Collinear.wbtw_of_dist_eq_of_dist_le {p p₁ p₂ p₃ : P} {r : ℝ}
    (h : Collinear ℝ ({p₁, p₂, p₃} : Set P)) (hp₁ : dist p₁ p = r) (hp₂ : dist p₂ p ≤ r)
    (hp₃ : dist p₃ p = r) (hp₁p₃ : p₁ ≠ p₃) : Wbtw ℝ p₁ p₂ p₃ :=
  by
  rcases h.wbtw_or_wbtw_or_wbtw with (hw | hw | hw)
  · exact hw
  · by_cases hp₃p₂ : p₃ = p₂
    · simp [hp₃p₂]
    have hs : Sbtw ℝ p₂ p₃ p₁ := ⟨hw, hp₃p₂, hp₁p₃.symm⟩
    have hs' := hs.dist_lt_max_dist p
    rw [hp₁, hp₃, lt_max_iff, lt_self_iff_false, or_false_iff] at hs'
    exact False.elim (hp₂.not_lt hs')
  · by_cases hp₁p₂ : p₁ = p₂
    · simp [hp₁p₂]
    have hs : Sbtw ℝ p₃ p₁ p₂ := ⟨hw, hp₁p₃, hp₁p₂⟩
    have hs' := hs.dist_lt_max_dist p
    rw [hp₁, hp₃, lt_max_iff, lt_self_iff_false, false_or_iff] at hs'
    exact False.elim (hp₂.not_lt hs')
#align collinear.wbtw_of_dist_eq_of_dist_le Collinear.wbtw_of_dist_eq_of_dist_le

/-- Given three collinear points, two (not equal) with distance `r` from `p` and one with
distance less than `r` from `p`, the third point is strictly between the other two points. -/
theorem Collinear.sbtw_of_dist_eq_of_dist_lt {p p₁ p₂ p₃ : P} {r : ℝ}
    (h : Collinear ℝ ({p₁, p₂, p₃} : Set P)) (hp₁ : dist p₁ p = r) (hp₂ : dist p₂ p < r)
    (hp₃ : dist p₃ p = r) (hp₁p₃ : p₁ ≠ p₃) : Sbtw ℝ p₁ p₂ p₃ :=
  by
  refine' ⟨h.wbtw_of_dist_eq_of_dist_le hp₁ hp₂.le hp₃ hp₁p₃, _, _⟩
  · rintro rfl
    exact hp₂.ne hp₁
  · rintro rfl
    exact hp₂.ne hp₃
#align collinear.sbtw_of_dist_eq_of_dist_lt Collinear.sbtw_of_dist_eq_of_dist_lt


/-
Copyright (c) 2018 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import order.conditionally_complete_lattice.basic
import data.set.finite

/-!
# Conditionally complete lattices and finite sets.

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

-/

open set

variables {α β γ : Type*}

section conditionally_complete_lattice
variables [conditionally_complete_lattice α] {s t : set α} {a b : α}

lemma finset.nonempty.sup'_eq_cSup_image {s : finset β} (hs : s.nonempty) (f : β → α) :
  s.sup' hs f = Sup (f '' s) :=
eq_of_forall_ge_iff $ λ a,
  by simp [cSup_le_iff (s.finite_to_set.image f).bdd_above (hs.to_set.image f)]

lemma finset.nonempty.sup'_id_eq_cSup {s : finset α} (hs : s.nonempty) :
  s.sup' hs id = Sup s :=
by rw [hs.sup'_eq_cSup_image, image_id]

end conditionally_complete_lattice

section conditionally_complete_linear_order
variables [conditionally_complete_linear_order α] {s t : set α} {a b : α}

lemma finset.nonempty.cSup_eq_max' {s : finset α} (h : s.nonempty) : Sup ↑s = s.max' h :=
eq_of_forall_ge_iff $ λ a, (cSup_le_iff s.bdd_above h.to_set).trans (s.max'_le_iff h).symm

lemma finset.nonempty.cInf_eq_min' {s : finset α} (h : s.nonempty) : Inf ↑s = s.min' h :=
@finset.nonempty.cSup_eq_max' αᵒᵈ _ s h

lemma finset.nonempty.cSup_mem {s : finset α} (h : s.nonempty) : Sup (s : set α) ∈ s :=
by { rw h.cSup_eq_max', exact s.max'_mem _ }

lemma finset.nonempty.cInf_mem {s : finset α} (h : s.nonempty) : Inf (s : set α) ∈ s :=
@finset.nonempty.cSup_mem αᵒᵈ _ _ h

lemma set.nonempty.cSup_mem (h : s.nonempty) (hs : s.finite) : Sup s ∈ s :=
by { lift s to finset α using hs, exact finset.nonempty.cSup_mem h }

lemma set.nonempty.cInf_mem (h : s.nonempty) (hs : s.finite) : Inf s ∈ s :=
@set.nonempty.cSup_mem αᵒᵈ _ _ h hs

lemma set.finite.cSup_lt_iff (hs : s.finite) (h : s.nonempty) : Sup s < a ↔ ∀ x ∈ s, x < a :=
⟨λ h x hx, (le_cSup hs.bdd_above hx).trans_lt h, λ H, H _ $ h.cSup_mem hs⟩

lemma set.finite.lt_cInf_iff (hs : s.finite) (h : s.nonempty) : a < Inf s ↔ ∀ x ∈ s, a < x :=
@set.finite.cSup_lt_iff αᵒᵈ _ _ _ hs h

end conditionally_complete_linear_order

/-!
### Relation between `Sup` / `Inf` and `finset.sup'` / `finset.inf'`

Like the `Sup` of a `conditionally_complete_lattice`, `finset.sup'` also requires the set to be
non-empty. As a result, we can translate between the two.
-/

namespace finset

lemma sup'_eq_cSup_image [conditionally_complete_lattice β] (s : finset α) (H) (f : α → β) :
  s.sup' H f = Sup (f '' s) :=
begin
  apply le_antisymm,
  { refine (finset.sup'_le _ _ $ λ a ha, _),
    refine le_cSup ⟨s.sup' H f, _⟩ ⟨a, ha, rfl⟩,
    rintros i ⟨j, hj, rfl⟩,
    exact finset.le_sup' _ hj },
  { apply cSup_le ((coe_nonempty.mpr H).image _),
    rintros _ ⟨a, ha, rfl⟩,
    exact finset.le_sup' _ ha, }
end

lemma inf'_eq_cInf_image [conditionally_complete_lattice β] (s : finset α) (H) (f : α → β) :
  s.inf' H f = Inf (f '' s) :=
@sup'_eq_cSup_image _ βᵒᵈ _ _ H _

lemma sup'_id_eq_cSup [conditionally_complete_lattice α] (s : finset α) (H) :
  s.sup' H id = Sup s :=
by rw [sup'_eq_cSup_image s H, set.image_id]

lemma inf'_id_eq_cInf [conditionally_complete_lattice α] (s : finset α) (H) :
  s.inf' H id = Inf s :=
@sup'_id_eq_cSup αᵒᵈ _ _ H

end finset


/-
Copyright (c) 2019 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau

Multiplication of submodules of an algebra.
-/

import ring_theory.algebra ring_theory.noetherian

universes u v

open lattice submodule

namespace algebra

variables {R : Type u} [comm_ring R]

section ring

variables {A : Type v} [ring A] [algebra R A]

set_option class.instance_max_depth 50
instance : has_mul (submodule R A) :=
⟨λ M N, ⨆ s : M, N.map $ algebra.lmul R A s.1⟩
set_option class.instance_max_depth 32

variables (S T : set A) {M N P Q : submodule R A} {m n : A}

theorem mul_mem_mul (hm : m ∈ M) (hn : n ∈ N) : m * n ∈ M * N :=
(le_supr _ ⟨m, hm⟩ : _ ≤ M * N) ⟨n, hn, rfl⟩

theorem mul_le : M * N ≤ P ↔ ∀ (m ∈ M) (n ∈ N), m * n ∈ P :=
⟨λ H m hm n hn, H $ mul_mem_mul hm hn,
λ H, supr_le $ λ ⟨m, hm⟩, map_le_iff_le_comap.2 $ λ n hn, H m hm n hn⟩

@[elab_as_eliminator] protected theorem mul_induction_on
  {C : A → Prop} {r : A} (hr : r ∈ M * N)
  (hm : ∀ (m ∈ M) (n ∈ N), C (m * n))
  (h0 : C 0) (ha : ∀ x y, C x → C y → C (x + y))
  (hs : ∀ (r : R) x, C x → C (r • x)) : C r :=
(@mul_le _ _ _ _ _ _ _ ⟨C, h0, ha, hs⟩).2 hm hr

variables R
theorem span_mul_span : span R S * span R T = span R ((S.prod T).image (λ p, p.1 * p.2)) :=
le_antisymm
  (mul_le.2 $ λ x1 hx1 x2 hx2, span_induction hx1
    (λ y1 hy1, span_induction hx2
      (λ y2 hy2, subset_span ⟨(y1, y2), ⟨hy1, hy2⟩, rfl⟩)
      ((mul_zero y1).symm ▸ zero_mem _)
      (λ r1 r2, (mul_add y1 r1 r2).symm ▸ add_mem _)
      (λ s r, (algebra.mul_smul_comm s y1 r).symm ▸ smul_mem _ _))
    ((zero_mul x2).symm ▸ zero_mem _)
    (λ r1 r2, (add_mul r1 r2 x2).symm ▸ add_mem _)
    (λ s r, (algebra.smul_mul_assoc s r x2).symm ▸ smul_mem _ _))
  (span_le.2 (set.image_subset_iff.2 $ λ ⟨x1, x2⟩ ⟨hx1, hx2⟩,
    mul_mem_mul (subset_span hx1) (subset_span hx2)))
variables {R}

theorem fg_mul (hm : M.fg) (hn : N.fg) : (M * N).fg :=
let ⟨m, hf1, hm⟩ := fg_def.1 hm, ⟨n, hf2, hn⟩ := fg_def.1 hn in
fg_def.2 ⟨(m.prod n).image (λ p, p.1 * p.2),
set.finite_image _ (set.finite_prod hf1 hf2),
span_mul_span R m n ▸ hm ▸ hn ▸ rfl⟩

variables (M N P Q)
set_option class.instance_max_depth 50
protected theorem mul_assoc : (M * N) * P = M * (N * P) :=
le_antisymm (mul_le.2 $ λ mn hmn p hp, suffices M * N ≤ (M * (N * P)).comap ((algebra.lmul R A).flip p), from this hmn,
  mul_le.2 $ λ m hm n hn, show m * n * p ∈ M * (N * P), from
  (mul_assoc m n p).symm ▸ mul_mem_mul hm (mul_mem_mul hn hp))
(mul_le.2 $ λ m hm np hnp, suffices N * P ≤ (M * N * P).comap (algebra.lmul R A m), from this hnp,
  mul_le.2 $ λ n hn p hp, show m * (n * p) ∈ M * N * P, from
  mul_assoc m n p ▸ mul_mem_mul (mul_mem_mul hm hn) hp)
set_option class.instance_max_depth 32

@[simp] theorem mul_bot : M * ⊥ = ⊥ :=
eq_bot_iff.2 $ mul_le.2 $ λ m hm n hn, by rw [submodule.mem_bot] at hn ⊢; rw [hn, mul_zero]

@[simp] theorem bot_mul : ⊥ * M = ⊥ :=
eq_bot_iff.2 $ mul_le.2 $ λ m hm n hn, by rw [submodule.mem_bot] at hm ⊢; rw [hm, zero_mul]
variables {M N P Q}

@[mono] theorem mul_le_mul (hmp : M ≤ P) (hnq : N ≤ Q) : M * N ≤ P * Q :=
mul_le.2 $ λ m hm n hn, mul_mem_mul (hmp hm) (hnq hn)

theorem mul_le_mul_left (h : M ≤ N) : M * P ≤ N * P :=
mul_le_mul h (le_refl P)

theorem mul_le_mul_right (h : N ≤ P) : M * N ≤ M * P :=
mul_le_mul (le_refl M) h

variables (M N P)
theorem mul_sup : M * (N ⊔ P) = M * N ⊔ M * P :=
le_antisymm (mul_le.2 $ λ m hm np hnp, let ⟨n, hn, p, hp, hnp⟩ := mem_sup.1 hnp in
  mem_sup.2 ⟨_, mul_mem_mul hm hn, _, mul_mem_mul hm hp, hnp ▸ (mul_add m n p).symm⟩)
(sup_le (mul_le_mul_right le_sup_left) (mul_le_mul_right le_sup_right))

theorem sup_mul : (M ⊔ N) * P = M * P ⊔ N * P :=
le_antisymm (mul_le.2 $ λ mn hmn p hp, let ⟨m, hm, n, hn, hmn⟩ := mem_sup.1 hmn in
  mem_sup.2 ⟨_, mul_mem_mul hm hp, _, mul_mem_mul hn hp, hmn ▸ (add_mul m n p).symm⟩)
(sup_le (mul_le_mul_left le_sup_left) (mul_le_mul_left le_sup_right))
variables {M N P}

instance : semigroup (submodule R A) :=
{ mul := (*),
  mul_assoc := algebra.mul_assoc }

instance : mul_zero_class (submodule R A) :=
{ zero_mul := bot_mul,
  mul_zero := mul_bot,
  .. submodule.add_comm_monoid, .. algebra.semigroup  }

instance : distrib (submodule R A) :=
{ left_distrib := mul_sup,
  right_distrib := sup_mul,
  .. submodule.add_comm_monoid, .. algebra.semigroup }

end ring

section comm_ring

variables {A : Type v} [comm_ring A] [algebra R A]
variables {M N : submodule R A} {m n : A}

theorem mul_mem_mul_rev (hm : m ∈ M) (hn : n ∈ N) : n * m ∈ M * N :=
mul_comm m n ▸ mul_mem_mul hm hn

variables (M N)
protected theorem mul_comm : M * N = N * M :=
le_antisymm (mul_le.2 $ λ r hrm s hsn, mul_mem_mul_rev hsn hrm)
(mul_le.2 $ λ r hrn s hsm, mul_mem_mul_rev hsm hrn)

instance : comm_semigroup (submodule R A) :=
{ mul_comm := algebra.mul_comm,
  .. algebra.semigroup }

end comm_ring

end algebra