/-
Copyright (c) 2021 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Sébastien Gouëzel
-/
import measure_theory.measure.lebesgue
import measure_theory.measure.haar
import linear_algebra.finite_dimensional
import analysis.normed_space.pointwise
import measure_theory.group.pointwise
import measure_theory.measure.doubling

/-!
# Relationship between the Haar and Lebesgue measures

We prove that the Haar measure and Lebesgue measure are equal on `ℝ` and on `ℝ^ι`, in
`measure_theory.add_haar_measure_eq_volume` and `measure_theory.add_haar_measure_eq_volume_pi`.

We deduce basic properties of any Haar measure on a finite dimensional real vector space:
* `map_linear_map_add_haar_eq_smul_add_haar`: a linear map rescales the Haar measure by the
  absolute value of its determinant.
* `add_haar_preimage_linear_map` : when `f` is a linear map with nonzero determinant, the measure
  of `f ⁻¹' s` is the measure of `s` multiplied by the absolute value of the inverse of the
  determinant of `f`.
* `add_haar_image_linear_map` : when `f` is a linear map, the measure of `f '' s` is the
  measure of `s` multiplied by the absolute value of the determinant of `f`.
* `add_haar_submodule` : a strict submodule has measure `0`.
* `add_haar_smul` : the measure of `r • s` is `|r| ^ dim * μ s`.
* `add_haar_ball`: the measure of `ball x r` is `r ^ dim * μ (ball 0 1)`.
* `add_haar_closed_ball`: the measure of `closed_ball x r` is `r ^ dim * μ (ball 0 1)`.
* `add_haar_sphere`: spheres have zero measure.

This makes it possible to associate a Lebesgue measure to an `n`-alternating map in dimension `n`.
This measure is called `alternating_map.measure`. Its main property is
`ω.measure_parallelepiped v`, stating that the associated measure of the parallelepiped spanned
by vectors `v₁, ..., vₙ` is given by `|ω v|`.

We also show that a Lebesgue density point `x` of a set `s` (with respect to closed balls) has
density one for the rescaled copies `{x} + r • t` of a given set `t` with positive measure, in
`tendsto_add_haar_inter_smul_one_of_density_one`. In particular, `s` intersects `{x} + r • t` for
small `r`, see `eventually_nonempty_inter_smul_of_density_one`.
-/

open topological_space set filter metric
open_locale ennreal pointwise topology nnreal

/-- The interval `[0,1]` as a compact set with non-empty interior. -/
def topological_space.positive_compacts.Icc01 : positive_compacts ℝ :=
{ carrier := Icc 0 1,
  is_compact' := is_compact_Icc,
  interior_nonempty' := by simp_rw [interior_Icc, nonempty_Ioo, zero_lt_one] }

universe u

/-- The set `[0,1]^ι` as a compact set with non-empty interior. -/
def topological_space.positive_compacts.pi_Icc01 (ι : Type*) [fintype ι] :
  positive_compacts (ι → ℝ) :=
{ carrier := pi univ (λ i, Icc 0 1),
  is_compact' := is_compact_univ_pi (λ i, is_compact_Icc),
  interior_nonempty' := by simp only [interior_pi_set, set.to_finite, interior_Icc,
    univ_pi_nonempty_iff, nonempty_Ioo, implies_true_iff, zero_lt_one] }

namespace measure_theory

open measure topological_space.positive_compacts finite_dimensional

/-!
### The Lebesgue measure is a Haar measure on `ℝ` and on `ℝ^ι`.
-/

/-- The Haar measure equals the Lebesgue measure on `ℝ`. -/
lemma add_haar_measure_eq_volume : add_haar_measure Icc01 = volume :=
by { convert (add_haar_measure_unique volume Icc01).symm, simp [Icc01] }

/-- The Haar measure equals the Lebesgue measure on `ℝ^ι`. -/
lemma add_haar_measure_eq_volume_pi (ι : Type*) [fintype ι] :
  add_haar_measure (pi_Icc01 ι) = volume :=
begin
  convert (add_haar_measure_unique volume (pi_Icc01 ι)).symm,
  simp only [pi_Icc01, volume_pi_pi (λ i, Icc (0 : ℝ) 1), positive_compacts.coe_mk,
    compacts.coe_mk, finset.prod_const_one, ennreal.of_real_one, real.volume_Icc, one_smul,
    sub_zero],
end

instance is_add_haar_measure_volume_pi (ι : Type*) [fintype ι] :
  is_add_haar_measure (volume : measure (ι → ℝ)) :=
by { rw ← add_haar_measure_eq_volume_pi, apply_instance }

namespace measure

/-!
### Strict subspaces have zero measure
-/

