/- LoVe Demo 6: Monads -/

import .lovelib

namespace LoVe

/- Motivating Example -/

def sum_2_5_7₁ (ns : list ℕ) : option ℕ :=
match list.nth ns 1 with
| none    := none
| some n2 :=
  match list.nth ns 4 with
  | none    := none
  | some n5 :=
    match list.nth ns 6 with
    | none    := none
    | some n7 := some (n2 + n5 + n7)
    end
  end
end

def bind_option {α : Type} {β : Type} :
  option α → (α → option β) → option β
| none     f := none
| (some a) f := f a

def sum_2_5_7₂ (ns : list ℕ) : option ℕ :=
bind_option (list.nth ns 1)
  (λn2, bind_option (list.nth ns 4)
    (λn5, bind_option (list.nth ns 6)
      (λn7, some (n2 + n5 + n7))))

#check bind

def sum_2_5_7₃ (ns : list ℕ) : option ℕ :=
bind (list.nth ns 1)
  (λn2, bind (list.nth ns 4)
    (λn5, bind (list.nth ns 6)
      (λn7, some (n2 + n5 + n7))))

#check (>>=)

def sum_2_5_7₄ (ns : list ℕ) : option ℕ :=
list.nth ns 1 >>=
  λn2, list.nth ns 4 >>=
    λn5, list.nth ns 6 >>=
      λn7, some (n2 + n5 + n7)

def sum_2_5_7₅ (ns : list ℕ) : option ℕ :=
  do n2 ← list.nth ns 1,
    do n5 ← list.nth ns 4,
      do n7 ← list.nth ns 6,
        some (n2 + n5 + n7)

def sum_2_5_7₆ (ns : list ℕ) : option ℕ :=
do
  n2 ← list.nth ns 1,
  n5 ← list.nth ns 4,
  n7 ← list.nth ns 6,
  some (n2 + n5 + n7)

/- A Type Class of Monads -/

class lawful_monad (m : Type → Type)
  extends has_bind m, has_pure m : Type 1 :=
(pure_bind {α β : Type} (a : α) (f : α → m β) :
  (pure a >>= f) = f a)
(bind_pure {α : Type} (ma : m α) :
  ma >>= pure = ma)
(bind_assoc {α β γ : Type} (f : α → m β) (g : β → m γ)
    (ma : m α) :
  ((ma >>= f) >>= g) = (ma >>= (λa, f a >>= g)))

attribute [simp]
  LoVe.lawful_monad.bind_pure
  LoVe.lawful_monad.bind_assoc
  LoVe.lawful_monad.pure_bind

open LoVe.lawful_monad

#print monad
#print is_lawful_monad

/- The Option Monad -/

namespace option

def pure {α : Type} : α → option α :=
option.some

def bind {α β : Type} : option α → (α → option β) → option β
| none     f := none
| (some a) f := f a

instance lawful_monad_option : lawful_monad option :=
{ pure       := @option.pure,
  bind       := @option.bind,
  pure_bind  :=
    begin
      intros α β a f,
      refl
    end,
  bind_pure  :=
    begin
      intros α m,
      cases m; refl
    end,
  bind_assoc :=
    begin
      intros α β γ f g m,
      cases m; refl
    end }

end option

/- The State Monad -/

namespace state

def state (σ : Type) (α : Type) :=
σ → α × σ

end state

namespace state

def read {σ : Type} : state σ σ
| s := (s, s)

def write {σ : Type} (s : σ) : state σ unit
| _ := ((), s)

def pure {σ α : Type} (a : α) : state σ α
| s := (a, s)

def bind {σ : Type} {α β : Type} (ma : state σ α)
  (f : α → state σ β) : state σ β
| s :=
  match ma s with
  | (a, s') := f a s'
  end

instance {σ : Type} : lawful_monad (state σ) :=
{ pure       := @state.pure σ,
  bind       := @state.bind σ,
  pure_bind  :=
    begin
      intros α β a f,
      apply funext,
      intro s,
      refl
    end,
  bind_pure  :=
    begin
      intros α m,
      apply funext,
      intro s,
      simp [bind],
      cases m s,
      refl
    end,
  bind_assoc :=
    begin
      intros α β γ f g m,
      apply funext,
      intro s,
      simp [bind],
      cases m s,
      refl
    end }

end state

namespace state

def diff_list : list ℕ → state ℕ (list ℕ)
| []        := pure []
| (n :: ns) :=
  do
    prev ← read,
    if n < prev then
      diff_list ns
    else
      do
        write n,
        ns' ← diff_list ns,
        pure (n :: ns')

#eval diff_list [1, 2, 3, 2] 0
#eval diff_list [1, 2, 3, 2, 4, 5, 2] 0

end state

/- Example: Generic Iteration over a List -/

def mmap {m : Type → Type} [lawful_monad m] {α β : Type}
    (f : α → m β) : list α → m (list β)
| []       := pure []
| (a :: as) :=
  do
    b ← f a,
    bs ← mmap as,
    pure (b :: bs)

lemma mmap_append {m : Type → Type} [lawful_monad m]
    {α β : Type} (f : α → m β) :
  ∀as as' : list α, mmap f (as ++ as') =
    do
      bs ← mmap f as,
      bs' ← mmap f as',
      pure (bs ++ bs')
| []        _   := by simp [mmap]
| (a :: as) as' := by simp [mmap, mmap_append as as']

def nths {α : Type} (xss : list (list α)) (n : ℕ) :
  option (list α) :=
mmap (λxs, list.nth xs n) xss

#eval nths
  [[11, 12, 13, 14],
   [21, 22, 23],
   [31, 32, 33]] 2

end LoVe


import data.real.basic
import algebra.archimedean
import tactic.norm_num

variables {β : Type} [linear_ordered_ring β] [floor_ring β]

namespace decimal

section
parameters {α : Type} [linear_ordered_ring α] [floor_ring α]
-- ⌊(real.sqrt 2 : ℝ)⌋

definition expansion_aux (r : α) : ℕ → α
| 0 := (r - floor r) * 10
| (n + 1) := (expansion_aux n - floor (expansion_aux n)) * 10

definition expansion_nonneg (r : α) : ℕ → ℕ
| 0 := int.to_nat (floor r)
| (n + 1) := int.to_nat (floor (expansion_aux r n))

lemma expansion_aux_is_zero (n : ℕ) (r : α) : expansion_aux r n = 0 → ∀ m, expansion_aux r (n + m) = 0 :=
begin
intros H m,induction m with d Hd,assumption,
show expansion_aux r (nat.succ (n + d)) = 0,
unfold expansion_aux,rw Hd,simp,
end

lemma int.succ_le_of_lt (a b : ℤ) : a < b → int.succ a ≤ b := id

@[simp] theorem rat.cast_floor
  {α} [linear_ordered_field α] [archimedean α] (x : ℚ) :
  by haveI := archimedean.floor_ring α; exact ⌊(x:α)⌋ = ⌊x⌋ :=
begin
  haveI := archimedean.floor_ring α,
  apply le_antisymm,
  { rw [le_floor, ← @rat.cast_le α, rat.cast_coe_int],
    apply floor_le },
  { rw [le_floor, ← rat.cast_coe_int, rat.cast_le],
    apply floor_le }
end

lemma floor_of_bounds (r : α) (z : ℤ) :
  ↑z ≤ r ∧ r < (z + 1) ↔ ⌊ r ⌋ = z :=
by rw [← le_floor, ← int.cast_one, ← int.cast_add, ← floor_lt,
  int.lt_add_one_iff, le_antisymm_iff, and.comm]

end

end decimal


import .subgraph

noncomputable theory
open_locale classical

universes u
variables {V : Type u}
namespace simple_graph

variables (G : simple_graph V)
include G

variables [fintype V]

  -- want a lemma that 2 * card_edges equals the card of a specific finset.
-- lemma 2 * G.card_edges = finset.card G.E_finset

@[elab_as_eliminator]
lemma induction_on
  (P : simple_graph V → Prop)
  (P_empty : P empty)
  (P_inductive : ∀ G ≠ empty, ∃ (H : simple_graph V),
    H.is_subgraph G ∧
    H.card_edges < G.card_edges ∧
    (P H → P G) ) : P G :=
begin
  by_cases h : G = empty, { rwa h },
  suffices : ∀ H : simple_graph V, H.card_edges < G.card_edges → P H,
  { have := P_inductive G h, tauto },
  induction G.card_edges using nat.strong_induction_on with k hk,
  intros H hHk,
  by_cases H_card : H = empty, { cc },
  rcases P_inductive H H_card with ⟨K, K_sub, K_card, hKH⟩,
  apply hKH, exact hk _ hHk _ K_card,
end
-- for every graph, there exists an edge so that P (G.erase e) → P G

def erase (e : G.E) : simple_graph V :=
{ adj := λ u v, if u ∈ e ∧ v ∈ e then false else G.adj u v,
  sym := by { unfold symmetric, intros, simp_rw [edge_symm, and_comm], cc } }

@[simp] lemma erase_adj_iff (e : G.E) (u v : V) :
  (G.erase e).adj u v ↔ G.adj u v ∧ (u ∉ e ∨ v ∉ e) :=
by { simp [erase]; tauto, }

lemma erase_is_subgraph (e : G.E) : (G.erase e).is_subgraph G := by tidy
-- writing this down in a way that avoids nat subtraction
lemma card_edges_erase (e : G.E) : (G.erase e).card_edges + 1 = G.card_edges :=
begin
  sorry
end
@[elab_as_eliminator]
lemma induction_on_erase
  (P : simple_graph V → Prop)
  (P_empty : P empty)
  (P_inductive : ∀ G : simple_graph V, G ≠ empty →
    ∃ e : G.E, P (G.erase e) → P G)
  : P G :=
begin
  apply G.induction_on, assumption,
  intros G₁ hG₁, cases P_inductive G₁ hG₁ with e he,
  use G₁.erase e,
  split, { apply erase_is_subgraph },
  split, linarith [G₁.card_edges_erase e],
  assumption,
end

end simple_graph


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

! This file was ported from Lean 3 source module data.real.enat_ennreal
! leanprover-community/mathlib commit ee05e9ce1322178f0c12004eb93c00d2c8c00ed2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Enat.Basic
import Mathbin.Data.Real.Ennreal

/-!
# Coercion from `ℕ∞` to `ℝ≥0∞`

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

In this file we define a coercion from `ℕ∞` to `ℝ≥0∞` and prove some basic lemmas about this map.
-/

open Classical NNReal ENNReal

noncomputable section

namespace ENat

variable {m n : ℕ∞}

/- warning: enat.has_coe_ennreal -> ENat.hasCoeENNReal is a dubious translation:
lean 3 declaration is
  CoeTCₓ.{1, 1} ENat ENNReal
but is expected to have type
  CoeTC.{1, 1} ENat ENNReal
Case conversion may be inaccurate. Consider using '#align enat.has_coe_ennreal ENat.hasCoeENNRealₓ'. -/
instance hasCoeENNReal : CoeTC ℕ∞ ℝ≥0∞ :=
  ⟨WithTop.map coe⟩
#align enat.has_coe_ennreal ENat.hasCoeENNReal

#print ENat.map_coe_nnreal /-
@[simp]
theorem map_coe_nnreal : WithTop.map (coe : ℕ → ℝ≥0) = (coe : ℕ∞ → ℝ≥0∞) :=
  rfl
#align enat.map_coe_nnreal ENat.map_coe_nnreal
-/

/- warning: enat.to_ennreal_order_embedding -> ENat.toENNRealOrderEmbedding is a dubious translation:
lean 3 declaration is
  OrderEmbedding.{0, 0} ENat ENNReal (Preorder.toLE.{0} ENat (PartialOrder.toPreorder.{0} ENat (OrderedAddCommMonoid.toPartialOrder.{0} ENat (OrderedSemiring.toOrderedAddCommMonoid.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat ENat.canonicallyOrderedCommSemiring)))))) (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder)))))
but is expected to have type
  OrderEmbedding.{0, 0} ENat ENNReal (Preorder.toLE.{0} ENat (PartialOrder.toPreorder.{0} ENat (OrderedSemiring.toPartialOrder.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat instENatCanonicallyOrderedCommSemiring))))) (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal)))))
Case conversion may be inaccurate. Consider using '#align enat.to_ennreal_order_embedding ENat.toENNRealOrderEmbeddingₓ'. -/
/-- Coercion `ℕ∞ → ℝ≥0∞` as an `order_embedding`. -/
@[simps (config := { fullyApplied := false })]
def toENNRealOrderEmbedding : ℕ∞ ↪o ℝ≥0∞ :=
  Nat.castOrderEmbedding.withTop_map
#align enat.to_ennreal_order_embedding ENat.toENNRealOrderEmbedding

/- warning: enat.to_ennreal_ring_hom -> ENat.toENNRealRingHom is a dubious translation:
lean 3 declaration is
  RingHom.{0, 0} ENat ENNReal (Semiring.toNonAssocSemiring.{0} ENat (OrderedSemiring.toSemiring.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat ENat.canonicallyOrderedCommSemiring)))) (Semiring.toNonAssocSemiring.{0} ENNReal (OrderedSemiring.toSemiring.{0} ENNReal (OrderedCommSemiring.toOrderedSemiring.{0} ENNReal (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENNReal ENNReal.canonicallyOrderedCommSemiring))))
but is expected to have type
  RingHom.{0, 0} ENat ENNReal (Semiring.toNonAssocSemiring.{0} ENat (OrderedSemiring.toSemiring.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat instENatCanonicallyOrderedCommSemiring)))) (Semiring.toNonAssocSemiring.{0} ENNReal (OrderedSemiring.toSemiring.{0} ENNReal (OrderedCommSemiring.toOrderedSemiring.{0} ENNReal (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENNReal ENNReal.instCanonicallyOrderedCommSemiringENNReal))))
Case conversion may be inaccurate. Consider using '#align enat.to_ennreal_ring_hom ENat.toENNRealRingHomₓ'. -/
/-- Coercion `ℕ∞ → ℝ≥0∞` as a ring homomorphism. -/
@[simps (config := { fullyApplied := false })]
def toENNRealRingHom : ℕ∞ →+* ℝ≥0∞ :=
  (Nat.castRingHom ℝ≥0).withTop_map Nat.cast_injective
#align enat.to_ennreal_ring_hom ENat.toENNRealRingHom

/- warning: enat.coe_ennreal_top -> ENat.toENNReal_top is a dubious translation:
lean 3 declaration is
  Eq.{1} ENNReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) (Top.top.{0} ENat ENat.hasTop)) (Top.top.{0} ENNReal (CompleteLattice.toHasTop.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder)))
but is expected to have type
  Eq.{1} ENNReal (ENat.toENNReal (Top.top.{0} ENat instENatTop)) (Top.top.{0} ENNReal (CompleteLattice.toTop.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal)))
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_top ENat.toENNReal_topₓ'. -/
@[simp, norm_cast]
theorem toENNReal_top : ((⊤ : ℕ∞) : ℝ≥0∞) = ⊤ :=
  rfl
#align enat.coe_ennreal_top ENat.toENNReal_top

#print ENat.toENNReal_coe /-
@[simp, norm_cast]
theorem toENNReal_coe (n : ℕ) : ((n : ℕ∞) : ℝ≥0∞) = n :=
  rfl
#align enat.coe_ennreal_coe ENat.toENNReal_coe
-/

