def x := 1

#check x

variable {α : Type}

def f (a : α) : α :=
a

def tst (xs : List Nat) : Nat :=
xs.foldl (init := 10) (· + ·)

#check tst [1, 2, 3]

#check fun x y : Nat => x + y

#check tst

#check (fun stx => if True then let e := stx; Pure.pure e else Pure.pure stx : Nat → Id Nat)

#check let x : Nat := 1; x

def foo (a : Nat) (b : Nat := 10) (c : Bool := Bool.true) : Nat :=
a + b

set_option pp.all true

#check foo 1

#check foo 3 (c := false)

def Nat.boo (a : Nat) :=
succ a -- succ here is resolved as `Nat.succ`.

#check Nat.boo

#check true

-- apply is still a valid identifier name
def apply := "hello"

#check apply

theorem simple1 (x y : Nat) (h : x = y) : x = y :=
by {
  assumption
}

theorem simple2 (x y : Nat) : x = y → x = y :=
by {
  intro h;
  assumption
}

syntax "intro2" : tactic

macro_rules
| `(tactic| intro2) => `(tactic| intro; intro )

theorem simple3 (x y : Nat) : x = x → x = y → x = y :=
by {
  intro2;
  assumption
}

macro "intro3" : tactic => `(intro; intro; intro)
macro "check2" x:term : command => `(#check $x #check $x)
macro "foo" x:term "," y:term : term => `($x + $y + $x)

set_option pp.all false

check2 0+1
check2 foo 0,1

theorem simple4 (x y : Nat) : y = y → x = x → x = y → x = y :=
by {
  intro3;
  assumption
}

theorem simple5 (x y z : Nat) : y = z → x = x → x = y → x = z :=
by {
  intro h1; intro _; intro h3;
  exact Eq.trans h3 h1
}

theorem simple6 (x y z : Nat) : y = z → x = x → x = y → x = z :=
by {
  intro h1; intro _; intro h3;
  refine Eq.trans ?_ h1;
  assumption
}

theorem simple7 (x y z : Nat) : y = z → x = x → x = y → x = z :=
by {
  intro h1; intro _; intro h3;
  refine' Eq.trans ?pre ?post;
  exact y;
  { exact h3 }
  { exact h1 }
}

theorem simple8 (x y z : Nat) : y = z → x = x → x = y → x = z := by
intro h1; intro _; intro h3
refine' Eq.trans ?pre ?post
case post => exact h1
case pre => exact h3

theorem simple9 (x y z : Nat) : y = z → x = x → x = y → x = z := by
intros h1 _ h3
traceState
focus
  refine' Eq.trans ?pre ?post
  first
    | exact h1
      assumption
    | exact y
      exact h3
      assumption

theorem simple9b (x y z : Nat) : y = z → x = x → x = y → x = z := by
intros h1 _ h3
traceState
focus
  refine' Eq.trans ?pre ?post
  first
    | exact h1
    | exact y; exact h3
  assumption

theorem simple9c (x y z : Nat) : y = z → x = x → x = y → x = z := by
  intros h1 _ h3
  solve
    | exact h1
    | refine' Eq.trans ?pre ?post; exact y; exact h3; assumption
    | exact h3

theorem simple9d (x y z : Nat) : y = z → x = x → x = y → x = z := by
  intros h1 _ h3
  refine' Eq.trans ?pre ?post
  solve
    | exact h1
    | exact y
    | exact h3
  solve
    | exact h1
    | exact h3
  solve
    | exact h1
    | assumption

namespace Foo
  def Prod.mk := 1
  #check (⟨2, 3⟩ : Prod _ _)
end Foo

theorem simple10 (x y z : Nat) : y = z → x = x → x = y → x = z :=
by {
  intro h1; intro h2; intro h3;
  skip;
  apply Eq.trans;
  exact h3;
  assumption
}

theorem simple11 (x y z : Nat) : y = z → x = x → x = y → x = z :=
by {
  intro h1; intro h2; intro h3;
  apply @Eq.trans;
  traceState;
  exact h3;
  assumption
}

theorem simple12 (x y z : Nat) : y = z → x = x → x = y → x = z :=
by {
  intro h1; intro h2; intro h3;
  apply @Eq.trans;
  try exact h1; -- `exact h1` fails
  traceState;
  try exact h3;
  traceState;
  try exact h1;
}

theorem simple13 (x y z : Nat) : y = z → x = x → x = y → x = z := by
intros h1 h2 h3
traceState
apply @Eq.trans
case b => exact y
traceState
repeat assumption

theorem simple13b (x y z : Nat) : y = z → x = x → x = y → x = z := by {
intros h1 h2 h3;
traceState;
apply @Eq.trans;
case b => exact y;
traceState;
repeat assumption
}

theorem simple14 (x y z : Nat) : y = z → x = x → x = y → x = z := by
intros
apply @Eq.trans
case b => exact y
repeat assumption

theorem simple15 (x y z : Nat) : y = z → x = x → x = y → x = z :=
by {
  intros h1 h2 h3;
  revert y;
  intros y h1 h3;
  apply Eq.trans;
  exact h3;
  exact h1
}

theorem simple16 (x y z : Nat) : y = z → x = x → x = y → x = z :=
by {
  intros h1 h2 h3;
  try clear x; -- should fail
  clear h2;
  traceState;
  apply Eq.trans;
  exact h3;
  exact h1
}

macro "blabla" : tactic => `(assumption)

-- Tactic head symbols do not become reserved words
def blabla := 100

#check blabla

theorem simple17 (x : Nat) (h : x = 0) : x = 0 :=
by blabla

theorem simple18 (x : Nat) (h : x = 0) : x = 0 :=
by blabla

theorem simple19 (x y : Nat) (h₁ : x = 0) (h₂ : x = y) : y = 0 :=
by subst x; subst y; exact rfl

theorem tstprec1 (x y z : Nat) : x + y * z = x + (y * z) :=
rfl

theorem tstprec2 (x y z : Nat) : y * z + x = (y * z) + x :=
rfl

set_option pp.all true

#check fun {α} (a : α) => a
#check @(fun α (a : α) => a)

#check
  let myid := fun {α} (a : α) => a;
  myid [myid 1]

-- In the following example, we need `@` otherwise we will try to insert mvars for α and [Add α],
-- and will fail to generate instance for [Add α]
#check @(fun α (s : Add α) (a : α) => a + a)

def g1 {α} (a₁ a₂ : α) {β} (b : β) : α × α × β :=
(a₁, a₂, b)

def id1 : {α : Type} → α → α :=
fun x => x

def listId : List ({α : Type} → α → α) :=
(fun x => x) :: []

def id2 : {α : Type} → α → α :=
@(fun α (x : α) => id1 x)

def id3 : {α : Type} → α → α :=
@(fun α x => id1 x)

def id4 : {α : Type} → α → α :=
fun x => id1 x

def id5 : {α : Type} → α → α :=
fun {α} x => id1 x

def id6 : {α : Type} → α → α :=
@(fun {α} x => id1 x)

def id7 : {α : Type} → α → α :=
fun {α} x => @id α x

def id8 : {α : Type} → α → α :=
fun {α} x => id (@id α x)

def altTst1 {m σ} [Alternative m] [Monad m] : Alternative (StateT σ m) :=
⟨StateT.failure, StateT.orElse⟩

def altTst2 {m σ} [Alternative m] [Monad m] : Alternative (StateT σ m) :=
⟨@(fun α => StateT.failure), @(fun α => StateT.orElse)⟩

def altTst3 {m σ} [Alternative m] [Monad m] : Alternative (StateT σ m) :=
⟨fun {α} => StateT.failure, fun {α} => StateT.orElse⟩

#check_failure 1 + true

/-
universes u v

/-
  MonadFunctorT.{u ?M_1 v} (λ (β : Type u), m α) (λ (β : Type u), m' α) n n'
-/
set_option pp.raw.maxDepth 100
set_option trace.Elab true

def adapt {m m' σ σ'} {n n' : Type → Type} [MonadFunctor m m' n n'] [MonadStateAdapter σ σ' m m'] : MonadStateAdapter σ σ' n n' :=
⟨fun split join => monadMap (adaptState split join : m α → m' α)⟩

-/

syntax "fn" (term:max)+ "=>" term : term

macro_rules
| `(fn $xs* => $b) => `(fun $xs* => $b)

set_option pp.all false

#check fn x => x+1

#check fn α (a : α) => a

def tst1 : {α : Type} → α → α :=
@(fn α a => a)

#check @tst1

syntax ident "==>" term : term

syntax "{" ident "}" "==>" term : term

macro_rules
| `($x:ident ==> $b)   => `(fn $x => $b)
| `({$x:ident} ==> $b) => `(fun {$x:ident} => $b)

#check x ==> x+1

def tst2a : {α : Type} → α → α :=
@(α ==> a ==> a)

def tst2b : {α : Type} → α → α :=
{α} ==> a ==> a

#check @tst2a
#check @tst2b

def tst3a : {α : Type} → {β : Type} → α → β → α × β :=
@(α ==> @(β ==> a ==> b ==> (a, b)))

def tst3b : {α : Type} → {β : Type} → α → β → α × β :=
{α} ==> {β} ==> a ==> b ==> (a, b)

syntax "function" (term:max)+ "=>" term : term

macro_rules
| `(function $xs* => $b) => `(@(fun $xs* => $b))

def tst4 : {α : Type} → {β : Type} → α → β → α × β :=
function α β a b => (a, b)

theorem simple20 (x y z : Nat) : y = z → x = x → x = y → x = z :=
by intros h1 h2 h3;
   try clear x; -- should fail
   clear h2;
   traceState;
   apply Eq.trans;
   exact h3;
   exact h1

theorem simple21 (x y z : Nat) : y = z → x = x → y = x → x = z :=
fun h1 _ h3 =>
  have : x = y := by { apply Eq.symm; assumption };
  Eq.trans this (by assumption)

theorem simple22 (x y z : Nat) : y = z → y = x → id (x = z + 0) :=
fun h1 h2 => show x = z + 0 by
  apply Eq.trans
  exact h2.symm
  assumption
  skip

theorem simple23 (x y z : Nat) : y = z → x = x → y = x → x = z :=
fun h1 _ h3 =>
  have : x = y := by apply Eq.symm; assumption
  Eq.trans this (by assumption)

theorem simple24 (x y z : Nat) : y = z → x = x → y = x → x = z :=
fun h1 _ h3 =>
  have h : x = y := by apply Eq.symm; assumption
  Eq.trans h (by assumption)

def f1 (x : Nat) : Nat :=
  let double x := x + x
  let rec loop x :=
    match x with
    | 0   => 0
    | x+1 => loop x + double x
  loop x

#eval f1 5

def f2 (x : Nat) : String :=
  let bad x : String := toString x
  bad x

def f3 x y :=
  x + y + 1

theorem f3eq x y : f3 x y = x + y + 1 :=
  rfl

def f4 (x y : Nat) : String :=
  if x > y + 1 then "hello" else "world"


/-
Copyright (c) 2020 Bhavik Mehta. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Bhavik Mehta
-/
import data.list.chain
import category_theory.punit
import category_theory.is_connected
import category_theory.sigma.basic
import category_theory.full_subcategory

/-!
# Connected components of a category

Defines a type `connected_components J` indexing the connected components of a category, and the
full subcategories giving each connected component: `component j : Type u₁`.
We show that each `component j` is in fact connected.

We show every category can be expressed as a disjoint union of its connected components, in
particular `decomposed J` is the category (definitionally) given by the sigma-type of the connected
components of `J`, and it is shown that this is equivalent to `J`.
-/

universes v₁ v₂ v₃ u₁ u₂

noncomputable theory

open category_theory.category

namespace category_theory

attribute [instance, priority 100] is_connected.is_nonempty

variables {J : Type u₁} [category.{v₁} J]
variables {C : Type u₂} [category.{u₁} C]

/-- This type indexes the connected components of the category `J`. -/
def connected_components (J : Type u₁) [category.{v₁} J] : Type u₁ := quotient (zigzag.setoid J)

instance [inhabited J] : inhabited (connected_components J) := ⟨quotient.mk' default⟩

/-- Given an index for a connected component, produce the actual component as a full subcategory. -/
@[derive category]
def component (j : connected_components J) : Type u₁ := {k : J // quotient.mk' k = j}

/-- The inclusion functor from a connected component to the whole category. -/
@[derive [full, faithful], simps {rhs_md := semireducible}]
def component.ι (j) : component j ⥤ J :=
full_subcategory_inclusion _

/-- Each connected component of the category is nonempty. -/
instance (j : connected_components J) : nonempty (component j) :=
begin
  apply quotient.induction_on' j,
  intro k,
  refine ⟨⟨k, rfl⟩⟩,
end

instance (j : connected_components J) : inhabited (component j) := classical.inhabited_of_nonempty'

/-- Each connected component of the category is connected. -/
instance (j : connected_components J) : is_connected (component j) :=
begin
  -- Show it's connected by constructing a zigzag (in `component j`) between any two objects
  apply is_connected_of_zigzag,
  rintro ⟨j₁, hj₁⟩ ⟨j₂, rfl⟩,
  -- We know that the underlying objects j₁ j₂ have some zigzag between them in `J`
  have h₁₂ : zigzag j₁ j₂ := quotient.exact' hj₁,
  -- Get an explicit zigzag as a list
  rcases list.exists_chain_of_relation_refl_trans_gen h₁₂ with ⟨l, hl₁, hl₂⟩,
  -- Everything which has a zigzag to j₂ can be lifted to the same component as `j₂`.
  let f : Π x, zigzag x j₂ → component (quotient.mk' j₂) := λ x h, ⟨x, quotient.sound' h⟩,
  -- Everything in our chosen zigzag from `j₁` to `j₂` has a zigzag to `j₂`.
  have hf : ∀ (a : J), a ∈ l → zigzag a j₂,
  { intros i hi,
    apply list.chain.induction (λ t, zigzag t j₂) _ hl₁ hl₂ _ _ _ (or.inr hi),
    { intros j k,
      apply relation.refl_trans_gen.head },
    { apply relation.refl_trans_gen.refl } },
  -- Now lift the zigzag from `j₁` to `j₂` in `J` to the same thing in `component j`.
  refine ⟨l.pmap f hf, _, _⟩,
  { refine @@list.chain_pmap_of_chain _ _ _ f (λ x y _ _ h, _) hl₁ h₁₂ _,
    exact zag_of_zag_obj (component.ι _) h },
  { erw list.last_pmap _ f (j₁ :: l) (by simpa [h₁₂] using hf) (list.cons_ne_nil _ _),
    exact subtype.ext hl₂ },
end

/--
The disjoint union of `J`s connected components, written explicitly as a sigma-type with the
category structure.
This category is equivalent to `J`.
-/
abbreviation decomposed (J : Type u₁) [category.{v₁} J] :=
Σ (j : connected_components J), component j

/--
The inclusion of each component into the decomposed category. This is just `sigma.incl` but having
this abbreviation helps guide typeclass search to get the right category instance on `decomposed J`.
-/
-- This name may cause clashes further down the road, and so might need to be changed.
abbreviation inclusion (j : connected_components J) : component j ⥤ decomposed J :=
sigma.incl _

/-- The forward direction of the equivalence between the decomposed category and the original. -/
@[simps {rhs_md := semireducible}]
def decomposed_to (J : Type u₁) [category.{v₁} J] : decomposed J ⥤ J :=
sigma.desc component.ι

@[simp]
lemma inclusion_comp_decomposed_to (j : connected_components J) :
  inclusion j ⋙ decomposed_to J = component.ι j :=
rfl

instance : full (decomposed_to J) :=
{ preimage :=
  begin
    rintro ⟨j', X, hX⟩ ⟨k', Y, hY⟩ f,
    dsimp at f,
    have : j' = k',
      rw [← hX, ← hY, quotient.eq'],
      exact relation.refl_trans_gen.single (or.inl ⟨f⟩),
    subst this,
    refine sigma.sigma_hom.mk f,
  end,
  witness' :=
  begin
    rintro ⟨j', X, hX⟩ ⟨_, Y, rfl⟩ f,
    have : quotient.mk' Y = j',
    { rw [← hX, quotient.eq'],
      exact relation.refl_trans_gen.single (or.inr ⟨f⟩) },
    subst this,
    refl,
  end }

instance : faithful (decomposed_to J) :=
{ map_injective' :=
  begin
    rintro ⟨_, j, rfl⟩ ⟨_, k, hY⟩ ⟨_, _, _, f⟩ ⟨_, _, _, g⟩ e,
    change f = g at e,
    subst e,
  end }

instance : ess_surj (decomposed_to J) :=
{ mem_ess_image := λ j, ⟨⟨_, j, rfl⟩, ⟨iso.refl _⟩⟩ }

instance : is_equivalence (decomposed_to J) :=
equivalence.of_fully_faithfully_ess_surj _

/-- This gives that any category is equivalent to a disjoint union of connected categories. -/
@[simps functor {rhs_md := semireducible}]
def decomposed_equiv : decomposed J ≌ J :=
(decomposed_to J).as_equivalence

end category_theory


/-
File: signature_recover_public_key_unreduced_mul_soundness.lean

Autogenerated file.
-/
import starkware.cairo.lean.semantics.soundness.hoare
import .signature_recover_public_key_code
import ..signature_recover_public_key_spec
open tactic

open starkware.cairo.common.cairo_secp.field
open starkware.cairo.common.cairo_secp.bigint
open starkware.cairo.common.cairo_secp.constants

variables {F : Type} [field F] [decidable_eq F] [prelude_hyps F]
variable  mem : F → F
variable  σ : register_state F

/- starkware.cairo.common.cairo_secp.field.unreduced_mul autogenerated soundness theorem -/

theorem auto_sound_unreduced_mul
    -- arguments
    (a b : BigInt3 F)
    -- code is in memory at σ.pc
    (h_mem : mem_at mem code_unreduced_mul σ.pc)
    -- input arguments on the stack
    (hin_a : a = cast_BigInt3 mem (σ.fp - 8))
    (hin_b : b = cast_BigInt3 mem (σ.fp - 5))
    -- conclusion
  : ensures_ret mem σ (λ κ τ, τ.ap = σ.ap + 17 ∧ spec_unreduced_mul mem κ a b (cast_UnreducedBigInt3 mem (τ.ap - 3))) :=
begin
  apply ensures_of_ensuresb, intro νbound,
  have h_mem_rec := h_mem,
  unpack_memory code_unreduced_mul at h_mem with ⟨hpc0, hpc1, hpc2, hpc3, hpc4, hpc5, hpc6, hpc7, hpc8, hpc9, hpc10, hpc11, hpc12, hpc13, hpc14, hpc15, hpc16, hpc17, hpc18, hpc19⟩,
  -- return
  step_assert_eq hpc0 with hret0,
  step_assert_eq hpc1 with hret1,
  step_assert_eq hpc2 with hret2,
  step_assert_eq hpc3 with hret3,
  step_assert_eq hpc4 hpc5 with hret4,
  step_assert_eq hpc6 with hret5,
  step_assert_eq hpc7 with hret6,
  step_assert_eq hpc8 with hret7,
  step_assert_eq hpc9 hpc10 with hret8,
  step_assert_eq hpc11 with hret9,
  step_assert_eq hpc12 with hret10,
  step_assert_eq hpc13 with hret11,
  step_assert_eq hpc14 with hret12,
  step_assert_eq hpc15 with hret13,
  step_assert_eq hpc16 with hret14,
  step_assert_eq hpc17 with hret15,
  step_assert_eq hpc18 with hret16,
  step_ret hpc19,
  -- finish
  step_done, use_only [rfl, rfl],
  split, refl,
  -- Final Proof
  -- user-provided reduction
  suffices auto_spec: auto_spec_unreduced_mul mem _ a b _,
  { apply sound_unreduced_mul, apply auto_spec },
  -- prove the auto generated assertion
  dsimp [auto_spec_unreduced_mul],
  try { norm_num1 }, try { arith_simps },
  try { split, linarith },
  try { ensures_simps; try { simp only [add_neg_eq_sub, hin_a, hin_b] }, },
  try { dsimp [cast_BigInt3, cast_UnreducedBigInt3] },
  try { arith_simps }, try { simp only [hret0, hret1, hret2, hret3, hret4, hret5, hret6, hret7, hret8, hret9, hret10, hret11, hret12, hret13, hret14, hret15, hret16] },
  try { arith_simps; try { split }; triv <|> refl <|> simp <|> abel; try { norm_num } },
end


import Mathlib.Data.Nat.Basic

def ff (n : ℕ) : ℕ := Id.run do
  let mut r := 0
  for _ in [: n] do
    r ← r + 1
  return r

theorem ff' : ff n = n := by
  simp [ff, forIn, Std.Range.forIn]
  let rec foo : ∀ fuel a, fuel + a = n →
      Id.run (Std.Range.forIn.loop (fun x r => ForInStep.yield (r + 1)) fuel a n 1 a) = n
  | 0, a, h => by simp [Std.Range.forIn.loop]; rwa [Nat.zero_add] at h
  | fuel+1, a, h => by
    simp [Std.Range.forIn.loop]; split
    · next h' => exact le_antisymm (h ▸ Nat.le_add_left ..) h'
    · next h' => rw [← Nat.add_right_comm] at h; exact foo _ _ h
  exact foo _ _ rfl

theorem ff'' : ff n = n := by
  simp [ff, forIn, Std.Range.forIn]
  sorry


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Johannes Hölzl
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.measure_theory.measure_space
import Mathlib.measure_theory.borel_space
import Mathlib.data.indicator_function
import Mathlib.data.support
import Mathlib.PostPort

universes u v l u_1 u_2 u_3 u_4 u_5

namespace Mathlib

/-!
# Lebesgue integral for `ennreal`-valued functions

We define simple functions and show that each Borel measurable function on `ennreal` can be
approximated by a sequence of simple functions.

To prove something for an arbitrary measurable function into `ennreal`, the theorem
`measurable.ennreal_induction` shows that is it sufficient to show that the property holds for
(multiples of) characteristic functions and is closed under addition and supremum of increasing
sequences of functions.

## Notation

We introduce the following notation for the lower Lebesgue integral of a function `f : α → ennreal`.

* `∫⁻ x, f x ∂μ`: integral of a function `f : α → ennreal` with respect to a measure `μ`;
* `∫⁻ x, f x`: integral of a function `f : α → ennreal` with respect to the canonical measure
  `volume` on `α`;
* `∫⁻ x in s, f x ∂μ`: integral of a function `f : α → ennreal` over a set `s` with respect
  to a measure `μ`, defined as `∫⁻ x, f x ∂(μ.restrict s)`;
* `∫⁻ x in s, f x`: integral of a function `f : α → ennreal` over a set `s` with respect
  to the canonical measure `volume`, defined as `∫⁻ x, f x ∂(volume.restrict s)`.

-/

namespace measure_theory

/-- A function `f` from a measurable space to any type is called *simple*,
if every preimage `f ⁻¹' {x}` is measurable, and the range is finite. This structure bundles
a function with these properties. -/
structure simple_func (α : Type u) [measurable_space α] (β : Type v)
where
  to_fun : α → β
  is_measurable_fiber' : ∀ (x : β), is_measurable (to_fun ⁻¹' singleton x)
  finite_range' : set.finite (set.range to_fun)

namespace simple_func

protected instance has_coe_to_fun {α : Type u_1} {β : Type u_2} [measurable_space α] : has_coe_to_fun (simple_func α β) :=
  has_coe_to_fun.mk (fun (x : simple_func α β) => α → β) to_fun

theorem coe_injective {α : Type u_1} {β : Type u_2} [measurable_space α] {f : simple_func α β} {g : simple_func α β} (H : ⇑f = ⇑g) : f = g := sorry

theorem ext {α : Type u_1} {β : Type u_2} [measurable_space α] {f : simple_func α β} {g : simple_func α β} (H : ∀ (a : α), coe_fn f a = coe_fn g a) : f = g :=
  coe_injective (funext H)

theorem finite_range {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) : set.finite (set.range ⇑f) :=
  finite_range' f

theorem is_measurable_fiber {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) (x : β) : is_measurable (⇑f ⁻¹' singleton x) :=
  is_measurable_fiber' f x

/-- Range of a simple function `α →ₛ β` as a `finset β`. -/
protected def range {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) : finset β :=
  set.finite.to_finset (finite_range f)

@[simp] theorem mem_range {α : Type u_1} {β : Type u_2} [measurable_space α] {f : simple_func α β} {b : β} : b ∈ simple_func.range f ↔ b ∈ set.range ⇑f :=
  set.finite.mem_to_finset

theorem mem_range_self {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) (x : α) : coe_fn f x ∈ simple_func.range f :=
  iff.mpr mem_range (Exists.intro x rfl)

@[simp] theorem coe_range {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) : ↑(simple_func.range f) = set.range ⇑f :=
  set.finite.coe_to_finset (finite_range f)

theorem mem_range_of_measure_ne_zero {α : Type u_1} {β : Type u_2} [measurable_space α] {f : simple_func α β} {x : β} {μ : measure α} (H : coe_fn μ (⇑f ⁻¹' singleton x) ≠ 0) : x ∈ simple_func.range f := sorry

theorem forall_range_iff {α : Type u_1} {β : Type u_2} [measurable_space α] {f : simple_func α β} {p : β → Prop} : (∀ (y : β), y ∈ simple_func.range f → p y) ↔ ∀ (x : α), p (coe_fn f x) := sorry

theorem exists_range_iff {α : Type u_1} {β : Type u_2} [measurable_space α] {f : simple_func α β} {p : β → Prop} : (∃ (y : β), ∃ (H : y ∈ simple_func.range f), p y) ↔ ∃ (x : α), p (coe_fn f x) := sorry

theorem preimage_eq_empty_iff {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) (b : β) : ⇑f ⁻¹' singleton b = ∅ ↔ ¬b ∈ simple_func.range f :=
  iff.trans set.preimage_singleton_eq_empty (not_congr (iff.symm mem_range))

theorem exists_forall_le {α : Type u_1} {β : Type u_2} [measurable_space α] [Nonempty β] [directed_order β] (f : simple_func α β) : ∃ (C : β), ∀ (x : α), coe_fn f x ≤ C :=
  Exists.imp (fun (C : β) => iff.mp forall_range_iff) (finset.exists_le (simple_func.range f))

/-- Constant function as a `simple_func`. -/
def const (α : Type u_1) {β : Type u_2} [measurable_space α] (b : β) : simple_func α β :=
  mk (fun (a : α) => b) sorry set.finite_range_const

protected instance inhabited {α : Type u_1} {β : Type u_2} [measurable_space α] [Inhabited β] : Inhabited (simple_func α β) :=
  { default := const α Inhabited.default }

theorem const_apply {α : Type u_1} {β : Type u_2} [measurable_space α] (a : α) (b : β) : coe_fn (const α b) a = b :=
  rfl

@[simp] theorem coe_const {α : Type u_1} {β : Type u_2} [measurable_space α] (b : β) : ⇑(const α b) = function.const α b :=
  rfl

@[simp] theorem range_const {β : Type u_2} (α : Type u_1) [measurable_space α] [Nonempty α] (b : β) : simple_func.range (const α b) = singleton b := sorry

theorem is_measurable_cut {α : Type u_1} {β : Type u_2} [measurable_space α] (r : α → β → Prop) (f : simple_func α β) (h : ∀ (b : β), is_measurable (set_of fun (a : α) => r a b)) : is_measurable (set_of fun (a : α) => r a (coe_fn f a)) := sorry

theorem is_measurable_preimage {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) (s : set β) : is_measurable (⇑f ⁻¹' s) :=
  is_measurable_cut (fun (_x : α) (b : β) => b ∈ s) f fun (b : β) => is_measurable.const (b ∈ s)

/-- A simple function is measurable -/
protected theorem measurable {α : Type u_1} {β : Type u_2} [measurable_space α] [measurable_space β] (f : simple_func α β) : measurable ⇑f :=
  fun (s : set β) (_x : is_measurable s) => is_measurable_preimage f s

protected theorem ae_measurable {α : Type u_1} {β : Type u_2} [measurable_space α] [measurable_space β] {μ : measure α} (f : simple_func α β) : ae_measurable ⇑f :=
  measurable.ae_measurable (simple_func.measurable f)

protected theorem sum_measure_preimage_singleton {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) {μ : measure α} (s : finset β) : (finset.sum s fun (y : β) => coe_fn μ (⇑f ⁻¹' singleton y)) = coe_fn μ (⇑f ⁻¹' ↑s) :=
  sum_measure_preimage_singleton s fun (_x : β) (_x_1 : _x ∈ s) => is_measurable_fiber f _x

theorem sum_range_measure_preimage_singleton {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) (μ : measure α) : (finset.sum (simple_func.range f) fun (y : β) => coe_fn μ (⇑f ⁻¹' singleton y)) = coe_fn μ set.univ := sorry

/-- If-then-else as a `simple_func`. -/
def piecewise {α : Type u_1} {β : Type u_2} [measurable_space α] (s : set α) (hs : is_measurable s) (f : simple_func α β) (g : simple_func α β) : simple_func α β :=
  mk (set.piecewise s ⇑f ⇑g) sorry sorry

@[simp] theorem coe_piecewise {α : Type u_1} {β : Type u_2} [measurable_space α] {s : set α} (hs : is_measurable s) (f : simple_func α β) (g : simple_func α β) : ⇑(piecewise s hs f g) = set.piecewise s ⇑f ⇑g :=
  rfl

theorem piecewise_apply {α : Type u_1} {β : Type u_2} [measurable_space α] {s : set α} (hs : is_measurable s) (f : simple_func α β) (g : simple_func α β) (a : α) : coe_fn (piecewise s hs f g) a = ite (a ∈ s) (coe_fn f a) (coe_fn g a) :=
  rfl

@[simp] theorem piecewise_compl {α : Type u_1} {β : Type u_2} [measurable_space α] {s : set α} (hs : is_measurable (sᶜ)) (f : simple_func α β) (g : simple_func α β) : piecewise (sᶜ) hs f g = piecewise s (is_measurable.of_compl hs) g f := sorry

@[simp] theorem piecewise_univ {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) (g : simple_func α β) : piecewise set.univ is_measurable.univ f g = f := sorry

@[simp] theorem piecewise_empty {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) (g : simple_func α β) : piecewise ∅ is_measurable.empty f g = g := sorry

theorem measurable_bind {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [measurable_space γ] (f : simple_func α β) (g : β → α → γ) (hg : ∀ (b : β), measurable (g b)) : measurable fun (a : α) => g (coe_fn f a) a :=
  fun (s : set γ) (hs : is_measurable s) => is_measurable_cut (fun (a : α) (b : β) => g b a ∈ s) f fun (b : β) => hg b hs

/-- If `f : α →ₛ β` is a simple function and `g : β → α →ₛ γ` is a family of simple functions,
then `f.bind g` binds the first argument of `g` to `f`. In other words, `f.bind g a = g (f a) a`. -/
def bind {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (f : simple_func α β) (g : β → simple_func α γ) : simple_func α γ :=
  mk (fun (a : α) => coe_fn (g (coe_fn f a)) a) sorry sorry

@[simp] theorem bind_apply {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (f : simple_func α β) (g : β → simple_func α γ) (a : α) : coe_fn (bind f g) a = coe_fn (g (coe_fn f a)) a :=
  rfl

/-- Given a function `g : β → γ` and a simple function `f : α →ₛ β`, `f.map g` return the simple
    function `g ∘ f : α →ₛ γ` -/
def map {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (g : β → γ) (f : simple_func α β) : simple_func α γ :=
  bind f (const α ∘ g)

theorem map_apply {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (g : β → γ) (f : simple_func α β) (a : α) : coe_fn (map g f) a = g (coe_fn f a) :=
  rfl

theorem map_map {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} [measurable_space α] (g : β → γ) (h : γ → δ) (f : simple_func α β) : map h (map g f) = map (h ∘ g) f :=
  rfl

@[simp] theorem coe_map {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (g : β → γ) (f : simple_func α β) : ⇑(map g f) = g ∘ ⇑f :=
  rfl

@[simp] theorem range_map {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [DecidableEq γ] (g : β → γ) (f : simple_func α β) : simple_func.range (map g f) = finset.image g (simple_func.range f) := sorry

@[simp] theorem map_const {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (g : β → γ) (b : β) : map g (const α b) = const α (g b) :=
  rfl

theorem map_preimage {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (f : simple_func α β) (g : β → γ) (s : set γ) : ⇑(map g f) ⁻¹' s = ⇑f ⁻¹' ↑(finset.filter (fun (b : β) => g b ∈ s) (simple_func.range f)) := sorry

theorem map_preimage_singleton {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (f : simple_func α β) (g : β → γ) (c : γ) : ⇑(map g f) ⁻¹' singleton c = ⇑f ⁻¹' ↑(finset.filter (fun (b : β) => g b = c) (simple_func.range f)) :=
  map_preimage f g (singleton c)

/-- Composition of a `simple_fun` and a measurable function is a `simple_func`. -/
def comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [measurable_space β] (f : simple_func β γ) (g : α → β) (hgm : measurable g) : simple_func α γ :=
  mk (⇑f ∘ g) sorry sorry

@[simp] theorem coe_comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [measurable_space β] (f : simple_func β γ) {g : α → β} (hgm : measurable g) : ⇑(comp f g hgm) = ⇑f ∘ g :=
  rfl

theorem range_comp_subset_range {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [measurable_space β] (f : simple_func β γ) {g : α → β} (hgm : measurable g) : simple_func.range (comp f g hgm) ⊆ simple_func.range f := sorry

/-- If `f` is a simple function taking values in `β → γ` and `g` is another simple function
with the same domain and codomain `β`, then `f.seq g = f a (g a)`. -/
def seq {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (f : simple_func α (β → γ)) (g : simple_func α β) : simple_func α γ :=
  bind f fun (f : β → γ) => map f g

@[simp] theorem seq_apply {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (f : simple_func α (β → γ)) (g : simple_func α β) (a : α) : coe_fn (seq f g) a = coe_fn f a (coe_fn g a) :=
  rfl

/-- Combine two simple functions `f : α →ₛ β` and `g : α →ₛ β`
into `λ a, (f a, g a)`. -/
def pair {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (f : simple_func α β) (g : simple_func α γ) : simple_func α (β × γ) :=
  seq (map Prod.mk f) g

@[simp] theorem pair_apply {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (f : simple_func α β) (g : simple_func α γ) (a : α) : coe_fn (pair f g) a = (coe_fn f a, coe_fn g a) :=
  rfl

theorem pair_preimage {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (f : simple_func α β) (g : simple_func α γ) (s : set β) (t : set γ) : ⇑(pair f g) ⁻¹' set.prod s t = ⇑f ⁻¹' s ∩ ⇑g ⁻¹' t :=
  rfl

/- A special form of `pair_preimage` -/

theorem pair_preimage_singleton {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] (f : simple_func α β) (g : simple_func α γ) (b : β) (c : γ) : ⇑(pair f g) ⁻¹' singleton (b, c) = ⇑f ⁻¹' singleton b ∩ ⇑g ⁻¹' singleton c := sorry

theorem bind_const {α : Type u_1} {β : Type u_2} [measurable_space α] (f : simple_func α β) : bind f (const α) = f := sorry

protected instance has_zero {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] : HasZero (simple_func α β) :=
  { zero := const α 0 }

protected instance has_add {α : Type u_1} {β : Type u_2} [measurable_space α] [Add β] : Add (simple_func α β) :=
  { add := fun (f g : simple_func α β) => seq (map Add.add f) g }

protected instance has_mul {α : Type u_1} {β : Type u_2} [measurable_space α] [Mul β] : Mul (simple_func α β) :=
  { mul := fun (f g : simple_func α β) => seq (map Mul.mul f) g }

protected instance has_sup {α : Type u_1} {β : Type u_2} [measurable_space α] [has_sup β] : has_sup (simple_func α β) :=
  has_sup.mk fun (f g : simple_func α β) => seq (map has_sup.sup f) g

protected instance has_inf {α : Type u_1} {β : Type u_2} [measurable_space α] [has_inf β] : has_inf (simple_func α β) :=
  has_inf.mk fun (f g : simple_func α β) => seq (map has_inf.inf f) g

protected instance has_le {α : Type u_1} {β : Type u_2} [measurable_space α] [HasLessEq β] : HasLessEq (simple_func α β) :=
  { LessEq := fun (f g : simple_func α β) => ∀ (a : α), coe_fn f a ≤ coe_fn g a }

@[simp] theorem coe_zero {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] : ⇑0 = 0 :=
  rfl

@[simp] theorem const_zero {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] : const α 0 = 0 :=
  rfl

@[simp] theorem coe_add {α : Type u_1} {β : Type u_2} [measurable_space α] [Add β] (f : simple_func α β) (g : simple_func α β) : ⇑(f + g) = ⇑f + ⇑g :=
  rfl

@[simp] theorem coe_mul {α : Type u_1} {β : Type u_2} [measurable_space α] [Mul β] (f : simple_func α β) (g : simple_func α β) : ⇑(f * g) = ⇑f * ⇑g :=
  rfl

@[simp] theorem coe_le {α : Type u_1} {β : Type u_2} [measurable_space α] [preorder β] {f : simple_func α β} {g : simple_func α β} : ⇑f ≤ ⇑g ↔ f ≤ g :=
  iff.rfl

@[simp] theorem range_zero {α : Type u_1} {β : Type u_2} [measurable_space α] [Nonempty α] [HasZero β] : simple_func.range 0 = singleton 0 := sorry

theorem eq_zero_of_mem_range_zero {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] {y : β} : y ∈ simple_func.range 0 → y = 0 :=
  iff.mpr forall_range_iff fun (x : α) => rfl

theorem sup_apply {α : Type u_1} {β : Type u_2} [measurable_space α] [has_sup β] (f : simple_func α β) (g : simple_func α β) (a : α) : coe_fn (f ⊔ g) a = coe_fn f a ⊔ coe_fn g a :=
  rfl

theorem mul_apply {α : Type u_1} {β : Type u_2} [measurable_space α] [Mul β] (f : simple_func α β) (g : simple_func α β) (a : α) : coe_fn (f * g) a = coe_fn f a * coe_fn g a :=
  rfl

theorem add_apply {α : Type u_1} {β : Type u_2} [measurable_space α] [Add β] (f : simple_func α β) (g : simple_func α β) (a : α) : coe_fn (f + g) a = coe_fn f a + coe_fn g a :=
  rfl

theorem add_eq_map₂ {α : Type u_1} {β : Type u_2} [measurable_space α] [Add β] (f : simple_func α β) (g : simple_func α β) : f + g = map (fun (p : β × β) => prod.fst p + prod.snd p) (pair f g) :=
  rfl

theorem mul_eq_map₂ {α : Type u_1} {β : Type u_2} [measurable_space α] [Mul β] (f : simple_func α β) (g : simple_func α β) : f * g = map (fun (p : β × β) => prod.fst p * prod.snd p) (pair f g) :=
  rfl

theorem sup_eq_map₂ {α : Type u_1} {β : Type u_2} [measurable_space α] [has_sup β] (f : simple_func α β) (g : simple_func α β) : f ⊔ g = map (fun (p : β × β) => prod.fst p ⊔ prod.snd p) (pair f g) :=
  rfl

theorem const_mul_eq_map {α : Type u_1} {β : Type u_2} [measurable_space α] [Mul β] (f : simple_func α β) (b : β) : const α b * f = map (fun (a : β) => b * a) f :=
  rfl

theorem map_add {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [Add β] [Add γ] {g : β → γ} (hg : ∀ (x y : β), g (x + y) = g x + g y) (f₁ : simple_func α β) (f₂ : simple_func α β) : map g (f₁ + f₂) = map g f₁ + map g f₂ :=
  ext fun (x : α) => hg (coe_fn f₁ x) (coe_fn f₂ x)

protected instance add_monoid {α : Type u_1} {β : Type u_2} [measurable_space α] [add_monoid β] : add_monoid (simple_func α β) :=
  function.injective.add_monoid (fun (f : simple_func α β) => (fun (this : α → β) => this) ⇑f) coe_injective sorry sorry

protected instance add_comm_monoid {α : Type u_1} {β : Type u_2} [measurable_space α] [add_comm_monoid β] : add_comm_monoid (simple_func α β) :=
  function.injective.add_comm_monoid (fun (f : simple_func α β) => (fun (this : α → β) => this) ⇑f) coe_injective sorry
    sorry

protected instance has_neg {α : Type u_1} {β : Type u_2} [measurable_space α] [Neg β] : Neg (simple_func α β) :=
  { neg := fun (f : simple_func α β) => map Neg.neg f }

@[simp] theorem coe_neg {α : Type u_1} {β : Type u_2} [measurable_space α] [Neg β] (f : simple_func α β) : ⇑(-f) = -⇑f :=
  rfl

protected instance has_sub {α : Type u_1} {β : Type u_2} [measurable_space α] [Sub β] : Sub (simple_func α β) :=
  { sub := fun (f g : simple_func α β) => seq (map Sub.sub f) g }

@[simp] theorem coe_sub {α : Type u_1} {β : Type u_2} [measurable_space α] [Sub β] (f : simple_func α β) (g : simple_func α β) : ⇑(f - g) = ⇑f - ⇑g :=
  rfl

theorem sub_apply {α : Type u_1} {β : Type u_2} [measurable_space α] [Sub β] (f : simple_func α β) (g : simple_func α β) (x : α) : coe_fn (f - g) x = coe_fn f x - coe_fn g x :=
  rfl

protected instance add_group {α : Type u_1} {β : Type u_2} [measurable_space α] [add_group β] : add_group (simple_func α β) :=
  function.injective.add_group_sub (fun (f : simple_func α β) => (fun (this : α → β) => this) ⇑f) coe_injective sorry
    sorry sorry sorry

protected instance add_comm_group {α : Type u_1} {β : Type u_2} [measurable_space α] [add_comm_group β] : add_comm_group (simple_func α β) :=
  function.injective.add_comm_group_sub (fun (f : simple_func α β) => (fun (this : α → β) => this) ⇑f) coe_injective sorry
    sorry sorry sorry

protected instance has_scalar {α : Type u_1} {β : Type u_2} [measurable_space α] {K : Type u_5} [has_scalar K β] : has_scalar K (simple_func α β) :=
  has_scalar.mk fun (k : K) (f : simple_func α β) => map (has_scalar.smul k) f

@[simp] theorem coe_smul {α : Type u_1} {β : Type u_2} [measurable_space α] {K : Type u_5} [has_scalar K β] (c : K) (f : simple_func α β) : ⇑(c • f) = c • ⇑f :=
  rfl

theorem smul_apply {α : Type u_1} {β : Type u_2} [measurable_space α] {K : Type u_5} [has_scalar K β] (k : K) (f : simple_func α β) (a : α) : coe_fn (k • f) a = k • coe_fn f a :=
  rfl

protected instance semimodule {α : Type u_1} {β : Type u_2} [measurable_space α] {K : Type u_5} [semiring K] [add_comm_monoid β] [semimodule K β] : semimodule K (simple_func α β) :=
  function.injective.semimodule K
    (add_monoid_hom.mk (fun (f : simple_func α β) => (fun (this : α → β) => this) ⇑f) sorry sorry) coe_injective sorry

theorem smul_eq_map {α : Type u_1} {β : Type u_2} [measurable_space α] {K : Type u_5} [has_scalar K β] (k : K) (f : simple_func α β) : k • f = map (has_scalar.smul k) f :=
  rfl

protected instance preorder {α : Type u_1} {β : Type u_2} [measurable_space α] [preorder β] : preorder (simple_func α β) :=
  preorder.mk LessEq (fun (a b : simple_func α β) => a ≤ b ∧ ¬b ≤ a) sorry sorry

protected instance partial_order {α : Type u_1} {β : Type u_2} [measurable_space α] [partial_order β] : partial_order (simple_func α β) :=
  partial_order.mk preorder.le preorder.lt sorry sorry sorry

protected instance order_bot {α : Type u_1} {β : Type u_2} [measurable_space α] [order_bot β] : order_bot (simple_func α β) :=
  order_bot.mk (const α ⊥) partial_order.le partial_order.lt sorry sorry sorry sorry

protected instance order_top {α : Type u_1} {β : Type u_2} [measurable_space α] [order_top β] : order_top (simple_func α β) :=
  order_top.mk (const α ⊤) partial_order.le partial_order.lt sorry sorry sorry sorry

protected instance semilattice_inf {α : Type u_1} {β : Type u_2} [measurable_space α] [semilattice_inf β] : semilattice_inf (simple_func α β) :=
  semilattice_inf.mk has_inf.inf partial_order.le partial_order.lt sorry sorry sorry sorry sorry sorry

protected instance semilattice_sup {α : Type u_1} {β : Type u_2} [measurable_space α] [semilattice_sup β] : semilattice_sup (simple_func α β) :=
  semilattice_sup.mk has_sup.sup partial_order.le partial_order.lt sorry sorry sorry sorry sorry sorry

protected instance semilattice_sup_bot {α : Type u_1} {β : Type u_2} [measurable_space α] [semilattice_sup_bot β] : semilattice_sup_bot (simple_func α β) :=
  semilattice_sup_bot.mk order_bot.bot semilattice_sup.le semilattice_sup.lt sorry sorry sorry sorry semilattice_sup.sup
    sorry sorry sorry

protected instance lattice {α : Type u_1} {β : Type u_2} [measurable_space α] [lattice β] : lattice (simple_func α β) :=
  lattice.mk semilattice_sup.sup semilattice_sup.le semilattice_sup.lt sorry sorry sorry sorry sorry sorry
    semilattice_inf.inf sorry sorry sorry

protected instance bounded_lattice {α : Type u_1} {β : Type u_2} [measurable_space α] [bounded_lattice β] : bounded_lattice (simple_func α β) :=
  bounded_lattice.mk lattice.sup lattice.le lattice.lt sorry sorry sorry sorry sorry sorry lattice.inf sorry sorry sorry
    order_top.top sorry order_bot.bot sorry

theorem finset_sup_apply {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [semilattice_sup_bot β] {f : γ → simple_func α β} (s : finset γ) (a : α) : coe_fn (finset.sup s f) a = finset.sup s fun (c : γ) => coe_fn (f c) a := sorry

/-- Restrict a simple function `f : α →ₛ β` to a set `s`. If `s` is measurable,
then `f.restrict s a = if a ∈ s then f a else 0`, otherwise `f.restrict s = const α 0`. -/
def restrict {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] (f : simple_func α β) (s : set α) : simple_func α β :=
  dite (is_measurable s) (fun (hs : is_measurable s) => piecewise s hs f 0) fun (hs : ¬is_measurable s) => 0

theorem restrict_of_not_measurable {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] {f : simple_func α β} {s : set α} (hs : ¬is_measurable s) : restrict f s = 0 :=
  dif_neg hs

@[simp] theorem coe_restrict {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] (f : simple_func α β) {s : set α} (hs : is_measurable s) : ⇑(restrict f s) = set.indicator s ⇑f := sorry

@[simp] theorem restrict_univ {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] (f : simple_func α β) : restrict f set.univ = f := sorry

@[simp] theorem restrict_empty {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] (f : simple_func α β) : restrict f ∅ = 0 := sorry

theorem map_restrict_of_zero {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [HasZero β] [HasZero γ] {g : β → γ} (hg : g 0 = 0) (f : simple_func α β) (s : set α) : map g (restrict f s) = restrict (map g f) s := sorry

theorem map_coe_ennreal_restrict {α : Type u_1} [measurable_space α] (f : simple_func α nnreal) (s : set α) : map coe (restrict f s) = restrict (map coe f) s :=
  map_restrict_of_zero ennreal.coe_zero f s

theorem map_coe_nnreal_restrict {α : Type u_1} [measurable_space α] (f : simple_func α nnreal) (s : set α) : map coe (restrict f s) = restrict (map coe f) s :=
  map_restrict_of_zero nnreal.coe_zero f s

theorem restrict_apply {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] (f : simple_func α β) {s : set α} (hs : is_measurable s) (a : α) : coe_fn (restrict f s) a = ite (a ∈ s) (coe_fn f a) 0 := sorry

theorem restrict_preimage {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] (f : simple_func α β) {s : set α} (hs : is_measurable s) {t : set β} (ht : ¬0 ∈ t) : ⇑(restrict f s) ⁻¹' t = s ∩ ⇑f ⁻¹' t := sorry

theorem restrict_preimage_singleton {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] (f : simple_func α β) {s : set α} (hs : is_measurable s) {r : β} (hr : r ≠ 0) : ⇑(restrict f s) ⁻¹' singleton r = s ∩ ⇑f ⁻¹' singleton r :=
  restrict_preimage f hs (ne.symm hr)

theorem mem_restrict_range {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] {r : β} {s : set α} {f : simple_func α β} (hs : is_measurable s) : r ∈ simple_func.range (restrict f s) ↔ r = 0 ∧ s ≠ set.univ ∨ r ∈ ⇑f '' s := sorry

theorem mem_image_of_mem_range_restrict {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] {r : β} {s : set α} {f : simple_func α β} (hr : r ∈ simple_func.range (restrict f s)) (h0 : r ≠ 0) : r ∈ ⇑f '' s := sorry

theorem restrict_mono {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] [preorder β] (s : set α) {f : simple_func α β} {g : simple_func α β} (H : f ≤ g) : restrict f s ≤ restrict g s := sorry

/-- Fix a sequence `i : ℕ → β`. Given a function `α → β`, its `n`-th approximation
by simple functions is defined so that in case `β = ennreal` it sends each `a` to the supremum
of the set `{i k | k ≤ n ∧ i k ≤ f a}`, see `approx_apply` and `supr_approx_apply` for details. -/
def approx {α : Type u_1} {β : Type u_2} [measurable_space α] [semilattice_sup_bot β] [HasZero β] (i : ℕ → β) (f : α → β) (n : ℕ) : simple_func α β :=
  finset.sup (finset.range n) fun (k : ℕ) => restrict (const α (i k)) (set_of fun (a : α) => i k ≤ f a)

theorem approx_apply {α : Type u_1} {β : Type u_2} [measurable_space α] [semilattice_sup_bot β] [HasZero β] [topological_space β] [order_closed_topology β] [measurable_space β] [opens_measurable_space β] {i : ℕ → β} {f : α → β} {n : ℕ} (a : α) (hf : measurable f) : coe_fn (approx i f n) a = finset.sup (finset.range n) fun (k : ℕ) => ite (i k ≤ f a) (i k) 0 := sorry

theorem monotone_approx {α : Type u_1} {β : Type u_2} [measurable_space α] [semilattice_sup_bot β] [HasZero β] (i : ℕ → β) (f : α → β) : monotone (approx i f) :=
  fun (n m : ℕ) (h : n ≤ m) => finset.sup_mono (iff.mpr finset.range_subset h)

theorem approx_comp {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [semilattice_sup_bot β] [HasZero β] [topological_space β] [order_closed_topology β] [measurable_space β] [opens_measurable_space β] [measurable_space γ] {i : ℕ → β} {f : γ → β} {g : α → γ} {n : ℕ} (a : α) (hf : measurable f) (hg : measurable g) : coe_fn (approx i (f ∘ g) n) a = coe_fn (approx i f n) (g a) := sorry

theorem supr_approx_apply {α : Type u_1} {β : Type u_2} [measurable_space α] [topological_space β] [complete_lattice β] [order_closed_topology β] [HasZero β] [measurable_space β] [opens_measurable_space β] (i : ℕ → β) (f : α → β) (a : α) (hf : measurable f) (h_zero : 0 = ⊥) : (supr fun (n : ℕ) => coe_fn (approx i f n) a) = supr fun (k : ℕ) => supr fun (h : i k ≤ f a) => i k := sorry

/-- A sequence of `ennreal`s such that its range is the set of non-negative rational numbers. -/
def ennreal_rat_embed (n : ℕ) : ennreal :=
  ennreal.of_real ↑(option.get_or_else (encodable.decode ℚ n) 0)

theorem ennreal_rat_embed_encode (q : ℚ) : ennreal_rat_embed (encodable.encode q) = ↑(nnreal.of_real ↑q) := sorry

/-- Approximate a function `α → ennreal` by a sequence of simple functions. -/
def eapprox {α : Type u_1} [measurable_space α] : (α → ennreal) → ℕ → simple_func α ennreal :=
  approx ennreal_rat_embed

theorem monotone_eapprox {α : Type u_1} [measurable_space α] (f : α → ennreal) : monotone (eapprox f) :=
  monotone_approx ennreal_rat_embed f

theorem supr_eapprox_apply {α : Type u_1} [measurable_space α] (f : α → ennreal) (hf : measurable f) (a : α) : (supr fun (n : ℕ) => coe_fn (eapprox f n) a) = f a := sorry

theorem eapprox_comp {α : Type u_1} {γ : Type u_3} [measurable_space α] [measurable_space γ] {f : γ → ennreal} {g : α → γ} {n : ℕ} (hf : measurable f) (hg : measurable g) : ⇑(eapprox (f ∘ g) n) = ⇑(eapprox f n) ∘ g :=
  funext fun (a : α) => approx_comp a hf hg

/-- Integral of a simple function whose codomain is `ennreal`. -/
def lintegral {α : Type u_1} [measurable_space α] (f : simple_func α ennreal) (μ : measure α) : ennreal :=
  finset.sum (simple_func.range f) fun (x : ennreal) => x * coe_fn μ (⇑f ⁻¹' singleton x)

theorem lintegral_eq_of_subset {α : Type u_1} [measurable_space α] {μ : measure α} (f : simple_func α ennreal) {s : finset ennreal} (hs : ∀ (x : α), coe_fn f x ≠ 0 → coe_fn μ (⇑f ⁻¹' singleton (coe_fn f x)) ≠ 0 → coe_fn f x ∈ s) : lintegral f μ = finset.sum s fun (x : ennreal) => x * coe_fn μ (⇑f ⁻¹' singleton x) := sorry

/-- Calculate the integral of `(g ∘ f)`, where `g : β → ennreal` and `f : α →ₛ β`.  -/
theorem map_lintegral {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} (g : β → ennreal) (f : simple_func α β) : lintegral (map g f) μ = finset.sum (simple_func.range f) fun (x : β) => g x * coe_fn μ (⇑f ⁻¹' singleton x) := sorry

theorem add_lintegral {α : Type u_1} [measurable_space α] {μ : measure α} (f : simple_func α ennreal) (g : simple_func α ennreal) : lintegral (f + g) μ = lintegral f μ + lintegral g μ := sorry

theorem const_mul_lintegral {α : Type u_1} [measurable_space α] {μ : measure α} (f : simple_func α ennreal) (x : ennreal) : lintegral (const α x * f) μ = x * lintegral f μ := sorry

/-- Integral of a simple function `α →ₛ ennreal` as a bilinear map. -/
def lintegralₗ {α : Type u_1} [measurable_space α] : linear_map ennreal (simple_func α ennreal) (linear_map ennreal (measure α) ennreal) :=
  linear_map.mk (fun (f : simple_func α ennreal) => linear_map.mk (lintegral f) sorry sorry) sorry sorry

@[simp] theorem zero_lintegral {α : Type u_1} [measurable_space α] {μ : measure α} : lintegral 0 μ = 0 :=
  iff.mp linear_map.ext_iff (linear_map.map_zero lintegralₗ) μ

theorem lintegral_add {α : Type u_1} [measurable_space α] {μ : measure α} {ν : measure α} (f : simple_func α ennreal) : lintegral f (μ + ν) = lintegral f μ + lintegral f ν :=
  linear_map.map_add (coe_fn lintegralₗ f) μ ν

theorem lintegral_smul {α : Type u_1} [measurable_space α] {μ : measure α} (f : simple_func α ennreal) (c : ennreal) : lintegral f (c • μ) = c • lintegral f μ :=
  linear_map.map_smul (coe_fn lintegralₗ f) c μ

@[simp] theorem lintegral_zero {α : Type u_1} [measurable_space α] (f : simple_func α ennreal) : lintegral f 0 = 0 :=
  linear_map.map_zero (coe_fn lintegralₗ f)

theorem lintegral_sum {α : Type u_1} [measurable_space α] {ι : Type u_2} (f : simple_func α ennreal) (μ : ι → measure α) : lintegral f (measure.sum μ) = tsum fun (i : ι) => lintegral f (μ i) := sorry

theorem restrict_lintegral {α : Type u_1} [measurable_space α] {μ : measure α} (f : simple_func α ennreal) {s : set α} (hs : is_measurable s) : lintegral (restrict f s) μ = finset.sum (simple_func.range f) fun (r : ennreal) => r * coe_fn μ (⇑f ⁻¹' singleton r ∩ s) := sorry

theorem lintegral_restrict {α : Type u_1} [measurable_space α] (f : simple_func α ennreal) (s : set α) (μ : measure α) : lintegral f (measure.restrict μ s) =
  finset.sum (simple_func.range f) fun (y : ennreal) => y * coe_fn μ (⇑f ⁻¹' singleton y ∩ s) := sorry

theorem restrict_lintegral_eq_lintegral_restrict {α : Type u_1} [measurable_space α] {μ : measure α} (f : simple_func α ennreal) {s : set α} (hs : is_measurable s) : lintegral (restrict f s) μ = lintegral f (measure.restrict μ s) := sorry

theorem const_lintegral {α : Type u_1} [measurable_space α] {μ : measure α} (c : ennreal) : lintegral (const α c) μ = c * coe_fn μ set.univ := sorry

theorem const_lintegral_restrict {α : Type u_1} [measurable_space α] {μ : measure α} (c : ennreal) (s : set α) : lintegral (const α c) (measure.restrict μ s) = c * coe_fn μ s := sorry

theorem restrict_const_lintegral {α : Type u_1} [measurable_space α] {μ : measure α} (c : ennreal) {s : set α} (hs : is_measurable s) : lintegral (restrict (const α c) s) μ = c * coe_fn μ s := sorry

theorem le_sup_lintegral {α : Type u_1} [measurable_space α] {μ : measure α} (f : simple_func α ennreal) (g : simple_func α ennreal) : lintegral f μ ⊔ lintegral g μ ≤ lintegral (f ⊔ g) μ := sorry

/-- `simple_func.lintegral` is monotone both in function and in measure. -/
theorem lintegral_mono {α : Type u_1} [measurable_space α] {f : simple_func α ennreal} {g : simple_func α ennreal} (hfg : f ≤ g) {μ : measure α} {ν : measure α} (hμν : μ ≤ ν) : lintegral f μ ≤ lintegral g ν := sorry

/-- `simple_func.lintegral` depends only on the measures of `f ⁻¹' {y}`. -/
theorem lintegral_eq_of_measure_preimage {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} [measurable_space β] {f : simple_func α ennreal} {g : simple_func β ennreal} {ν : measure β} (H : ∀ (y : ennreal), coe_fn μ (⇑f ⁻¹' singleton y) = coe_fn ν (⇑g ⁻¹' singleton y)) : lintegral f μ = lintegral g ν := sorry

/-- If two simple functions are equal a.e., then their `lintegral`s are equal. -/
theorem lintegral_congr {α : Type u_1} [measurable_space α] {μ : measure α} {f : simple_func α ennreal} {g : simple_func α ennreal} (h : filter.eventually_eq (measure.ae μ) ⇑f ⇑g) : lintegral f μ = lintegral g μ := sorry

theorem lintegral_map {α : Type u_1} [measurable_space α] {μ : measure α} {β : Type u_2} [measurable_space β] {μ' : measure β} (f : simple_func α ennreal) (g : simple_func β ennreal) (m : α → β) (eq : ∀ (a : α), coe_fn f a = coe_fn g (m a)) (h : ∀ (s : set β), is_measurable s → coe_fn μ' s = coe_fn μ (m ⁻¹' s)) : lintegral f μ = lintegral g μ' := sorry

theorem support_eq {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] (f : simple_func α β) : function.support ⇑f =
  set.Union
    fun (y : β) =>
      set.Union fun (H : y ∈ finset.filter (fun (y : β) => y ≠ 0) (simple_func.range f)) => ⇑f ⁻¹' singleton y := sorry

/-- A `simple_func` has finite measure support if it is equal to `0` outside of a set of finite
measure. -/
protected def fin_meas_supp {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] (f : simple_func α β) (μ : measure α) :=
  filter.eventually_eq (measure.cofinite μ) (⇑f) 0

theorem fin_meas_supp_iff_support {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] {f : simple_func α β} {μ : measure α} : simple_func.fin_meas_supp f μ ↔ coe_fn μ (function.support ⇑f) < ⊤ :=
  iff.rfl

theorem fin_meas_supp_iff {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] {f : simple_func α β} {μ : measure α} : simple_func.fin_meas_supp f μ ↔ ∀ (y : β), y ≠ 0 → coe_fn μ (⇑f ⁻¹' singleton y) < ⊤ := sorry

namespace fin_meas_supp

theorem meas_preimage_singleton_ne_zero {α : Type u_1} {β : Type u_2} [measurable_space α] [HasZero β] {μ : measure α} {f : simple_func α β} (h : simple_func.fin_meas_supp f μ) {y : β} (hy : y ≠ 0) : coe_fn μ (⇑f ⁻¹' singleton y) < ⊤ :=
  iff.mp fin_meas_supp_iff h y hy

protected theorem map {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [HasZero β] [HasZero γ] {μ : measure α} {f : simple_func α β} {g : β → γ} (hf : simple_func.fin_meas_supp f μ) (hg : g 0 = 0) : simple_func.fin_meas_supp (map g f) μ :=
  flip lt_of_le_of_lt hf (measure_mono (function.support_comp_subset hg ⇑f))

theorem of_map {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [HasZero β] [HasZero γ] {μ : measure α} {f : simple_func α β} {g : β → γ} (h : simple_func.fin_meas_supp (map g f) μ) (hg : ∀ (b : β), g b = 0 → b = 0) : simple_func.fin_meas_supp f μ :=
  flip lt_of_le_of_lt h (measure_mono (function.support_subset_comp hg fun (x : α) => coe_fn f x))

theorem map_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [HasZero β] [HasZero γ] {μ : measure α} {f : simple_func α β} {g : β → γ} (hg : ∀ {b : β}, g b = 0 ↔ b = 0) : simple_func.fin_meas_supp (map g f) μ ↔ simple_func.fin_meas_supp f μ :=
  { mp := fun (h : simple_func.fin_meas_supp (map g f) μ) => of_map h fun (b : β) => iff.mp hg,
    mpr := fun (h : simple_func.fin_meas_supp f μ) => fin_meas_supp.map h (iff.mpr hg rfl) }

protected theorem pair {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] [HasZero β] [HasZero γ] {μ : measure α} {f : simple_func α β} {g : simple_func α γ} (hf : simple_func.fin_meas_supp f μ) (hg : simple_func.fin_meas_supp g μ) : simple_func.fin_meas_supp (pair f g) μ := sorry

protected theorem map₂ {α : Type u_1} {β : Type u_2} {γ : Type u_3} {δ : Type u_4} [measurable_space α] [HasZero β] [HasZero γ] [HasZero δ] {μ : measure α} {f : simple_func α β} (hf : simple_func.fin_meas_supp f μ) {g : simple_func α γ} (hg : simple_func.fin_meas_supp g μ) {op : β → γ → δ} (H : op 0 0 = 0) : simple_func.fin_meas_supp (map (function.uncurry op) (pair f g)) μ :=
  fin_meas_supp.map (fin_meas_supp.pair hf hg) H

protected theorem add {α : Type u_1} [measurable_space α] {μ : measure α} {β : Type u_2} [add_monoid β] {f : simple_func α β} {g : simple_func α β} (hf : simple_func.fin_meas_supp f μ) (hg : simple_func.fin_meas_supp g μ) : simple_func.fin_meas_supp (f + g) μ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (simple_func.fin_meas_supp (f + g) μ)) (add_eq_map₂ f g)))
    (fin_meas_supp.map₂ hf hg (zero_add 0))

protected theorem mul {α : Type u_1} [measurable_space α] {μ : measure α} {β : Type u_2} [monoid_with_zero β] {f : simple_func α β} {g : simple_func α β} (hf : simple_func.fin_meas_supp f μ) (hg : simple_func.fin_meas_supp g μ) : simple_func.fin_meas_supp (f * g) μ :=
  eq.mpr (id (Eq._oldrec (Eq.refl (simple_func.fin_meas_supp (f * g) μ)) (mul_eq_map₂ f g)))
    (fin_meas_supp.map₂ hf hg (zero_mul 0))

theorem lintegral_lt_top {α : Type u_1} [measurable_space α] {μ : measure α} {f : simple_func α ennreal} (hm : simple_func.fin_meas_supp f μ) (hf : filter.eventually (fun (a : α) => coe_fn f a < ⊤) (measure.ae μ)) : lintegral f μ < ⊤ := sorry

theorem of_lintegral_lt_top {α : Type u_1} [measurable_space α] {μ : measure α} {f : simple_func α ennreal} (h : lintegral f μ < ⊤) : simple_func.fin_meas_supp f μ := sorry

theorem iff_lintegral_lt_top {α : Type u_1} [measurable_space α] {μ : measure α} {f : simple_func α ennreal} (hf : filter.eventually (fun (a : α) => coe_fn f a < ⊤) (measure.ae μ)) : simple_func.fin_meas_supp f μ ↔ lintegral f μ < ⊤ :=
  { mp := fun (h : simple_func.fin_meas_supp f μ) => lintegral_lt_top h hf,
    mpr := fun (h : lintegral f μ < ⊤) => of_lintegral_lt_top h }

end fin_meas_supp

/-- To prove something for an arbitrary simple function, it suffices to show
that the property holds for (multiples of) characteristic functions and is closed under
addition (of functions with disjoint support).

It is possible to make the hypotheses in `h_sum` a bit stronger, and such conditions can be added
once we need them (for example it is only necessary to consider the case where `g` is a multiple
of a characteristic function, and that this multiple doesn't appear in the image of `f`) -/
protected theorem induction {α : Type u_1} {γ : Type u_2} [measurable_space α] [add_monoid γ] {P : simple_func α γ → Prop} (h_ind : ∀ (c : γ) {s : set α} (hs : is_measurable s), P (piecewise s hs (const α c) (const α 0))) (h_sum : ∀ {f g : simple_func α γ}, set.univ ⊆ ⇑f ⁻¹' singleton 0 ∪ ⇑g ⁻¹' singleton 0 → P f → P g → P (f + g)) (f : simple_func α γ) : P f := sorry

end simple_func

/-- The lower Lebesgue integral of a function `f` with respect to a measure `μ`. -/
def lintegral {α : Type u_1} [measurable_space α] (μ : measure α) (f : α → ennreal) : ennreal :=
  supr fun (g : simple_func α ennreal) => supr fun (hf : ⇑g ≤ f) => simple_func.lintegral g μ

/-! 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 simple_func.lintegral_eq_lintegral {α : Type u_1} [measurable_space α] (f : simple_func α ennreal) (μ : measure α) : (lintegral μ fun (a : α) => coe_fn f a) = simple_func.lintegral f μ := sorry

theorem lintegral_mono' {α : Type u_1} [measurable_space α] {μ : measure α} {ν : measure α} (hμν : μ ≤ ν) {f : α → ennreal} {g : α → ennreal} (hfg : f ≤ g) : (lintegral μ fun (a : α) => f a) ≤ lintegral ν fun (a : α) => g a := sorry

theorem lintegral_mono {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} {g : α → ennreal} (hfg : f ≤ g) : (lintegral μ fun (a : α) => f a) ≤ lintegral μ fun (a : α) => g a :=
  lintegral_mono' (le_refl μ) hfg

theorem lintegral_mono_nnreal {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → nnreal} {g : α → nnreal} (h : f ≤ g) : (lintegral μ fun (a : α) => ↑(f a)) ≤ lintegral μ fun (a : α) => ↑(g a) := sorry

theorem monotone_lintegral {α : Type u_1} [measurable_space α] (μ : measure α) : monotone (lintegral μ) :=
  lintegral_mono

@[simp] theorem lintegral_const {α : Type u_1} [measurable_space α] {μ : measure α} (c : ennreal) : (lintegral μ fun (a : α) => c) = c * coe_fn μ set.univ := sorry

@[simp] theorem lintegral_one {α : Type u_1} [measurable_space α] {μ : measure α} : (lintegral μ fun (a : α) => 1) = coe_fn μ set.univ :=
  eq.mpr (id (Eq._oldrec (Eq.refl ((lintegral μ fun (a : α) => 1) = coe_fn μ set.univ)) (lintegral_const 1)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (1 * coe_fn μ set.univ = coe_fn μ set.univ)) (one_mul (coe_fn μ set.univ))))
      (Eq.refl (coe_fn μ set.univ)))

theorem set_lintegral_const {α : Type u_1} [measurable_space α] {μ : measure α} (s : set α) (c : ennreal) : (lintegral (measure.restrict μ s) fun (a : α) => c) = c * coe_fn μ s := sorry

theorem set_lintegral_one {α : Type u_1} [measurable_space α] {μ : measure α} (s : set α) : (lintegral (measure.restrict μ s) fun (a : α) => 1) = coe_fn μ s := sorry

/-- `∫⁻ a in s, f a ∂μ` is defined as the supremum of integrals of simple functions
`φ : α →ₛ ennreal` such that `φ ≤ f`. This lemma says that it suffices to take
functions `φ : α →ₛ ℝ≥0`. -/
theorem lintegral_eq_nnreal {α : Type u_1} [measurable_space α] (f : α → ennreal) (μ : measure α) : (lintegral μ fun (a : α) => f a) =
  supr
    fun (φ : simple_func α nnreal) =>
      supr fun (hf : ∀ (x : α), ↑(coe_fn φ x) ≤ f x) => simple_func.lintegral (simple_func.map coe φ) μ := sorry

theorem exists_simple_func_forall_lintegral_sub_lt_of_pos {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} (h : (lintegral μ fun (x : α) => f x) < ⊤) {ε : ennreal} (hε : 0 < ε) : ∃ (φ : simple_func α nnreal),
  (∀ (x : α), ↑(coe_fn φ x) ≤ f x) ∧
    ∀ (ψ : simple_func α nnreal),
      (∀ (x : α), ↑(coe_fn ψ x) ≤ f x) → simple_func.lintegral (simple_func.map coe (ψ - φ)) μ < ε := sorry

theorem supr_lintegral_le {α : Type u_1} [measurable_space α] {μ : measure α} {ι : Sort u_2} (f : ι → α → ennreal) : (supr fun (i : ι) => lintegral μ fun (a : α) => f i a) ≤ lintegral μ fun (a : α) => supr fun (i : ι) => f i a := sorry

theorem supr2_lintegral_le {α : Type u_1} [measurable_space α] {μ : measure α} {ι : Sort u_2} {ι' : ι → Sort u_3} (f : (i : ι) → ι' i → α → ennreal) : (supr fun (i : ι) => supr fun (h : ι' i) => lintegral μ fun (a : α) => f i h a) ≤
  lintegral μ fun (a : α) => supr fun (i : ι) => supr fun (h : ι' i) => f i h a := sorry

theorem le_infi_lintegral {α : Type u_1} [measurable_space α] {μ : measure α} {ι : Sort u_2} (f : ι → α → ennreal) : (lintegral μ fun (a : α) => infi fun (i : ι) => f i a) ≤ infi fun (i : ι) => lintegral μ fun (a : α) => f i a := sorry

theorem le_infi2_lintegral {α : Type u_1} [measurable_space α] {μ : measure α} {ι : Sort u_2} {ι' : ι → Sort u_3} (f : (i : ι) → ι' i → α → ennreal) : (lintegral μ fun (a : α) => infi fun (i : ι) => infi fun (h : ι' i) => f i h a) ≤
  infi fun (i : ι) => infi fun (h : ι' i) => lintegral μ fun (a : α) => f i h a := sorry

theorem lintegral_mono_ae {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} {g : α → ennreal} (h : filter.eventually (fun (a : α) => f a ≤ g a) (measure.ae μ)) : (lintegral μ fun (a : α) => f a) ≤ lintegral μ fun (a : α) => g a := sorry

theorem lintegral_congr_ae {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} {g : α → ennreal} (h : filter.eventually_eq (measure.ae μ) f g) : (lintegral μ fun (a : α) => f a) = lintegral μ fun (a : α) => g a :=
  le_antisymm (lintegral_mono_ae (filter.eventually_eq.le h))
    (lintegral_mono_ae (filter.eventually_eq.le (filter.eventually_eq.symm h)))

theorem lintegral_congr {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} {g : α → ennreal} (h : ∀ (a : α), f a = g a) : (lintegral μ fun (a : α) => f a) = lintegral μ fun (a : α) => g a := sorry

theorem set_lintegral_congr {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} {s : set α} {t : set α} (h : filter.eventually_eq (measure.ae μ) s t) : (lintegral (measure.restrict μ s) fun (x : α) => f x) = lintegral (measure.restrict μ t) fun (x : α) => f x := sorry

/-- Monotone convergence theorem -- sometimes called Beppo-Levi convergence.

See `lintegral_supr_directed` for a more general form. -/
theorem lintegral_supr {α : Type u_1} [measurable_space α] {μ : measure α} {f : ℕ → α → ennreal} (hf : ∀ (n : ℕ), measurable (f n)) (h_mono : monotone f) : (lintegral μ fun (a : α) => supr fun (n : ℕ) => f n a) = supr fun (n : ℕ) => lintegral μ fun (a : α) => f n a := sorry

/-- Monotone convergence theorem -- sometimes called Beppo-Levi convergence. Version with
ae_measurable functions. -/
theorem lintegral_supr' {α : Type u_1} [measurable_space α] {μ : measure α} {f : ℕ → α → ennreal} (hf : ∀ (n : ℕ), ae_measurable (f n)) (h_mono : filter.eventually (fun (x : α) => monotone fun (n : ℕ) => f n x) (measure.ae μ)) : (lintegral μ fun (a : α) => supr fun (n : ℕ) => f n a) = supr fun (n : ℕ) => lintegral μ fun (a : α) => f n a := sorry

theorem lintegral_eq_supr_eapprox_lintegral {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} (hf : measurable f) : (lintegral μ fun (a : α) => f a) = supr fun (n : ℕ) => simple_func.lintegral (simple_func.eapprox f n) μ := 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. This lemma states states this fact in terms of `ε` and `δ`. -/
theorem exists_pos_set_lintegral_lt_of_measure_lt {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} (h : (lintegral μ fun (x : α) => f x) < ⊤) {ε : ennreal} (hε : 0 < ε) : ∃ (δ : ennreal),
  ∃ (H : δ > 0), ∀ (s : set α), coe_fn μ s < δ → (lintegral (measure.restrict μ s) fun (x : α) => f x) < ε := 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 tendsto_set_lintegral_zero {α : Type u_1} [measurable_space α] {μ : measure α} {ι : Type u_2} {f : α → ennreal} (h : (lintegral μ fun (x : α) => f x) < ⊤) {l : filter ι} {s : ι → set α} (hl : filter.tendsto (⇑μ ∘ s) l (nhds 0)) : filter.tendsto (fun (i : ι) => lintegral (measure.restrict μ (s i)) fun (x : α) => f x) l (nhds 0) := sorry

@[simp] theorem lintegral_add {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} {g : α → ennreal} (hf : measurable f) (hg : measurable g) : (lintegral μ fun (a : α) => f a + g a) = (lintegral μ fun (a : α) => f a) + lintegral μ fun (a : α) => g a := sorry

theorem lintegral_add' {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} {g : α → ennreal} (hf : ae_measurable f) (hg : ae_measurable g) : (lintegral μ fun (a : α) => f a + g a) = (lintegral μ fun (a : α) => f a) + lintegral μ fun (a : α) => g a := sorry

theorem lintegral_zero {α : Type u_1} [measurable_space α] {μ : measure α} : (lintegral μ fun (a : α) => 0) = 0 := sorry

theorem lintegral_zero_fun {α : Type u_1} [measurable_space α] {μ : measure α} : (lintegral μ fun (a : α) => HasZero.zero a) = 0 := sorry

@[simp] theorem lintegral_smul_measure {α : Type u_1} [measurable_space α] {μ : measure α} (c : ennreal) (f : α → ennreal) : (lintegral (c • μ) fun (a : α) => f a) = c * lintegral μ fun (a : α) => f a := sorry

@[simp] theorem lintegral_sum_measure {α : Type u_1} [measurable_space α] {ι : Type u_2} (f : α → ennreal) (μ : ι → measure α) : (lintegral (measure.sum μ) fun (a : α) => f a) = tsum fun (i : ι) => lintegral (μ i) fun (a : α) => f a := sorry

@[simp] theorem lintegral_add_measure {α : Type u_1} [measurable_space α] (f : α → ennreal) (μ : measure α) (ν : measure α) : (lintegral (μ + ν) fun (a : α) => f a) = (lintegral μ fun (a : α) => f a) + lintegral ν fun (a : α) => f a := sorry

@[simp] theorem lintegral_zero_measure {α : Type u_1} [measurable_space α] (f : α → ennreal) : (lintegral 0 fun (a : α) => f a) = 0 := sorry

theorem lintegral_finset_sum {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} (s : finset β) {f : β → α → ennreal} (hf : ∀ (b : β), measurable (f b)) : (lintegral μ fun (a : α) => finset.sum s fun (b : β) => f b a) =
  finset.sum s fun (b : β) => lintegral μ fun (a : α) => f b a := sorry

@[simp] theorem lintegral_const_mul {α : Type u_1} [measurable_space α] {μ : measure α} (r : ennreal) {f : α → ennreal} (hf : measurable f) : (lintegral μ fun (a : α) => r * f a) = r * lintegral μ fun (a : α) => f a := sorry

theorem lintegral_const_mul'' {α : Type u_1} [measurable_space α] {μ : measure α} (r : ennreal) {f : α → ennreal} (hf : ae_measurable f) : (lintegral μ fun (a : α) => r * f a) = r * lintegral μ fun (a : α) => f a := sorry

theorem lintegral_const_mul_le {α : Type u_1} [measurable_space α] {μ : measure α} (r : ennreal) (f : α → ennreal) : (r * lintegral μ fun (a : α) => f a) ≤ lintegral μ fun (a : α) => r * f a := sorry

theorem lintegral_const_mul' {α : Type u_1} [measurable_space α] {μ : measure α} (r : ennreal) (f : α → ennreal) (hr : r ≠ ⊤) : (lintegral μ fun (a : α) => r * f a) = r * lintegral μ fun (a : α) => f a := sorry

theorem lintegral_mul_const {α : Type u_1} [measurable_space α] {μ : measure α} (r : ennreal) {f : α → ennreal} (hf : measurable f) : (lintegral μ fun (a : α) => f a * r) = (lintegral μ fun (a : α) => f a) * r := sorry

theorem lintegral_mul_const'' {α : Type u_1} [measurable_space α] {μ : measure α} (r : ennreal) {f : α → ennreal} (hf : ae_measurable f) : (lintegral μ fun (a : α) => f a * r) = (lintegral μ fun (a : α) => f a) * r := sorry

theorem lintegral_mul_const_le {α : Type u_1} [measurable_space α] {μ : measure α} (r : ennreal) (f : α → ennreal) : (lintegral μ fun (a : α) => f a) * r ≤ lintegral μ fun (a : α) => f a * r := sorry

theorem lintegral_mul_const' {α : Type u_1} [measurable_space α] {μ : measure α} (r : ennreal) (f : α → ennreal) (hr : r ≠ ⊤) : (lintegral μ fun (a : α) => f a * r) = (lintegral μ fun (a : α) => f a) * r := sorry

/- A double integral of a product where each factor contains only one variable
  is a product of integrals -/

theorem lintegral_lintegral_mul {α : Type u_1} [measurable_space α] {μ : measure α} {β : Type u_2} [measurable_space β] {ν : measure β} {f : α → ennreal} {g : β → ennreal} (hf : measurable f) (hg : measurable g) : (lintegral μ fun (x : α) => lintegral ν fun (y : β) => f x * g y) =
  (lintegral μ fun (x : α) => f x) * lintegral ν fun (y : β) => g y := sorry

-- TODO: Need a better way of rewriting inside of a integral

theorem lintegral_rw₁ {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} {f : α → β} {f' : α → β} (h : filter.eventually_eq (measure.ae μ) f f') (g : β → ennreal) : (lintegral μ fun (a : α) => g (f a)) = lintegral μ fun (a : α) => g (f' a) :=
  lintegral_congr_ae
    (filter.eventually.mono h
      fun (a : α) (h : f a = f' a) => eq.mpr (id (Eq._oldrec (Eq.refl (g (f a) = g (f' a))) h)) (Eq.refl (g (f' a))))

-- TODO: Need a better way of rewriting inside of a integral

theorem lintegral_rw₂ {α : Type u_1} {β : Type u_2} {γ : Type u_3} [measurable_space α] {μ : measure α} {f₁ : α → β} {f₁' : α → β} {f₂ : α → γ} {f₂' : α → γ} (h₁ : filter.eventually_eq (measure.ae μ) f₁ f₁') (h₂ : filter.eventually_eq (measure.ae μ) f₂ f₂') (g : β → γ → ennreal) : (lintegral μ fun (a : α) => g (f₁ a) (f₂ a)) = lintegral μ fun (a : α) => g (f₁' a) (f₂' a) := sorry

@[simp] theorem lintegral_indicator {α : Type u_1} [measurable_space α] {μ : measure α} (f : α → ennreal) {s : set α} (hs : is_measurable s) : (lintegral μ fun (a : α) => set.indicator s f a) = lintegral (measure.restrict μ s) fun (a : α) => f a := sorry

/-- Chebyshev's inequality -/
theorem mul_meas_ge_le_lintegral {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} (hf : measurable f) (ε : ennreal) : ε * coe_fn μ (set_of fun (x : α) => ε ≤ f x) ≤ lintegral μ fun (a : α) => f a := sorry

theorem meas_ge_le_lintegral_div {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} (hf : measurable f) {ε : ennreal} (hε : ε ≠ 0) (hε' : ε ≠ ⊤) : coe_fn μ (set_of fun (x : α) => ε ≤ f x) ≤ (lintegral μ fun (a : α) => f a) / ε := sorry

@[simp] theorem lintegral_eq_zero_iff {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} (hf : measurable f) : (lintegral μ fun (a : α) => f a) = 0 ↔ filter.eventually_eq (measure.ae μ) f 0 := sorry

@[simp] theorem lintegral_eq_zero_iff' {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} (hf : ae_measurable f) : (lintegral μ fun (a : α) => f a) = 0 ↔ filter.eventually_eq (measure.ae μ) f 0 := sorry

theorem lintegral_pos_iff_support {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} (hf : measurable f) : (0 < lintegral μ fun (a : α) => f a) ↔ 0 < coe_fn μ (function.support f) := sorry

/-- Weaker version of the monotone convergence theorem-/
theorem lintegral_supr_ae {α : Type u_1} [measurable_space α] {μ : measure α} {f : ℕ → α → ennreal} (hf : ∀ (n : ℕ), measurable (f n)) (h_mono : ∀ (n : ℕ), filter.eventually (fun (a : α) => f n a ≤ f (Nat.succ n) a) (measure.ae μ)) : (lintegral μ fun (a : α) => supr fun (n : ℕ) => f n a) = supr fun (n : ℕ) => lintegral μ fun (a : α) => f n a := sorry

theorem lintegral_sub {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} {g : α → ennreal} (hf : measurable f) (hg : measurable g) (hg_fin : (lintegral μ fun (a : α) => g a) < ⊤) (h_le : filter.eventually_le (measure.ae μ) g f) : (lintegral μ fun (a : α) => f a - g a) = (lintegral μ fun (a : α) => f a) - lintegral μ fun (a : α) => g a := sorry

/-- Monotone convergence theorem for nonincreasing sequences of functions -/
theorem lintegral_infi_ae {α : Type u_1} [measurable_space α] {μ : measure α} {f : ℕ → α → ennreal} (h_meas : ∀ (n : ℕ), measurable (f n)) (h_mono : ∀ (n : ℕ), filter.eventually_le (measure.ae μ) (f (Nat.succ n)) (f n)) (h_fin : (lintegral μ fun (a : α) => f 0 a) < ⊤) : (lintegral μ fun (a : α) => infi fun (n : ℕ) => f n a) = infi fun (n : ℕ) => lintegral μ fun (a : α) => f n a := sorry

/-- Monotone convergence theorem for nonincreasing sequences of functions -/
theorem lintegral_infi {α : Type u_1} [measurable_space α] {μ : measure α} {f : ℕ → α → ennreal} (h_meas : ∀ (n : ℕ), measurable (f n)) (h_mono : ∀ {m n : ℕ}, m ≤ n → f n ≤ f m) (h_fin : (lintegral μ fun (a : α) => f 0 a) < ⊤) : (lintegral μ fun (a : α) => infi fun (n : ℕ) => f n a) = infi fun (n : ℕ) => lintegral μ fun (a : α) => f n a :=
  lintegral_infi_ae h_meas (fun (n : ℕ) => ae_of_all μ (h_mono (le_of_lt (nat.lt_succ_self n)))) h_fin

/-- Known as Fatou's lemma, version with `ae_measurable` functions -/
theorem lintegral_liminf_le' {α : Type u_1} [measurable_space α] {μ : measure α} {f : ℕ → α → ennreal} (h_meas : ∀ (n : ℕ), ae_measurable (f n)) : (lintegral μ fun (a : α) => filter.liminf filter.at_top fun (n : ℕ) => f n a) ≤
  filter.liminf filter.at_top fun (n : ℕ) => lintegral μ fun (a : α) => f n a := sorry

/-- Known as Fatou's lemma -/
theorem lintegral_liminf_le {α : Type u_1} [measurable_space α] {μ : measure α} {f : ℕ → α → ennreal} (h_meas : ∀ (n : ℕ), measurable (f n)) : (lintegral μ fun (a : α) => filter.liminf filter.at_top fun (n : ℕ) => f n a) ≤
  filter.liminf filter.at_top fun (n : ℕ) => lintegral μ fun (a : α) => f n a :=
  lintegral_liminf_le' fun (n : ℕ) => measurable.ae_measurable (h_meas n)

theorem limsup_lintegral_le {α : Type u_1} [measurable_space α] {μ : measure α} {f : ℕ → α → ennreal} {g : α → ennreal} (hf_meas : ∀ (n : ℕ), measurable (f n)) (h_bound : ∀ (n : ℕ), filter.eventually_le (measure.ae μ) (f n) g) (h_fin : (lintegral μ fun (a : α) => g a) < ⊤) : (filter.limsup filter.at_top fun (n : ℕ) => lintegral μ fun (a : α) => f n a) ≤
  lintegral μ fun (a : α) => filter.limsup filter.at_top fun (n : ℕ) => f n a := sorry

/-- Dominated convergence theorem for nonnegative functions -/
theorem tendsto_lintegral_of_dominated_convergence {α : Type u_1} [measurable_space α] {μ : measure α} {F : ℕ → α → ennreal} {f : α → ennreal} (bound : α → ennreal) (hF_meas : ∀ (n : ℕ), measurable (F n)) (h_bound : ∀ (n : ℕ), filter.eventually_le (measure.ae μ) (F n) bound) (h_fin : (lintegral μ fun (a : α) => bound a) < ⊤) (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 : ℕ) => lintegral μ fun (a : α) => F n a) filter.at_top (nhds (lintegral μ fun (a : α) => f a)) := sorry

/-- Dominated convergence theorem for nonnegative functions which are just almost everywhere
measurable. -/
theorem tendsto_lintegral_of_dominated_convergence' {α : Type u_1} [measurable_space α] {μ : measure α} {F : ℕ → α → ennreal} {f : α → ennreal} (bound : α → ennreal) (hF_meas : ∀ (n : ℕ), ae_measurable (F n)) (h_bound : ∀ (n : ℕ), filter.eventually_le (measure.ae μ) (F n) bound) (h_fin : (lintegral μ fun (a : α) => bound a) < ⊤) (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 : ℕ) => lintegral μ fun (a : α) => F n a) filter.at_top (nhds (lintegral μ fun (a : α) => f a)) := sorry

/-- Dominated convergence theorem for filters with a countable basis -/
theorem tendsto_lintegral_filter_of_dominated_convergence {α : Type u_1} [measurable_space α] {μ : measure α} {ι : Type u_2} {l : filter ι} {F : ι → α → ennreal} {f : α → ennreal} (bound : α → ennreal) (hl_cb : filter.is_countably_generated l) (hF_meas : filter.eventually (fun (n : ι) => measurable (F n)) l) (h_bound : filter.eventually (fun (n : ι) => filter.eventually (fun (a : α) => F n a ≤ bound a) (measure.ae μ)) l) (h_fin : (lintegral μ fun (a : α) => bound a) < ⊤) (h_lim : filter.eventually (fun (a : α) => filter.tendsto (fun (n : ι) => F n a) l (nhds (f a))) (measure.ae μ)) : filter.tendsto (fun (n : ι) => lintegral μ fun (a : α) => F n a) l (nhds (lintegral μ fun (a : α) => f a)) := sorry

/-- Monotone convergence for a suprema over a directed family and indexed by an encodable type -/
theorem lintegral_supr_directed {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} [encodable β] {f : β → α → ennreal} (hf : ∀ (b : β), measurable (f b)) (h_directed : directed LessEq f) : (lintegral μ fun (a : α) => supr fun (b : β) => f b a) = supr fun (b : β) => lintegral μ fun (a : α) => f b a := sorry

theorem lintegral_tsum {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} [encodable β] {f : β → α → ennreal} (hf : ∀ (i : β), measurable (f i)) : (lintegral μ fun (a : α) => tsum fun (i : β) => f i a) = tsum fun (i : β) => lintegral μ fun (a : α) => f i a := sorry

theorem lintegral_Union {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} [encodable β] {s : β → set α} (hm : ∀ (i : β), is_measurable (s i)) (hd : pairwise (disjoint on s)) (f : α → ennreal) : (lintegral (measure.restrict μ (set.Union fun (i : β) => s i)) fun (a : α) => f a) =
  tsum fun (i : β) => lintegral (measure.restrict μ (s i)) fun (a : α) => f a := sorry

theorem lintegral_Union_le {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} [encodable β] (s : β → set α) (f : α → ennreal) : (lintegral (measure.restrict μ (set.Union fun (i : β) => s i)) fun (a : α) => f a) ≤
  tsum fun (i : β) => lintegral (measure.restrict μ (s i)) fun (a : α) => f a := sorry

theorem lintegral_map {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} [measurable_space β] {f : β → ennreal} {g : α → β} (hf : measurable f) (hg : measurable g) : (lintegral (coe_fn (measure.map g) μ) fun (a : β) => f a) = lintegral μ fun (a : α) => f (g a) := sorry

theorem lintegral_map' {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} [measurable_space β] {f : β → ennreal} {g : α → β} (hf : ae_measurable f) (hg : measurable g) : (lintegral (coe_fn (measure.map g) μ) fun (a : β) => f a) = lintegral μ fun (a : α) => f (g a) :=
  Eq.trans (Eq.trans (lintegral_congr_ae (ae_measurable.ae_eq_mk hf)) (lintegral_map (ae_measurable.measurable_mk hf) hg))
    (lintegral_congr_ae (ae_eq_comp hg (filter.eventually_eq.symm (ae_measurable.ae_eq_mk hf))))

theorem lintegral_comp {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} [measurable_space β] {f : β → ennreal} {g : α → β} (hf : measurable f) (hg : measurable g) : lintegral μ (f ∘ g) = lintegral (coe_fn (measure.map g) μ) fun (a : β) => f a :=
  Eq.symm (lintegral_map hf hg)

theorem set_lintegral_map {α : Type u_1} {β : Type u_2} [measurable_space α] {μ : measure α} [measurable_space β] {f : β → ennreal} {g : α → β} {s : set β} (hs : is_measurable s) (hf : measurable f) (hg : measurable g) : (lintegral (measure.restrict (coe_fn (measure.map g) μ) s) fun (y : β) => f y) =
  lintegral (measure.restrict μ (g ⁻¹' s)) fun (x : α) => f (g x) := sorry

theorem lintegral_dirac' {α : Type u_1} [measurable_space α] (a : α) {f : α → ennreal} (hf : measurable f) : (lintegral (measure.dirac a) fun (a : α) => f a) = f a := sorry

theorem lintegral_dirac {α : Type u_1} [measurable_space α] [measurable_singleton_class α] (a : α) (f : α → ennreal) : (lintegral (measure.dirac a) fun (a : α) => f a) = f a := sorry

theorem lintegral_count' {α : Type u_1} [measurable_space α] {f : α → ennreal} (hf : measurable f) : (lintegral measure.count fun (a : α) => f a) = tsum fun (a : α) => f a := sorry

theorem lintegral_count {α : Type u_1} [measurable_space α] [measurable_singleton_class α] (f : α → ennreal) : (lintegral measure.count fun (a : α) => f a) = tsum fun (a : α) => f a := sorry

theorem ae_lt_top {α : Type u_1} [measurable_space α] {μ : measure α} {f : α → ennreal} (hf : measurable f) (h2f : (lintegral μ fun (x : α) => f x) < ⊤) : filter.eventually (fun (x : α) => f x < ⊤) (measure.ae μ) := sorry

/-- Given a measure `μ : measure α` and a function `f : α → ennreal`, `μ.with_density f` is the
measure such that for a measurable set `s` we have `μ.with_density f s = ∫⁻ a in s, f a ∂μ`. -/
def measure.with_density {α : Type u_1} [measurable_space α] (μ : measure α) (f : α → ennreal) : measure α :=
  measure.of_measurable (fun (s : set α) (hs : is_measurable s) => lintegral (measure.restrict μ s) fun (a : α) => f a)
    sorry sorry

@[simp] theorem with_density_apply {α : Type u_1} [measurable_space α] {μ : measure α} (f : α → ennreal) {s : set α} (hs : is_measurable s) : coe_fn (measure.with_density μ f) s = lintegral (measure.restrict μ s) fun (a : α) => f a :=
  measure.of_measurable_apply s hs

end measure_theory

/-- To prove something for an arbitrary measurable function into `ennreal`, it suffices to show
that the property holds for (multiples of) characteristic functions and is closed under addition
and supremum of increasing sequences of functions.

It is possible to make the hypotheses in the induction steps a bit stronger, and such conditions
can be added once we need them (for example in `h_sum` it is only necessary to consider the sum of
a simple function with a multiple of a characteristic function and that the intersection
of their images is a subset of `{0}`. -/
theorem measurable.ennreal_induction {α : Type u_1} [measurable_space α] {P : (α → ennreal) → Prop} (h_ind : ∀ (c : ennreal) {s : set α}, is_measurable s → P (set.indicator s fun (_x : α) => c)) (h_sum : ∀ {f g : α → ennreal},
  set.univ ⊆ f ⁻¹' singleton 0 ∪ g ⁻¹' singleton 0 → measurable f → measurable g → P f → P g → P (f + g)) (h_supr : ∀ {f : ℕ → α → ennreal},
  (∀ (n : ℕ), measurable (f n)) → monotone f → (∀ (n : ℕ), P (f n)) → P fun (x : α) => supr fun (n : ℕ) => f n x) {f : α → ennreal} (hf : measurable f) : P f := sorry

namespace measure_theory

/-- This is Exercise 1.2.1 from [tao2010]. It allows you to express integration of a measurable
function with respect to `(μ.with_density f)` as an integral with respect to `μ`, called the base
measure. `μ` is often the Lebesgue measure, and in this circumstance `f` is the probability density
function, and `(μ.with_density f)` represents any continuous random variable as a
probability measure, such as the uniform distribution between 0 and 1, the Gaussian distribution,
the exponential distribution, the Beta distribution, or the Cauchy distribution (see Section 2.4
of [wasserman2004]). Thus, this method shows how to one can calculate expectations, variances,
and other moments as a function of the probability density function.
 -/
theorem lintegral_with_density_eq_lintegral_mul {α : Type u_1} [measurable_space α] (μ : measure α) {f : α → ennreal} (h_mf : measurable f) {g : α → ennreal} : measurable g → (lintegral (measure.with_density μ f) fun (a : α) => g a) = lintegral μ fun (a : α) => Mul.mul f g a := sorry


/-
Copyright (c) 2020 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import order.antisymmetrization
import order.category.Preorder

/-!
# Category of partial orders

This defines `PartialOrder`, the category of partial orders with monotone maps.
-/

open category_theory

universe u

/-- The category of partially ordered types. -/
def PartialOrder := bundled partial_order

namespace PartialOrder

instance : bundled_hom.parent_projection @partial_order.to_preorder := ⟨⟩

attribute [derive [large_category, concrete_category]] PartialOrder

instance : has_coe_to_sort PartialOrder Type* := bundled.has_coe_to_sort

/-- Construct a bundled PartialOrder from the underlying type and typeclass. -/
def of (α : Type*) [partial_order α] : PartialOrder := bundled.of α

@[simp] lemma coe_of (α : Type*) [partial_order α] : ↥(of α) = α := rfl

instance : inhabited PartialOrder := ⟨of punit⟩

instance (α : PartialOrder) : partial_order α := α.str

instance has_forget_to_Preorder : has_forget₂ PartialOrder Preorder := bundled_hom.forget₂ _ _

/-- Constructs an equivalence between partial orders from an order isomorphism between them. -/
@[simps] def iso.mk {α β : PartialOrder.{u}} (e : α ≃o β) : α ≅ β :=
{ hom := e,
  inv := e.symm,
  hom_inv_id' := by { ext, exact e.symm_apply_apply x },
  inv_hom_id' := by { ext, exact e.apply_symm_apply x } }

/-- `order_dual` as a functor. -/
@[simps] def dual : PartialOrder ⥤ PartialOrder :=
{ obj := λ X, of Xᵒᵈ, map := λ X Y, order_hom.dual }

/-- The equivalence between `PartialOrder` and itself induced by `order_dual` both ways. -/
@[simps functor inverse] def dual_equiv : PartialOrder ≌ PartialOrder :=
equivalence.mk dual dual
  (nat_iso.of_components (λ X, iso.mk $ order_iso.dual_dual X) $ λ X Y f, rfl)
  (nat_iso.of_components (λ X, iso.mk $ order_iso.dual_dual X) $ λ X Y f, rfl)

end PartialOrder

lemma PartialOrder_dual_comp_forget_to_Preorder :
  PartialOrder.dual ⋙ forget₂ PartialOrder Preorder =
    forget₂ PartialOrder Preorder ⋙ Preorder.dual := rfl

/-- `antisymmetrization` as a functor. It is the free functor. -/
def Preorder_to_PartialOrder : Preorder.{u} ⥤ PartialOrder :=
{ obj := λ X, PartialOrder.of (antisymmetrization X (≤)),
  map := λ X Y f, f.antisymmetrization,
  map_id' := λ X,
    by { ext, exact quotient.induction_on' x (λ x, quotient.map'_mk' _ (λ a b, id) _) },
  map_comp' := λ X Y Z f g,
    by { ext, exact quotient.induction_on' x (λ x, order_hom.antisymmetrization_apply_mk _ _) } }

/-- `Preorder_to_PartialOrder` is left adjoint to the forgetful functor, meaning it is the free
functor from `Preorder` to `PartialOrder`. -/
def Preorder_to_PartialOrder_forget_adjunction :
  Preorder_to_PartialOrder.{u} ⊣ forget₂ PartialOrder Preorder :=
adjunction.mk_of_hom_equiv
  { hom_equiv := λ X Y, { to_fun := λ f,
      ⟨f ∘ to_antisymmetrization (≤), f.mono.comp to_antisymmetrization_mono⟩,
    inv_fun := λ f, ⟨λ a, quotient.lift_on' a f $ λ a b h, (antisymm_rel.image h f.mono).eq, λ a b,
      quotient.induction_on₂' a b $ λ a b h, f.mono h⟩,
    left_inv := λ f, order_hom.ext _ _ $ funext $ λ x, quotient.induction_on' x $ λ x, rfl,
    right_inv := λ f, order_hom.ext _ _ $ funext $ λ x, rfl },
  hom_equiv_naturality_left_symm' := λ X Y Z f g,
    order_hom.ext _ _ $ funext $ λ x, quotient.induction_on' x $ λ x, rfl,
  hom_equiv_naturality_right' := λ X Y Z f g, order_hom.ext _ _ $ funext $ λ x, rfl }

/-- `Preorder_to_PartialOrder` and `order_dual` commute. -/
@[simps] def Preorder_to_PartialOrder_comp_to_dual_iso_to_dual_comp_Preorder_to_PartialOrder :
 (Preorder_to_PartialOrder.{u} ⋙ PartialOrder.dual) ≅
    (Preorder.dual ⋙ Preorder_to_PartialOrder) :=
nat_iso.of_components (λ X, PartialOrder.iso.mk $ order_iso.dual_antisymmetrization _) $
  λ X Y f, order_hom.ext _ _ $ funext $ λ x, quotient.induction_on' x $ λ x, rfl