/-- If a set is disjoint of its translates by infinitely many bounded vectors, then it has measure
zero. This auxiliary lemma proves this assuming additionally that the set is bounded. -/
lemma add_haar_eq_zero_of_disjoint_translates_aux
  {E : Type*} [normed_add_comm_group E] [normed_space ℝ E] [measurable_space E] [borel_space E]
  [finite_dimensional ℝ E] (μ : measure E) [is_add_haar_measure μ]
  {s : set E} (u : ℕ → E) (sb : bounded s) (hu : bounded (range u))
  (hs : pairwise (disjoint on (λ n, {u n} + s))) (h's : measurable_set s) :
  μ s = 0 :=
begin
  by_contra h,
  apply lt_irrefl ∞,
  calc
  ∞ = ∑' (n : ℕ), μ s : (ennreal.tsum_const_eq_top_of_ne_zero h).symm
  ... = ∑' (n : ℕ), μ ({u n} + s) :
    by { congr' 1, ext1 n, simp only [image_add_left, measure_preimage_add, singleton_add] }
  ... = μ (⋃ n, {u n} + s) :
    by rw measure_Union hs
      (λ n, by simpa only [image_add_left, singleton_add] using measurable_id.const_add _ h's)
  ... = μ (range u + s) : by rw [← Union_add, Union_singleton_eq_range]
  ... < ∞ : bounded.measure_lt_top (hu.add sb)
end

/-- If a set is disjoint of its translates by infinitely many bounded vectors, then it has measure
zero. -/
lemma add_haar_eq_zero_of_disjoint_translates
  {E : Type*} [normed_add_comm_group E] [normed_space ℝ E] [measurable_space E] [borel_space E]
  [finite_dimensional ℝ E] (μ : measure E) [is_add_haar_measure μ]
  {s : set E} (u : ℕ → E) (hu : bounded (range u))
  (hs : pairwise (disjoint on (λ n, {u n} + s))) (h's : measurable_set s) :
  μ s = 0 :=
begin
  suffices H : ∀ R, μ (s ∩ closed_ball 0 R) = 0,
  { apply le_antisymm _ (zero_le _),
    calc μ s ≤ ∑' (n : ℕ), μ (s ∩ closed_ball 0 n) :
      by { conv_lhs { rw ← Union_inter_closed_ball_nat s 0 }, exact measure_Union_le _ }
    ... = 0 : by simp only [H, tsum_zero] },
  assume R,
  apply add_haar_eq_zero_of_disjoint_translates_aux μ u
    (bounded.mono (inter_subset_right _ _) bounded_closed_ball) hu _
    (h's.inter (measurable_set_closed_ball)),
  apply pairwise_disjoint.mono hs (λ n, _),
  exact add_subset_add (subset.refl _) (inter_subset_left _ _)
end

/-- A strict vector subspace has measure zero. -/
lemma add_haar_submodule
  {E : Type*} [normed_add_comm_group E] [normed_space ℝ E] [measurable_space E] [borel_space E]
  [finite_dimensional ℝ E] (μ : measure E) [is_add_haar_measure μ]
  (s : submodule ℝ E) (hs : s ≠ ⊤) : μ s = 0 :=
begin
  obtain ⟨x, hx⟩ : ∃ x, x ∉ s,
    by simpa only [submodule.eq_top_iff', not_exists, ne.def, not_forall] using hs,
  obtain ⟨c, cpos, cone⟩ : ∃ (c : ℝ), 0 < c ∧ c < 1 := ⟨1/2, by norm_num, by norm_num⟩,
  have A : bounded (range (λ (n : ℕ), (c ^ n) • x)),
  { have : tendsto (λ (n : ℕ), (c ^ n) • x) at_top (𝓝 ((0 : ℝ) • x)) :=
      (tendsto_pow_at_top_nhds_0_of_lt_1 cpos.le cone).smul_const x,
    exact bounded_range_of_tendsto _ this },
  apply add_haar_eq_zero_of_disjoint_translates μ _ A _
    (submodule.closed_of_finite_dimensional s).measurable_set,
  assume m n hmn,
  simp only [function.on_fun, image_add_left, singleton_add, disjoint_left, mem_preimage,
             set_like.mem_coe],
  assume y hym hyn,
  have A : (c ^ n - c ^ m) • x ∈ s,
  { convert s.sub_mem hym hyn,
    simp only [sub_smul, neg_sub_neg, add_sub_add_right_eq_sub] },
  have H : c ^ n - c ^ m ≠ 0,
    by simpa only [sub_eq_zero, ne.def] using (strict_anti_pow cpos cone).injective.ne hmn.symm,
  have : x ∈ s,
  { convert s.smul_mem (c ^ n - c ^ m)⁻¹ A,
    rw [smul_smul, inv_mul_cancel H, one_smul] },
  exact hx this
end

/-- A strict affine subspace has measure zero. -/
lemma add_haar_affine_subspace
  {E : Type*} [normed_add_comm_group E] [normed_space ℝ E] [measurable_space E] [borel_space E]
  [finite_dimensional ℝ E] (μ : measure E) [is_add_haar_measure μ]
  (s : affine_subspace ℝ E) (hs : s ≠ ⊤) : μ s = 0 :=
begin
  rcases s.eq_bot_or_nonempty with rfl|hne,
  { rw [affine_subspace.bot_coe, measure_empty] },
  rw [ne.def, ← affine_subspace.direction_eq_top_iff_of_nonempty hne] at hs,
  rcases hne with ⟨x, hx : x ∈ s⟩,
  simpa only [affine_subspace.coe_direction_eq_vsub_set_right hx, vsub_eq_sub,
    sub_eq_add_neg, image_add_right, neg_neg, measure_preimage_add_right]
    using add_haar_submodule μ s.direction hs
end

/-!
### Applying a linear map rescales Haar measure by the determinant

We first prove this on `ι → ℝ`, using that this is already known for the product Lebesgue
measure (thanks to matrices computations). Then, we extend this to any finite-dimensional real
vector space by using a linear equiv with a space of the form `ι → ℝ`, and arguing that such a
linear equiv maps Haar measure to Haar measure.
-/

lemma map_linear_map_add_haar_pi_eq_smul_add_haar
  {ι : Type*} [finite ι] {f : (ι → ℝ) →ₗ[ℝ] (ι → ℝ)} (hf : f.det ≠ 0)
  (μ : measure (ι → ℝ)) [is_add_haar_measure μ] :
  measure.map f μ = ennreal.of_real (abs (f.det)⁻¹) • μ :=
begin
  casesI nonempty_fintype ι,
  /- We have already proved the result for the Lebesgue product measure, using matrices.
  We deduce it for any Haar measure by uniqueness (up to scalar multiplication). -/
  have := add_haar_measure_unique μ (pi_Icc01 ι),
  rw [this, add_haar_measure_eq_volume_pi, measure.map_smul,
    real.map_linear_map_volume_pi_eq_smul_volume_pi hf, smul_comm],
end

variables {E : Type*} [normed_add_comm_group E] [normed_space ℝ E] [measurable_space E]
  [borel_space E] [finite_dimensional ℝ E] (μ : measure E) [is_add_haar_measure μ]
  {F : Type*} [normed_add_comm_group F] [normed_space ℝ F] [complete_space F]

lemma map_linear_map_add_haar_eq_smul_add_haar
  {f : E →ₗ[ℝ] E} (hf : f.det ≠ 0) :
  measure.map f μ = ennreal.of_real (abs (f.det)⁻¹) • μ :=
begin
  -- we reduce to the case of `E = ι → ℝ`, for which we have already proved the result using
  -- matrices in `map_linear_map_add_haar_pi_eq_smul_add_haar`.
  let ι := fin (finrank ℝ E),
  haveI : finite_dimensional ℝ (ι → ℝ) := by apply_instance,
  have : finrank ℝ E = finrank ℝ (ι → ℝ), by simp,
  have e : E ≃ₗ[ℝ] ι → ℝ := linear_equiv.of_finrank_eq E (ι → ℝ) this,
  -- next line is to avoid `g` getting reduced by `simp`.
  obtain ⟨g, hg⟩ : ∃ g, g = (e : E →ₗ[ℝ] (ι → ℝ)).comp (f.comp (e.symm : (ι → ℝ) →ₗ[ℝ] E)) :=
    ⟨_, rfl⟩,
  have gdet : g.det = f.det, by { rw [hg], exact linear_map.det_conj f e },
  rw ← gdet at hf ⊢,
  have fg : f = (e.symm : (ι → ℝ) →ₗ[ℝ] E).comp (g.comp (e : E →ₗ[ℝ] (ι → ℝ))),
  { ext x,
    simp only [linear_equiv.coe_coe, function.comp_app, linear_map.coe_comp,
      linear_equiv.symm_apply_apply, hg] },
  simp only [fg, linear_equiv.coe_coe, linear_map.coe_comp],
  have Ce : continuous e := (e : E →ₗ[ℝ] (ι → ℝ)).continuous_of_finite_dimensional,
  have Cg : continuous g := linear_map.continuous_of_finite_dimensional g,
  have Cesymm : continuous e.symm := (e.symm : (ι → ℝ) →ₗ[ℝ] E).continuous_of_finite_dimensional,
  rw [← map_map Cesymm.measurable (Cg.comp Ce).measurable, ← map_map Cg.measurable Ce.measurable],
  haveI : is_add_haar_measure (map e μ) := (e : E ≃+ (ι → ℝ)).is_add_haar_measure_map μ Ce Cesymm,
  have ecomp : (e.symm) ∘ e = id,
    by { ext x, simp only [id.def, function.comp_app, linear_equiv.symm_apply_apply] },
  rw [map_linear_map_add_haar_pi_eq_smul_add_haar hf (map e μ), measure.map_smul,
    map_map Cesymm.measurable Ce.measurable, ecomp, measure.map_id]
end

/-- The preimage of a set `s` under a linear map `f` with nonzero determinant has measure
equal to `μ s` times the absolute value of the inverse of the determinant of `f`. -/
@[simp] lemma add_haar_preimage_linear_map
  {f : E →ₗ[ℝ] E} (hf : f.det ≠ 0) (s : set E) :
  μ (f ⁻¹' s) = ennreal.of_real (abs (f.det)⁻¹) * μ s :=
calc μ (f ⁻¹' s) = measure.map f μ s :
  ((f.equiv_of_det_ne_zero hf).to_continuous_linear_equiv.to_homeomorph
    .to_measurable_equiv.map_apply s).symm
... = ennreal.of_real (abs (f.det)⁻¹) * μ s :
  by { rw map_linear_map_add_haar_eq_smul_add_haar μ hf, refl }

/-- The preimage of a set `s` under a continuous linear map `f` with nonzero determinant has measure
equal to `μ s` times the absolute value of the inverse of the determinant of `f`. -/
@[simp] lemma add_haar_preimage_continuous_linear_map
  {f : E →L[ℝ] E} (hf : linear_map.det (f : E →ₗ[ℝ] E) ≠ 0) (s : set E) :
  μ (f ⁻¹' s) = ennreal.of_real (abs (linear_map.det (f : E →ₗ[ℝ] E))⁻¹) * μ s :=
add_haar_preimage_linear_map μ hf s

/-- The preimage of a set `s` under a linear equiv `f` has measure
equal to `μ s` times the absolute value of the inverse of the determinant of `f`. -/
@[simp] lemma add_haar_preimage_linear_equiv
  (f : E ≃ₗ[ℝ] E) (s : set E) :
  μ (f ⁻¹' s) = ennreal.of_real (abs (f.symm : E →ₗ[ℝ] E).det) * μ s :=
begin
  have A : (f : E →ₗ[ℝ] E).det ≠ 0 := (linear_equiv.is_unit_det' f).ne_zero,
  convert add_haar_preimage_linear_map μ A s,
  simp only [linear_equiv.det_coe_symm]
end

/-- The preimage of a set `s` under a continuous linear equiv `f` has measure
equal to `μ s` times the absolute value of the inverse of the determinant of `f`. -/
@[simp] lemma add_haar_preimage_continuous_linear_equiv
  (f : E ≃L[ℝ] E) (s : set E) :
  μ (f ⁻¹' s) = ennreal.of_real (abs (f.symm : E →ₗ[ℝ] E).det) * μ s :=
add_haar_preimage_linear_equiv μ _ s

/-- The image of a set `s` under a linear map `f` has measure
equal to `μ s` times the absolute value of the determinant of `f`. -/
@[simp] lemma add_haar_image_linear_map
  (f : E →ₗ[ℝ] E) (s : set E) :
  μ (f '' s) = ennreal.of_real (abs f.det) * μ s :=
begin
  rcases ne_or_eq f.det 0 with hf|hf,
  { let g := (f.equiv_of_det_ne_zero hf).to_continuous_linear_equiv,
    change μ (g '' s) = _,
    rw [continuous_linear_equiv.image_eq_preimage g s, add_haar_preimage_continuous_linear_equiv],
    congr,
    ext x,
    simp only [linear_equiv.coe_to_continuous_linear_equiv, linear_equiv.of_is_unit_det_apply,
               linear_equiv.coe_coe, continuous_linear_equiv.symm_symm], },
  { simp only [hf, zero_mul, ennreal.of_real_zero, abs_zero],
    have : μ f.range = 0 :=
      add_haar_submodule μ _ (linear_map.range_lt_top_of_det_eq_zero hf).ne,
    exact le_antisymm (le_trans (measure_mono (image_subset_range _ _)) this.le) (zero_le _) }
end

/-- The image of a set `s` under a continuous linear map `f` has measure
equal to `μ s` times the absolute value of the determinant of `f`. -/
@[simp] lemma add_haar_image_continuous_linear_map
  (f : E →L[ℝ] E) (s : set E) :
  μ (f '' s) = ennreal.of_real (abs (f : E →ₗ[ℝ] E).det) * μ s :=
add_haar_image_linear_map μ _ s

/-- The image of a set `s` under a continuous linear equiv `f` has measure
equal to `μ s` times the absolute value of the determinant of `f`. -/
@[simp] lemma add_haar_image_continuous_linear_equiv
  (f : E ≃L[ℝ] E) (s : set E) :
  μ (f '' s) = ennreal.of_real (abs (f : E →ₗ[ℝ] E).det) * μ s :=
μ.add_haar_image_linear_map (f : E →ₗ[ℝ] E) s

/-!
### Basic properties of Haar measures on real vector spaces
-/

lemma map_add_haar_smul {r : ℝ} (hr : r ≠ 0) :
  measure.map ((•) r) μ = ennreal.of_real (abs (r ^ (finrank ℝ E))⁻¹) • μ :=
begin
  let f : E →ₗ[ℝ] E := r • 1,
  change measure.map f μ = _,
  have hf : f.det ≠ 0,
  { simp only [mul_one, linear_map.det_smul, ne.def, monoid_hom.map_one],
    assume h,
    exact hr (pow_eq_zero h) },
  simp only [map_linear_map_add_haar_eq_smul_add_haar μ hf, mul_one, linear_map.det_smul,
    monoid_hom.map_one],
end

@[simp] lemma add_haar_preimage_smul {r : ℝ} (hr : r ≠ 0) (s : set E) :
  μ (((•) r) ⁻¹' s) = ennreal.of_real (abs (r ^ (finrank ℝ E))⁻¹) * μ s :=
calc μ (((•) r) ⁻¹' s) = measure.map ((•) r) μ s :
  ((homeomorph.smul (is_unit_iff_ne_zero.2 hr).unit).to_measurable_equiv.map_apply s).symm
... = ennreal.of_real (abs (r^(finrank ℝ E))⁻¹) * μ s : by { rw map_add_haar_smul μ hr, refl }

/-- Rescaling a set by a factor `r` multiplies its measure by `abs (r ^ dim)`. -/
@[simp] lemma add_haar_smul (r : ℝ) (s : set E) :
  μ (r • s) = ennreal.of_real (abs (r ^ (finrank ℝ E))) * μ s :=
begin
  rcases ne_or_eq r 0 with h|rfl,
  { rw [← preimage_smul_inv₀ h, add_haar_preimage_smul μ (inv_ne_zero h), inv_pow, inv_inv] },
  rcases eq_empty_or_nonempty s with rfl|hs,
  { simp only [measure_empty, mul_zero, smul_set_empty] },
  rw [zero_smul_set hs, ← singleton_zero],
  by_cases h : finrank ℝ E = 0,
  { haveI : subsingleton E := finrank_zero_iff.1 h,
    simp only [h, one_mul, ennreal.of_real_one, abs_one, subsingleton.eq_univ_of_nonempty hs,
      pow_zero, subsingleton.eq_univ_of_nonempty (singleton_nonempty (0 : E))] },
  { haveI : nontrivial E := nontrivial_of_finrank_pos (bot_lt_iff_ne_bot.2 h),
    simp only [h, zero_mul, ennreal.of_real_zero, abs_zero, ne.def, not_false_iff, zero_pow',
      measure_singleton] }
end

lemma add_haar_smul_of_nonneg {r : ℝ} (hr : 0 ≤ r) (s : set E) :
  μ (r • s) = ennreal.of_real (r ^ finrank ℝ E) * μ s :=
by rw [add_haar_smul, abs_pow, abs_of_nonneg hr]

variables {μ} {s : set E}

-- Note: We might want to rename this once we acquire the lemma corresponding to
-- `measurable_set.const_smul`
lemma null_measurable_set.const_smul (hs : null_measurable_set s μ) (r : ℝ) :
  null_measurable_set (r • s) μ :=
begin
  obtain rfl | hs' := s.eq_empty_or_nonempty,
  { simp },
  obtain rfl | hr := eq_or_ne r 0,
  { simpa [zero_smul_set hs'] using null_measurable_set_singleton _ },
  obtain ⟨t, ht, hst⟩ := hs,
  refine ⟨_, ht.const_smul_of_ne_zero hr, _⟩,
  rw ←measure_symm_diff_eq_zero_iff at ⊢ hst,
  rw [←smul_set_symm_diff₀ hr, add_haar_smul μ, hst, mul_zero],
end

variables (μ)

@[simp] lemma add_haar_image_homothety (x : E) (r : ℝ) (s : set E) :
  μ (affine_map.homothety x r '' s) = ennreal.of_real (abs (r ^ (finrank ℝ E))) * μ s :=
calc μ (affine_map.homothety x r '' s) = μ ((λ y, y + x) '' (r • ((λ y, y + (-x)) '' s))) :
  by { simp only [← image_smul, image_image, ← sub_eq_add_neg], refl }
... = ennreal.of_real (abs (r ^ (finrank ℝ E))) * μ s :
  by simp only [image_add_right, measure_preimage_add_right, add_haar_smul]

/-- The integral of `f (R • x)` with respect to an additive Haar measure is a multiple of the
integral of `f`. The formula we give works even when `f` is not integrable or `R = 0`
thanks to the convention that a non-integrable function has integral zero. -/
lemma integral_comp_smul (f : E → F) (R : ℝ) :
  ∫ x, f (R • x) ∂μ = |(R ^ finrank ℝ E)⁻¹| • ∫ x, f x ∂μ :=
begin
  rcases eq_or_ne R 0 with rfl|hR,
  { simp only [zero_smul, integral_const],
    rcases nat.eq_zero_or_pos (finrank ℝ E) with hE|hE,
    { haveI : subsingleton E, from finrank_zero_iff.1 hE,
      have : f = (λ x, f 0), { ext x, rw subsingleton.elim x 0 },
      conv_rhs { rw this },
      simp only [hE, pow_zero, inv_one, abs_one, one_smul, integral_const] },
    { haveI : nontrivial E, from finrank_pos_iff.1 hE,
      simp only [zero_pow hE, measure_univ_of_is_add_left_invariant, ennreal.top_to_real, zero_smul,
        inv_zero, abs_zero]} },
  { calc ∫ x, f (R • x) ∂μ = ∫ y, f y ∂(measure.map (λ x, R • x) μ) :
      (integral_map_equiv (homeomorph.smul (is_unit_iff_ne_zero.2 hR).unit)
        .to_measurable_equiv f).symm
    ... = |(R ^ finrank ℝ E)⁻¹| • ∫ x, f x ∂μ :
      by simp only [map_add_haar_smul μ hR, integral_smul_measure, ennreal.to_real_of_real,
                    abs_nonneg] }
end

/-- The integral of `f (R • x)` with respect to an additive Haar measure is a multiple of the
integral of `f`. The formula we give works even when `f` is not integrable or `R = 0`
thanks to the convention that a non-integrable function has integral zero. -/
lemma integral_comp_smul_of_nonneg (f : E → F) (R : ℝ) {hR : 0 ≤ R} :
  ∫ x, f (R • x) ∂μ = (R ^ finrank ℝ E)⁻¹ • ∫ x, f x ∂μ :=
by rw [integral_comp_smul μ f R, abs_of_nonneg (inv_nonneg.2 (pow_nonneg hR _))]

/-- The integral of `f (R⁻¹ • x)` with respect to an additive Haar measure is a multiple of the
integral of `f`. The formula we give works even when `f` is not integrable or `R = 0`
thanks to the convention that a non-integrable function has integral zero. -/
lemma integral_comp_inv_smul (f : E → F) (R : ℝ) :
  ∫ x, f (R⁻¹ • x) ∂μ = |(R ^ finrank ℝ E)| • ∫ x, f x ∂μ :=
by rw [integral_comp_smul μ f (R⁻¹), inv_pow, inv_inv]

/-- The integral of `f (R⁻¹ • x)` with respect to an additive Haar measure is a multiple of the
integral of `f`. The formula we give works even when `f` is not integrable or `R = 0`
thanks to the convention that a non-integrable function has integral zero. -/
lemma integral_comp_inv_smul_of_nonneg (f : E → F) {R : ℝ} (hR : 0 ≤ R) :
  ∫ x, f (R⁻¹ • x) ∂μ = R ^ finrank ℝ E • ∫ x, f x ∂μ :=
by rw [integral_comp_inv_smul μ f R, abs_of_nonneg ((pow_nonneg hR _))]

/-! We don't need to state `map_add_haar_neg` here, because it has already been proved for
general Haar measures on general commutative groups. -/

/-! ### Measure of balls -/

lemma add_haar_ball_center
  {E : Type*} [normed_add_comm_group E] [measurable_space E]
  [borel_space E] (μ : measure E) [is_add_haar_measure μ] (x : E) (r : ℝ) :
  μ (ball x r) = μ (ball (0 : E) r) :=
begin
  have : ball (0 : E) r = ((+) x) ⁻¹' (ball x r), by simp [preimage_add_ball],
  rw [this, measure_preimage_add]
end

lemma add_haar_closed_ball_center
  {E : Type*} [normed_add_comm_group E] [measurable_space E]
  [borel_space E] (μ : measure E) [is_add_haar_measure μ] (x : E) (r : ℝ) :
  μ (closed_ball x r) = μ (closed_ball (0 : E) r) :=
begin
  have : closed_ball (0 : E) r = ((+) x) ⁻¹' (closed_ball x r), by simp [preimage_add_closed_ball],
  rw [this, measure_preimage_add]
end

lemma add_haar_ball_mul_of_pos (x : E) {r : ℝ} (hr : 0 < r) (s : ℝ) :
  μ (ball x (r * s)) = ennreal.of_real (r ^ (finrank ℝ E)) * μ (ball 0 s) :=
begin
  have : ball (0 : E) (r * s) = r • ball 0 s,
    by simp only [smul_ball hr.ne' (0 : E) s, real.norm_eq_abs, abs_of_nonneg hr.le, smul_zero],
  simp only [this, add_haar_smul, abs_of_nonneg hr.le, add_haar_ball_center, abs_pow],
end

lemma add_haar_ball_of_pos (x : E) {r : ℝ} (hr : 0 < r) :
  μ (ball x r) = ennreal.of_real (r ^ (finrank ℝ E)) * μ (ball 0 1) :=
by rw [← add_haar_ball_mul_of_pos μ x hr, mul_one]

lemma add_haar_ball_mul [nontrivial E] (x : E) {r : ℝ} (hr : 0 ≤ r) (s : ℝ) :
  μ (ball x (r * s)) = ennreal.of_real (r ^ (finrank ℝ E)) * μ (ball 0 s) :=
begin
  rcases has_le.le.eq_or_lt hr with h|h,
  { simp only [← h, zero_pow finrank_pos, measure_empty, zero_mul, ennreal.of_real_zero,
               ball_zero] },
  { exact add_haar_ball_mul_of_pos μ x h s }
end

lemma add_haar_ball [nontrivial E] (x : E) {r : ℝ} (hr : 0 ≤ r) :
  μ (ball x r) = ennreal.of_real (r ^ (finrank ℝ E)) * μ (ball 0 1) :=
by rw [← add_haar_ball_mul μ x hr, mul_one]

lemma add_haar_closed_ball_mul_of_pos (x : E) {r : ℝ} (hr : 0 < r) (s : ℝ) :
  μ (closed_ball x (r * s)) = ennreal.of_real (r ^ (finrank ℝ E)) * μ (closed_ball 0 s) :=
begin
  have : closed_ball (0 : E) (r * s) = r • closed_ball 0 s,
    by simp [smul_closed_ball' hr.ne' (0 : E), abs_of_nonneg hr.le],
  simp only [this, add_haar_smul, abs_of_nonneg hr.le, add_haar_closed_ball_center, abs_pow],
end

lemma add_haar_closed_ball_mul (x : E) {r : ℝ} (hr : 0 ≤ r) {s : ℝ} (hs : 0 ≤ s) :
  μ (closed_ball x (r * s)) = ennreal.of_real (r ^ (finrank ℝ E)) * μ (closed_ball 0 s) :=
begin
  have : closed_ball (0 : E) (r * s) = r • closed_ball 0 s,
    by simp [smul_closed_ball r (0 : E) hs, abs_of_nonneg hr],
  simp only [this, add_haar_smul, abs_of_nonneg hr, add_haar_closed_ball_center, abs_pow],
end

/-- The measure of a closed ball can be expressed in terms of the measure of the closed unit ball.
Use instead `add_haar_closed_ball`, which uses the measure of the open unit ball as a standard
form. -/

lemma add_haar_closed_unit_ball_eq_add_haar_unit_ball :
  μ (closed_ball (0 : E) 1) = μ (ball 0 1) :=
begin
  apply le_antisymm _ (measure_mono ball_subset_closed_ball),
  have A : tendsto (λ (r : ℝ), ennreal.of_real (r ^ (finrank ℝ E)) * μ (closed_ball (0 : E) 1))
    (𝓝[<] 1) (𝓝 (ennreal.of_real (1 ^ (finrank ℝ E)) * μ (closed_ball (0 : E) 1))),
  { refine ennreal.tendsto.mul _ (by simp) tendsto_const_nhds (by simp),
    exact ennreal.tendsto_of_real ((tendsto_id'.2 nhds_within_le_nhds).pow _) },
  simp only [one_pow, one_mul, ennreal.of_real_one] at A,
  refine le_of_tendsto A _,
  refine mem_nhds_within_Iio_iff_exists_Ioo_subset.2 ⟨(0 : ℝ), by simp, λ r hr, _⟩,
  dsimp,
  rw ← add_haar_closed_ball' μ (0 : E) hr.1.le,
  exact measure_mono (closed_ball_subset_ball hr.2)
end

lemma add_haar_closed_ball (x : E) {r : ℝ} (hr : 0 ≤ r) :
  μ (closed_ball x r) = ennreal.of_real (r ^ (finrank ℝ E)) * μ (ball 0 1) :=
by rw [add_haar_closed_ball' μ x hr, add_haar_closed_unit_ball_eq_add_haar_unit_ball]

lemma add_haar_closed_ball_eq_add_haar_ball [nontrivial E] (x : E) (r : ℝ) :
  μ (closed_ball x r) = μ (ball x r) :=
begin
  by_cases h : r < 0,
  { rw [metric.closed_ball_eq_empty.mpr h, metric.ball_eq_empty.mpr h.le] },
  push_neg at h,
  rw [add_haar_closed_ball μ x h, add_haar_ball μ x h],
end

lemma add_haar_sphere_of_ne_zero (x : E) {r : ℝ} (hr : r ≠ 0) :
  μ (sphere x r) = 0 :=
begin
  rcases hr.lt_or_lt with h|h,
  { simp only [empty_diff, measure_empty, ← closed_ball_diff_ball, closed_ball_eq_empty.2 h] },
  { rw [← closed_ball_diff_ball,
        measure_diff ball_subset_closed_ball measurable_set_ball measure_ball_lt_top.ne,
        add_haar_ball_of_pos μ _ h, add_haar_closed_ball μ _ h.le, tsub_self];
    apply_instance }
end

lemma add_haar_sphere [nontrivial E] (x : E) (r : ℝ) :
  μ (sphere x r) = 0 :=
begin
  rcases eq_or_ne r 0 with rfl|h,
  { rw [sphere_zero, measure_singleton] },
  { exact add_haar_sphere_of_ne_zero μ x h }
end

lemma add_haar_singleton_add_smul_div_singleton_add_smul
  {r : ℝ} (hr : r ≠ 0) (x y : E) (s t : set E) :
  μ ({x} + r • s) / μ ({y} + r • t) = μ s / μ t :=
calc
μ ({x} + r • s) / μ ({y} + r • t)
    = ennreal.of_real (|r| ^ finrank ℝ E) * μ s * (ennreal.of_real (|r| ^ finrank ℝ E) * μ t)⁻¹ :
  by simp only [div_eq_mul_inv, add_haar_smul, image_add_left, measure_preimage_add, abs_pow,
    singleton_add]
... = ennreal.of_real (|r| ^ finrank ℝ E) * (ennreal.of_real (|r| ^ finrank ℝ E))⁻¹ *
        (μ s * (μ t)⁻¹) :
  begin
    rw ennreal.mul_inv,
    { ring },
    { simp only [pow_pos (abs_pos.mpr hr), ennreal.of_real_eq_zero, not_le, ne.def, true_or] },
    { simp only [ennreal.of_real_ne_top, true_or, ne.def, not_false_iff] },
  end
... = μ s / μ t :
  begin
    rw [ennreal.mul_inv_cancel, one_mul, div_eq_mul_inv],
    { simp only [pow_pos (abs_pos.mpr hr), ennreal.of_real_eq_zero, not_le, ne.def], },
    { simp only [ennreal.of_real_ne_top, ne.def, not_false_iff] }
  end

@[priority 100] instance is_doubling_measure_of_is_add_haar_measure : is_doubling_measure μ :=
begin
  refine ⟨⟨(2 : ℝ≥0) ^ (finrank ℝ E), _⟩⟩,
  filter_upwards [self_mem_nhds_within] with r hr x,
  rw [add_haar_closed_ball_mul μ x zero_le_two (le_of_lt hr), add_haar_closed_ball_center μ x,
    ennreal.of_real, real.to_nnreal_pow zero_le_two],
  simp only [real.to_nnreal_bit0, real.to_nnreal_one, le_refl],
end

section

/-!
### The Lebesgue measure associated to an alternating map
-/

variables {ι G : Type*} [fintype ι] [decidable_eq ι]
[normed_add_comm_group G] [normed_space ℝ G] [measurable_space G] [borel_space G]

lemma add_haar_parallelepiped (b : basis ι ℝ G) (v : ι → G) :
  b.add_haar (parallelepiped v) = ennreal.of_real (|b.det v|) :=
begin
  haveI : finite_dimensional ℝ G, from finite_dimensional.of_fintype_basis b,
  have A : parallelepiped v = (b.constr ℕ v) '' (parallelepiped b),
  { rw image_parallelepiped,
    congr' 1 with i,
    exact (b.constr_basis ℕ v i).symm },
  rw [A, add_haar_image_linear_map, basis.add_haar_self, mul_one,
    ← linear_map.det_to_matrix b, ← basis.to_matrix_eq_to_matrix_constr],
  refl,
end

variables [finite_dimensional ℝ G] {n : ℕ} [_i : fact (finrank ℝ G = n)]
include _i

/-- The Lebesgue measure associated to an alternating map. It gives measure `|ω v|` to the
parallelepiped spanned by the vectors `v₁, ..., vₙ`. Note that it is not always a Haar measure,
as it can be zero, but it is always locally finite and translation invariant. -/
@[irreducible] noncomputable def _root_.alternating_map.measure
  (ω : alternating_map ℝ G ℝ (fin n)) : measure G :=
‖ω (fin_basis_of_finrank_eq ℝ G _i.out)‖₊ • (fin_basis_of_finrank_eq ℝ G _i.out).add_haar

lemma _root_.alternating_map.measure_parallelepiped
  (ω : alternating_map ℝ G ℝ (fin n)) (v : fin n → G) :
  ω.measure (parallelepiped v) = ennreal.of_real (|ω v|) :=
begin
  conv_rhs { rw ω.eq_smul_basis_det (fin_basis_of_finrank_eq ℝ G _i.out) },
  simp only [add_haar_parallelepiped, alternating_map.measure, coe_nnreal_smul_apply,
    alternating_map.smul_apply, algebra.id.smul_eq_mul, abs_mul,
    ennreal.of_real_mul (abs_nonneg _), real.ennnorm_eq_of_real_abs]
end

instance (ω : alternating_map ℝ G ℝ (fin n)) : is_add_left_invariant ω.measure :=
by { rw [alternating_map.measure], apply_instance }

instance (ω : alternating_map ℝ G ℝ (fin n)) : is_locally_finite_measure ω.measure :=
by { rw [alternating_map.measure], apply_instance }

end

/-!
### Density points

Besicovitch covering theorem ensures that, for any locally finite measure on a finite-dimensional
real vector space, almost every point of a set `s` is a density point, i.e.,
`μ (s ∩ closed_ball x r) / μ (closed_ball x r)` tends to `1` as `r` tends to `0`
(see `besicovitch.ae_tendsto_measure_inter_div`).
When `μ` is a Haar measure, one can deduce the same property for any rescaling sequence of sets,
of the form `{x} + r • t` where `t` is a set with positive finite measure, instead of the sequence
of closed balls.

We argue first for the dual property, i.e., if `s` has density `0` at `x`, then
`μ (s ∩ ({x} + r • t)) / μ ({x} + r • t)` tends to `0`. First when `t` is contained in the ball
of radius `1`, in `tendsto_add_haar_inter_smul_zero_of_density_zero_aux1`,
(by arguing by inclusion). Then when `t` is bounded, reducing to the previous one by rescaling, in
`tendsto_add_haar_inter_smul_zero_of_density_zero_aux2`.
Then for a general set `t`, by cutting it into a bounded part and a part with small measure, in
`tendsto_add_haar_inter_smul_zero_of_density_zero`.
Going to the complement, one obtains the desired property at points of density `1`, first when
`s` is measurable in `tendsto_add_haar_inter_smul_one_of_density_one_aux`, and then without this
assumption in `tendsto_add_haar_inter_smul_one_of_density_one` by applying the previous lemma to
the measurable hull `to_measurable μ s`
-/

lemma tendsto_add_haar_inter_smul_zero_of_density_zero_aux1
  (s : set E) (x : E)
  (h : tendsto (λ r, μ (s ∩ closed_ball x r) / μ (closed_ball x r)) (𝓝[>] 0) (𝓝 0))
  (t : set E) (u : set E) (h'u : μ u ≠ 0) (t_bound : t ⊆ closed_ball 0 1) :
  tendsto (λ (r : ℝ), μ (s ∩ ({x} + r • t)) / μ ({x} + r • u)) (𝓝[>] 0) (𝓝 0) :=
begin
  have A : tendsto (λ (r : ℝ), μ (s ∩ ({x} + r • t)) / μ (closed_ball x r)) (𝓝[>] 0) (𝓝 0),
  { apply tendsto_of_tendsto_of_tendsto_of_le_of_le' tendsto_const_nhds h
      (eventually_of_forall (λ b, zero_le _)),
    filter_upwards [self_mem_nhds_within],
    rintros r (rpos : 0 < r),
    apply mul_le_mul_right' (measure_mono (inter_subset_inter_right _ _)) _,
    assume y hy,
    have : y - x ∈ r • closed_ball (0 : E) 1,
    { apply smul_set_mono t_bound,
      simpa [neg_add_eq_sub] using hy },
    simpa only [smul_closed_ball _ _ zero_le_one, real.norm_of_nonneg rpos.le,
      mem_closed_ball_iff_norm, mul_one, sub_zero, smul_zero] },
  have B : tendsto (λ (r : ℝ), μ (closed_ball x r) / μ ({x} + r • u)) (𝓝[>] 0)
    (𝓝 (μ (closed_ball x 1) / μ ({x} + u))),
  { apply tendsto_const_nhds.congr' _,
    filter_upwards [self_mem_nhds_within],
    rintros r (rpos : 0 < r),
    have : closed_ball x r = {x} + r • closed_ball 0 1,
      by simp only [_root_.smul_closed_ball, real.norm_of_nonneg rpos.le, zero_le_one, add_zero,
        mul_one, singleton_add_closed_ball, smul_zero],
    simp only [this, add_haar_singleton_add_smul_div_singleton_add_smul μ rpos.ne'],
    simp only [add_haar_closed_ball_center, image_add_left, measure_preimage_add, singleton_add] },
  have C : tendsto (λ (r : ℝ),
    (μ (s ∩ ({x} + r • t)) / μ (closed_ball x r)) * (μ (closed_ball x r) / μ ({x} + r • u)))
    (𝓝[>] 0) (𝓝 (0 * (μ (closed_ball x 1) / μ ({x} + u)))),
  { apply ennreal.tendsto.mul A _ B (or.inr ennreal.zero_ne_top),
    simp only [ennreal.div_eq_top, h'u, measure_closed_ball_lt_top.ne, false_or, image_add_left,
      eq_self_iff_true, not_true, ne.def, not_false_iff, measure_preimage_add, singleton_add,
      and_false, false_and] },
  simp only [zero_mul] at C,
  apply C.congr' _,
  filter_upwards [self_mem_nhds_within],
  rintros r (rpos : 0 < r),
  calc μ (s ∩ ({x} + r • t)) / μ (closed_ball x r) * (μ (closed_ball x r) / μ ({x} + r • u))
    = (μ (closed_ball x r) * (μ (closed_ball x r))⁻¹) * (μ (s ∩ ({x} + r • t)) / μ ({x} + r • u)) :
      by { simp only [div_eq_mul_inv], ring }
    ... = μ (s ∩ ({x} + r • t)) / μ ({x} + r • u) :
      by rw [ennreal.mul_inv_cancel (measure_closed_ball_pos μ x rpos).ne'
          measure_closed_ball_lt_top.ne, one_mul],
end

lemma tendsto_add_haar_inter_smul_zero_of_density_zero_aux2
  (s : set E) (x : E)
  (h : tendsto (λ r, μ (s ∩ closed_ball x r) / μ (closed_ball x r)) (𝓝[>] 0) (𝓝 0))
  (t : set E) (u : set E) (h'u : μ u ≠ 0)
  (R : ℝ) (Rpos : 0 < R) (t_bound : t ⊆ closed_ball 0 R) :
  tendsto (λ (r : ℝ), μ (s ∩ ({x} + r • t)) / μ ({x} + r • u)) (𝓝[>] 0) (𝓝 0) :=
begin
  set t' := R⁻¹ • t with ht',
  set u' := R⁻¹ • u with hu',
  have A : tendsto (λ (r : ℝ), μ (s ∩ ({x} + r • t')) / μ ({x} + r • u')) (𝓝[>] 0) (𝓝 0),
  { apply tendsto_add_haar_inter_smul_zero_of_density_zero_aux1 μ s x h
      t' u',
    { simp only [h'u, (pow_pos Rpos _).ne', abs_nonpos_iff, add_haar_smul, not_false_iff,
        ennreal.of_real_eq_zero, inv_eq_zero, inv_pow, ne.def, or_self, mul_eq_zero] },
    { convert smul_set_mono t_bound,
      rw [smul_closed_ball _ _ Rpos.le, smul_zero, real.norm_of_nonneg (inv_nonneg.2 Rpos.le),
        inv_mul_cancel Rpos.ne'] } },
  have B : tendsto (λ (r : ℝ), R * r) (𝓝[>] 0) (𝓝[>] (R * 0)),
  { apply tendsto_nhds_within_of_tendsto_nhds_of_eventually_within,
    { exact (tendsto_const_nhds.mul tendsto_id).mono_left nhds_within_le_nhds },
    { filter_upwards [self_mem_nhds_within],
      assume r rpos,
      rw mul_zero,
      exact mul_pos Rpos rpos } },
  rw mul_zero at B,
  apply (A.comp B).congr' _,
  filter_upwards [self_mem_nhds_within],
  rintros r (rpos : 0 < r),
  have T : (R * r) • t' = r • t,
    by rw [mul_comm, ht', smul_smul, mul_assoc, mul_inv_cancel Rpos.ne', mul_one],
  have U : (R * r) • u' = r • u,
    by rw [mul_comm, hu', smul_smul, mul_assoc, mul_inv_cancel Rpos.ne', mul_one],
  dsimp,
  rw [T, U],
end

/-- Consider a point `x` at which a set `s` has density zero, with respect to closed balls. Then it
also has density zero with respect to any measurable set `t`: the proportion of points in `s`
belonging to a rescaled copy `{x} + r • t` of `t` tends to zero as `r` tends to zero. -/
lemma tendsto_add_haar_inter_smul_zero_of_density_zero
  (s : set E) (x : E)
  (h : tendsto (λ r, μ (s ∩ closed_ball x r) / μ (closed_ball x r)) (𝓝[>] 0) (𝓝 0))
  (t : set E) (ht : measurable_set t) (h''t : μ t ≠ ∞) :
  tendsto (λ (r : ℝ), μ (s ∩ ({x} + r • t)) / μ ({x} + r • t)) (𝓝[>] 0) (𝓝 0) :=
begin
  refine tendsto_order.2 ⟨λ a' ha', (ennreal.not_lt_zero ha').elim, λ ε (εpos : 0 < ε), _⟩,
  rcases eq_or_ne (μ t) 0 with h't|h't,
  { apply eventually_of_forall (λ r, _),
    suffices H : μ (s ∩ ({x} + r • t)) = 0,
      by { rw H, simpa only [ennreal.zero_div] using εpos },
    apply le_antisymm _ (zero_le _),
    calc μ (s ∩ ({x} + r • t)) ≤ μ ({x} + r • t) : measure_mono (inter_subset_right _ _)
    ... = 0 : by simp only [h't, add_haar_smul, image_add_left, measure_preimage_add,
      singleton_add, mul_zero] },
  obtain ⟨n, npos, hn⟩ : ∃ (n : ℕ), 0 < n ∧ μ (t \ closed_ball 0 n) < (ε / 2) * μ t,
  { have A : tendsto (λ (n : ℕ), μ (t \ closed_ball 0 n)) at_top
      (𝓝 (μ (⋂ (n : ℕ), t \ closed_ball 0 n))),
    { have N : ∃ (n : ℕ), μ (t \ closed_ball 0 n) ≠ ∞ :=
        ⟨0, ((measure_mono (diff_subset t _)).trans_lt h''t.lt_top).ne⟩,
      refine tendsto_measure_Inter (λ n, ht.diff measurable_set_closed_ball) (λ m n hmn, _) N,
      exact diff_subset_diff subset.rfl (closed_ball_subset_closed_ball (nat.cast_le.2 hmn)) },
    have : (⋂ (n : ℕ), t \ closed_ball 0 n) = ∅,
      by simp_rw [diff_eq, ← inter_Inter, Inter_eq_compl_Union_compl, compl_compl,
          Union_closed_ball_nat, compl_univ, inter_empty],
    simp only [this, measure_empty] at A,
    have I : 0 < (ε / 2) * μ t := ennreal.mul_pos (ennreal.half_pos εpos.ne').ne' h't,
    exact (eventually.and (Ioi_mem_at_top 0) ((tendsto_order.1 A).2 _ I)).exists },
  have L : tendsto (λ (r : ℝ), μ (s ∩ ({x} + r • (t ∩ closed_ball 0 n))) / μ ({x} + r • t))
    (𝓝[>] 0) (𝓝 0) :=
      tendsto_add_haar_inter_smul_zero_of_density_zero_aux2 μ s x h
        _ t h't n (nat.cast_pos.2 npos) (inter_subset_right _ _),
  filter_upwards [(tendsto_order.1 L).2 _ (ennreal.half_pos εpos.ne'), self_mem_nhds_within],
  rintros r hr (rpos : 0 < r),
  have I : μ (s ∩ ({x} + r • t)) ≤
    μ (s ∩ ({x} + r • (t ∩ closed_ball 0 n))) + μ ({x} + r • (t \ closed_ball 0 n)) := calc
  μ (s ∩ ({x} + r • t))
      = μ ((s ∩ ({x} + r • (t ∩ closed_ball 0 n))) ∪ (s ∩ ({x} + r • (t \ closed_ball 0 n)))) :
    by rw [← inter_union_distrib_left, ← add_union, ← smul_set_union, inter_union_diff]
  ... ≤ μ (s ∩ ({x} + r • (t ∩ closed_ball 0 n))) + μ (s ∩ ({x} + r • (t \ closed_ball 0 n))) :
    measure_union_le _ _
  ... ≤ μ (s ∩ ({x} + r • (t ∩ closed_ball 0 n))) + μ ({x} + r • (t \ closed_ball 0 n)) :
    add_le_add le_rfl (measure_mono (inter_subset_right _ _)),
  calc μ (s ∩ ({x} + r • t)) / μ ({x} + r • t)
  ≤ (μ (s ∩ ({x} + r • (t ∩ closed_ball 0 n))) + μ ({x} + r • (t \ closed_ball 0 n))) /
      μ ({x} + r • t) : mul_le_mul_right' I _
  ... < ε / 2 + ε / 2 :
    begin
      rw ennreal.add_div,
      apply ennreal.add_lt_add hr _,
      rwa [add_haar_singleton_add_smul_div_singleton_add_smul μ rpos.ne',
           ennreal.div_lt_iff (or.inl h't) (or.inl h''t)],
    end
  ... = ε : ennreal.add_halves _
end

lemma tendsto_add_haar_inter_smul_one_of_density_one_aux
  (s : set E) (hs : measurable_set s) (x : E)
  (h : tendsto (λ r, μ (s ∩ closed_ball x r) / μ (closed_ball x r)) (𝓝[>] 0) (𝓝 1))
  (t : set E) (ht : measurable_set t) (h't : μ t ≠ 0) (h''t : μ t ≠ ∞) :
  tendsto (λ (r : ℝ), μ (s ∩ ({x} + r • t)) / μ ({x} + r • t)) (𝓝[>] 0) (𝓝 1) :=
begin
  have I : ∀ u v, μ u ≠ 0 → μ u ≠ ∞ → measurable_set v →
    μ u / μ u - μ (vᶜ ∩ u) / μ u = μ (v ∩ u) / μ u,
  { assume u v uzero utop vmeas,
    simp_rw [div_eq_mul_inv],
    rw ← ennreal.sub_mul, swap,
    { simp only [uzero, ennreal.inv_eq_top, implies_true_iff, ne.def, not_false_iff] },
    congr' 1,
    apply ennreal.sub_eq_of_add_eq
      (ne_top_of_le_ne_top utop (measure_mono (inter_subset_right _ _))),
    rw [inter_comm _ u, inter_comm _ u],
    exact measure_inter_add_diff u vmeas },
  have L : tendsto (λ r, μ (sᶜ ∩ closed_ball x r) / μ (closed_ball x r)) (𝓝[>] 0) (𝓝 0),
  { have A : tendsto (λ r, μ (closed_ball x r) / μ (closed_ball x r)) (𝓝[>] 0) (𝓝 1),
    { apply tendsto_const_nhds.congr' _,
      filter_upwards [self_mem_nhds_within],
      assume r hr,
      rw [div_eq_mul_inv, ennreal.mul_inv_cancel],
      { exact (measure_closed_ball_pos μ _ hr).ne' },
      { exact measure_closed_ball_lt_top.ne } },
    have B := ennreal.tendsto.sub A h (or.inl ennreal.one_ne_top),
    simp only [tsub_self] at B,
    apply B.congr' _,
    filter_upwards [self_mem_nhds_within],
    rintros r (rpos : 0 < r),
    convert I (closed_ball x r) sᶜ (measure_closed_ball_pos μ _ rpos).ne'
      (measure_closed_ball_lt_top).ne hs.compl,
    rw compl_compl },
  have L' : tendsto (λ (r : ℝ), μ (sᶜ ∩ ({x} + r • t)) / μ ({x} + r • t)) (𝓝[>] 0) (𝓝 0) :=
    tendsto_add_haar_inter_smul_zero_of_density_zero μ sᶜ x L t ht h''t,
  have L'' : tendsto (λ (r : ℝ), μ ({x} + r • t) / μ ({x} + r • t)) (𝓝[>] 0) (𝓝 1),
  { apply tendsto_const_nhds.congr' _,
    filter_upwards [self_mem_nhds_within],
    rintros r (rpos : 0 < r),
    rw [add_haar_singleton_add_smul_div_singleton_add_smul μ rpos.ne', ennreal.div_self h't h''t] },
  have := ennreal.tendsto.sub L'' L' (or.inl ennreal.one_ne_top),
  simp only [tsub_zero] at this,
  apply this.congr' _,
  filter_upwards [self_mem_nhds_within],
  rintros r (rpos : 0 < r),
  refine I ({x} + r • t) s _ _ hs,
  { simp only [h't, abs_of_nonneg rpos.le, pow_pos rpos, add_haar_smul, image_add_left,
      ennreal.of_real_eq_zero, not_le, or_false, ne.def, measure_preimage_add, abs_pow,
      singleton_add, mul_eq_zero] },
  { simp only [h''t, ennreal.of_real_ne_top, add_haar_smul, image_add_left, with_top.mul_eq_top_iff,
      ne.def, not_false_iff, measure_preimage_add, singleton_add, and_false, false_and, or_self] }
end

/-- Consider a point `x` at which a set `s` has density one, with respect to closed balls (i.e.,
a Lebesgue density point of `s`). Then `s` has also density one at `x` with respect to any
measurable set `t`: the proportion of points in `s` belonging to a rescaled copy `{x} + r • t`
of `t` tends to one as `r` tends to zero. -/
lemma tendsto_add_haar_inter_smul_one_of_density_one
  (s : set E) (x : E)
  (h : tendsto (λ r, μ (s ∩ closed_ball x r) / μ (closed_ball x r)) (𝓝[>] 0) (𝓝 1))
  (t : set E) (ht : measurable_set t) (h't : μ t ≠ 0) (h''t : μ t ≠ ∞) :
  tendsto (λ (r : ℝ), μ (s ∩ ({x} + r • t)) / μ ({x} + r • t)) (𝓝[>] 0) (𝓝 1) :=
begin
  have : tendsto (λ (r : ℝ), μ (to_measurable μ s ∩ ({x} + r • t)) / μ ({x} + r • t))
    (𝓝[>] 0) (𝓝 1),
  { apply tendsto_add_haar_inter_smul_one_of_density_one_aux μ _
      (measurable_set_to_measurable _ _) _ _ t ht h't h''t,
    apply tendsto_of_tendsto_of_tendsto_of_le_of_le' h tendsto_const_nhds,
    { refine eventually_of_forall (λ r, mul_le_mul_right' _ _),
      exact measure_mono (inter_subset_inter_left _ (subset_to_measurable _ _)) },
    { filter_upwards [self_mem_nhds_within],
      rintros r (rpos : 0 < r),
      apply ennreal.div_le_of_le_mul,
      rw one_mul,
      exact measure_mono (inter_subset_right _ _) } },
  apply this.congr (λ r, _),
  congr' 1,
  apply measure_to_measurable_inter_of_sigma_finite,
  simp only [image_add_left, singleton_add],
  apply (continuous_add_left (-x)).measurable (ht.const_smul₀ r)
end

/-- Consider a point `x` at which a set `s` has density one, with respect to closed balls (i.e.,
a Lebesgue density point of `s`). Then `s` intersects the rescaled copies `{x} + r • t` of a given
set `t` with positive measure, for any small enough `r`. -/
lemma eventually_nonempty_inter_smul_of_density_one (s : set E) (x : E)
  (h : tendsto (λ r, μ (s ∩ closed_ball x r) / μ (closed_ball x r)) (𝓝[>] 0) (𝓝 1))
  (t : set E) (ht : measurable_set t) (h't : μ t ≠ 0) :
  ∀ᶠ r in 𝓝[>] (0 : ℝ), (s ∩ ({x} + r • t)).nonempty :=
begin
  obtain ⟨t', t'_meas, t't, t'pos, t'top⟩ :
    ∃ t', measurable_set t' ∧ t' ⊆ t ∧ 0 < μ t' ∧ μ t' < ⊤ :=
      exists_subset_measure_lt_top ht h't.bot_lt,
  filter_upwards [(tendsto_order.1
    (tendsto_add_haar_inter_smul_one_of_density_one μ s x h t'
      t'_meas t'pos.ne' t'top.ne)).1 0 zero_lt_one],
  assume r hr,
  have : μ (s ∩ ({x} + r • t')) ≠ 0 :=
    λ h', by simpa only [ennreal.not_lt_zero, ennreal.zero_div, h'] using hr,
  have : (s ∩ ({x} + r • t')).nonempty := nonempty_of_measure_ne_zero this,
  apply this.mono (inter_subset_inter subset.rfl _),
  exact add_subset_add subset.rfl (smul_set_mono t't),
end

end measure

end measure_theory


-- TODO: move
class is_norm (α : Type*) {β : Type*} (N : β → ℝ) [normed_field α] [add_comm_group β] [vector_space α β] :=
  (norm_eq_zero_iff: ∀ x : β, N x = 0 ↔ x = 0)
  (norm_smul : ∀ c : α, ∀ x : β, N (c • x) = @has_norm.norm α (normed_field.to_has_norm α) c * N x)
  (triangle : ∀ x y : β, N (x + y) ≤ N x + N y)

-- TODO: move
namespace is_norm

variables (α : Type*) {β : Type*} (N : β → ℝ) [normed_field α] [add_comm_group β] [vector_space α β]

--local notation `∥` x `∥` := N x

lemma norm_zero [is_norm α N] : N 0 = 0 :=
by rw norm_eq_zero_iff α N 0

lemma norm_neg [is_norm α N] (x : β) :N (-x) = N x :=
begin
  rw [←one_smul α x, ←neg_smul, @norm_smul α β N, @norm_neg α normed_ring.to_normed_group],
  rw [@norm_one α (by apply_instance),one_mul,one_smul]
end

lemma norm_nonneg [is_norm α N] (x : β) : 0 ≤ N x :=
begin
  apply (@mul_le_mul_left _ _ _ _ (2 : ℝ) (lt_trans zero_lt_one one_lt_two)).1,
  convert is_norm.triangle α N x (-x),
  { rw [add_neg_self, norm_zero α N, mul_zero] },
  { rw [two_mul,norm_neg α N] }
end

/-
/-- Equivalent norms with explicit coefficients -/
def equiv_by (b₁ : ℝ) (b₂ : ℝ) (N₁ : β → ℝ) (N₂ : β → ℝ)  :=
  ∀ x, N₁ x ≤ b₂ * N₂ x ∧ N₂ x ≤ b₁ * N₁ x

/-- Equivalent norms -/
def equiv (N₁ : β → ℝ) (N₂ : β → ℝ) :=
  ∃ b₁ b₂, equiv_by b₁ b₂ N₁ N₂

lemma equiv_by_abs {N₁ : β → ℝ} {N₂ : β → ℝ} [is_norm α N₁] [is_norm α N₂]
  {b₁ : ℝ} {b₂ : ℝ} (h : equiv_by b₁ b₂ N₁ N₂) :
  equiv_by (abs b₁) (abs b₂) N₁ N₂ :=
begin
  unfold equiv_by at *,
  intros x,
  apply and.intro,
  { apply le_trans (h x).1,
    apply mul_le_mul _ (le_refl _) (norm_nonneg α N₂ _) (abs_nonneg _),
    apply le_abs_self },
  { apply le_trans (h x).2,
    apply mul_le_mul _ (le_refl _) (norm_nonneg α N₁ _) (abs_nonneg _),
    apply le_abs_self }
end

--set_option pp.all true

instance normed_space_is_norm [normed_space α β] :
  is_norm α (norm : β → ℝ) :=
{
  norm_eq_zero_iff := begin
  intros x,
  apply iff.intro,
  intros hx,
  apply eq_of_dist_eq_zero,
  rw [normed_group.dist_eq x],
  simp [hx], end,
  norm_smul := sorry,
  triangle := sorry,
}

-/
end is_norm

section norm

variables {α : Type*} (N : α → ℝ) [hα : add_comm_group α] [hℝα : vector_space ℝ α] [hN : is_norm ℝ N]

def norm_cone : set (α × ℝ) :=
  { x : α × ℝ | N x.1 ≤ x.2 }

set_option trace.class_instances true
-- TODO: why does ` variables ` not work?
lemma cone_norm_cone  {α : Type*} (N : α → ℝ) [hα : add_comm_group α] [hℝα : vector_space ℝ α] [hN : is_norm ℝ N] :
@cone (α × ℝ) (@prod.add_comm_group α ℝ hα (ring.to_add_comm_group ℝ) )
(@prod.vector_space ℝ α ℝ _ _ (/-ring.to_add_comm_group ℝ-/_) hℝα discrete_field.to_vector_space) (@norm_cone α N) :=
begin
  intros x ha c hc,
  unfold norm_cone at *,
  simp [is_norm.norm_smul N c x.fst, real.norm_eq_abs, abs_of_nonneg hc],
  apply mul_le_mul (le_refl _),
  { simp at ha, assumption },
  { apply is_norm.norm_nonneg ℝ N },
  { exact hc }
end

open lattice

-- TODO: why does ` variables ` not work?
--
def dual_norm {α : Type*} (N : α → ℝ) [hα : add_comm_group α] [hℝα : vector_space ℝ α] [hN : is_norm ℝ N] [real_inner_product_space α] :
α → ℝ := lattice.Sup { c : ℝ | ∃ x, c = ⟪ y, x ⟫ ∧ N x ≤ 1 }

/-- The dual norm is well defined if  the norm induced by the inner product
 (norm) can be bounded by the primal norm. In finite dimensional real vector
 spaces all norms are equivalent and the condition is always true. -/
lemma dual_norm_bounded [real_inner_product_space α] (y : α) (b : ℝ) (hb : 0 ≤ b ∧ ∀x, norm x ≤ b * N x):
  bdd_above { c | ∃ x, c = ⟪ y, x ⟫ ∧ N x ≤ 1 } :=
begin
  unfold bdd_above,
  use norm y * b,
  intros c hc,
  apply exists.elim hc,
  intros x hx,
  rw hx.1,
  have h_norm_x : norm x ≤ b,
  begin
    apply le_trans (hb.2 x) _,
    apply mul_le_of_le_one_right hb.1 hx.2,
  end,
  apply le_trans (real_inner_product_space.cauchy_schwartz' y x),
  apply mul_le_mul (le_refl _) h_norm_x (norm_nonneg _) (norm_nonneg _)
end

--set_option pp.all true

instance dual_norm_is_norm
{α : Type*} (N : α → ℝ) [hα : real_inner_product_space α] [vector_space ℝ α] [is_norm ℝ N]
 (b : ℝ) (hb : 0 ≤ b ∧ ∀x, norm x ≤ b * N x):
  is_norm ℝ (dual_norm N) :=
{
  norm_eq_zero_iff :=
  begin
    intros y,
    apply iff.intro,
    { unfold dual_norm,
      intro h,
      have h_nonempty : { c | ∃ x, c = ⟪ y, x ⟫ ∧ N x ≤ 1 } ≠ ∅,
      begin
        apply @set.ne_empty_of_mem ℝ _ 0,
        use 0,
        apply and.intro,
        apply (real_inner_product_space.inner_zero_right y).symm,
        rw is_norm.norm_zero ℝ N,
        linarith
      end,
      have hy : ⟪y, (N y)⁻¹ • y⟫ ≤ 0,
      begin
        apply (lattice.cSup_le_iff (dual_norm_bounded _ y b hb) h_nonempty).1 (le_of_eq h) ⟪y, (N y)⁻¹ • y⟫,
        use (N y)⁻¹ • y,
        apply and.intro rfl,
        rw @is_norm.norm_smul ℝ α N _,
        rw real.norm_eq_abs,
        rw abs_of_nonneg (inv_nonneg.2 (is_norm.norm_nonneg ℝ N y)),
        by_cases h_cases: N y = 0,
        { simp [h_cases], linarith },
        { rw inv_mul_cancel h_cases },
      end,
      have hy : (N y)⁻¹ * ⟪y, y⟫ ≤ 0,
      begin
        rw [←real_inner_product_space.inner_smul_right],
        exact hy,
      end
      show y = 0,
      begin
        apply real_inner_product_space.eq_zero_of_inner_self_eq_zero,

      end
    },
    { sorry }
  end,
  norm_smul := sorry,
  triangle := sorry
}

lemma inner_product_cone [real_inner_product_space α] :
  dual_cone (norm_cone norm) = norm_cone (dual_norm norm) := --TODO use dual norm
begin
  apply set.subset.antisymm,
  { intros x hx, unfold norm_cone, },
  {sorry}
end

end norm
.


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro

! This file was ported from Lean 3 source module data.multiset.lattice
! leanprover-community/mathlib commit f2f413b9d4be3a02840d0663dace76e8fe3da053
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Multiset.FinsetOps
import Mathbin.Data.Multiset.Fold

/-!
# Lattice operations on multisets

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

namespace Multiset

variable {α : Type _}

/-! ### sup -/

section Sup

-- can be defined with just `[has_bot α]` where some lemmas hold without requiring `[order_bot α]`
variable [SemilatticeSup α] [OrderBot α]

#print Multiset.sup /-
/-- Supremum of a multiset: `sup {a, b, c} = a ⊔ b ⊔ c` -/
def sup (s : Multiset α) : α :=
  s.fold (· ⊔ ·) ⊥
#align multiset.sup Multiset.sup
-/

/- warning: multiset.sup_coe -> Multiset.sup_coe is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] (l : List.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (List.{u1} α) (Multiset.{u1} α) (HasLiftT.mk.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (CoeTCₓ.coe.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (coeBase.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (Multiset.hasCoe.{u1} α)))) l)) (List.foldr.{u1, u1} α α (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1)) (Bot.bot.{u1} α (OrderBot.toHasBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1))) _inst_2)) l)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] (l : List.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (Multiset.ofList.{u1} α l)) (List.foldr.{u1, u1} α α (fun (x._@.Mathlib.Data.Multiset.Lattice._hyg.73 : α) (x._@.Mathlib.Data.Multiset.Lattice._hyg.75 : α) => Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) x._@.Mathlib.Data.Multiset.Lattice._hyg.73 x._@.Mathlib.Data.Multiset.Lattice._hyg.75) (Bot.bot.{u1} α (OrderBot.toBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1))) _inst_2)) l)
Case conversion may be inaccurate. Consider using '#align multiset.sup_coe Multiset.sup_coeₓ'. -/
@[simp]
theorem sup_coe (l : List α) : sup (l : Multiset α) = l.foldr (· ⊔ ·) ⊥ :=
  rfl
#align multiset.sup_coe Multiset.sup_coe

/- warning: multiset.sup_zero -> Multiset.sup_zero is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))], Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (OfNat.ofNat.{u1} (Multiset.{u1} α) 0 (OfNat.mk.{u1} (Multiset.{u1} α) 0 (Zero.zero.{u1} (Multiset.{u1} α) (Multiset.hasZero.{u1} α))))) (Bot.bot.{u1} α (OrderBot.toHasBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1))) _inst_2))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))], Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (OfNat.ofNat.{u1} (Multiset.{u1} α) 0 (Zero.toOfNat0.{u1} (Multiset.{u1} α) (Multiset.instZeroMultiset.{u1} α)))) (Bot.bot.{u1} α (OrderBot.toBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1))) _inst_2))
Case conversion may be inaccurate. Consider using '#align multiset.sup_zero Multiset.sup_zeroₓ'. -/
@[simp]
theorem sup_zero : (0 : Multiset α).sup = ⊥ :=
  fold_zero _ _
#align multiset.sup_zero Multiset.sup_zero

/- warning: multiset.sup_cons -> Multiset.sup_cons is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] (a : α) (s : Multiset.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (Multiset.cons.{u1} α a s)) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a (Multiset.sup.{u1} α _inst_1 _inst_2 s))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] (a : α) (s : Multiset.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (Multiset.cons.{u1} α a s)) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) a (Multiset.sup.{u1} α _inst_1 _inst_2 s))
Case conversion may be inaccurate. Consider using '#align multiset.sup_cons Multiset.sup_consₓ'. -/
@[simp]
theorem sup_cons (a : α) (s : Multiset α) : (a ::ₘ s).sup = a ⊔ s.sup :=
  fold_cons_left _ _ _ _
#align multiset.sup_cons Multiset.sup_cons

#print Multiset.sup_singleton /-
@[simp]
theorem sup_singleton {a : α} : ({a} : Multiset α).sup = a :=
  sup_bot_eq
#align multiset.sup_singleton Multiset.sup_singleton
-/

/- warning: multiset.sup_add -> Multiset.sup_add is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (HAdd.hAdd.{u1, u1, u1} (Multiset.{u1} α) (Multiset.{u1} α) (Multiset.{u1} α) (instHAdd.{u1} (Multiset.{u1} α) (Multiset.hasAdd.{u1} α)) s₁ s₂)) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) (Multiset.sup.{u1} α _inst_1 _inst_2 s₁) (Multiset.sup.{u1} α _inst_1 _inst_2 s₂))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (HAdd.hAdd.{u1, u1, u1} (Multiset.{u1} α) (Multiset.{u1} α) (Multiset.{u1} α) (instHAdd.{u1} (Multiset.{u1} α) (Multiset.instAddMultiset.{u1} α)) s₁ s₂)) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) (Multiset.sup.{u1} α _inst_1 _inst_2 s₁) (Multiset.sup.{u1} α _inst_1 _inst_2 s₂))
Case conversion may be inaccurate. Consider using '#align multiset.sup_add Multiset.sup_addₓ'. -/
@[simp]
theorem sup_add (s₁ s₂ : Multiset α) : (s₁ + s₂).sup = s₁.sup ⊔ s₂.sup :=
  Eq.trans (by simp [sup]) (fold_add _ _ _ _ _)
#align multiset.sup_add Multiset.sup_add

#print Multiset.sup_le /-
theorem sup_le {s : Multiset α} {a : α} : s.sup ≤ a ↔ ∀ b ∈ s, b ≤ a :=
  Multiset.induction_on s (by simp)
    (by simp (config := { contextual := true }) [or_imp, forall_and])
#align multiset.sup_le Multiset.sup_le
-/

#print Multiset.le_sup /-
theorem le_sup {s : Multiset α} {a : α} (h : a ∈ s) : a ≤ s.sup :=
  sup_le.1 le_rfl _ h
#align multiset.le_sup Multiset.le_sup
-/

#print Multiset.sup_mono /-
theorem sup_mono {s₁ s₂ : Multiset α} (h : s₁ ⊆ s₂) : s₁.sup ≤ s₂.sup :=
  sup_le.2 fun b hb => le_sup (h hb)
#align multiset.sup_mono Multiset.sup_mono
-/

variable [DecidableEq α]

#print Multiset.sup_dedup /-
@[simp]
theorem sup_dedup (s : Multiset α) : (dedup s).sup = s.sup :=
  fold_dedup_idem _ _ _
#align multiset.sup_dedup Multiset.sup_dedup
-/

/- warning: multiset.sup_ndunion -> Multiset.sup_ndunion is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (Multiset.ndunion.{u1} α (fun (a : α) (b : α) => _inst_3 a b) s₁ s₂)) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) (Multiset.sup.{u1} α _inst_1 _inst_2 s₁) (Multiset.sup.{u1} α _inst_1 _inst_2 s₂))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (Multiset.ndunion.{u1} α (fun (a : α) (b : α) => _inst_3 a b) s₁ s₂)) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) (Multiset.sup.{u1} α _inst_1 _inst_2 s₁) (Multiset.sup.{u1} α _inst_1 _inst_2 s₂))
Case conversion may be inaccurate. Consider using '#align multiset.sup_ndunion Multiset.sup_ndunionₓ'. -/
@[simp]
theorem sup_ndunion (s₁ s₂ : Multiset α) : (ndunion s₁ s₂).sup = s₁.sup ⊔ s₂.sup := by
  rw [← sup_dedup, dedup_ext.2, sup_dedup, sup_add] <;> simp
#align multiset.sup_ndunion Multiset.sup_ndunion

/- warning: multiset.sup_union -> Multiset.sup_union is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (Union.union.{u1} (Multiset.{u1} α) (Multiset.hasUnion.{u1} α (fun (a : α) (b : α) => _inst_3 a b)) s₁ s₂)) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) (Multiset.sup.{u1} α _inst_1 _inst_2 s₁) (Multiset.sup.{u1} α _inst_1 _inst_2 s₂))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (Union.union.{u1} (Multiset.{u1} α) (Multiset.instUnionMultiset.{u1} α (fun (a : α) (b : α) => _inst_3 a b)) s₁ s₂)) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) (Multiset.sup.{u1} α _inst_1 _inst_2 s₁) (Multiset.sup.{u1} α _inst_1 _inst_2 s₂))
Case conversion may be inaccurate. Consider using '#align multiset.sup_union Multiset.sup_unionₓ'. -/
@[simp]
theorem sup_union (s₁ s₂ : Multiset α) : (s₁ ∪ s₂).sup = s₁.sup ⊔ s₂.sup := by
  rw [← sup_dedup, dedup_ext.2, sup_dedup, sup_add] <;> simp
