/-
Copyright (c) 2019 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.simple_func_dense
import Mathlib.analysis.normed_space.bounded_linear_maps
import Mathlib.topology.sequences
import Mathlib.PostPort

universes u_1 u_2 u_3 u_4

namespace Mathlib

/-!
# Bochner integral

The Bochner integral extends the definition of the Lebesgue integral to functions that map from a
measure space into a Banach space (complete normed vector space). It is constructed here by
extending the integral on simple functions.

## Main definitions

The Bochner integral is defined following these steps:

1. Define the integral on simple functions of the type `simple_func α E` (notation : `α →ₛ E`)
  where `E` is a real normed space.

  (See `simple_func.bintegral` and section `bintegral` for details. Also see `simple_func.integral`
  for the integral on simple functions of the type `simple_func α ennreal`.)

2. Use `α →ₛ E` to cut out the simple functions from L1 functions, and define integral
  on these. The type of simple functions in L1 space is written as `α →₁ₛ[μ] E`.

3. Show that the embedding of `α →₁ₛ[μ] E` into L1 is a dense and uniform one.

4. Show that the integral defined on `α →₁ₛ[μ] E` is a continuous linear map.

5. Define the Bochner integral on L1 functions by extending the integral on integrable simple
  functions `α →₁ₛ[μ] E` using `continuous_linear_map.extend`. Define the Bochner integral on
  functions as the Bochner integral of its equivalence class in L1 space.

## Main statements

1. Basic properties of the Bochner integral on functions of type `α → E`, where `α` is a measure
   space and `E` is a real normed space.

  * `integral_zero`                  : `∫ 0 ∂μ = 0`
  * `integral_add`                   : `∫ x, f x + g x ∂μ = ∫ x, f ∂μ + ∫ x, g x ∂μ`
  * `integral_neg`                   : `∫ x, - f x ∂μ = - ∫ x, f x ∂μ`
  * `integral_sub`                   : `∫ x, f x - g x ∂μ = ∫ x, f x ∂μ - ∫ x, g x ∂μ`
  * `integral_smul`                  : `∫ x, r • f x ∂μ = r • ∫ x, f x ∂μ`
  * `integral_congr_ae`              : `f =ᵐ[μ] g → ∫ x, f x ∂μ = ∫ x, g x ∂μ`
  * `norm_integral_le_integral_norm` : `∥∫ x, f x ∂μ∥ ≤ ∫ x, ∥f x∥ ∂μ`

2. Basic properties of the Bochner integral on functions of type `α → ℝ`, where `α` is a measure
  space.

  * `integral_nonneg_of_ae` : `0 ≤ᵐ[μ] f → 0 ≤ ∫ x, f x ∂μ`
  * `integral_nonpos_of_ae` : `f ≤ᵐ[μ] 0 → ∫ x, f x ∂μ ≤ 0`
  * `integral_mono_ae`      : `f ≤ᵐ[μ] g → ∫ x, f x ∂μ ≤ ∫ x, g x ∂μ`
  * `integral_nonneg`       : `0 ≤ f → 0 ≤ ∫ x, f x ∂μ`
  * `integral_nonpos`       : `f ≤ 0 → ∫ x, f x ∂μ ≤ 0`
  * `integral_mono`         : `f ≤ᵐ[μ] g → ∫ x, f x ∂μ ≤ ∫ x, g x ∂μ`

3. Propositions connecting the Bochner integral with the integral on `ennreal`-valued functions,
   which is called `lintegral` and has the notation `∫⁻`.

  * `integral_eq_lintegral_max_sub_lintegral_min` : `∫ x, f x ∂μ = ∫⁻ x, f⁺ x ∂μ - ∫⁻ x, f⁻ x ∂μ`,
    where `f⁺` is the positive part of `f` and `f⁻` is the negative part of `f`.
  * `integral_eq_lintegral_of_nonneg_ae`          : `0 ≤ᵐ[μ] f → ∫ x, f x ∂μ = ∫⁻ x, f x ∂μ`

4. `tendsto_integral_of_dominated_convergence` : the Lebesgue dominated convergence theorem

## Notes

Some tips on how to prove a proposition if the API for the Bochner integral is not enough so that
you need to unfold the definition of the Bochner integral and go back to simple functions.

One method is to use the theorem `integrable.induction` in the file `set_integral`, which allows
you to prove something for an arbitrary measurable + integrable function.

Another method is using the following steps.
See `integral_eq_lintegral_max_sub_lintegral_min` for a complicated example, which proves that
`∫ f = ∫⁻ f⁺ - ∫⁻ f⁻`, with the first integral sign being the Bochner integral of a real-valued
function `f : α → ℝ`, and second and third integral sign being the integral on ennreal-valued
functions (called `lintegral`). The proof of `integral_eq_lintegral_max_sub_lintegral_min` is
scattered in sections with the name `pos_part`.

Here are the usual steps of proving that a property `p`, say `∫ f = ∫⁻ f⁺ - ∫⁻ f⁻`, holds for all
functions :

1. First go to the `L¹` space.

   For example, if you see `ennreal.to_real (∫⁻ a, ennreal.of_real $ ∥f a∥)`, that is the norm of
   `f` in `L¹` space. Rewrite using `l1.norm_of_fun_eq_lintegral_norm`.

2. Show that the set `{f ∈ L¹ | ∫ f = ∫⁻ f⁺ - ∫⁻ f⁻}` is closed in `L¹` using `is_closed_eq`.

3. Show that the property holds for all simple functions `s` in `L¹` space.

   Typically, you need to convert various notions to their `simple_func` counterpart, using lemmas
   like `l1.integral_coe_eq_integral`.

4. Since simple functions are dense in `L¹`,
```
univ = closure {s simple}
     = closure {s simple | ∫ s = ∫⁻ s⁺ - ∫⁻ s⁻} : the property holds for all simple functions
     ⊆ closure {f | ∫ f = ∫⁻ f⁺ - ∫⁻ f⁻}
     = {f | ∫ f = ∫⁻ f⁺ - ∫⁻ f⁻} : closure of a closed set is itself
```
Use `is_closed_property` or `dense_range.induction_on` for this argument.

## Notations

* `α →ₛ E`  : simple functions (defined in `measure_theory/integration`)
* `α →₁[μ] E` : functions in L1 space, i.e., equivalence classes of integrable functions (defined in
                `measure_theory/l1_space`)
* `α →₁ₛ[μ] E` : simple functions in L1 space, i.e., equivalence classes of integrable simple
                 functions

Note : `ₛ` is typed using `\_s`. Sometimes it shows as a box if font is missing.

## Tags

Bochner integral, simple function, function space, Lebesgue dominated convergence theorem

-/

namespace measure_theory

namespace simple_func

/-- Positive part of a simple function. -/
def pos_part {α : Type u_1} {E : Type u_2} [measurable_space α] [linear_order E] [HasZero E]
    (f : simple_func α E) : simple_func α E :=
  map (fun (b : E) => max b 0) f

/-- Negative part of a simple function. -/
def neg_part {α : Type u_1} {E : Type u_2} [measurable_space α] [linear_order E] [HasZero E] [Neg E]
    (f : simple_func α E) : simple_func α E :=
  pos_part (-f)

theorem pos_part_map_norm {α : Type u_1} [measurable_space α] (f : simple_func α ℝ) :
    map norm (pos_part f) = pos_part f :=
  sorry

theorem neg_part_map_norm {α : Type u_1} [measurable_space α] (f : simple_func α ℝ) :
    map norm (neg_part f) = neg_part f :=
  eq.mpr
    (id (Eq._oldrec (Eq.refl (map norm (neg_part f) = neg_part f)) (neg_part.equations._eqn_1 f)))
    (pos_part_map_norm (-f))

theorem pos_part_sub_neg_part {α : Type u_1} [measurable_space α] (f : simple_func α ℝ) :
    pos_part f - neg_part f = f :=
  sorry

end simple_func

end measure_theory

namespace measure_theory

namespace simple_func

/-!
### The Bochner integral of simple functions

Define the Bochner integral of simple functions of the type `α →ₛ β` where `β` is a normed group,
and prove basic property of this integral.
-/

/-- For simple functions with a `normed_group` as codomain, being integrable is the same as having
    finite volume support. -/