/- warning: enat.coe_ennreal_le -> ENat.toENNReal_le is a dubious translation:
lean 3 declaration is
  forall {m : ENat} {n : ENat}, Iff (LE.le.{0} ENNReal (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) m) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) n)) (LE.le.{0} ENat (Preorder.toLE.{0} ENat (PartialOrder.toPreorder.{0} ENat (OrderedAddCommMonoid.toPartialOrder.{0} ENat (OrderedSemiring.toOrderedAddCommMonoid.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat ENat.canonicallyOrderedCommSemiring)))))) m n)
but is expected to have type
  forall {m : ENat} {n : ENat}, Iff (LE.le.{0} ENNReal (Preorder.toLE.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal))))) (ENat.toENNReal m) (ENat.toENNReal n)) (LE.le.{0} ENat (Preorder.toLE.{0} ENat (PartialOrder.toPreorder.{0} ENat (OrderedSemiring.toPartialOrder.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat instENatCanonicallyOrderedCommSemiring))))) m n)
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_le ENat.toENNReal_leₓ'. -/
@[simp, norm_cast]
theorem toENNReal_le : (m : ℝ≥0∞) ≤ n ↔ m ≤ n :=
  toENNRealOrderEmbedding.le_iff_le
#align enat.coe_ennreal_le ENat.toENNReal_le

/- warning: enat.coe_ennreal_lt -> ENat.toENNReal_lt is a dubious translation:
lean 3 declaration is
  forall {m : ENat} {n : ENat}, Iff (LT.lt.{0} ENNReal (Preorder.toLT.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) m) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) n)) (LT.lt.{0} ENat (Preorder.toLT.{0} ENat (PartialOrder.toPreorder.{0} ENat (OrderedAddCommMonoid.toPartialOrder.{0} ENat (OrderedSemiring.toOrderedAddCommMonoid.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat ENat.canonicallyOrderedCommSemiring)))))) m n)
but is expected to have type
  forall {m : ENat} {n : ENat}, Iff (LT.lt.{0} ENNReal (Preorder.toLT.{0} ENNReal (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal))))) (ENat.toENNReal m) (ENat.toENNReal n)) (LT.lt.{0} ENat (Preorder.toLT.{0} ENat (PartialOrder.toPreorder.{0} ENat (OrderedSemiring.toPartialOrder.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat instENatCanonicallyOrderedCommSemiring))))) m n)
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_lt ENat.toENNReal_ltₓ'. -/
@[simp, norm_cast]
theorem toENNReal_lt : (m : ℝ≥0∞) < n ↔ m < n :=
  toENNRealOrderEmbedding.lt_iff_lt
#align enat.coe_ennreal_lt ENat.toENNReal_lt

/- warning: enat.coe_ennreal_mono -> ENat.toENNReal_mono is a dubious translation:
lean 3 declaration is
  Monotone.{0, 0} ENat ENNReal (PartialOrder.toPreorder.{0} ENat (OrderedAddCommMonoid.toPartialOrder.{0} ENat (OrderedSemiring.toOrderedAddCommMonoid.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat ENat.canonicallyOrderedCommSemiring))))) (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)))
but is expected to have type
  Monotone.{0, 0} ENat ENNReal (PartialOrder.toPreorder.{0} ENat (OrderedSemiring.toPartialOrder.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat instENatCanonicallyOrderedCommSemiring)))) (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal)))) ENat.toENNReal
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_mono ENat.toENNReal_monoₓ'. -/
@[mono]
theorem toENNReal_mono : Monotone (coe : ℕ∞ → ℝ≥0∞) :=
  toENNRealOrderEmbedding.Monotone
#align enat.coe_ennreal_mono ENat.toENNReal_mono

/- warning: enat.coe_ennreal_strict_mono -> ENat.toENNReal_strictMono is a dubious translation:
lean 3 declaration is
  StrictMono.{0, 0} ENat ENNReal (PartialOrder.toPreorder.{0} ENat (OrderedAddCommMonoid.toPartialOrder.{0} ENat (OrderedSemiring.toOrderedAddCommMonoid.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat ENat.canonicallyOrderedCommSemiring))))) (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.completeLinearOrder)))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)))
but is expected to have type
  StrictMono.{0, 0} ENat ENNReal (PartialOrder.toPreorder.{0} ENat (OrderedSemiring.toPartialOrder.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat instENatCanonicallyOrderedCommSemiring)))) (PartialOrder.toPreorder.{0} ENNReal (CompleteSemilatticeInf.toPartialOrder.{0} ENNReal (CompleteLattice.toCompleteSemilatticeInf.{0} ENNReal (CompleteLinearOrder.toCompleteLattice.{0} ENNReal ENNReal.instCompleteLinearOrderENNReal)))) ENat.toENNReal
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_strict_mono ENat.toENNReal_strictMonoₓ'. -/
@[mono]
theorem toENNReal_strictMono : StrictMono (coe : ℕ∞ → ℝ≥0∞) :=
  toENNRealOrderEmbedding.StrictMono
#align enat.coe_ennreal_strict_mono ENat.toENNReal_strictMono

/- warning: enat.coe_ennreal_zero -> ENat.toENNReal_zero is a dubious translation:
lean 3 declaration is
  Eq.{1} ENNReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) (OfNat.ofNat.{0} ENat 0 (OfNat.mk.{0} ENat 0 (Zero.zero.{0} ENat ENat.hasZero)))) (OfNat.ofNat.{0} ENNReal 0 (OfNat.mk.{0} ENNReal 0 (Zero.zero.{0} ENNReal ENNReal.hasZero)))
but is expected to have type
  Eq.{1} ENNReal (ENat.toENNReal (OfNat.ofNat.{0} ENat 0 (Zero.toOfNat0.{0} ENat instENatZero))) (OfNat.ofNat.{0} ENNReal 0 (Zero.toOfNat0.{0} ENNReal instENNRealZero))
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_zero ENat.toENNReal_zeroₓ'. -/
@[simp, norm_cast]
theorem toENNReal_zero : ((0 : ℕ∞) : ℝ≥0∞) = 0 :=
  map_zero toENNRealRingHom
#align enat.coe_ennreal_zero ENat.toENNReal_zero

/- warning: enat.coe_ennreal_add -> ENat.toENNReal_add is a dubious translation:
lean 3 declaration is
  forall (m : ENat) (n : ENat), Eq.{1} ENNReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) (HAdd.hAdd.{0, 0, 0} ENat ENat ENat (instHAdd.{0} ENat (Distrib.toHasAdd.{0} ENat (NonUnitalNonAssocSemiring.toDistrib.{0} ENat (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENat (Semiring.toNonAssocSemiring.{0} ENat (OrderedSemiring.toSemiring.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat ENat.canonicallyOrderedCommSemiring)))))))) m n)) (HAdd.hAdd.{0, 0, 0} ENNReal ENNReal ENNReal (instHAdd.{0} ENNReal (Distrib.toHasAdd.{0} ENNReal (NonUnitalNonAssocSemiring.toDistrib.{0} ENNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENNReal (Semiring.toNonAssocSemiring.{0} ENNReal (OrderedSemiring.toSemiring.{0} ENNReal (OrderedCommSemiring.toOrderedSemiring.{0} ENNReal (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENNReal ENNReal.canonicallyOrderedCommSemiring)))))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) m) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) n))
but is expected to have type
  forall (m : ENat) (n : ENat), Eq.{1} ENNReal (ENat.toENNReal (HAdd.hAdd.{0, 0, 0} ENat ENat ENat (instHAdd.{0} ENat (Distrib.toAdd.{0} ENat (NonUnitalNonAssocSemiring.toDistrib.{0} ENat (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENat (Semiring.toNonAssocSemiring.{0} ENat (OrderedSemiring.toSemiring.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat instENatCanonicallyOrderedCommSemiring)))))))) m n)) (HAdd.hAdd.{0, 0, 0} ENNReal ENNReal ENNReal (instHAdd.{0} ENNReal (Distrib.toAdd.{0} ENNReal (NonUnitalNonAssocSemiring.toDistrib.{0} ENNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENNReal (Semiring.toNonAssocSemiring.{0} ENNReal (OrderedSemiring.toSemiring.{0} ENNReal (OrderedCommSemiring.toOrderedSemiring.{0} ENNReal (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENNReal ENNReal.instCanonicallyOrderedCommSemiringENNReal)))))))) (ENat.toENNReal m) (ENat.toENNReal n))
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_add ENat.toENNReal_addₓ'. -/
@[simp]
theorem toENNReal_add (m n : ℕ∞) : ↑(m + n) = (m + n : ℝ≥0∞) :=
  map_add toENNRealRingHom m n
#align enat.coe_ennreal_add ENat.toENNReal_add

#print ENat.toENNReal_one /-
@[simp]
theorem toENNReal_one : ((1 : ℕ∞) : ℝ≥0∞) = 1 :=
  map_one toENNRealRingHom
#align enat.coe_ennreal_one ENat.toENNReal_one
-/

/- warning: enat.coe_ennreal_bit0 clashes with [anonymous] -> [anonymous]
warning: enat.coe_ennreal_bit0 -> [anonymous] is a dubious translation:
lean 3 declaration is
  forall (n : ENat), Eq.{1} ENNReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) (bit0.{0} ENat (Distrib.toHasAdd.{0} ENat (NonUnitalNonAssocSemiring.toDistrib.{0} ENat (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENat (Semiring.toNonAssocSemiring.{0} ENat (OrderedSemiring.toSemiring.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat ENat.canonicallyOrderedCommSemiring))))))) n)) (bit0.{0} ENNReal (Distrib.toHasAdd.{0} ENNReal (NonUnitalNonAssocSemiring.toDistrib.{0} ENNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENNReal (Semiring.toNonAssocSemiring.{0} ENNReal (OrderedSemiring.toSemiring.{0} ENNReal (OrderedCommSemiring.toOrderedSemiring.{0} ENNReal (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENNReal ENNReal.canonicallyOrderedCommSemiring))))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) n))
but is expected to have type
  forall {n : Type.{u}} {β : Type.{v}}, (Nat -> n -> β) -> Nat -> (List.{u} n) -> (List.{v} β)
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_bit0 [anonymous]ₓ'. -/
@[simp]
theorem [anonymous] (n : ℕ∞) : ↑(bit0 n) = bit0 (n : ℝ≥0∞) :=
  toENNReal_add n n
#align enat.coe_ennreal_bit0 [anonymous]

/- warning: enat.coe_ennreal_bit1 clashes with [anonymous] -> [anonymous]
warning: enat.coe_ennreal_bit1 -> [anonymous] is a dubious translation:
lean 3 declaration is
  forall (n : ENat), Eq.{1} ENNReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) (bit1.{0} ENat (AddMonoidWithOne.toOne.{0} ENat (AddCommMonoidWithOne.toAddMonoidWithOne.{0} ENat ENat.addCommMonoidWithOne)) (Distrib.toHasAdd.{0} ENat (NonUnitalNonAssocSemiring.toDistrib.{0} ENat (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENat (Semiring.toNonAssocSemiring.{0} ENat (OrderedSemiring.toSemiring.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat ENat.canonicallyOrderedCommSemiring))))))) n)) (bit1.{0} ENNReal (AddMonoidWithOne.toOne.{0} ENNReal (AddCommMonoidWithOne.toAddMonoidWithOne.{0} ENNReal ENNReal.addCommMonoidWithOne)) (Distrib.toHasAdd.{0} ENNReal (NonUnitalNonAssocSemiring.toDistrib.{0} ENNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENNReal (Semiring.toNonAssocSemiring.{0} ENNReal (OrderedSemiring.toSemiring.{0} ENNReal (OrderedCommSemiring.toOrderedSemiring.{0} ENNReal (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENNReal ENNReal.canonicallyOrderedCommSemiring))))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) n))
but is expected to have type
  forall {n : Type.{u}} {β : Type.{v}}, (Nat -> n -> β) -> Nat -> (List.{u} n) -> (List.{v} β)
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_bit1 [anonymous]ₓ'. -/
@[simp]
theorem [anonymous] (n : ℕ∞) : ↑(bit1 n) = bit1 (n : ℝ≥0∞) :=
  map_bit1 toENNRealRingHom n
#align enat.coe_ennreal_bit1 [anonymous]

/- warning: enat.coe_ennreal_mul -> ENat.toENNReal_mul is a dubious translation:
lean 3 declaration is
  forall (m : ENat) (n : ENat), Eq.{1} ENNReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) (HMul.hMul.{0, 0, 0} ENat ENat ENat (instHMul.{0} ENat (Distrib.toHasMul.{0} ENat (NonUnitalNonAssocSemiring.toDistrib.{0} ENat (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENat (Semiring.toNonAssocSemiring.{0} ENat (OrderedSemiring.toSemiring.{0} ENat (OrderedCommSemiring.toOrderedSemiring.{0} ENat (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENat ENat.canonicallyOrderedCommSemiring)))))))) m n)) (HMul.hMul.{0, 0, 0} ENNReal ENNReal ENNReal (instHMul.{0} ENNReal (Distrib.toHasMul.{0} ENNReal (NonUnitalNonAssocSemiring.toDistrib.{0} ENNReal (NonAssocSemiring.toNonUnitalNonAssocSemiring.{0} ENNReal (Semiring.toNonAssocSemiring.{0} ENNReal (OrderedSemiring.toSemiring.{0} ENNReal (OrderedCommSemiring.toOrderedSemiring.{0} ENNReal (CanonicallyOrderedCommSemiring.toOrderedCommSemiring.{0} ENNReal ENNReal.canonicallyOrderedCommSemiring)))))))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) m) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) n))
but is expected to have type
  forall (m : ENat) (n : ENat), Eq.{1} ENNReal (ENat.toENNReal (HMul.hMul.{0, 0, 0} ENat ENat ENat (instHMul.{0} ENat (CanonicallyOrderedCommSemiring.toMul.{0} ENat instENatCanonicallyOrderedCommSemiring)) m n)) (HMul.hMul.{0, 0, 0} ENNReal ENNReal ENNReal (instHMul.{0} ENNReal (CanonicallyOrderedCommSemiring.toMul.{0} ENNReal ENNReal.instCanonicallyOrderedCommSemiringENNReal)) (ENat.toENNReal m) (ENat.toENNReal n))
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_mul ENat.toENNReal_mulₓ'. -/
@[simp]
theorem toENNReal_mul (m n : ℕ∞) : ↑(m * n) = (m * n : ℝ≥0∞) :=
  map_mul toENNRealRingHom m n
#align enat.coe_ennreal_mul ENat.toENNReal_mul

/- warning: enat.coe_ennreal_min -> ENat.toENNReal_min is a dubious translation:
lean 3 declaration is
  forall (m : ENat) (n : ENat), Eq.{1} ENNReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) (LinearOrder.min.{0} ENat ENat.linearOrder m n)) (LinearOrder.min.{0} ENNReal (ConditionallyCompleteLinearOrder.toLinearOrder.{0} ENNReal (ConditionallyCompleteLinearOrderBot.toConditionallyCompleteLinearOrder.{0} ENNReal (CompleteLinearOrder.toConditionallyCompleteLinearOrderBot.{0} ENNReal ENNReal.completeLinearOrder))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) m) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) n))
but is expected to have type
  forall (m : ENat) (n : ENat), Eq.{1} ENNReal (ENat.toENNReal (Min.min.{0} ENat (CanonicallyLinearOrderedAddMonoid.toMin.{0} ENat instENatCanonicallyLinearOrderedAddMonoid) m n)) (Min.min.{0} ENNReal (CanonicallyLinearOrderedAddMonoid.toMin.{0} ENNReal ENNReal.instCanonicallyLinearOrderedAddMonoidENNReal) (ENat.toENNReal m) (ENat.toENNReal n))
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_min ENat.toENNReal_minₓ'. -/
@[simp]
theorem toENNReal_min (m n : ℕ∞) : ↑(min m n) = (min m n : ℝ≥0∞) :=
  toENNReal_mono.map_min