#align multiset.sup_union Multiset.sup_union

/- warning: multiset.sup_ndinsert -> Multiset.sup_ndinsert is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (a : α) (s : Multiset.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (Multiset.ndinsert.{u1} α (fun (a : α) (b : α) => _inst_3 a b) a s)) (Sup.sup.{u1} α (SemilatticeSup.toHasSup.{u1} α _inst_1) a (Multiset.sup.{u1} α _inst_1 _inst_2 s))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeSup.{u1} α] [_inst_2 : OrderBot.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeSup.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (a : α) (s : Multiset.{u1} α), Eq.{succ u1} α (Multiset.sup.{u1} α _inst_1 _inst_2 (Multiset.ndinsert.{u1} α (fun (a : α) (b : α) => _inst_3 a b) a s)) (Sup.sup.{u1} α (SemilatticeSup.toSup.{u1} α _inst_1) a (Multiset.sup.{u1} α _inst_1 _inst_2 s))
Case conversion may be inaccurate. Consider using '#align multiset.sup_ndinsert Multiset.sup_ndinsertₓ'. -/
@[simp]
theorem sup_ndinsert (a : α) (s : Multiset α) : (ndinsert a s).sup = a ⊔ s.sup := by
  rw [← sup_dedup, dedup_ext.2, sup_dedup, sup_cons] <;> simp
#align multiset.sup_ndinsert Multiset.sup_ndinsert

/- warning: multiset.nodup_sup_iff -> Multiset.nodup_sup_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_4 : DecidableEq.{succ u1} α] {m : Multiset.{u1} (Multiset.{u1} α)}, Iff (Multiset.Nodup.{u1} α (Multiset.sup.{u1} (Multiset.{u1} α) (Lattice.toSemilatticeSup.{u1} (Multiset.{u1} α) (Multiset.lattice.{u1} α (fun (a : α) (b : α) => _inst_4 a b))) (Multiset.orderBot.{u1} α) m)) (forall (a : Multiset.{u1} α), (Membership.Mem.{u1, u1} (Multiset.{u1} α) (Multiset.{u1} (Multiset.{u1} α)) (Multiset.hasMem.{u1} (Multiset.{u1} α)) a m) -> (Multiset.Nodup.{u1} α a))
but is expected to have type
  forall {α : Type.{u1}} [_inst_4 : DecidableEq.{succ u1} α] {m : Multiset.{u1} (Multiset.{u1} α)}, Iff (Multiset.Nodup.{u1} α (Multiset.sup.{u1} (Multiset.{u1} α) (Lattice.toSemilatticeSup.{u1} (Multiset.{u1} α) (Multiset.instLatticeMultiset.{u1} α (fun (a : α) (b : α) => _inst_4 a b))) (Multiset.instOrderBotMultisetToLEToPreorderInstPartialOrderMultiset.{u1} α) m)) (forall (a : Multiset.{u1} α), (Membership.mem.{u1, u1} (Multiset.{u1} α) (Multiset.{u1} (Multiset.{u1} α)) (Multiset.instMembershipMultiset.{u1} (Multiset.{u1} α)) a m) -> (Multiset.Nodup.{u1} α a))