theorem integrable_iff_fin_meas_supp {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [measurable_space E] {f : simple_func α E} {μ : measure α} :
    integrable ⇑f ↔ simple_func.fin_meas_supp f μ :=
  sorry

theorem fin_meas_supp.integrable {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [measurable_space E] {μ : measure α} {f : simple_func α E} (h : simple_func.fin_meas_supp f μ) :
    integrable ⇑f :=
  iff.mpr integrable_iff_fin_meas_supp h

theorem integrable_pair {α : Type u_1} {E : Type u_2} {F : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] [normed_group F] {μ : measure α} [measurable_space F]
    {f : simple_func α E} {g : simple_func α F} :
    integrable ⇑f → integrable ⇑g → integrable ⇑(pair f g) :=
  sorry

/-- Bochner integral of simple functions whose codomain is a real `normed_space`. -/
def integral {α : Type u_1} {F : Type u_3} [measurable_space α] [normed_group F] [normed_space ℝ F]
    (μ : measure α) (f : simple_func α F) : F :=
  finset.sum (simple_func.range f)
    fun (x : F) => ennreal.to_real (coe_fn μ (⇑f ⁻¹' singleton x)) • x

theorem integral_eq_sum_filter {α : Type u_1} {F : Type u_3} [measurable_space α] [normed_group F]
    [normed_space ℝ F] (f : simple_func α F) (μ : measure α) :
    integral μ f =
        finset.sum (finset.filter (fun (x : F) => x ≠ 0) (simple_func.range f))
          fun (x : F) => ennreal.to_real (coe_fn μ (⇑f ⁻¹' singleton x)) • x :=
  sorry

/-- The Bochner integral is equal to a sum over any set that includes `f.range` (except `0`). -/
theorem integral_eq_sum_of_subset {α : Type u_1} {F : Type u_3} [measurable_space α]
    [normed_group F] [normed_space ℝ F] {f : simple_func α F} {μ : measure α} {s : finset F}
    (hs : finset.filter (fun (x : F) => x ≠ 0) (simple_func.range f) ⊆ s) :
    integral μ f =
        finset.sum s fun (x : F) => ennreal.to_real (coe_fn μ (⇑f ⁻¹' singleton x)) • x :=
  sorry

/-- Calculate the integral of `g ∘ f : α →ₛ F`, where `f` is an integrable function from `α` to `E`
    and `g` is a function from `E` to `F`. We require `g 0 = 0` so that `g ∘ f` is integrable. -/
theorem map_integral {α : Type u_1} {E : Type u_2} {F : Type u_3} [measurable_space α]
    [normed_group E] [measurable_space E] [normed_group F] {μ : measure α} [normed_space ℝ F]
    (f : simple_func α E) (g : E → F) (hf : integrable ⇑f) (hg : g 0 = 0) :
    integral μ (map g f) =
        finset.sum (simple_func.range f)
          fun (x : E) => ennreal.to_real (coe_fn μ (⇑f ⁻¹' singleton x)) • g x :=
  sorry

/-- `simple_func.integral` and `simple_func.lintegral` agree when the integrand has type
    `α →ₛ ennreal`. But since `ennreal` is not a `normed_space`, we need some form of coercion.
    See `integral_eq_lintegral` for a simpler version. -/
theorem integral_eq_lintegral' {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [measurable_space E] {μ : measure α} {f : simple_func α E} {g : E → ennreal}
    (hf : integrable ⇑f) (hg0 : g 0 = 0) (hgt : ∀ (b : E), g b < ⊤) :
    integral μ (map (ennreal.to_real ∘ g) f) =
        ennreal.to_real (lintegral μ fun (a : α) => g (coe_fn f a)) :=
  sorry

theorem integral_congr {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [measurable_space E] {μ : measure α} [normed_space ℝ E] {f : simple_func α E}
    {g : simple_func α E} (hf : integrable ⇑f) (h : filter.eventually_eq (measure.ae μ) ⇑f ⇑g) :
    integral μ f = integral μ g :=
  sorry

/-- `simple_func.bintegral` and `simple_func.integral` agree when the integrand has type
    `α →ₛ ennreal`. But since `ennreal` is not a `normed_space`, we need some form of coercion. -/
theorem integral_eq_lintegral {α : Type u_1} [measurable_space α] {μ : measure α}
    {f : simple_func α ℝ} (hf : integrable ⇑f) (h_pos : filter.eventually_le (measure.ae μ) 0 ⇑f) :
    integral μ f = ennreal.to_real (lintegral μ fun (a : α) => ennreal.of_real (coe_fn f a)) :=
  sorry

theorem integral_add {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [measurable_space E] {μ : measure α} [normed_space ℝ E] {f : simple_func α E}
    {g : simple_func α E} (hf : integrable ⇑f) (hg : integrable ⇑g) :
    integral μ (f + g) = integral μ f + integral μ g :=
  sorry

theorem integral_neg {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [measurable_space E] {μ : measure α} [normed_space ℝ E] {f : simple_func α E}
    (hf : integrable ⇑f) : integral μ (-f) = -integral μ f :=
  sorry

theorem integral_sub {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [measurable_space E] {μ : measure α} [normed_space ℝ E] [borel_space E] {f : simple_func α E}
    {g : simple_func α E} (hf : integrable ⇑f) (hg : integrable ⇑g) :
    integral μ (f - g) = integral μ f - integral μ g :=
  sorry

theorem integral_smul {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [measurable_space E] {μ : measure α} [normed_space ℝ E] (r : ℝ) {f : simple_func α E}
    (hf : integrable ⇑f) : integral μ (r • f) = r • integral μ f :=
  sorry

theorem norm_integral_le_integral_norm {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [measurable_space E] {μ : measure α} [normed_space ℝ E] (f : simple_func α E)
    (hf : integrable ⇑f) : norm (integral μ f) ≤ integral μ (map norm f) :=
  sorry

theorem integral_add_measure {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [measurable_space E] {μ : measure α} [normed_space ℝ E]
    {ν :
      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")
          [])}
    (f : simple_func α E) (hf : integrable ⇑f) : integral (μ + ν) f = integral μ f + integral ν f :=
  sorry

end simple_func

namespace l1

-- We use `Type*` instead of `add_subgroup` because otherwise we loose dot notation.

/-- `l1.simple_func` is a subspace of L1 consisting of equivalence classes of an integrable simple
    function. -/
def simple_func (α : Type u_1) (E : Type u_2) [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    (μ : measure α) :=
  ↥(add_subgroup.mk
      (set_of
        fun (f : l1 α E μ) =>
          ∃ (s : simple_func α E), ae_eq_fun.mk (⇑s) (simple_func.ae_measurable s) = ↑f)
      sorry sorry sorry)

namespace simple_func

/-! Simple functions in L1 space form a `normed_space`. -/

protected instance measure_theory.l1.has_coe {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [measurable_space E]
    [borel_space E] {μ : measure α} : has_coe (simple_func α E μ) (l1 α E μ) :=
  Mathlib.coe_subtype

protected instance has_coe_to_fun {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [measurable_space E]
    [borel_space E] {μ : measure α} : has_coe_to_fun (simple_func α E μ) :=
  has_coe_to_fun.mk (fun (f : simple_func α E μ) => α → E) fun (f : simple_func α E μ) => ⇑↑f

@[simp] theorem coe_coe {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) : ⇑↑f = ⇑f :=
  rfl

protected theorem eq {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} {f : simple_func α E μ} {g : simple_func α E μ} : ↑f = ↑g → f = g :=
  subtype.eq

protected theorem eq' {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} {f : simple_func α E μ} {g : simple_func α E μ} : ↑f = ↑g → f = g :=
  subtype.eq ∘ subtype.eq

protected theorem eq_iff {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} {f : simple_func α E μ} {g : simple_func α E μ} : ↑f = ↑g ↔ f = g :=
  iff.symm subtype.ext_iff

protected theorem eq_iff' {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} {f : simple_func α E μ} {g : simple_func α E μ} : ↑f = ↑g ↔ f = g :=
  { mp := simple_func.eq', mpr := congr_arg fun {f : simple_func α E μ} => ↑f }

/-- L1 simple functions forms a `emetric_space`, with the emetric being inherited from L1 space,
  i.e., `edist f g = ∫⁻ a, edist (f a) (g a)`.
  Not declared as an instance as `α →₁ₛ[μ] β` will only be useful in the construction of the Bochner
  integral. -/
protected def emetric_space {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} : emetric_space (simple_func α E μ) :=
  subtype.emetric_space

/-- L1 simple functions forms a `metric_space`, with the metric being inherited from L1 space,
  i.e., `dist f g = ennreal.to_real (∫⁻ a, edist (f a) (g a)`).
  Not declared as an instance as `α →₁ₛ[μ] β` will only be useful in the construction of the Bochner
  integral. -/
protected def metric_space {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} : metric_space (simple_func α E μ) :=
  subtype.metric_space

/-- Functions `α →₁ₛ[μ] E` form an additive commutative group. -/
protected def add_comm_group {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} : add_comm_group (simple_func α E μ) :=
  add_subgroup.to_add_comm_group
    (add_subgroup.mk
      (set_of
        fun (f : l1 α E μ) =>
          ∃ (s : simple_func α E), ae_eq_fun.mk (⇑s) (simple_func.ae_measurable s) = ↑f)
      (_proof_3 α E μ) (_proof_4 α E μ) (_proof_5 α E μ))

protected instance inhabited {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} : Inhabited (simple_func α E μ) :=
  { default := 0 }

@[simp] theorem coe_zero {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} : ↑0 = 0 :=
  rfl

@[simp] theorem coe_add {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) (g : simple_func α E μ) : ↑(f + g) = ↑f + ↑g :=
  rfl

@[simp] theorem coe_neg {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) : ↑(-f) = -↑f :=
  rfl

@[simp] theorem coe_sub {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) (g : simple_func α E μ) : ↑(f - g) = ↑f - ↑g :=
  rfl

@[simp] theorem edist_eq {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) (g : simple_func α E μ) : edist f g = edist ↑f ↑g :=
  rfl

@[simp] theorem dist_eq {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) (g : simple_func α E μ) : dist f g = dist ↑f ↑g :=
  rfl

/-- The norm on `α →₁ₛ[μ] E` is inherited from L1 space. That is, `∥f∥ = ∫⁻ a, edist (f a) 0`.
  Not declared as an instance as `α →₁ₛ[μ] E` will only be useful in the construction of the Bochner
  integral. -/
protected def has_norm {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} : has_norm (simple_func α E μ) :=
  has_norm.mk fun (f : simple_func α E μ) => norm ↑f

theorem norm_eq {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) : norm f = norm ↑f :=
  rfl

theorem norm_eq' {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) : norm f = ennreal.to_real (edist (↑f) 0) :=
  rfl

/-- Not declared as an instance as `α →₁ₛ[μ] E` will only be useful in the construction of the
Bochner integral. -/
protected def normed_group {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} : normed_group (simple_func α E μ) :=
  normed_group.of_add_dist sorry sorry

/-- Not declared as an instance as `α →₁ₛ[μ] E` will only be useful in the construction of the
Bochner integral. -/
protected def has_scalar {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} {𝕜 : Type u_4} [normed_field 𝕜] [normed_space 𝕜 E] :
    has_scalar 𝕜 (simple_func α E μ) :=
  has_scalar.mk fun (k : 𝕜) (f : simple_func α E μ) => { val := k • ↑f, property := sorry }

@[simp] theorem coe_smul {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} {𝕜 : Type u_4} [normed_field 𝕜] [normed_space 𝕜 E] (c : 𝕜)
    (f : simple_func α E μ) : ↑(c • f) = c • ↑f :=
  rfl

/-- Not declared as an instance as `α →₁ₛ[μ] E` will only be useful in the construction of the
  Bochner integral. -/
protected def semimodule {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} {𝕜 : Type u_4} [normed_field 𝕜] [normed_space 𝕜 E] :
    semimodule 𝕜 (simple_func α E μ) :=
  semimodule.mk sorry sorry

/-- Not declared as an instance as `α →₁ₛ[μ] E` will only be useful in the construction of the
Bochner integral. -/
protected def normed_space {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} {𝕜 : Type u_4} [normed_field 𝕜] [normed_space 𝕜 E] :
    normed_space 𝕜 (simple_func α E μ) :=
  normed_space.mk sorry

/-- Construct the equivalence class `[f]` of an integrable simple function `f`. -/
def of_simple_func {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E) (hf : integrable ⇑f) : simple_func α E μ :=
  { val := of_fun (⇑f) hf, property := sorry }

theorem of_simple_func_eq_of_fun {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E) (hf : integrable ⇑f) :
    ↑(of_simple_func f hf) = of_fun (⇑f) hf :=
  rfl

theorem of_simple_func_eq_mk {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E) (hf : integrable ⇑f) :
    ↑(of_simple_func f hf) = ae_eq_fun.mk (⇑f) (simple_func.ae_measurable f) :=
  rfl

theorem of_simple_func_zero {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} : of_simple_func 0 (integrable_zero α E μ) = 0 :=
  rfl

theorem of_simple_func_add {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E) (g : simple_func α E) (hf : integrable ⇑f)
    (hg : integrable ⇑g) :
    of_simple_func (f + g) (integrable.add hf hg) = of_simple_func f hf + of_simple_func g hg :=
  rfl

theorem of_simple_func_neg {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E) (hf : integrable ⇑f) :
    of_simple_func (-f) (integrable.neg hf) = -of_simple_func f hf :=
  rfl

theorem of_simple_func_sub {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E) (g : simple_func α E) (hf : integrable ⇑f)
    (hg : integrable ⇑g) :
    of_simple_func (f - g) (integrable.sub hf hg) = of_simple_func f hf - of_simple_func g hg :=
  sorry

theorem of_simple_func_smul {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} {𝕜 : Type u_4} [normed_field 𝕜] [normed_space 𝕜 E] (f : simple_func α E)
    (hf : integrable ⇑f) (c : 𝕜) :
    of_simple_func (c • f) (integrable.smul c hf) = c • of_simple_func f hf :=
  rfl

theorem norm_of_simple_func {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E) (hf : integrable ⇑f) :
    norm (of_simple_func f hf) =
        ennreal.to_real (lintegral μ fun (a : α) => edist (coe_fn f a) 0) :=
  rfl

/-- Find a representative of a `l1.simple_func`. -/
def to_simple_func {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) : simple_func α E :=
  classical.some sorry

/-- `f.to_simple_func` is measurable. -/
protected theorem measurable {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) : measurable ⇑(to_simple_func f) :=
  simple_func.measurable (to_simple_func f)

protected theorem ae_measurable {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) : ae_measurable ⇑(to_simple_func f) :=
  measurable.ae_measurable (simple_func.measurable f)

/-- `f.to_simple_func` is integrable. -/
protected theorem integrable {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) : integrable ⇑(to_simple_func f) :=
  sorry

theorem of_simple_func_to_simple_func {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [measurable_space E]
    [borel_space E] {μ : measure α} (f : simple_func α E μ) :
    of_simple_func (to_simple_func f) (simple_func.integrable f) = f :=
  sorry

theorem to_simple_func_of_simple_func {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [measurable_space E]
    [borel_space E] {μ : measure α} (f : simple_func α E) (hfi : integrable ⇑f) :
    filter.eventually_eq (measure.ae μ) ⇑(to_simple_func (of_simple_func f hfi)) ⇑f :=
  sorry

theorem to_simple_func_eq_to_fun {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) :
    filter.eventually_eq (measure.ae μ) ⇑(to_simple_func f) ⇑f :=
  sorry

theorem zero_to_simple_func (α : Type u_1) (E : Type u_2) [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} : filter.eventually_eq (measure.ae μ) (⇑(to_simple_func 0)) 0 :=
  sorry

theorem add_to_simple_func {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) (g : simple_func α E μ) :
    filter.eventually_eq (measure.ae μ) (⇑(to_simple_func (f + g)))
        (⇑(to_simple_func f) + ⇑(to_simple_func g)) :=
  sorry

theorem neg_to_simple_func {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) :
    filter.eventually_eq (measure.ae μ) (⇑(to_simple_func (-f))) (-⇑(to_simple_func f)) :=
  sorry

theorem sub_to_simple_func {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) (g : simple_func α E μ) :
    filter.eventually_eq (measure.ae μ) (⇑(to_simple_func (f - g)))
        (⇑(to_simple_func f) - ⇑(to_simple_func g)) :=
  sorry

theorem smul_to_simple_func {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} {𝕜 : Type u_4} [normed_field 𝕜] [normed_space 𝕜 E] (k : 𝕜)
    (f : simple_func α E μ) :
    filter.eventually_eq (measure.ae μ) (⇑(to_simple_func (k • f))) (k • ⇑(to_simple_func f)) :=
  sorry

theorem lintegral_edist_to_simple_func_lt_top {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [measurable_space E]
    [borel_space E] {μ : measure α} (f : simple_func α E μ) (g : simple_func α E μ) :
    (lintegral μ fun (x : α) => edist (coe_fn (to_simple_func f) x) (coe_fn (to_simple_func g) x)) <
        ⊤ :=
  sorry

theorem dist_to_simple_func {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) (g : simple_func α E μ) :
    dist f g =
        ennreal.to_real
          (lintegral μ
            fun (x : α) => edist (coe_fn (to_simple_func f) x) (coe_fn (to_simple_func g) x)) :=
  sorry

theorem norm_to_simple_func {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) :
    norm f = ennreal.to_real (lintegral μ fun (a : α) => ↑(nnnorm (coe_fn (to_simple_func f) a))) :=
  sorry

-- calc ∥f∥ = ennreal.to_real (∫⁻ (x : α), (coe ∘ nnnorm) (f.to_simple_func x) ∂μ) :

theorem norm_eq_integral {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (f : simple_func α E μ) :
    norm f = simple_func.integral μ (simple_func.map norm (to_simple_func f)) :=
  sorry

--   by { rw norm_to_simple_func }

-- ... = (f.to_simple_func.map norm).integral μ :

protected theorem uniform_continuous {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [measurable_space E]
    [borel_space E] {μ : measure α} : uniform_continuous coe :=
  uniform_continuous_comap

protected theorem uniform_embedding {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [measurable_space E]
    [borel_space E] {μ : measure α} : uniform_embedding coe :=
  uniform_embedding_comap subtype.val_injective

protected theorem uniform_inducing {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [measurable_space E]
    [borel_space E] {μ : measure α} : uniform_inducing coe :=
  uniform_embedding.to_uniform_inducing simple_func.uniform_embedding

protected theorem dense_embedding {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [measurable_space E]
    [borel_space E] {μ : measure α} : dense_embedding coe :=
  sorry

protected theorem dense_inducing {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} : dense_inducing coe :=
  dense_embedding.to_dense_inducing simple_func.dense_embedding

protected theorem dense_range {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} : dense_range coe :=
  dense_inducing.dense simple_func.dense_inducing

/-- The uniform and dense embedding of L1 simple functions into L1 functions. -/
def coe_to_l1 (α : Type u_1) (E : Type u_2) [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} (𝕜 : Type u_4) [normed_field 𝕜] [normed_space 𝕜 E] :
    continuous_linear_map 𝕜 (simple_func α E μ) (l1 α E μ) :=
  continuous_linear_map.mk (linear_map.mk coe sorry sorry)

/-- Positive part of a simple function in L1 space.  -/
def pos_part {α : Type u_1} [measurable_space α] {μ : measure α} (f : simple_func α ℝ μ) :
    simple_func α ℝ μ :=
  { val := pos_part ↑f, property := sorry }

/-- Negative part of a simple function in L1 space. -/
def neg_part {α : Type u_1} [measurable_space α] {μ : measure α} (f : simple_func α ℝ μ) :
    simple_func α ℝ μ :=
  pos_part (-f)

theorem coe_pos_part {α : Type u_1} [measurable_space α] {μ : measure α} (f : simple_func α ℝ μ) :
    ↑(pos_part f) = pos_part ↑f :=
  rfl

theorem coe_neg_part {α : Type u_1} [measurable_space α] {μ : measure α} (f : simple_func α ℝ μ) :
    ↑(neg_part f) = neg_part ↑f :=
  rfl

/-! Define the Bochner integral on `α →₁ₛ[μ] E` and prove basic properties of this integral. -/

/-- The Bochner integral over simple functions in l1 space. -/
def integral {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] (f : simple_func α E μ) : E :=
  simple_func.integral μ (to_simple_func f)

theorem integral_eq_integral {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] (f : simple_func α E μ) :
    integral f = simple_func.integral μ (to_simple_func f) :=
  rfl

theorem integral_eq_lintegral {α : Type u_1} [measurable_space α] {μ : measure α}
    {f : simple_func α ℝ μ} (h_pos : filter.eventually_le (measure.ae μ) 0 ⇑(to_simple_func f)) :
    integral f =
        ennreal.to_real
          (lintegral μ fun (a : α) => ennreal.of_real (coe_fn (to_simple_func f) a)) :=
  sorry

theorem integral_congr {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] {f : simple_func α E μ} {g : simple_func α E μ}
    (h : filter.eventually_eq (measure.ae μ) ⇑(to_simple_func f) ⇑(to_simple_func g)) :
    integral f = integral g :=
  simple_func.integral_congr (simple_func.integrable f) h

theorem integral_add {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] (f : simple_func α E μ) (g : simple_func α E μ) :
    integral (f + g) = integral f + integral g :=
  sorry

theorem integral_smul {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] (r : ℝ) (f : simple_func α E μ) :
    integral (r • f) = r • integral f :=
  sorry

theorem norm_integral_le_norm {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] (f : simple_func α E μ) : norm (integral f) ≤ norm f :=
  eq.mpr (id (Eq._oldrec (Eq.refl (norm (integral f) ≤ norm f)) (integral.equations._eqn_1 f)))
    (eq.mpr
      (id
        (Eq._oldrec (Eq.refl (norm (simple_func.integral μ (to_simple_func f)) ≤ norm f))
          (norm_eq_integral f)))
      (simple_func.norm_integral_le_integral_norm (to_simple_func f) (simple_func.integrable f)))

/-- The Bochner integral over simple functions in l1 space as a continuous linear map. -/
def integral_clm {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] : continuous_linear_map ℝ (simple_func α E μ) E :=
  linear_map.mk_continuous (linear_map.mk integral integral_add integral_smul) 1 sorry

theorem norm_Integral_le_one {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] : norm integral_clm ≤ 1 :=
  linear_map.mk_continuous_norm_le (linear_map.mk integral integral_add integral_smul) zero_le_one
    integral_clm._proof_1

theorem pos_part_to_simple_func {α : Type u_1} [measurable_space α] {μ : measure α}
    (f : simple_func α ℝ μ) :
    filter.eventually_eq (measure.ae μ) ⇑(to_simple_func (pos_part f))
        ⇑(simple_func.pos_part (to_simple_func f)) :=
  sorry

theorem neg_part_to_simple_func {α : Type u_1} [measurable_space α] {μ : measure α}
    (f : simple_func α ℝ μ) :
    filter.eventually_eq (measure.ae μ) ⇑(to_simple_func (neg_part f))
        ⇑(simple_func.neg_part (to_simple_func f)) :=
  sorry

theorem integral_eq_norm_pos_part_sub {α : Type u_1} [measurable_space α] {μ : measure α}
    (f : simple_func α ℝ μ) : integral f = norm (pos_part f) - norm (neg_part f) :=
  sorry

end simple_func

/-- The Bochner integral in l1 space as a continuous linear map. -/
def integral_clm {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] [complete_space E] : continuous_linear_map ℝ (l1 α E μ) E :=
  continuous_linear_map.extend simple_func.integral_clm (simple_func.coe_to_l1 α E ℝ)
    simple_func.dense_range simple_func.uniform_inducing

/-- The Bochner integral in l1 space -/
def integral {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] [complete_space E] (f : l1 α E μ) : E :=
  coe_fn integral_clm f

theorem integral_eq {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] [complete_space E] (f : l1 α E μ) :
    integral f = coe_fn integral_clm f :=
  rfl

theorem simple_func.integral_l1_eq_integral {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [measurable_space E]
    [borel_space E] {μ : measure α} [normed_space ℝ E] [complete_space E] (f : simple_func α E μ) :
    integral ↑f = simple_func.integral f :=
  uniformly_extend_of_ind simple_func.uniform_inducing simple_func.dense_range
    (continuous_linear_map.uniform_continuous simple_func.integral_clm) f

@[simp] theorem integral_zero (α : Type u_1) (E : Type u_2) [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] [complete_space E] : integral 0 = 0 :=
  continuous_linear_map.map_zero integral_clm

theorem integral_add {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] [complete_space E] (f : l1 α E μ) (g : l1 α E μ) :
    integral (f + g) = integral f + integral g :=
  continuous_linear_map.map_add integral_clm f g

theorem integral_neg {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] [complete_space E] (f : l1 α E μ) :
    integral (-f) = -integral f :=
  continuous_linear_map.map_neg integral_clm f

theorem integral_sub {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] [complete_space E] (f : l1 α E μ) (g : l1 α E μ) :
    integral (f - g) = integral f - integral g :=
  continuous_linear_map.map_sub integral_clm f g

theorem integral_smul {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] [complete_space E] (r : ℝ) (f : l1 α E μ) :
    integral (r • f) = r • integral f :=
  continuous_linear_map.map_smul r integral_clm f

theorem norm_Integral_le_one {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] [complete_space E] : norm integral_clm ≤ 1 :=
  sorry

theorem norm_integral_le {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] [complete_space E] (f : l1 α E μ) :
    norm (integral f) ≤ norm f :=
  sorry

theorem continuous_integral {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [measurable_space E] [borel_space E]
    {μ : measure α} [normed_space ℝ E] [complete_space E] :
    continuous fun (f : l1 α E μ) => integral f :=
  sorry

theorem integral_eq_norm_pos_part_sub {α : Type u_1} [measurable_space α] {μ : measure α}
    (f : l1 α ℝ μ) : integral f = norm (pos_part f) - norm (neg_part f) :=
  sorry

end l1

/-- The Bochner integral -/
def integral {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] (μ : measure α) (f : α → E) : E :=
  dite (integrable f) (fun (hf : integrable f) => l1.integral (l1.of_fun f hf))
    fun (hf : ¬integrable f) => 0

/-! In the notation for integrals, an expression like `∫ x, g ∥x∥ ∂μ` will not be parsed correctly,
  and needs parentheses. We do not set the binding power of `r` to `0`, because then
  `∫ x, f x = 0` will be parsed incorrectly. -/

theorem integral_eq {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} (f : α → E) (hf : integrable f) :
    (integral μ fun (a : α) => f a) = l1.integral (l1.of_fun f hf) :=
  dif_pos hf

theorem l1.integral_eq_integral {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} (f : l1 α E μ) :
    l1.integral f = integral μ fun (a : α) => coe_fn f a :=
  sorry

theorem integral_undef {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {f : α → E} {μ : measure α} (h : ¬integrable f) :
    (integral μ fun (a : α) => f a) = 0 :=
  dif_neg h

theorem integral_non_ae_measurable {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {f : α → E} {μ : measure α}
    (h : ¬ae_measurable f) : (integral μ fun (a : α) => f a) = 0 :=
  integral_undef (not_and_of_not_left (has_finite_integral fun (a : α) => f a) h)

theorem integral_zero (α : Type u_1) (E : Type u_2) [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} : (integral μ fun (a : α) => 0) = 0 :=
  sorry

@[simp] theorem integral_zero' (α : Type u_1) (E : Type u_2) [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} : integral μ 0 = 0 :=
  integral_zero α E

theorem integral_add {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {f : α → E} {g : α → E} {μ : measure α} (hf : integrable f)
    (hg : integrable g) :
    (integral μ fun (a : α) => f a + g a) =
        (integral μ fun (a : α) => f a) + integral μ fun (a : α) => g a :=
  sorry

theorem integral_add' {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {f : α → E} {g : α → E} {μ : measure α} (hf : integrable f)
    (hg : integrable g) :
    (integral μ fun (a : α) => Add.add f g a) =
        (integral μ fun (a : α) => f a) + integral μ fun (a : α) => g a :=
  integral_add hf hg

theorem integral_neg {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} (f : α → E) :
    (integral μ fun (a : α) => -f a) = -integral μ fun (a : α) => f a :=
  sorry

theorem integral_neg' {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} (f : α → E) :
    (integral μ fun (a : α) => Neg.neg f a) = -integral μ fun (a : α) => f a :=
  integral_neg f

theorem integral_sub {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {f : α → E} {g : α → E} {μ : measure α} (hf : integrable f)
    (hg : integrable g) :
    (integral μ fun (a : α) => f a - g a) =
        (integral μ fun (a : α) => f a) - integral μ fun (a : α) => g a :=
  sorry

theorem integral_sub' {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {f : α → E} {g : α → E} {μ : measure α} (hf : integrable f)
    (hg : integrable g) :
    (integral μ fun (a : α) => Sub.sub f g a) =
        (integral μ fun (a : α) => f a) - integral μ fun (a : α) => g a :=
  integral_sub hf hg

theorem integral_smul {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} (r : ℝ) (f : α → E) :
    (integral μ fun (a : α) => r • f a) = r • integral μ fun (a : α) => f a :=
  sorry

theorem integral_mul_left {α : Type u_1} [measurable_space α] {μ : measure α} (r : ℝ) (f : α → ℝ) :
    (integral μ fun (a : α) => r * f a) = r * integral μ fun (a : α) => f a :=
  integral_smul r f

theorem integral_mul_right {α : Type u_1} [measurable_space α] {μ : measure α} (r : ℝ) (f : α → ℝ) :
    (integral μ fun (a : α) => f a * r) = (integral μ fun (a : α) => f a) * r :=
  sorry

theorem integral_div {α : Type u_1} [measurable_space α] {μ : measure α} (r : ℝ) (f : α → ℝ) :
    (integral μ fun (a : α) => f a / r) = (integral μ fun (a : α) => f a) / r :=
  integral_mul_right (r⁻¹) f

theorem integral_congr_ae {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {f : α → E} {g : α → E} {μ : measure α}
    (h : filter.eventually_eq (measure.ae μ) f g) :
    (integral μ fun (a : α) => f a) = integral μ fun (a : α) => g a :=
  sorry

@[simp] theorem l1.integral_of_fun_eq_integral {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {μ : measure α} {f : α → E}
    (hf : integrable f) :
    (integral μ fun (a : α) => coe_fn (l1.of_fun f hf) a) = integral μ fun (a : α) => f a :=
  integral_congr_ae (l1.to_fun_of_fun f hf)

theorem continuous_integral {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} :
    continuous fun (f : l1 α E μ) => integral μ fun (a : α) => coe_fn f a :=
  sorry

theorem norm_integral_le_lintegral_norm {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {μ : measure α} (f : α → E) :
    norm (integral μ fun (a : α) => f a) ≤
        ennreal.to_real (lintegral μ fun (a : α) => ennreal.of_real (norm (f a))) :=
  sorry

theorem ennnorm_integral_le_lintegral_ennnorm {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {μ : measure α} (f : α → E) :
    ↑(nnnorm (integral μ fun (a : α) => f a)) ≤ lintegral μ fun (a : α) => ↑(nnnorm (f a)) :=
  sorry

theorem integral_eq_zero_of_ae {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} {f : α → E}
    (hf : filter.eventually_eq (measure.ae μ) f 0) : (integral μ fun (a : α) => f a) = 0 :=
  sorry

/-- If `f` has finite integral, then `∫ x in s, f x ∂μ` is absolutely continuous in `s`: it tends
to zero as `μ s` tends to zero. -/
theorem has_finite_integral.tendsto_set_integral_nhds_zero {α : Type u_1} {E : Type u_2}
    [measurable_space α] [normed_group E] [topological_space.second_countable_topology E]
    [normed_space ℝ E] [complete_space E] [measurable_space E] [borel_space E] {μ : measure α}
    {ι : Type u_3} {f : α → E} (hf : has_finite_integral f) {l : filter ι} {s : ι → set α}
    (hs : filter.tendsto (⇑μ ∘ s) l (nhds 0)) :
    filter.tendsto (fun (i : ι) => integral (measure.restrict μ (s i)) fun (x : α) => f x) l
        (nhds 0) :=
  sorry

/-- If `f` is integrable, then `∫ x in s, f x ∂μ` is absolutely continuous in `s`: it tends
to zero as `μ s` tends to zero. -/
theorem integrable.tendsto_set_integral_nhds_zero {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {μ : measure α} {ι : Type u_3}
    {f : α → E} (hf : integrable f) {l : filter ι} {s : ι → set α}
    (hs : filter.tendsto (⇑μ ∘ s) l (nhds 0)) :
    filter.tendsto (fun (i : ι) => integral (measure.restrict μ (s i)) fun (x : α) => f x) l
        (nhds 0) :=
  has_finite_integral.tendsto_set_integral_nhds_zero (and.right hf) hs

/-- If `F i → f` in `L1`, then `∫ x, F i x ∂μ → ∫ x, f x∂μ`. -/
theorem tendsto_integral_of_l1 {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} {ι : Type u_3} (f : α → E)
    (hfi : integrable f) {F : ι → α → E} {l : filter ι}
    (hFi : filter.eventually (fun (i : ι) => integrable (F i)) l)
    (hF :
      filter.tendsto (fun (i : ι) => lintegral μ fun (x : α) => edist (F i x) (f x)) l (nhds 0)) :
    filter.tendsto (fun (i : ι) => integral μ fun (x : α) => F i x) l
        (nhds (integral μ fun (x : α) => f x)) :=
  sorry

/-- Lebesgue dominated convergence theorem provides sufficient conditions under which almost
  everywhere convergence of a sequence of functions implies the convergence of their integrals. -/
theorem tendsto_integral_of_dominated_convergence {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {μ : measure α} {F : ℕ → α → E}
    {f : α → E} (bound : α → ℝ) (F_measurable : ∀ (n : ℕ), ae_measurable (F n))
    (f_measurable : ae_measurable f) (bound_integrable : integrable bound)
    (h_bound : ∀ (n : ℕ), filter.eventually (fun (a : α) => norm (F n a) ≤ bound a) (measure.ae μ))
    (h_lim :
      filter.eventually
        (fun (a : α) => filter.tendsto (fun (n : ℕ) => F n a) filter.at_top (nhds (f a)))
        (measure.ae μ)) :
    filter.tendsto (fun (n : ℕ) => integral μ fun (a : α) => F n a) filter.at_top
        (nhds (integral μ fun (a : α) => f a)) :=
  sorry

/-- Lebesgue dominated convergence theorem for filters with a countable basis -/
theorem tendsto_integral_filter_of_dominated_convergence {α : Type u_1} {E : Type u_2}
    [measurable_space α] [normed_group E] [topological_space.second_countable_topology E]
    [normed_space ℝ E] [complete_space E] [measurable_space E] [borel_space E] {μ : measure α}
    {ι : Type u_3} {l : filter ι} {F : ι → α → E} {f : α → E} (bound : α → ℝ)
    (hl_cb : filter.is_countably_generated l)
    (hF_meas : filter.eventually (fun (n : ι) => ae_measurable (F n)) l)
    (f_measurable : ae_measurable f)
    (h_bound :
      filter.eventually
        (fun (n : ι) => filter.eventually (fun (a : α) => norm (F n a) ≤ bound a) (measure.ae μ)) l)
    (bound_integrable : integrable bound)
    (h_lim :
      filter.eventually (fun (a : α) => filter.tendsto (fun (n : ι) => F n a) l (nhds (f a)))
        (measure.ae μ)) :
    filter.tendsto (fun (n : ι) => integral μ fun (a : α) => F n a) l
        (nhds (integral μ fun (a : α) => f a)) :=
  sorry

/-- The Bochner integral of a real-valued function `f : α → ℝ` is the difference between the
  integral of the positive part of `f` and the integral of the negative part of `f`.  -/
theorem integral_eq_lintegral_max_sub_lintegral_min {α : Type u_1} [measurable_space α]
    {μ : measure α} {f : α → ℝ} (hf : integrable f) :
    (integral μ fun (a : α) => f a) =
        ennreal.to_real (lintegral μ fun (a : α) => ennreal.of_real (max (f a) 0)) -
          ennreal.to_real (lintegral μ fun (a : α) => ennreal.of_real (-min (f a) 0)) :=
  sorry

-- Go to the `L¹` space

-- Go to the `L¹` space

theorem integral_eq_lintegral_of_nonneg_ae {α : Type u_1} [measurable_space α] {μ : measure α}
    {f : α → ℝ} (hf : filter.eventually_le (measure.ae μ) 0 f) (hfm : ae_measurable f) :
    (integral μ fun (a : α) => f a) =
        ennreal.to_real (lintegral μ fun (a : α) => ennreal.of_real (f a)) :=
  sorry

theorem integral_nonneg_of_ae {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ℝ}
    (hf : filter.eventually_le (measure.ae μ) 0 f) : 0 ≤ integral μ fun (a : α) => f a :=
  sorry

theorem lintegral_coe_eq_integral {α : Type u_1} [measurable_space α] {μ : measure α}
    (f : α → nnreal) (hfi : integrable fun (x : α) => ↑(f x)) :
    (lintegral μ fun (a : α) => ↑(f a)) = ennreal.of_real (integral μ fun (x : α) => ↑(f x)) :=
  sorry

theorem integral_to_real {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal}
    (hfm : ae_measurable f) (hf : filter.eventually (fun (x : α) => f x < ⊤) (measure.ae μ)) :
    (integral μ fun (a : α) => ennreal.to_real (f a)) =
        ennreal.to_real (lintegral μ fun (a : α) => f a) :=
  sorry

theorem integral_nonneg {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ℝ}
    (hf : 0 ≤ f) : 0 ≤ integral μ fun (a : α) => f a :=
  integral_nonneg_of_ae (filter.eventually_of_forall hf)

theorem integral_nonpos_of_ae {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ℝ}
    (hf : filter.eventually_le (measure.ae μ) f 0) : (integral μ fun (a : α) => f a) ≤ 0 :=
  sorry

theorem integral_nonpos {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ℝ}
    (hf : f ≤ 0) : (integral μ fun (a : α) => f a) ≤ 0 :=
  integral_nonpos_of_ae (filter.eventually_of_forall hf)

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

theorem integral_eq_zero_iff_of_nonneg {α : Type u_1} [measurable_space α] {μ : measure α}
    {f : α → ℝ} (hf : 0 ≤ f) (hfi : integrable f) :
    (integral μ fun (x : α) => f x) = 0 ↔ filter.eventually_eq (measure.ae μ) f 0 :=
  integral_eq_zero_iff_of_nonneg_ae (filter.eventually_of_forall hf) hfi

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

theorem integral_pos_iff_support_of_nonneg {α : Type u_1} [measurable_space α] {μ : measure α}
    {f : α → ℝ} (hf : 0 ≤ f) (hfi : integrable f) :
    (0 < integral μ fun (x : α) => f x) ↔ 0 < coe_fn μ (function.support f) :=
  integral_pos_iff_support_of_nonneg_ae (filter.eventually_of_forall hf) hfi

theorem l1.norm_eq_integral_norm {α : Type u_1} [measurable_space α] {μ : measure α} {H : Type u_4}
    [normed_group H] [topological_space.second_countable_topology H] [measurable_space H]
    [borel_space H] (f : l1 α H μ) : norm f = integral μ fun (a : α) => norm (coe_fn f a) :=
  sorry

theorem l1.norm_of_fun_eq_integral_norm {α : Type u_1} [measurable_space α] {μ : measure α}
    {H : Type u_4} [normed_group H] [topological_space.second_countable_topology H]
    [measurable_space H] [borel_space H] {f : α → H} (hf : integrable f) :
    norm (l1.of_fun f hf) = integral μ fun (a : α) => norm (f a) :=
  sorry

theorem integral_mono_ae {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ℝ} {g : α → ℝ}
    (hf : integrable f) (hg : integrable g) (h : filter.eventually_le (measure.ae μ) f g) :
    (integral μ fun (a : α) => f a) ≤ integral μ fun (a : α) => g a :=
  le_of_sub_nonneg
    (Eq.subst (integral_sub hg hf) integral_nonneg_of_ae
      (filter.eventually.mono h fun (a : α) => sub_nonneg_of_le))

theorem integral_mono {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ℝ} {g : α → ℝ}
    (hf : integrable f) (hg : integrable g) (h : f ≤ g) :
    (integral μ fun (a : α) => f a) ≤ integral μ fun (a : α) => g a :=
  integral_mono_ae hf hg (filter.eventually_of_forall h)

theorem integral_mono_of_nonneg {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ℝ}
    {g : α → ℝ} (hf : filter.eventually_le (measure.ae μ) 0 f) (hgi : integrable g)
    (h : filter.eventually_le (measure.ae μ) f g) :
    (integral μ fun (a : α) => f a) ≤ integral μ fun (a : α) => g a :=
  sorry

theorem norm_integral_le_integral_norm {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {μ : measure α} (f : α → E) :
    norm (integral μ fun (a : α) => f a) ≤ integral μ fun (a : α) => norm (f a) :=
  sorry

theorem norm_integral_le_of_norm_le {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {μ : measure α} {f : α → E} {g : α → ℝ}
    (hg : integrable g) (h : filter.eventually (fun (x : α) => norm (f x) ≤ g x) (measure.ae μ)) :
    norm (integral μ fun (x : α) => f x) ≤ integral μ fun (x : α) => g x :=
  le_trans (norm_integral_le_integral_norm f)
    (integral_mono_of_nonneg (filter.eventually_of_forall fun (x : α) => norm_nonneg (f x)) hg h)

theorem integral_finset_sum {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} {ι : Type u_3} (s : finset ι)
    {f : ι → α → E} (hf : ∀ (i : ι), integrable (f i)) :
    (integral μ fun (a : α) => finset.sum s fun (i : ι) => f i a) =
        finset.sum s fun (i : ι) => integral μ fun (a : α) => f i a :=
  sorry

theorem simple_func.integral_eq_integral {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {μ : measure α} (f : simple_func α E)
    (hfi : integrable ⇑f) : simple_func.integral μ f = integral μ fun (x : α) => coe_fn f x :=
  sorry

@[simp] theorem integral_const {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} (c : E) :
    (integral μ fun (x : α) => c) = ennreal.to_real (coe_fn μ set.univ) • c :=
  sorry

theorem norm_integral_le_of_norm_le_const {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {μ : measure α} [finite_measure μ]
    {f : α → E} {C : ℝ} (h : filter.eventually (fun (x : α) => norm (f x) ≤ C) (measure.ae μ)) :
    norm (integral μ fun (x : α) => f x) ≤ C * ennreal.to_real (coe_fn μ set.univ) :=
  sorry

theorem tendsto_integral_approx_on_univ_of_measurable {α : Type u_1} {E : Type u_2}
    [measurable_space α] [normed_group E] [topological_space.second_countable_topology E]
    [normed_space ℝ E] [complete_space E] [measurable_space E] [borel_space E] {μ : measure α}
    {f : α → E} (fmeas : measurable f) (hf : integrable f) :
    filter.tendsto
        (fun (n : ℕ) => simple_func.integral μ (simple_func.approx_on f fmeas set.univ 0 trivial n))
        filter.at_top (nhds (integral μ fun (x : α) => f x)) :=
  sorry

theorem integral_add_measure {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} {ν : measure α} {f : α → E}
    (hμ : integrable f) (hν : integrable f) :
    (integral (μ + ν) fun (x : α) => f x) =
        (integral μ fun (x : α) => f x) + integral ν fun (x : α) => f x :=
  sorry

@[simp] theorem integral_zero_measure {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] (f : α → E) :
    (integral 0 fun (x : α) => f x) = 0 :=
  sorry

@[simp] theorem integral_smul_measure {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {μ : measure α} (f : α → E)
    (c : ennreal) :
    (integral (c • μ) fun (x : α) => f x) = ennreal.to_real c • integral μ fun (x : α) => f x :=
  sorry

theorem integral_map_of_measurable {α : Type u_1} {E : Type u_2} [measurable_space α]
    [normed_group E] [topological_space.second_countable_topology E] [normed_space ℝ E]
    [complete_space E] [measurable_space E] [borel_space E] {μ : measure α} {β : Type u_3}
    [measurable_space β] {φ : α → β} (hφ : measurable φ) {f : β → E} (hfm : measurable f) :
    (integral (coe_fn (measure.map φ) μ) fun (y : β) => f y) = integral μ fun (x : α) => f (φ x) :=
  sorry

theorem integral_map {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] {μ : measure α} {β : Type u_3} [measurable_space β]
    {φ : α → β} (hφ : measurable φ) {f : β → E} (hfm : ae_measurable f) :
    (integral (coe_fn (measure.map φ) μ) fun (y : β) => f y) = integral μ fun (x : α) => f (φ x) :=
  sorry

theorem integral_dirac' {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] (f : α → E) (a : α) (hfm : measurable f) :
    (integral (measure.dirac a) fun (x : α) => f x) = f a :=
  sorry

theorem integral_dirac {α : Type u_1} {E : Type u_2} [measurable_space α] [normed_group E]
    [topological_space.second_countable_topology E] [normed_space ℝ E] [complete_space E]
    [measurable_space E] [borel_space E] [measurable_singleton_class α] (f : α → E) (a : α) :
    (integral (measure.dirac a) fun (x : α) => f x) = f a :=
  sorry

end Mathlib


import geometry.manifold.smooth_manifold_with_corners

example : times_cont_diff ℝ ⊤ (λ x : ℝ × ℝ, x.1 * x.2) :=
begin
  library_search,
end


mutual
inductive A : Type
| a : A

inductive B : Type
| b : B
end

example (x : PSigma fun (a : A) => True) : A := by
  cases x with | mk x₁ x₂ => ?_
  induction x₁
  done


/-
Copyright (c) 2022 Andrew Yang. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Andrew Yang
-/
import morphisms.basic
import algebraic_geometry.fiber
import algebraic_geometry.prime_spectrum_more
import morphisms.quasi_compact
import for_mathlib.topology
import for_mathlib.specializing
import for_mathlib.pi
import ring_theory.ideal.minimal_prime

/-!
# Universally closed morphism

A morphism of schemes `f : X ⟶ Y` is universally closed if `X ×[Y] Y' ⟶ Y'` is a closed map
for all base change `Y' ⟶ Y`.

We show that being universally closed is local at the target, and is stable under compositions and
base changes.

-/

noncomputable theory

open category_theory category_theory.limits opposite topological_space

universes v u

namespace algebraic_geometry

variables {X Y : Scheme.{u}} (f : X ⟶ Y)

open category_theory.morphism_property
open algebraic_geometry.morphism_property (topologically)

/--
A morphism of schemes `f : X ⟶ Y` is universally closed if the base change `X ×[Y] Y' ⟶ Y'`
along any morphism `Y' ⟶ Y` is (topologically) a closed map.
-/
@[mk_iff]
class universally_closed (f : X ⟶ Y) : Prop :=
(out : universally (topologically @is_closed_map) f)

lemma universally_closed_eq :
  @universally_closed = universally (topologically @is_closed_map) :=
begin
  ext X Y f, rw universally_closed_iff
end

lemma universally_closed_respects_iso :
  respects_iso @universally_closed :=
universally_closed_eq.symm ▸ universally_respects_iso (topologically @is_closed_map)

lemma universally_closed_stable_under_base_change :
  stable_under_base_change @universally_closed :=
universally_closed_eq.symm ▸ universally_stable_under_base_change (topologically @is_closed_map)

lemma universally_closed_stable_under_composition :
  stable_under_composition @universally_closed :=
begin
  rw universally_closed_eq,
  exact stable_under_composition.universally (λ X Y Z f g hf hg, is_closed_map.comp hg hf),
end

instance universally_closed_type_comp {X Y Z : Scheme} (f : X ⟶ Y) (g : Y ⟶ Z)
  [hf : universally_closed f] [hg : universally_closed g] :
  universally_closed (f ≫ g) :=
universally_closed_stable_under_composition f g hf hg

instance universally_closed_fst {X Y Z : Scheme} (f : X ⟶ Z) (g : Y ⟶ Z)
  [hg : universally_closed g] :
  universally_closed (pullback.fst : pullback f g ⟶ _) :=
universally_closed_stable_under_base_change.fst f g hg

instance universally_closed_snd {X Y Z : Scheme} (f : X ⟶ Z) (g : Y ⟶ Z)
  [hf : universally_closed f] :
  universally_closed (pullback.snd : pullback f g ⟶ _) :=
universally_closed_stable_under_base_change.snd f g hf

lemma morphism_restrict_base {X Y : Scheme} (f : X ⟶ Y) (U : opens Y.carrier) :
  ⇑(f ∣_ U).1.base = U.1.restrict_preimage f.1 :=
funext (λ x, subtype.ext $ morphism_restrict_base_coe f U x)

lemma universally_closed_is_local_at_target :
  property_is_local_at_target @universally_closed :=
begin
  rw universally_closed_eq,
  apply universally_is_local_at_target_of_morphism_restrict,
  { exact stable_under_composition.respects_iso (λ X Y Z f g hf hg, is_closed_map.comp hg hf)
      (λ X Y f, (Top.homeo_of_iso (Scheme.forget_to_Top.map_iso f)).is_closed_map) },
  { intros X Y f ι U hU H,
    simp_rw [topologically, morphism_restrict_base] at H,
    exact (is_closed_map_iff_is_closed_map_of_supr_eq_top hU).mpr H }
end

lemma universally_closed.open_cover_iff {X Y : Scheme.{u}} (f : X ⟶ Y)
  (𝒰 : Scheme.open_cover.{u} Y) :
  universally_closed f ↔
    (∀ i, universally_closed (pullback.snd : pullback f (𝒰.map i) ⟶ _)) :=
universally_closed_is_local_at_target.open_cover_iff f 𝒰

lemma universally_closed.is_closed_map (f : X ⟶ Y) [H : universally_closed f] :
  is_closed_map f.1.base :=
(topologically @is_closed_map).universally_le _ _ _ H.out

lemma universally_closed.compact_space_of_field {R : Type*} [field R] {X : Scheme}
  (f : X ⟶ Scheme.Spec.obj (op $ CommRing.of R)) [H : universally_closed f] :
    compact_space X.carrier :=
begin
  classical,
  unfreezingI { contrapose H },
  rw universally_closed_iff,
  delta morphism_property.universally topologically is_closed_map,
  push_neg,
  let S := Scheme.Spec.obj (op $ CommRing.of R),
  let T := Scheme.Spec.obj (op $ CommRing.of $ mv_polynomial X.affine_opens R),
  let Ti : X.affine_opens → opens T.carrier := λ i, prime_spectrum.basic_open (mv_polynomial.X i),
  let g : T ⟶ S := Scheme.Spec.map (CommRing.of_hom
    (algebra_map R (mv_polynomial X.affine_opens R) : _)).op,
  let p₁ : pullback g f ⟶ _ := pullback.fst,
  let p₂ : pullback g f ⟶ _ := pullback.snd,
  let XTi : X.affine_opens → Scheme := λ i, pullback
    (T.of_restrict (Ti i).open_embedding ≫ g)
    (X.of_restrict i.1.open_embedding ≫ f),
  let hi : ∀ i, XTi i ⟶ pullback g f :=
    λ i, pullback.map _ _ _ _ _ _ _ (category.comp_id _) (category.comp_id _),
  haveI : unique S.carrier := show unique (prime_spectrum R), by apply_instance,
  let s : S.carrier := (show prime_spectrum R, from ⊥),
  let t : T.carrier := prime_spectrum.comap
    (mv_polynomial.eval $ λ (_ : X.affine_opens), (1 : R)) s,
  let Z := (⨆ i, (hi i).opens_range).compl,
  refine ⟨_, T, _, g, _, is_pullback.of_has_pullback _ _, Z.1, Z.2, λ hZ, _⟩,
  change is_closed (p₁.1.base '' Z.1) at hZ,
  have : t ∉ p₁.1.base '' Z.1,
  { rintro ⟨x, hx, hxt⟩,
    apply hx,
    apply opens.mem_supr.mpr,
    let i : X.affine_opens := ⟨_, range_is_affine_open_of_open_immersion
      (X.affine_cover.map $ p₂.1.base x)⟩,
    have : t ∈ (Ti i).val,
    { rintro ht, dsimp [s] at ht, rw [mv_polynomial.eval_X] at ht, exact one_ne_zero ht },
    refine ⟨i, _⟩,
    change x ∈ set.range _,
    simp only [pullback.range_map, Scheme.of_restrict_val_base, set.mem_inter_iff, opens.inclusion,
      set.mem_preimage, continuous_map.coe_mk, subtype.range_coe_subtype],
    exact ⟨hxt.symm ▸ this, X.affine_cover.covers _⟩ },
  obtain ⟨q : mv_polynomial _ R, hq₁, hq₂⟩ : ∃ q, p₁.1.base '' Z.1 ⊆ prime_spectrum.zero_locus {q} ∧
    t ∉ prime_spectrum.zero_locus ({q} : set (mv_polynomial X.affine_opens R)),
  { by_contra hq, push_neg at hq, apply this,
    obtain ⟨s, hs⟩ := (prime_spectrum.is_closed_iff_zero_locus _).mp hZ,
    simp_rw hs at hq ⊢,
    intros q hq',
    exact hq q (prime_spectrum.zero_locus_anti_mono $ set.singleton_subset_iff.mpr hq') rfl },
  obtain ⟨σq, q', hq'⟩ := q.exists_finset_rename,
  let ϕ := @mv_polynomial.aeval R (mv_polynomial _ R) _ _ _ _
    (λ i : X.affine_opens, if i ∈ σq then mv_polynomial.X i else 0),
  let t' := prime_spectrum.comap ϕ.to_ring_hom t,
  have ϕq : ϕ q = q,
  { rw [hq', mv_polynomial.aeval_rename, mv_polynomial.rename],
    have : ∀ i : σq, ↑i ∈ σq := subtype.prop,
    simp_rw [function.comp, if_pos (this _)] },
  have ht'₁ : t' ∉ prime_spectrum.zero_locus ({q} : set (mv_polynomial X.affine_opens R)),
  { rwa [← @set.mem_preimage _ _ (prime_spectrum.comap ϕ.to_ring_hom),
      prime_spectrum.preimage_comap_zero_locus, set.image_singleton,
      alg_hom.to_ring_hom_eq_coe, alg_hom.coe_to_ring_hom, ϕq] },
  have ht'₂ : ∀ i ∉ σq, t' ∉ Ti i,
  { intros i hi,
    change t' ∉ (prime_spectrum.basic_open (mv_polynomial.X i)).1,
    rw [subtype.val_eq_coe, prime_spectrum.basic_open_eq_zero_locus_compl, set.not_mem_compl_iff,
      ← @set.mem_preimage _ _ (prime_spectrum.comap ϕ.to_ring_hom),
      prime_spectrum.preimage_comap_zero_locus, set.image_singleton,
      alg_hom.to_ring_hom_eq_coe, alg_hom.coe_to_ring_hom, mv_polynomial.aeval_X, if_neg hi,
      prime_spectrum.zero_locus_singleton_zero],
    trivial },
  obtain ⟨x, hx⟩ : ∃ x, x ∉ ⨆ i : σq, i.1.1,
  { have : (⨆ i : σq, i.1.1) ≠ ⊤,
    { intro e, apply H, rw [← is_compact_univ_iff, ← opens.coe_top, ← e, opens.coe_supr],
      apply is_compact_Union, exact λ i, i.1.2.is_compact },
    contrapose! this, rw eq_top_iff, exact λ x _, this x },
  let Tp : pullback.triplet g f := ⟨t', x, default, unique.eq_default _, unique.eq_default _⟩,
  obtain ⟨z, hz : p₁.1.base z = t', rfl : p₂.1.base z = x⟩ := Tp.exists_preimage,
  apply ht'₁,
  apply hq₁,
  refine ⟨_, λ hz', _, hz⟩,
  obtain ⟨i, z', rfl⟩ := opens.mem_supr.mp hz',
  by_cases hi : i ∈ σq,
  { apply hx, refine opens.mem_supr.mpr ⟨⟨i, hi⟩, _⟩,
    rw [← Scheme.comp_val_base_apply, pullback.lift_snd],
    exact ((Scheme.forget_to_Top.map pullback.snd) z').prop },
  { rw [← Scheme.comp_val_base_apply, pullback.lift_fst] at hz,
    apply ht'₂ i hi, rw ← hz,
    exact ((Scheme.forget_to_Top.map pullback.fst) z').prop }
end
.
lemma universally_closed.to_closed_map (f : X ⟶ Y) [universally_closed f] :
  is_closed_map f.1.base :=
begin
  apply universally_le (topologically @is_closed_map),
  rwa ← universally_closed_eq,
end

lemma universally_closed.is_compact_preimage (f : X ⟶ Y) [H : universally_closed f]
  {K : set Y.carrier} (hK : is_compact K) : is_compact (f.1.base ⁻¹' K) :=
begin
  refine proper_of_compact_fibers _ (λ x, _) (universally_closed.to_closed_map f) hK,
  haveI : universally_closed (f.fiber_to_residue_field x),
  { delta Scheme.hom.fiber_to_residue_field, apply_instance },
  have := @universally_closed.compact_space_of_field
    (local_ring.residue_field (Y.presheaf.stalk x)) _ _ (f.fiber_to_residue_field x) _,
  rw [← f.range_fiber_ι x, ← set.image_univ],
  rw [← is_compact_univ_iff] at this,
  exact this.image (by continuity)
end

@[priority 100]
instance universally_closed.to_quasi_compact (f : X ⟶ Y) [universally_closed f] :
  quasi_compact f :=
⟨λ U hU, universally_closed.is_compact_preimage f⟩

section specializing

lemma image_is_closed_iff_is_stable_under_specialization_of_affine
  [compact_space X.carrier] {R : CommRing}
  (f : X ⟶ Scheme.Spec.obj (op R)) {Z : set X.carrier}
  (hZ : is_closed Z) : is_closed (f.1.base '' Z) ↔ stable_under_specialization (f.1.base '' Z) :=
begin
  have : ∀ i, ∃ I : ideal (X.affine_cover_ring i), (X.affine_cover.map i).1.base ⁻¹' Z =
    prime_spectrum.zero_locus (↑I : set (X.affine_cover_ring i)),
  { intro i, apply (prime_spectrum.is_closed_iff_zero_locus_ideal _).mp (is_closed.preimage _ hZ),
    continuity },
  choose I hI,
  let gi : ∀ i : X.affine_cover.J, R ⟶ CommRing.of (X.affine_cover_ring i ⧸ I i) :=
    λ i, (Scheme.Spec.preimage (X.affine_cover.map i ≫ f)).unop ≫ (ideal.quotient.mk _),
  have hgi : ∀ i, Scheme.Spec.map (gi i).op =
    Scheme.Spec.map (quiver.hom.op (ideal.quotient.mk (I i))) ≫ X.affine_cover.map i ≫ f,
  { intro i, simpa only [functor.map_comp, op_comp, quiver.hom.op_unop, functor.image_preimage] },
  let S := Π i : X.affine_cover.finite_subcover.J, X.affine_cover_ring i.1 ⧸ I i.1,
  let g : R →+* S := pi.ring_hom (λ i, gi i.1),
  have : f.1.base '' Z =
    set.range (Scheme.Spec.map (show R ⟶ CommRing.of S, from g).op).1.base,
  { apply le_antisymm,
    { rintro _ ⟨x, hxZ, rfl⟩,
      let i := X.affine_cover.finite_subcover.f x,
      obtain ⟨y, hy : (X.affine_cover.map i.1).1.base y = x⟩ :=
        X.affine_cover.finite_subcover.covers x,
      obtain ⟨y', rfl⟩ : y ∈ set.range (prime_spectrum.comap (ideal.quotient.mk (I i.1))),
      { rw prime_spectrum.range_comap_of_surjective _ _ (ideal.quotient.mk_surjective),
        rwa [ideal.mk_ker, ← hI, set.mem_preimage, hy] },
      let g' : S →+* _ ⧸ I i.1 := pi.eval_ring_hom _ (X.affine_cover.finite_subcover.f x),
      have : g'.comp g = gi i.1 := by { ext, refl },
      refine ⟨prime_spectrum.comap g' y', _⟩,
      rw [← hy, ← Scheme.comp_val_base_apply],
      transitivity (Scheme.Spec.map (gi i.1).op).1.base y',
      { rw ← this, refl }, { rw hgi, refl } },
    { rintros _ ⟨x, rfl⟩,
      obtain ⟨⟨i, x⟩, rfl⟩ := (prime_spectrum.pi_equiv _).symm.surjective x,
      refine ⟨(X.affine_cover.map i.1).val.base
        (prime_spectrum.comap (ideal.quotient.mk _) x), _, _⟩,
      { rw [← set.mem_preimage, hI], intros y hy, change (I i.1)^.quotient.mk y ∈ x.as_ideal,
        rw [ideal.quotient.eq_zero_iff_mem.mpr hy], exact zero_mem _ },
      { let g' : S →+* _ ⧸ I i.1 := pi.eval_ring_hom _ i,
        have : g'.comp g = gi i.1 := by { ext, refl },
        transitivity (Scheme.Spec.map (gi i.1).op).1.base x,
          { rw hgi, refl }, { rw ← this, refl } } } },
  rw this,
  exact prime_spectrum.image_is_closed_iff_is_stable_under_specialization _
end

lemma image_is_closed_iff_is_stable_under_specialization
  (f : X ⟶ Y) [quasi_compact f] {Z : set X.carrier}
  (hZ : is_closed Z) : is_closed (f.1.base '' Z) ↔ stable_under_specialization (f.1.base '' Z) :=
begin
  refine ⟨is_closed.stable_under_specialization, λ h, _⟩,
  rw is_closed_iff_coe_preimage_of_supr_eq_top Y.affine_cover.supr_opens_range,
  intro i,
  haveI := (quasi_compact_iff_forall_affine f).mp infer_instance _
    (range_is_affine_open_of_open_immersion (Y.affine_cover.map i)),
  haveI := (quasi_compact.affine_open_cover_iff Y.affine_cover f).mp infer_instance i,
  let Z' : set (Y.affine_cover.obj i).carrier := _,
  have : is_closed Z' ↔ stable_under_specialization Z' :=
    image_is_closed_iff_is_stable_under_specialization_of_affine
    (pullback.snd : pullback f (Y.affine_cover.map i) ⟶ _)
    (hZ.preimage (pullback.fst : pullback f (Y.affine_cover.map i) ⟶ _).1.base.2),
  let Z'' : set (Y.affine_cover.map i).opens_range := _, change is_closed Z'',
  let e := homeomorph.of_embedding (Y.affine_cover.map i).1.base
    PresheafedSpace.is_open_immersion.base_open.to_embedding,
  have hZ : e ⁻¹' Z'' = Z',
  { ext x, simp only [set.mem_preimage, set.mem_image],
    split,
    { rintro ⟨y, hyZ, hxy : f.1.base y = (Y.affine_cover.map i).1.base x⟩,
      let T : pullback.triplet f (Y.affine_cover.map i) := ⟨y, x, _, hxy, rfl⟩,
      obtain ⟨z, hzx : _ = y, hzy⟩ := T.exists_preimage,
      refine ⟨z, _, hzy⟩, rwa ← hzx at hyZ },
    { rintro ⟨x, hx, rfl⟩, refine ⟨_, hx, _⟩, rw [continuous_map.to_fun_eq_coe,
        ← Scheme.comp_val_base_apply, pullback.condition], refl } },
  rw [← e.quotient_map.is_closed_preimage, hZ, this, ← hZ],
  apply stable_under_specialization.preimage,
  apply stable_under_specialization.preimage h,
  all_goals { continuity }
end

lemma quasi_compact.is_closed_map_iff_specializing_map
  (f : X ⟶ Y) [quasi_compact f] : is_closed_map f.1.base ↔ specializing_map f.1.base :=
begin
  refine ⟨is_closed_map.specializing_map, _⟩,
  intros H Z hZ,
  rw image_is_closed_iff_is_stable_under_specialization f hZ,
  exact H.stable_under_specialization_image hZ.stable_under_specialization,
end

lemma universally_closed_eq_quasi_compact_and_universally_specializing :
  @universally_closed = @quasi_compact ⊓ (topologically @specializing_map).universally :=
begin
  ext X Y f,
  split,
  { introI _, refine ⟨infer_instance, λ X' Y' i₁ i₂ f' h, _⟩,
    haveI := universally_closed_stable_under_base_change h.flip infer_instance,
    exact (quasi_compact.is_closed_map_iff_specializing_map f').mp
      (universally_closed.is_closed_map f') },
  { rintro ⟨h₁, h₂⟩, constructor, introsI X' Y' i₁ i₂ f' h,
    haveI := quasi_compact_stable_under_base_change h.flip infer_instance,
    exact (quasi_compact.is_closed_map_iff_specializing_map f').mpr (h₂ _ _ _ h) }
end

end specializing

end algebraic_geometry


/-
Copyright (c) 2022 Devon Tuma. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Devon Tuma
-/
import computational_monads.simulation_semantics.constructions.logging.query_log.basic
import computational_monads.simulation_semantics.constructions.tracking_oracle

/-!
# Logging Oracles

This file defines a `logging_oracle` for simulating a computation while logging all queries.
The implementation is as a `tracking_oracle`, using a `query_log` as the internal state to
log the input and output of each query.
-/

open oracle_comp oracle_spec

variables {α β γ : Type} {spec spec' spec'' : oracle_spec}

def logging_oracle (spec : oracle_spec) : sim_oracle spec spec (query_log spec) :=
⟪query | λ i t u, query_log.log_query i t u, query_log.init spec⟫

namespace logging_oracle

variables (a : α) (oa : oracle_comp spec α) (ob : α → oracle_comp spec β) (i : spec.ι)
  (t : spec.domain i) (log : query_log spec)

@[simp] lemma apply : (logging_oracle spec) i (t, log) =
  query i t >>= λ u, return (u, log.log_query i t u) := rfl

section simulate

lemma simulate_return : simulate (logging_oracle _) (return a) log = return ⟨a, log⟩ := rfl

lemma simulate_query : simulate (logging_oracle _) (query i t) log =
  do {u ← query i t, return (u, log.log_query i t u)} := rfl

lemma simulate_bind : simulate (logging_oracle _) (oa >>= ob) log =
  (simulate (logging_oracle _) oa log) >>= (λ x, simulate (logging_oracle _) (ob x.1) x.2) := rfl

end simulate

section support

@[simp] lemma support_simulate' : (simulate' (logging_oracle spec) oa log).support = oa.support :=
sorry --tracking_oracle.support_simulate'_query_oracle_eq_support _ _ oa log

lemma support_default_simulate' :
  (default_simulate' (logging_oracle spec) oa).support = oa.support :=
support_simulate' oa (query_log.init spec)

end support

section distribution_semantics

section eval_dist

@[simp] lemma eval_dist_simulate' : ⁅simulate' (logging_oracle spec) oa log⁆ = ⁅oa⁆ :=
tracking_oracle.eval_dist_simulate'_query_eq_eval_dist _ _ oa log

lemma eval_dist_default_simulate' : ⁅default_simulate' (logging_oracle spec) oa⁆ = ⁅oa⁆ :=
logging_oracle.eval_dist_simulate' oa (query_log.init spec)

end eval_dist

end distribution_semantics

end logging_oracle


import Std.Data.HashMap

import Lean.Meta.Basic
import Lean.Elab.Command

import LeanSat.Data.Sexp
import LeanSat.Dsl.Sexp
import LeanSat.Playground.SmtSolvers

/-! This module attempts to translate certain Lean expressions to SMTLIB expressions. Satisfiability
can then be checked.

Planned features:
- Recursive translation of definitions appearing in an expression.
- Partial elimination of higher-order structure with values of known "shape" through reduction
  (in the kernel or custom). For example, `List.all (· == 1) [a,b]` contains a higher-order lambda,
  but reduces to the first-order `a == 1 ∧ a == b`. Custom reduction might be needed to support
  `partial` definitions with no terminating values in the type theory.
- Encoding of universal quantifiers into quantifier-free (`QF`) theories via constants.
- Good support for bitvectors and `QF_BV`.

Potential features:
- Encoding existential quantifiers with skolemization.
- Encoding rank-1 polymorphic definitions via λ-lifting and combinators, similarly to what is done
  for Isabelle/HOL and HOL Light. [1]
- Model reconstruction.
- Unsatisfiability proof reconstruction.

Limitations:
- Support for value-indexed types (or otherwise "very-dependent" ones) is not planned.

[1] https://matryoshka-project.github.io/pubs/seventeen.pdf -/

open Lean (Name MetaM)

set_option autoImplicit false

private constant TranslationExtRefPointed : NonemptyType.{0}

private def TranslationExtRef : Type := TranslationExtRefPointed.type

instance : Nonempty TranslationExtRef := TranslationExtRefPointed.property

structure TranslationState where
  -- Each entry is a type name translated to an SMT sort.
  -- TODO(WN): quotient keys by defeq with a discrimination tree?
  -- TODO(WN): this can't simply be a namemap. For example, we may want
  -- BitVec n => _ BV n
  -- Maybe a typeclass for extensibility? -/
  -- sortMap : Std.HashMap Name String
  -- TODO(WN): could readd the sortMap if we want to support ADTs

  /-- Each entry is a translation of a Lean value. We take:
  - `def` to `define-fun`(`-rec`)
  - `constant` to `declare-const` or `declare-fun` -/
  valMap : Std.HashMap Name String

  exts : Array TranslationExtRef

/-- We define a `TranslationM` monad which keeps track of which Lean constants have been
translated to which SMT expressions. -/
abbrev TranslationM := StateT TranslationState <| MetaM

namespace TranslationM

open Lean Meta

/-- Extends base translation capabilities with new primitives. Used to support particular theories,
and potential user extensions. -/
structure TranslationExt where
  translateSort : Expr → TranslationM (Option Sexp)
  translateVal  : Expr → TranslationM (Option Sexp)
  deriving Inhabited

unsafe def mkTranslationExtImp (ext : TranslationExt) : TranslationExtRef :=
  unsafeCast ext

@[implementedBy mkTranslationExtImp]
constant mkTranslationExt (ext : TranslationExt) : TranslationExtRef

instance : Inhabited TranslationExtRef where
  default := mkTranslationExt default

unsafe def getExtsImp : TranslationM (Array TranslationExt) :=
  return (← get).exts.map unsafeCast

@[implementedBy getExtsImp]
constant getExts : TranslationM (Array TranslationExt)

def addExt (ext : TranslationExt) : TranslationM Unit := do
  modify fun st => { st with exts := st.exts.push (mkTranslationExt ext) }

def translateSort (e : Expr) : TranslationM Sexp := do
  for ext in ← getExts do
    if let some s ← ext.translateSort e then return s

  -- if let some nm := expr.constName? then
  -- if let some sortNm := (← get).sortMap.find? nm
  -- then return sortNm

  throwError "type {e} has no corresponding SMT-LIB sort"

/-- Translate an `.fvar` into an SMT-LIB `sorted_var`. -/
def translateSortedFVar (e : Expr) : TranslationM Sexp :=
  if let .fvar _ _ := e then do
    let fv ← getFVarLocalDecl e
    let tp ← inferType e
    return sexp!{({toString fv.userName} {← translateSort tp})}
  else unreachable!

partial def translateVal (e : Expr) : TranslationM Sexp :=
  go e
where go (e : Expr) : TranslationM Sexp := do
  for ext in ← getExts do
    if let some s ← ext.translateVal e then return s

  match e with
  | .const nm _ _ => do
    return .atom (toString nm)
    -- if let some val := (← get).valMap.find? nm
    --   then return .atom val
    -- throwError "constant '{nm}' has no corresponding SMT-LIB"
  | e@(.fvar _ _) => do
    let nm ← (toString ∘ LocalDecl.userName) <$> getFVarLocalDecl e
    return .atom nm
  | e@(.app _ _ _) => e.withApp fun fn args => do
    let fnSmt ← go fn
    let argsSmt ← args.mapM go
    return sexp!{({fnSmt} ...{argsSmt.toList})}
  | .lam x xTp body _ => do
    throwError "cannot translate lambda to SMT-LIB"
  | .letE _ _ _ _ _ => throwError "TODO"
  | .lit _ _ => throwError "TODO"
  | .mdata _ e _ => go e
  | e@(.forallE _ _ _ _) => do
    forallTelescopeReducing e fun args tp => do
      let binders ← args.mapM translateSortedFVar
      return sexp!{(forall (...{binders.toList}) {← go tp})}
  | e => throwError "cannot translate expression {e} to SMT-LIB"

def setValFor (valNm : Name) (val : String) : TranslationM Unit := do
  modify fun st => { st with valMap := st.valMap.insert valNm val }

open Lean Meta in
def translateDef (constName : Name) : TranslationM Sexp := do
  let .defnInfo defInfo ← getConstInfo constName | throwError "'{constName}' is not a 'def'"
  if defInfo.safety != .«safe» then throwError "'{constName}' is not safe"
  forallTelescopeReducing defInfo.type fun args bodyTp => do
    let binders ← args.mapM translateSortedFVar
    let bodySort ← translateSort bodyTp
    lambdaTelescope defInfo.value fun args' body => do
      if args.size != args'.size then
        throwError "argument and type telescopes don't match: {args} and {args'}"

      let nm := toString constName
      setValFor constName nm

      return sexp!{
        (define-fun {nm} (...{binders.toList}) {bodySort}
          {← translateVal body})
      }

def translateDefs (nms : Array Name) : TranslationM (Array Sexp) := do
  let mut ret := #[]
  for nm in nms do
    ret := ret.push <| ← translateDef nm
  return ret

/-- Add support for the Core theory to the translation. -/
def addTheoryCore : TranslationM Unit :=
  let translateSort (e : Expr) : TranslationM (Option Sexp) :=
    if e.isProp || e.constName? matches some ``Bool then return some "Bool"
    else return none

  -- TODO how can we use discr trees here instead of nested ifs?
  let translateVal (e : Expr) : TranslationM (Option Sexp) := do
    match e.constName? with
    | some nm => match nm with
      | ``true | ``True => return some "true"
      | ``false | ``False => return some "false"
      | _ => return none
    | none =>
      -- TODO ne, iff, ite, distinct
      -- TODO boolean operations
      if let some arg := e.not? then
        return some sexp!{(not {← translateVal arg})}
      if let some (p, q) := e.and? then
        return some sexp!{(and {← translateVal p} {← translateVal q})}
      if let some (p, q) := e.app2? ``Or then
        return some sexp!{(or {← translateVal p} {← translateVal q})}
      if let some (p, q) := e.arrow? then
        if (← inferType e).isProp then
          return some sexp!{(=> {← translateVal p} {← translateVal q})}
      if let some (_, a, b) := e.eq? then
        return some sexp!{(= {← translateVal a} {← translateVal b})}
      return none

  addExt { translateSort, translateVal }

def ofNatNumeral? (e : Expr) : Option Nat := do
  let some (_, n, _) := e.app3? ``OfNat.ofNat | none
  let some n := n.natLit? | none
  return n

@[inline] def app6? (e : Expr) (fName : Name) : Option (Expr × Expr × Expr × Expr × Expr × Expr) :=
  if e.isAppOfArity fName 6 then
    some (
      e.appFn!.appFn!.appFn!.appFn!.appFn!.appArg!,
      e.appFn!.appFn!.appFn!.appFn!.appArg!,
      e.appFn!.appFn!.appFn!.appArg!,
      e.appFn!.appFn!.appArg!,
      e.appFn!.appArg!,
      e.appArg!
    )
  else
    none

/-- Add support for the Ints theory to the translation. -/
def addTheoryInts : TranslationM Unit :=
  -- TODO how can we use discr trees here instead of nested ifs?
  let translateSort (e : Expr) : TranslationM (Option Sexp) :=
    if e.constName? matches some ``Int then return some "Int"
    else return none

  -- TODO hadd, hsub, hmul, hdiv etc
  let translateVal (e : Expr) : TranslationM (Option Sexp) := do
    if let some n := ofNatNumeral? e then
      return toString n
    if let some (_, _, a) := e.app3? ``Neg.neg then
      return some sexp!{(- {← translateVal a})}
    if let some (_, _, a, b) := e.app4? ``Sub.sub then
      return some sexp!{(- {← translateVal a} {← translateVal b})}
    if let some (_, _, _, _, a, b) := app6? e ``HSub.hSub then
      return some sexp!{(- {← translateVal a} {← translateVal b})}
    if let some (_, _, a, b) := e.app4? ``Add.add then
      return some sexp!{(+ {← translateVal a} {← translateVal b})}
    if let some (_, _, _, _, a, b) := app6? e ``HAdd.hAdd then
      return some sexp!{(+ {← translateVal a} {← translateVal b})}
    if let some (_, _, a, b) := e.app4? ``Mul.mul then
      return some sexp!{(* {← translateVal a} {← translateVal b})}
    if let some (_, _, _, _, a, b) := app6? e ``HMul.hMul then
      return some sexp!{(* {← translateVal a} {← translateVal b})}
    if let some (_, _, a, b) := e.app4? ``Div.div then
      return some sexp!{(div {← translateVal a} {← translateVal b})}
    if let some (_, _, _, _, a, b) := app6? e ``HDiv.hDiv then
      return some sexp!{(div {← translateVal a} {← translateVal b})}
    if let some (_, _, a, b) := e.app4? ``Mod.mod then
      return some sexp!{(mod {← translateVal a} {← translateVal b})}
    if let some (_, _, _, _, a, b) := app6? e ``HMod.hMod then
      return some sexp!{(mod {← translateVal a} {← translateVal b})}
    if let some (_, _, a, b) := e.app4? ``LE.le then
      return some sexp!{(<= {← translateVal a} {← translateVal b})}
    if let some (_, _, a, b) := e.app4? ``LT.lt then
      return some sexp!{(< {← translateVal a} {← translateVal b})}
    if let some (_, _, a, b) := e.app4? ``GE.ge then
      return some sexp!{(>= {← translateVal a} {← translateVal b})}
    if let some (_, _, a, b) := e.app4? ``GT.gt then
      return some sexp!{(> {← translateVal a} {← translateVal b})}
    return none

  addExt { translateSort, translateVal }

syntax (name := smt) "smt!" : tactic
open Lean Elab Tactic in
@[tactic smt]
def smtTac : Tactic
  | stx@`(tactic| smt!) => do
    let goalTp ← getMainTarget
    let translateGoal : TranslationM (List Sexp) :=
      forallTelescopeReducing goalTp fun xs tp => do
        addTheoryCore
        addTheoryInts
        let mut cmds := #[]
        for x in xs do
          let tp ← inferType x
          let nm ← LocalDecl.userName <$> Meta.getFVarLocalDecl x
          cmds := cmds.push sexp!{(declare-const {toString nm} {← translateSort tp})}
        cmds := cmds.push sexp!{(assert {← translateVal tp})}
        return cmds.toList
    let st : TranslationState := {
      -- sortMap := Std.HashMap.empty.insert ``Int "Int"
      valMap := Std.HashMap.empty
      exts := #[]
    }
    let cmds ← translateGoal.run' st
    for cmd in cmds do
      logInfo <| toString cmd
  | _ => throwUnsupportedSyntax

end TranslationM

open Lean Elab Command in
elab "#smtlib" ts:term,* : command => do
  let st : TranslationState := {
    -- sortMap := Std.HashMap.empty.insert ``Int "Int"
    valMap := Std.HashMap.empty
    exts := #[]
  }
  let ts := ts.getElems
  liftTermElabM none do
    let mut st := st
    let (_, st') ← liftM <| TranslationM.addTheoryCore |>.run st
    st := st'
    let (_, st') ← liftM <| TranslationM.addTheoryInts |>.run st
    st := st'
    for t in ts do
      if t.isIdent then
        let (smt, st') ← liftM <| TranslationM.translateDef t.getId |>.run st
        st := st'
        logInfo <| toString smt
      else
        let e ← Term.elabTerm t none
        let (smt, st') ← liftM <| TranslationM.translateVal e |>.run st
        st := st'
        logInfo <| toString smt

open TranslationM

def fooConst : Int := 1337

def fooId (x : Int) : Int := x

def fooTwo (x y : Int) : Int := y

def fooTwo' (x y : Int) : Int := fooTwo x y

def fooArith (x y : Int) : Int := (x + y) * fooId x

def fooArith' (x y : Int) : Int := x * (y + x)

def bar (x : List Int) : List Int := List.filter (· == 1) x

#smtlib fooConst, fooId, fooTwo, fooTwo', (∀ x y, fooTwo' x y = fooTwo x y), fooArith, fooArith'

example : ∀ x y, fooTwo' x y = fooTwo x y := by
  smt!
  intro x y
  rfl

#eval callZ3 (verbose := true) sexps!{
  (set-logic LIA)
  (set-option :produce-models true)

  (define-fun fooConst () Int 1337)
  (define-fun fooId ((x Int)) Int x)
  (define-fun fooTwo ((x Int) (y Int)) Int y)
  (define-fun fooArith ((x Int) (y Int)) Int (* (+ x y) (fooId x)))
  (define-fun fooArith_ ((x Int) (y Int)) Int (* x (+ y x)))
  (declare-const a Int)
  (declare-const b Int)
  (assert (not (= (fooArith a b) (fooArith_ a b))))
  -- (assert (not (= fooArith fooArith_)))

  (check-sat)
  (get-model)
}


import mcl.defs
import mcl.rhl
--import parlang
import syncablep

open mcl
open mcl.mclk
open mcl.rhl
open parlang
open parlang.state
open parlang.thread_state

namespace assign_mcl

def sigc : signature_core
| "tid" := { scope := scope.tlocal, type := ⟨1, type.int⟩ }
| _ := { scope := scope.shared, type := ⟨1, type.int⟩ }

def sig : signature := ⟨sigc, ⟨rfl, rfl, rfl⟩⟩

lemma a_is_shared : is_shared (sig.val "a") := by apply eq.refl
lemma tid_is_tlocal : is_tlocal (sig.val "tid") := by apply eq.refl

-- TODO generate those proofs directly from signature
-- make type classes out of those
-- make name explicit in state.update
def read_tid := (@expression.tlocal_var sig _ _ "tid" (λ_, 0) rfl rfl rfl)

instance : has_one (expression sig (type_of (sig.val "b"))) := begin
    have : type_of (sig.val "b") = type.int := by apply eq.refl,
    rw this,
    apply_instance,
end

def p₁ : mclp sig := mclp.intro (λ m, 100) (
    mclk.shared_assign "a" v[read_tid] rfl rfl read_tid ;;
    mclk.shared_assign "b" v[read_tid] rfl rfl (read_tid + (expression.literal_int 1 rfl))
)

def p₂ : mclp sig := mclp.intro (λ m, 100) (
    mclk.shared_assign "b" v[read_tid] rfl rfl (read_tid + (expression.literal_int 1 rfl)) ;;
    mclk.shared_assign "a" v[read_tid] rfl rfl read_tid
)

end assign_mcl