/-
Copyright (c) 2020 Zhouhang Zhou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Zhouhang Zhou, Yury Kudryashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.measure_theory.bochner_integration
import Mathlib.analysis.normed_space.indicator_function
import Mathlib.PostPort

universes u_1 u_2 u_3 u_5 u_4

namespace Mathlib

/-!
# Set integral

In this file we prove some properties of `∫ x in s, f x ∂μ`. Recall that this notation
is defined as `∫ x, f x ∂(μ.restrict s)`. In `integral_indicator` we prove that for a measurable
function `f` and a measurable set `s` this definition coincides with another natural definition:
`∫ x, indicator s f x ∂μ = ∫ x in s, f x ∂μ`, where `indicator s f x` is equal to `f x` for `x ∈ s`
and is zero otherwise.

Since `∫ x in s, f x ∂μ` is a notation, one can rewrite or apply any theorem about `∫ x, f x ∂μ`
directly. In this file we prove some theorems about dependence of `∫ x in s, f x ∂μ` on `s`, e.g.
`integral_union`, `integral_empty`, `integral_univ`.

We also define `integrable_on f s μ := integrable f (μ.restrict s)` and prove theorems like
`integrable_on_union : integrable_on f (s ∪ t) μ ↔ integrable_on f s μ ∧ integrable_on f t μ`.

Next we define a predicate `integrable_at_filter (f : α → E) (l : filter α) (μ : measure α)`
saying that `f` is integrable at some set `s ∈ l` and prove that a measurable function is integrable
at `l` with respect to `μ` provided that `f` is bounded above at `l ⊓ μ.ae` and `μ` is finite
at `l`.

