/-
Copyright (c) 2022 Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Damiano Testa
-/
import algebra.covariant_and_contravariant
import algebra.group_with_zero.defs

/-!
# Multiplication by ·positive· elements is monotonic

Let `α` be a type with `<` and `0`.  We use the type `{x : α // 0 < x}` of positive elements of `α`
to prove results about monotonicity of multiplication.  We also introduce the local notation `α>0`
for the subtype `{x : α // 0 < x}`:

*  the notation `α>0` to stands for `{x : α // 0 < x}`.

If the type `α` also has a multiplication, then we combine this with (`contravariant_`)
`covariant_class`es to assume that multiplication by positive elements is (strictly) monotone on a
`mul_zero_class`, `monoid_with_zero`,...
More specifically, we use extensively the following typeclasses:

* monotone left
* * `covariant_class α>0 α (λ x y, x * y) (≤)`, abbreviated `pos_mul_mono α`,
    expressing that multiplication by positive elements on the left is monotone;
* * `covariant_class α>0 α (λ x y, x * y) (<)`, abbreviated `pos_mul_strict_mono α`,
    expressing that multiplication by positive elements on the left is strictly monotone;
* monotone right
* * `covariant_class α>0 α (λ x y, y * x) (≤)`, abbreviated `mul_pos_mono α`,
    expressing that multiplication by positive elements on the right is monotone;
* * `covariant_class α>0 α (λ x y, y * x) (<)`, abbreviated `mul_pos_strict_mono α`,
    expressing that multiplication by positive elements on the right is strictly monotone.
* reverse monotone left
* * `contravariant_class α>0 α (λ x y, x * y) (≤)`, abbreviated `pos_mul_mono_rev α`,
    expressing that multiplication by positive elements on the left is reverse monotone;
* * `contravariant_class α>0 α (λ x y, x * y) (<)`, abbreviated `pos_mul_reflect_lt α`,
    expressing that multiplication by positive elements on the left is strictly reverse monotone;
* reverse reverse monotone right
* * `contravariant_class α>0 α (λ x y, y * x) (≤)`, abbreviated `mul_pos_mono_rev α`,
    expressing that multiplication by positive elements on the right is reverse monotone;
* * `contravariant_class α>0 α (λ x y, y * x) (<)`, abbreviated `mul_pos_reflect_lt α`,
    expressing that multiplication by positive elements on the right is strictly reverse monotone.

##  Formalization comments

We use `α>0 = {x : α // 0 < x}` with a strict inequality since in most cases what happens with `0`
is clear.  This creates a few bumps in the first couple of proofs, where we have to split cases on
whether an element is `0` or not, but goes smoothly after that.  A further advantage is that we
only introduce notation for the positive elements and we do not need also the non-negative ones.
-/

/- I am changing the file `algebra/order/monoid_lemmas` incrementally, with the idea of
reproducing almost all of the proofs in `algebra/order/ring` with weaker assumptions. -/

universe u
variable {α : Type u}