Case conversion may be inaccurate. Consider using '#align multiset.nodup_sup_iff Multiset.nodup_sup_iffₓ'. -/
theorem nodup_sup_iff {α : Type _} [DecidableEq α] {m : Multiset (Multiset α)} :
    m.sup.Nodup ↔ ∀ a : Multiset α, a ∈ m → a.Nodup :=
  by
  apply m.induction_on
  · simp
  · intro a s h
    simp [h]
#align multiset.nodup_sup_iff Multiset.nodup_sup_iff

end Sup

/-! ### inf -/

section Inf

-- can be defined with just `[has_top α]` where some lemmas hold without requiring `[order_top α]`
variable [SemilatticeInf α] [OrderTop α]

#print Multiset.inf /-
/-- Infimum of a multiset: `inf {a, b, c} = a ⊓ b ⊓ c` -/
def inf (s : Multiset α) : α :=
  s.fold (· ⊓ ·) ⊤
#align multiset.inf Multiset.inf
-/

/- warning: multiset.inf_coe -> Multiset.inf_coe is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] (l : List.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (List.{u1} α) (Multiset.{u1} α) (HasLiftT.mk.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (CoeTCₓ.coe.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (coeBase.{succ u1, succ u1} (List.{u1} α) (Multiset.{u1} α) (Multiset.hasCoe.{u1} α)))) l)) (List.foldr.{u1, u1} α α (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1)) (Top.top.{u1} α (OrderTop.toHasTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1))) _inst_2)) l)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] (l : List.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (Multiset.ofList.{u1} α l)) (List.foldr.{u1, u1} α α (fun (x._@.Mathlib.Data.Multiset.Lattice._hyg.698 : α) (x._@.Mathlib.Data.Multiset.Lattice._hyg.700 : α) => Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) x._@.Mathlib.Data.Multiset.Lattice._hyg.698 x._@.Mathlib.Data.Multiset.Lattice._hyg.700) (Top.top.{u1} α (OrderTop.toTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1))) _inst_2)) l)
Case conversion may be inaccurate. Consider using '#align multiset.inf_coe Multiset.inf_coeₓ'. -/
@[simp]
theorem inf_coe (l : List α) : inf (l : Multiset α) = l.foldr (· ⊓ ·) ⊤ :=
  rfl