Finally, we prove a version of the
[Fundamental theorem of calculus](https://en.wikipedia.org/wiki/Fundamental_theorem_of_calculus)
for set integral, see `filter.tendsto.integral_sub_linear_is_o_ae` and its corollaries.
Namely, consider a measurably generated filter `l`, a measure `μ` finite at this filter, and
a function `f` that has a finite limit `c` at `l ⊓ μ.ae`. Then `∫ x in s, f x ∂μ = μ s • c + o(μ s)`
as `s` tends to `l.lift' powerset`, i.e. for any `ε>0` there exists `t ∈ l` such that
`∥∫ x in s, f x ∂μ - μ s • c∥ ≤ ε * μ s` whenever `s ⊆ t`. We also formulate a version of this
theorem for a locally finite measure `μ` and a function `f` continuous at a point `a`.

## Notation

`∫ a in s, f a` is `measure_theory.integral (s.indicator f)`

## TODO

The file ends with over a hundred lines of commented out code. This is the old contents of this file
using the `indicator` approach to the definition of `∫ x in s, f x ∂μ`. This code should be
migrated to the new definition.

-/

theorem piecewise_ae_eq_restrict {α : Type u_1} {β : Type u_2} [measurable_space α]
    {μ : measure_theory.measure α} {s : set α} {f : α → β} {g : α → β} (hs : is_measurable s) :
    filter.eventually_eq (measure_theory.measure.ae (measure_theory.measure.restrict μ s))
        (set.piecewise s f g) f :=
  sorry

theorem piecewise_ae_eq_restrict_compl {α : Type u_1} {β : Type u_2} [measurable_space α]
    {μ : measure_theory.measure α} {s : set α} {f : α → β} {g : α → β} (hs : is_measurable s) :
    filter.eventually_eq (measure_theory.measure.ae (measure_theory.measure.restrict μ (sᶜ)))
        (set.piecewise s f g) g :=
  sorry

theorem indicator_ae_eq_restrict {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β]
    {μ : measure_theory.measure α} {s : set α} {f : α → β} (hs : is_measurable s) :
    filter.eventually_eq (measure_theory.measure.ae (measure_theory.measure.restrict μ s))
        (set.indicator s f) f :=
  piecewise_ae_eq_restrict hs

theorem indicator_ae_eq_restrict_compl {α : Type u_1} {β : Type u_2} [measurable_space α]
    [HasZero β] {μ : measure_theory.measure α} {s : set α} {f : α → β} (hs : is_measurable s) :
    filter.eventually_eq (measure_theory.measure.ae (measure_theory.measure.restrict μ (sᶜ)))
        (set.indicator s f) 0 :=
  piecewise_ae_eq_restrict_compl hs

/-- A function `f` is measurable at filter `l` w.r.t. a measure `μ` if it is ae-measurable
w.r.t. `μ.restrict s` for some `s ∈ l`. -/
def measurable_at_filter {α : Type u_1} {β : Type u_2} [measurable_space α] [measurable_space β]
    (f : α → β) (l : filter α)
    (μ :
      autoParam (measure_theory.measure α)
        (Lean.Syntax.ident Lean.SourceInfo.none
          (String.toSubstring "Mathlib.measure_theory.volume_tac")
          (Lean.Name.mkStr
            (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "measure_theory")
            "volume_tac")
          [])) :=
  ∃ (s : set α), ∃ (H : s ∈ l), ae_measurable f

@[simp] theorem measurable_at_bot {α : Type u_1} {β : Type u_2} [measurable_space α]
    [measurable_space β] {μ : measure_theory.measure α} {f : α → β} : measurable_at_filter f ⊥ :=
  sorry

protected theorem measurable_at_filter.eventually {α : Type u_1} {β : Type u_2} [measurable_space α]
    [measurable_space β] {l : filter α} {f : α → β} {μ : measure_theory.measure α}
    (h : measurable_at_filter f l) :
    filter.eventually (fun (s : set α) => ae_measurable f) (filter.lift' l set.powerset) :=
  iff.mpr (filter.eventually_lift'_powerset' fun (s t : set α) => ae_measurable.mono_set) h

protected theorem measurable_at_filter.filter_mono {α : Type u_1} {β : Type u_2}
    [measurable_space α] [measurable_space β] {l : filter α} {l' : filter α} {f : α → β}
    {μ : measure_theory.measure α} (h : measurable_at_filter f l) (h' : l' ≤ l) :
    measurable_at_filter f l' :=
  sorry

protected theorem ae_measurable.measurable_at_filter {α : Type u_1} {β : Type u_2}
    [measurable_space α] [measurable_space β] {l : filter α} {f : α → β}
    {μ : measure_theory.measure α} (h : ae_measurable f) : measurable_at_filter f l :=
  Exists.intro set.univ
    (Exists.intro filter.univ_mem_sets
      (eq.mpr (id (Eq._oldrec (Eq.refl (ae_measurable f)) measure_theory.measure.restrict_univ)) h))

theorem ae_measurable.measurable_at_filter_of_mem {α : Type u_1} {β : Type u_2} [measurable_space α]
    [measurable_space β] {l : filter α} {f : α → β} {μ : measure_theory.measure α} {s : set α}
    (h : ae_measurable f) (hl : s ∈ l) : measurable_at_filter f l :=
  Exists.intro s (Exists.intro hl h)

protected theorem measurable.measurable_at_filter {α : Type u_1} {β : Type u_2} [measurable_space α]
    [measurable_space β] {l : filter α} {f : α → β} {μ : measure_theory.measure α}
    (h : measurable f) : measurable_at_filter f l :=
  ae_measurable.measurable_at_filter (measurable.ae_measurable h)

namespace measure_theory

theorem has_finite_integral_restrict_of_bounded {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] {f : α → E} {s : set α} {μ : measure α} {C : ℝ} (hs : coe_fn μ s < ⊤)
    (hf : filter.eventually (fun (x : α) => norm (f x) ≤ C) (measure.ae (measure.restrict μ s))) :
    has_finite_integral f :=
  has_finite_integral_of_bounded hf

/-- A function is `integrable_on` a set `s` if it is a measurable function and if the integral of
  its pointwise norm over `s` is less than infinity. -/
def integrable_on {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] (f : α → E) (s : set α)
    (μ :
      autoParam (measure α)
        (Lean.Syntax.ident Lean.SourceInfo.none
          (String.toSubstring "Mathlib.measure_theory.volume_tac")
          (Lean.Name.mkStr
            (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "measure_theory")
            "volume_tac")
          [])) :=
  integrable f

theorem integrable_on.integrable {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {s : set α} {μ : measure α} (h : integrable_on f s) :
    integrable f :=
  h

@[simp] theorem integrable_on_empty {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {μ : measure α} : integrable_on f ∅ :=
  sorry

@[simp] theorem integrable_on_univ {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {μ : measure α} :
    integrable_on f set.univ ↔ integrable f :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (integrable_on f set.univ ↔ integrable f))
        (integrable_on.equations._eqn_1 f set.univ)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (integrable f ↔ integrable f)) measure.restrict_univ))
      (iff.refl (integrable f)))

theorem integrable_on_zero {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {s : set α} {μ : measure α} : integrable_on (fun (_x : α) => 0) s :=
  integrable_zero α E (measure.restrict μ s)

theorem integrable_on_const {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {s : set α} {μ : measure α} {C : E} :
    integrable_on (fun (_x : α) => C) s ↔ C = 0 ∨ coe_fn μ s < ⊤ :=
  sorry

theorem integrable_on.mono {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {s : set α} {t : set α} {μ : measure α} {ν : measure α}
    (h : integrable_on f t) (hs : s ⊆ t) (hμ : μ ≤ ν) : integrable_on f s :=
  integrable.mono_measure h (measure.restrict_mono hs hμ)

theorem integrable_on.mono_set {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {s : set α} {t : set α} {μ : measure α} (h : integrable_on f t)
    (hst : s ⊆ t) : integrable_on f s :=
  integrable_on.mono h hst (le_refl μ)

theorem integrable_on.mono_measure {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {μ : measure α} {ν : measure α}
    (h : integrable_on f s) (hμ : μ ≤ ν) : integrable_on f s :=
  integrable_on.mono h (set.subset.refl s) hμ

theorem integrable_on.mono_set_ae {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {t : set α} {μ : measure α}
    (h : integrable_on f t) (hst : filter.eventually_le (measure.ae μ) s t) : integrable_on f s :=
  integrable.mono_measure (integrable_on.integrable h) (restrict_mono_ae hst)

theorem integrable.integrable_on {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {s : set α} {μ : measure α} (h : integrable f) :
    integrable_on f s :=
  integrable.mono_measure h measure.restrict_le_self

theorem integrable.integrable_on' {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {μ : measure α}
    (h : integrable f) : integrable_on f s :=
  h

theorem integrable_on.left_of_union {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {t : set α} {μ : measure α}
    (h : integrable_on f (s ∪ t)) : integrable_on f s :=
  integrable_on.mono_set h (set.subset_union_left s t)

theorem integrable_on.right_of_union {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {t : set α} {μ : measure α}
    (h : integrable_on f (s ∪ t)) : integrable_on f t :=
  integrable_on.mono_set h (set.subset_union_right s t)

theorem integrable_on.union {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {s : set α} {t : set α} {μ : measure α}
    (hs : integrable_on f s) (ht : integrable_on f t) : integrable_on f (s ∪ t) :=
  integrable.mono_measure (integrable.add_measure hs ht) (measure.restrict_union_le s t)

@[simp] theorem integrable_on_union {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {t : set α} {μ : measure α} :
    integrable_on f (s ∪ t) ↔ integrable_on f s ∧ integrable_on f t :=
  sorry

@[simp] theorem integrable_on_finite_union {α : Type u_1} {β : Type u_2} {E : Type u_3}
    [measurable_space α] [normed_group E] [measurable_space E] {f : α → E} {μ : measure α}
    {s : set β} (hs : set.finite s) {t : β → set α} :
    integrable_on f (set.Union fun (i : β) => set.Union fun (H : i ∈ s) => t i) ↔
        ∀ (i : β), i ∈ s → integrable_on f (t i) :=
  sorry

@[simp] theorem integrable_on_finset_union {α : Type u_1} {β : Type u_2} {E : Type u_3}
    [measurable_space α] [normed_group E] [measurable_space E] {f : α → E} {μ : measure α}
    {s : finset β} {t : β → set α} :
    integrable_on f (set.Union fun (i : β) => set.Union fun (H : i ∈ s) => t i) ↔
        ∀ (i : β), i ∈ s → integrable_on f (t i) :=
  integrable_on_finite_union (finset.finite_to_set s)

theorem integrable_on.add_measure {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {μ : measure α} {ν : measure α}
    (hμ : integrable_on f s) (hν : integrable_on f s) : integrable_on f s :=
  id
    (eq.mpr (id (Eq._oldrec (Eq.refl (integrable f)) (measure.restrict_add μ ν s)))
      (integrable.add_measure (integrable_on.integrable hμ) hν))

@[simp] theorem integrable_on_add_measure {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {μ : measure α} {ν : measure α} :
    integrable_on f s ↔ integrable_on f s ∧ integrable_on f s :=
  sorry

theorem ae_measurable_indicator_iff {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {μ : measure α}
    (hs : is_measurable s) : ae_measurable f ↔ ae_measurable (set.indicator s f) :=
  sorry

theorem integrable_indicator_iff {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {s : set α} {μ : measure α} (hs : is_measurable s) :
    integrable (set.indicator s f) ↔ integrable_on f s :=
  sorry

theorem integrable_on.indicator {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {s : set α} {μ : measure α} (h : integrable_on f s)
    (hs : is_measurable s) : integrable (set.indicator s f) :=
  iff.mpr (integrable_indicator_iff hs) h

/-- We say that a function `f` is *integrable at filter* `l` if it is integrable on some
set `s ∈ l`. Equivalently, it is eventually integrable on `s` in `l.lift' powerset`. -/
def integrable_at_filter {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] (f : α → E) (l : filter α)
    (μ :
      autoParam (measure α)
        (Lean.Syntax.ident Lean.SourceInfo.none
          (String.toSubstring "Mathlib.measure_theory.volume_tac")
          (Lean.Name.mkStr
            (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "measure_theory")
            "volume_tac")
          [])) :=
  ∃ (s : set α), ∃ (H : s ∈ l), integrable_on f s

protected theorem integrable_at_filter.eventually {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {μ : measure α} {l : filter α}
    (h : integrable_at_filter f l) :
    filter.eventually (fun (s : set α) => integrable_on f s) (filter.lift' l set.powerset) :=
  sorry

theorem integrable_at_filter.filter_mono {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {μ : measure α} {l : filter α} {l' : filter α}
    (hl : l ≤ l') (hl' : integrable_at_filter f l') : integrable_at_filter f l :=
  sorry

theorem integrable_at_filter.inf_of_left {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {μ : measure α} {l : filter α} {l' : filter α}
    (hl : integrable_at_filter f l) : integrable_at_filter f (l ⊓ l') :=
  integrable_at_filter.filter_mono inf_le_left hl

theorem integrable_at_filter.inf_of_right {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {μ : measure α} {l : filter α} {l' : filter α}
    (hl : integrable_at_filter f l) : integrable_at_filter f (l' ⊓ l) :=
  integrable_at_filter.filter_mono inf_le_right hl

@[simp] theorem integrable_at_filter.inf_ae_iff {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {μ : measure α} {l : filter α} :
    integrable_at_filter f (l ⊓ measure.ae μ) ↔ integrable_at_filter f l :=
  sorry

theorem integrable_at_filter.of_inf_ae {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {μ : measure α} {l : filter α} :
    integrable_at_filter f (l ⊓ measure.ae μ) → integrable_at_filter f l :=
  iff.mp integrable_at_filter.inf_ae_iff

/-- If `μ` is a measure finite at filter `l` and `f` is a function such that its norm is bounded
above at `l`, then `f` is integrable at `l`. -/
theorem measure.finite_at_filter.integrable_at_filter {α : Type u_1} {E : Type u_3}
    [measurable_space α] [normed_group E] [measurable_space E] {f : α → E} {μ : measure α}
    {l : filter α} [filter.is_measurably_generated l] (hfm : measurable_at_filter f l)
    (hμ : measure.finite_at_filter μ l) (hf : filter.is_bounded_under LessEq l (norm ∘ f)) :
    integrable_at_filter f l :=
  sorry

theorem measure.finite_at_filter.integrable_at_filter_of_tendsto_ae {α : Type u_1} {E : Type u_3}
    [measurable_space α] [normed_group E] [measurable_space E] {f : α → E} {μ : measure α}
    {l : filter α} [filter.is_measurably_generated l] (hfm : measurable_at_filter f l)
    (hμ : measure.finite_at_filter μ l) {b : E}
    (hf : filter.tendsto f (l ⊓ measure.ae μ) (nhds b)) : integrable_at_filter f l :=
  integrable_at_filter.of_inf_ae
    (measure.finite_at_filter.integrable_at_filter
      (measurable_at_filter.filter_mono hfm inf_le_left) (measure.finite_at_filter.inf_of_left hμ)
      (filter.tendsto.is_bounded_under_le (filter.tendsto.norm hf)))

theorem Mathlib.filter.tendsto.integrable_at_filter_ae {α : Type u_1} {E : Type u_3}
    [measurable_space α] [normed_group E] [measurable_space E] {f : α → E} {μ : measure α}
    {l : filter α} [filter.is_measurably_generated l] (hfm : measurable_at_filter f l)
    (hμ : measure.finite_at_filter μ l) {b : E}
    (hf : filter.tendsto f (l ⊓ measure.ae μ) (nhds b)) : integrable_at_filter f l :=
  measure.finite_at_filter.integrable_at_filter_of_tendsto_ae

theorem measure.finite_at_filter.integrable_at_filter_of_tendsto {α : Type u_1} {E : Type u_3}
    [measurable_space α] [normed_group E] [measurable_space E] {f : α → E} {μ : measure α}
    {l : filter α} [filter.is_measurably_generated l] (hfm : measurable_at_filter f l)
    (hμ : measure.finite_at_filter μ l) {b : E} (hf : filter.tendsto f l (nhds b)) :
    integrable_at_filter f l :=
  measure.finite_at_filter.integrable_at_filter hfm hμ
    (filter.tendsto.is_bounded_under_le (filter.tendsto.norm hf))

theorem Mathlib.filter.tendsto.integrable_at_filter {α : Type u_1} {E : Type u_3}
    [measurable_space α] [normed_group E] [measurable_space E] {f : α → E} {μ : measure α}
    {l : filter α} [filter.is_measurably_generated l] (hfm : measurable_at_filter f l)
    (hμ : measure.finite_at_filter μ l) {b : E} (hf : filter.tendsto f l (nhds b)) :
    integrable_at_filter f l :=
  measure.finite_at_filter.integrable_at_filter_of_tendsto

theorem integrable_add {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [opens_measurable_space E] {f : α → E}
    {g : α → E} (h : set.univ ⊆ f ⁻¹' singleton 0 ∪ g ⁻¹' singleton 0) (hf : measurable f)
    (hg : measurable g) : integrable (f + g) ↔ integrable f ∧ integrable g :=
  sorry

/-- To prove something for an arbitrary integrable function in a second countable
Borel normed group, it suffices to show that
* the property holds for (multiples of) characteristic functions;
* is closed under addition;
* the set of functions in the `L¹` space for which the property holds is closed.
* the property is closed under the almost-everywhere equal relation.

It is possible to make the hypotheses in the induction steps a bit stronger, and such conditions
can be added once we need them (for example in `h_sum` it is only necessary to consider the sum of
a simple function with a multiple of a characteristic function and that the intersection
of their images is a subset of `{0}`).
-/
theorem integrable.induction {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] (P : (α → E) → Prop)
    (h_ind :
      ∀ (c : E) {s : set α},
        is_measurable s → coe_fn μ s < ⊤ → P (set.indicator s fun (_x : α) => c))
    (h_sum :
      ∀ {f g : α → E},
        set.univ ⊆ f ⁻¹' singleton 0 ∪ g ⁻¹' singleton 0 →
          integrable f → integrable g → P f → P g → P (f + g))
    (h_closed : is_closed (set_of fun (f : l1 α E μ) => P ⇑f))
    (h_ae : ∀ {f g : α → E}, filter.eventually_eq (measure.ae μ) f g → integrable f → P f → P g)
    {f : α → E} (hf : integrable f) : P f :=
  sorry

theorem set_integral_congr_ae {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {g : α → E} {s : set α} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E]
    (hs : is_measurable s)
    (h : filter.eventually (fun (x : α) => x ∈ s → f x = g x) (measure.ae μ)) :
    (integral (measure.restrict μ s) fun (x : α) => f x) =
        integral (measure.restrict μ s) fun (x : α) => g x :=
  integral_congr_ae (iff.mpr (ae_restrict_iff' hs) h)

theorem set_integral_congr {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {g : α → E} {s : set α} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E]
    (hs : is_measurable s) (h : set.eq_on f g s) :
    (integral (measure.restrict μ s) fun (x : α) => f x) =
        integral (measure.restrict μ s) fun (x : α) => g x :=
  set_integral_congr_ae hs (filter.eventually_of_forall h)

theorem integral_union {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {s : set α} {t : set α} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E]
    (hst : disjoint s t) (hs : is_measurable s) (ht : is_measurable t) (hfs : integrable_on f s)
    (hft : integrable_on f t) :
    (integral (measure.restrict μ (s ∪ t)) fun (x : α) => f x) =
        (integral (measure.restrict μ s) fun (x : α) => f x) +
          integral (measure.restrict μ t) fun (x : α) => f x :=
  sorry

theorem integral_empty {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E] :
    (integral (measure.restrict μ ∅) fun (x : α) => f x) = 0 :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl ((integral (measure.restrict μ ∅) fun (x : α) => f x) = 0))
        measure.restrict_empty))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl ((integral 0 fun (x : α) => f x) = 0))
          (integral_zero_measure fun (x : α) => f x)))
      (Eq.refl 0))

theorem integral_univ {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E] :
    (integral (measure.restrict μ set.univ) fun (x : α) => f x) = integral μ fun (x : α) => f x :=
  sorry

theorem integral_add_compl {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {s : set α} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E]
    (hs : is_measurable s) (hfi : integrable f) :
    ((integral (measure.restrict μ s) fun (x : α) => f x) +
          integral (measure.restrict μ (sᶜ)) fun (x : α) => f x) =
        integral μ fun (x : α) => f x :=
  sorry

/-- For a function `f` and a measurable set `s`, the integral of `indicator s f`
over the whole space is equal to `∫ x in s, f x ∂μ` defined as `∫ x, f x ∂(μ.restrict s)`. -/
theorem integral_indicator {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {f : α → E} {s : set α} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E]
    (hs : is_measurable s) :
    (integral μ fun (x : α) => set.indicator s f x) =
        integral (measure.restrict μ s) fun (x : α) => f x :=
  sorry

theorem set_integral_const {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {s : set α} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E] (c : E) :
    (integral (measure.restrict μ s) fun (x : α) => c) = ennreal.to_real (coe_fn μ s) • c :=
  sorry

@[simp] theorem integral_indicator_const {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E] (e : E)
    {s : set α} (s_meas : is_measurable s) :
    (integral μ fun (a : α) => set.indicator s (fun (x : α) => e) a) =
        ennreal.to_real (coe_fn μ s) • e :=
  sorry

theorem set_integral_map {α : Type u_1} {E : Type u_3} [measurable_space α] [normed_group E]
    [measurable_space E] {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E]
    {β : Type u_2} [measurable_space β] {g : α → β} {f : β → E} {s : set β} (hs : is_measurable s)
    (hf : ae_measurable f) (hg : measurable g) :
    (integral (measure.restrict (coe_fn (measure.map g) μ) s) fun (y : β) => f y) =
        integral (measure.restrict μ (g ⁻¹' s)) fun (x : α) => f (g x) :=
  sorry

theorem norm_set_integral_le_of_norm_le_const_ae {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E] {C : ℝ}
    (hs : coe_fn μ s < ⊤)
    (hC : filter.eventually (fun (x : α) => norm (f x) ≤ C) (measure.ae (measure.restrict μ s))) :
    norm (integral (measure.restrict μ s) fun (x : α) => f x) ≤ C * ennreal.to_real (coe_fn μ s) :=
  sorry

theorem norm_set_integral_le_of_norm_le_const_ae' {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E] {C : ℝ}
    (hs : coe_fn μ s < ⊤)
    (hC : filter.eventually (fun (x : α) => x ∈ s → norm (f x) ≤ C) (measure.ae μ))
    (hfm : ae_measurable f) :
    norm (integral (measure.restrict μ s) fun (x : α) => f x) ≤ C * ennreal.to_real (coe_fn μ s) :=
  sorry

theorem norm_set_integral_le_of_norm_le_const_ae'' {α : Type u_1} {E : Type u_3}
    [measurable_space α] [normed_group E] [measurable_space E] {f : α → E} {s : set α}
    {μ : measure α} [borel_space E] [topological_space.second_countable_topology E]
    [complete_space E] [normed_space ℝ E] {C : ℝ} (hs : coe_fn μ s < ⊤) (hsm : is_measurable s)
    (hC : filter.eventually (fun (x : α) => x ∈ s → norm (f x) ≤ C) (measure.ae μ)) :
    norm (integral (measure.restrict μ s) fun (x : α) => f x) ≤ C * ennreal.to_real (coe_fn μ s) :=
  sorry

theorem norm_set_integral_le_of_norm_le_const {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E] {C : ℝ}
    (hs : coe_fn μ s < ⊤) (hC : ∀ (x : α), x ∈ s → norm (f x) ≤ C) (hfm : ae_measurable f) :
    norm (integral (measure.restrict μ s) fun (x : α) => f x) ≤ C * ennreal.to_real (coe_fn μ s) :=
  norm_set_integral_le_of_norm_le_const_ae' hs (filter.eventually_of_forall hC) hfm

theorem norm_set_integral_le_of_norm_le_const' {α : Type u_1} {E : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] {f : α → E} {s : set α} {μ : measure α} [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [normed_space ℝ E] {C : ℝ}
    (hs : coe_fn μ s < ⊤) (hsm : is_measurable s) (hC : ∀ (x : α), x ∈ s → norm (f x) ≤ C) :
    norm (integral (measure.restrict μ s) fun (x : α) => f x) ≤ C * ennreal.to_real (coe_fn μ s) :=
  norm_set_integral_le_of_norm_le_const_ae'' hs hsm (filter.eventually_of_forall hC)

theorem set_integral_eq_zero_iff_of_nonneg_ae {α : Type u_1} [measurable_space α] {s : set α}
    {μ : measure α} {f : α → ℝ} (hf : filter.eventually_le (measure.ae (measure.restrict μ s)) 0 f)
    (hfi : integrable_on f s) :
    (integral (measure.restrict μ s) fun (x : α) => f x) = 0 ↔
        filter.eventually_eq (measure.ae (measure.restrict μ s)) f 0 :=
  integral_eq_zero_iff_of_nonneg_ae hf hfi

theorem set_integral_pos_iff_support_of_nonneg_ae {α : Type u_1} [measurable_space α] {s : set α}
    {μ : measure α} {f : α → ℝ} (hf : filter.eventually_le (measure.ae (measure.restrict μ s)) 0 f)
    (hfi : integrable_on f s) :
    (0 < integral (measure.restrict μ s) fun (x : α) => f x) ↔
        0 < coe_fn μ (function.support f ∩ s) :=
  sorry

end measure_theory

/-- Fundamental theorem of calculus for set integrals: if `μ` is a measure that is finite at a
filter `l` and `f` is a measurable function that has a finite limit `b` at `l ⊓ μ.ae`, then `∫ x in
s i, f x ∂μ = μ (s i) • b + o(μ (s i))` at a filter `li` provided that `s i` tends to `l.lift'
powerset` along `li`. Since `μ (s i)` is an `ennreal` number, we use `(μ (s i)).to_real` in the
actual statement.

Often there is a good formula for `(μ (s i)).to_real`, so the formalization can take an optional
argument `m` with this formula and a proof `of `(λ i, (μ (s i)).to_real) =ᶠ[li] m`. Without these
arguments, `m i = (μ (s i)).to_real` is used in the output. -/
theorem filter.tendsto.integral_sub_linear_is_o_ae {α : Type u_1} {E : Type u_3}
    [measurable_space α] {ι : Type u_5} [measurable_space E] [normed_group E] [normed_space ℝ E]
    [topological_space.second_countable_topology E] [complete_space E] [borel_space E]
    {μ : measure_theory.measure α} {l : filter α} [filter.is_measurably_generated l] {f : α → E}
    {b : E} (h : filter.tendsto f (l ⊓ measure_theory.measure.ae μ) (nhds b))
    (hfm : measurable_at_filter f l) (hμ : measure_theory.measure.finite_at_filter μ l)
    {s : ι → set α} {li : filter ι} (hs : filter.tendsto s li (filter.lift' l set.powerset))
    (m : optParam (ι → ℝ) fun (i : ι) => ennreal.to_real (coe_fn μ (s i)))
    (hsμ :
      autoParam (filter.eventually_eq li (fun (i : ι) => ennreal.to_real (coe_fn μ (s i))) m)
        (Lean.Syntax.ident Lean.SourceInfo.none
          (String.toSubstring "Mathlib.tactic.interactive.refl")
          (Lean.Name.mkStr
            (Lean.Name.mkStr
              (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "tactic")
              "interactive")
            "refl")
          [])) :
    asymptotics.is_o
        (fun (i : ι) =>
          (measure_theory.integral (measure_theory.measure.restrict μ (s i)) fun (x : α) => f x) -
            m i • b)
        m li :=
  sorry

/-- Fundamental theorem of calculus for set integrals, `nhds_within` version: if `μ` is a locally
finite measure and `f` is an almost everywhere measurable function that is continuous at a point `a`
within a measurable set `t`, then `∫ x in s i, f x ∂μ = μ (s i) • f a + o(μ (s i))` at a filter `li`
provided that `s i` tends to `(𝓝[t] a).lift' powerset` along `li`.  Since `μ (s i)` is an `ennreal`
number, we use `(μ (s i)).to_real` in the actual statement.

Often there is a good formula for `(μ (s i)).to_real`, so the formalization can take an optional
argument `m` with this formula and a proof `of `(λ i, (μ (s i)).to_real) =ᶠ[li] m`. Without these
arguments, `m i = (μ (s i)).to_real` is used in the output. -/
theorem continuous_within_at.integral_sub_linear_is_o_ae {α : Type u_1} {E : Type u_3}
    [measurable_space α] {ι : Type u_5} [measurable_space E] [normed_group E] [topological_space α]
    [opens_measurable_space α] [normed_space ℝ E] [topological_space.second_countable_topology E]
    [complete_space E] [borel_space E] {μ : measure_theory.measure α}
    [measure_theory.locally_finite_measure μ] {a : α} {t : set α} {f : α → E}
    (ha : continuous_within_at f t a) (ht : is_measurable t)
    (hfm : measurable_at_filter f (nhds_within a t)) {s : ι → set α} {li : filter ι}
    (hs : filter.tendsto s li (filter.lift' (nhds_within a t) set.powerset))
    (m : optParam (ι → ℝ) fun (i : ι) => ennreal.to_real (coe_fn μ (s i)))
    (hsμ :
      autoParam (filter.eventually_eq li (fun (i : ι) => ennreal.to_real (coe_fn μ (s i))) m)
        (Lean.Syntax.ident Lean.SourceInfo.none
          (String.toSubstring "Mathlib.tactic.interactive.refl")
          (Lean.Name.mkStr
            (Lean.Name.mkStr
              (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "tactic")
              "interactive")
            "refl")
          [])) :
    asymptotics.is_o
        (fun (i : ι) =>
          (measure_theory.integral (measure_theory.measure.restrict μ (s i)) fun (x : α) => f x) -
            m i • f a)
        m li :=
  filter.tendsto.integral_sub_linear_is_o_ae (filter.tendsto.mono_left ha inf_le_left) hfm
    (measure_theory.measure.finite_at_nhds_within μ a t) hs m

/-- Fundamental theorem of calculus for set integrals, `nhds` version: if `μ` is a locally finite
measure and `f` is an almost everywhere measurable function that is continuous at a point `a`, then
`∫ x in s i, f x ∂μ = μ (s i) • f a + o(μ (s i))` at `li` provided that `s` tends to `(𝓝 a).lift'
powerset` along `li.  Since `μ (s i)` is an `ennreal` number, we use `(μ (s i)).to_real` in the
actual statement.

Often there is a good formula for `(μ (s i)).to_real`, so the formalization can take an optional
argument `m` with this formula and a proof `of `(λ i, (μ (s i)).to_real) =ᶠ[li] m`. Without these
arguments, `m i = (μ (s i)).to_real` is used in the output. -/
theorem continuous_at.integral_sub_linear_is_o_ae {α : Type u_1} {E : Type u_3} [measurable_space α]
    {ι : Type u_5} [measurable_space E] [normed_group E] [topological_space α]
    [opens_measurable_space α] [normed_space ℝ E] [topological_space.second_countable_topology E]
    [complete_space E] [borel_space E] {μ : measure_theory.measure α}
    [measure_theory.locally_finite_measure μ] {a : α} {f : α → E} (ha : continuous_at f a)
    (hfm : measurable_at_filter f (nhds a)) {s : ι → set α} {li : filter ι}
    (hs : filter.tendsto s li (filter.lift' (nhds a) set.powerset))
    (m : optParam (ι → ℝ) fun (i : ι) => ennreal.to_real (coe_fn μ (s i)))
    (hsμ :
      autoParam (filter.eventually_eq li (fun (i : ι) => ennreal.to_real (coe_fn μ (s i))) m)
        (Lean.Syntax.ident Lean.SourceInfo.none
          (String.toSubstring "Mathlib.tactic.interactive.refl")
          (Lean.Name.mkStr
            (Lean.Name.mkStr
              (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "tactic")
              "interactive")
            "refl")
          [])) :
    asymptotics.is_o
        (fun (i : ι) =>
          (measure_theory.integral (measure_theory.measure.restrict μ (s i)) fun (x : α) => f x) -
            m i • f a)
        m li :=
  filter.tendsto.integral_sub_linear_is_o_ae (filter.tendsto.mono_left ha inf_le_left) hfm
    (measure_theory.measure.finite_at_nhds μ a) hs m

/-- If a function is integrable at `𝓝[s] x` for each point `x` of a compact set `s`, then it is
integrable on `s`. -/
theorem is_compact.integrable_on_of_nhds_within {α : Type u_1} {E : Type u_3} [measurable_space α]
    [measurable_space E] [normed_group E] [topological_space α] {μ : measure_theory.measure α}
    {s : set α} (hs : is_compact s) {f : α → E}
    (hf : ∀ (x : α), x ∈ s → measure_theory.integrable_at_filter f (nhds_within x s)) :
    measure_theory.integrable_on f s :=
  sorry

/-- A function which is continuous on a set `s` is almost everywhere measurable with respect to
`μ.restrict s`. -/
theorem continuous_on.ae_measurable {α : Type u_1} {E : Type u_3} [measurable_space α]
    [measurable_space E] [normed_group E] [topological_space α] [opens_measurable_space α]
    [borel_space E] {f : α → E} {s : set α} {μ : measure_theory.measure α} (hf : continuous_on f s)
    (hs : is_measurable s) : ae_measurable f :=
  sorry

theorem continuous_on.integrable_at_nhds_within {α : Type u_1} {E : Type u_3} [measurable_space α]
    [measurable_space E] [normed_group E] [topological_space α] [opens_measurable_space α]
    [borel_space E] {μ : measure_theory.measure α} [measure_theory.locally_finite_measure μ] {a : α}
    {t : set α} {f : α → E} (hft : continuous_on f t) (ht : is_measurable t) (ha : a ∈ t) :
    measure_theory.integrable_at_filter f (nhds_within a t) :=
  filter.tendsto.integrable_at_filter
    (Exists.intro t (Exists.intro self_mem_nhds_within (continuous_on.ae_measurable hft ht)))
    (measure_theory.measure.finite_at_nhds_within μ a t) (hft a ha)

/-- Fundamental theorem of calculus for set integrals, `nhds_within` version: if `μ` is a locally
finite measure, `f` is continuous on a measurable set `t`, and `a ∈ t`, then `∫ x in (s i), f x ∂μ =
μ (s i) • f a + o(μ (s i))` at `li` provided that `s i` tends to `(𝓝[t] a).lift' powerset` along
`li`.  Since `μ (s i)` is an `ennreal` number, we use `(μ (s i)).to_real` in the actual statement.

Often there is a good formula for `(μ (s i)).to_real`, so the formalization can take an optional
argument `m` with this formula and a proof `of `(λ i, (μ (s i)).to_real) =ᶠ[li] m`. Without these
arguments, `m i = (μ (s i)).to_real` is used in the output. -/
theorem continuous_on.integral_sub_linear_is_o_ae {α : Type u_1} {E : Type u_3} [measurable_space α]
    {ι : Type u_5} [measurable_space E] [normed_group E] [topological_space α]
    [opens_measurable_space α] [normed_space ℝ E] [topological_space.second_countable_topology E]
    [complete_space E] [borel_space E] {μ : measure_theory.measure α}
    [measure_theory.locally_finite_measure μ] {a : α} {t : set α} {f : α → E}
    (hft : continuous_on f t) (ha : a ∈ t) (ht : is_measurable t) {s : ι → set α} {li : filter ι}
    (hs : filter.tendsto s li (filter.lift' (nhds_within a t) set.powerset))
    (m : optParam (ι → ℝ) fun (i : ι) => ennreal.to_real (coe_fn μ (s i)))
    (hsμ :
      autoParam (filter.eventually_eq li (fun (i : ι) => ennreal.to_real (coe_fn μ (s i))) m)
        (Lean.Syntax.ident Lean.SourceInfo.none
          (String.toSubstring "Mathlib.tactic.interactive.refl")
          (Lean.Name.mkStr
            (Lean.Name.mkStr
              (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "tactic")
              "interactive")
            "refl")
          [])) :
    asymptotics.is_o
        (fun (i : ι) =>
          (measure_theory.integral (measure_theory.measure.restrict μ (s i)) fun (x : α) => f x) -
            m i • f a)
        m li :=
  continuous_within_at.integral_sub_linear_is_o_ae (hft a ha) ht
    (Exists.intro t (Exists.intro self_mem_nhds_within (continuous_on.ae_measurable hft ht))) hs m

/-- A function `f` continuous on a compact set `s` is integrable on this set with respect to any
locally finite measure. -/
theorem continuous_on.integrable_on_compact {α : Type u_1} {E : Type u_3} [measurable_space α]
    [measurable_space E] [normed_group E] [topological_space α] [opens_measurable_space α]
    [borel_space E] [t2_space α] {μ : measure_theory.measure α}
    [measure_theory.locally_finite_measure μ] {s : set α} (hs : is_compact s) {f : α → E}
    (hf : continuous_on f s) : measure_theory.integrable_on f s :=
  is_compact.integrable_on_of_nhds_within hs
    fun (x : α) (hx : x ∈ s) =>
      continuous_on.integrable_at_nhds_within hf (is_compact.is_measurable hs) hx

/-- A continuous function `f` is integrable on any compact set with respect to any locally finite
measure. -/
theorem continuous.integrable_on_compact {α : Type u_1} {E : Type u_3} [measurable_space α]
    [measurable_space E] [normed_group E] [topological_space α] [opens_measurable_space α]
    [t2_space α] [borel_space E] {μ : measure_theory.measure α}
    [measure_theory.locally_finite_measure μ] {s : set α} (hs : is_compact s) {f : α → E}
    (hf : continuous f) : measure_theory.integrable_on f s :=
  continuous_on.integrable_on_compact hs (continuous.continuous_on hf)

/-- A continuous function with compact closure of the support is integrable on the whole space. -/
theorem continuous.integrable_of_compact_closure_support {α : Type u_1} {E : Type u_3}
    [measurable_space α] [measurable_space E] [normed_group E] [topological_space α]
    [opens_measurable_space α] [t2_space α] [borel_space E] {μ : measure_theory.measure α}
    [measure_theory.locally_finite_measure μ] {f : α → E} (hf : continuous f)
    (hfc : is_compact (closure (function.support f))) : measure_theory.integrable f :=
  sorry

/-! ### Continuous linear maps composed with integration

The goal of this section is to prove that integration commutes with continuous linear maps.
The first step is to prove that, given a function `φ : α → E` which is measurable and integrable,
and a continuous linear map `L : E →L[ℝ] F`, the function `λ a, L(φ a)` is also measurable
and integrable. Note we cannot write this as `L ∘ φ` since the type of `L` is not an actual
function type.

The next step is translate this to `l1`, replacing the function `φ` by a term with type
`α →₁[μ] E` (an equivalence class of integrable functions).
The corresponding "composition" is `L.comp_l1 φ : α →₁[μ] F`. This is then upgraded to
a linear map `L.comp_l1ₗ : (α →₁[μ] E) →ₗ[ℝ] (α →₁[μ] F)` and a continuous linear map
`L.comp_l1L : (α →₁[μ] E) →L[ℝ] (α →₁[μ] F)`.

Then we can prove the commutation result using continuity of all relevant operations
and the result on simple functions.
-/

namespace continuous_linear_map

theorem norm_comp_l1_apply_le {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [opens_measurable_space E]
    [topological_space.second_countable_topology E] (φ : measure_theory.l1 α E μ)
    (L : continuous_linear_map ℝ E F) :
    filter.eventually (fun (a : α) => norm (coe_fn L (coe_fn φ a)) ≤ norm L * norm (coe_fn φ a))
        (measure_theory.measure.ae μ) :=
  filter.eventually_of_forall fun (a : α) => le_op_norm L (coe_fn φ a)

theorem integrable_comp {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [measurable_space F] [borel_space F]
    [opens_measurable_space E] {φ : α → E} (L : continuous_linear_map ℝ E F)
    (φ_int : measure_theory.integrable φ) :
    measure_theory.integrable fun (a : α) => coe_fn L (φ a) :=
  measure_theory.integrable.mono'
    (measure_theory.integrable.const_mul (measure_theory.integrable.norm φ_int) (norm L))
    (measurable.comp_ae_measurable (continuous_linear_map.measurable L)
      (measure_theory.integrable.ae_measurable φ_int))
    (filter.eventually_of_forall fun (a : α) => le_op_norm L (φ a))

/-- Composing `φ : α →₁[μ] E` with `L : E →L[ℝ] F`. -/
def comp_l1 {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α] [measurable_space E]
    [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E] [normed_group F]
    [normed_space ℝ F] [measurable_space F] [borel_space F] [borel_space E]
    [topological_space.second_countable_topology E] [topological_space.second_countable_topology F]
    (L : continuous_linear_map ℝ E F) (φ : measure_theory.l1 α E μ) : measure_theory.l1 α F μ :=
  measure_theory.l1.of_fun (fun (a : α) => coe_fn L (coe_fn φ a)) sorry

theorem comp_l1_apply {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [measurable_space F] [borel_space F] [borel_space E]
    [topological_space.second_countable_topology E] [topological_space.second_countable_topology F]
    (L : continuous_linear_map ℝ E F) (φ : measure_theory.l1 α E μ) :
    filter.eventually (fun (a : α) => coe_fn (comp_l1 L φ) a = coe_fn L (coe_fn φ a))
        (measure_theory.measure.ae μ) :=
  measure_theory.l1.to_fun_of_fun (fun (a : α) => coe_fn L (coe_fn φ a)) (comp_l1._proof_6 L φ)

theorem integrable_comp_l1 {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [measurable_space F] [borel_space F] [borel_space E]
    [topological_space.second_countable_topology E] (L : continuous_linear_map ℝ E F)
    (φ : measure_theory.l1 α E μ) :
    measure_theory.integrable fun (a : α) => coe_fn L (coe_fn φ a) :=
  integrable_comp L (measure_theory.l1.integrable φ)

theorem measurable_comp_l1 {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [measurable_space F] [borel_space F] [borel_space E]
    [topological_space.second_countable_topology E] (L : continuous_linear_map ℝ E F)
    (φ : measure_theory.l1 α E μ) : measurable fun (a : α) => coe_fn L (coe_fn φ a) :=
  measurable.comp (continuous_linear_map.measurable L) (measure_theory.l1.measurable φ)

theorem integral_comp_l1 {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [measurable_space F] [borel_space F] [borel_space E]
    [topological_space.second_countable_topology E] [topological_space.second_countable_topology F]
    [complete_space F] (L : continuous_linear_map ℝ E F) (φ : measure_theory.l1 α E μ) :
    (measure_theory.integral μ fun (a : α) => coe_fn (comp_l1 L φ) a) =
        measure_theory.integral μ fun (a : α) => coe_fn L (coe_fn φ a) :=
  sorry

/-- Composing `φ : α →₁[μ] E` with `L : E →L[ℝ] F`, seen as a `ℝ`-linear map on `α →₁[μ] E`. -/
def comp_l1ₗ {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α] [measurable_space E]
    [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E] [normed_group F]
    [normed_space ℝ F] [measurable_space F] [borel_space F] [borel_space E]
    [topological_space.second_countable_topology E] [topological_space.second_countable_topology F]
    (L : continuous_linear_map ℝ E F) :
    linear_map ℝ (measure_theory.l1 α E μ) (measure_theory.l1 α F μ) :=
  linear_map.mk (fun (φ : measure_theory.l1 α E μ) => comp_l1 L φ) sorry sorry

theorem norm_comp_l1_le {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [measurable_space F] [borel_space F] [borel_space E]
    [topological_space.second_countable_topology E] [topological_space.second_countable_topology F]
    (φ : measure_theory.l1 α E μ) (L : continuous_linear_map ℝ E F) :
    norm (comp_l1 L φ) ≤ norm L * norm φ :=
  sorry

/-- Composing `φ : α →₁[μ] E` with `L : E →L[ℝ] F`, seen as a continuous `ℝ`-linear map on
`α →₁[μ] E`. -/
def comp_l1L {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α] [measurable_space E]
    [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E] [normed_group F]
    [normed_space ℝ F] [measurable_space F] [borel_space F] [borel_space E]
    [topological_space.second_countable_topology E] [topological_space.second_countable_topology F]
    (L : continuous_linear_map ℝ E F) :
    continuous_linear_map ℝ (measure_theory.l1 α E μ) (measure_theory.l1 α F μ) :=
  linear_map.mk_continuous (comp_l1ₗ L) (norm L) sorry

theorem norm_compl1L_le {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [measurable_space F] [borel_space F] [borel_space E]
    [topological_space.second_countable_topology E] [topological_space.second_countable_topology F]
    (L : continuous_linear_map ℝ E F) : norm (comp_l1L L) ≤ norm L :=
  op_norm_le_bound (comp_l1L L) (norm_nonneg L)
    fun (φ : measure_theory.l1 α E μ) => norm_comp_l1_le φ L

theorem continuous_integral_comp_l1 {α : Type u_1} {E : Type u_3} {F : Type u_4}
    [measurable_space α] [measurable_space E] [normed_group E] {μ : measure_theory.measure α}
    [normed_space ℝ E] [normed_group F] [normed_space ℝ F] [measurable_space F] [borel_space F]
    [borel_space E] [topological_space.second_countable_topology E]
    [topological_space.second_countable_topology F] [complete_space F]
    (L : continuous_linear_map ℝ E F) :
    continuous
        fun (φ : measure_theory.l1 α E μ) =>
          measure_theory.integral μ fun (a : α) => coe_fn L (coe_fn φ a) :=
  sorry

theorem integral_comp_comm {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [measurable_space F] [borel_space F] [borel_space E]
    [topological_space.second_countable_topology E] [topological_space.second_countable_topology F]
    [complete_space F] [complete_space E] (L : continuous_linear_map ℝ E F) {φ : α → E}
    (φ_int : measure_theory.integrable φ) :
    (measure_theory.integral μ fun (a : α) => coe_fn L (φ a)) =
        coe_fn L (measure_theory.integral μ fun (a : α) => φ a) :=
  sorry

theorem integral_comp_l1_comm {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [measurable_space F] [borel_space F] [borel_space E]
    [topological_space.second_countable_topology E] [topological_space.second_countable_topology F]
    [complete_space F] [complete_space E] (L : continuous_linear_map ℝ E F)
    (φ : measure_theory.l1 α E μ) :
    (measure_theory.integral μ fun (a : α) => coe_fn L (coe_fn φ a)) =
        coe_fn L (measure_theory.integral μ fun (a : α) => coe_fn φ a) :=
  integral_comp_comm L (measure_theory.l1.integrable φ)

end continuous_linear_map

theorem fst_integral {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [measurable_space F]
    [borel_space F] [topological_space.second_countable_topology F] [complete_space F]
    {f : α → E × F} (hf : measure_theory.integrable f) :
    prod.fst (measure_theory.integral μ fun (x : α) => f x) =
        measure_theory.integral μ fun (x : α) => prod.fst (f x) :=
  Eq.symm (continuous_linear_map.integral_comp_comm (continuous_linear_map.fst ℝ E F) hf)

theorem snd_integral {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [measurable_space F]
    [borel_space F] [topological_space.second_countable_topology F] [complete_space F]
    {f : α → E × F} (hf : measure_theory.integrable f) :
    prod.snd (measure_theory.integral μ fun (x : α) => f x) =
        measure_theory.integral μ fun (x : α) => prod.snd (f x) :=
  Eq.symm (continuous_linear_map.integral_comp_comm (continuous_linear_map.snd ℝ E F) hf)

theorem integral_pair {α : Type u_1} {E : Type u_3} {F : Type u_4} [measurable_space α]
    [measurable_space E] [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E]
    [normed_group F] [normed_space ℝ F] [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] [measurable_space F]
    [borel_space F] [topological_space.second_countable_topology F] [complete_space F] {f : α → E}
    {g : α → F} (hf : measure_theory.integrable f) (hg : measure_theory.integrable g) :
    (measure_theory.integral μ fun (x : α) => (f x, g x)) =
        (measure_theory.integral μ fun (x : α) => f x,
        measure_theory.integral μ fun (x : α) => g x) :=
  (fun (this : measure_theory.integrable fun (x : α) => (f x, g x)) =>
      prod.ext (fst_integral this) (snd_integral this))
    (measure_theory.integrable.prod_mk hf hg)

theorem integral_smul_const {α : Type u_1} {E : Type u_3} [measurable_space α] [measurable_space E]
    [normed_group E] {μ : measure_theory.measure α} [normed_space ℝ E] [borel_space E]
    [topological_space.second_countable_topology E] [complete_space E] (f : α → ℝ) (c : E) :
    (measure_theory.integral μ fun (x : α) => f x • c) =
        (measure_theory.integral μ fun (x : α) => f x) • c :=
  sorry

end Mathlib


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

! This file was ported from Lean 3 source module topology.uniform_space.compare_reals
! leanprover-community/mathlib commit e1a7bdeb4fd826b7e71d130d34988f0a2d26a177
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Topology.UniformSpace.AbsoluteValue
import Mathlib.Topology.Instances.Real
import Mathlib.Topology.Instances.Rat
import Mathlib.Topology.UniformSpace.Completion

/-!
# Comparison of Cauchy reals and Bourbaki reals

In `Data.Real.Basic` real numbers are defined using the so called Cauchy construction (although
it is due to Georg Cantor). More precisely, this construction applies to commutative rings equipped
with an absolute value with values in a linear ordered field.

On the other hand, in the `UniformSpace` folder, we construct completions of general uniform
spaces, which allows to construct the Bourbaki real numbers. In this file we build uniformly
continuous bijections from Cauchy reals to Bourbaki reals and back. This is a cross sanity check of
both constructions. Of course those two constructions are variations on the completion idea, simply
with different level of generality. Comparing with Dedekind cuts or quasi-morphisms would be of a
completely different nature.

Note that `MetricSpace/cau_seq_filter` also relates the notions of Cauchy sequences in metric
spaces and Cauchy filters in general uniform spaces, and `MetricSpace/Completion` makes sure
the completion (as a uniform space) of a metric space is a metric space.

Historical note: mathlib used to define real numbers in an intermediate way, using completion
of uniform spaces but extending multiplication in an ad-hoc way.

TODO:
* Upgrade this isomorphism to a topological ring isomorphism.
* Do the same comparison for p-adic numbers

## Implementation notes

The heavy work is done in `Topology/UniformSpace/AbstractCompletion` which provides an abstract
caracterization of completions of uniform spaces, and isomorphisms between them. The only work left
here is to prove the uniform space structure coming from the absolute value on ℚ (with values in ℚ,
not referring to ℝ) coincides with the one coming from the metric space structure (which of course
does use ℝ).

## References

* [N. Bourbaki, *Topologie générale*][bourbaki1966]

## Tags

real numbers, completion, uniform spaces
-/

open Set Function Filter CauSeq UniformSpace

/-- The metric space uniform structure on ℚ (which presupposes the existence
of real numbers) agrees with the one coming directly from (abs : ℚ → ℚ). -/
theorem Rat.uniformSpace_eq :
    (AbsoluteValue.abs : AbsoluteValue ℚ ℚ).uniformSpace = PseudoMetricSpace.toUniformSpace := by
  ext s
  rw [(AbsoluteValue.hasBasis_uniformity _).mem_iff, Metric.uniformity_basis_dist_rat.mem_iff]
  simp only [Rat.dist_eq, AbsoluteValue.abs_apply, ← Rat.cast_sub, ← Rat.cast_abs, Rat.cast_lt,
    abs_sub_comm]
#align rat.uniform_space_eq Rat.uniformSpace_eq

/-- Cauchy reals packaged as a completion of ℚ using the absolute value route. -/
def rationalCauSeqPkg : @AbstractCompletion ℚ <| (@AbsoluteValue.abs ℚ _).uniformSpace :=
@AbstractCompletion.mk
  (space := ℝ)
  (coe := ((↑) : ℚ → ℝ))
  (uniformStruct := by infer_instance)
  (complete := by infer_instance)
  (separation := by infer_instance)
  (uniformInducing := by
    rw [Rat.uniformSpace_eq]
    exact Rat.uniformEmbedding_coe_real.toUniformInducing)
  (dense := Rat.denseEmbedding_coe_real.dense)
#align rational_cau_seq_pkg rationalCauSeqPkg

namespace CompareReals

/-- Type wrapper around ℚ to make sure the absolute value uniform space instance is picked up
instead of the metric space one. We proved in rat.uniform_space_eq that they are equal,
but they are not definitionaly equal, so it would confuse the type class system (and probably
also human readers). -/
def Q :=
  ℚ deriving CommRing, Inhabited
set_option linter.uppercaseLean3 false in
#align compare_reals.Q CompareReals.Q

instance uniformSpace : UniformSpace Q :=
  (@AbsoluteValue.abs ℚ _).uniformSpace

/-- Real numbers constructed as in Bourbaki. -/
def Bourbakiℝ : Type :=
  Completion Q deriving Inhabited
set_option linter.uppercaseLean3 false in
#align compare_reals.Bourbakiℝ CompareReals.Bourbakiℝ

instance Bourbaki.uniformSpace : UniformSpace Bourbakiℝ :=
  Completion.uniformSpace Q
#align compare_reals.bourbaki.uniform_space CompareReals.Bourbaki.uniformSpace

/-- Bourbaki reals packaged as a completion of Q using the general theory. -/
def bourbakiPkg : AbstractCompletion Q :=
  Completion.cPkg
set_option linter.uppercaseLean3 false in
#align compare_reals.Bourbaki_pkg CompareReals.bourbakiPkg

/-- The uniform bijection between Bourbaki and Cauchy reals. -/
noncomputable def compareEquiv : Bourbakiℝ ≃ᵤ ℝ :=
  bourbakiPkg.compareEquiv rationalCauSeqPkg
#align compare_reals.compare_equiv CompareReals.compareEquiv

theorem compare_uc : UniformContinuous compareEquiv :=
  bourbakiPkg.uniformContinuous_compareEquiv rationalCauSeqPkg
#align compare_reals.compare_uc CompareReals.compare_uc

theorem compare_uc_symm : UniformContinuous compareEquiv.symm :=
  bourbakiPkg.uniformContinuous_compareEquiv_symm rationalCauSeqPkg
#align compare_reals.compare_uc_symm CompareReals.compare_uc_symm

end CompareReals


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Bhavik Mehta
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.nat.factorial
import Mathlib.PostPort

namespace Mathlib

/-!
# Binomial coefficients

This file contains a definition of binomial coefficients and simple lemmas (i.e. those not
requiring more imports).

## Main definition and results

- `nat.choose`: binomial coefficients, defined inductively
- `nat.choose_eq_factorial_div_factorial`: a proof that `choose n k = n! / (k! * (n - k)!)`
- `nat.choose_symm`: symmetry of binomial coefficients
- `nat.choose_le_succ_of_lt_half_left`: `choose n k` is increasing for small values of `k`
- `nat.choose_le_middle`: `choose n r` is maximised when `r` is `n/2`

-/

namespace nat

/-- `choose n k` is the number of `k`-element subsets in an `n`-element set. Also known as binomial
coefficients. -/
def choose : ℕ → ℕ → ℕ := sorry

@[simp] theorem choose_zero_right (n : ℕ) : choose n 0 = 1 :=
  nat.cases_on n (Eq.refl (choose 0 0)) fun (n : ℕ) => Eq.refl (choose (Nat.succ n) 0)

@[simp] theorem choose_zero_succ (k : ℕ) : choose 0 (Nat.succ k) = 0 := rfl

theorem choose_succ_succ (n : ℕ) (k : ℕ) :
    choose (Nat.succ n) (Nat.succ k) = choose n k + choose n (Nat.succ k) :=
  rfl

theorem choose_eq_zero_of_lt {n : ℕ} {k : ℕ} : n < k → choose n k = 0 := sorry

@[simp] theorem choose_self (n : ℕ) : choose n n = 1 := sorry

@[simp] theorem choose_succ_self (n : ℕ) : choose n (Nat.succ n) = 0 :=
  choose_eq_zero_of_lt (lt_succ_self n)

@[simp] theorem choose_one_right (n : ℕ) : choose n 1 = n := sorry

/- The `n+1`-st triangle number is `n` more than the `n`-th triangle number -/

theorem triangle_succ (n : ℕ) : (n + 1) * (n + 1 - 1) / bit0 1 = n * (n - 1) / bit0 1 + n := sorry

/-- `choose n 2` is the `n`-th triangle number. -/
theorem choose_two_right (n : ℕ) : choose n (bit0 1) = n * (n - 1) / bit0 1 := sorry

theorem choose_pos {n : ℕ} {k : ℕ} : k ≤ n → 0 < choose n k := sorry

theorem succ_mul_choose_eq (n : ℕ) (k : ℕ) :
    Nat.succ n * choose n k = choose (Nat.succ n) (Nat.succ k) * Nat.succ k :=
  sorry

theorem choose_mul_factorial_mul_factorial {n : ℕ} {k : ℕ} :
    k ≤ n → choose n k * factorial k * factorial (n - k) = factorial n :=
  sorry

theorem choose_eq_factorial_div_factorial {n : ℕ} {k : ℕ} (hk : k ≤ n) :
    choose n k = factorial n / (factorial k * factorial (n - k)) :=
  sorry

theorem factorial_mul_factorial_dvd_factorial {n : ℕ} {k : ℕ} (hk : k ≤ n) :
    factorial k * factorial (n - k) ∣ factorial n :=
  sorry

@[simp] theorem choose_symm {n : ℕ} {k : ℕ} (hk : k ≤ n) : choose n (n - k) = choose n k := sorry

theorem choose_symm_of_eq_add {n : ℕ} {a : ℕ} {b : ℕ} (h : n = a + b) : choose n a = choose n b :=
  sorry

theorem choose_symm_add {a : ℕ} {b : ℕ} : choose (a + b) a = choose (a + b) b :=
  choose_symm_of_eq_add rfl

theorem choose_symm_half (m : ℕ) : choose (bit0 1 * m + 1) (m + 1) = choose (bit0 1 * m + 1) m :=
  sorry

theorem choose_succ_right_eq (n : ℕ) (k : ℕ) : choose n (k + 1) * (k + 1) = choose n k * (n - k) :=
  sorry

@[simp] theorem choose_succ_self_right (n : ℕ) : choose (n + 1) n = n + 1 := sorry

theorem choose_mul_succ_eq (n : ℕ) (k : ℕ) :
    choose n k * (n + 1) = choose (n + 1) k * (n + 1 - k) :=
  sorry

/-! ### Inequalities -/

/-- Show that `nat.choose` is increasing for small values of the right argument. -/
theorem choose_le_succ_of_lt_half_left {r : ℕ} {n : ℕ} (h : r < n / bit0 1) :
    choose n r ≤ choose n (r + 1) :=
  sorry

/-- Show that for small values of the right argument, the middle value is largest. -/
/-- `choose n r` is maximised when `r` is `n/2`. -/
theorem choose_le_middle (r : ℕ) (n : ℕ) : choose n r ≤ choose n (n / bit0 1) := sorry

end Mathlib


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov, Frédéric Dupuis, Heather Macbeth
-/
import analysis.normed.group.basic
import topology.algebra.module.basic
import linear_algebra.basis

/-!
# (Semi-)linear isometries

In this file we define `linear_isometry σ₁₂ E E₂` (notation: `E →ₛₗᵢ[σ₁₂] E₂`) to be a semilinear
isometric embedding of `E` into `E₂` and `linear_isometry_equiv` (notation: `E ≃ₛₗᵢ[σ₁₂] E₂`) to be
a semilinear isometric equivalence between `E` and `E₂`.  The notation for the associated purely
linear concepts is `E →ₗᵢ[R] E₂`, `E ≃ₗᵢ[R] E₂`, and `E →ₗᵢ⋆[R] E₂`, `E ≃ₗᵢ⋆[R] E₂` for
the star-linear versions.

We also prove some trivial lemmas and provide convenience constructors.

Since a lot of elementary properties don't require `‖x‖ = 0 → x = 0` we start setting up the
theory for `seminormed_add_comm_group` and we specialize to `normed_add_comm_group` when needed.
-/
open function set

variables {R R₂ R₃ R₄ E E₂ E₃ E₄ F 𝓕 : Type*} [semiring R] [semiring R₂] [semiring R₃] [semiring R₄]
  {σ₁₂ : R →+* R₂} {σ₂₁ : R₂ →+* R} {σ₁₃ : R →+* R₃} {σ₃₁ : R₃ →+* R} {σ₁₄ : R →+* R₄}
  {σ₄₁ : R₄ →+* R} {σ₂₃ : R₂ →+* R₃} {σ₃₂ : R₃ →+* R₂} {σ₂₄ : R₂ →+* R₄} {σ₄₂ : R₄ →+* R₂}
  {σ₃₄ : R₃ →+* R₄} {σ₄₃ : R₄ →+* R₃}
  [ring_hom_inv_pair σ₁₂ σ₂₁] [ring_hom_inv_pair σ₂₁ σ₁₂]
  [ring_hom_inv_pair σ₁₃ σ₃₁] [ring_hom_inv_pair σ₃₁ σ₁₃]
  [ring_hom_inv_pair σ₂₃ σ₃₂] [ring_hom_inv_pair σ₃₂ σ₂₃]
  [ring_hom_inv_pair σ₁₄ σ₄₁] [ring_hom_inv_pair σ₄₁ σ₁₄]
  [ring_hom_inv_pair σ₂₄ σ₄₂] [ring_hom_inv_pair σ₄₂ σ₂₄]
  [ring_hom_inv_pair σ₃₄ σ₄₃] [ring_hom_inv_pair σ₄₃ σ₃₄]
  [ring_hom_comp_triple σ₁₂ σ₂₃ σ₁₃] [ring_hom_comp_triple σ₁₂ σ₂₄ σ₁₄]
  [ring_hom_comp_triple σ₂₃ σ₃₄ σ₂₄] [ring_hom_comp_triple σ₁₃ σ₃₄ σ₁₄]
  [ring_hom_comp_triple σ₃₂ σ₂₁ σ₃₁] [ring_hom_comp_triple σ₄₂ σ₂₁ σ₄₁]
  [ring_hom_comp_triple σ₄₃ σ₃₂ σ₄₂] [ring_hom_comp_triple σ₄₃ σ₃₁ σ₄₁]
  [seminormed_add_comm_group E] [seminormed_add_comm_group E₂] [seminormed_add_comm_group E₃]
  [seminormed_add_comm_group E₄] [module R E] [module R₂ E₂] [module R₃ E₃] [module R₄ E₄]
  [normed_add_comm_group F] [module R F]

/-- A `σ₁₂`-semilinear isometric embedding of a normed `R`-module into an `R₂`-module. -/
structure linear_isometry (σ₁₂ : R →+* R₂) (E E₂ : Type*) [seminormed_add_comm_group E]
  [seminormed_add_comm_group E₂] [module R E] [module R₂ E₂] extends E →ₛₗ[σ₁₂] E₂ :=
(norm_map' : ∀ x, ‖to_linear_map x‖ = ‖x‖)

notation E ` →ₛₗᵢ[`:25 σ₁₂:25 `] `:0 E₂:0 := linear_isometry σ₁₂ E E₂
notation E ` →ₗᵢ[`:25 R:25 `] `:0 E₂:0 := linear_isometry (ring_hom.id R) E E₂
notation E ` →ₗᵢ⋆[`:25 R:25 `] `:0 E₂:0 := linear_isometry (star_ring_end R) E E₂

set_option old_structure_cmd true
/-- `semilinear_isometry_class F σ E E₂` asserts `F` is a type of bundled `σ`-semilinear isometries
`E → E₂`.

See also `linear_isometry_class F R E E₂` for the case where `σ` is the identity map on `R`.

A map `f` between an `R`-module and an `S`-module over a ring homomorphism `σ : R →+* S`
is semilinear if it satisfies the two properties `f (x + y) = f x + f y` and
`f (c • x) = (σ c) • f x`. -/
class semilinear_isometry_class (𝓕 : Type*) {R R₂ : out_param Type*} [semiring R] [semiring R₂]
  (σ₁₂ : out_param $ R →+* R₂) (E E₂ : out_param Type*) [seminormed_add_comm_group E]
  [seminormed_add_comm_group E₂] [module R E] [module R₂ E₂]
  extends semilinear_map_class 𝓕 σ₁₂ E E₂ :=
(norm_map : ∀ (f : 𝓕) (x : E), ‖f x‖ = ‖x‖)

/-- `linear_isometry_class F R E E₂` asserts `F` is a type of bundled `R`-linear isometries
`M → M₂`.

This is an abbreviation for `semilinear_isometry_class F (ring_hom.id R) E E₂`.
-/
abbreviation linear_isometry_class (𝓕 : Type*) (R E E₂ : out_param Type*) [semiring R]
  [seminormed_add_comm_group E] [seminormed_add_comm_group E₂] [module R E] [module R E₂] :=
semilinear_isometry_class 𝓕 (ring_hom.id R) E E₂

set_option old_structure_cmd false

namespace semilinear_isometry_class

protected lemma isometry [semilinear_isometry_class 𝓕 σ₁₂ E E₂] (f : 𝓕) : isometry f :=
add_monoid_hom_class.isometry_of_norm _ (norm_map _)

@[continuity] protected lemma continuous [semilinear_isometry_class 𝓕 σ₁₂ E E₂] (f : 𝓕) :
  continuous f :=
(semilinear_isometry_class.isometry f).continuous

@[simp] lemma nnnorm_map [semilinear_isometry_class 𝓕 σ₁₂ E E₂] (f : 𝓕) (x : E) :
  ‖f x‖₊ = ‖x‖₊ :=
nnreal.eq $ norm_map f x

protected lemma lipschitz [semilinear_isometry_class 𝓕 σ₁₂ E E₂] (f : 𝓕) :
  lipschitz_with 1 f :=
(semilinear_isometry_class.isometry f).lipschitz

protected lemma antilipschitz [semilinear_isometry_class 𝓕 σ₁₂ E E₂] (f : 𝓕) :
  antilipschitz_with 1 f :=
(semilinear_isometry_class.isometry f).antilipschitz

lemma ediam_image [semilinear_isometry_class 𝓕 σ₁₂ E E₂] (f : 𝓕) (s : set E) :
  emetric.diam (f '' s) = emetric.diam s :=
(semilinear_isometry_class.isometry f).ediam_image s

lemma ediam_range [semilinear_isometry_class 𝓕 σ₁₂ E E₂] (f : 𝓕) :
  emetric.diam (range f) = emetric.diam (univ : set E) :=
(semilinear_isometry_class.isometry f).ediam_range

lemma diam_image [semilinear_isometry_class 𝓕 σ₁₂ E E₂] (f : 𝓕) (s : set E) :
  metric.diam (f '' s) = metric.diam s :=
(semilinear_isometry_class.isometry f).diam_image s

lemma diam_range [semilinear_isometry_class 𝓕 σ₁₂ E E₂] (f : 𝓕) :
  metric.diam (range f) = metric.diam (univ : set E) :=
(semilinear_isometry_class.isometry f).diam_range

@[priority 100]
instance [s : semilinear_isometry_class 𝓕 σ₁₂ E E₂] : continuous_semilinear_map_class 𝓕 σ₁₂ E E₂ :=
{ map_continuous := semilinear_isometry_class.continuous,
  ..s }

end semilinear_isometry_class

namespace linear_isometry

/-- We use `f₁` when we need the domain to be a `normed_space`. -/
variables (f : E →ₛₗᵢ[σ₁₂] E₂) (f₁ : F →ₛₗᵢ[σ₁₂] E₂)

lemma to_linear_map_injective : injective (to_linear_map : (E →ₛₗᵢ[σ₁₂] E₂) → (E →ₛₗ[σ₁₂] E₂))
| ⟨f, _⟩ ⟨g, _⟩ rfl := rfl

@[simp] lemma to_linear_map_inj {f g : E →ₛₗᵢ[σ₁₂] E₂} :
  f.to_linear_map = g.to_linear_map ↔ f = g := to_linear_map_injective.eq_iff

instance : semilinear_isometry_class (E →ₛₗᵢ[σ₁₂] E₂) σ₁₂ E E₂ :=
{ coe := λ f, f.to_fun,
  coe_injective' := λ f g h, to_linear_map_injective (fun_like.coe_injective h),
  map_add := λ f, map_add f.to_linear_map,
  map_smulₛₗ := λ f, map_smulₛₗ f.to_linear_map,
  norm_map := λ f, f.norm_map' }

/-- Helper instance for when there's too many metavariables to apply `fun_like.has_coe_to_fun`
directly.
-/
instance : has_coe_to_fun (E →ₛₗᵢ[σ₁₂] E₂) (λ _, E → E₂) := ⟨λ f, f.to_fun⟩

@[simp] lemma coe_to_linear_map : ⇑f.to_linear_map = f := rfl

@[simp] lemma coe_mk (f : E →ₛₗ[σ₁₂] E₂) (hf) : ⇑(mk f hf) = f := rfl

lemma coe_injective : @injective (E →ₛₗᵢ[σ₁₂] E₂) (E → E₂) coe_fn :=
fun_like.coe_injective

/-- See Note [custom simps projection]. We need to specify this projection explicitly in this case,
  because it is a composition of multiple projections. -/
def simps.apply (σ₁₂ : R →+* R₂) (E E₂ : Type*) [seminormed_add_comm_group E]
  [seminormed_add_comm_group E₂] [module R E] [module R₂ E₂] (h : E →ₛₗᵢ[σ₁₂] E₂) : E → E₂ := h

initialize_simps_projections linear_isometry (to_linear_map_to_fun → apply)

@[ext] lemma ext {f g : E →ₛₗᵢ[σ₁₂] E₂} (h : ∀ x, f x = g x) : f = g :=
coe_injective $ funext h

protected lemma congr_arg [semilinear_isometry_class 𝓕 σ₁₂ E E₂] {f : 𝓕} :
  Π {x x' : E}, x = x' → f x = f x'
| _ _ rfl := rfl

protected lemma congr_fun [semilinear_isometry_class 𝓕 σ₁₂ E E₂] {f g : 𝓕} (h : f = g) (x : E) :
  f x = g x := h ▸ rfl

@[simp] protected lemma map_zero : f 0 = 0 := f.to_linear_map.map_zero

@[simp] protected lemma map_add (x y : E) : f (x + y) = f x + f y := f.to_linear_map.map_add x y

@[simp] protected lemma map_neg (x : E) : f (- x) = - f x := f.to_linear_map.map_neg x

@[simp] protected lemma map_sub (x y : E) : f (x - y) = f x - f y := f.to_linear_map.map_sub x y

@[simp] protected lemma map_smulₛₗ (c : R) (x : E) : f (c • x) = σ₁₂ c • f x :=
f.to_linear_map.map_smulₛₗ c x

@[simp] protected lemma map_smul [module R E₂] (f : E →ₗᵢ[R] E₂) (c : R) (x : E) :
  f (c • x) = c • f x :=
f.to_linear_map.map_smul c x

@[simp] lemma norm_map (x : E) : ‖f x‖ = ‖x‖ := semilinear_isometry_class.norm_map f x

@[simp] lemma nnnorm_map (x : E) : ‖f x‖₊ = ‖x‖₊ := nnreal.eq $ norm_map f x

protected lemma isometry : isometry f := add_monoid_hom_class.isometry_of_norm _ (norm_map _)

@[simp] lemma is_complete_image_iff [semilinear_isometry_class 𝓕 σ₁₂ E E₂] (f : 𝓕) {s : set E} :
  is_complete (f '' s) ↔ is_complete s :=
is_complete_image_iff (semilinear_isometry_class.isometry f).uniform_inducing

lemma is_complete_map_iff [ring_hom_surjective σ₁₂] {p : submodule R E} :
  is_complete (p.map f.to_linear_map : set E₂) ↔ is_complete (p : set E) :=
f.is_complete_image_iff

instance complete_space_map [semilinear_isometry_class 𝓕 σ₁₂ E E₂] (f : 𝓕) [ring_hom_surjective σ₁₂]
  (p : submodule R E) [complete_space p] : complete_space (p.map f) :=
((is_complete_map_iff' f).2 $ complete_space_coe_iff_is_complete.1 ‹_›).complete_space_coe

instance complete_space_map' [ring_hom_surjective σ₁₂] (p : submodule R E) [complete_space p] :
  complete_space (p.map f.to_linear_map) :=
(f.is_complete_map_iff.2 $ complete_space_coe_iff_is_complete.1 ‹_›).complete_space_coe

@[simp] lemma dist_map (x y : E) : dist (f x) (f y) = dist x y := f.isometry.dist_eq x y
@[simp] lemma edist_map (x y : E) : edist (f x) (f y) = edist x y := f.isometry.edist_eq x y

protected lemma injective : injective f₁ := isometry.injective (linear_isometry.isometry f₁)

@[simp] lemma map_eq_iff {x y : F} : f₁ x = f₁ y ↔ x = y := f₁.injective.eq_iff

lemma map_ne {x y : F} (h : x ≠ y) : f₁ x ≠ f₁ y := f₁.injective.ne h

protected lemma lipschitz : lipschitz_with 1 f := f.isometry.lipschitz

protected lemma antilipschitz : antilipschitz_with 1 f := f.isometry.antilipschitz

@[continuity] protected lemma continuous : continuous f := f.isometry.continuous

@[simp] lemma preimage_ball (x : E) (r : ℝ) :
  f ⁻¹' (metric.ball (f x) r) = metric.ball x r :=
f.isometry.preimage_ball x r

@[simp] lemma preimage_sphere (x : E) (r : ℝ) :
  f ⁻¹' (metric.sphere (f x) r) = metric.sphere x r :=
f.isometry.preimage_sphere x r

@[simp] lemma preimage_closed_ball (x : E) (r : ℝ) :
  f ⁻¹' (metric.closed_ball (f x) r) = metric.closed_ball x r :=
f.isometry.preimage_closed_ball x r

lemma ediam_image (s : set E) : emetric.diam (f '' s) = emetric.diam s :=
f.isometry.ediam_image s

lemma ediam_range : emetric.diam (range f) = emetric.diam (univ : set E) :=
f.isometry.ediam_range

lemma diam_image (s : set E) : metric.diam (f '' s) = metric.diam s :=
isometry.diam_image (linear_isometry.isometry f) s

lemma diam_range : metric.diam (range f) = metric.diam (univ : set E) :=
isometry.diam_range (linear_isometry.isometry f)

/-- Interpret a linear isometry as a continuous linear map. -/
def to_continuous_linear_map : E →SL[σ₁₂] E₂ := ⟨f.to_linear_map, f.continuous⟩

lemma to_continuous_linear_map_injective :
  function.injective (to_continuous_linear_map : _ → E →SL[σ₁₂] E₂) :=
λ x y h, coe_injective (congr_arg _ h : ⇑x.to_continuous_linear_map = _)

@[simp] lemma to_continuous_linear_map_inj {f g : E →ₛₗᵢ[σ₁₂] E₂} :
  f.to_continuous_linear_map = g.to_continuous_linear_map ↔ f = g :=
to_continuous_linear_map_injective.eq_iff

@[simp] lemma coe_to_continuous_linear_map : ⇑f.to_continuous_linear_map = f := rfl

@[simp] lemma comp_continuous_iff {α : Type*} [topological_space α] {g : α → E} :
  continuous (f ∘ g) ↔ continuous g :=
f.isometry.comp_continuous_iff

/-- The identity linear isometry. -/
def id : E →ₗᵢ[R] E := ⟨linear_map.id, λ x, rfl⟩

@[simp] lemma coe_id : ((id : E →ₗᵢ[R] E) : E → E) = _root_.id := rfl

@[simp] lemma id_apply (x : E) : (id : E →ₗᵢ[R] E) x = x := rfl

@[simp] lemma id_to_linear_map : (id.to_linear_map : E →ₗ[R] E) = linear_map.id := rfl

@[simp] lemma id_to_continuous_linear_map :
  id.to_continuous_linear_map = continuous_linear_map.id R E := rfl

instance : inhabited (E →ₗᵢ[R] E) := ⟨id⟩

/-- Composition of linear isometries. -/
def comp (g : E₂ →ₛₗᵢ[σ₂₃] E₃) (f : E →ₛₗᵢ[σ₁₂] E₂) : E →ₛₗᵢ[σ₁₃] E₃ :=
⟨g.to_linear_map.comp f.to_linear_map, λ x, (norm_map g _).trans (norm_map f _)⟩

include σ₁₃
@[simp] lemma coe_comp (g : E₂ →ₛₗᵢ[σ₂₃] E₃) (f : E →ₛₗᵢ[σ₁₂] E₂) :
  ⇑(g.comp f) = g ∘ f :=
rfl
omit σ₁₃

@[simp] lemma id_comp : (id : E₂ →ₗᵢ[R₂] E₂).comp f = f := ext $ λ x, rfl

@[simp] lemma comp_id : f.comp id = f := ext $ λ x, rfl

include σ₁₃ σ₂₄ σ₁₄
lemma comp_assoc (f : E₃ →ₛₗᵢ[σ₃₄] E₄) (g : E₂ →ₛₗᵢ[σ₂₃] E₃) (h : E →ₛₗᵢ[σ₁₂] E₂) :
  (f.comp g).comp h = f.comp (g.comp h) :=
rfl
omit σ₁₃ σ₂₄ σ₁₄

instance : monoid (E →ₗᵢ[R] E) :=
{ one := id,
  mul := comp,
  mul_assoc := comp_assoc,
  one_mul := id_comp,
  mul_one := comp_id }

@[simp] lemma coe_one : ((1 : E →ₗᵢ[R] E) : E → E) = _root_.id := rfl
@[simp] lemma coe_mul (f g : E →ₗᵢ[R] E) : ⇑(f * g) = f ∘ g := rfl

lemma one_def : (1 : E →ₗᵢ[R] E) = id := rfl
lemma mul_def (f g : E →ₗᵢ[R] E) : (f * g : E →ₗᵢ[R] E) = f.comp g := rfl

end linear_isometry

/-- Construct a `linear_isometry` from a `linear_map` satisfying `isometry`. -/
def linear_map.to_linear_isometry (f : E →ₛₗ[σ₁₂] E₂) (hf : isometry f) : E →ₛₗᵢ[σ₁₂] E₂ :=
{ norm_map' := by { simp_rw [←dist_zero_right, ←f.map_zero], exact λ x, hf.dist_eq x _ },
  .. f }

namespace submodule

variables {R' : Type*} [ring R'] [module R' E] (p : submodule R' E)

/-- `submodule.subtype` as a `linear_isometry`. -/
def subtypeₗᵢ : p →ₗᵢ[R'] E := ⟨p.subtype, λ x, rfl⟩

@[simp] lemma coe_subtypeₗᵢ : ⇑p.subtypeₗᵢ = p.subtype := rfl

@[simp] lemma subtypeₗᵢ_to_linear_map : p.subtypeₗᵢ.to_linear_map = p.subtype := rfl

@[simp] lemma subtypeₗᵢ_to_continuous_linear_map :
  p.subtypeₗᵢ.to_continuous_linear_map = p.subtypeL := rfl

end submodule

/-- A semilinear isometric equivalence between two normed vector spaces. -/
structure linear_isometry_equiv (σ₁₂ : R →+* R₂) {σ₂₁ : R₂ →+* R} [ring_hom_inv_pair σ₁₂ σ₂₁]
  [ring_hom_inv_pair σ₂₁ σ₁₂] (E E₂ : Type*) [seminormed_add_comm_group E]
  [seminormed_add_comm_group E₂] [module R E] [module R₂ E₂] extends E ≃ₛₗ[σ₁₂] E₂ :=
(norm_map' : ∀ x, ‖to_linear_equiv x‖ = ‖x‖)

notation E ` ≃ₛₗᵢ[`:25 σ₁₂:25 `] `:0 E₂:0 := linear_isometry_equiv σ₁₂ E E₂
notation E ` ≃ₗᵢ[`:25 R:25 `] `:0 E₂:0 := linear_isometry_equiv (ring_hom.id R) E E₂
notation E ` ≃ₗᵢ⋆[`:25 R:25 `] `:0 E₂:0 :=
  linear_isometry_equiv (star_ring_end R) E E₂

set_option old_structure_cmd true
/-- `semilinear_isometry_equiv_class F σ E E₂` asserts `F` is a type of bundled `σ`-semilinear
isometric equivs `E → E₂`.

See also `linear_isometry_equiv_class F R E E₂` for the case where `σ` is the identity map on `R`.

A map `f` between an `R`-module and an `S`-module over a ring homomorphism `σ : R →+* S`
is semilinear if it satisfies the two properties `f (x + y) = f x + f y` and
`f (c • x) = (σ c) • f x`. -/
class semilinear_isometry_equiv_class (𝓕 : Type*) {R R₂ : out_param Type*}
  [semiring R] [semiring R₂] (σ₁₂ : out_param $ R →+* R₂) {σ₂₁ : out_param $ R₂ →+* R}
  [ring_hom_inv_pair σ₁₂ σ₂₁] [ring_hom_inv_pair σ₂₁ σ₁₂] (E E₂ : out_param Type*)
  [seminormed_add_comm_group E] [seminormed_add_comm_group E₂] [module R E] [module R₂ E₂]
  extends semilinear_equiv_class 𝓕 σ₁₂ E E₂ :=
(norm_map : ∀ (f : 𝓕) (x : E), ‖f x‖ = ‖x‖)

/-- `linear_isometry_equiv_class F R E E₂` asserts `F` is a type of bundled `R`-linear isometries
`M → M₂`.

This is an abbreviation for `semilinear_isometry_equiv_class F (ring_hom.id R) E E₂`.
-/
abbreviation linear_isometry_equiv_class (𝓕 : Type*) (R E E₂ : out_param Type*) [semiring R]
  [seminormed_add_comm_group E] [seminormed_add_comm_group E₂] [module R E] [module R E₂] :=
semilinear_isometry_equiv_class 𝓕 (ring_hom.id R) E E₂

set_option old_structure_cmd false

namespace semilinear_isometry_equiv_class
variables (𝓕)

include σ₂₁
-- `σ₂₁` becomes a metavariable, but it's OK since it's an outparam
@[priority 100, nolint dangerous_instance]
instance [s : semilinear_isometry_equiv_class 𝓕 σ₁₂ E E₂] : semilinear_isometry_class 𝓕 σ₁₂ E E₂ :=
{ coe := (coe : 𝓕 → E → E₂),
  coe_injective' := @fun_like.coe_injective 𝓕 _ _ _,
  ..s }
omit σ₂₁

end semilinear_isometry_equiv_class

namespace linear_isometry_equiv

variables (e : E ≃ₛₗᵢ[σ₁₂] E₂)

include σ₂₁

lemma to_linear_equiv_injective : injective (to_linear_equiv : (E ≃ₛₗᵢ[σ₁₂] E₂) → (E ≃ₛₗ[σ₁₂] E₂))
| ⟨e, _⟩ ⟨_, _⟩ rfl := rfl

@[simp] lemma to_linear_equiv_inj {f g : E ≃ₛₗᵢ[σ₁₂] E₂} :
  f.to_linear_equiv = g.to_linear_equiv ↔ f = g :=
to_linear_equiv_injective.eq_iff

instance : semilinear_isometry_equiv_class (E ≃ₛₗᵢ[σ₁₂] E₂) σ₁₂ E E₂ :=
{ coe := λ e, e.to_fun,
  inv := λ e, e.inv_fun,
  coe_injective' := λ f g h₁ h₂,
    by { cases f with f' _, cases g with g' _, cases f', cases g', congr', },
  left_inv := λ e, e.left_inv,
  right_inv := λ e, e.right_inv,
  map_add := λ f, map_add f.to_linear_equiv,
  map_smulₛₗ := λ e, map_smulₛₗ e.to_linear_equiv,
  norm_map := λ e, e.norm_map' }

/-- Helper instance for when there's too many metavariables to apply `fun_like.has_coe_to_fun`
directly.
-/
instance : has_coe_to_fun (E ≃ₛₗᵢ[σ₁₂] E₂) (λ _, E → E₂) := ⟨λ f, f.to_fun⟩

lemma coe_injective : @function.injective (E ≃ₛₗᵢ[σ₁₂] E₂) (E → E₂) coe_fn :=
fun_like.coe_injective

@[simp] lemma coe_mk (e : E ≃ₛₗ[σ₁₂] E₂) (he : ∀ x, ‖e x‖ = ‖x‖) :
  ⇑(mk e he) = e :=
rfl

@[simp] lemma coe_to_linear_equiv (e : E ≃ₛₗᵢ[σ₁₂] E₂) : ⇑e.to_linear_equiv = e := rfl

@[ext] lemma ext {e e' : E ≃ₛₗᵢ[σ₁₂] E₂} (h : ∀ x, e x = e' x) : e = e' :=
to_linear_equiv_injective $ linear_equiv.ext h

protected lemma congr_arg {f : E ≃ₛₗᵢ[σ₁₂] E₂} : Π {x x' : E}, x = x' → f x = f x'
| _ _ rfl := rfl

protected lemma congr_fun {f g : E ≃ₛₗᵢ[σ₁₂] E₂} (h : f = g) (x : E) : f x = g x := h ▸ rfl

/-- Construct a `linear_isometry_equiv` from a `linear_equiv` and two inequalities:
`∀ x, ‖e x‖ ≤ ‖x‖` and `∀ y, ‖e.symm y‖ ≤ ‖y‖`. -/
def of_bounds (e : E ≃ₛₗ[σ₁₂] E₂) (h₁ : ∀ x, ‖e x‖ ≤ ‖x‖) (h₂ : ∀ y, ‖e.symm y‖ ≤ ‖y‖) :
  E ≃ₛₗᵢ[σ₁₂] E₂ :=
⟨e, λ x, le_antisymm (h₁ x) $ by simpa only [e.symm_apply_apply] using h₂ (e x)⟩

@[simp] lemma norm_map (x : E) : ‖e x‖ = ‖x‖ := e.norm_map' x

/-- Reinterpret a `linear_isometry_equiv` as a `linear_isometry`. -/
def to_linear_isometry : E →ₛₗᵢ[σ₁₂] E₂ := ⟨e.1, e.2⟩

lemma to_linear_isometry_injective :
  function.injective (to_linear_isometry : _ → E →ₛₗᵢ[σ₁₂] E₂) :=
λ x y h, coe_injective (congr_arg _ h : ⇑x.to_linear_isometry = _)

@[simp] lemma to_linear_isometry_inj {f g : E ≃ₛₗᵢ[σ₁₂] E₂} :
  f.to_linear_isometry = g.to_linear_isometry ↔ f = g :=
to_linear_isometry_injective.eq_iff

@[simp] lemma coe_to_linear_isometry : ⇑e.to_linear_isometry = e := rfl

protected lemma isometry : isometry e := e.to_linear_isometry.isometry

/-- Reinterpret a `linear_isometry_equiv` as an `isometry_equiv`. -/
def to_isometry_equiv : E ≃ᵢ E₂ := ⟨e.to_linear_equiv.to_equiv, e.isometry⟩

lemma to_isometry_equiv_injective :
  function.injective (to_isometry_equiv : (E ≃ₛₗᵢ[σ₁₂] E₂) → E ≃ᵢ E₂) :=
λ x y h, coe_injective (congr_arg _ h : ⇑x.to_isometry_equiv = _)

@[simp] lemma to_isometry_equiv_inj {f g : E ≃ₛₗᵢ[σ₁₂] E₂} :
  f.to_isometry_equiv = g.to_isometry_equiv ↔ f = g :=
to_isometry_equiv_injective.eq_iff

@[simp] lemma coe_to_isometry_equiv : ⇑e.to_isometry_equiv = e := rfl

lemma range_eq_univ (e : E ≃ₛₗᵢ[σ₁₂] E₂) : set.range e = set.univ :=
by { rw ← coe_to_isometry_equiv, exact isometry_equiv.range_eq_univ _, }

/-- Reinterpret a `linear_isometry_equiv` as an `homeomorph`. -/
def to_homeomorph : E ≃ₜ E₂ := e.to_isometry_equiv.to_homeomorph

lemma to_homeomorph_injective :
  function.injective (to_homeomorph : (E ≃ₛₗᵢ[σ₁₂] E₂) → E ≃ₜ E₂) :=
λ x y h, coe_injective (congr_arg _ h : ⇑x.to_homeomorph = _)

@[simp] lemma to_homeomorph_inj {f g : E ≃ₛₗᵢ[σ₁₂] E₂} :
  f.to_homeomorph = g.to_homeomorph ↔ f = g :=
to_homeomorph_injective.eq_iff

@[simp] lemma coe_to_homeomorph : ⇑e.to_homeomorph = e := rfl

protected lemma continuous : continuous e := e.isometry.continuous
protected lemma continuous_at {x} : continuous_at e x := e.continuous.continuous_at
protected lemma continuous_on {s} : continuous_on e s := e.continuous.continuous_on

protected lemma continuous_within_at {s x} : continuous_within_at e s x :=
e.continuous.continuous_within_at

/-- Interpret a `linear_isometry_equiv` as a continuous linear equiv. -/
def to_continuous_linear_equiv : E ≃SL[σ₁₂] E₂ :=
{ .. e.to_linear_isometry.to_continuous_linear_map,
  .. e.to_homeomorph }

lemma to_continuous_linear_equiv_injective :
  function.injective (to_continuous_linear_equiv : _ → E ≃SL[σ₁₂] E₂) :=
λ x y h, coe_injective (congr_arg _ h : ⇑x.to_continuous_linear_equiv = _)

@[simp] lemma to_continuous_linear_equiv_inj {f g : E ≃ₛₗᵢ[σ₁₂] E₂} :
  f.to_continuous_linear_equiv = g.to_continuous_linear_equiv ↔ f = g :=
to_continuous_linear_equiv_injective.eq_iff

@[simp] lemma coe_to_continuous_linear_equiv : ⇑e.to_continuous_linear_equiv = e := rfl

omit σ₂₁

variables (R E)

/-- Identity map as a `linear_isometry_equiv`. -/
def refl : E ≃ₗᵢ[R] E := ⟨linear_equiv.refl R E, λ x, rfl⟩

/-- Linear isometry equiv between a space and its lift to another universe. -/
def ulift : ulift E ≃ₗᵢ[R] E :=
{ norm_map' := λ x, rfl,
  .. continuous_linear_equiv.ulift }

variables {R E}

instance : inhabited (E ≃ₗᵢ[R] E) := ⟨refl R E⟩

@[simp] lemma coe_refl : ⇑(refl R E) = id := rfl

/-- The inverse `linear_isometry_equiv`. -/
def symm : E₂ ≃ₛₗᵢ[σ₂₁] E :=
⟨e.to_linear_equiv.symm,
  λ x, (e.norm_map _).symm.trans $ congr_arg norm $ e.to_linear_equiv.apply_symm_apply x⟩

@[simp] lemma apply_symm_apply (x : E₂) : e (e.symm x) = x := e.to_linear_equiv.apply_symm_apply x
@[simp] lemma symm_apply_apply (x : E) : e.symm (e x) = x := e.to_linear_equiv.symm_apply_apply x
@[simp] lemma map_eq_zero_iff {x : E} : e x = 0 ↔ x = 0 := e.to_linear_equiv.map_eq_zero_iff
@[simp] lemma symm_symm : e.symm.symm = e := ext $ λ x, rfl

@[simp] lemma to_linear_equiv_symm : e.to_linear_equiv.symm = e.symm.to_linear_equiv := rfl
@[simp] lemma to_isometry_equiv_symm : e.to_isometry_equiv.symm = e.symm.to_isometry_equiv := rfl
@[simp] lemma to_homeomorph_symm : e.to_homeomorph.symm = e.symm.to_homeomorph := rfl

/-- See Note [custom simps projection]. We need to specify this projection explicitly in this case,
  because it is a composition of multiple projections. -/
def simps.apply (σ₁₂ : R →+* R₂) {σ₂₁ : R₂ →+* R} [ring_hom_inv_pair σ₁₂ σ₂₁]
  [ring_hom_inv_pair σ₂₁ σ₁₂] (E E₂ : Type*) [seminormed_add_comm_group E]
  [seminormed_add_comm_group E₂] [module R E] [module R₂ E₂] (h : E ≃ₛₗᵢ[σ₁₂] E₂) : E → E₂ := h

/-- See Note [custom simps projection] -/
def simps.symm_apply (σ₁₂ : R →+* R₂) {σ₂₁ : R₂ →+* R} [ring_hom_inv_pair σ₁₂ σ₂₁]
  [ring_hom_inv_pair σ₂₁ σ₁₂] (E E₂ : Type*) [seminormed_add_comm_group E]
  [seminormed_add_comm_group E₂]
  [module R E] [module R₂ E₂] (h : E ≃ₛₗᵢ[σ₁₂] E₂) : E₂ → E := h.symm

initialize_simps_projections linear_isometry_equiv
  (to_linear_equiv_to_fun → apply, to_linear_equiv_inv_fun → symm_apply)

include σ₃₁ σ₃₂
/-- Composition of `linear_isometry_equiv`s as a `linear_isometry_equiv`. -/
def trans (e' : E₂ ≃ₛₗᵢ[σ₂₃] E₃) : E ≃ₛₗᵢ[σ₁₃] E₃ :=
⟨e.to_linear_equiv.trans e'.to_linear_equiv, λ x, (e'.norm_map _).trans (e.norm_map _)⟩

include σ₁₃ σ₂₁
@[simp] lemma coe_trans (e₁ : E ≃ₛₗᵢ[σ₁₂] E₂) (e₂ : E₂ ≃ₛₗᵢ[σ₂₃] E₃) : ⇑(e₁.trans e₂) = e₂ ∘ e₁ :=
rfl

@[simp] lemma trans_apply (e₁ : E ≃ₛₗᵢ[σ₁₂] E₂) (e₂ : E₂ ≃ₛₗᵢ[σ₂₃] E₃) (c : E) :
  (e₁.trans e₂ : E ≃ₛₗᵢ[σ₁₃] E₃) c = e₂ (e₁ c) :=
rfl

@[simp] lemma to_linear_equiv_trans (e' : E₂ ≃ₛₗᵢ[σ₂₃] E₃) :
  (e.trans e').to_linear_equiv = e.to_linear_equiv.trans e'.to_linear_equiv :=
rfl

omit σ₁₃ σ₂₁ σ₃₁ σ₃₂

@[simp] lemma trans_refl : e.trans (refl R₂ E₂) = e := ext $ λ x, rfl
@[simp] lemma refl_trans : (refl R E).trans e = e := ext $ λ x, rfl
@[simp] lemma self_trans_symm : e.trans e.symm = refl R E := ext e.symm_apply_apply
@[simp] lemma symm_trans_self : e.symm.trans e = refl R₂ E₂ := ext e.apply_symm_apply
@[simp] lemma symm_comp_self : e.symm ∘ e = id := funext e.symm_apply_apply
@[simp] lemma self_comp_symm : e ∘ e.symm = id := e.symm.symm_comp_self

include σ₁₃ σ₂₁ σ₃₂ σ₃₁
@[simp] lemma symm_trans (e₁ : E ≃ₛₗᵢ[σ₁₂] E₂) (e₂ : E₂ ≃ₛₗᵢ[σ₂₃] E₃) :
  (e₁.trans e₂).symm = e₂.symm.trans e₁.symm :=
rfl

lemma coe_symm_trans (e₁ : E ≃ₛₗᵢ[σ₁₂] E₂) (e₂ : E₂ ≃ₛₗᵢ[σ₂₃] E₃) :
  ⇑(e₁.trans e₂).symm = e₁.symm ∘ e₂.symm :=
rfl

include σ₁₄ σ₄₁ σ₄₂ σ₄₃ σ₂₄
lemma trans_assoc (eEE₂ : E ≃ₛₗᵢ[σ₁₂] E₂) (eE₂E₃ : E₂ ≃ₛₗᵢ[σ₂₃] E₃) (eE₃E₄ : E₃ ≃ₛₗᵢ[σ₃₄] E₄) :
  eEE₂.trans (eE₂E₃.trans eE₃E₄) = (eEE₂.trans eE₂E₃).trans eE₃E₄ :=
rfl
omit σ₂₁ σ₃₁ σ₄₁ σ₃₂ σ₄₂ σ₄₃ σ₁₃ σ₂₄ σ₁₄

instance : group (E ≃ₗᵢ[R] E) :=
{ mul := λ e₁ e₂, e₂.trans e₁,
  one := refl _ _,
  inv := symm,
  one_mul := trans_refl,
  mul_one := refl_trans,
  mul_assoc := λ _ _ _, trans_assoc _ _ _,
  mul_left_inv := self_trans_symm }

@[simp] lemma coe_one : ⇑(1 : E ≃ₗᵢ[R] E) = id := rfl
@[simp] lemma coe_mul (e e' : E ≃ₗᵢ[R] E) : ⇑(e * e') = e ∘ e' := rfl
@[simp] lemma coe_inv (e : E ≃ₗᵢ[R] E) : ⇑(e⁻¹) = e.symm := rfl

lemma one_def : (1 : E ≃ₗᵢ[R] E) = refl _ _ := rfl
lemma mul_def (e e' : E ≃ₗᵢ[R] E) : (e * e' : E ≃ₗᵢ[R] E) = e'.trans e := rfl
lemma inv_def (e : E ≃ₗᵢ[R] E) : (e⁻¹ : E ≃ₗᵢ[R] E) = e.symm := rfl

/-! Lemmas about mixing the group structure with definitions. Because we have multiple ways to
express `linear_isometry_equiv.refl`, `linear_isometry_equiv.symm`, and
`linear_isometry_equiv.trans`, we want simp lemmas for every combination.
The assumption made here is that if you're using the group structure, you want to preserve it
after simp.

This copies the approach used by the lemmas near `equiv.perm.trans_one`. -/

@[simp] lemma trans_one : e.trans (1 : E₂ ≃ₗᵢ[R₂] E₂) = e := trans_refl _
@[simp] lemma one_trans : (1 : E ≃ₗᵢ[R] E).trans e = e := refl_trans _
@[simp] lemma refl_mul (e : E ≃ₗᵢ[R] E) : (refl _ _) * e = e := trans_refl _
@[simp] lemma mul_refl (e : E ≃ₗᵢ[R] E) : e * (refl _ _) = e := refl_trans _

include σ₂₁

/-- Reinterpret a `linear_isometry_equiv` as a `continuous_linear_equiv`. -/
instance : has_coe_t (E ≃ₛₗᵢ[σ₁₂] E₂) (E ≃SL[σ₁₂] E₂) :=
⟨λ e, ⟨e.to_linear_equiv, e.continuous, e.to_isometry_equiv.symm.continuous⟩⟩

instance : has_coe_t (E ≃ₛₗᵢ[σ₁₂] E₂) (E →SL[σ₁₂] E₂) := ⟨λ e, ↑(e : E ≃SL[σ₁₂] E₂)⟩

@[simp] lemma coe_coe : ⇑(e : E ≃SL[σ₁₂] E₂) = e := rfl

@[simp] lemma coe_coe' : ((e : E ≃SL[σ₁₂] E₂) : E →SL[σ₁₂] E₂) = e := rfl

@[simp] lemma coe_coe'' : ⇑(e : E →SL[σ₁₂] E₂) = e := rfl

omit σ₂₁

@[simp] lemma map_zero : e 0 = 0 := e.1.map_zero

@[simp] lemma map_add (x y : E) : e (x + y) = e x + e y := e.1.map_add x y

@[simp] lemma map_sub (x y : E) : e (x - y) = e x - e y := e.1.map_sub x y

@[simp] lemma map_smulₛₗ (c : R) (x : E) : e (c • x) = σ₁₂ c • e x := e.1.map_smulₛₗ c x

@[simp] lemma map_smul [module R E₂] {e : E ≃ₗᵢ[R] E₂} (c : R) (x : E) : e (c • x) = c • e x :=
e.1.map_smul c x

@[simp] lemma nnnorm_map (x : E) : ‖e x‖₊ = ‖x‖₊ := semilinear_isometry_class.nnnorm_map e x

@[simp] lemma dist_map (x y : E) : dist (e x) (e y) = dist x y :=
e.to_linear_isometry.dist_map x y

@[simp] lemma edist_map (x y : E) : edist (e x) (e y) = edist x y :=
e.to_linear_isometry.edist_map x y

protected lemma bijective : bijective e := e.1.bijective
protected lemma injective : injective e := e.1.injective
protected lemma surjective : surjective e := e.1.surjective

@[simp] lemma map_eq_iff {x y : E} : e x = e y ↔ x = y := e.injective.eq_iff

lemma map_ne {x y : E} (h : x ≠ y) : e x ≠ e y := e.injective.ne h

protected lemma lipschitz : lipschitz_with 1 e := e.isometry.lipschitz

protected lemma antilipschitz : antilipschitz_with 1 e := e.isometry.antilipschitz

lemma image_eq_preimage (s : set E) : e '' s = e.symm ⁻¹' s :=
e.to_linear_equiv.image_eq_preimage s

@[simp] lemma ediam_image (s : set E) : emetric.diam (e '' s) = emetric.diam s :=
e.isometry.ediam_image s

@[simp] lemma diam_image (s : set E) : metric.diam (e '' s) = metric.diam s :=
e.isometry.diam_image s

@[simp] lemma preimage_ball (x : E₂) (r : ℝ) :
  e ⁻¹' (metric.ball x r) = metric.ball (e.symm x) r :=
e.to_isometry_equiv.preimage_ball x r

@[simp] lemma preimage_sphere (x : E₂) (r : ℝ) :
  e ⁻¹' (metric.sphere x r) = metric.sphere (e.symm x) r :=
e.to_isometry_equiv.preimage_sphere x r

@[simp] lemma preimage_closed_ball (x : E₂) (r : ℝ) :
  e ⁻¹' (metric.closed_ball x r) = metric.closed_ball (e.symm x) r :=
e.to_isometry_equiv.preimage_closed_ball x r

@[simp] lemma image_ball (x : E) (r : ℝ) :
  e '' (metric.ball x r) = metric.ball (e x) r :=
e.to_isometry_equiv.image_ball x r

@[simp] lemma image_sphere (x : E) (r : ℝ) :
  e '' (metric.sphere x r) = metric.sphere (e x) r :=
e.to_isometry_equiv.image_sphere x r

@[simp] lemma image_closed_ball (x : E) (r : ℝ) :
  e '' (metric.closed_ball x r) = metric.closed_ball (e x) r :=
e.to_isometry_equiv.image_closed_ball x r

variables {α : Type*} [topological_space α]

@[simp] lemma comp_continuous_on_iff {f : α → E} {s : set α} :
  continuous_on (e ∘ f) s ↔ continuous_on f s :=
e.isometry.comp_continuous_on_iff

@[simp] lemma comp_continuous_iff {f : α → E} :
  continuous (e ∘ f) ↔ continuous f :=
e.isometry.comp_continuous_iff

instance complete_space_map (p : submodule R E) [complete_space p] :
  complete_space (p.map (e.to_linear_equiv : E →ₛₗ[σ₁₂] E₂)) :=
e.to_linear_isometry.complete_space_map' p

include σ₂₁
/-- Construct a linear isometry equiv from a surjective linear isometry. -/
noncomputable def of_surjective (f : F →ₛₗᵢ[σ₁₂] E₂)
  (hfr : function.surjective f) :
  F ≃ₛₗᵢ[σ₁₂] E₂ :=
{ norm_map' := f.norm_map,
  .. linear_equiv.of_bijective f.to_linear_map ⟨f.injective, hfr⟩ }

@[simp] lemma coe_of_surjective (f : F →ₛₗᵢ[σ₁₂] E₂) (hfr : function.surjective f) :
  ⇑(linear_isometry_equiv.of_surjective f hfr) = f :=
by { ext, refl }

/-- If a linear isometry has an inverse, it is a linear isometric equivalence. -/
def of_linear_isometry (f : E →ₛₗᵢ[σ₁₂] E₂) (g : E₂ →ₛₗ[σ₂₁] E)
  (h₁ : f.to_linear_map.comp g = linear_map.id) (h₂ : g.comp f.to_linear_map = linear_map.id) :
  E ≃ₛₗᵢ[σ₁₂] E₂ :=
{ norm_map' := λ x, f.norm_map x,
  .. linear_equiv.of_linear f.to_linear_map g h₁ h₂ }

@[simp] lemma coe_of_linear_isometry (f : E →ₛₗᵢ[σ₁₂] E₂) (g : E₂ →ₛₗ[σ₂₁] E)
  (h₁ : f.to_linear_map.comp g = linear_map.id) (h₂ : g.comp f.to_linear_map = linear_map.id) :
  (of_linear_isometry f g h₁ h₂ : E → E₂) = (f : E → E₂) :=
rfl

@[simp] lemma coe_of_linear_isometry_symm (f : E →ₛₗᵢ[σ₁₂] E₂)
  (g : E₂ →ₛₗ[σ₂₁] E) (h₁ : f.to_linear_map.comp g = linear_map.id)
  (h₂ : g.comp f.to_linear_map = linear_map.id) :
  ((of_linear_isometry f g h₁ h₂).symm : E₂ → E) = (g : E₂ → E) :=
rfl

omit σ₂₁

variables (R)
/-- The negation operation on a normed space `E`, considered as a linear isometry equivalence. -/
def neg : E ≃ₗᵢ[R] E :=
{ norm_map' := norm_neg,
  .. linear_equiv.neg R }

variables {R}
@[simp] lemma coe_neg : (neg R : E → E) = λ x, -x := rfl

@[simp] lemma symm_neg : (neg R : E ≃ₗᵢ[R] E).symm = neg R := rfl

variables (R E E₂ E₃)

/-- The natural equivalence `(E × E₂) × E₃ ≃ E × (E₂ × E₃)` is a linear isometry. -/
def prod_assoc [module R E₂] [module R E₃] : (E × E₂) × E₃ ≃ₗᵢ[R] E × E₂ × E₃ :=
{ to_fun    := equiv.prod_assoc E E₂ E₃,
  inv_fun   := (equiv.prod_assoc E E₂ E₃).symm,
  map_add'  := by simp,
  map_smul' := by simp,
  norm_map' :=
    begin
      rintros ⟨⟨e, f⟩, g⟩,
      simp only [linear_equiv.coe_mk, equiv.prod_assoc_apply, prod.norm_def, max_assoc],
    end,
  .. equiv.prod_assoc E E₂ E₃, }

@[simp] lemma coe_prod_assoc [module R E₂] [module R E₃] :
  (prod_assoc R E E₂ E₃ : (E × E₂) × E₃ → E × E₂ × E₃) = equiv.prod_assoc E E₂ E₃ :=
rfl

@[simp] lemma coe_prod_assoc_symm [module R E₂] [module R E₃] :
  ((prod_assoc R E E₂ E₃).symm : E × E₂ × E₃ → (E × E₂) × E₃) = (equiv.prod_assoc E E₂ E₃).symm :=
rfl

/-- If `p` is a submodule that is equal to `⊤`, then `linear_isometry_equiv.of_top p hp` is the
"identity" equivalence between `p` and `E`. -/
@[simps to_linear_equiv apply symm_apply_coe]
def of_top {R : Type*} [ring R] [module R E] (p : submodule R E) (hp : p = ⊤) :
  p ≃ₗᵢ[R] E :=
{ to_linear_equiv := linear_equiv.of_top p hp, .. p.subtypeₗᵢ }

variables {R E E₂ E₃} {R' : Type*} [ring R'] [module R' E] (p q : submodule R' E)

/-- `linear_equiv.of_eq` as a `linear_isometry_equiv`. -/
def of_eq (hpq : p = q) :
  p ≃ₗᵢ[R'] q :=
{ norm_map' := λ x, rfl,
  ..linear_equiv.of_eq p q hpq }

variables {p q}

@[simp] lemma coe_of_eq_apply (h : p = q) (x : p) : (of_eq p q h x : E) = x := rfl
@[simp] lemma of_eq_symm (h : p = q) : (of_eq p q h).symm = of_eq q p h.symm := rfl
@[simp] lemma of_eq_rfl : of_eq p p rfl = linear_isometry_equiv.refl R' p := by ext; refl

end linear_isometry_equiv

/-- Two linear isometries are equal if they are equal on basis vectors. -/
lemma basis.ext_linear_isometry {ι : Type*} (b : basis ι R E) {f₁ f₂ : E →ₛₗᵢ[σ₁₂] E₂}
  (h : ∀ i, f₁ (b i) = f₂ (b i)) : f₁ = f₂ :=
linear_isometry.to_linear_map_injective $ b.ext h

include σ₂₁

/-- Two linear isometric equivalences are equal if they are equal on basis vectors. -/
lemma basis.ext_linear_isometry_equiv {ι : Type*} (b : basis ι R E) {f₁ f₂ : E ≃ₛₗᵢ[σ₁₂] E₂}
  (h : ∀ i, f₁ (b i) = f₂ (b i)) : f₁ = f₂ :=
linear_isometry_equiv.to_linear_equiv_injective $ b.ext' h

omit σ₂₁

/-- Reinterpret a `linear_isometry` as a `linear_isometry_equiv` to the range. -/
@[simps to_linear_equiv apply_coe]
noncomputable def linear_isometry.equiv_range {R S : Type*} [semiring R] [ring S] [module S E]
  [module R F] {σ₁₂ : R →+* S} {σ₂₁ : S →+* R} [ring_hom_inv_pair σ₁₂ σ₂₁]
  [ring_hom_inv_pair σ₂₁ σ₁₂] (f : F →ₛₗᵢ[σ₁₂] E) :
  F ≃ₛₗᵢ[σ₁₂] f.to_linear_map.range :=
{ to_linear_equiv := linear_equiv.of_injective f.to_linear_map f.injective, .. f }