#align enat.coe_ennreal_min ENat.toENNReal_min

/- warning: enat.coe_ennreal_max -> ENat.toENNReal_max is a dubious translation:
lean 3 declaration is
  forall (m : ENat) (n : ENat), Eq.{1} ENNReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) (LinearOrder.max.{0} ENat ENat.linearOrder m n)) (LinearOrder.max.{0} ENNReal (ConditionallyCompleteLinearOrder.toLinearOrder.{0} ENNReal (ConditionallyCompleteLinearOrderBot.toConditionallyCompleteLinearOrder.{0} ENNReal (CompleteLinearOrder.toConditionallyCompleteLinearOrderBot.{0} ENNReal ENNReal.completeLinearOrder))) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) m) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) n))
but is expected to have type
  forall (m : ENat) (n : ENat), Eq.{1} ENNReal (ENat.toENNReal (Max.max.{0} ENat (CanonicallyLinearOrderedAddMonoid.toMax.{0} ENat instENatCanonicallyLinearOrderedAddMonoid) m n)) (Max.max.{0} ENNReal (CanonicallyLinearOrderedAddMonoid.toMax.{0} ENNReal ENNReal.instCanonicallyLinearOrderedAddMonoidENNReal) (ENat.toENNReal m) (ENat.toENNReal n))
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_max ENat.toENNReal_maxₓ'. -/
@[simp]
theorem toENNReal_max (m n : ℕ∞) : ↑(max m n) = (max m n : ℝ≥0∞) :=
  toENNReal_mono.map_max
#align enat.coe_ennreal_max ENat.toENNReal_max

/- warning: enat.coe_ennreal_sub -> ENat.toENNReal_sub is a dubious translation:
lean 3 declaration is
  forall (m : ENat) (n : ENat), Eq.{1} ENNReal ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) (HSub.hSub.{0, 0, 0} ENat ENat ENat (instHSub.{0} ENat ENat.hasSub) m n)) (HSub.hSub.{0, 0, 0} ENNReal ENNReal ENNReal (instHSub.{0} ENNReal ENNReal.hasSub) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) m) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) ENat ENNReal (HasLiftT.mk.{1, 1} ENat ENNReal (CoeTCₓ.coe.{1, 1} ENat ENNReal ENat.hasCoeENNReal)) n))
but is expected to have type
  forall (m : ENat) (n : ENat), Eq.{1} ENNReal (ENat.toENNReal (HSub.hSub.{0, 0, 0} ENat ENat ENat (instHSub.{0} ENat instENatSub) m n)) (HSub.hSub.{0, 0, 0} ENNReal ENNReal ENNReal (instHSub.{0} ENNReal ENNReal.instSubENNReal) (ENat.toENNReal m) (ENat.toENNReal n))
Case conversion may be inaccurate. Consider using '#align enat.coe_ennreal_sub ENat.toENNReal_subₓ'. -/
@[simp]
theorem toENNReal_sub (m n : ℕ∞) : ↑(m - n) = (m - n : ℝ≥0∞) :=
  WithTop.map_sub Nat.cast_tsub Nat.cast_zero m n
#align enat.coe_ennreal_sub ENat.toENNReal_sub

end ENat


/-
Copyright (c) 2019 Zhouhang Zhou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Zhouhang Zhou
-/
import measure_theory.function.lp_space

/-!
# Integrable functions and `L¹` space

In the first part of this file, the predicate `integrable` is defined and basic properties of
integrable functions are proved.

Such a predicate is already available under the name `mem_ℒp 1`. We give a direct definition which
is easier to use, and show that it is equivalent to `mem_ℒp 1`

In the second part, we establish an API between `integrable` and the space `L¹` of equivalence
classes of integrable functions, already defined as a special case of `L^p` spaces for `p = 1`.

## Notation

* `α →₁[μ] β` is the type of `L¹` space, where `α` is a `measure_space` and `β` is a `normed_group`
  with a `second_countable_topology`. `f : α →ₘ β` is a "function" in `L¹`. In comments, `[f]` is
  also used to denote an `L¹` function.

  `₁` can be typed as `\1`.

## Main definitions

* Let `f : α → β` be a function, where `α` is a `measure_space` and `β` a `normed_group`.
  Then `has_finite_integral f` means `(∫⁻ a, nnnorm (f a)) < ∞`.

* If `β` is moreover a `measurable_space` then `f` is called `integrable` if
  `f` is `measurable` and `has_finite_integral f` holds.

## Implementation notes

To prove something for an arbitrary integrable function, a useful theorem is
`integrable.induction` in the file `set_integral`.

## Tags

integrable, function space, l1

-/

noncomputable theory

open_locale classical topological_space big_operators ennreal measure_theory nnreal

open set filter topological_space ennreal emetric measure_theory

variables {α β γ δ : Type*} {m : measurable_space α} {μ ν : measure α} [measurable_space δ]
variables [normed_group β]
variables [normed_group γ]

namespace measure_theory

/-! ### Some results about the Lebesgue integral involving a normed group -/

lemma lintegral_nnnorm_eq_lintegral_edist (f : α → β) :
  ∫⁻ a, nnnorm (f a) ∂μ = ∫⁻ a, edist (f a) 0 ∂μ :=
by simp only [edist_eq_coe_nnnorm]

lemma lintegral_norm_eq_lintegral_edist (f : α → β) :
  ∫⁻ a, (ennreal.of_real ∥f a∥) ∂μ = ∫⁻ a, edist (f a) 0 ∂μ :=
by simp only [of_real_norm_eq_coe_nnnorm, edist_eq_coe_nnnorm]

lemma lintegral_edist_triangle {f g h : α → β}
  (hf : ae_strongly_measurable f μ) (hg : ae_strongly_measurable g μ)
  (hh : ae_strongly_measurable h μ) :
  ∫⁻ a, edist (f a) (g a) ∂μ ≤ ∫⁻ a, edist (f a) (h a) ∂μ + ∫⁻ a, edist (g a) (h a) ∂μ :=
begin
  rw ← lintegral_add' (hf.edist hh) (hg.edist hh),
  refine lintegral_mono (λ a, _),
  apply edist_triangle_right
end

lemma lintegral_nnnorm_zero : ∫⁻ a : α, nnnorm (0 : β) ∂μ = 0 := by simp

lemma lintegral_nnnorm_add
  {f : α → β} {g : α → γ} (hf : ae_strongly_measurable f μ) (hg : ae_strongly_measurable g μ) :
  ∫⁻ a, nnnorm (f a) + nnnorm (g a) ∂μ = ∫⁻ a, nnnorm (f a) ∂μ + ∫⁻ a, nnnorm (g a) ∂μ :=
lintegral_add' hf.ennnorm hg.ennnorm

lemma lintegral_nnnorm_neg {f : α → β} :
  ∫⁻ a, nnnorm ((-f) a) ∂μ = ∫⁻ a, nnnorm (f a) ∂μ :=
by simp only [pi.neg_apply, nnnorm_neg]

/-! ### The predicate `has_finite_integral` -/

/-- `has_finite_integral f μ` means that the integral `∫⁻ a, ∥f a∥ ∂μ` is finite.
  `has_finite_integral f` means `has_finite_integral f volume`. -/
def has_finite_integral {m : measurable_space α} (f : α → β) (μ : measure α . volume_tac) : Prop :=
∫⁻ a, nnnorm (f a) ∂μ < ∞

lemma has_finite_integral_iff_norm (f : α → β) :
  has_finite_integral f μ ↔ ∫⁻ a, (ennreal.of_real ∥f a∥) ∂μ < ∞ :=
by simp only [has_finite_integral, of_real_norm_eq_coe_nnnorm]

lemma has_finite_integral_iff_edist (f : α → β) :
  has_finite_integral f μ ↔ ∫⁻ a, edist (f a) 0 ∂μ < ∞ :=
by simp only [has_finite_integral_iff_norm, edist_dist, dist_zero_right]

lemma has_finite_integral_iff_of_real {f : α → ℝ} (h : 0 ≤ᵐ[μ] f) :
  has_finite_integral f μ ↔ ∫⁻ a, ennreal.of_real (f a) ∂μ < ∞ :=
have lintegral_eq : ∫⁻ a, (ennreal.of_real ∥f a∥) ∂μ = ∫⁻ a, ennreal.of_real (f a) ∂μ :=
begin
  refine lintegral_congr_ae (h.mono $ λ a h, _),
  rwa [real.norm_eq_abs, abs_of_nonneg]
end,
by rw [has_finite_integral_iff_norm, lintegral_eq]

lemma has_finite_integral_iff_of_nnreal {f : α → ℝ≥0} :
  has_finite_integral (λ x, (f x : ℝ)) μ ↔ ∫⁻ a, f a ∂μ < ∞ :=
by simp [has_finite_integral_iff_norm]

lemma has_finite_integral.mono {f : α → β} {g : α → γ} (hg : has_finite_integral g μ)
  (h : ∀ᵐ a ∂μ, ∥f a∥ ≤ ∥g a∥) : has_finite_integral f μ :=
begin
  simp only [has_finite_integral_iff_norm] at *,
  calc ∫⁻ a, (ennreal.of_real ∥f a∥) ∂μ ≤ ∫⁻ (a : α), (ennreal.of_real ∥g a∥) ∂μ :
    lintegral_mono_ae (h.mono $ assume a h, of_real_le_of_real h)
    ... < ∞ : hg
end

lemma has_finite_integral.mono' {f : α → β} {g : α → ℝ} (hg : has_finite_integral g μ)
  (h : ∀ᵐ a ∂μ, ∥f a∥ ≤ g a) : has_finite_integral f μ :=
hg.mono $ h.mono $ λ x hx, le_trans hx (le_abs_self _)

lemma has_finite_integral.congr' {f : α → β} {g : α → γ} (hf : has_finite_integral f μ)
  (h : ∀ᵐ a ∂μ, ∥f a∥ = ∥g a∥) :
  has_finite_integral g μ :=
hf.mono $ eventually_eq.le $ eventually_eq.symm h

lemma has_finite_integral_congr' {f : α → β} {g : α → γ} (h : ∀ᵐ a ∂μ, ∥f a∥ = ∥g a∥) :
  has_finite_integral f μ ↔ has_finite_integral g μ :=
⟨λ hf, hf.congr' h, λ hg, hg.congr' $ eventually_eq.symm h⟩

lemma has_finite_integral.congr {f g : α → β} (hf : has_finite_integral f μ) (h : f =ᵐ[μ] g) :
  has_finite_integral g μ :=
hf.congr' $ h.fun_comp norm

lemma has_finite_integral_congr {f g : α → β} (h : f =ᵐ[μ] g) :
  has_finite_integral f μ ↔ has_finite_integral g μ :=
has_finite_integral_congr' $ h.fun_comp norm

lemma has_finite_integral_const_iff {c : β} :
  has_finite_integral (λ x : α, c) μ ↔ c = 0 ∨ μ univ < ∞ :=
by simp [has_finite_integral, lintegral_const, lt_top_iff_ne_top, or_iff_not_imp_left]

lemma has_finite_integral_const [is_finite_measure μ] (c : β) :
  has_finite_integral (λ x : α, c) μ :=
has_finite_integral_const_iff.2 (or.inr $ measure_lt_top _ _)

lemma has_finite_integral_of_bounded [is_finite_measure μ] {f : α → β} {C : ℝ}
  (hC : ∀ᵐ a ∂μ, ∥f a∥ ≤ C) : has_finite_integral f μ :=
(has_finite_integral_const C).mono' hC

lemma has_finite_integral.mono_measure {f : α → β} (h : has_finite_integral f ν) (hμ : μ ≤ ν) :
  has_finite_integral f μ :=