#align multiset.inf_coe Multiset.inf_coe

/- warning: multiset.inf_zero -> Multiset.inf_zero is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))], Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (OfNat.ofNat.{u1} (Multiset.{u1} α) 0 (OfNat.mk.{u1} (Multiset.{u1} α) 0 (Zero.zero.{u1} (Multiset.{u1} α) (Multiset.hasZero.{u1} α))))) (Top.top.{u1} α (OrderTop.toHasTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1))) _inst_2))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))], Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (OfNat.ofNat.{u1} (Multiset.{u1} α) 0 (Zero.toOfNat0.{u1} (Multiset.{u1} α) (Multiset.instZeroMultiset.{u1} α)))) (Top.top.{u1} α (OrderTop.toTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1))) _inst_2))
Case conversion may be inaccurate. Consider using '#align multiset.inf_zero Multiset.inf_zeroₓ'. -/
@[simp]
theorem inf_zero : (0 : Multiset α).inf = ⊤ :=
  fold_zero _ _
#align multiset.inf_zero Multiset.inf_zero

/- warning: multiset.inf_cons -> Multiset.inf_cons is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] (a : α) (s : Multiset.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (Multiset.cons.{u1} α a s)) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a (Multiset.inf.{u1} α _inst_1 _inst_2 s))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] (a : α) (s : Multiset.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (Multiset.cons.{u1} α a s)) (Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) a (Multiset.inf.{u1} α _inst_1 _inst_2 s))
Case conversion may be inaccurate. Consider using '#align multiset.inf_cons Multiset.inf_consₓ'. -/
@[simp]
theorem inf_cons (a : α) (s : Multiset α) : (a ::ₘ s).inf = a ⊓ s.inf :=
  fold_cons_left _ _ _ _