/-  Notation for positive elements
https://
leanprover.zulipchat.com/#narrow/stream/113488-general/topic/notation.20for.20positive.20elements
-/
local notation `α>0` := {x : α // 0 < x}

namespace zero_lt

section abbreviations_strict_mono
variables (X : Type u) [has_mul X] [has_zero X] [has_lt X]

/--  `zero_lt.pos_mul_strict_mono α` is an abbreviation for
`covariant_class α>0 α (λ x y, x * y) (<)`,
expressing that multiplication by positive elements on the left is strictly monotone. -/
abbreviation pos_mul_strict_mono : Prop :=
covariant_class {x : X // 0 < x} X (λ x y, x * y) (<)

/--  `zero_lt.mul_pos_strict_mono α` is an abbreviation for
`covariant_class α>0 α (λ x y, y * x) (<)`,
expressing that multiplication by positive elements on the right is strictly monotone. -/
abbreviation mul_pos_strict_mono : Prop :=
covariant_class {x : X // 0 < x} X (λ x y, y * x) (<)

/--  `zero_lt.pos_mul_reflect_lt α` is an abbreviation for
`contravariant_class α>0 α (λ x y, x * y) (<)`,
expressing that multiplication by positive elements on the left is strictly reverse monotone. -/
abbreviation pos_mul_reflect_lt : Prop :=
contravariant_class {x : X // 0 < x} X (λ x y, x * y) (<)

/--  `zero_lt.mul_pos_reflect_lt α` is an abbreviation for
`contravariant_class α>0 α (λ x y, y * x) (<)`,
expressing that multiplication by positive elements on the right is strictly reverse monotone. -/
abbreviation mul_pos_reflect_lt : Prop :=
contravariant_class {x : X // 0 < x} X (λ x y, y * x) (<)

end abbreviations_strict_mono

section abbreviations_mono
variables (X : Type*) [has_mul X] [has_zero X] [preorder X]

/--  `zero_lt.pos_mul_mono α` is an abbreviation for
`covariant_class α>0 α (λ x y, x * y) (≤)`,
expressing that multiplication by positive elements on the left is monotone. -/
abbreviation pos_mul_mono : Prop :=
covariant_class {x : X // 0 < x} X (λ x y, x * y) (≤)

/--  `zero_lt.mul_pos_mono α` is an abbreviation for
`covariant_class α>0 α (λ x y, y * x) (≤)`,
expressing that multiplication by positive elements on the right is monotone. -/
abbreviation mul_pos_mono : Prop :=
covariant_class {x : X // 0 < x} X (λ x y, y * x) (≤)

/--  `zero_lt.pos_mul_mono_rev α` is an abbreviation for
`contravariant_class α>0 α (λ x y, x * y) (≤)`,
expressing that multiplication by positive elements on the left is reverse monotone. -/
abbreviation pos_mul_mono_rev : Prop :=
contravariant_class {x : X // 0 < x} X (λ x y, x * y) (≤)

/--  `zero_lt.mul_pos_mono_rev α` is an abbreviation for
`contravariant_class α>0 α (λ x y, y * x) (≤)`,
expressing that multiplication by positive elements on the right is reverse monotone. -/
abbreviation mul_pos_mono_rev : Prop :=
contravariant_class {x : X // 0 < x} X (λ x y, y * x) (≤)

end abbreviations_mono

section has_mul_zero_lt
variables [has_mul α] [has_zero α] [has_lt α]

lemma mul_lt_mul_left' [pos_mul_strict_mono α] {a b c : α} (bc : b < c) (a0 : 0 < a) :
  a * b < a * c :=
@covariant_class.elim α>0 α (λ x y, x * y) (<) _ ⟨a, a0⟩ _ _ bc

lemma mul_lt_mul_right' [mul_pos_strict_mono α]
  {a b c : α} (bc : b < c) (a0 : 0 < a) :
  b * a < c * a :=
@covariant_class.elim α>0 α (λ x y, y * x) (<) _ ⟨a, a0⟩ _ _ bc

-- proven with `a0 : 0 ≤ a` as `lt_of_mul_lt_mul_left''`
lemma lt_of_mul_lt_mul_left' [pos_mul_reflect_lt α]
  {a b c : α} (bc : a * b < a * c) (a0 : 0 < a) :
  b < c :=
@contravariant_class.elim α>0 α (λ x y, x * y) (<) _ ⟨a, a0⟩ _ _ bc

-- proven with `a0 : 0 ≤ a` as `lt_of_mul_lt_mul_right''`
lemma lt_of_mul_lt_mul_right' [mul_pos_reflect_lt α]
  {a b c : α} (bc : b * a < c * a) (a0 : 0 < a) :
  b < c :=
@contravariant_class.elim α>0 α (λ x y, y * x) (<) _ ⟨a, a0⟩ _ _ bc

@[simp]
lemma mul_lt_mul_iff_left [pos_mul_strict_mono α] [pos_mul_reflect_lt α]
  {a b c : α} (a0 : 0 < a) :
  a * b < a * c ↔ b < c :=
@rel_iff_cov α>0 α (λ x y, x * y) (<) _ _ ⟨a, a0⟩ _ _

@[simp]
lemma mul_lt_mul_iff_right [mul_pos_strict_mono α] [mul_pos_reflect_lt α]
  {a b c : α} (a0 : 0 < a) :
  b * a < c * a ↔ b < c :=
@rel_iff_cov α>0 α (λ x y, y * x) (<) _ _ ⟨a, a0⟩ _ _

end has_mul_zero_lt

section has_mul_zero_le
variables [has_mul α] [has_zero α] [preorder α]

lemma mul_le_mul_left' [pos_mul_mono α] {a b c : α} (bc : b ≤ c) (a0 : 0 < a) :
  a * b ≤ a * c :=
@covariant_class.elim α>0 α (λ x y, x * y) (≤) _ ⟨a, a0⟩ _ _ bc

lemma mul_le_mul_right' [mul_pos_mono α]
  {a b c : α} (bc : b ≤ c) (a0 : 0 < a) :
  b * a ≤ c * a :=
@covariant_class.elim α>0 α (λ x y, y * x) (≤) _ ⟨a, a0⟩ _ _ bc

lemma le_of_mul_le_mul_left' [pos_mul_mono_rev α]
  {a b c : α} (bc : a * b ≤ a * c) (a0 : 0 < a) :
  b ≤ c :=
@contravariant_class.elim α>0 α (λ x y, x * y) (≤) _ ⟨a, a0⟩ _ _ bc

lemma le_of_mul_le_mul_right' [mul_pos_mono_rev α]
  {a b c : α} (bc : b * a ≤ c * a) (a0 : 0 < a) :
  b ≤ c :=
@contravariant_class.elim α>0 α (λ x y, y * x) (≤) _ ⟨a, a0⟩ _ _ bc

@[simp]
lemma mul_le_mul_iff_left [pos_mul_mono α] [pos_mul_mono_rev α]
  {a b c : α} (a0 : 0 < a) :
  a * b ≤ a * c ↔ b ≤ c :=
@rel_iff_cov α>0 α (λ x y, x * y) (≤) _ _ ⟨a, a0⟩ _ _

@[simp]
lemma mul_le_mul_iff_right [mul_pos_mono α] [mul_pos_mono_rev α]
  {a b c : α} (a0 : 0 < a) :
  b * a ≤ c * a ↔ b ≤ c :=
@rel_iff_cov α>0 α (λ x y, y * x) (≤) _ _ ⟨a, a0⟩ _ _

end has_mul_zero_le

section mul_zero_class_partial_order
variables [mul_zero_class α] [partial_order α]

lemma lt_of_mul_lt_mul_left'' [pos_mul_reflect_lt α]
  {a b c : α} (bc : a * b < a * c) (a0 : 0 ≤ a) :
  b < c :=
begin
  by_cases a₀ : a = 0,
  { exact (lt_irrefl (0 : α) (by simpa only [a₀, zero_mul] using bc)).elim },
  { exact lt_of_mul_lt_mul_left' bc ((ne.symm a₀).le_iff_lt.mp a0) }
end

lemma lt_of_mul_lt_mul_right'' [mul_pos_reflect_lt α]
  {a b c : α} (bc : b * a < c * a) (a0 : 0 ≤ a) :
  b < c :=
begin
  by_cases a₀ : a = 0,
  { exact (lt_irrefl (0 : α) (by simpa only [a₀, mul_zero] using bc)).elim },
  { exact lt_of_mul_lt_mul_right' bc ((ne.symm a₀).le_iff_lt.mp a0) }
end

end mul_zero_class_partial_order
end zero_lt


/-
Copyright (c) 2020 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/
import measure_theory.measure.measure_space
import measure_theory.measure.regular
import topology.sets.compacts

/-!
# Contents

In this file we work with *contents*. A content `λ` is a function from a certain class of subsets
(such as the compact subsets) to `ℝ≥0` that is
* additive: If `K₁` and `K₂` are disjoint sets in the domain of `λ`,
  then `λ(K₁ ∪ K₂) = λ(K₁) + λ(K₂)`;
* subadditive: If `K₁` and `K₂` are in the domain of `λ`, then `λ(K₁ ∪ K₂) ≤ λ(K₁) + λ(K₂)`;
* monotone: If `K₁ ⊆ K₂` are in the domain of `λ`, then `λ(K₁) ≤ λ(K₂)`.

We show that:
* Given a content `λ` on compact sets, let us define a function `λ*` on open sets, by letting
  `λ* U` be the supremum of `λ K` for `K` included in `U`. This is a countably subadditive map that
  vanishes at `∅`. In Halmos (1950) this is called the *inner content* `λ*` of `λ`, and formalized
  as `inner_content`.
* Given an inner content, we define an outer measure `μ*`, by letting `μ* E` be the infimum of
  `λ* U` over the open sets `U` containing `E`. This is indeed an outer measure. It is formalized
  as `outer_measure`.
* Restricting this outer measure to Borel sets gives a regular measure `μ`.

We define bundled contents as `content`.
In this file we only work on contents on compact sets, and inner contents on open sets, and both
contents and inner contents map into the extended nonnegative reals. However, in other applications
other choices can be made, and it is not a priori clear what the best interface should be.

## Main definitions

For `μ : content G`, we define
* `μ.inner_content` : the inner content associated to `μ`.
* `μ.outer_measure` : the outer measure associated to `μ`.
* `μ.measure`       : the Borel measure associated to `μ`.

We prove that, on a locally compact space, the measure `μ.measure` is regular.

## References

* Paul Halmos (1950), Measure Theory, §53
* <https://en.wikipedia.org/wiki/Content_(measure_theory)>
-/

universes u v w
noncomputable theory

open set topological_space
open_locale nnreal ennreal measure_theory

namespace measure_theory

variables {G : Type w} [topological_space G]

/-- A content is an additive function on compact sets taking values in `ℝ≥0`. It is a device
from which one can define a measure. -/
structure content (G : Type w) [topological_space G] :=
(to_fun : compacts G → ℝ≥0)
(mono' : ∀ (K₁ K₂ : compacts G), (K₁ : set G) ⊆ K₂ → to_fun K₁ ≤ to_fun K₂)
(sup_disjoint' : ∀ (K₁ K₂ : compacts G), disjoint (K₁ : set G) K₂ →
   to_fun (K₁ ⊔ K₂) = to_fun K₁ + to_fun K₂)
(sup_le' : ∀ (K₁ K₂ : compacts G), to_fun (K₁ ⊔ K₂) ≤ to_fun K₁ + to_fun K₂)

instance : inhabited (content G) :=
⟨{ to_fun := λ K, 0,
  mono' := by simp,
  sup_disjoint' := by simp,
  sup_le' := by simp }⟩

/-- Although the `to_fun` field of a content takes values in `ℝ≥0`, we register a coercion to
functions taking values in `ℝ≥0∞` as most constructions below rely on taking suprs and infs, which
is more convenient in a complete lattice, and aim at constructing a measure. -/
instance : has_coe_to_fun (content G) (λ _, compacts G → ℝ≥0∞) := ⟨λ μ s, μ.to_fun s⟩

namespace content

variable (μ : content G)

lemma apply_eq_coe_to_fun (K : compacts G) : μ K = μ.to_fun K := rfl

lemma mono (K₁ K₂ : compacts G) (h : (K₁ : set G) ⊆ K₂) : μ K₁ ≤ μ K₂ :=
by simp [apply_eq_coe_to_fun, μ.mono' _ _ h]

lemma sup_disjoint (K₁ K₂ : compacts G) (h : disjoint (K₁ : set G) K₂) :
  μ (K₁ ⊔ K₂) = μ K₁ + μ K₂ :=
by simp [apply_eq_coe_to_fun, μ.sup_disjoint' _ _ h]

lemma sup_le (K₁ K₂ : compacts G) : μ (K₁ ⊔ K₂) ≤ μ K₁ + μ K₂ :=
by { simp only [apply_eq_coe_to_fun], norm_cast, exact μ.sup_le' _ _ }

lemma lt_top (K : compacts G) : μ K < ∞ :=
ennreal.coe_lt_top

lemma empty : μ ⊥ = 0 :=
begin
  have := μ.sup_disjoint' ⊥ ⊥,
  simpa [apply_eq_coe_to_fun] using this,
end

/-- Constructing the inner content of a content. From a content defined on the compact sets, we
  obtain a function defined on all open sets, by taking the supremum of the content of all compact
  subsets. -/
def inner_content (U : opens G) : ℝ≥0∞ := ⨆ (K : compacts G) (h : (K : set G) ⊆ U), μ K

lemma le_inner_content (K : compacts G) (U : opens G) (h2 : (K : set G) ⊆ U) :
  μ K ≤ μ.inner_content U :=
le_supr_of_le K $ le_supr _ h2

lemma inner_content_le (U : opens G) (K : compacts G) (h2 : (U : set G) ⊆ K) :
  μ.inner_content U ≤ μ K :=
supr₂_le $ λ K' hK', μ.mono _ _ (subset.trans hK' h2)

lemma inner_content_of_is_compact {K : set G} (h1K : is_compact K) (h2K : is_open K) :
  μ.inner_content ⟨K, h2K⟩ = μ ⟨K, h1K⟩ :=
le_antisymm (supr₂_le $ λ K' hK', μ.mono _ ⟨K, h1K⟩ hK')
            (μ.le_inner_content _ _ subset.rfl)

lemma inner_content_bot :
  μ.inner_content ⊥ = 0 :=
begin
  refine le_antisymm _ (zero_le _), rw ←μ.empty,
  refine supr₂_le (λ K hK, _),
  have : K = ⊥, { ext1, rw [subset_empty_iff.mp hK, compacts.coe_bot] }, rw this, refl'
end

/-- This is "unbundled", because that it required for the API of `induced_outer_measure`. -/
lemma inner_content_mono ⦃U V : set G⦄ (hU : is_open U) (hV : is_open V)
  (h2 : U ⊆ V) : μ.inner_content ⟨U, hU⟩ ≤ μ.inner_content ⟨V, hV⟩ :=
bsupr_mono $ λ K hK, hK.trans h2

lemma inner_content_exists_compact {U : opens G}
  (hU : μ.inner_content U ≠ ∞) {ε : ℝ≥0} (hε : ε ≠ 0) :
  ∃ K : compacts G, (K : set G) ⊆ U ∧ μ.inner_content U ≤ μ K + ε :=
begin
  have h'ε := ennreal.coe_ne_zero.2 hε,
  cases le_or_lt (μ.inner_content U) ε,
  { exact ⟨⊥, empty_subset _, le_add_left h⟩ },
  have := ennreal.sub_lt_self hU h.ne_bot h'ε,
  conv at this {to_rhs, rw inner_content }, simp only [lt_supr_iff] at this,
  rcases this with ⟨U, h1U, h2U⟩, refine ⟨U, h1U, _⟩,
  rw [← tsub_le_iff_right], exact le_of_lt h2U
end

/-- The inner content of a supremum of opens is at most the sum of the individual inner
contents. -/
lemma inner_content_Sup_nat [t2_space G] (U : ℕ → opens G) :
  μ.inner_content (⨆ (i : ℕ), U i) ≤ ∑' (i : ℕ), μ.inner_content (U i) :=
begin
  have h3 : ∀ (t : finset ℕ) (K : ℕ → compacts G), μ (t.sup K) ≤ t.sum (λ i, μ (K i)),
  { intros t K, refine finset.induction_on t _ _,
    { simp only [μ.empty, nonpos_iff_eq_zero, finset.sum_empty, finset.sup_empty], },
    { intros n s hn ih, rw [finset.sup_insert, finset.sum_insert hn],
      exact le_trans (μ.sup_le _ _) (add_le_add_left ih _) }},
  refine supr₂_le (λ K hK, _),
  obtain ⟨t, ht⟩ := K.is_compact.elim_finite_subcover  _ (λ i, (U i).is_open) _, swap,
  { rwa [← opens.coe_supr] },
  rcases K.is_compact.finite_compact_cover t (coe ∘ U) (λ i _, (U _).is_open) (by simp only [ht])
    with ⟨K', h1K', h2K', h3K'⟩,
  let L : ℕ → compacts G := λ n, ⟨K' n, h1K' n⟩,
  convert le_trans (h3 t L) _,
  { ext1, rw [compacts.coe_finset_sup, finset.sup_eq_supr], exact h3K' },
  refine le_trans (finset.sum_le_sum _) (ennreal.sum_le_tsum t),
  intros i hi, refine le_trans _ (le_supr _ (L i)),
  refine le_trans _ (le_supr _ (h2K' i)), refl'
end

/-- The inner content of a union of sets is at most the sum of the individual inner contents.
  This is the "unbundled" version of `inner_content_Sup_nat`.
  It required for the API of `induced_outer_measure`. -/
lemma inner_content_Union_nat [t2_space G] ⦃U : ℕ → set G⦄ (hU : ∀ (i : ℕ), is_open (U i)) :
  μ.inner_content ⟨⋃ (i : ℕ), U i, is_open_Union hU⟩ ≤ ∑' (i : ℕ), μ.inner_content ⟨U i, hU i⟩ :=
by { have := μ.inner_content_Sup_nat (λ i, ⟨U i, hU i⟩), rwa [opens.supr_def] at this }

lemma inner_content_comap (f : G ≃ₜ G)
  (h : ∀ ⦃K : compacts G⦄, μ (K.map f f.continuous) = μ K) (U : opens G) :
  μ.inner_content (opens.comap f.to_continuous_map U) = μ.inner_content U :=
begin
  refine (compacts.equiv f).surjective.supr_congr _ (λ K, supr_congr_Prop image_subset_iff _),
  intro hK, simp only [equiv.coe_fn_mk, subtype.mk_eq_mk, ennreal.coe_eq_coe, compacts.equiv],
  apply h,
end

@[to_additive]
lemma is_mul_left_invariant_inner_content [group G] [topological_group G]
  (h : ∀ (g : G) {K : compacts G}, μ (K.map _ $ continuous_mul_left g) = μ K) (g : G)
  (U : opens G) :
  μ.inner_content (opens.comap (homeomorph.mul_left g).to_continuous_map U) = μ.inner_content U :=
by convert μ.inner_content_comap (homeomorph.mul_left g) (λ K, h g) U

@[to_additive]
lemma inner_content_pos_of_is_mul_left_invariant [t2_space G] [group G] [topological_group G]
  (h3 : ∀ (g : G) {K : compacts G}, μ (K.map _ $ continuous_mul_left g) = μ K)
  (K : compacts G) (hK : μ K ≠ 0) (U : opens G) (hU : (U : set G).nonempty) :
  0 < μ.inner_content U :=
begin
  have : (interior (U : set G)).nonempty, rwa [U.is_open.interior_eq],
  rcases compact_covered_by_mul_left_translates K.2 this with ⟨s, hs⟩,
  suffices : μ K ≤ s.card * μ.inner_content U,
  { exact (ennreal.mul_pos_iff.mp $ hK.bot_lt.trans_le this).2 },
  have : (K : set G) ⊆ ↑⨆ (g ∈ s), opens.comap (homeomorph.mul_left g).to_continuous_map U,
  { simpa only [opens.supr_def, opens.coe_comap, subtype.coe_mk] },
  refine (μ.le_inner_content _ _ this).trans _,
  refine (rel_supr_sum (μ.inner_content) (μ.inner_content_bot) (≤)
    (μ.inner_content_Sup_nat) _ _).trans _,
  simp only [μ.is_mul_left_invariant_inner_content h3, finset.sum_const, nsmul_eq_mul, le_refl]
end

lemma inner_content_mono' ⦃U V : set G⦄ (hU : is_open U) (hV : is_open V) (h2 : U ⊆ V) :
  μ.inner_content ⟨U, hU⟩ ≤ μ.inner_content ⟨V, hV⟩ :=
bsupr_mono $ λ K hK, hK.trans h2

section outer_measure

/-- Extending a content on compact sets to an outer measure on all sets. -/
protected def outer_measure : outer_measure G :=
induced_outer_measure (λ U hU, μ.inner_content ⟨U, hU⟩) is_open_empty μ.inner_content_bot

variables [t2_space G]

lemma outer_measure_opens (U : opens G) : μ.outer_measure U = μ.inner_content U :=
induced_outer_measure_eq' (λ _, is_open_Union) μ.inner_content_Union_nat μ.inner_content_mono U.2

lemma outer_measure_of_is_open (U : set G) (hU : is_open U) :
  μ.outer_measure U = μ.inner_content ⟨U, hU⟩ :=
μ.outer_measure_opens ⟨U, hU⟩

lemma outer_measure_le
  (U : opens G) (K : compacts G) (hUK : (U : set G) ⊆ K) : μ.outer_measure U ≤ μ K :=
(μ.outer_measure_opens U).le.trans $ μ.inner_content_le U K hUK

lemma le_outer_measure_compacts (K : compacts G) : μ K ≤ μ.outer_measure K :=
begin
  rw [content.outer_measure, induced_outer_measure_eq_infi],
  { exact le_infi (λ U, le_infi $ λ hU, le_infi $ μ.le_inner_content K ⟨U, hU⟩) },
  { exact μ.inner_content_Union_nat },
  { exact μ.inner_content_mono }
end

lemma outer_measure_eq_infi (A : set G) :
  μ.outer_measure A = ⨅ (U : set G) (hU : is_open U) (h : A ⊆ U), μ.inner_content ⟨U, hU⟩ :=
induced_outer_measure_eq_infi _ μ.inner_content_Union_nat μ.inner_content_mono A

lemma outer_measure_interior_compacts (K : compacts G) : μ.outer_measure (interior K) ≤ μ K :=
(μ.outer_measure_opens $ opens.interior K).le.trans $ μ.inner_content_le _ _ interior_subset

lemma outer_measure_exists_compact {U : opens G} (hU : μ.outer_measure U ≠ ∞) {ε : ℝ≥0}
  (hε : ε ≠ 0) : ∃ K : compacts G, (K : set G) ⊆ U ∧ μ.outer_measure U ≤ μ.outer_measure K + ε :=
begin
  rw [μ.outer_measure_opens] at hU ⊢,
  rcases μ.inner_content_exists_compact hU hε with ⟨K, h1K, h2K⟩,
  exact ⟨K, h1K, le_trans h2K $ add_le_add_right (μ.le_outer_measure_compacts K) _⟩,
end

lemma outer_measure_exists_open {A : set G} (hA : μ.outer_measure A ≠ ∞) {ε : ℝ≥0} (hε : ε ≠ 0) :
  ∃ U : opens G, A ⊆ U ∧ μ.outer_measure U ≤ μ.outer_measure A + ε :=
begin
  rcases induced_outer_measure_exists_set _ _ μ.inner_content_mono hA (ennreal.coe_ne_zero.2 hε)
    with ⟨U, hU, h2U, h3U⟩,
  exact ⟨⟨U, hU⟩, h2U, h3U⟩, swap, exact μ.inner_content_Union_nat
end

lemma outer_measure_preimage (f : G ≃ₜ G) (h : ∀ ⦃K : compacts G⦄, μ (K.map f f.continuous) = μ K)
  (A : set G) : μ.outer_measure (f ⁻¹' A) = μ.outer_measure A :=
begin
  refine induced_outer_measure_preimage _ μ.inner_content_Union_nat μ.inner_content_mono _
    (λ s, f.is_open_preimage) _,
  intros s hs, convert μ.inner_content_comap f h ⟨s, hs⟩
end

lemma outer_measure_lt_top_of_is_compact [locally_compact_space G]
  {K : set G} (hK : is_compact K) : μ.outer_measure K < ∞ :=
begin
  rcases exists_compact_superset hK with ⟨F, h1F, h2F⟩,
  calc
  μ.outer_measure K ≤ μ.outer_measure (interior F) : outer_measure.mono' _ h2F
  ... ≤ μ ⟨F, h1F⟩ :
    by apply μ.outer_measure_le ⟨interior F, is_open_interior⟩ ⟨F, h1F⟩ interior_subset
  ... < ⊤ : μ.lt_top _
end

@[to_additive]
lemma is_mul_left_invariant_outer_measure [group G] [topological_group G]
  (h : ∀ (g : G) {K : compacts G}, μ (K.map _ $ continuous_mul_left g) = μ K) (g : G)
  (A : set G) : μ.outer_measure ((λ h, g * h) ⁻¹' A) = μ.outer_measure A :=
by convert μ.outer_measure_preimage (homeomorph.mul_left g) (λ K, h g) A

lemma outer_measure_caratheodory (A : set G) :
  measurable_set[μ.outer_measure.caratheodory] A ↔ ∀ (U : opens G),
  μ.outer_measure (U ∩ A) + μ.outer_measure (U \ A) ≤ μ.outer_measure U :=
begin
  rw [opens.forall],
  apply induced_outer_measure_caratheodory,
  apply inner_content_Union_nat,
  apply inner_content_mono'
end

@[to_additive]
lemma outer_measure_pos_of_is_mul_left_invariant [group G] [topological_group G]
  (h3 : ∀ (g : G) {K : compacts G}, μ (K.map _ $ continuous_mul_left g) = μ K)
  (K : compacts G) (hK : μ K ≠ 0) {U : set G} (h1U : is_open U) (h2U : U.nonempty) :
  0 < μ.outer_measure U :=
by { convert μ.inner_content_pos_of_is_mul_left_invariant h3 K hK ⟨U, h1U⟩ h2U,
     exact μ.outer_measure_opens ⟨U, h1U⟩ }

variables [S : measurable_space G] [borel_space G]
include S

/-- For the outer measure coming from a content, all Borel sets are measurable. -/
lemma borel_le_caratheodory : S ≤ μ.outer_measure.caratheodory :=
begin
  rw [@borel_space.measurable_eq G _ _],
  refine measurable_space.generate_from_le _,
  intros U hU,
  rw μ.outer_measure_caratheodory,
  intro U',
  rw μ.outer_measure_of_is_open ((U' : set G) ∩ U) (U'.is_open.inter hU),
  simp only [inner_content, supr_subtype'], rw [opens.coe_mk],
  haveI : nonempty {L : compacts G // (L : set G) ⊆ U' ∩ U} := ⟨⟨⊥, empty_subset _⟩⟩,
  rw [ennreal.supr_add],
  refine supr_le _, rintro ⟨L, hL⟩, simp only [subset_inter_iff] at hL,
  have : ↑U' \ U ⊆ U' \ L := diff_subset_diff_right hL.2,
  refine le_trans (add_le_add_left (μ.outer_measure.mono' this) _) _,
  rw μ.outer_measure_of_is_open (↑U' \ L) (is_open.sdiff U'.2 L.2.is_closed),
  simp only [inner_content, supr_subtype'], rw [opens.coe_mk],
  haveI : nonempty {M : compacts G // (M : set G) ⊆ ↑U' \ L} := ⟨⟨⊥, empty_subset _⟩⟩,
  rw [ennreal.add_supr], refine supr_le _, rintro ⟨M, hM⟩, simp only [subset_diff] at hM,
  have : (↑(L ⊔ M) : set G) ⊆ U',
  { simp only [union_subset_iff, compacts.coe_sup, hM, hL, and_self] },
  rw μ.outer_measure_of_is_open ↑U' U'.2,
  refine le_trans (ge_of_eq _) (μ.le_inner_content _ _ this),
  exact μ.sup_disjoint _ _ hM.2.symm,
end

/-- The measure induced by the outer measure coming from a content, on the Borel sigma-algebra. -/
protected def measure : measure G := μ.outer_measure.to_measure μ.borel_le_caratheodory

lemma measure_apply {s : set G} (hs : measurable_set s) : μ.measure s = μ.outer_measure s :=
to_measure_apply _ _ hs

/-- In a locally compact space, any measure constructed from a content is regular. -/
instance regular [locally_compact_space G] : μ.measure.regular :=
begin
  haveI : μ.measure.outer_regular,
  { refine ⟨λ A hA r (hr : _ < _), _⟩,
    rw [μ.measure_apply hA, outer_measure_eq_infi] at hr,
    simp only [infi_lt_iff] at hr,
    rcases hr with ⟨U, hUo, hAU, hr⟩,
    rw [← μ.outer_measure_of_is_open U hUo, ← μ.measure_apply hUo.measurable_set] at hr,
    exact ⟨U, hAU, hUo, hr⟩ },
  haveI : is_finite_measure_on_compacts μ.measure,
  { refine ⟨λ K hK, _⟩,
    rw [measure_apply _ hK.measurable_set],
    exact μ.outer_measure_lt_top_of_is_compact hK },
  refine ⟨λ U hU r hr, _⟩,
  rw [measure_apply _ hU.measurable_set, μ.outer_measure_of_is_open U hU] at hr,
  simp only [inner_content, lt_supr_iff] at hr,
  rcases hr with ⟨K, hKU, hr⟩,
  refine ⟨K, hKU, K.2, hr.trans_le _⟩,
  exact (μ.le_outer_measure_compacts K).trans (le_to_measure_apply _ _ _)
end

end outer_measure

section regular_contents

/-- A content `μ` is called regular if for every compact set `K`,
  `μ(K) = inf {μ(K') : K ⊂ int K' ⊂ K'`. See Paul Halmos (1950), Measure Theory, §54-/
def content_regular := ∀ ⦃K : topological_space.compacts G⦄,
  μ K = ⨅ (K' : topological_space.compacts G) (hK: (K : set G) ⊆ interior (K' : set G) ), μ K'

lemma content_regular_exists_compact (H : content_regular μ) (K : topological_space.compacts G)
  {ε : nnreal} (hε : ε ≠ 0) :
  ∃ (K' : topological_space.compacts G), (K.carrier ⊆ interior K'.carrier) ∧ μ K' ≤ μ K + ε :=
begin
  by_contra hc,
  simp only [not_exists, not_and, not_le] at hc,
  have lower_bound_infi : μ K + ε ≤ ⨅ (K' : topological_space.compacts G)
    (h: (K : set G) ⊆ interior (K' : set G) ), μ K' :=
    le_infi (λ K', le_infi ( λ K'_hyp, le_of_lt (hc K' K'_hyp))),
  rw ← H at lower_bound_infi,
  exact (lt_self_iff_false (μ K)).mp (lt_of_le_of_lt' lower_bound_infi
    (ennreal.lt_add_right (ne_top_of_lt (μ.lt_top K)) (ennreal.coe_ne_zero.mpr hε))),
end

variables [measurable_space G] [t2_space G] [borel_space G]

/--If `μ` is a regular content, then the measure induced by `μ` will agree with `μ`
  on compact sets.-/
lemma measure_eq_content_of_regular
 (H : measure_theory.content.content_regular μ) (K : topological_space.compacts G) :
  μ.measure ↑K = μ K :=
begin
  refine le_antisymm _ _,
  { apply ennreal.le_of_forall_pos_le_add,
    intros ε εpos content_K_finite,
    obtain ⟨ K', K'_hyp ⟩ := content_regular_exists_compact μ H K (ne_bot_of_gt εpos),
    calc μ.measure ↑K ≤ μ.measure (interior ↑K') : _
                  ... ≤ μ K' : _
                  ... ≤ μ K + ε : K'_hyp.right,

    { rw [μ.measure_apply ((is_open_interior).measurable_set),
        μ.measure_apply K.is_compact.measurable_set],
      exact μ.outer_measure.mono K'_hyp.left },
    { rw μ.measure_apply (is_open.measurable_set is_open_interior),
      exact μ.outer_measure_interior_compacts K' } },
  { rw (μ.measure_apply (is_compact.measurable_set K.is_compact)),
    exact μ.le_outer_measure_compacts K },
end

end regular_contents

end content

end measure_theory


/-
Copyright (c) 2022 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import topology.order.basic
import topology.filter

/-!
# Topology on filters of a space with order topology

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

In this file we prove that `𝓝 (f x)` tends to `𝓝 filter.at_top` provided that `f` tends to
`filter.at_top`, and similarly for `filter.at_bot`.
-/

open_locale topology

namespace filter

variables {α X : Type*} [topological_space X] [partial_order X] [order_topology X]

protected lemma tendsto_nhds_at_top [no_max_order X] : tendsto 𝓝 (at_top : filter X) (𝓝 at_top) :=
filter.tendsto_nhds_at_top_iff.2 $ λ x, (eventually_gt_at_top x).mono $ λ y, le_mem_nhds

protected lemma tendsto_nhds_at_bot [no_min_order X] : tendsto 𝓝 (at_bot : filter X) (𝓝 at_bot) :=
@filter.tendsto_nhds_at_top Xᵒᵈ _ _ _ _

lemma tendsto.nhds_at_top [no_max_order X] {f : α → X} {l : filter α} (h : tendsto f l at_top) :
  tendsto (𝓝 ∘ f) l (𝓝 at_top) :=
filter.tendsto_nhds_at_top.comp h

lemma tendsto.nhds_at_bot [no_min_order X] {f : α → X} {l : filter α} (h : tendsto f l at_bot) :
  tendsto (𝓝 ∘ f) l (𝓝 at_bot) :=
@tendsto.nhds_at_top α Xᵒᵈ _ _ _ _ _ _ h

end filter


/-
Copyright (c) 2019 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Jeremy Avigad

We introduce these relations:

  `is_O f g l` : "f is big O of g along l"
  `is_o f g l` : "f is little o of g along l"

Here `l` is any filter on the domain of `f` and `g`, which are assumed to be the same. The codomains
of `f` and `g` do not need to be the same; all that is needed that there is a norm associated with
these types, and it is the norm that is compared asymptotically.

Often the ranges of `f` and `g` will be the real numbers, in which case the norm is the absolute
value. In general, we have

  `is_O f g l ↔ is_O (λ x, ∥f x∥) (λ x, ∥g x∥) l`,

and similarly for `is_o`. But our setup allows us to use the notions e.g. with functions
to the integers, rationals, complex numbers, or any normed vector space without mentioning the
norm explicitly.

If `f` and `g` are functions to a normed field like the reals or complex numbers and `g` is always
nonzero, we have

  `is_o f g l ↔ tendsto (λ x, f x / (g x)) (nhds 0) l`.

In fact, the right-to-left direction holds without the hypothesis on `g`, and in the other direction
it suffices to assume that `f` is zero wherever `g` is. (This generalization is useful in defining
the Fréchet derivative.)
-/
import analysis.normed_space.basic
open filter

namespace asymptotics

variables {α : Type*} {β : Type*} {γ : Type*} {δ : Type*}

section
variables [has_norm β] [has_norm γ] [has_norm δ]

def is_O (f : α → β) (g : α → γ) (l : filter α) : Prop :=
∃ c > 0, { x | ∥ f x ∥ ≤ c * ∥ g x ∥ } ∈ l

def is_o (f : α → β) (g : α → γ) (l : filter α) : Prop :=
∀ c > 0, { x | ∥ f x ∥ ≤  c * ∥ g x ∥ } ∈ l

theorem is_O_refl (f : α → β) (l : filter α) : is_O f f l :=
⟨1, zero_lt_one, by { filter_upwards [univ_mem_sets], intros x _, simp }⟩

theorem is_O.comp {f : α → β} {g : α → γ} {l : filter α} (hfg : is_O f g l)
    {δ : Type*} (k : δ → α) :
  is_O (f ∘ k) (g ∘ k) (l.comap k) :=
let ⟨c, cpos, hfgc⟩ := hfg in
⟨c, cpos, mem_comap_sets.mpr ⟨_, hfgc, set.subset.refl _⟩⟩

theorem is_o.comp {f : α → β} {g : α → γ} {l : filter α} (hfg : is_o f g l)
    {δ : Type*} (k : δ → α) :
  is_o (f ∘ k) (g ∘ k) (l.comap k) :=
λ c cpos, mem_comap_sets.mpr ⟨_, hfg c cpos, set.subset.refl _⟩

theorem is_O.mono {f : α → β} {g : α → γ} {l₁ l₂ : filter α} (h : l₁ ≤ l₂)
  (h' : is_O f g l₂) : is_O f g l₁ :=
let ⟨c, cpos, h'c⟩ := h' in ⟨c, cpos, h (h'c)⟩

theorem is_o.mono {f : α → β} {g : α → γ} {l₁ l₂ : filter α} (h : l₁ ≤ l₂)
  (h' : is_o f g l₂) : is_o f g l₁ :=
λ c cpos, h (h' c cpos)

theorem is_o.to_is_O {f : α → β} {g : α → γ} {l : filter α} (hgf : is_o f g l) : is_O f g l :=
⟨1, zero_lt_one, hgf 1 zero_lt_one⟩

theorem is_O.trans {f : α → β} {g : α → γ} {k : α → δ} {l : filter α}
    (h₁ : is_O f g l) (h₂ : is_O g k l) :
  is_O f k l :=
let ⟨c,  cpos,  hc⟩  := h₁,
    ⟨c', c'pos, hc'⟩ := h₂ in
begin
  use [c * c', mul_pos cpos c'pos],
  filter_upwards [hc, hc'], dsimp,
  intros x h₁x h₂x, rw mul_assoc,
  exact le_trans h₁x (mul_le_mul_of_nonneg_left h₂x (le_of_lt cpos))
end

theorem is_o.trans_is_O {f : α → β} {g : α → γ} {k : α → δ} {l : filter α}
    (h₁ : is_o f g l) (h₂ : is_O g k l) :
  is_o f k l :=
begin
  intros c cpos,
  rcases h₂ with ⟨c', c'pos, hc'⟩,
  have cc'pos := div_pos cpos c'pos,
  filter_upwards [h₁ (c / c') cc'pos, hc'], dsimp,
  intros x h₁x h₂x,
  refine le_trans h₁x (le_trans (mul_le_mul_of_nonneg_left h₂x (le_of_lt cc'pos)) _),
  rw [←mul_assoc, div_mul_cancel _ (ne_of_gt c'pos)]
end

theorem is_O.trans_is_o {f : α → β} {g : α → γ} {k : α → δ} {l : filter α}
    (h₁ : is_O f g l) (h₂ : is_o g k l) :
  is_o f k l :=
begin
  intros c cpos,
  rcases h₁ with ⟨c', c'pos, hc'⟩,
  have cc'pos := div_pos cpos c'pos,
  filter_upwards [hc', h₂ (c / c') cc'pos], dsimp,
  intros x h₁x h₂x,
  refine le_trans h₁x (le_trans (mul_le_mul_of_nonneg_left h₂x (le_of_lt c'pos)) _),
  rw [←mul_assoc, mul_div_cancel' _ (ne_of_gt c'pos)]
end

theorem is_o.trans {f : α → β} {g : α → γ} {k : α → δ} {l : filter α}
    (h₁ : is_o f g l) (h₂ : is_o g k l) :
  is_o f k l :=
h₁.to_is_O.trans_is_o h₂

theorem is_o_join {f : α → β} {g : α → γ} {l₁ l₂ : filter α}
    (h₁ : is_o f g l₁) (h₂ : is_o f g l₂) :
  is_o f g (l₁ ⊔ l₂) :=
begin
  intros c cpos,
  rw mem_sup_sets,
  exact ⟨h₁ c cpos, h₂ c cpos⟩
end

theorem is_O_congr {f₁ f₂ : α → β} {g₁ g₂ : α → γ} {l : filter α}
    (hf : {x | f₁ x = f₂ x} ∈ l) (hg : {x | g₁ x = g₂ x} ∈ l) :
  is_O f₁ g₁ l ↔ is_O f₂ g₂ l :=
bex_congr $ λ c _, filter.congr_sets $
by filter_upwards [hf, hg] λ x e₁ e₂,
  by dsimp at e₁ e₂ ⊢; rw [e₁, e₂]

theorem is_o_congr {f₁ f₂ : α → β} {g₁ g₂ : α → γ} {l : filter α}
    (hf : {x | f₁ x = f₂ x} ∈ l) (hg : {x | g₁ x = g₂ x} ∈ l) :
  is_o f₁ g₁ l ↔ is_o f₂ g₂ l :=
ball_congr $ λ c _, filter.congr_sets $
by filter_upwards [hf, hg] λ x e₁ e₂,
  by dsimp at e₁ e₂ ⊢; rw [e₁, e₂]

theorem is_O.congr {f₁ f₂ : α → β} {g₁ g₂ : α → γ} {l : filter α}
    (hf : ∀ x, f₁ x = f₂ x) (hg : ∀ x, g₁ x = g₂ x) :
  is_O f₁ g₁ l → is_O f₂ g₂ l :=
(is_O_congr (univ_mem_sets' hf) (univ_mem_sets' hg)).1

theorem is_o.congr {f₁ f₂ : α → β} {g₁ g₂ : α → γ} {l : filter α}
    (hf : ∀ x, f₁ x = f₂ x) (hg : ∀ x, g₁ x = g₂ x) :
  is_o f₁ g₁ l → is_o f₂ g₂ l :=
(is_o_congr (univ_mem_sets' hf) (univ_mem_sets' hg)).1

theorem is_O_congr_left {f₁ f₂ : α → β} {g : α → γ} {l : filter α}
    (h : {x | f₁ x = f₂ x} ∈ l) :
  is_O f₁ g l ↔ is_O f₂ g l :=
is_O_congr h (univ_mem_sets' $ λ _, rfl)

theorem is_o_congr_left {f₁ f₂ : α → β} {g : α → γ} {l : filter α}
    (h : {x | f₁ x = f₂ x} ∈ l) :
  is_o f₁ g l ↔ is_o f₂ g l :=
is_o_congr h (univ_mem_sets' $ λ _, rfl)

theorem is_O.congr_left {f₁ f₂ : α → β} {g : α → γ} {l : filter α}
  (hf : ∀ x, f₁ x = f₂ x) : is_O f₁ g l → is_O f₂ g l :=
is_O.congr hf (λ _, rfl)

theorem is_o.congr_left {f₁ f₂ : α → β} {g : α → γ} {l : filter α}
  (hf : ∀ x, f₁ x = f₂ x) : is_o f₁ g l → is_o f₂ g l :=
is_o.congr hf (λ _, rfl)

theorem is_O_congr_right {f : α → β} {g₁ g₂ : α → γ} {l : filter α}
    (h : {x | g₁ x = g₂ x} ∈ l) :
  is_O f g₁ l ↔ is_O f g₂ l :=
is_O_congr (univ_mem_sets' $ λ _, rfl) h

theorem is_o_congr_right {f : α → β} {g₁ g₂ : α → γ} {l : filter α}
    (h : {x | g₁ x = g₂ x} ∈ l) :
  is_o f g₁ l ↔ is_o f g₂ l :=
is_o_congr (univ_mem_sets' $ λ _, rfl) h

theorem is_O.congr_right {f : α → β} {g₁ g₂ : α → γ} {l : filter α}
  (hg : ∀ x, g₁ x = g₂ x) : is_O f g₁ l → is_O f g₂ l :=
is_O.congr (λ _, rfl) hg

theorem is_o.congr_right {f : α → β} {g₁ g₂ : α → γ} {l : filter α}
  (hg : ∀ x, g₁ x = g₂ x) : is_o f g₁ l → is_o f g₂ l :=
is_o.congr (λ _, rfl) hg

end

section
variables [has_norm β] [normed_group γ]

@[simp]
theorem is_O_norm_right {f : α → β} {g : α → γ} {l : filter α} :
  is_O f (λ x, ∥g x∥) l ↔ is_O f g l :=
by simp only [is_O, norm_norm]

@[simp]
theorem is_o_norm_right {f : α → β} {g : α → γ} {l : filter α} :
  is_o f (λ x, ∥g x∥) l ↔ is_o f g l :=
by simp only [is_o, norm_norm]

@[simp]
theorem is_O_neg_right {f : α → β} {g : α → γ} {l : filter α} :
  is_O f (λ x, -(g x)) l ↔ is_O f g l :=
by { rw ←is_O_norm_right, simp only [norm_neg], rw is_O_norm_right }

@[simp]
theorem is_o_neg_right {f : α → β} {g : α → γ} {l : filter α} :
  is_o f (λ x, -(g x)) l ↔ is_o f g l :=
by { rw ←is_o_norm_right, simp only [norm_neg], rw is_o_norm_right }

theorem is_O_iff {f : α → β} {g : α → γ} {l : filter α} :
  is_O f g l ↔ ∃ c, { x | ∥f x∥ ≤ c * ∥g x∥ } ∈ l :=
suffices (∃ c, { x | ∥f x∥ ≤ c * ∥g x∥ } ∈ l) → is_O f g l,
  from ⟨λ ⟨c, cpos, hc⟩, ⟨c, hc⟩, this⟩,
assume ⟨c, hc⟩,
or.elim (lt_or_ge 0 c)
  (assume : c > 0, ⟨c, this, hc⟩)
  (assume h'c : c ≤ 0,
    have {x : α | ∥f x∥ ≤ 1 * ∥g x∥} ∈ l,
      begin
        filter_upwards [hc], intros x,
        show ∥f x∥ ≤ c * ∥g x∥ → ∥f x∥ ≤ 1 * ∥g x∥,
        { intro hx, apply le_trans hx,
          apply mul_le_mul_of_nonneg_right _ (norm_nonneg _),
          show c ≤ 1, from le_trans h'c zero_le_one }
      end,
    ⟨1, zero_lt_one, this⟩)

theorem is_O_join {f : α → β} {g : α → γ} {l₁ l₂ : filter α}
    (h₁ : is_O f g l₁) (h₂ : is_O f g l₂) :
  is_O f g (l₁ ⊔ l₂) :=
begin
  rcases h₁ with ⟨c₁, c₁pos, hc₁⟩,
  rcases h₂ with ⟨c₂, c₂pos, hc₂⟩,
  have : 0 < max c₁ c₂, by { rw lt_max_iff, left, exact c₁pos },
  use [max c₁ c₂, this],
  rw mem_sup_sets, split,
  { filter_upwards [hc₁], dsimp, intros x hx,
    exact le_trans hx (mul_le_mul_of_nonneg_right (le_max_left _ _) (norm_nonneg _)) },
  filter_upwards [hc₂], dsimp, intros x hx,
    exact le_trans hx (mul_le_mul_of_nonneg_right (le_max_right _ _) (norm_nonneg _))
end

end

section
variables [normed_group β] [has_norm γ]

@[simp] theorem is_O_norm_left {f : α → β} {g : α → γ} {l : filter α} :
  is_O (λ x, ∥f x∥) g l ↔ is_O f g l :=
by simp only [is_O, norm_norm]

@[simp] theorem is_o_norm_left {f : α → β} {g : α → γ} {l : filter α} :
  is_o (λ x, ∥f x∥) g l ↔ is_o f g l :=
by simp only [is_o, norm_norm]

@[simp] theorem is_O_neg_left {f : α → β} {g : α → γ} {l : filter α} :
  is_O (λ x, -f x) g l ↔ is_O f g l :=
by { rw ←is_O_norm_left, simp only [norm_neg], rw is_O_norm_left }

@[simp] theorem is_o_neg_left {f : α → β} {g : α → γ} {l : filter α} :
  is_o (λ x, -f x) g l ↔ is_o f g l :=
by { rw ←is_o_norm_left, simp only [norm_neg], rw is_o_norm_left }

theorem is_O.add {f₁ f₂ : α → β} {g : α → γ} {l : filter α} (h₁ : is_O f₁ g l) (h₂ : is_O f₂ g l) :
  is_O (λ x, f₁ x + f₂ x) g l :=
let ⟨c₁, c₁pos, hc₁⟩ := h₁,
    ⟨c₂, c₂pos, hc₂⟩ := h₂ in
begin
  use [c₁ + c₂, add_pos c₁pos c₂pos],
  filter_upwards [hc₁, hc₂],
  intros x hx₁ hx₂,
  show ∥f₁ x + f₂ x∥ ≤ (c₁ + c₂) * ∥g x∥,
  apply le_trans (norm_triangle _ _),
  rw add_mul,
  exact add_le_add hx₁ hx₂
end

theorem is_o.add {f₁ f₂ : α → β} {g : α → γ} {l : filter α} (h₁ : is_o f₁ g l) (h₂ : is_o f₂ g l) :
  is_o (λ x, f₁ x + f₂ x) g l :=
begin
  intros c cpos,
  filter_upwards [h₁ (c / 2) (half_pos cpos), h₂ (c / 2) (half_pos cpos)],
  intros x hx₁ hx₂, dsimp at hx₁ hx₂,
  apply le_trans (norm_triangle _ _),
  apply le_trans (add_le_add hx₁ hx₂),
  rw [←mul_add, ←two_mul, ←mul_assoc, div_mul_cancel _ two_ne_zero]
end

theorem is_O.sub {f₁ f₂ : α → β} {g : α → γ} {l : filter α} (h₁ : is_O f₁ g l) (h₂ : is_O f₂ g l) :
  is_O (λ x, f₁ x - f₂ x) g l :=
h₁.add (is_O_neg_left.mpr h₂)

theorem is_o.sub {f₁ f₂ : α → β} {g : α → γ} {l : filter α} (h₁ : is_o f₁ g l) (h₂ : is_o f₂ g l) :
  is_o (λ x, f₁ x - f₂ x) g l :=
h₁.add (is_o_neg_left.mpr h₂)

theorem is_O_comm {f₁ f₂ : α → β} {g : α → γ} {l : filter α} :
  is_O (λ x, f₁ x - f₂ x) g l ↔ is_O (λ x, f₂ x - f₁ x) g l :=
by simpa using @is_O_neg_left _ _ _ _ _ (λ x, f₂ x - f₁ x) g l

theorem is_O.symm {f₁ f₂ : α → β} {g : α → γ} {l : filter α} :
  is_O (λ x, f₁ x - f₂ x) g l → is_O (λ x, f₂ x - f₁ x) g l :=
is_O_comm.1

theorem is_O.tri {f₁ f₂ f₃ : α → β} {g : α → γ} {l : filter α}
    (h₁ : is_O (λ x, f₁ x - f₂ x) g l)
    (h₂ : is_O (λ x, f₂ x - f₃ x) g l) :
  is_O (λ x, f₁ x - f₃ x) g l :=
(h₁.add h₂).congr_left (by simp)

theorem is_O.congr_of_sub {f₁ f₂ : α → β} {g : α → γ} {l : filter α}
    (h : is_O (λ x, f₁ x - f₂ x) g l) :
  is_O f₁ g l ↔ is_O f₂ g l :=
⟨λ h', (h'.sub h).congr_left (λ x, sub_sub_cancel _ _),
 λ h', (h.add h').congr_left (λ x, sub_add_cancel _ _)⟩

theorem is_o_comm {f₁ f₂ : α → β} {g : α → γ} {l : filter α} :
  is_o (λ x, f₁ x - f₂ x) g l ↔ is_o (λ x, f₂ x - f₁ x) g l :=
by simpa using @is_o_neg_left _ _ _ _ _ (λ x, f₂ x - f₁ x) g l

theorem is_o.symm {f₁ f₂ : α → β} {g : α → γ} {l : filter α} :
  is_o (λ x, f₁ x - f₂ x) g l → is_o (λ x, f₂ x - f₁ x) g l :=
is_o_comm.1

theorem is_o.tri {f₁ f₂ f₃ : α → β} {g : α → γ} {l : filter α}
    (h₁ : is_o (λ x, f₁ x - f₂ x) g l)
    (h₂ : is_o (λ x, f₂ x - f₃ x) g l) :
  is_o (λ x, f₁ x - f₃ x) g l :=
(h₁.add h₂).congr_left (by simp)

theorem is_o.congr_of_sub {f₁ f₂ : α → β} {g : α → γ} {l : filter α}
    (h : is_o (λ x, f₁ x - f₂ x) g l) :
  is_o f₁ g l ↔ is_o f₂ g l :=
⟨λ h', (h'.sub h).congr_left (λ x, sub_sub_cancel _ _),
 λ h', (h.add h').congr_left (λ x, sub_add_cancel _ _)⟩

end

section
variables [normed_group β] [normed_group γ]

theorem is_O_zero (g : α → γ) (l : filter α) :
  is_O (λ x, (0 : β)) g l :=
⟨1, zero_lt_one, by { filter_upwards [univ_mem_sets], intros x _, simp }⟩

theorem is_O_refl_left {f : α → β} {g : α → γ} {l : filter α} :
  is_O (λ x, f x - f x) g l :=
by simpa using is_O_zero g l

theorem is_O_zero_right_iff {f : α → β} {l : filter α} :
  is_O f (λ x, (0 : γ)) l ↔ {x | f x = 0} ∈ l :=
begin
  rw [is_O_iff], split,
  { rintros ⟨c, hc⟩,
    filter_upwards [hc], dsimp,
    intro x, rw [norm_zero, mul_zero], intro hx,
    rw ←norm_eq_zero,
    exact le_antisymm hx (norm_nonneg _) },
  intro h, use 0,
  filter_upwards [h], dsimp,
  intros x hx,
  rw [hx, norm_zero, zero_mul]
end

theorem is_o_zero (g : α → γ) (l : filter α) :
  is_o (λ x, (0 : β)) g l :=
λ c cpos,
by { filter_upwards [univ_mem_sets], intros x _, simp,
     exact mul_nonneg (le_of_lt cpos) (norm_nonneg _)}

theorem is_o_refl_left {f : α → β} {g : α → γ} {l : filter α} :
  is_o (λ x, f x - f x) g l :=
by simpa using is_o_zero g l

theorem is_o_zero_right_iff {f : α → β} (l : filter α) :
  is_o f (λ x, (0 : γ)) l ↔ {x | f x = 0} ∈ l :=
begin
  split,
  { intro h, exact is_O_zero_right_iff.mp h.to_is_O },
  intros h c cpos,
  filter_upwards [h], dsimp,
  intros x hx,
  rw [hx, norm_zero, norm_zero, mul_zero]
end

end

section
variables [has_norm β] [normed_field γ]

theorem is_O_const_one (c : β) (l : filter α) :
  is_O (λ x : α, c) (λ x, (1 : γ)) l :=
begin
  rw is_O_iff,
  refine ⟨∥c∥, _⟩,
  simp only [norm_one, mul_one],
  apply univ_mem_sets',
  simp [le_refl],
end

end

section
variables [normed_field β] [normed_group γ]

theorem is_O_const_mul_left {f : α → β} {g : α → γ} {l : filter α} (h : is_O f g l) (c : β) :
  is_O (λ x, c * f x) g l :=
 begin
  cases classical.em (c = 0) with h'' h'',
  { simp [h''], apply is_O_zero },
  rcases h with ⟨c', c'pos, h'⟩,
  have cne0 : ∥c∥ ≠ 0, from mt (norm_eq_zero _).mp h'',
  have cpos : ∥c∥ > 0, from lt_of_le_of_ne (norm_nonneg _) (ne.symm cne0),
  refine ⟨∥c∥ * c', mul_pos cpos c'pos, _⟩,
  filter_upwards [h'], dsimp,
  intros x h₀,
  rw [normed_field.norm_mul, mul_assoc],
  exact mul_le_mul_of_nonneg_left h₀ (norm_nonneg _)
end

theorem is_O_const_mul_left_iff {f : α → β} {g : α → γ} {l : filter α} {c : β} (hc : c ≠ 0) :
  is_O (λ x, c * f x) g l ↔ is_O f g l :=
begin
  split,
  { intro h,
    convert is_O_const_mul_left h c⁻¹, ext,
    rw [←mul_assoc, inv_mul_cancel hc, one_mul] },
  intro h, apply is_O_const_mul_left h
end

theorem is_o_const_mul_left {f : α → β} {g : α → γ} {l : filter α}
    (h : is_o f g l) (c : β) :
  is_o (λ x, c * f x) g l :=
begin
  cases classical.em (c = 0) with h'' h'',
  { simp [h''], apply is_o_zero },
  have cne0 : ∥c∥ ≠ 0, from mt (norm_eq_zero _).mp h'',
  have cpos : ∥c∥ > 0, from lt_of_le_of_ne (norm_nonneg _) (ne.symm cne0),
  intros c' c'pos, dsimp,
  filter_upwards [h (c' / ∥c∥) (div_pos c'pos cpos)], dsimp,
  intros x hx, rw [normed_field.norm_mul],
  apply le_trans (mul_le_mul_of_nonneg_left hx (le_of_lt cpos)),
  rw [←mul_assoc, mul_div_cancel' _ cne0]
end

theorem is_o_const_mul_left_iff {f : α → β} {g : α → γ} {l : filter α} {c : β} (hc : c ≠ 0) :
  is_o (λ x, c * f x) g l ↔ is_o f g l :=
begin
  split,
  { intro h,
    convert is_o_const_mul_left h c⁻¹, ext,
    rw [←mul_assoc, inv_mul_cancel hc, one_mul] },
  intro h',
  apply is_o_const_mul_left h'
end

end

section
variables [normed_group β] [normed_field γ]

theorem is_O_of_is_O_const_mul_right {f : α → β} {g : α → γ} {l : filter α} {c : γ}
    (h : is_O f (λ x, c * g x) l) :
  is_O f g l  :=
begin
  cases classical.em (c = 0) with h' h',
  { simp [h', is_O_zero_right_iff] at h, rw is_O_congr_left h, apply is_O_zero },
  have cne0 : ∥c∥ ≠ 0, from mt (norm_eq_zero _).mp h',
  have cpos : ∥c∥ > 0, from lt_of_le_of_ne (norm_nonneg _) (ne.symm cne0),
  rcases h with ⟨c', c'pos, h''⟩,
  refine ⟨c' * ∥c∥, mul_pos c'pos cpos, _⟩,
  convert h'', ext x, dsimp,
  rw [normed_field.norm_mul, mul_assoc]
end

theorem is_O_const_mul_right_iff {f : α → β} {g : α → γ} {l : filter α} {c : γ} (hc : c ≠ 0) :
  is_O f (λ x, c * g x) l ↔ is_O f g l :=
begin
  split,
  { intro h, exact is_O_of_is_O_const_mul_right h },
  intro h,
  apply is_O_of_is_O_const_mul_right,
  show is_O f (λ (x : α), c⁻¹ * (c * g x)) l,
  convert h, ext, rw [←mul_assoc, inv_mul_cancel hc, one_mul]
end

theorem is_o_of_is_o_const_mul_right {f : α → β} {g : α → γ} {l : filter α} {c : γ}
    (h : is_o f (λ x, c * g x) l) :
  is_o f g l  :=
begin
  cases classical.em (c = 0) with h' h',
  { simp [h', is_o_zero_right_iff] at h, rw is_o_congr_left h, apply is_o_zero },
  have cne0 : ∥c∥ ≠ 0, from mt (norm_eq_zero _).mp h',
  have cpos : ∥c∥ > 0, from lt_of_le_of_ne (norm_nonneg _) (ne.symm cne0),
  intros c' c'pos,
  convert h (c' / ∥c∥) (div_pos c'pos cpos), dsimp,
  ext x, rw [normed_field.norm_mul, ←mul_assoc, div_mul_cancel _ cne0]
end

theorem is_o_const_mul_right {f : α → β} {g : α → γ} {l : filter α} {c : γ} (hc : c ≠ 0) :
  is_o f (λ x, c * g x) l ↔ is_o f g l :=
begin
  split,
  { intro h, exact is_o_of_is_o_const_mul_right h },
  intro h,
  apply is_o_of_is_o_const_mul_right,
  show is_o f (λ (x : α), c⁻¹ * (c * g x)) l,
  convert h, ext, rw [←mul_assoc, inv_mul_cancel hc, one_mul]
end

theorem is_o_one_iff {f : α → β} {l : filter α} :
  is_o f (λ x, (1 : γ)) l ↔ tendsto f l (nhds 0) :=
begin
  rw [normed_space.tendsto_nhds_zero, is_o], split,
  { intros h e epos,
    filter_upwards [h (e / 2) (half_pos epos)], simp,
    intros x hx,
    exact lt_of_le_of_lt hx (half_lt_self epos) },
  intros h e epos,
  filter_upwards [h e epos], simp,
  intros x hx,
  exact le_of_lt hx
end

end

section
variables [normed_group β] [normed_group γ]

theorem is_O.trans_tendsto {f : α → β} {g : α → γ} {l : filter α}
    (h₁ : is_O f g l) (h₂ : tendsto g l (nhds 0)) :
  tendsto f l (nhds 0) :=
(@is_o_one_iff _ _ ℝ _ _ _ _).1 $ h₁.trans_is_o $ is_o_one_iff.2 h₂

theorem is_o.trans_tendsto {f : α → β} {g : α → γ} {l : filter α}
  (h₁ : is_o f g l) : tendsto g l (nhds 0) → tendsto f l (nhds 0) :=
h₁.to_is_O.trans_tendsto

end

section
variables [normed_field β] [normed_field γ]

theorem is_O_mul {f₁ f₂ : α → β} {g₁ g₂ : α → γ} {l : filter α}
    (h₁ : is_O f₁ g₁ l) (h₂ : is_O f₂ g₂ l) :
  is_O (λ x, f₁ x * f₂ x) (λ x, g₁ x * g₂ x) l :=
begin
  rcases h₁ with ⟨c₁, c₁pos, hc₁⟩,
  rcases h₂ with ⟨c₂, c₂pos, hc₂⟩,
  refine ⟨c₁ * c₂, mul_pos c₁pos c₂pos, _⟩,
  filter_upwards [hc₁, hc₂], dsimp,
  intros x hx₁ hx₂,
  rw [normed_field.norm_mul, normed_field.norm_mul, mul_assoc, mul_left_comm c₂, ←mul_assoc],
  exact mul_le_mul hx₁ hx₂ (norm_nonneg _) (mul_nonneg (le_of_lt c₁pos) (norm_nonneg _))
end

theorem is_o_mul_left {f₁ f₂ : α → β} {g₁ g₂ : α → γ} {l : filter α}
    (h₁ : is_O f₁ g₁ l) (h₂ : is_o f₂ g₂ l):
  is_o (λ x, f₁ x * f₂ x) (λ x, g₁ x * g₂ x) l :=
begin
  intros c cpos,
  rcases h₁ with ⟨c₁, c₁pos, hc₁⟩,
  filter_upwards [hc₁, h₂ (c / c₁) (div_pos cpos c₁pos)], dsimp,
  intros x hx₁ hx₂,
  rw [normed_field.norm_mul, normed_field.norm_mul],
  apply le_trans (mul_le_mul hx₁ hx₂ (norm_nonneg _) (mul_nonneg (le_of_lt c₁pos) (norm_nonneg _))),
  rw [mul_comm c₁, mul_assoc, mul_left_comm c₁, ←mul_assoc _ c₁, div_mul_cancel _ (ne_of_gt c₁pos)],
  rw [mul_left_comm]
end

theorem is_o_mul_right {f₁ f₂ : α → β} {g₁ g₂ : α → γ} {l : filter α}
    (h₁ : is_o f₁ g₁ l) (h₂ : is_O f₂ g₂ l):
  is_o (λ x, f₁ x * f₂ x) (λ x, g₁ x * g₂ x) l :=
by convert is_o_mul_left h₂ h₁; simp only [mul_comm]

theorem is_o_mul {f₁ f₂ : α → β} {g₁ g₂ : α → γ} {l : filter α}
    (h₁ : is_o f₁ g₁ l) (h₂ : is_o f₂ g₂ l):
  is_o (λ x, f₁ x * f₂ x) (λ x, g₁ x * g₂ x) l :=
is_o_mul_left h₁.to_is_O h₂

end

/-
Note: the theorems in the next two sections can also be used for the integers, since
scalar multiplication is multiplication.
-/

section
variables {K : Type*} [normed_field K] [normed_space K β] [normed_group γ]

set_option class.instance_max_depth 43

theorem is_O_const_smul_left {f : α → β} {g : α → γ} {l : filter α} (h : is_O f g l) (c : K) :
  is_O (λ x, c • f x) g l :=
begin
  rw [←is_O_norm_left], simp only [norm_smul],
  apply is_O_const_mul_left,
  rw [is_O_norm_left],
  apply h
end

theorem is_O_const_smul_left_iff {f : α → β} {g : α → γ} {l : filter α} {c : K} (hc : c ≠ 0) :
  is_O (λ x, c • f x) g l ↔ is_O f g l :=
begin
  have cne0 : ∥c∥ ≠ 0, from mt (norm_eq_zero _).mp hc,
  rw [←is_O_norm_left], simp only [norm_smul],
  rw [is_O_const_mul_left_iff cne0, is_O_norm_left]
end

theorem is_o_const_smul_left {f : α → β} {g : α → γ} {l : filter α} (h : is_o f g l) (c : K) :
  is_o (λ x, c • f x) g l :=
begin
  rw [←is_o_norm_left], simp only [norm_smul],
  apply is_o_const_mul_left,
  rw [is_o_norm_left],
  apply h
end

theorem is_o_const_smul_left_iff {f : α → β} {g : α → γ} {l : filter α} {c : K} (hc : c ≠ 0) :
  is_o (λ x, c • f x) g l ↔ is_o f g l :=
begin
  have cne0 : ∥c∥ ≠ 0, from mt (norm_eq_zero _).mp hc,
  rw [←is_o_norm_left], simp only [norm_smul],
  rw [is_o_const_mul_left_iff cne0, is_o_norm_left]
end

end

section
variables {K : Type*} [normed_group β] [normed_field K] [normed_space K γ]

set_option class.instance_max_depth 43

theorem is_O_const_smul_right {f : α → β} {g : α → γ} {l : filter α} {c : K} (hc : c ≠ 0) :
  is_O f (λ x, c • g x) l ↔ is_O f g l :=
begin
  have cne0 : ∥c∥ ≠ 0, from mt (norm_eq_zero _).mp hc,
  rw [←is_O_norm_right], simp only [norm_smul],
  rw [is_O_const_mul_right_iff cne0, is_O_norm_right]
end

theorem is_o_const_smul_right {f : α → β} {g : α → γ} {l : filter α} {c : K} (hc : c ≠ 0) :
  is_o f (λ x, c • g x) l ↔ is_o f g l :=
begin
  have cne0 : ∥c∥ ≠ 0, from mt (norm_eq_zero _).mp hc,
  rw [←is_o_norm_right], simp only [norm_smul],
  rw [is_o_const_mul_right cne0, is_o_norm_right]
end

end

section
variables {K : Type*} [normed_field K] [normed_space K β] [normed_space K γ]

set_option class.instance_max_depth 43

theorem is_O_smul {k : α → K} {f : α → β} {g : α → γ} {l : filter α} (h : is_O f g l) :
  is_O (λ x, k x • f x) (λ x, k x • g x) l :=
begin
  rw [←is_O_norm_left, ←is_O_norm_right], simp only [norm_smul],
  apply is_O_mul (is_O_refl _ _),
  rw [is_O_norm_left, is_O_norm_right],
  exact h
end

theorem is_o_smul {k : α → K} {f : α → β} {g : α → γ} {l : filter α} (h : is_o f g l) :
  is_o (λ x, k x • f x) (λ x, k x • g x) l :=
begin
  rw [←is_o_norm_left, ←is_o_norm_right], simp only [norm_smul],
  apply is_o_mul_left (is_O_refl _ _),
  rw [is_o_norm_left, is_o_norm_right],
  exact h
end

end

section
variables [normed_field β]

theorem tendsto_nhds_zero_of_is_o {f g : α → β} {l : filter α} (h : is_o f g l) :
  tendsto (λ x, f x / (g x)) l (nhds 0) :=
have eq₁ : is_o (λ x, f x / g x) (λ x, g x / g x) l,
  from is_o_mul_right h (is_O_refl _ _),
have eq₂ : is_O (λ x, g x / g x) (λ x, (1 : β)) l,
  begin
    use [1, zero_lt_one],
    filter_upwards [univ_mem_sets], simp,
    intro x,
    cases classical.em (∥g x∥ = 0) with h' h'; simp [h'],
    exact zero_le_one
  end,
is_o_one_iff.mp (eq₁.trans_is_O eq₂)

private theorem is_o_of_tendsto {f g : α → β} {l : filter α}
    (hgf : ∀ x, g x = 0 → f x = 0) (h : tendsto (λ x, f x / (g x)) l (nhds 0)) :
  is_o f g l :=
have eq₁ : is_o (λ x, f x / (g x)) (λ x, (1 : β)) l,
  from is_o_one_iff.mpr h,
have eq₂ : is_o (λ x, f x / g x * g x) g l,
  by convert is_o_mul_right eq₁ (is_O_refl _ _); simp,
have eq₃ : is_O f (λ x, f x / g x * g x) l,
  begin
    use [1, zero_lt_one],
    refine filter.univ_mem_sets' (assume x, _),
    suffices : ∥f x∥ ≤ ∥f x∥ / ∥g x∥ * ∥g x∥, { simpa },
    by_cases g x = 0,
    { simp only [h, hgf x h, norm_zero, mul_zero] },
    { rw [div_mul_cancel], exact mt (norm_eq_zero _).1 h }
  end,
eq₃.trans_is_o eq₂

theorem is_o_iff_tendsto [normed_field β] {f g : α → β} {l : filter α}
    (hgf : ∀ x, g x = 0 → f x = 0) :
  is_o f g l ↔ tendsto (λ x, f x / (g x)) l (nhds 0) :=
iff.intro tendsto_nhds_zero_of_is_o (is_o_of_tendsto hgf)

end

end asymptotics