lt_of_le_of_lt (lintegral_mono' hμ le_rfl) h

lemma has_finite_integral.add_measure {f : α → β} (hμ : has_finite_integral f μ)
  (hν : has_finite_integral f ν) : has_finite_integral f (μ + ν) :=
begin
  simp only [has_finite_integral, lintegral_add_measure] at *,
  exact add_lt_top.2 ⟨hμ, hν⟩
end

lemma has_finite_integral.left_of_add_measure {f : α → β} (h : has_finite_integral f (μ + ν)) :
  has_finite_integral f μ :=
h.mono_measure $ measure.le_add_right $ le_rfl

lemma has_finite_integral.right_of_add_measure {f : α → β} (h : has_finite_integral f (μ + ν)) :
  has_finite_integral f ν :=
h.mono_measure $ measure.le_add_left $ le_rfl

@[simp] lemma has_finite_integral_add_measure {f : α → β} :
  has_finite_integral f (μ + ν) ↔ has_finite_integral f μ ∧ has_finite_integral f ν :=
⟨λ h, ⟨h.left_of_add_measure, h.right_of_add_measure⟩, λ h, h.1.add_measure h.2⟩

lemma has_finite_integral.smul_measure {f : α → β} (h : has_finite_integral f μ) {c : ℝ≥0∞}
  (hc : c ≠ ∞) : has_finite_integral f (c • μ) :=
begin
  simp only [has_finite_integral, lintegral_smul_measure] at *,
  exact mul_lt_top hc h.ne
end

@[simp] lemma has_finite_integral_zero_measure {m : measurable_space α} (f : α → β) :
  has_finite_integral f (0 : measure α) :=
by simp only [has_finite_integral, lintegral_zero_measure, with_top.zero_lt_top]

variables (α β μ)
@[simp] lemma has_finite_integral_zero : has_finite_integral (λa:α, (0:β)) μ :=
by simp [has_finite_integral]
variables {α β μ}

lemma has_finite_integral.neg {f : α → β} (hfi : has_finite_integral f μ) :
  has_finite_integral (-f) μ :=
by simpa [has_finite_integral] using hfi

@[simp] lemma has_finite_integral_neg_iff {f : α → β} :
  has_finite_integral (-f) μ ↔ has_finite_integral f μ :=
⟨λ h, neg_neg f ▸ h.neg, has_finite_integral.neg⟩

lemma has_finite_integral.norm {f : α → β} (hfi : has_finite_integral f μ) :
  has_finite_integral (λa, ∥f a∥) μ :=
have eq : (λa, (nnnorm ∥f a∥ : ℝ≥0∞)) = λa, (nnnorm (f a) : ℝ≥0∞),
  by { funext, rw nnnorm_norm },
by { rwa [has_finite_integral, eq] }

lemma has_finite_integral_norm_iff (f : α → β) :
  has_finite_integral (λa, ∥f a∥) μ ↔ has_finite_integral f μ :=
has_finite_integral_congr' $ eventually_of_forall $ λ x, norm_norm (f x)

lemma has_finite_integral_to_real_of_lintegral_ne_top
  {f : α → ℝ≥0∞} (hf : ∫⁻ x, f x ∂μ ≠ ∞) :
  has_finite_integral (λ x, (f x).to_real) μ :=
begin
  have : ∀ x, (∥(f x).to_real∥₊ : ℝ≥0∞) =
    @coe ℝ≥0 ℝ≥0∞ _ (⟨(f x).to_real, ennreal.to_real_nonneg⟩ : ℝ≥0),
  { intro x, rw real.nnnorm_of_nonneg },
  simp_rw [has_finite_integral, this],
  refine lt_of_le_of_lt (lintegral_mono (λ x, _)) (lt_top_iff_ne_top.2 hf),
  by_cases hfx : f x = ∞,
  { simp [hfx] },
  { lift f x to ℝ≥0 using hfx with fx,
    simp [← h] }
end

lemma is_finite_measure_with_density_of_real {f : α → ℝ} (hfi : has_finite_integral f μ) :
  is_finite_measure (μ.with_density (λ x, ennreal.of_real $ f x)) :=
begin
  refine is_finite_measure_with_density ((lintegral_mono $ λ x, _).trans_lt hfi).ne,
  exact real.of_real_le_ennnorm (f x)
end

section dominated_convergence

variables {F : ℕ → α → β} {f : α → β} {bound : α → ℝ}

lemma all_ae_of_real_F_le_bound (h : ∀ n, ∀ᵐ a ∂μ, ∥F n a∥ ≤ bound a) :
  ∀ n, ∀ᵐ a ∂μ, ennreal.of_real ∥F n a∥ ≤ ennreal.of_real (bound a) :=
λn, (h n).mono $ λ a h, ennreal.of_real_le_of_real h

lemma all_ae_tendsto_of_real_norm (h : ∀ᵐ a ∂μ, tendsto (λ n, F n a) at_top $ 𝓝 $ f a) :
  ∀ᵐ a ∂μ, tendsto (λn, ennreal.of_real ∥F n a∥) at_top $ 𝓝 $ ennreal.of_real ∥f a∥ :=
h.mono $
  λ a h, tendsto_of_real $ tendsto.comp (continuous.tendsto continuous_norm _) h

lemma all_ae_of_real_f_le_bound (h_bound : ∀ n, ∀ᵐ a ∂μ, ∥F n a∥ ≤ bound a)
  (h_lim : ∀ᵐ a ∂μ, tendsto (λ n, F n a) at_top (𝓝 (f a))) :
  ∀ᵐ a ∂μ, ennreal.of_real ∥f a∥ ≤ ennreal.of_real (bound a) :=
begin
  have F_le_bound := all_ae_of_real_F_le_bound h_bound,
  rw ← ae_all_iff at F_le_bound,
  apply F_le_bound.mp ((all_ae_tendsto_of_real_norm h_lim).mono _),
  assume a tendsto_norm F_le_bound,
  exact le_of_tendsto' tendsto_norm (F_le_bound)
end

lemma has_finite_integral_of_dominated_convergence {F : ℕ → α → β} {f : α → β} {bound : α → ℝ}
  (bound_has_finite_integral : has_finite_integral bound μ)
  (h_bound : ∀ n, ∀ᵐ a ∂μ, ∥F n a∥ ≤ bound a)
  (h_lim : ∀ᵐ a ∂μ, tendsto (λ n, F n a) at_top (𝓝 (f a))) :
  has_finite_integral f μ :=
/- `∥F n a∥ ≤ bound a` and `∥F n a∥ --> ∥f a∥` implies `∥f a∥ ≤ bound a`,
  and so `∫ ∥f∥ ≤ ∫ bound < ∞` since `bound` is has_finite_integral -/
begin
  rw has_finite_integral_iff_norm,
  calc ∫⁻ a, (ennreal.of_real ∥f a∥) ∂μ ≤ ∫⁻ a, ennreal.of_real (bound a) ∂μ :
    lintegral_mono_ae $ all_ae_of_real_f_le_bound h_bound h_lim
    ... < ∞ :
    begin
      rw ← has_finite_integral_iff_of_real,
      { exact bound_has_finite_integral },
      exact (h_bound 0).mono (λ a h, le_trans (norm_nonneg _) h)
    end
end

lemma tendsto_lintegral_norm_of_dominated_convergence
  {F : ℕ → α → β} {f : α → β} {bound : α → ℝ}
  (F_measurable : ∀ n, ae_strongly_measurable (F n) μ)
  (bound_has_finite_integral : has_finite_integral bound μ)
  (h_bound : ∀ n, ∀ᵐ a ∂μ, ∥F n a∥ ≤ bound a)
  (h_lim : ∀ᵐ a ∂μ, tendsto (λ n, F n a) at_top (𝓝 (f a))) :
  tendsto (λn, ∫⁻ a, (ennreal.of_real ∥F n a - f a∥) ∂μ) at_top (𝓝 0) :=
have f_measurable : ae_strongly_measurable f μ :=
  ae_strongly_measurable_of_tendsto_ae _ F_measurable h_lim,
let b := λ a, 2 * ennreal.of_real (bound a) in
/- `∥F n a∥ ≤ bound a` and `F n a --> f a` implies `∥f a∥ ≤ bound a`, and thus by the
  triangle inequality, have `∥F n a - f a∥ ≤ 2 * (bound a). -/
have hb : ∀ n, ∀ᵐ a ∂μ, ennreal.of_real ∥F n a - f a∥ ≤ b a,
begin
  assume n,
  filter_upwards [all_ae_of_real_F_le_bound h_bound n, all_ae_of_real_f_le_bound h_bound h_lim]
    with a h₁ h₂,
  calc ennreal.of_real ∥F n a - f a∥ ≤ (ennreal.of_real ∥F n a∥) + (ennreal.of_real ∥f a∥) :
  begin
    rw [← ennreal.of_real_add],
    apply of_real_le_of_real,
    { apply norm_sub_le }, { exact norm_nonneg _ }, { exact norm_nonneg _ }
  end
    ... ≤ (ennreal.of_real (bound a)) + (ennreal.of_real (bound a)) : add_le_add h₁ h₂
    ... = b a : by rw ← two_mul
end,
/- On the other hand, `F n a --> f a` implies that `∥F n a - f a∥ --> 0`  -/
have h : ∀ᵐ a ∂μ, tendsto (λ n, ennreal.of_real ∥F n a - f a∥) at_top (𝓝 0),
begin
  rw ← ennreal.of_real_zero,
  refine h_lim.mono (λ a h, (continuous_of_real.tendsto _).comp _),
  rwa ← tendsto_iff_norm_tendsto_zero
end,
/- Therefore, by the dominated convergence theorem for nonnegative integration, have
  ` ∫ ∥f a - F n a∥ --> 0 ` -/
begin
  suffices h : tendsto (λn, ∫⁻ a, (ennreal.of_real ∥F n a - f a∥) ∂μ) at_top (𝓝 (∫⁻ (a:α), 0 ∂μ)),
  { rwa lintegral_zero at h },
  -- Using the dominated convergence theorem.
  refine tendsto_lintegral_of_dominated_convergence' _ _ hb _ _,
  -- Show `λa, ∥f a - F n a∥` is almost everywhere measurable for all `n`
  { exact λ n,  measurable_of_real.comp_ae_measurable
      ((F_measurable n).sub f_measurable).norm.ae_measurable },
  -- Show `2 * bound` is has_finite_integral
  { rw has_finite_integral_iff_of_real at bound_has_finite_integral,
    { calc ∫⁻ a, b a ∂μ = 2 * ∫⁻ a, ennreal.of_real (bound a) ∂μ :
        by { rw lintegral_const_mul', exact coe_ne_top }
        ... ≠ ∞ : mul_ne_top coe_ne_top bound_has_finite_integral.ne },
    filter_upwards [h_bound 0] with _ h using le_trans (norm_nonneg _) h },
  -- Show `∥f a - F n a∥ --> 0`
  { exact h }
end

end dominated_convergence

section pos_part
/-! Lemmas used for defining the positive part of a `L¹` function -/

lemma has_finite_integral.max_zero {f : α → ℝ} (hf : has_finite_integral f μ) :
  has_finite_integral (λa, max (f a) 0) μ :=
hf.mono $ eventually_of_forall $ λ x, by simp [real.norm_eq_abs, abs_le, abs_nonneg, le_abs_self]

lemma has_finite_integral.min_zero {f : α → ℝ} (hf : has_finite_integral f μ) :
  has_finite_integral (λa, min (f a) 0) μ :=
hf.mono $ eventually_of_forall $ λ x,
  by simp [real.norm_eq_abs, abs_le, abs_nonneg, neg_le, neg_le_abs_self, abs_eq_max_neg, le_total]

end pos_part

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

lemma has_finite_integral.smul (c : 𝕜) {f : α → β} : has_finite_integral f μ →
  has_finite_integral (c • f) μ :=
begin
  simp only [has_finite_integral], assume hfi,
  calc
    ∫⁻ (a : α), nnnorm (c • f a) ∂μ = ∫⁻ (a : α), (nnnorm c) * nnnorm (f a) ∂μ :
      by simp only [nnnorm_smul, ennreal.coe_mul]
    ... < ∞ :
    begin
      rw lintegral_const_mul',
      exacts [mul_lt_top coe_ne_top hfi.ne, coe_ne_top]
    end
end

lemma has_finite_integral_smul_iff {c : 𝕜} (hc : c ≠ 0) (f : α → β) :
  has_finite_integral (c • f) μ ↔ has_finite_integral f μ :=
begin
  split,
  { assume h,
    simpa only [smul_smul, inv_mul_cancel hc, one_smul] using h.smul c⁻¹ },
  exact has_finite_integral.smul _
end

lemma has_finite_integral.const_mul {f : α → ℝ} (h : has_finite_integral f μ) (c : ℝ) :
  has_finite_integral (λ x, c * f x) μ :=
(has_finite_integral.smul c h : _)

lemma has_finite_integral.mul_const {f : α → ℝ} (h : has_finite_integral f μ) (c : ℝ) :
  has_finite_integral (λ x, f x * c) μ :=
by simp_rw [mul_comm, h.const_mul _]

end normed_space

/-! ### The predicate `integrable` -/

-- variables [measurable_space β] [measurable_space γ] [measurable_space δ]

/-- `integrable f μ` means that `f` is measurable and that the integral `∫⁻ a, ∥f a∥ ∂μ` is finite.
  `integrable f` means `integrable f volume`. -/
def integrable {α} {m : measurable_space α} (f : α → β) (μ : measure α . volume_tac) : Prop :=
ae_strongly_measurable f μ ∧ has_finite_integral f μ

lemma mem_ℒp_one_iff_integrable {f : α → β} : mem_ℒp f 1 μ ↔ integrable f μ :=
by simp_rw [integrable, has_finite_integral, mem_ℒp, snorm_one_eq_lintegral_nnnorm]

lemma integrable.ae_strongly_measurable {f : α → β} (hf : integrable f μ) :
  ae_strongly_measurable f μ :=
hf.1

lemma integrable.ae_measurable [measurable_space β] [borel_space β]
  {f : α → β} (hf : integrable f μ) :
  ae_measurable f μ :=
hf.ae_strongly_measurable.ae_measurable

lemma integrable.has_finite_integral {f : α → β} (hf : integrable f μ) : has_finite_integral f μ :=
hf.2

lemma integrable.mono {f : α → β} {g : α → γ}
  (hg : integrable g μ) (hf : ae_strongly_measurable f μ) (h : ∀ᵐ a ∂μ, ∥f a∥ ≤ ∥g a∥) :
  integrable f μ :=
⟨hf, hg.has_finite_integral.mono h⟩

lemma integrable.mono' {f : α → β} {g : α → ℝ}
  (hg : integrable g μ) (hf : ae_strongly_measurable f μ) (h : ∀ᵐ a ∂μ, ∥f a∥ ≤ g a) :
  integrable f μ :=
⟨hf, hg.has_finite_integral.mono' h⟩

lemma integrable.congr' {f : α → β} {g : α → γ}
  (hf : integrable f μ) (hg : ae_strongly_measurable g μ) (h : ∀ᵐ a ∂μ, ∥f a∥ = ∥g a∥) :
  integrable g μ :=
⟨hg, hf.has_finite_integral.congr' h⟩

lemma integrable_congr' {f : α → β} {g : α → γ}
  (hf : ae_strongly_measurable f μ) (hg : ae_strongly_measurable g μ) (h : ∀ᵐ a ∂μ, ∥f a∥ = ∥g a∥) :
  integrable f μ ↔ integrable g μ :=
⟨λ h2f, h2f.congr' hg h, λ h2g, h2g.congr' hf $ eventually_eq.symm h⟩

lemma integrable.congr {f g : α → β} (hf : integrable f μ) (h : f =ᵐ[μ] g) :
  integrable g μ :=
⟨hf.1.congr h, hf.2.congr h⟩

lemma integrable_congr {f g : α → β} (h : f =ᵐ[μ] g) :
  integrable f μ ↔ integrable g μ :=
⟨λ hf, hf.congr h, λ hg, hg.congr h.symm⟩

lemma integrable_const_iff {c : β} : integrable (λ x : α, c) μ ↔ c = 0 ∨ μ univ < ∞ :=
begin
  have : ae_strongly_measurable (λ (x : α), c) μ := ae_strongly_measurable_const,
  rw [integrable, and_iff_right this, has_finite_integral_const_iff]
end

lemma integrable_const [is_finite_measure μ] (c : β) : integrable (λ x : α, c) μ :=
integrable_const_iff.2 $ or.inr $ measure_lt_top _ _

lemma integrable.mono_measure {f : α → β} (h : integrable f ν) (hμ : μ ≤ ν) : integrable f μ :=
⟨h.ae_strongly_measurable.mono_measure hμ, h.has_finite_integral.mono_measure hμ⟩

lemma integrable.of_measure_le_smul {μ' : measure α} (c : ℝ≥0∞) (hc : c ≠ ∞)
  (hμ'_le : μ' ≤ c • μ) {f : α → β} (hf : integrable f μ) :
  integrable f μ' :=
by { rw ← mem_ℒp_one_iff_integrable at hf ⊢, exact hf.of_measure_le_smul c hc hμ'_le, }

lemma integrable.add_measure {f : α → β} (hμ : integrable f μ) (hν : integrable f ν) :
  integrable f (μ + ν) :=
begin
  simp_rw ← mem_ℒp_one_iff_integrable at hμ hν ⊢,
  refine ⟨hμ.ae_strongly_measurable.add_measure hν.ae_strongly_measurable, _⟩,
  rw [snorm_one_add_measure, ennreal.add_lt_top],
  exact ⟨hμ.snorm_lt_top, hν.snorm_lt_top⟩,
end

lemma integrable.left_of_add_measure {f : α → β} (h : integrable f (μ + ν)) : integrable f μ :=
by { rw ← mem_ℒp_one_iff_integrable at h ⊢, exact h.left_of_add_measure, }

lemma integrable.right_of_add_measure {f : α → β} (h : integrable f (μ + ν)) : integrable f ν :=
by { rw ← mem_ℒp_one_iff_integrable at h ⊢, exact h.right_of_add_measure, }

@[simp] lemma integrable_add_measure {f : α → β} :
  integrable f (μ + ν) ↔ integrable f μ ∧ integrable f ν :=
⟨λ h, ⟨h.left_of_add_measure, h.right_of_add_measure⟩, λ h, h.1.add_measure h.2⟩

@[simp] lemma integrable_zero_measure {m : measurable_space α} {f : α → β} :
  integrable f (0 : measure α) :=
⟨ae_measurable_zero_measure f, has_finite_integral_zero_measure f⟩

theorem integrable_finset_sum_measure {ι} {m : measurable_space α} {f : α → β}
  {μ : ι → measure α} {s : finset ι} :
  integrable f (∑ i in s, μ i) ↔ ∀ i ∈ s, integrable f (μ i) :=
by induction s using finset.induction_on; simp [*]

lemma integrable.smul_measure {f : α → β} (h : integrable f μ) {c : ℝ≥0∞} (hc : c ≠ ∞) :
  integrable f (c • μ) :=
by { rw ← mem_ℒp_one_iff_integrable at h ⊢, exact h.smul_measure hc, }

lemma integrable.to_average {f : α → β} (h : integrable f μ) :
  integrable f ((μ univ)⁻¹ • μ) :=
begin
  rcases eq_or_ne μ 0 with rfl|hne,
  { rwa smul_zero },
  { apply h.smul_measure, simpa }
end

lemma integrable_map_measure {f : α → δ} {g : δ → β}
  (hg : ae_strongly_measurable g (measure.map f μ)) (hf : measurable f) :
  integrable g (measure.map f μ) ↔ integrable (g ∘ f) μ :=
by { simp_rw ← mem_ℒp_one_iff_integrable, exact mem_ℒp_map_measure_iff hg hf, }

lemma integrable.comp_measurable {f : α → δ} {g : δ → β}
  (hg : integrable g (measure.map f μ)) (hf : measurable f) : integrable (g ∘ f) μ :=
(integrable_map_measure hg.ae_strongly_measurable hf).mp hg

lemma _root_.measurable_embedding.integrable_map_iff
  {f : α → δ} (hf : measurable_embedding f) {g : δ → β} :
  integrable g (measure.map f μ) ↔ integrable (g ∘ f) μ :=
by { simp_rw ← mem_ℒp_one_iff_integrable, exact hf.mem_ℒp_map_measure_iff, }

lemma integrable_map_equiv (f : α ≃ᵐ δ) (g : δ → β) :
  integrable g (measure.map f μ) ↔ integrable (g ∘ f) μ :=
by { simp_rw ← mem_ℒp_one_iff_integrable, exact f.mem_ℒp_map_measure_iff, }

lemma measure_preserving.integrable_comp {ν : measure δ} {g : δ → β}
  {f : α → δ} (hf : measure_preserving f μ ν) (hg : ae_strongly_measurable g ν) :
  integrable (g ∘ f) μ ↔ integrable g ν :=
by { rw ← hf.map_eq at hg ⊢, exact (integrable_map_measure hg hf.measurable).symm }

lemma measure_preserving.integrable_comp_emb {f : α → δ} {ν} (h₁ : measure_preserving f μ ν)
  (h₂ : measurable_embedding f) {g : δ → β} :
  integrable (g ∘ f) μ ↔ integrable g ν :=
h₁.map_eq ▸ iff.symm h₂.integrable_map_iff

lemma lintegral_edist_lt_top {f g : α → β}
  (hf : integrable f μ) (hg : integrable g μ) :
  ∫⁻ a, edist (f a) (g a) ∂μ < ∞ :=
lt_of_le_of_lt
  (lintegral_edist_triangle hf.ae_strongly_measurable hg.ae_strongly_measurable
    (ae_strongly_measurable_const : ae_strongly_measurable (λa, (0 : β)) μ))
  (ennreal.add_lt_top.2 $ by { simp_rw ← has_finite_integral_iff_edist,
                               exact ⟨hf.has_finite_integral, hg.has_finite_integral⟩ })

variables (α β μ)
@[simp] lemma integrable_zero : integrable (λ _, (0 : β)) μ :=
by simp [integrable, ae_strongly_measurable_const]
variables {α β μ}

lemma integrable.add' {f g : α → β} (hf : integrable f μ)
  (hg : integrable g μ) :
  has_finite_integral (f + g) μ :=
calc ∫⁻ a, nnnorm (f a + g a) ∂μ ≤ ∫⁻ a, nnnorm (f a) + nnnorm (g a) ∂μ :
  lintegral_mono (λ a, by exact_mod_cast nnnorm_add_le _ _)
... = _ : lintegral_nnnorm_add hf.ae_strongly_measurable hg.ae_strongly_measurable
... < ∞ : add_lt_top.2 ⟨hf.has_finite_integral, hg.has_finite_integral⟩

lemma integrable.add
  {f g : α → β} (hf : integrable f μ) (hg : integrable g μ) : integrable (f + g) μ :=
⟨hf.ae_strongly_measurable.add hg.ae_strongly_measurable, hf.add' hg⟩

lemma integrable_finset_sum {ι} (s : finset ι)
  {f : ι → α → β} (hf : ∀ i ∈ s, integrable (f i) μ) : integrable (λ a, ∑ i in s, f i a) μ :=
begin
  simp only [← finset.sum_apply],
  exact finset.sum_induction f (λ g, integrable g μ) (λ _ _, integrable.add)
    (integrable_zero _ _ _) hf,
end

lemma integrable.neg {f : α → β} (hf : integrable f μ) : integrable (-f) μ :=
⟨hf.ae_strongly_measurable.neg, hf.has_finite_integral.neg⟩

@[simp] lemma integrable_neg_iff {f : α → β} :
  integrable (-f) μ ↔ integrable f μ :=
⟨λ h, neg_neg f ▸ h.neg, integrable.neg⟩

lemma integrable.sub' {f g : α → β}
  (hf : integrable f μ) (hg : integrable g μ) : has_finite_integral (f - g) μ :=
calc ∫⁻ a, nnnorm (f a - g a) ∂μ ≤ ∫⁻ a, nnnorm (f a) + nnnorm (-g a) ∂μ :
  lintegral_mono (assume a, by { simp only [sub_eq_add_neg], exact_mod_cast nnnorm_add_le _ _ } )
... = _ :
  by { simp only [nnnorm_neg],
       exact lintegral_nnnorm_add hf.ae_strongly_measurable hg.ae_strongly_measurable }
... < ∞ : add_lt_top.2 ⟨hf.has_finite_integral, hg.has_finite_integral⟩

lemma integrable.sub {f g : α → β}
  (hf : integrable f μ) (hg : integrable g μ) : integrable (f - g) μ :=
by simpa only [sub_eq_add_neg] using hf.add hg.neg

lemma integrable.norm {f : α → β} (hf : integrable f μ) :
  integrable (λa, ∥f a∥) μ :=
⟨hf.ae_strongly_measurable.norm, hf.has_finite_integral.norm⟩

lemma integrable_norm_iff {f : α → β} (hf : ae_strongly_measurable f μ) :
  integrable (λa, ∥f a∥) μ ↔ integrable f μ :=
by simp_rw [integrable, and_iff_right hf, and_iff_right hf.norm, has_finite_integral_norm_iff]

lemma integrable_of_norm_sub_le {f₀ f₁ : α → β} {g : α → ℝ}
  (hf₁_m : ae_strongly_measurable f₁ μ)
  (hf₀_i : integrable f₀ μ)
  (hg_i : integrable g μ)
  (h : ∀ᵐ a ∂μ, ∥f₀ a - f₁ a∥ ≤ g a) :
  integrable f₁ μ :=
begin
  have : ∀ᵐ a ∂μ, ∥f₁ a∥ ≤ ∥f₀ a∥ + g a,
  { apply h.mono,
    intros a ha,
    calc ∥f₁ a∥ ≤ ∥f₀ a∥ + ∥f₀ a - f₁ a∥ : norm_le_insert _ _
    ... ≤ ∥f₀ a∥ + g a : add_le_add_left ha _ },
  exact integrable.mono' (hf₀_i.norm.add hg_i) hf₁_m this
end

lemma integrable.prod_mk {f : α → β} {g : α → γ} (hf : integrable f μ) (hg : integrable g μ) :
  integrable (λ x, (f x, g x)) μ :=
⟨hf.ae_strongly_measurable.prod_mk hg.ae_strongly_measurable,
  (hf.norm.add' hg.norm).mono $ eventually_of_forall $ λ x,
  calc max ∥f x∥ ∥g x∥ ≤ ∥f x∥ + ∥g x∥   : max_le_add_of_nonneg (norm_nonneg _) (norm_nonneg _)
                 ... ≤ ∥(∥f x∥ + ∥g x∥)∥ : le_abs_self _⟩

lemma mem_ℒp.integrable {q : ℝ≥0∞} (hq1 : 1 ≤ q) {f : α → β} [is_finite_measure μ]
  (hfq : mem_ℒp f q μ) : integrable f μ :=
mem_ℒp_one_iff_integrable.mp (hfq.mem_ℒp_of_exponent_le hq1)

lemma lipschitz_with.integrable_comp_iff_of_antilipschitz {K K'} {f : α → β} {g : β → γ}
  (hg : lipschitz_with K g) (hg' : antilipschitz_with K' g) (g0 : g 0 = 0) :
  integrable (g ∘ f) μ ↔ integrable f μ :=
by simp [← mem_ℒp_one_iff_integrable, hg.mem_ℒp_comp_iff_of_antilipschitz hg' g0]

lemma integrable.real_to_nnreal {f : α → ℝ} (hf : integrable f μ) :
  integrable (λ x, ((f x).to_nnreal : ℝ)) μ :=
begin
  refine ⟨hf.ae_strongly_measurable.ae_measurable
    .real_to_nnreal.coe_nnreal_real.ae_strongly_measurable, _⟩,
  rw has_finite_integral_iff_norm,
  refine lt_of_le_of_lt _ ((has_finite_integral_iff_norm _).1 hf.has_finite_integral),
  apply lintegral_mono,
  assume x,
  simp [real.norm_eq_abs, ennreal.of_real_le_of_real, abs_le, abs_nonneg, le_abs_self],
end

lemma of_real_to_real_ae_eq {f : α → ℝ≥0∞} (hf : ∀ᵐ x ∂μ, f x < ∞) :
  (λ x, ennreal.of_real (f x).to_real) =ᵐ[μ] f :=
begin
  filter_upwards [hf],
  assume x hx,
  simp only [hx.ne, of_real_to_real, ne.def, not_false_iff],
end

lemma coe_to_nnreal_ae_eq {f : α → ℝ≥0∞} (hf : ∀ᵐ x ∂μ, f x < ∞) :
  (λ x, ((f x).to_nnreal : ℝ≥0∞)) =ᵐ[μ] f :=
begin
  filter_upwards [hf],
  assume x hx,
  simp only [hx.ne, ne.def, not_false_iff, coe_to_nnreal],
end

section
variables  {E : Type*} [normed_group E] [normed_space ℝ E]

lemma integrable_with_density_iff_integrable_coe_smul
  {f : α → ℝ≥0} (hf : measurable f) {g : α → E} :
  integrable g (μ.with_density (λ x, f x)) ↔ integrable (λ x, (f x : ℝ) • g x) μ :=
begin
  by_cases H : ae_strongly_measurable (λ (x : α), (f x : ℝ) • g x) μ,
  { simp only [integrable, ae_strongly_measurable_with_density_iff hf, has_finite_integral, H,
      true_and],
    rw lintegral_with_density_eq_lintegral_mul₀' hf.coe_nnreal_ennreal.ae_measurable,
    { congr',
      ext1 x,
      simp only [nnnorm_smul, nnreal.nnnorm_eq, coe_mul, pi.mul_apply] },
    { rw ae_measurable_with_density_ennreal_iff hf,
      convert H.ennnorm,
      ext1 x,
      simp only [nnnorm_smul, nnreal.nnnorm_eq, coe_mul] } },
  { simp only [integrable, ae_strongly_measurable_with_density_iff hf, H, false_and] }
end

lemma integrable_with_density_iff_integrable_smul {f : α → ℝ≥0} (hf : measurable f) {g : α → E} :
  integrable g (μ.with_density (λ x, f x)) ↔ integrable (λ x, f x • g x) μ :=
integrable_with_density_iff_integrable_coe_smul hf

lemma integrable_with_density_iff_integrable_smul'
  {f : α → ℝ≥0∞} (hf : measurable f) (hflt : ∀ᵐ x ∂μ, f x < ∞) {g : α → E} :
  integrable g (μ.with_density f) ↔ integrable (λ x, (f x).to_real • g x) μ :=
begin
  rw [← with_density_congr_ae (coe_to_nnreal_ae_eq hflt),
      integrable_with_density_iff_integrable_smul],
  { refl },
  { exact hf.ennreal_to_nnreal },
end

lemma integrable_with_density_iff_integrable_coe_smul₀
  {f : α → ℝ≥0} (hf : ae_measurable f μ) {g : α → E} :
  integrable g (μ.with_density (λ x, f x)) ↔ integrable (λ x, (f x : ℝ) • g x) μ :=
calc
integrable g (μ.with_density (λ x, f x))
    ↔ integrable g (μ.with_density (λ x, hf.mk f x)) :
begin
  suffices : (λ x, (f x : ℝ≥0∞)) =ᵐ[μ] (λ x, hf.mk f x), by rw with_density_congr_ae this,
  filter_upwards [hf.ae_eq_mk] with x hx,
  simp [hx],
end
... ↔ integrable (λ x, (hf.mk f x : ℝ) • g x) μ :
  integrable_with_density_iff_integrable_coe_smul hf.measurable_mk
... ↔ integrable (λ x, (f x : ℝ) • g x) μ :
begin
  apply integrable_congr,
  filter_upwards [hf.ae_eq_mk] with x hx,
  simp [hx],
end

lemma integrable_with_density_iff_integrable_smul₀
  {f : α → ℝ≥0} (hf : ae_measurable f μ) {g : α → E} :
  integrable g (μ.with_density (λ x, f x)) ↔ integrable (λ x, f x • g x) μ :=
integrable_with_density_iff_integrable_coe_smul₀ hf

end

lemma integrable_with_density_iff {f : α → ℝ≥0∞} (hf : measurable f)
  (hflt : ∀ᵐ x ∂μ, f x < ∞) {g : α → ℝ} :
  integrable g (μ.with_density f) ↔ integrable (λ x, g x * (f x).to_real) μ :=
begin
  have : (λ x, g x * (f x).to_real) = (λ x, (f x).to_real • g x), by simp [mul_comm],
  rw this,
  exact integrable_with_density_iff_integrable_smul' hf hflt,
end

section
variables {E : Type*} [normed_group E] [normed_space ℝ E]

lemma mem_ℒ1_smul_of_L1_with_density {f : α → ℝ≥0} (f_meas : measurable f)
  (u : Lp E 1 (μ.with_density (λ x, f x))) :
  mem_ℒp (λ x, f x • u x) 1 μ :=
mem_ℒp_one_iff_integrable.2 $ (integrable_with_density_iff_integrable_smul f_meas).1 $
mem_ℒp_one_iff_integrable.1 (Lp.mem_ℒp u)

variable (μ)
/-- The map `u ↦ f • u` is an isometry between the `L^1` spaces for `μ.with_density f` and `μ`. -/
noncomputable def with_density_smul_li {f : α → ℝ≥0} (f_meas : measurable f) :
  Lp E 1 (μ.with_density (λ x, f x)) →ₗᵢ[ℝ] Lp E 1 μ :=
{ to_fun := λ u, (mem_ℒ1_smul_of_L1_with_density f_meas u).to_Lp _,
  map_add' :=
  begin
    assume u v,
    ext1,
    filter_upwards [(mem_ℒ1_smul_of_L1_with_density f_meas u).coe_fn_to_Lp,
      (mem_ℒ1_smul_of_L1_with_density f_meas v).coe_fn_to_Lp,
      (mem_ℒ1_smul_of_L1_with_density f_meas (u + v)).coe_fn_to_Lp,
      Lp.coe_fn_add ((mem_ℒ1_smul_of_L1_with_density f_meas u).to_Lp _)
        ((mem_ℒ1_smul_of_L1_with_density f_meas v).to_Lp _),
      (ae_with_density_iff f_meas.coe_nnreal_ennreal).1 (Lp.coe_fn_add u v)],
    assume x hu hv huv h' h'',
    rw [huv, h', pi.add_apply, hu, hv],
    rcases eq_or_ne (f x) 0 with hx|hx,
    { simp only [hx, zero_smul, add_zero] },
    { rw [h'' _, pi.add_apply, smul_add],
      simpa only [ne.def, ennreal.coe_eq_zero] using hx }
  end,
  map_smul' :=
  begin
    assume r u,
    ext1,
    filter_upwards [(ae_with_density_iff f_meas.coe_nnreal_ennreal).1 (Lp.coe_fn_smul r u),
      (mem_ℒ1_smul_of_L1_with_density f_meas (r • u)).coe_fn_to_Lp,
      Lp.coe_fn_smul r ((mem_ℒ1_smul_of_L1_with_density f_meas u).to_Lp _),
      (mem_ℒ1_smul_of_L1_with_density f_meas u).coe_fn_to_Lp],
    assume x h h' h'' h''',
    rw [ring_hom.id_apply, h', h'', pi.smul_apply, h'''],
    rcases eq_or_ne (f x) 0 with hx|hx,
    { simp only [hx, zero_smul, smul_zero] },
    { rw [h _, smul_comm, pi.smul_apply],
      simpa only [ne.def, ennreal.coe_eq_zero] using hx }
  end,
  norm_map' :=
  begin
    assume u,
    simp only [snorm, linear_map.coe_mk, Lp.norm_to_Lp, one_ne_zero, ennreal.one_ne_top,
      ennreal.one_to_real, if_false, snorm', ennreal.rpow_one, _root_.div_one, Lp.norm_def],
    rw lintegral_with_density_eq_lintegral_mul_non_measurable _ f_meas.coe_nnreal_ennreal
      (filter.eventually_of_forall (λ x, ennreal.coe_lt_top)),
    congr' 1,
    apply lintegral_congr_ae,
    filter_upwards [(mem_ℒ1_smul_of_L1_with_density f_meas u).coe_fn_to_Lp] with x hx,
    rw [hx, pi.mul_apply],
    change ↑∥(f x : ℝ) • u x∥₊ = ↑(f x) * ↑∥u x∥₊,
    simp only [nnnorm_smul, nnreal.nnnorm_eq, ennreal.coe_mul],
  end }

@[simp] lemma with_density_smul_li_apply {f : α → ℝ≥0} (f_meas : measurable f)
  (u : Lp E 1 (μ.with_density (λ x, f x))) :
  with_density_smul_li μ f_meas u =
    (mem_ℒ1_smul_of_L1_with_density f_meas u).to_Lp (λ x, f x • u x) :=
rfl

end

lemma mem_ℒ1_to_real_of_lintegral_ne_top
  {f : α → ℝ≥0∞} (hfm : ae_measurable f μ) (hfi : ∫⁻ x, f x ∂μ ≠ ∞) :
  mem_ℒp (λ x, (f x).to_real) 1 μ :=
begin
  rw [mem_ℒp, snorm_one_eq_lintegral_nnnorm],
  exact ⟨(ae_measurable.ennreal_to_real hfm).ae_strongly_measurable,
          has_finite_integral_to_real_of_lintegral_ne_top hfi⟩
end

lemma integrable_to_real_of_lintegral_ne_top
  {f : α → ℝ≥0∞} (hfm : ae_measurable f μ) (hfi : ∫⁻ x, f x ∂μ ≠ ∞) :
  integrable (λ x, (f x).to_real) μ :=
mem_ℒp_one_iff_integrable.1 $ mem_ℒ1_to_real_of_lintegral_ne_top hfm hfi

section pos_part
/-! ### Lemmas used for defining the positive part of a `L¹` function -/

lemma integrable.max_zero {f : α → ℝ} (hf : integrable f μ) : integrable (λ a, max (f a) 0) μ :=
⟨(hf.ae_strongly_measurable.ae_measurable.max ae_measurable_const).ae_strongly_measurable,
  hf.has_finite_integral.max_zero⟩

lemma integrable.min_zero {f : α → ℝ} (hf : integrable f μ) : integrable (λ a, min (f a) 0) μ :=
⟨(hf.ae_strongly_measurable.ae_measurable.min ae_measurable_const).ae_strongly_measurable,
  hf.has_finite_integral.min_zero⟩

end pos_part

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

lemma integrable.smul (c : 𝕜) {f : α → β}
  (hf : integrable f μ) : integrable (c • f) μ :=
⟨hf.ae_strongly_measurable.const_smul c, hf.has_finite_integral.smul c⟩

lemma integrable_smul_iff {c : 𝕜} (hc : c ≠ 0) (f : α → β) :
  integrable (c • f) μ ↔ integrable f μ :=
and_congr (ae_strongly_measurable_const_smul_iff₀ hc) (has_finite_integral_smul_iff hc f)

lemma integrable.const_mul {f : α → ℝ} (h : integrable f μ) (c : ℝ) :
  integrable (λ x, c * f x) μ :=
integrable.smul c h

lemma integrable.mul_const {f : α → ℝ} (h : integrable f μ) (c : ℝ) :
  integrable (λ x, f x * c) μ :=
by simp_rw [mul_comm, h.const_mul _]

end normed_space

section normed_space_over_complete_field
variables {𝕜 : Type*} [nondiscrete_normed_field 𝕜] [complete_space 𝕜]
variables {E : Type*} [normed_group E] [normed_space 𝕜 E]

lemma integrable_smul_const {f : α → 𝕜} {c : E} (hc : c ≠ 0) :
  integrable (λ x, f x • c) μ ↔ integrable f μ :=
begin
  simp_rw [integrable, ae_strongly_measurable_smul_const_iff hc, and.congr_right_iff,
    has_finite_integral, nnnorm_smul, ennreal.coe_mul],
  intro hf, rw [lintegral_mul_const' _ _ ennreal.coe_ne_top, ennreal.mul_lt_top_iff],
  have : ∀ x : ℝ≥0∞, x = 0 → x < ∞ := by simp,
  simp [hc, or_iff_left_of_imp (this _)]
end
end normed_space_over_complete_field

section is_R_or_C
variables {𝕜 : Type*} [is_R_or_C 𝕜] {f : α → 𝕜}

lemma integrable.re_im_iff :
  integrable (λ x, is_R_or_C.re (f x)) μ ∧ integrable (λ x, is_R_or_C.im (f x)) μ ↔
  integrable f μ :=
by { simp_rw ← mem_ℒp_one_iff_integrable, exact mem_ℒp_re_im_iff }

lemma integrable.re (hf : integrable f μ) : integrable (λ x, is_R_or_C.re (f x)) μ :=
by { rw ← mem_ℒp_one_iff_integrable at hf ⊢, exact hf.re, }

lemma integrable.im (hf : integrable f μ) : integrable (λ x, is_R_or_C.im (f x)) μ :=
by { rw ← mem_ℒp_one_iff_integrable at hf ⊢, exact hf.im, }

end is_R_or_C

section inner_product
variables {𝕜 E : Type*} [is_R_or_C 𝕜] [inner_product_space 𝕜 E] {f : α → E}

local notation `⟪`x`, `y`⟫` := @inner 𝕜 E _ x y

lemma integrable.const_inner (c : E) (hf : integrable f μ) : integrable (λ x, ⟪c, f x⟫) μ :=
by { rw ← mem_ℒp_one_iff_integrable at hf ⊢, exact hf.const_inner c, }

lemma integrable.inner_const (hf : integrable f μ) (c : E) : integrable (λ x, ⟪f x, c⟫) μ :=
by { rw ← mem_ℒp_one_iff_integrable at hf ⊢, exact hf.inner_const c, }

end inner_product

section trim

variables {H : Type*} [normed_group H] {m0 : measurable_space α} {μ' : measure α} {f : α → H}

lemma integrable.trim (hm : m ≤ m0) (hf_int : integrable f μ') (hf : strongly_measurable[m] f) :
  integrable f (μ'.trim hm) :=
begin
  refine ⟨hf.ae_strongly_measurable, _⟩,
  rw [has_finite_integral, lintegral_trim hm _],
  { exact hf_int.2, },
  { exact @strongly_measurable.ennnorm _ m _ _ f hf },
end

lemma integrable_of_integrable_trim (hm : m ≤ m0) (hf_int : integrable f (μ'.trim hm)) :
  integrable f μ' :=
begin
  obtain ⟨hf_meas_ae, hf⟩ := hf_int,
  refine ⟨ae_strongly_measurable_of_ae_strongly_measurable_trim hm hf_meas_ae, _⟩,
  rw has_finite_integral at hf ⊢,
  rwa lintegral_trim_ae hm _ at hf,
  exact ae_strongly_measurable.ennnorm hf_meas_ae
end

end trim

section sigma_finite

variables {E : Type*} {m0 : measurable_space α} [normed_group E]

lemma integrable_of_forall_fin_meas_le' {μ : measure α} (hm : m ≤ m0) [sigma_finite (μ.trim hm)]
  (C : ℝ≥0∞) (hC : C < ∞) {f : α → E} (hf_meas : ae_strongly_measurable f μ)
  (hf : ∀ s, measurable_set[m] s → μ s ≠ ∞ → ∫⁻ x in s, nnnorm (f x) ∂μ ≤ C) :
  integrable f μ :=
⟨hf_meas, (lintegral_le_of_forall_fin_meas_le' hm C hf_meas.ennnorm hf).trans_lt hC⟩

lemma integrable_of_forall_fin_meas_le [sigma_finite μ]
  (C : ℝ≥0∞) (hC : C < ∞) {f : α → E} (hf_meas : ae_strongly_measurable f μ)
  (hf : ∀ s : set α, measurable_set s → μ s ≠ ∞ → ∫⁻ x in s, nnnorm (f x) ∂μ ≤ C) :
  integrable f μ :=
@integrable_of_forall_fin_meas_le' _ _ _ _ _ _ _ (by rwa trim_eq_self) C hC _ hf_meas hf

end sigma_finite

/-! ### The predicate `integrable` on measurable functions modulo a.e.-equality -/

namespace ae_eq_fun

section

/-- A class of almost everywhere equal functions is `integrable` if its function representative
is integrable. -/
def integrable (f : α →ₘ[μ] β) : Prop := integrable f μ

lemma integrable_mk {f : α → β} (hf : ae_strongly_measurable f μ ) :
  (integrable (mk f hf : α →ₘ[μ] β)) ↔ measure_theory.integrable f μ :=
begin
  simp [integrable],
  apply integrable_congr,
  exact coe_fn_mk f hf
end

lemma integrable_coe_fn {f : α →ₘ[μ] β} : (measure_theory.integrable f μ) ↔ integrable f :=
by rw [← integrable_mk, mk_coe_fn]

lemma integrable_zero : integrable (0 : α →ₘ[μ] β) :=
(integrable_zero α β μ).congr (coe_fn_mk _ _).symm

end

section

lemma integrable.neg {f : α →ₘ[μ] β} : integrable f → integrable (-f) :=
induction_on f $ λ f hfm hfi, (integrable_mk _).2 ((integrable_mk hfm).1 hfi).neg

section

lemma integrable_iff_mem_L1 {f : α →ₘ[μ] β} : integrable f ↔ f ∈ (α →₁[μ] β) :=
by rw [← integrable_coe_fn, ← mem_ℒp_one_iff_integrable, Lp.mem_Lp_iff_mem_ℒp]

lemma integrable.add {f g : α →ₘ[μ] β} : integrable f → integrable g → integrable (f + g) :=
begin
  refine induction_on₂ f g (λ f hf g hg hfi hgi, _),
  simp only [integrable_mk, mk_add_mk] at hfi hgi ⊢,
  exact hfi.add hgi
end

lemma integrable.sub {f g : α →ₘ[μ] β} (hf : integrable f) (hg : integrable g) :
  integrable (f - g) :=
(sub_eq_add_neg f g).symm ▸ hf.add hg.neg

end

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

lemma integrable.smul {c : 𝕜} {f : α →ₘ[μ] β} : integrable f → integrable (c • f) :=
induction_on f $ λ f hfm hfi, (integrable_mk _).2 $ ((integrable_mk hfm).1 hfi).smul _

end normed_space

end

end ae_eq_fun

namespace L1

lemma integrable_coe_fn (f : α →₁[μ] β) :
  integrable f μ :=
by { rw ← mem_ℒp_one_iff_integrable, exact Lp.mem_ℒp f }

lemma has_finite_integral_coe_fn (f : α →₁[μ] β) :
  has_finite_integral f μ :=
(integrable_coe_fn f).has_finite_integral

lemma strongly_measurable_coe_fn (f : α →₁[μ] β) : strongly_measurable f :=
Lp.strongly_measurable f

lemma measurable_coe_fn [measurable_space β] [borel_space β] (f : α →₁[μ] β) :
  measurable f :=
(Lp.strongly_measurable f).measurable

lemma ae_strongly_measurable_coe_fn (f : α →₁[μ] β) : ae_strongly_measurable f μ :=
Lp.ae_strongly_measurable f

lemma ae_measurable_coe_fn [measurable_space β] [borel_space β] (f : α →₁[μ] β) :
  ae_measurable f μ :=
(Lp.strongly_measurable f).measurable.ae_measurable

lemma edist_def (f g : α →₁[μ] β) :
  edist f g = ∫⁻ a, edist (f a) (g a) ∂μ :=
by { simp [Lp.edist_def, snorm, snorm'], simp [edist_eq_coe_nnnorm_sub] }

lemma dist_def (f g : α →₁[μ] β) :
  dist f g = (∫⁻ a, edist (f a) (g a) ∂μ).to_real :=
by { simp [Lp.dist_def, snorm, snorm'], simp [edist_eq_coe_nnnorm_sub] }

lemma norm_def (f : α →₁[μ] β) :
  ∥f∥ = (∫⁻ a, nnnorm (f a) ∂μ).to_real :=
by { simp [Lp.norm_def, snorm, snorm'] }

/-- Computing the norm of a difference between two L¹-functions. Note that this is not a
  special case of `norm_def` since `(f - g) x` and `f x - g x` are not equal
  (but only a.e.-equal). -/
lemma norm_sub_eq_lintegral (f g : α →₁[μ] β) :
  ∥f - g∥ = (∫⁻ x, (nnnorm (f x - g x) : ℝ≥0∞) ∂μ).to_real :=
begin
  rw [norm_def],
  congr' 1,
  rw lintegral_congr_ae,
  filter_upwards [Lp.coe_fn_sub f g] with _ ha,
  simp only [ha, pi.sub_apply],
end

lemma of_real_norm_eq_lintegral (f : α →₁[μ] β) :
  ennreal.of_real ∥f∥ = ∫⁻ x, (nnnorm (f x) : ℝ≥0∞) ∂μ :=
by { rw [norm_def, ennreal.of_real_to_real], exact ne_of_lt (has_finite_integral_coe_fn f) }

/-- Computing the norm of a difference between two L¹-functions. Note that this is not a
  special case of `of_real_norm_eq_lintegral` since `(f - g) x` and `f x - g x` are not equal
  (but only a.e.-equal). -/
lemma of_real_norm_sub_eq_lintegral (f g : α →₁[μ] β) :
  ennreal.of_real ∥f - g∥ = ∫⁻ x, (nnnorm (f x - g x) : ℝ≥0∞) ∂μ :=
begin
  simp_rw [of_real_norm_eq_lintegral, ← edist_eq_coe_nnnorm],
  apply lintegral_congr_ae,
  filter_upwards [Lp.coe_fn_sub f g] with _ ha,
  simp only [ha, pi.sub_apply],
end

end L1

namespace integrable

/-- Construct the equivalence class `[f]` of an integrable function `f`, as a member of the
space `L1 β 1 μ`. -/
def to_L1 (f : α → β) (hf : integrable f μ) : α →₁[μ] β :=
(mem_ℒp_one_iff_integrable.2 hf).to_Lp f

@[simp] lemma to_L1_coe_fn (f : α →₁[μ] β) (hf : integrable f μ) : hf.to_L1 f = f :=
by simp [integrable.to_L1]

lemma coe_fn_to_L1 {f : α → β} (hf : integrable f μ) : hf.to_L1 f =ᵐ[μ] f :=
ae_eq_fun.coe_fn_mk _ _

@[simp] lemma to_L1_zero (h : integrable (0 : α → β) μ) : h.to_L1 0 = 0 := rfl

@[simp] lemma to_L1_eq_mk (f : α → β) (hf : integrable f μ) :
  (hf.to_L1 f : α →ₘ[μ] β) = ae_eq_fun.mk f hf.ae_strongly_measurable :=
rfl

@[simp] lemma to_L1_eq_to_L1_iff (f g : α → β) (hf : integrable f μ) (hg : integrable g μ) :
  to_L1 f hf = to_L1 g hg ↔ f =ᵐ[μ] g :=
mem_ℒp.to_Lp_eq_to_Lp_iff _ _

lemma to_L1_add (f g : α → β) (hf : integrable f μ) (hg : integrable g μ) :
  to_L1 (f + g) (hf.add hg) = to_L1 f hf + to_L1 g hg := rfl

lemma to_L1_neg (f : α → β) (hf : integrable f μ) :
  to_L1 (- f) (integrable.neg hf) = - to_L1 f hf := rfl

lemma to_L1_sub (f g : α → β) (hf : integrable f μ) (hg : integrable g μ) :
  to_L1 (f - g) (hf.sub hg) = to_L1 f hf - to_L1 g hg := rfl

lemma norm_to_L1 (f : α → β) (hf : integrable f μ) :
  ∥hf.to_L1 f∥ = ennreal.to_real (∫⁻ a, edist (f a) 0 ∂μ) :=
by { simp [to_L1, snorm, snorm'], simp [edist_eq_coe_nnnorm] }

lemma norm_to_L1_eq_lintegral_norm (f : α → β) (hf : integrable f μ) :
  ∥hf.to_L1 f∥ = ennreal.to_real (∫⁻ a, (ennreal.of_real ∥f a∥) ∂μ) :=
by { rw [norm_to_L1, lintegral_norm_eq_lintegral_edist] }

@[simp] lemma edist_to_L1_to_L1 (f g : α → β) (hf : integrable f μ) (hg : integrable g μ) :
  edist (hf.to_L1 f) (hg.to_L1 g) = ∫⁻ a, edist (f a) (g a) ∂μ :=
by { simp [integrable.to_L1, snorm, snorm'], simp [edist_eq_coe_nnnorm_sub] }

@[simp] lemma edist_to_L1_zero (f : α → β) (hf : integrable f μ) :
  edist (hf.to_L1 f) 0 = ∫⁻ a, edist (f a) 0 ∂μ :=
by { simp [integrable.to_L1, snorm, snorm'], simp [edist_eq_coe_nnnorm] }

variables {𝕜 : Type*} [normed_field 𝕜] [normed_space 𝕜 β]

lemma to_L1_smul (f : α → β) (hf : integrable f μ) (k : 𝕜) :
  to_L1 (λ a, k • f a) (hf.smul k) = k • to_L1 f hf := rfl

lemma to_L1_smul' (f : α → β) (hf : integrable f μ) (k : 𝕜) :
  to_L1 (k • f) (hf.smul k) = k • to_L1 f hf := rfl

end integrable

end measure_theory

open measure_theory

variables {E : Type*} [normed_group E]
          {𝕜 : Type*} [nondiscrete_normed_field 𝕜] [normed_space 𝕜 E]
          {H : Type*} [normed_group H] [normed_space 𝕜 H]

lemma measure_theory.integrable.apply_continuous_linear_map {φ : α → H →L[𝕜] E}
  (φ_int : integrable φ μ) (v : H) : integrable (λ a, φ a v) μ :=
(φ_int.norm.mul_const ∥v∥).mono' (φ_int.ae_strongly_measurable.apply_continuous_linear_map v)
  (eventually_of_forall $ λ a, (φ a).le_op_norm v)

lemma continuous_linear_map.integrable_comp {φ : α → H} (L : H →L[𝕜] E)
  (φ_int : integrable φ μ) : integrable (λ (a : α), L (φ a)) μ :=
((integrable.norm φ_int).const_mul ∥L∥).mono'
  (L.continuous.comp_ae_strongly_measurable φ_int.ae_strongly_measurable)
  (eventually_of_forall $ λ a, L.le_op_norm (φ a))


def g : List Nat → List Nat → Nat
| [],         y::ys => y
| [],         ys    => 0
| x1::x2::xs, ys    => g xs ys
| x::xs,      y::ys => g xs ys + y
| x::xs,      []    => g xs []

universes u v

inductive Imf {α : Type u} {β : Type v} (f : α → β) : β → Type (max u v)
| mk : (a : α) → Imf f (f a)

def h {α β} {f : α → β} : {b : β} → Imf f b → α
| _, Imf.mk a => a

theorem ex1 {α β} (f : α → β) (a : α) : h (Imf.mk (f := f) a) = a :=
rfl

def v₁ : Imf Nat.succ 1 :=
Imf.mk 0

def v₂ : Imf (fun x => 1 + x) 1 :=
Imf.mk 0

theorem ex2 : h v₁ = 0 :=
rfl

theorem ex3 : h v₂ = 0 :=
rfl

theorem ex4 {α} : {a b : α} → a = b → b = a
| _, _, rfl => rfl

theorem ex5 {α} : {a b : α} → a = b → b = a
| a, .(a), rfl => rfl

theorem ex6 {α} : {a b : α} → a = b → b = a
| a, .(a), Eq.refl .(a) => rfl

theorem ex7 {α} : {a b : α} → a = b → b = a
| .(a), a, Eq.refl .(a) => rfl


/-
Copyright (c) 2022 Pedro Sánchez Terraf. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Pedro Sánchez Terraf
-/
import set_theory.cardinal.continuum
import measure_theory.measurable_space_def
import ordinal_indices
import omega_1
import tactic.induction

/-!
# The Borel hierarchy

In this file we recursively define the classical Borel hierarchy of sets.
It is developed in purely set-theoretic terms, as a way to generate
$\sigma$-algebras of sets.

## Mathematical overview

For a second countable topological space $X$, the Borel hierarchy is defined
recursively as follows (viz., Sect. 11.B in [kechris1995]):
$$
\begin{align*}
  \mathbf{\Sigma}^0_1(X)     &= \{U \subseteq X : U \text { is open }\} \\
  \mathbf{\Pi}^0_{\xi}(X)    &= \{X \setminus Q : Q \in \mathbf{\Sigma}^0_{\xi}(X)\} \\
  \mathbf{\Sigma}^0_{\xi}(X) &= \left\{\textstyle\bigcup_n A_n : A_n \in
    \mathbf{\Pi}^0_{\xi_n}(X), \xi_n<\xi, n \in \mathbb{N}\right\}, \text { if } \xi>1,
\end{align*}
$$
where $\xi$ and $\xi_n$ are ordinals between $1$ and $\omega_1$.

This is a streamlined version of the notation for classes of *definable* subsets
of the space $X$ (aka, **pointclasses**): $\mathbf{\Pi}^0_1(X)$ are the closed sets;
$\mathbf{\Sigma}^0_2(X)$ are the $F_\sigma$ sets; $\mathbf{\Pi}^0_2(X)$, the $G_\delta$;
and so forth. This is especially useful for infinite indices $\xi$.

In this file, we provide an inductive definition of the above hierarchy of subsets
of an arbitrary type, without assuming a topology but using an extra parameter `s`
which is intended to be a countable base of a topology.

## Main definitions

- `sigma0_pi0_rec`: Recursive definition of the hierarchy, not intended for direct use.
- `sigma0`, `pi0` : The classical pointclasses, obtained from `sigma0_pi0_rec`
using its `bool` argument.

## Implementation notes

Traditionally, pointclasses $\Sigma^0_\alpha$ and $\Pi^0_\alpha$ are defined for positive
values of the ordinal $\alpha$. Here the definition is extended in such a way that
$\Sigma^0_0 = \emptyset$ (which results in more generality for some lemmas) and
$\Pi^0_0$ coincides with the set of generators.

## References

The definition of the hierarchy was extracted from [kechris1995].
-/

universe u

namespace pointclass

section sigma0_pi0

/-!
### Σ and Π pointclasses

This section includes basic definitions and API.
-/
open set

variables {α : Type u} (s : set (set α)) (i k : ordinal.{u})

/--
Simultaneous recursive definition of Σ⁰ᵢ and Π⁰ᵢ pointclasses by recursion
on ordinals (a variant of 11.B in Kechris, [Classical Descriptive Set Theory][kechris1995]).

The definition is encoded as a single inductive predicate, where the `bool` argument
determines if we are defining Σ⁰ᵢ (for `ff`) or Π⁰ᵢ (for `tt`). The parameter
`s : set (set α)` is the generating family.

The main difference with the classical definition is that this hierarchy starts
at level 0: Π⁰₀ coincides with `s`
augmented with `∅` and `univ` (intended to be a countable basis of a topology) and
Σ⁰₀ is the empty family.
-/
inductive sigma0_pi0_rec {α : Type u} (s : set (set α)) :
  ordinal.{u} → bool → set α → Prop
| basic (x ∈ s) : sigma0_pi0_rec 0 tt x
| empty         : sigma0_pi0_rec 0 tt ∅
| univ          : sigma0_pi0_rec 0 tt univ
| compl {i x}   : sigma0_pi0_rec i ff x → sigma0_pi0_rec i tt xᶜ
| union {i} (f : ℕ → set α) (g : ℕ → ordinal.{u}) :
    (∀ n, g n < i) → (∀ n, sigma0_pi0_rec (g n) tt (f n)) → sigma0_pi0_rec i ff (⋃ n, f n)

/--
The family of (boldface) Σ⁰ᵢ pointsets, which are countable unions of Π⁰ⱼ sets
(given by the function `pointclass.pi0` below) of smaller index. The parameter
`s : set (set α)` is the family of sets from which the generation begins.
-/
def sigma0 : set (set α) := sigma0_pi0_rec s i ff

/--
The family of (boldface) Π⁰ᵢ pointsets, which are the complements of Σ⁰ᵢ sets
(given by the function `pointclass.sigma0` above). The parameter
`s : set (set α)` is the family of sets from which the generation begins.

When the ordinal argument is `0`, `pi0` returns the generating family `s`
augmented with `∅` and `univ`.
-/
def pi0 : set (set α) := sigma0_pi0_rec s i tt

lemma sigma0_pi0_rec_def' {b : bool} :
  sigma0_pi0_rec s i b = if b then pi0 s i else sigma0 s i :=
by { cases b; refl }

/-- `sigma0` comprises unions of previous `pi0`. -/
lemma sigma0_eq_Union_pi0 :
  sigma0 s i = set.range (λ (f : ℕ → ⋃ j (hij : j < i), pi0 s j), ⋃ n, (f n).1) :=
begin
  ext x, split; intro hx,
  { induction' hx with _ _ _ _ _ _ i f g glt hf,
    existsi λ n, (⟨f n, _⟩ : ↥⋃ j < i, pi0 s j),
    { simp only [eq_self_iff_true] },
    { rw mem_Union,
      use g n,
      rw mem_Union,
      exact ⟨glt n, hf n⟩ } },
  { cases hx with f hf,
    rw ← hf,
    dsimp only at hf,
    choose g hg using λ n, (mem_Union.mp (f n).property),
    simp only [mem_Union, exists_prop] at hg,
    exact sigma0_pi0_rec.union _ g (λ n, (hg n).1) (λ n, (hg n).2) }
end

/-- Base case for `sigma0`. -/
@[simp] lemma sigma0_zero : sigma0 s 0 = ∅ :=
by simp [sigma0_eq_Union_pi0, ordinal.not_lt_zero]

lemma pi0_subset_sigma0 (hik : i < k) :
  pi0 s i ⊆ sigma0 s k :=
begin
  simp only [sigma0_eq_Union_pi0, hik],
  intros x hx,
  apply mem_range.mpr,
  have hxU : x ∈ ⋃ j < k, pi0 s j,
  { simp only [mem_Union, exists_prop],
    exact ⟨i, hik, hx⟩ },
  exact ⟨(λ n : ℕ, (⟨x, hxU⟩ : ⋃ (j < k), pi0 s j)), Union_const x⟩
end

/-- `pi0` comprises complements of `sigma0` at the same level. -/
lemma pi0_eq_compl_sigma0 (hi : i ≠ 0) :
  pi0 s i = compl '' sigma0 s i :=
begin
  ext x, split; intro hx; induction' hx with hcomp IH c d IH,
  any_goals { contradiction },
  { simp only [mem_image], use d, tauto },
  { have := sigma0_pi0_rec.compl IH.1, rwa ← IH.2 }
end

/-- Base case for `pi0`. -/
lemma pi0_zero :
  pi0 s 0 = s ∪ {∅, univ} :=
begin
  ext x, simp only [mem_insert_iff, union_insert, union_singleton],
  split; intro hx,
  { induction' hx with _ hx _ v hv,
    any_goals { tauto },
    simp only [sigma0_pi0_rec_def'] at hv,
    have : sigma0 s 0 v,
    { exact hv },
    exfalso,
    rw sigma0_zero at this,
    exact this },
  { rcases hx with rfl | rfl | x_in_s,
    exacts [sigma0_pi0_rec.empty, sigma0_pi0_rec.univ, sigma0_pi0_rec.basic x x_in_s] }
end

lemma sigma0_one :
  sigma0 s 1 = set.range (λ (f : ℕ → s ∪ {∅, univ}), ⋃ n, (f n).1) :=
begin
  change sigma0_pi0_rec s 1 ff = range (λ (f : ℕ → ↥(s ∪ {∅, univ})), ⋃ (n : ℕ), ↑(f n)),
  ext z,
  change sigma0_pi0_rec s 1 ff z ↔ ∃ (y : ℕ → ↥(s ∪ {∅, univ})), (⋃ (n : ℕ), ↑(y n)) = z,
  refine ⟨λ h, _, λ h, _⟩,
  { -- Subgoal solved by Junyan Xu.
    induction' h with _ _ _ _ _ _ _ f g glt1 IH f_Union,
    simp_rw ordinal.lt_one_iff_zero at glt1,
    simp_rw glt1 at IH,
    change ∀ n, f n ∈ pi0 s 0 at IH,
    simp_rw pi0_zero at IH,
    exact ⟨λ n, ⟨f n, IH n⟩, rfl⟩ },
  { rcases h with ⟨f, rfl⟩,
    apply sigma0_pi0_rec.union (λ n, (f n).val) (λ n, 0),
    { simp },
    { change ∀ (n : ℕ), sigma0_pi0_rec s 0 tt ↑(f n),
      intro n,
      have hfn := (f n).property,
      simp only [subtype.val_eq_coe, mem_insert_iff, union_insert, union_singleton] at hfn,
      rcases hfn with nul | uni | bas,
      { rw nul, exact sigma0_pi0_rec.empty },
      { rw uni, exact sigma0_pi0_rec.univ },
      { exact sigma0_pi0_rec.basic (f n) bas } } }
end

/-- `sigma0` is monotone. -/
lemma sigma0_subset_sigma0 (hik : i ≤ k) :
  sigma0 s i ⊆ sigma0 s k :=
begin
  rw le_iff_lt_or_eq at hik,
  cases hik,
  -- Take care of the trivial case `i = k` first,
  swap,
  { simp only [hik, subset_rfl] },
  -- Now the interesting `i < k`:
  repeat { rw sigma0_eq_Union_pi0 },
  intros x hx,
  cases hx with f hf,
  simp only at hf,
  have hfUn : ∀ (n : ℕ), ↑(f n) ∈ (⋃ j < k, pi0 s j),
  { intro n,
    apply mem_Union.mpr,
    -- Awful proof ahead :(
    have : ∃ j (hjk : j < k), (f n).val ∈ pi0 s j :=
      (let (Exists.intro j q) := (mem_Union.mp (f n).property) in
        let (Exists.intro l r) := mem_Union.mp q in
        Exists.intro j (Exists.intro (trans l hik) r)),
    cases this with j hj,
    exact ⟨j, mem_Union.mpr hj⟩ },
  apply mem_range.mpr,
  existsi (λ n : ℕ, (⟨f n, hfUn n⟩ : ⋃ j < k, (pi0 s j))),
  tauto,
end

/--
The sequence of `pi0` families is nondecreasing.

The hypothesis `i ≠ 0` is required in case elements of the generating set are
not closed. If the underlying space is zero-dimensional, one can take a basis
of clopen sets and the inclusion will hold unconditionally.
-/
lemma pi0_subset_pi0 (hi : i ≠ 0) (hik : i ≤ k) :
  pi0 s i ⊆ pi0 s k :=
begin
  rw [pi0_eq_compl_sigma0, pi0_eq_compl_sigma0],
  exacts [image_subset _ (sigma0_subset_sigma0 s i k hik),
    ordinal.one_le_iff_ne_zero.mp (trans (ordinal.one_le_iff_ne_zero.mpr hi) hik),
    hi]
end

/-- `sigma0` is closed under countable unions. -/
lemma Union_of_sigma0_sequence {g : ℕ → sigma0 s i} :
  (⋃ n, (g n).val) ∈ sigma0 s i :=
begin
  have hg : ∀ n : ℕ, (g n).val ∈ sigma0 s i := λ n, (g n).property,
  simp only [subtype.val_eq_coe, sigma0_eq_Union_pi0] at *,
  choose o ho using hg,
  have : ℕ × ℕ ≃ ℕ,
  { exact denumerable.eqv (ℕ × ℕ) },
  cases this with tup untup htup huntup,
  use λ n, let p := (untup n) in o p.1 p.2,
  ext x, split; intro hx; simp only [mem_Union] at hx ⊢;
    cases hx with j hxin,
  { let n := (untup j).fst,
    use n,
    rw [← ho n, mem_Union],
    exact ⟨(untup j).snd, hxin⟩ },
  { simp only [mem_Union, ← ho j] at hxin,
    cases hxin with k hk,
    existsi tup ⟨j, k⟩,
    have fstj : (untup (tup ⟨j, k⟩)).fst = j,
    { exact (congr_arg prod.fst (htup (j, k))).trans rfl },
    have sndk : (untup (tup ⟨j, k⟩)).snd = k,
    { exact (congr_arg prod.snd (htup (j, k))).trans rfl },
    rw [fstj, sndk],
    exact hk }
end

/-- Variant of `pointclass.Union_of_sigma0_sequence`. -/
lemma Union_of_mem_sigma0 {f : ℕ → set α} (hf : ∀ n, f n ∈ sigma0 s i) :
  (⋃ n, f n) ∈ sigma0 s i :=
by exact @Union_of_sigma0_sequence _ s i (λ n, {val := f n, property := hf n} : ℕ → sigma0 s i)

lemma self_subset_sigma0 (hi : i ≠ 0) :
  s ⊆ sigma0 s i :=
begin
  calc
  s   ⊆ s ∪ {∅, univ} : subset_union_left _ _
  ... = pi0 s 0      : eq.symm (pi0_zero s)
  ... ⊆ sigma0 s i   : pi0_subset_sigma0 s 0 i (ordinal.pos_iff_ne_zero.mpr hi),
end

theorem empty_mem_sigma0 (hi : i ≠ 0) :
  ∅ ∈ sigma0 s i :=
begin
  have : ∅ ∈ s ∪ {∅, univ} := by { apply mem_union_right, simp },
  have that : s ∪ {∅, univ} = pi0 s 0 := eq.symm (pi0_zero s),
  rw that at this,
  calc
  ∅   ∈ pi0 s 0      : this
  ... ⊆ sigma0 s i   : pi0_subset_sigma0 s 0 i (ordinal.pos_iff_ne_zero.mpr hi)
end

end sigma0_pi0

section gen_measurable
/-!
### Generated σ-algebra by recursion
-/
variables {α : Type u} (s : set (set α)) (i k : ordinal.{u})

open set ordinal cardinal
open_locale ordinal

/--
The σ-algebra recursively generated by the family `s`. It is obtained at the
`ω₁`th level of the `sigma0` hierarchy.
-/
def gen_measurable := sigma0 s ω₁

lemma gen_measurable_eq_Union_sigma0 :
  gen_measurable s = ⋃ (j < ω₁), sigma0 s j :=
begin
  unfold gen_measurable,
  apply subset_antisymm,
  { rw sigma0_eq_Union_pi0,
    intros x hx,
    simp only [mem_Union, exists_prop] at *,
    cases hx with f hf,
    let g := λ n, (f n).property,
    simp only [mem_Union, exists_prop] at g,
    choose o ho using g,
    use order.succ(sup o),
    split,
    { exact is_limit.succ_lt is_limit_omega_1 (sup_sequence_lt_omega_1 o (λ n, (ho n).left)) },
    rw sigma0_eq_Union_pi0,
    rw mem_range,
    have typf : ∀ n, ↑(f n) ∈ ⋃ (j < order.succ (sup o)), pi0 s j,
    { intro n, apply mem_Union.2,
      specialize ho n,
      use o n,
      exact mem_Union.2 ⟨lt_of_le_of_lt (le_sup o n) (order.lt_succ (sup o)), ho.2⟩ },
    use λ n, (⟨f n, typf n⟩ : ⋃ (j < order.succ (sup o)), pi0 s j),
    tauto },
  { simp only [Union_subset_iff],
    exact λ _ hi, sigma0_subset_sigma0 s _ _ (le_of_lt hi) }
end

theorem compl_mem_gen_measurable (t : set α) (ht : t ∈ gen_measurable s) :
  tᶜ ∈ gen_measurable s :=
begin
  rw gen_measurable_eq_Union_sigma0 at ht,
  simp only [mem_Union, exists_prop] at ht,
  cases ht with o ho,
  rcases classical.em (o=0) with rfl | onon,
  { finish },
  calc
  tᶜ  ∈ pi0 s o          : by { rw pi0_eq_compl_sigma0,
    simp only [mem_image, compl_inj_iff, exists_eq_right], exacts [ho.2, onon] }
  ... ⊆ gen_measurable s : pi0_subset_sigma0 s o ω₁ ho.1,
end

theorem Union_mem_gen_measurable {f : ℕ → set α} (hf : ∀ n, f n ∈ gen_measurable s) :
  (⋃ n, f n) ∈ gen_measurable s := Union_of_mem_sigma0 s ω₁ hf

open measurable_space

lemma generate_measurable_of_mem_sigma0 (t) (ht : t ∈ sigma0 s i) :
  generate_measurable s t :=
begin
  induction i using ordinal.induction with i IH generalizing t,
  rw [sigma0_eq_Union_pi0, mem_range] at ht,
  rcases ht with ⟨f, hf⟩,
  have typf : ∀ n : ℕ, generate_measurable s (f n),
  { intro n,
    have fn_in : (f n).val ∈ ⋃ j < i, pi0 s j := (f n).property,
    simp only [subtype.val_eq_coe, mem_Union, exists_prop] at fn_in,
    rcases fn_in with ⟨o, ⟨o_lt_i, fn_in⟩⟩,
    rcases classical.em (o=0) with rfl | honz,
    { -- Case `(f n).val ∈ pi0 s 0`.
      rw pi0_zero at fn_in,
      rcases fn_in with  fn_in | fn_emp | fn_in,
      { exact generate_measurable.basic _ fn_in },
      { rw fn_emp, exact generate_measurable.empty },
      { rw mem_singleton_iff at fn_in, rw [fn_in, ←compl_empty],
        exact generate_measurable.compl _ generate_measurable.empty } },
    { -- Case `(f n).val ∈ pi0 s o` with `o ≠ 0`.
      simp only at IH,
      rw pi0_eq_compl_sigma0 s o honz at fn_in,
      cases fn_in with x hx,
      rw ← compl_compl ↑(f n),
      apply generate_measurable.compl,
      rw [←hx.2, compl_compl],
      exact IH o o_lt_i x hx.1 } },
  rw ← hf,
  exact generate_measurable.union (λ n, (f n).val) typf
end

/-- `gen_measurable s` generates precisely the smallest σ-algebra containing `s`. -/
theorem generate_measurable_eq_gen_measurable :
  {t | generate_measurable s t} = gen_measurable s :=
begin
  ext t, refine ⟨λ ht, _, λ ht, _⟩,
  { have om1_nonz : ω₁ ≠ 0,
    { unfold omega_1, exact ne_zero_of_out_nonempty _ },
    induction ht with u hu u hu IH f hf IH,
    exacts
      [self_subset_sigma0 s ω₁ om1_nonz hu,
      empty_mem_sigma0 s ω₁ om1_nonz,
      compl_mem_gen_measurable s u IH,
      Union_mem_gen_measurable s IH] },
  { exact generate_measurable_of_mem_sigma0 s ω₁ t ht }
end

end gen_measurable

/-
section card_gen_measurable

/-!
### Cardinality of sigma-algebras
-/

variables {α : Type u} (s : set (set α)) (i k : ordinal.{u})

open set measurable_space cardinal ordinal
open_locale cardinal ordinal

/- The result holds for arbitrary `i`, but it is easier to prove
this way -/
lemma cardinal_sigma0_le (hi : i ≤ ω₁):
  #(sigma0 s i) ≤ (max (#s) 2) ^ aleph_0.{u} :=
begin
  induction i using ordinal.induction with i IH,
  have Upi0sub : (⋃ j < i, pi0 s j) ⊆ s ∪ {∅, univ} ∪ ⋃ j < i, compl '' sigma0 s j,
  { simp only [mem_singleton_iff,union_insert,union_singleton,mem_insert_iff,Union_subset_iff],
    intros j hj x hx,
    rcases classical.em (j=0) with rfl | hjnz,
    { simp only [mem_singleton_iff,union_insert,union_singleton,mem_insert_iff,pi0_zero] at hx,
      exact mem_union_left _ hx },
    rw pi0_eq_compl_sigma0 s j hjnz at hx,
    apply mem_union_right _ (mem_Union.mpr _),
    use j,
    apply mem_Union.mpr,
    use hj,
    exact hx },
  have cardcompl : ∀ j, #(sigma0 s j) = #(compl '' sigma0 s j) :=
    λ j, cardinal.eq.mpr (⟨equiv.set.image _ _  compl_injective⟩),
  have A := aleph_0_le_aleph 1,
  have B : aleph 1 ≤ (max (#s) 2) ^ aleph_0.{u} :=
    aleph_one_le_continuum.trans (power_le_power_right (le_max_right _ _)),
  have C : ℵ₀ ≤ (max (#s) 2) ^ aleph_0.{u} := A.trans B,
  have L : #(↥(s ∪ {∅, univ})) ≤ (max (#s) 2) ^ aleph_0.{u},
  { apply_rules [(mk_union_le _ _).trans, add_le_of_le C, mk_image_le.trans],
    { exact (le_max_left _ _).trans (self_le_power _ one_lt_aleph_0.le) },
    repeat { simp only [mk_fintype,fintype.card_unique,nat.cast_one,mk_singleton],
      exact one_lt_aleph_0.le.trans C } },
  have K : #(↥⋃ j < i, compl '' sigma0 s j) ≤ (max (#s) 2) ^ aleph_0.{u},
  { apply mk_Union_ordinal_le_of_le,
    exact (hi.trans $ ord_le_ord.mpr B),
    exact C,
    intros j hj,
    rw ← cardcompl,
    apply IH j hj,
    exact (le_of_lt $ lt_of_lt_of_le hj hi) },
  have J : #(↥(s ∪ {∅, univ} ∪ ⋃ j < i, compl '' sigma0 s j)) ≤ (max (#s) 2) ^ aleph_0.{u},
    { calc
      #(↥(s ∪ {∅, univ} ∪ ⋃ j < i, compl '' sigma0 s j)) ≤
        #(↥(s ∪ {∅, univ})) + #(↥⋃ j < i, compl '' sigma0 s j) : mk_union_le _ _
      ... ≤ (max (#s) 2) ^ aleph_0.{u} + (max (#s) 2) ^ aleph_0.{u} :
        (add_le_add (le_refl _) K).trans (add_le_add L (le_refl _))
      ... = (max (#s) 2) ^ aleph_0.{u} :
        (add_eq_max C).trans (max_eq_right (le_refl _)) },
  -- The main calculation:
  calc
  #↥(sigma0 s i) =
    #↥(range (λ (f : ℕ → (↥⋃ j < i, pi0 s j)), ⋃ n, ↑(f n))) :
    by { rw sigma0_eq_Union_pi0, simp }
  ... ≤ #(ℕ → (↥⋃ j < i, pi0 s j))                  : mk_range_le
  ... = prod (λ n : ℕ, #(↥⋃ j < i, pi0 s j))        : mk_pi _
  ... = #(↥⋃ j < i, pi0 s j) ^ aleph_0.{u}          : by { simp [prod_const] }
  ... ≤ #(↥(s ∪ {∅, univ} ∪ ⋃ j < i, compl '' sigma0 s j)) ^ aleph_0.{u} :
    power_le_power_right (mk_le_mk_of_subset Upi0sub)
  ... ≤ (max (# ↥s) 2 ^ aleph_0.{u}) ^ aleph_0.{u}  : power_le_power_right J
  ... ≤ (max (# ↥s) 2 ^ aleph_0.{u})                :
    by { rwa [← power_mul, aleph_0_mul_aleph_0] }
end

theorem cardinal_gen_measurable_le :
  #(gen_measurable s) ≤ (max (#s) 2) ^ aleph_0.{u} := cardinal_sigma0_le _ _ (le_refl _)

theorem cardinal_generate_measurable_le :
  #{t | generate_measurable s t} ≤ (max (#s) 2) ^ aleph_0.{u} :=
begin
  rw generate_measurable_eq_gen_measurable,
  exact cardinal_gen_measurable_le s,
end

theorem cardinal_measurable_set_le' :
  #{t | @measurable_set α (generate_from s) t} ≤ (max (#s) 2) ^ aleph_0.{u} :=
cardinal_generate_measurable_le _

theorem cardinal_generate_measurable_le_continuum (hs : #s ≤ 𝔠) :
  #{t | generate_measurable s t} ≤ 𝔠 :=
(cardinal_generate_measurable_le _).trans begin
  rw ←continuum_power_aleph_0,
  exact_mod_cast power_le_power_right (max_le hs (nat_lt_continuum 2).le)
end

theorem cardinal_measurable_set_le_continuum :
  #s ≤ 𝔠 → #{t | @measurable_set α (generate_from s) t} ≤ 𝔠 :=
cardinal_generate_measurable_le_continuum _

end card_gen_measurable
-/

end pointclass