#align multiset.inf_cons Multiset.inf_cons

#print Multiset.inf_singleton /-
@[simp]
theorem inf_singleton {a : α} : ({a} : Multiset α).inf = a :=
  inf_top_eq
#align multiset.inf_singleton Multiset.inf_singleton
-/

/- warning: multiset.inf_add -> Multiset.inf_add is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (HAdd.hAdd.{u1, u1, u1} (Multiset.{u1} α) (Multiset.{u1} α) (Multiset.{u1} α) (instHAdd.{u1} (Multiset.{u1} α) (Multiset.hasAdd.{u1} α)) s₁ s₂)) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) (Multiset.inf.{u1} α _inst_1 _inst_2 s₁) (Multiset.inf.{u1} α _inst_1 _inst_2 s₂))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (HAdd.hAdd.{u1, u1, u1} (Multiset.{u1} α) (Multiset.{u1} α) (Multiset.{u1} α) (instHAdd.{u1} (Multiset.{u1} α) (Multiset.instAddMultiset.{u1} α)) s₁ s₂)) (Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) (Multiset.inf.{u1} α _inst_1 _inst_2 s₁) (Multiset.inf.{u1} α _inst_1 _inst_2 s₂))
Case conversion may be inaccurate. Consider using '#align multiset.inf_add Multiset.inf_addₓ'. -/
@[simp]
theorem inf_add (s₁ s₂ : Multiset α) : (s₁ + s₂).inf = s₁.inf ⊓ s₂.inf :=
  Eq.trans (by simp [inf]) (fold_add _ _ _ _ _)
#align multiset.inf_add Multiset.inf_add

#print Multiset.le_inf /-
theorem le_inf {s : Multiset α} {a : α} : a ≤ s.inf ↔ ∀ b ∈ s, a ≤ b :=
  Multiset.induction_on s (by simp)
    (by simp (config := { contextual := true }) [or_imp, forall_and])
#align multiset.le_inf Multiset.le_inf
-/

#print Multiset.inf_le /-
theorem inf_le {s : Multiset α} {a : α} (h : a ∈ s) : s.inf ≤ a :=
  le_inf.1 le_rfl _ h
#align multiset.inf_le Multiset.inf_le
-/

#print Multiset.inf_mono /-
theorem inf_mono {s₁ s₂ : Multiset α} (h : s₁ ⊆ s₂) : s₂.inf ≤ s₁.inf :=
  le_inf.2 fun b hb => inf_le (h hb)
#align multiset.inf_mono Multiset.inf_mono
-/

variable [DecidableEq α]

#print Multiset.inf_dedup /-
@[simp]
theorem inf_dedup (s : Multiset α) : (dedup s).inf = s.inf :=
  fold_dedup_idem _ _ _
#align multiset.inf_dedup Multiset.inf_dedup
-/

/- warning: multiset.inf_ndunion -> Multiset.inf_ndunion is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (Multiset.ndunion.{u1} α (fun (a : α) (b : α) => _inst_3 a b) s₁ s₂)) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) (Multiset.inf.{u1} α _inst_1 _inst_2 s₁) (Multiset.inf.{u1} α _inst_1 _inst_2 s₂))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (Multiset.ndunion.{u1} α (fun (a : α) (b : α) => _inst_3 a b) s₁ s₂)) (Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) (Multiset.inf.{u1} α _inst_1 _inst_2 s₁) (Multiset.inf.{u1} α _inst_1 _inst_2 s₂))
Case conversion may be inaccurate. Consider using '#align multiset.inf_ndunion Multiset.inf_ndunionₓ'. -/
@[simp]
theorem inf_ndunion (s₁ s₂ : Multiset α) : (ndunion s₁ s₂).inf = s₁.inf ⊓ s₂.inf := by
  rw [← inf_dedup, dedup_ext.2, inf_dedup, inf_add] <;> simp
#align multiset.inf_ndunion Multiset.inf_ndunion

/- warning: multiset.inf_union -> Multiset.inf_union is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (Union.union.{u1} (Multiset.{u1} α) (Multiset.hasUnion.{u1} α (fun (a : α) (b : α) => _inst_3 a b)) s₁ s₂)) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) (Multiset.inf.{u1} α _inst_1 _inst_2 s₁) (Multiset.inf.{u1} α _inst_1 _inst_2 s₂))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (s₁ : Multiset.{u1} α) (s₂ : Multiset.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (Union.union.{u1} (Multiset.{u1} α) (Multiset.instUnionMultiset.{u1} α (fun (a : α) (b : α) => _inst_3 a b)) s₁ s₂)) (Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) (Multiset.inf.{u1} α _inst_1 _inst_2 s₁) (Multiset.inf.{u1} α _inst_1 _inst_2 s₂))
Case conversion may be inaccurate. Consider using '#align multiset.inf_union Multiset.inf_unionₓ'. -/
@[simp]
theorem inf_union (s₁ s₂ : Multiset α) : (s₁ ∪ s₂).inf = s₁.inf ⊓ s₂.inf := by
  rw [← inf_dedup, dedup_ext.2, inf_dedup, inf_add] <;> simp
#align multiset.inf_union Multiset.inf_union

/- warning: multiset.inf_ndinsert -> Multiset.inf_ndinsert is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (a : α) (s : Multiset.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (Multiset.ndinsert.{u1} α (fun (a : α) (b : α) => _inst_3 a b) a s)) (Inf.inf.{u1} α (SemilatticeInf.toHasInf.{u1} α _inst_1) a (Multiset.inf.{u1} α _inst_1 _inst_2 s))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : SemilatticeInf.{u1} α] [_inst_2 : OrderTop.{u1} α (Preorder.toLE.{u1} α (PartialOrder.toPreorder.{u1} α (SemilatticeInf.toPartialOrder.{u1} α _inst_1)))] [_inst_3 : DecidableEq.{succ u1} α] (a : α) (s : Multiset.{u1} α), Eq.{succ u1} α (Multiset.inf.{u1} α _inst_1 _inst_2 (Multiset.ndinsert.{u1} α (fun (a : α) (b : α) => _inst_3 a b) a s)) (Inf.inf.{u1} α (SemilatticeInf.toInf.{u1} α _inst_1) a (Multiset.inf.{u1} α _inst_1 _inst_2 s))
Case conversion may be inaccurate. Consider using '#align multiset.inf_ndinsert Multiset.inf_ndinsertₓ'. -/
@[simp]
theorem inf_ndinsert (a : α) (s : Multiset α) : (ndinsert a s).inf = a ⊓ s.inf := by
  rw [← inf_dedup, dedup_ext.2, inf_dedup, inf_cons] <;> simp
#align multiset.inf_ndinsert Multiset.inf_ndinsert

end Inf

end Multiset


/-
Copyright (c) 2022 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 probability.notation
import probability.integration

/-!
# Variance of random variables

We define the variance of a real-valued random variable as `Var[X] = 𝔼[(X - 𝔼[X])^2]` (in the
`probability_theory` locale).

We prove the basic properties of the variance:
* `variance_le_expectation_sq`: the inequality `Var[X] ≤ 𝔼[X^2]`.
* `meas_ge_le_variance_div_sq`: Chebyshev's inequality, i.e.,
      `ℙ {ω | c ≤ |X ω - 𝔼[X]|} ≤ ennreal.of_real (Var[X] / c ^ 2)`.
* `indep_fun.variance_add`: the variance of the sum of two independent random variables is the sum
  of the variances.
* `indep_fun.variance_sum`: the variance of a finite sum of pairwise independent random variables is
  the sum of the variances.
-/

open measure_theory filter finset

noncomputable theory

open_locale big_operators measure_theory probability_theory ennreal nnreal

namespace probability_theory

/-- The variance of a random variable is `𝔼[X^2] - 𝔼[X]^2` or, equivalently, `𝔼[(X - 𝔼[X])^2]`. We
use the latter as the definition, to ensure better behavior even in garbage situations. -/
def variance {Ω : Type*} {m : measurable_space Ω} (f : Ω → ℝ) (μ : measure Ω) : ℝ :=
μ[(f - (λ x, μ[f])) ^ 2]

@[simp] lemma variance_zero {Ω : Type*} {m : measurable_space Ω} (μ : measure Ω) :
  variance 0 μ = 0 :=
by simp [variance]

lemma variance_nonneg {Ω : Type*} {m : measurable_space Ω} (f : Ω → ℝ) (μ : measure Ω) :
  0 ≤ variance f μ :=
integral_nonneg (λ x, sq_nonneg _)

lemma variance_mul {Ω : Type*} {m : measurable_space Ω} (c : ℝ) (f : Ω → ℝ) (μ : measure Ω) :
  variance (λ x, c * f x) μ = c^2 * variance f μ :=
calc
variance (λ x, c * f x) μ
    = ∫ x, (c * f x - ∫ y, c * f y ∂μ) ^ 2 ∂μ : rfl
... = ∫ x, (c * (f x - ∫ y, f y ∂μ)) ^ 2 ∂μ :
  by { congr' 1 with x, simp_rw [integral_mul_left, mul_sub] }
... = c^2 * variance f μ :
  by { simp_rw [mul_pow, integral_mul_left], refl }

lemma variance_smul {Ω : Type*} {m : measurable_space Ω} (c : ℝ) (f : Ω → ℝ) (μ : measure Ω) :
  variance (c • f) μ = c^2 * variance f μ :=
variance_mul c f μ

lemma variance_smul' {A : Type*} [comm_semiring A] [algebra A ℝ]
  {Ω : Type*} {m : measurable_space Ω} (c : A) (f : Ω → ℝ) (μ : measure Ω) :
  variance (c • f) μ = c^2 • variance f μ :=
begin
  convert variance_smul (algebra_map A ℝ c) f μ,
  { ext1 x, simp only [algebra_map_smul], },
  { simp only [algebra.smul_def, map_pow], }
end

localized
"notation `Var[` X `]` := probability_theory.variance X measure_theory.measure_space.volume"
in probability_theory

variables {Ω : Type*} [measure_space Ω] [is_probability_measure (volume : measure Ω)]

lemma variance_def' {X : Ω → ℝ} (hX : mem_ℒp X 2) :
  Var[X] = 𝔼[X^2] - 𝔼[X]^2 :=
begin
  rw [variance, sub_sq', integral_sub', integral_add'], rotate,
  { exact hX.integrable_sq },
  { convert integrable_const (𝔼[X] ^ 2),
    apply_instance },
  { apply hX.integrable_sq.add,
    convert integrable_const (𝔼[X] ^ 2),
    apply_instance },
  { exact ((hX.integrable ennreal.one_le_two).const_mul 2).mul_const' _ },
  simp only [integral_mul_right, pi.pow_apply, pi.mul_apply, pi.bit0_apply, pi.one_apply,
    integral_const (integral ℙ X ^ 2), integral_mul_left (2 : ℝ), one_mul,
    variance, pi.pow_apply, measure_univ, ennreal.one_to_real, algebra.id.smul_eq_mul],
  ring,
end

lemma variance_le_expectation_sq {X : Ω → ℝ} :
  Var[X] ≤ 𝔼[X^2] :=
begin
  by_cases h_int : integrable X, swap,
  { simp only [variance, integral_undef h_int, pi.pow_apply, pi.sub_apply, sub_zero] },
  by_cases hX : mem_ℒp X 2,
  { rw variance_def' hX,
    simp only [sq_nonneg, sub_le_self_iff] },
  { rw [variance, integral_undef],
    { exact integral_nonneg (λ a, sq_nonneg _) },
    { assume h,
      have A : mem_ℒp (X - λ (x : Ω), 𝔼[X]) 2 ℙ := (mem_ℒp_two_iff_integrable_sq
        (h_int.ae_strongly_measurable.sub ae_strongly_measurable_const)).2 h,
      have B : mem_ℒp (λ (x : Ω), 𝔼[X]) 2 ℙ := mem_ℒp_const _,
      apply hX,
      convert A.add B,
      simp } }
end

/-- *Chebyshev's inequality* : one can control the deviation probability of a real random variable
from its expectation in terms of the variance. -/
theorem meas_ge_le_variance_div_sq {X : Ω → ℝ} (hX : mem_ℒp X 2) {c : ℝ} (hc : 0 < c) :
  ℙ {ω | c ≤ |X ω - 𝔼[X]|} ≤ ennreal.of_real (Var[X] / c ^ 2) :=
begin
  have A : (ennreal.of_real c : ℝ≥0∞) ≠ 0,
    by simp only [hc, ne.def, ennreal.of_real_eq_zero, not_le],
  have B : ae_strongly_measurable (λ (ω : Ω), 𝔼[X]) ℙ := ae_strongly_measurable_const,
  convert meas_ge_le_mul_pow_snorm ℙ ennreal.two_ne_zero ennreal.two_ne_top
    (hX.ae_strongly_measurable.sub B) A,
  { ext ω,
    set d : ℝ≥0 := ⟨c, hc.le⟩ with hd,
    have cd : c = d, by simp only [subtype.coe_mk],
    simp only [pi.sub_apply, ennreal.coe_le_coe, ← real.norm_eq_abs, ← coe_nnnorm,
      nnreal.coe_le_coe, cd, ennreal.of_real_coe_nnreal] },
  { rw (hX.sub (mem_ℒp_const _)).snorm_eq_integral_rpow_norm
      ennreal.two_ne_zero ennreal.two_ne_top,
    simp only [pi.sub_apply, ennreal.to_real_bit0, ennreal.one_to_real],
    rw ennreal.of_real_rpow_of_nonneg _ zero_le_two, rotate,
    { apply real.rpow_nonneg_of_nonneg,
      exact integral_nonneg (λ x, real.rpow_nonneg_of_nonneg (norm_nonneg _) _) },
    rw [variance, ← real.rpow_mul, inv_mul_cancel], rotate,
    { exact two_ne_zero },
    { exact integral_nonneg (λ x, real.rpow_nonneg_of_nonneg (norm_nonneg _) _) },
    simp only [pi.pow_apply, pi.sub_apply, real.rpow_two, real.rpow_one, real.norm_eq_abs,
      pow_bit0_abs, ennreal.of_real_inv_of_pos hc, ennreal.rpow_two],
    rw [← ennreal.of_real_pow (inv_nonneg.2 hc.le), ← ennreal.of_real_mul (sq_nonneg _),
      div_eq_inv_mul, inv_pow] }
end

/-- The variance of the sum of two independent random variables is the sum of the variances. -/
theorem indep_fun.variance_add {X Y : Ω → ℝ}
  (hX : mem_ℒp X 2) (hY : mem_ℒp Y 2) (h : indep_fun X Y) :
  Var[X + Y] = Var[X] + Var[Y] :=
calc
Var[X + Y] = 𝔼[λ a, (X a)^2 + (Y a)^2 + 2 * X a * Y a] - 𝔼[X+Y]^2 :
  by simp [variance_def' (hX.add hY), add_sq']
... = (𝔼[X^2] + 𝔼[Y^2] + 2 * 𝔼[X * Y]) - (𝔼[X] + 𝔼[Y])^2 :
begin
  simp only [pi.add_apply, pi.pow_apply, pi.mul_apply, mul_assoc],
  rw [integral_add, integral_add, integral_add, integral_mul_left],
  { exact hX.integrable ennreal.one_le_two },
  { exact hY.integrable ennreal.one_le_two },
  { exact hX.integrable_sq },
  { exact hY.integrable_sq },
  { exact hX.integrable_sq.add hY.integrable_sq },
  { apply integrable.const_mul,
    exact h.integrable_mul (hX.integrable ennreal.one_le_two) (hY.integrable ennreal.one_le_two) }
end
... = (𝔼[X^2] + 𝔼[Y^2] + 2 * (𝔼[X] * 𝔼[Y])) - (𝔼[X] + 𝔼[Y])^2 :
begin
  congr,
  exact h.integral_mul_of_integrable
    (hX.integrable ennreal.one_le_two) (hY.integrable ennreal.one_le_two),
end
... = Var[X] + Var[Y] :
  by { simp only [variance_def', hX, hY, pi.pow_apply], ring }

/-- The variance of a finite sum of pairwise independent random variables is the sum of the
variances. -/
theorem indep_fun.variance_sum {ι : Type*} {X : ι → Ω → ℝ} {s : finset ι}
  (hs : ∀ i ∈ s, mem_ℒp (X i) 2) (h : set.pairwise ↑s (λ i j, indep_fun (X i) (X j))) :
  Var[∑ i in s, X i] = ∑ i in s, Var[X i] :=
begin
  classical,
  induction s using finset.induction_on with k s ks IH,
  { simp only [finset.sum_empty, variance_zero] },
  rw [variance_def' (mem_ℒp_finset_sum' _ hs), sum_insert ks, sum_insert ks],
  simp only [add_sq'],
  calc 𝔼[X k ^ 2 + (∑ i in s, X i) ^ 2 + 2 * X k * ∑ i in s, X i] - 𝔼[X k + ∑ i in s, X i] ^ 2
  = (𝔼[X k ^ 2] + 𝔼[(∑ i in s, X i) ^ 2] + 𝔼[2 * X k * ∑ i in s, X i])
    - (𝔼[X k] + 𝔼[∑ i in s, X i]) ^ 2 :
  begin
    rw [integral_add', integral_add', integral_add'],
    { exact mem_ℒp.integrable ennreal.one_le_two (hs _ (mem_insert_self _ _)) },
    { apply integrable_finset_sum' _ (λ i hi, _),
      exact mem_ℒp.integrable ennreal.one_le_two (hs _ (mem_insert_of_mem hi)) },
    { exact mem_ℒp.integrable_sq (hs _ (mem_insert_self _ _)) },
    { apply mem_ℒp.integrable_sq,
      exact mem_ℒp_finset_sum' _ (λ i hi, (hs _ (mem_insert_of_mem hi))) },
    { apply integrable.add,
      { exact mem_ℒp.integrable_sq (hs _ (mem_insert_self _ _)) },
      { apply mem_ℒp.integrable_sq,
        exact mem_ℒp_finset_sum' _ (λ i hi, (hs _ (mem_insert_of_mem hi))) } },
    { rw mul_assoc,
      apply integrable.const_mul _ 2,
      simp only [mul_sum, sum_apply, pi.mul_apply],
      apply integrable_finset_sum _ (λ i hi, _),
      apply indep_fun.integrable_mul _
        (mem_ℒp.integrable ennreal.one_le_two (hs _ (mem_insert_self _ _)))
        (mem_ℒp.integrable ennreal.one_le_two (hs _ (mem_insert_of_mem hi))),
      apply h (mem_insert_self _ _) (mem_insert_of_mem hi),
      exact (λ hki, ks (hki.symm ▸ hi)) }
  end
  ... = Var[X k] + Var[∑ i in s, X i] +
    (𝔼[2 * X k * ∑ i in s, X i] - 2 * 𝔼[X k] * 𝔼[∑ i in s, X i]) :
  begin
    rw [variance_def' (hs _ (mem_insert_self _ _)),
        variance_def' (mem_ℒp_finset_sum' _ (λ i hi, (hs _ (mem_insert_of_mem hi))))],
    ring,
  end
  ... = Var[X k] + Var[∑ i in s, X i] :
  begin
    simp only [mul_assoc, integral_mul_left, pi.mul_apply, pi.bit0_apply, pi.one_apply, sum_apply,
      add_right_eq_self, mul_sum],
    rw integral_finset_sum s (λ i hi, _), swap,
    { apply integrable.const_mul _ 2,
      apply indep_fun.integrable_mul _
        (mem_ℒp.integrable ennreal.one_le_two (hs _ (mem_insert_self _ _)))
        (mem_ℒp.integrable ennreal.one_le_two (hs _ (mem_insert_of_mem hi))),
      apply h (mem_insert_self _ _) (mem_insert_of_mem hi),
      exact (λ hki, ks (hki.symm ▸ hi)) },
    rw [integral_finset_sum s
      (λ i hi, (mem_ℒp.integrable ennreal.one_le_two (hs _ (mem_insert_of_mem hi)))),
      mul_sum, mul_sum, ← sum_sub_distrib],
    apply finset.sum_eq_zero (λ i hi, _),
    rw [integral_mul_left, indep_fun.integral_mul_of_integrable', sub_self],
    { apply h (mem_insert_self _ _) (mem_insert_of_mem hi),
      exact (λ hki, ks (hki.symm ▸ hi)) },
    { exact mem_ℒp.integrable ennreal.one_le_two (hs _ (mem_insert_self _ _)) },
    { exact mem_ℒp.integrable ennreal.one_le_two (hs _ (mem_insert_of_mem hi)) }
  end
  ... = Var[X k] + ∑ i in s, Var[X i] :
    by rw IH (λ i hi, hs i (mem_insert_of_mem hi))
      (h.mono (by simp only [coe_insert, set.subset_insert]))
end

end probability_theory


import ring_theory.int.basic data.nat.parity

/-! # IMO 2007 N2 -/

namespace IMOSL
namespace IMO2007N2

/-- Final solution, part 1 -/
theorem final_solution_part1 {b n : ℕ} (h : 0 < b)
    (h0 : ∀ k : ℕ, 0 < k → ∃ c : ℤ, (k : ℤ) ∣ b - c ^ n) :
  ∃ a : ℤ, (b : ℤ) = a ^ n :=
begin
  replace h0 := h0 (b ^ 2) (pow_pos h 2),
  suffices h1 : ∃ c : ℤ, associated (c ^ n) b,
  { cases h1 with c h1,
    rw int.associated_iff at h1,
    cases h1 with h1 h1,
    exact ⟨c, h1.symm⟩,
    use (-c); rw [neg_pow, h1, odd.neg_one_pow, neg_one_mul, neg_neg],
    rw nat.odd_iff_not_even; intros h0,
    refine not_lt_of_le (even.pow_nonneg h0 c) _,
    rwa [h1, right.neg_neg_iff, int.coe_nat_pos] },

  rcases h0 with ⟨c, d, h0⟩,
  rw [nat.cast_pow, sq, mul_assoc, sub_eq_iff_eq_add, ← sub_eq_iff_eq_add', ← mul_one_sub] at h0,
  refine exists_associated_pow_of_mul_eq_pow (int.is_unit_iff.mpr _) h0; left,
  rw [← int.coe_gcd, nat.cast_eq_one, int.gcd_eq_one_iff_coprime],
  refine ⟨d, 1, _⟩,
  rw [one_mul, mul_comm, add_sub_cancel'_right]
end

end IMO2007N2
end IMOSL


import tactic
import data.real.basic

/-
- nom des variables dans la def de limite !
- variables muettes = variables globales : bof
- calcul avec abs : abs (l'-l) /2 >0 ??
(x ≠ 0 → |x| >0)
- ne pas ajouter l'inégalité au contexte si elle y est déjà !
- 'dsimp only at h' effectue les beta-réduction : (λ x, f x) 37 = f 37
- max : def et propriétés
- utiliser specialize ??

-/

-- dEAduction tactics
import structures2      -- hypo_analysis, targets_analysis
import utils            -- no_meta_vars
import user_notations   -- notations that can be used in deaduction UI for a new object

-- dEAduction definitions
-- import set_definitions
import real_definitions

local attribute [instance] classical.prop_decidable

section course
-- notation `|` x `|` := abs x

-----------------
-- definitions --
-----------------
namespace definitions
/- dEAduction
PrettyName
  Définitions
-/

/-
abs_pos : 0 < |a| ↔ a ≠ 0
abs_mul x y : |x * y| = |x| * |y|
abs_add x y : |x + y| ≤ |x| + |y|
-/

/-
Max :
def
max ≥ n et n'
1) We will be using `max` a lot in this workshop. `max A B` is
the max of `A` and `B`. `max` is a definition, not a theorem, so
that means that there will be an API associated with it, i.e.
a list of little theorems which make `max` possible to use.
We just saw the two important theorems which we'll be using:
`le_max_left A B : A ≤ max A B` and
`le_max-right A B : B ≤ max A B`.
There are other cool functions in the `max` API, for example
`max_le : A ≤ C → B ≤ C → max A B ≤ C`. The easiest way to
find your way around the `max` API is to *guess* what the names
of the theorems are! For example what do you think
`max A B < C ↔ A < C ∧ B < C` is called?
-/
namespace maximum

lemma theorem.ppe_max_gauche : ∀ a b : ℝ, a ≤ max a b :=
begin
  intros a b,
  exact le_max_left a b,
end

lemma theorem.ppe_max_droite : ∀ a b : ℝ,  b ≤ max a b :=
begin
  intros a b,
  exact le_max_right a b,
end

lemma theorem.max_ppe (a b c : ℝ) (Ha: a ≤ c) (Hb: b ≤ c) : max a b ≤ c :=
begin
  exact max_le Ha Hb,
end

lemma theorem.max_pp (a b c : ℝ) (Ha: a < c) (Hb: b < c) : max a b < c :=
begin
  exact max_lt Ha Hb,
end

end maximum

namespace valeur_absolue

lemma theorem.valeur_absolue
(x : ℝ) :
((0 ≤ x) → (abs x = x)) and ((x ≤ 0) → (abs x = -x)) :=
begin
  split, exact abs_of_nonneg, exact abs_of_nonpos,
end

lemma theorem.majoration_valeur_absolue
(x : ℝ) (r : ℝ) :
(|x| < r) ↔ ((-r < x) ∧ (x < r))
:=
/- dEAduction
PrettyName
  Majoration d'une valeur absolue
-/
begin
  exact abs_lt
end

lemma theorem.inegalite_triangulaire
(x : ℝ) (y : ℝ) : |x + y| ≤ |x| + |y|
:=
/- dEAduction
PrettyName
  Inégalité triangulaire
-/
begin
  exact abs_add x y
end

lemma theorem.valeur_absolue_produit
(x : ℝ) (y : ℝ) : |x * y| = |x| * |y|
:=
/- dEAduction
PrettyName
  Valeur absolue d'un produit
-/
begin
  exact abs_mul x y
end

end valeur_absolue

/-- `l` is the limit of the sequence `a` of reals -/
definition limite (u : ℕ → ℝ) (l : ℝ) : Prop :=
∀ ε > 0, ∃ N, ∀ n ≥ N, | u n - l | < ε

lemma definition.limite
{u : ℕ → ℝ} {l : ℝ} :
(limite u l) ↔
∀ ε > 0, ∃ N, ∀ n ≥ N, | u n - l | < ε
:=
/- dEAduction
PrettyName
  Limite d'une suite
Variables
  ε, N, n
-/
begin
  refl
end

end definitions

-----------------
--  exercices  --
-----------------
namespace exercices
open definitions
/-
div_pos : 0 < a → 0 < b → 0 < a / b
lt_div_iff : 0 < c → (a < b / c ↔ a * c < b)
lt_div_iff' : 0 < c → (a < b / c ↔ c * a < b)
-/

lemma exercise.test_compute
:
0 ≤ 1 ∧ 2+2 = 4
:=
begin
  todo
end

lemma exercise.limite_constante
(u : ℕ → ℝ) (c : ℝ) (H : ∀ n, u n = c) :
limite u c :=
/- dEAduction
PrettyName
  La limite d'une suite constante
-/
begin
--   rw definition.limite,
--   intros ε Hε,
--   use 0,
--   intros n H1,
--   rw H,
-- `[ solve1 {norm_num at * }, trace "EFFECTIVE CODE n°4.0"] <|> `[ `[ norm_num at *, trace "EFFECTIVE CODE n°5.0"] <|> `[ skip, trace "EFFECTIVE CODE n°5.1"], compute_n 10, trace "EFFECTIVE CODE n°4.1"],
  todo
end

lemma exercise.limite_unique
(u : ℕ → ℝ) (l : ℝ)(l' : ℝ) (H : limite u l) (H' : limite u l') :
l = l'
:=
/- dEAduction
PrettyName
  Unicité de la limite
-/
begin
  -- by_contradiction,
  -- wlog Hll': l < l',
  -- exact lt_or_gt_of_ne a,
  -- set ε := (l'-l)/2 with Heps,
  -- have Hpos: ε >0, by compute1,
  -- specialize H ε Hpos, cases H with N1,
  -- specialize H' ε Hpos, cases H' with N2,
  -- set n := max N1 N2 with Hn,
  -- have HnsuppN1: n ≥ N1, from le_max_left N1 N2,
  -- have ineq1 := H_h n HnsuppN1,
  -- have HnsuppN2: n ≥ N2, from le_max_right N1 N2,
  -- have ineq1 := H'_h n HnsuppN2,

  -- sorry,

  todo
end

lemma exercise.limite_somme
(u u': ℕ → ℝ) (l l' : ℝ) (H : limite u l)
(H' : limite u' l') :
limite (λn, u n + u' n) (l+l')
:=
/- dEAduction
PrettyName
  Limite d'une somme
-/
begin
--   rw definitions.definition.limite,
-- rw definitions.definition.limite at H H',
-- intro ε, intro H1,
-- have H2 := H _ H1,
-- have H3 := H' _ H1,
-- cases H2 with n H4,
-- cases H3 with n' H5,
-- let x2 := max n n', have H7 : x2 = max n n', refl,
-- have H9 := @definitions.maximum.theorem.ppe_max_gauche,
-- have H10 := H9 n n',
-- -- norm_num at H10,
-- rw H7 at H10,
  todo
end

-- preuve par DL ?
lemma exercise.limite_produit
(u u': ℕ → ℝ) (l l' : ℝ) (H : limite u l)
(H' : limite u' l') :
limite (λn, (u n) * (u' n)) (l*l')
:=
/- dEAduction
PrettyName
  Limite d'un produit
-/
begin
  todo
end

lemma exercise.limite_inegalites
(u u': ℕ → ℝ) (l l' : ℝ) (H : limite u l)
(H' : limite u' l')
(H'' : ∀n, u n ≤ u' n ) :
l ≤ l'
:=
/- dEAduction
PrettyName
  Passage à la limite dans une inégalité
-/
begin
  todo
end

lemma exercise.gendarmes
(u u' v  : ℕ → ℝ) (l : ℝ)
(H : limite u l) (H' : limite u' l)
(H'' : ∀n, (((u n) ≤ v n) and ((v n) ≤ u' n))) :
limite v l
:=
/- dEAduction
PrettyName
  Théorème des gendarmes
-/
begin
  todo
end

-- Let's make a new definition.
definition is_bounded (a : ℕ → ℝ) := ∃ B, ∀ n, |a n| ≤ B

-- Now try this:
lemma tendsto_bounded_mul_zero {a : ℕ → ℝ} {b : ℕ → ℝ}
  (hA : is_bounded a) (hB : limite b 0)
  : limite (λ n, (a n) * (b n)) 0 :=
begin
  todo,
end

end exercices

/-
Lemmes évidents :
constant -> convergent
convergent -> borné ?
-/

end course


/-
Copyright (c) 2014 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Amelia Livingston, Yury Kudryashov,
Neil Strickland, Aaron Anderson
-/
import algebra.group_with_zero.basic
import algebra.divisibility.units

/-!
# Divisibility in groups with zero.

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

Lemmas about divisibility in groups and monoids with zero.

-/

variables {α : Type*}

section semigroup_with_zero

variables [semigroup_with_zero α] {a : α}

theorem eq_zero_of_zero_dvd (h : 0 ∣ a) : a = 0 :=
dvd.elim h (λ c H', H'.trans (zero_mul c))

/-- Given an element `a` of a commutative semigroup with zero, there exists another element whose
    product with zero equals `a` iff `a` equals zero. -/
@[simp] lemma zero_dvd_iff : 0 ∣ a ↔ a = 0 :=
⟨eq_zero_of_zero_dvd, λ h, by { rw h, use 0, simp }⟩

@[simp] theorem dvd_zero (a : α) : a ∣ 0 := dvd.intro 0 (by simp)

end semigroup_with_zero

/-- Given two elements `b`, `c` of a `cancel_monoid_with_zero` and a nonzero element `a`,
 `a*b` divides `a*c` iff `b` divides `c`. -/
theorem mul_dvd_mul_iff_left [cancel_monoid_with_zero α] {a b c : α}
  (ha : a ≠ 0) : a * b ∣ a * c ↔ b ∣ c :=
exists_congr $ λ d, by rw [mul_assoc, mul_right_inj' ha]

/-- Given two elements `a`, `b` of a commutative `cancel_monoid_with_zero` and a nonzero
  element `c`, `a*c` divides `b*c` iff `a` divides `b`. -/
theorem mul_dvd_mul_iff_right [cancel_comm_monoid_with_zero α] {a b c : α} (hc : c ≠ 0) :
  a * c ∣ b * c ↔ a ∣ b :=
exists_congr $ λ d, by rw [mul_right_comm, mul_left_inj' hc]

section comm_monoid_with_zero

variable [comm_monoid_with_zero α]

/-- `dvd_not_unit a b` expresses that `a` divides `b` "strictly", i.e. that `b` divided by `a`
is not a unit. -/
def dvd_not_unit (a b : α) : Prop := a ≠ 0 ∧ ∃ x, ¬is_unit x ∧ b = a * x

lemma dvd_not_unit_of_dvd_of_not_dvd {a b : α} (hd : a ∣ b) (hnd : ¬ b ∣ a) :
  dvd_not_unit a b :=
begin
  split,
  { rintro rfl, exact hnd (dvd_zero _) },
  { rcases hd with ⟨c, rfl⟩,
    refine ⟨c, _, rfl⟩,
    rintro ⟨u, rfl⟩,
    simpa using hnd }
end

end comm_monoid_with_zero

lemma dvd_and_not_dvd_iff [cancel_comm_monoid_with_zero α] {x y : α} :
  x ∣ y ∧ ¬y ∣ x ↔ dvd_not_unit x y :=
⟨λ ⟨⟨d, hd⟩, hyx⟩, ⟨λ hx0, by simpa [hx0] using hyx, ⟨d,
    mt is_unit_iff_dvd_one.1 (λ ⟨e, he⟩, hyx ⟨e, by rw [hd, mul_assoc, ← he, mul_one]⟩), hd⟩⟩,
  λ ⟨hx0, d, hdu, hdx⟩, ⟨⟨d, hdx⟩, λ ⟨e, he⟩, hdu (is_unit_of_dvd_one _
    ⟨e, mul_left_cancel₀ hx0 $ by conv {to_lhs, rw [he, hdx]};simp [mul_assoc]⟩)⟩⟩

section monoid_with_zero

variable [monoid_with_zero α]

theorem ne_zero_of_dvd_ne_zero {p q : α} (h₁ : q ≠ 0)
  (h₂ : p ∣ q) : p ≠ 0 :=
begin
  rcases h₂ with ⟨u, rfl⟩,
  exact left_ne_zero_of_mul h₁,
end

end monoid_with_zero


/-
Copyright (c) 2020 Adam Topaz. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Adam Topaz
-/
import data.finset.basic

/-!
# Constructions involving sets of sets.

## Finite Intersections

We define a structure `has_finite_inter` which asserts that a set `S` of subsets of `α` is
closed under finite intersections.

We define `finite_inter_closure` which, given a set `S` of subsets of `α`, is the smallest
set of subsets of `α` which is closed under finite intersections.

`finite_inter_closure S` is endowed with a term of type `has_finite_inter` using
`finite_inter_closure_has_finite_inter`.

-/

variables {α : Type*} (S : set (set α))

/-- A structure encapsulating the fact that a set of sets is closed under finite intersection. -/
structure has_finite_inter :=
(univ_mem : set.univ ∈ S)
(inter_mem : ∀ ⦃s⦄, s ∈ S → ∀ ⦃t⦄, t ∈ S → s ∩ t ∈ S)

namespace has_finite_inter

-- Satisfying the inhabited linter...
instance : inhabited (has_finite_inter ({set.univ} : set (set α))) :=
⟨⟨by tauto, λ _ h1 _ h2, by simp [set.mem_singleton_iff.1 h1, set.mem_singleton_iff.1 h2]⟩⟩

/-- The smallest set of sets containing `S` which is closed under finite intersections. -/
inductive finite_inter_closure : set (set α)
| basic {s} : s ∈ S → finite_inter_closure s
| univ : finite_inter_closure set.univ
| inter {s t} : finite_inter_closure s → finite_inter_closure t → finite_inter_closure (s ∩ t)

/-- Defines `has_finite_inter` for `finite_inter_closure S`. -/
def finite_inter_closure_has_finite_inter : has_finite_inter (finite_inter_closure S) :=
{ univ_mem := finite_inter_closure.univ,
  inter_mem := λ _ h _, finite_inter_closure.inter h }

variable {S}
lemma finite_inter_mem (cond : has_finite_inter S) (F : finset (set α)) :
  ↑F ⊆ S → ⋂₀ (↑F : set (set α)) ∈ S :=
begin
  classical,
  refine finset.induction_on F (λ _, _) _,
  { simp [cond.univ_mem] },
  { intros a s h1 h2 h3,
    suffices : a ∩ ⋂₀ ↑s ∈ S, by simpa,
    exact cond.inter_mem (h3 (finset.mem_insert_self a s))
                         (h2 $ λ x hx, h3 $ finset.mem_insert_of_mem hx) }
end

lemma finite_inter_closure_insert {A : set α} (cond : has_finite_inter S)
  (P ∈ finite_inter_closure (insert A S)) : P ∈ S ∨ ∃ Q ∈ S, P = A ∩ Q :=
begin
  induction H with S h T1 T2 _ _ h1 h2,
  { cases h,
    { exact or.inr ⟨set.univ, cond.univ_mem, by simpa⟩ },
    { exact or.inl h } },
  { exact or.inl cond.univ_mem },
  { rcases h1 with (h | ⟨Q, hQ, rfl⟩); rcases h2 with (i | ⟨R, hR, rfl⟩),
    { exact or.inl (cond.inter_mem h i) },
    { exact or.inr ⟨T1 ∩ R, cond.inter_mem h hR,
        by simp only [ ←set.inter_assoc, set.inter_comm _ A]⟩ },
    { exact or.inr ⟨Q ∩ T2, cond.inter_mem hQ i, by simp only [set.inter_assoc]⟩ },
    { exact or.inr ⟨Q ∩ R, cond.inter_mem hQ hR,
        by { ext x, split; simp { contextual := tt} }⟩ } }
end

end has_finite_inter