/-
Copyright (c) 2015 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Jakob von Raumer

Squares in a type
-/
import hott.types.eq

universes u v w
hott_theory

namespace hott
open eq hott.equiv hott.is_equiv

namespace eq

  variables {A : Type _} {B : Type _} {C : Type _}
       {a a' a'' a₀₀ a₂₀ a₄₀ a₀₂ a₂₂ a₂₄ a₀₄ a₄₂ a₄₄ a₁ a₂ a₃ a₄ : A}
            /-a₀₀-/ {p₁₀ p₁₀' : a₀₀ = a₂₀} /-a₂₀-/ {p₃₀ : a₂₀ = a₄₀} /-a₄₀-/
       {p₀₁ p₀₁' : a₀₀ = a₀₂} /-s₁₁-/ {p₂₁ p₂₁' : a₂₀ = a₂₂} /-s₃₁-/ {p₄₁ : a₄₀ = a₄₂}
            /-a₀₂-/ {p₁₂ p₁₂' : a₀₂ = a₂₂} /-a₂₂-/ {p₃₂ : a₂₂ = a₄₂} /-a₄₂-/
       {p₀₃ : a₀₂ = a₀₄} /-s₁₃-/ {p₂₃ : a₂₂ = a₂₄} /-s₃₃-/ {p₄₃ : a₄₂ = a₄₄}
            /-a₀₄-/ {p₁₄ : a₀₄ = a₂₄} /-a₂₄-/ {p₃₄ : a₂₄ = a₄₄} /-a₄₄-/
            {b : B} {c : C}

  inductive square {A : Type u} {a₀₀ : A}
    : Π{a₂₀ a₀₂ a₂₂ : A}, a₀₀ = a₂₀ → a₀₂ = a₂₂ → a₀₀ = a₀₂ → a₂₀ = a₂₂ → Type u
  | ids : square idp idp idp idp
  /- square top bottom left right -/

  variables {s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁} {s₃₁ : square p₃₀ p₃₂ p₂₁ p₄₁}
            {s₁₃ : square p₁₂ p₁₄ p₀₃ p₂₃} {s₃₃ : square p₃₂ p₃₄ p₂₃ p₄₃}

  @[hott, reducible] def ids              := @square.ids
  @[hott, reducible] def idsquare (a : A) := @square.ids A a

  @[hott] def hrefl (p : a = a') : square idp idp p p :=
  by induction p; exact ids

  @[hott] def vrefl (p : a = a') : square p p idp idp :=
  by induction p; exact ids

  @[hott, reducible, hsimp] def hrfl {p : a = a'} : square idp idp p p := hrefl _
  @[hott, reducible, hsimp] def vrfl {p : a = a'} : square p p idp idp := vrefl _

  @[hott] def hdeg_square {p q : a = a'} (r : p = q) : square idp idp p q :=
  by induction r;apply hrefl

  @[hott] def vdeg_square {p q : a = a'} (r : p = q) : square p q idp idp :=
  by induction r;apply vrefl

  @[hott, hsimp] def hdeg_square_idp (p : a = a') : hdeg_square (refl p) = hrfl :=
  idp

  @[hott, hsimp] def vdeg_square_idp (p : a = a') : vdeg_square (refl p) = vrfl :=
  idp

  @[hott] def hconcat (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) (s₃₁ : square p₃₀ p₃₂ p₂₁ p₄₁)
    : square (p₁₀ ⬝ p₃₀) (p₁₂ ⬝ p₃₂) p₀₁ p₄₁ :=
  by induction s₃₁; exact s₁₁

  @[hott] def vconcat (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) (s₁₃ : square p₁₂ p₁₄ p₀₃ p₂₃)
    : square p₁₀ p₁₄ (p₀₁ ⬝ p₀₃) (p₂₁ ⬝ p₂₃) :=
  by induction s₁₃; exact s₁₁

  @[hott] def dconcat {p₀₀ : a₀₀ = a} {p₂₂ : a = a₂₂}
    (s₂₁ : square p₀₀ p₁₂ p₀₁ p₂₂) (s₁₂ : square p₁₀ p₂₂ p₀₀ p₂₁) : square p₁₀ p₁₂ p₀₁ p₂₁ :=
  by induction s₁₂; exact s₂₁

  @[hott] def hinverse (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) : square p₁₀⁻¹ p₁₂⁻¹ p₂₁ p₀₁ :=
  by induction s₁₁;exact ids

  @[hott] def vinverse (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) : square p₁₂ p₁₀ p₀₁⁻¹ p₂₁⁻¹ :=
  by induction s₁₁;exact ids

  @[hott] def eq_vconcat {p : a₀₀ = a₂₀} (r : p = p₁₀) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) :
    square p p₁₂ p₀₁ p₂₁ :=
  by induction r; exact s₁₁

  @[hott] def vconcat_eq {p : a₀₂ = a₂₂} (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) (r : p₁₂ = p) :
    square p₁₀ p p₀₁ p₂₁ :=
  by induction r; exact s₁₁

  @[hott] def eq_hconcat {p : a₀₀ = a₀₂} (r : p = p₀₁) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) :
    square p₁₀ p₁₂ p p₂₁ :=
  by induction r; exact s₁₁

  @[hott] def hconcat_eq {p : a₂₀ = a₂₂} (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) (r : p₂₁ = p) :
    square p₁₀ p₁₂ p₀₁ p :=
  by induction r; exact s₁₁

  infix ` ⬝h `:69 := hconcat --type using \tr
  infix ` ⬝v `:70 := vconcat --type using \tr
  infixl ` ⬝hp `:71 := hconcat_eq --type using \tr
  infixl ` ⬝vp `:73 := vconcat_eq --type using \tr
  infixr ` ⬝ph `:72 := eq_hconcat --type using \tr
  infixr ` ⬝pv `:74 := eq_vconcat --type using \tr
  postfix `⁻¹ʰ`:(max+1) := hinverse --type using \-1h
  postfix `⁻¹ᵛ`:(max+1) := vinverse --type using \-1v

  @[hott] def transpose (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) : square p₀₁ p₂₁ p₁₀ p₁₂ :=
  by induction s₁₁;exact ids

  @[hott, hsimp] def transpose_hrefl (p : a = a') : transpose (hrefl p) = vrefl p :=
  by induction p; refl

  @[hott, hsimp] def transpose_vrefl (p : a = a') : transpose (vrefl p) = hrefl p :=
  by induction p; refl

  @[hott] def aps (f : A → B) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : square (ap f p₁₀) (ap f p₁₂) (ap f p₀₁) (ap f p₂₁) :=
  by induction s₁₁;exact ids

  /- canceling, whiskering and moving thinks along the sides of the square -/
  @[hott] def whisker_tl (p : a = a₀₀) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : square (p ⬝ p₁₀) p₁₂ (p ⬝ p₀₁) p₂₁ :=
  by induction s₁₁;induction p;constructor

  @[hott] def whisker_br (p : a₂₂ = a) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : square p₁₀ (p₁₂ ⬝ p) p₀₁ (p₂₁ ⬝ p) :=
  by induction p;exact s₁₁

  @[hott] def whisker_rt (p : a = a₂₀) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : square (p₁₀ ⬝ p⁻¹) p₁₂ p₀₁ (p ⬝ p₂₁) :=
  by induction s₁₁;induction p;constructor

  @[hott] def whisker_tr (p : a₂₀ = a) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : square (p₁₀ ⬝ p) p₁₂ p₀₁ (p⁻¹ ⬝ p₂₁) :=
  by induction s₁₁;induction p;constructor

  @[hott] def whisker_bl (p : a = a₀₂) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : square p₁₀ (p ⬝ p₁₂) (p₀₁ ⬝ p⁻¹) p₂₁ :=
  by induction s₁₁;induction p;constructor

  @[hott] def whisker_lb (p : a₀₂ = a) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : square p₁₀ (p⁻¹ ⬝ p₁₂) (p₀₁ ⬝ p) p₂₁ :=
  by induction s₁₁;induction p;constructor

  @[hott] def cancel_tl (p : a = a₀₀) (s₁₁ : square (p ⬝ p₁₀) p₁₂ (p ⬝ p₀₁) p₂₁)
    : square p₁₀ p₁₂ p₀₁ p₂₁ :=
  by induction p; hsimp at s₁₁; exact s₁₁

  @[hott] def cancel_br (p : a₂₂ = a) (s₁₁ : square p₁₀ (p₁₂ ⬝ p) p₀₁ (p₂₁ ⬝ p))
    : square p₁₀ p₁₂ p₀₁ p₂₁ :=
  by induction p;exact s₁₁

  @[hott] def cancel_rt (p : a = a₂₀) (s₁₁ : square (p₁₀ ⬝ p⁻¹) p₁₂ p₀₁ (p ⬝ p₂₁))
    : square p₁₀ p₁₂ p₀₁ p₂₁ :=
  by induction p; hsimp at s₁₁; exact s₁₁

  @[hott] def cancel_tr (p : a₂₀ = a) (s₁₁ : square (p₁₀ ⬝ p) p₁₂ p₀₁ (p⁻¹ ⬝ p₂₁))
    : square p₁₀ p₁₂ p₀₁ p₂₁ :=
  by induction p; hsimp at s₁₁; exact s₁₁

  @[hott] def cancel_bl (p : a = a₀₂) (s₁₁ : square p₁₀ (p ⬝ p₁₂) (p₀₁ ⬝ p⁻¹) p₂₁)
    : square p₁₀ p₁₂ p₀₁ p₂₁ :=
  by induction p; hsimp at s₁₁; exact s₁₁

  @[hott] def cancel_lb (p : a₀₂ = a) (s₁₁ : square p₁₀ (p⁻¹ ⬝ p₁₂) (p₀₁ ⬝ p) p₂₁)
    : square p₁₀ p₁₂ p₀₁ p₂₁ :=
  by induction p; hsimp at s₁₁; exact s₁₁

  @[hott] def move_top_of_left   {p : a₀₀ = a} {q : a = a₀₂} (s : square p₁₀ p₁₂ (p ⬝ q) p₂₁)
    : square (p⁻¹ ⬝ p₁₀) p₁₂ q p₂₁ :=
  by apply cancel_tl p; hsimp; exact s

  @[hott] def move_top_of_left'  {p : a = a₀₀} {q : a = a₀₂} (s : square p₁₀ p₁₂ (p⁻¹ ⬝ q) p₂₁)
    : square (p ⬝ p₁₀) p₁₂ q p₂₁ :=
  by apply cancel_tl p⁻¹; hsimp; exact s

  @[hott] def move_left_of_top   {p : a₀₀ = a} {q : a = a₂₀} (s : square (p ⬝ q) p₁₂ p₀₁ p₂₁)
    : square q p₁₂ (p⁻¹ ⬝ p₀₁) p₂₁ :=
  by apply cancel_tl p; hsimp; exact s

  @[hott] def move_left_of_top'  {p : a = a₀₀} {q : a = a₂₀} (s : square (p⁻¹ ⬝ q) p₁₂ p₀₁ p₂₁)
    : square q p₁₂ (p ⬝ p₀₁) p₂₁ :=
  by apply cancel_tl p⁻¹; hsimp; exact s

  @[hott] def move_bot_of_right  {p : a₂₀ = a} {q : a = a₂₂} (s : square p₁₀ p₁₂ p₀₁ (p ⬝ q))
    : square p₁₀ (p₁₂ ⬝ q⁻¹) p₀₁ p :=
  by apply cancel_br q; hsimp; exact s

  @[hott] def move_bot_of_right' {p : a₂₀ = a} {q : a₂₂ = a} (s : square p₁₀ p₁₂ p₀₁ (p ⬝ q⁻¹))
    : square p₁₀ (p₁₂ ⬝ q) p₀₁ p :=
  by apply cancel_br q⁻¹; hsimp; exact s

  @[hott] def move_right_of_bot  {p : a₀₂ = a} {q : a = a₂₂} (s : square p₁₀ (p ⬝ q) p₀₁ p₂₁)
    : square p₁₀ p p₀₁ (p₂₁ ⬝ q⁻¹) :=
  by apply cancel_br q; hsimp; exact s

  @[hott] def move_right_of_bot' {p : a₀₂ = a} {q : a₂₂ = a} (s : square p₁₀ (p ⬝ q⁻¹) p₀₁ p₂₁)
    : square p₁₀ p p₀₁ (p₂₁ ⬝ q) :=
  by apply cancel_br q⁻¹; hsimp; exact s

  @[hott] def move_top_of_right  {p : a₂₀ = a} {q : a = a₂₂} (s : square p₁₀ p₁₂ p₀₁ (p ⬝ q))
    : square (p₁₀ ⬝ p) p₁₂ p₀₁ q :=
  by apply cancel_rt p; hsimp; exact s

  @[hott] def move_right_of_top  {p : a₀₀ = a} {q : a = a₂₀} (s : square (p ⬝ q) p₁₂ p₀₁ p₂₁)
    : square p p₁₂ p₀₁ (q ⬝ p₂₁) :=
  by apply cancel_tr q; hsimp; exact s

  @[hott] def move_bot_of_left   {p : a₀₀ = a} {q : a = a₀₂} (s : square p₁₀ p₁₂ (p ⬝ q) p₂₁)
    : square  p₁₀ (q ⬝ p₁₂) p p₂₁ :=
  by apply cancel_lb q; hsimp; exact s

  @[hott] def move_left_of_bot   {p : a₀₂ = a} {q : a = a₂₂} (s : square p₁₀ (p ⬝ q) p₀₁ p₂₁)
    : square p₁₀ q (p₀₁ ⬝ p) p₂₁ :=
  by apply cancel_bl p; hsimp; exact s

  /- some higher ∞-groupoid operations -/

  @[hott] def vconcat_vrfl (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : s₁₁ ⬝v vrefl p₁₂ = s₁₁ :=
  by induction s₁₁; reflexivity

  @[hott] def hconcat_hrfl (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : s₁₁ ⬝h hrefl p₂₁ = s₁₁ :=
  by induction s₁₁; reflexivity

  /- equivalences -/

  @[hott] def eq_of_square (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) : p₁₀ ⬝ p₂₁ = p₀₁ ⬝ p₁₂ :=
  by induction s₁₁; refl

  @[hott] def square_of_eq (r : p₁₀ ⬝ p₂₁ = p₀₁ ⬝ p₁₂) : square p₁₀ p₁₂ p₀₁ p₂₁ :=
  by induction p₁₂; dsimp at r; induction r; induction p₂₁; induction p₁₀; exact ids

  @[hott] def eq_top_of_square (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : p₁₀ = p₀₁ ⬝ p₁₂ ⬝ p₂₁⁻¹ :=
  by induction s₁₁; refl

  @[hott] def square_of_eq_top (r : p₁₀ = p₀₁ ⬝ p₁₂ ⬝ p₂₁⁻¹) : square p₁₀ p₁₂ p₀₁ p₂₁ :=
  by induction p₂₁; induction p₁₂; dsimp at r;induction r;induction p₁₀;exact ids

  @[hott] def eq_bot_of_square (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : p₁₂ = p₀₁⁻¹ ⬝ p₁₀ ⬝ p₂₁ :=
  by induction s₁₁; refl

  @[hott, hsimp] def eq_bot_of_square_vrefl (p : a = a') : eq_bot_of_square (vrefl p) = (idp_con p)⁻¹ :=
  by induction p; refl

  @[hott, hsimp] def eq_bot_of_square_hrefl (p : a = a') :
    eq_bot_of_square (hrefl p) = (con.left_inv p)⁻¹ :=
  by induction p; refl

  @[hott, hsimp] def eq_top_of_square_vrefl (p : a = a') : eq_top_of_square (vrefl p) = (idp_con p)⁻¹ :=
  by induction p; refl

  @[hott, hsimp] def eq_top_of_square_hrefl (p : a = a') :
    eq_top_of_square (hrefl p) = (con.right_inv p)⁻¹ :=
  by induction p; refl

  @[hott] def square_of_eq_bot (r : p₀₁⁻¹ ⬝ p₁₀ ⬝ p₂₁ = p₁₂) : square p₁₀ p₁₂ p₀₁ p₂₁ :=
  by induction p₂₁; induction p₁₀; dsimp at r; induction r; induction p₀₁; exact ids

  @[hott] def square_equiv_eq (t : a₀₀ = a₀₂) (b : a₂₀ = a₂₂)
    (l : a₀₀ = a₂₀) (r : a₀₂ = a₂₂) : square t b l r ≃ t ⬝ r = l ⬝ b :=
  begin
    fapply equiv.MK,
    { exact eq_of_square},
    { exact square_of_eq},
    { intro s, induction b, dsimp [concat] at s, induction s, induction r, induction t, refl},
    { intro s, induction s, refl},
  end

  @[hott] def hdeg_square_equiv' (p q : a = a') : square idp idp p q ≃ p = q :=
  begin
    transitivity, {apply square_equiv_eq},
    transitivity, {apply eq_equiv_eq_symm},
    {apply equiv_eq_closed_right, apply idp_con},
  end

  @[hott] def vdeg_square_equiv' (p q : a = a') : square p q idp idp ≃ p = q :=
  begin
    transitivity, {apply square_equiv_eq},
    {apply equiv_eq_closed_right, apply idp_con},
  end

  @[hott] def eq_of_hdeg_square {p q : a = a'} (s : square idp idp p q) : p = q :=
  to_fun (hdeg_square_equiv' _ _) s

  @[hott] def eq_of_vdeg_square {p q : a = a'} (s : square p q idp idp) : p = q :=
  to_fun (vdeg_square_equiv' _ _) s

  @[hott] def top_deg_square (l : a₁ = a₂) (b : a₂ = a₃) (r : a₄ = a₃)
    : square (l ⬝ b ⬝ r⁻¹) b l r :=
  by induction r;induction b;induction l;constructor

  @[hott] def bot_deg_square (l : a₁ = a₂) (t : a₁ = a₃) (r : a₃ = a₄)
    : square t (l⁻¹ ⬝ t ⬝ r) l r :=
  by induction r;induction t;induction l;constructor

  /-
    the following two equivalences have as underlying inverse function the functions
    hdeg_square and vdeg_square, respectively.
    See example below the @[hott] def
  -/
  @[hott] def hdeg_square_equiv (p q : a = a') :
    square idp idp p q ≃ p = q :=
  begin
    fapply equiv_change_fun,
    { fapply equiv_change_inv, apply hdeg_square_equiv', exact hdeg_square,
        intro s, induction s, induction p, reflexivity},
    { exact eq_of_hdeg_square},
    { reflexivity}
  end

  @[hott] def vdeg_square_equiv (p q : a = a') :
    square p q idp idp ≃ p = q :=
  begin
    fapply equiv_change_fun,
    { fapply equiv_change_inv, apply vdeg_square_equiv',exact vdeg_square,
        intro s, induction s, induction p, reflexivity},
    { exact eq_of_vdeg_square},
    { reflexivity}
  end

  example (p q : a = a') : (hdeg_square_equiv p q)⁻¹ᶠ = hdeg_square := idp

  /-
    characterization of pathovers in a equality type. The type B of the equality is fixed here.
    A version where B may also varies over the path p is given in the file squareover
  -/

  @[hott] def eq_pathover {f g : A → B} {p : a = a'} {q : f a = g a} {r : f a' = g a'}
    (s : square q r (ap f p) (ap g p)) : q =[p; λ a, f a  = g a] r :=
  begin induction p, apply pathover_idp_of_eq, exact eq_of_vdeg_square s end

  @[hott] def eq_pathover_constant_left {g : A → B} {p : a = a'} {b : B} {q : b = g a} {r : b = g a'}
    (s : square q r idp (ap g p)) : q =[p; λ a, b = g a] r :=
  eq_pathover (ap_constant p b ⬝ph s)

  @[hott] def eq_pathover_id_left {g : A → A} {p : a = a'} {q : a = g a} {r : a' = g a'}
    (s : square q r p (ap g p)) : q =[p; λ a, a = g a] r :=
  eq_pathover (ap_id p ⬝ph s)

  @[hott] def eq_pathover_constant_right {f : A → B} {p : a = a'} {b : B} {q : f a = b} {r : f a' = b}
    (s : square q r (ap f p) idp) : q =[p; λ a, f a = b] r :=
  eq_pathover (s ⬝hp (ap_constant p b)⁻¹)

  @[hott] def eq_pathover_id_right {f : A → A} {p : a = a'} {q : f a = a} {r : f a' = a'}
    (s : square q r (ap f p) p) : q =[p; λ a, f a = a] r :=
  eq_pathover (s ⬝hp (ap_id p)⁻¹)

  @[hott] def eq_pathover_compose_right {f : A → C} (h : B → C) (g : A → B) {p : a = a'}
    {q : f a = h (g a)} {r : f a' = h (g a')}
    (s : square q r (ap f p) (ap h (ap g p))) : q =[p; λ a, f a = h (g a)] r :=
  eq_pathover (s ⬝hp (ap_compose h g p)⁻¹)

  @[hott] def eq_pathover_compose_left (h : B → C) (g : A → B) {f : A → C} {p : a = a'}
    {q : h (g a) = f a} {r : h (g a') = f a'}
    (s : square q r (ap h (ap g p)) (ap f p)) : q =[p; λ a, h (g a) = f a] r :=
  eq_pathover (ap_compose h g p ⬝ph s)

  @[hott] def square_of_pathover
    {f g : A → B} {p : a = a'} {q : f a = g a} {r : f a' = g a'}
    (s : q =[p; λ a, f a = g a] r) : square q r (ap f p) (ap g p) :=
  by induction p;apply vdeg_square;exact (eq_of_pathover_idp s)

  @[hott] def eq_pathover_constant_left_id_right {p : a = a'} {a₀ : A} {q : a₀ = a} {r : a₀ = a'}
    (s : square q r idp p) : q =[p] r :=
  eq_pathover (ap_constant p a₀ ⬝ph s ⬝hp (ap_id p)⁻¹)

  @[hott] def eq_pathover_id_left_constant_right {p : a = a'} {a₀ : A} {q : a = a₀} {r : a' = a₀}
    (s : square q r p idp) : q =[p; λ a, a = a₀] r :=
  eq_pathover (ap_id p ⬝ph s ⬝hp (ap_constant p a₀)⁻¹)

  @[hott] def loop_pathover {p : a = a'} {q : a = a} {r : a' = a'} (s : square q r p p) : q =[p; λ a, a = a] r :=
  eq_pathover (ap_id p ⬝ph s ⬝hp (ap_id p)⁻¹)

  /- interaction of equivalences with operations on squares -/

  @[hott] def eq_pathover_equiv_square {f g : A → B}
    (p : a = a') (q : f a = g a) (r : f a' = g a') : q =[p; λ a, f a = g a] r ≃ square q r (ap f p) (ap g p) :=
  equiv.MK square_of_pathover
           eq_pathover
           begin
             intro s, induction p, dsimp [square_of_pathover,eq_pathover],
             transitivity, {apply ap vdeg_square, apply to_right_inv (pathover_idp _ _ _)},
             {apply to_left_inv (vdeg_square_equiv _ _)}
           end
           begin
             intro s, induction p, dsimp [square_of_pathover,eq_pathover],
             transitivity, {apply ap (pathover_idp_of_eq _), apply to_right_inv (vdeg_square_equiv _ _)},
             {apply to_left_inv (pathover_idp _ _ _)},
           end

  @[hott] def square_of_pathover_eq_concato {f g : A → B} {p : a = a'} {q q' : f a = g a}
    {r : f a' = g a'} (s' : q = q') (s : q' =[p; λ a, f a = g a] r)
    : square_of_pathover (s' ⬝po s) = s' ⬝pv square_of_pathover s :=
  by induction s;induction s';reflexivity

  @[hott] def square_of_pathover_concato_eq {f g : A → B} {p : a = a'} {q : f a = g a}
    {r r' : f a' = g a'} (s' : r = r') (s : q =[p; λ a, f a = g a] r)
    : square_of_pathover (s ⬝op s') = square_of_pathover s ⬝vp s' :=
  by induction s;induction s';reflexivity

  @[hott] def square_of_pathover_concato {f g : A → B} {p : a = a'} {p' : a' = a''} {q : f a = g a}
    {q' : f a' = g a'} {q'' : f a'' = g a''} (s : q =[p; λ a, f a = g a] q') (s' : q' =[p'; λ a, f a = g a] q'')
    : square_of_pathover (s ⬝o s')
  = ap_con f p p' ⬝ph (square_of_pathover s ⬝v square_of_pathover s') ⬝hp (ap_con g p p')⁻¹ :=
  by induction s'; induction s; symmetry; apply vconcat_vrfl

  @[hott] def eq_of_square_hrfl (p : a = a') : eq_of_square hrfl = idp_con p :=
  by induction p;reflexivity

  @[hott] def eq_of_square_vrfl (p : a = a') : eq_of_square vrfl = (idp_con p)⁻¹ :=
  by induction p;reflexivity

  @[hott] def eq_of_square_hdeg_square {p q : a = a'} (r : p = q)
    : eq_of_square (hdeg_square r) = idp_con _ ⬝ r⁻¹ :=
  by induction r;induction p;reflexivity

  @[hott] def eq_of_square_vdeg_square {p q : a = a'} (r : p = q)
    : eq_of_square (vdeg_square r) = r ⬝ (idp_con _)⁻¹ :=
  by induction r;induction p;reflexivity

  @[hott] def eq_of_square_eq_vconcat {p : a₀₀ = a₂₀} (r : p = p₁₀) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : eq_of_square (r ⬝pv s₁₁) = whisker_right p₂₁ r ⬝ eq_of_square s₁₁ :=
  by induction s₁₁; eq_cases r;reflexivity

  @[hott] def eq_of_square_eq_hconcat {p : a₀₀ = a₀₂} (r : p = p₀₁) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    : eq_of_square (r ⬝ph s₁₁) = eq_of_square s₁₁ ⬝ (whisker_right p₁₂ r)⁻¹ :=
  by induction r;reflexivity

  @[hott] def eq_of_square_vconcat_eq {p : a₀₂ = a₂₂} (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) (r : p₁₂ = p)
    : eq_of_square (s₁₁ ⬝vp r) = eq_of_square s₁₁ ⬝ whisker_left p₀₁ r :=
  by induction r;reflexivity

  @[hott] def eq_of_square_hconcat_eq {p : a₂₀ = a₂₂} (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) (r : p₂₁ = p)
    : eq_of_square (s₁₁ ⬝hp r) = (whisker_left p₁₀ r)⁻¹ ⬝ eq_of_square s₁₁ :=
  by induction s₁₁; induction r;reflexivity

  @[hott] def change_path_eq_pathover {A B : Type _} {a a' : A} {f g : A → B}
    {p p' : a = a'} (r : p = p')
    {q : f a = g a} {q' : f a' = g a'}
    (s : square q q' (ap f p) (ap g p)) :
    change_path r (eq_pathover s) = eq_pathover ((ap02 f r)⁻¹ ⬝ph s ⬝hp (ap02 g r)) :=
  by induction r; reflexivity

  @[hott] def eq_hconcat_hdeg_square {A : Type _} {a a' : A} {p₁ p₂ p₃ : a = a'} (q₁ : p₁ = p₂)
    (q₂ : p₂ = p₃) : q₁ ⬝ph hdeg_square q₂ = hdeg_square (q₁ ⬝ q₂) :=
  by induction q₁; induction q₂; reflexivity

  @[hott] def hdeg_square_hconcat_eq {A : Type _} {a a' : A} {p₁ p₂ p₃ : a = a'} (q₁ : p₁ = p₂)
    (q₂ : p₂ = p₃) : hdeg_square q₁ ⬝hp q₂ = hdeg_square (q₁ ⬝ q₂) :=
  by induction q₁; induction q₂; reflexivity

  @[hott] def eq_hconcat_eq_hdeg_square {A : Type _} {a a' : A} {p₁ p₂ p₃ p₄ : a = a'} (q₁ : p₁ = p₂)
    (q₂ : p₂ = p₃) (q₃ : p₃ = p₄) : q₁ ⬝ph hdeg_square q₂ ⬝hp q₃ = hdeg_square (q₁ ⬝ q₂ ⬝ q₃) :=
  by induction q₃; apply eq_hconcat_hdeg_square

  -- @[hott] def vconcat_eq [unfold 11] {p : a₀₂ = a₂₂} (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) (r : p₁₂ = p) :
  --   square p₁₀ p p₀₁ p₂₁ :=
  -- by induction r; exact s₁₁

  -- @[hott] def eq_hconcat [unfold 11] {p : a₀₀ = a₀₂} (r : p = p₀₁)
  --   (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) : square p₁₀ p₁₂ p p₂₁ :=
  -- by induction r; exact s₁₁

  -- @[hott] def hconcat_eq [unfold 11] {p : a₂₀ = a₂₂}
  --   (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) (r : p₂₁ = p) : square p₁₀ p₁₂ p₀₁ p :=
  -- by induction r; exact s₁₁

  /- recursors for squares where some sides are reflexivity -/

  @[hott,elab_as_eliminator] def rec_on_b {a₀₀ : A}
    {P : Π{a₂₀ a₁₂ : A} {t : a₀₀ = a₂₀} {l : a₀₀ = a₁₂} {r : a₂₀ = a₁₂}, square t idp l r → Type _}
    {a₂₀ a₁₂ : A} {t : a₀₀ = a₂₀} {l : a₀₀ = a₁₂} {r : a₂₀ = a₁₂}
      (s : square t idp l r) (H : P ids) : P s :=
  have H2 : P (square_of_eq (eq_of_square s)),
    begin
      induction r, induction t,
      hgeneralize: eq_of_square s = es, dsimp at es, induction es,
      apply H
    end,
  left_inv (square_equiv_eq _ _ _ _).to_fun s ▸ H2

  @[hott, elab_as_eliminator] def rec_on_r {a₀₀ : A}
    {P : Π{a₀₂ a₂₁ : A} {t : a₀₀ = a₂₁} {b : a₀₂ = a₂₁} {l : a₀₀ = a₀₂}, square t b l idp → Type _}
    {a₀₂ a₂₁ : A} {t : a₀₀ = a₂₁} {b : a₀₂ = a₂₁} {l : a₀₀ = a₀₂}
      (s : square t b l idp) (H : P ids) : P s :=
  let p : l ⬝ b = t := (eq_of_square s)⁻¹ in
  have H2 : P (square_of_eq (eq_of_square s)⁻¹⁻¹),
    from @eq.rec_on _ _ (λx p, P (square_of_eq p⁻¹)) _ p (by induction b; induction l; exact H),
  have H3 : square_of_eq (eq_of_square s) = s,
    from left_inv (square_equiv_eq _ _ _ _).to_fun s,
  by rwra [inv_inv, H3] at H2

  @[hott, elab_as_eliminator] def rec_on_l {a₀₁ : A}
    {P : Π {a₂₀ a₂₂ : A} {t : a₀₁ = a₂₀} {b : a₀₁ = a₂₂} {r : a₂₀ = a₂₂},
      square t b idp r → Type _}
    {a₂₀ a₂₂ : A} {t : a₀₁ = a₂₀} {b : a₀₁ = a₂₂} {r : a₂₀ = a₂₂}
      (s : square t b idp r) (H : P ids) : P s :=
  let p : t ⬝ r = b := eq_of_square s ⬝ idp_con _ in
  have H2 : P (square_of_eq (p ⬝ (idp_con _)⁻¹)),
    from eq.rec_on p (by induction r; induction t; exact H),
  have H3 : square_of_eq (eq_of_square s) = s,
    from left_inv (square_equiv_eq _ _ _ _).to_fun s,
  by rwra [con_inv_cancel_right, H3] at H2

  @[hott, elab_as_eliminator] def rec_on_t {a₁₀ : A}
    {P : Π {a₀₂ a₂₂ : A} {b : a₀₂ = a₂₂} {l : a₁₀ = a₀₂} {r : a₁₀ = a₂₂}, square idp b l r → Type _}
    {a₀₂ a₂₂ : A} {b : a₀₂ = a₂₂} {l : a₁₀ = a₀₂} {r : a₁₀ = a₂₂}
      (s : square idp b l r) (H : P ids) : P s :=
  let p : l ⬝ b = r := (eq_of_square s)⁻¹ ⬝ idp_con _ in
  have H2 : P (square_of_eq ((p ⬝ (idp_con _)⁻¹)⁻¹)),
    from eq.rec_on p (by induction b; induction l; exact H),
  have H3 : P (square_of_eq ((eq_of_square s)⁻¹⁻¹)),
    by rwra con_inv_cancel_right at H2,
  have H4 : P (square_of_eq (eq_of_square s)),
    by hsimp at H3; apply H3,
  left_inv (square_equiv_eq _ _ _ _).to_fun s ▸ H4

  @[hott, elab_as_eliminator] def rec_on_tb {a : A}
    {P : Π{b : A} {l : a = b} {r : a = b}, square idp idp l r → Type _}
    {b : A} {l : a = b} {r : a = b}
      (s : square idp idp l r) (H : P ids) : P s :=
  have H2 : P (square_of_eq (eq_of_square s)),
    begin
      induction r,
      hgeneralize: eq_of_square s = es,
      dsimp at es, induction es,
      apply H,
    end,
  left_inv (square_equiv_eq _ _ _ _).to_fun s ▸ H2

  @[hott, elab_as_eliminator] def rec_on_lr {a : A}
    {P : Π{a' : A} {t : a = a'} {b : a = a'}, square t b idp idp → Type _}
    {a' : A} {t : a = a'} {b : a = a'}
      (s : square t b idp idp) (H : P ids) : P s :=
  let p : idp ⬝ b = t := (eq_of_square s)⁻¹ in
  have H2 : P (square_of_eq (eq_of_square s)⁻¹⁻¹),
    from @eq.rec_on _ _ (λx q, P (square_of_eq q⁻¹)) _ p (by induction b; exact H),
  have H3 : square_of_eq (eq_of_square s) = s,
    from left_inv (square_equiv_eq _ _ _ _).to_fun s,
  by rwra [inv_inv, H3] at H2

  --we can also do the other recursors (tl, tr, bl, br, tbl, tbr, tlr, blr), but let's postpone this until they are needed

  @[hott] def whisker_square (r₁₀ : p₁₀ = p₁₀') (r₁₂ : p₁₂ = p₁₂')
    (r₀₁ : p₀₁ = p₀₁') (r₂₁ : p₂₁ = p₂₁') (s : square p₁₀ p₁₂ p₀₁ p₂₁)
      : square p₁₀' p₁₂' p₀₁' p₂₁' :=
  by induction r₁₀; induction r₁₂; induction r₀₁; induction r₂₁; exact s

  /- squares commute with some operations on 2-paths -/

  @[hott] def square_inv2 {p₁ p₂ p₃ p₄ : a = a'}
    {t : p₁ = p₂} {b : p₃ = p₄} {l : p₁ = p₃} {r : p₂ = p₄} (s : square t b l r)
    : square (inverse2 t) (inverse2 b) (inverse2 l) (inverse2 r) :=
  by induction s;constructor

  @[hott] def square_con2 {p₁ p₂ p₃ p₄ : a₁ = a₂} {q₁ q₂ q₃ q₄ : a₂ = a₃}
    {t₁ : p₁ = p₂} {b₁ : p₃ = p₄} {l₁ : p₁ = p₃} {r₁ : p₂ = p₄}
    {t₂ : q₁ = q₂} {b₂ : q₃ = q₄} {l₂ : q₁ = q₃} {r₂ : q₂ = q₄}
    (s₁ : square t₁ b₁ l₁ r₁) (s₂ : square t₂ b₂ l₂ r₂)
      : square (t₁ ◾ t₂) (b₁ ◾ b₂) (l₁ ◾ l₂) (r₁ ◾ r₂) :=
  by induction s₂;induction s₁;constructor

  open is_trunc
  @[hott] def is_set.elims [H : is_set A] : square p₁₀ p₁₂ p₀₁ p₂₁ :=
  square_of_eq (is_set.elim _ _)

  @[hott, instance] def is_trunc_square (n : trunc_index) [H : is_trunc n .+2 A]
    : is_trunc n (square p₁₀ p₁₂ p₀₁ p₂₁) :=
  is_trunc_equiv_closed_rev n (square_equiv_eq _ _ _ _) (by apply_instance)

  -- @[hott] def square_of_con_inv_hsquare {p₁ p₂ p₃ p₄ : a₁ = a₂}
  --   {t : p₁ = p₂} {b : p₃ = p₄} {l : p₁ = p₃} {r : p₂ = p₄}
  --   (s : square (con_inv_eq_idp t) (con_inv_eq_idp b) (l ◾ r⁻²) idp)
  --     : square t b l r :=
  -- sorry --by induction s

  /- Square fillers -/
  -- TODO replace by "more algebraic" fillers?

  variables (p₁₀ p₁₂ p₀₁ p₂₁)
  @[hott] def square_fill_t : Σ (p : a₀₀ = a₂₀), square p p₁₂ p₀₁ p₂₁ :=
  by induction p₀₁; induction p₂₁; exact ⟨_, vrefl _⟩

  @[hott] def square_fill_b : Σ (p : a₀₂ = a₂₂), square p₁₀ p p₀₁ p₂₁ :=
  by induction p₀₁; induction p₂₁; exact ⟨_, vrefl _⟩

  @[hott] def square_fill_l : Σ (p : a₀₀ = a₀₂), square p₁₀ p₁₂ p p₂₁ :=
  by induction p₁₀; induction p₁₂; exact ⟨_, hrefl _⟩

  @[hott] def square_fill_r : Σ (p : a₂₀ = a₂₂) , square p₁₀ p₁₂ p₀₁ p :=
  by induction p₁₀; induction p₁₂; exact ⟨_, hrefl _⟩

  /- Squares having an 'ap' term on one face -/
  --TODO find better names
  @[hott] def square_Flr_ap_idp {c : B} {f : A → B} (p : Π a, f a = c)
    {a b : A} (q : a = b) : square (p a) (p b) (ap f q) idp  :=
  by induction q; apply vrfl

  @[hott] def square_Flr_idp_ap {c : B} {f : A → B} (p : Π a, c = f a)
    {a b : A} (q : a = b) : square (p a) (p b) idp (ap f q) :=
  by induction q; apply vrfl

  @[hott] def square_ap_idp_Flr {b : B} {f : A → B} (p : Π a, f a = b)
    {a b : A} (q : a = b) : square (ap f q) idp (p a) (p b) :=
  by induction q; apply hrfl

  /- Matching eq_hconcat with hconcat etc. -/
  -- TODO maybe rename hconcat_eq and the like?
  variable (s₁₁)
  @[hott] def ph_eq_pv_h_vp {p : a₀₀ = a₀₂} (r : p = p₀₁) :
    r ⬝ph s₁₁ =  (idp_con _)⁻¹ ⬝pv ((hdeg_square r) ⬝h s₁₁) ⬝vp idp_con _ :=
  by induction r; induction s₁₁; refl

  @[hott] def hdeg_h_eq_pv_ph_vp {p : a₀₀ = a₀₂} (r : p = p₀₁) :
    hdeg_square r ⬝h s₁₁ = idp_con _ ⬝pv (r ⬝ph s₁₁) ⬝vp (idp_con _)⁻¹ :=
  by induction r; induction s₁₁; refl

  @[hott] def hp_eq_h {p : a₂₀ = a₂₂} (r : p₂₁ = p) :
    s₁₁ ⬝hp r = s₁₁ ⬝h hdeg_square r :=
  by induction r; induction s₁₁; refl

  @[hott] def pv_eq_ph_vdeg_v_vh {p : a₀₀ = a₂₀} (r : p = p₁₀) :
    r ⬝pv s₁₁ = (idp_con _)⁻¹ ⬝ph ((vdeg_square r) ⬝v s₁₁) ⬝hp idp_con _ :=
  by induction r; induction s₁₁; refl

  @[hott] def vdeg_v_eq_ph_pv_hp {p : a₀₀ = a₂₀} (r : p = p₁₀) :
    vdeg_square r ⬝v s₁₁ = idp_con _ ⬝ph (r ⬝pv s₁₁) ⬝hp (idp_con _)⁻¹ :=
  by induction r; induction s₁₁; refl

  @[hott] def vp_eq_v {p : a₀₂ = a₂₂} (r : p₁₂ = p) :
    s₁₁ ⬝vp r = s₁₁ ⬝v vdeg_square r :=
  by induction r; induction s₁₁; refl

  @[hott] def natural_square {f g : A → B} (p : f ~ g) (q : a = a') :
    square (p a) (p a') (ap f q) (ap g q) :=
  square_of_pathover (apd p q)

  @[hott, hsimp] def natural_square_tr {f g : A → B} (p : f ~ g) (q : a = a') :
    square (ap f q) (ap g q) (p a) (p a') :=
  transpose (natural_square p q)

  @[hott] def natural_square011 {A A' : Type _} {B : A → Type _}
    {a a' : A} {p : a = a'} {b : B a} {b' : B a'} (q : b =[p] b')
    {l r  : Π⦃a⦄, B a → A'} (g : Π⦃a⦄ (b : B a), l b = r b)
    : square (apd011 l p q) (apd011 r p q) (g b) (g b') :=
  begin
    induction q, exact hrfl
  end

  @[hott] def natural_square0111' {A A' : Type _} {B : A → Type _} (C : Π⦃a⦄, B a → Type _)
    {a a' : A} {p : a = a'} {b : B a} {b' : B a'} {q : b =[p] b'}
    {c : C b} {c' : C b'} (s : c =[apd011 C p q; id] c')
    {l r  : Π⦃a⦄ {b : B a}, C b → A'}
    (g : Π⦃a⦄ {b : B a} (c : C b), l c = r c)
    : square (apd0111 l p q s) (apd0111 r p q s) (g c) (g c') :=
  begin
    induction q, dsimp at s, apply idp_rec_on s, exact hrfl
  end

  -- this can be generalized a bit, by making the domain and codomain of k different, and also have
  -- a function at the RHS of s (similar to m)
  @[hott] def natural_square0111 {A A' : Type _} {B : A → Type _} (C : Π⦃a⦄, B a → Type _)
    {a a' : A} {p : a = a'} {b : B a} {b' : B a'} {q : b =[p] b'}
    {c : C b} {c' : C b'} (r : c =[apd011 C p q; id] c')
    {k : A → A} {l : Π⦃a⦄, B a → B (k a)} (m : Π⦃a⦄ {b : B a}, C b → C (l b))
    {f  : Π⦃a⦄ {b : B a}, C b → A'}
    (s : Π⦃a⦄ {b : B a} (c : C b), f (m c) = f c)
    : square (@apd0111 _ _ B _ _ _ _ _ _ _ (λa b (c : C b), f (m c)) p q r) (apd0111 f p q r) (s c) (s c') :=
  begin
    induction q, dsimp at r, apply idp_rec_on r, exact hrfl
  end

  @[hott] def natural_square2 {A : Type _} {B : Type _} {C : A → B → Type _} {X : Type _}
    {a a₂ : A} {b b₂ : B} {c : C a b} {c₂ : C a₂ b₂} {f : A → X} {g : B → X}
    (h : Πa b, C a b → f a = g b) (p : a = a₂) (q : b = b₂) (r : transport11 C p q c = c₂) :
    square (h a b c) (h a₂ b₂ c₂) (ap f p) (ap g q) :=
  by hinduction p; hinduction q; hinduction r; exact vrfl

  /- some higher coherence conditions -/

  @[hott] theorem whisker_bl_whisker_tl_eq (p : a = a')
    : whisker_bl p (whisker_tl p ids) = con.right_inv p ⬝ph vrfl :=
  by induction p; reflexivity

  @[hott] theorem ap_is_constant_natural_square {g : B → C} {f : A → B} (H : Πa, g (f a) = c) (p : a = a') :
    (ap_is_constant (g ∘ f) H p)⁻¹ ⬝ph natural_square H p ⬝hp ap_constant p c =
      whisker_bl (H a') (whisker_tl (H a) ids) :=
  begin induction p, dsimp [ap_is_constant], rwr [inv_inv, whisker_bl_whisker_tl_eq] end

  @[hott] def inv_ph_eq_of_eq_ph {p : a₀₀ = a₀₂} {r : p₀₁ = p} {s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁}
    {s₁₁' : square p₁₀ p₁₂ p p₂₁} (t : s₁₁ = r ⬝ph s₁₁') : r⁻¹ ⬝ph s₁₁ = s₁₁' :=
  by induction r; exact t

  -- the following is used for torus.elim_surf
  @[hott] theorem whisker_square_aps_eq {f : A → B}
    {q₁₀ : f a₀₀ = f a₂₀} {q₀₁ : f a₀₀ = f a₀₂} {q₂₁ : f a₂₀ = f a₂₂} {q₁₂ : f a₀₂ = f a₂₂}
    {r₁₀ : ap f p₁₀ = q₁₀} {r₀₁ : ap f p₀₁ = q₀₁} {r₂₁ : ap f p₂₁ = q₂₁} {r₁₂ : ap f p₁₂ = q₁₂}
    {s₁₁ : p₁₀ ⬝ p₂₁ = p₀₁ ⬝ p₁₂} {t₁₁ : square q₁₀ q₁₂ q₀₁ q₂₁}
    (u : square (ap02 f s₁₁) (eq_of_square t₁₁)
                (ap_con f p₁₀ p₂₁ ⬝ (r₁₀ ◾ r₂₁)) (ap_con f p₀₁ p₁₂ ⬝ (r₀₁ ◾ r₁₂)))
    : whisker_square r₁₀ r₁₂ r₀₁ r₂₁ (aps f (square_of_eq s₁₁)) = t₁₁ :=
  begin
    induction r₁₀, induction r₀₁, induction r₁₂, induction r₂₁,
    induction p₁₂, induction p₁₀, induction p₂₁, dsimp at *, induction s₁₁, dsimp at *,
    dsimp [square_of_eq],
    apply eq_of_fn_eq_fn (square_equiv_eq _ _ _ _), dsimp,
    exact (eq_bot_of_square u)⁻¹
  end

  @[hott] def natural_square_eq {A B : Type _} {a a' : A} {f g : A → B} (p : f ~ g) (q : a = a')
    : natural_square p q = square_of_pathover (apd p q) :=
  idp

  @[hott] def eq_of_square_hrfl_hconcat_eq {A : Type _} {a a' : A} {p p' : a = a'} (q : p = p')
    : eq_of_square (hrfl ⬝hp q⁻¹) = idp_con _ ⬝ q :=
  by induction q; induction p; reflexivity

  @[hott, hsimp] def aps_vrfl {A B : Type _} {a a' : A} (f : A → B) (p : a = a') :
    aps f (vrefl p) = vrefl (ap f p) :=
  by induction p; reflexivity

  @[hott, hsimp] def aps_hrfl {A B : Type _} {a a' : A} (f : A → B) (p : a = a') :
    aps f (hrefl p) = hrefl (ap f p) :=
  by induction p; reflexivity

  -- should the following two equalities be cubes?
  @[hott] def natural_square_ap_fn {A B C : Type _} {a a' : A} {g h : A → B} (f : B → C) (p : g ~ h)
    (q : a = a') : natural_square (λa, ap f (p a)) q =
      ap_compose f g q ⬝ph (aps f (natural_square p q) ⬝hp (ap_compose f h q)⁻¹) :=
  begin
    induction q, symmetry, exact (aps_vrfl _ _)
  end

  @[hott] def natural_square_compose {A B C : Type _} {a a' : A} {g g' : B → C}
    (p : g ~ g') (f : A → B) (q : a = a') : natural_square (λa, p (f a)) q =
    ap_compose g f q ⬝ph (natural_square p (ap f q) ⬝hp (ap_compose g' f q)⁻¹) :=
  by induction q; reflexivity

  @[hott] def natural_square_eq2 {A B : Type _} {a a' : A} {f f' : A → B} (p : f ~ f') {q q' : a = a'}
    (r : q = q') : natural_square p q = ap02 f r ⬝ph (natural_square p q' ⬝hp (ap02 f' r)⁻¹) :=
  by induction r; reflexivity

  @[hott, hsimp] def natural_square_refl {A B : Type _} {a a' : A} (f : A → B) (q : a = a')
    : natural_square (homotopy.refl f) q = hrfl :=
  by induction q; reflexivity

  @[hott, hsimp] def natural_square_idp {A B : Type _} {f g : A → B} (h : f ~ g)  (a : A)
    : natural_square h (idpath a) = vrefl (h a) :=
  by reflexivity

  @[hott] def natural_square_tr_idp {A B : Type _} {f g : A → B} (h : f ~ g) (a : A)
    : natural_square_tr h (idpath a) = hrefl (h a) :=
  by hsimp

  @[hott] def aps_eq_hconcat {p₀₁'} (f : A → B) (q : p₀₁' = p₀₁) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) :
    aps f (q ⬝ph s₁₁) = ap02 f q ⬝ph aps f s₁₁ :=
  by induction q; reflexivity

  @[hott] def aps_hconcat_eq {p₂₁'} (f : A → B) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) (r : p₂₁' = p₂₁) :
    aps f (s₁₁ ⬝hp r⁻¹) = aps f s₁₁ ⬝hp (ap02 f r)⁻¹ :=
  by induction r; reflexivity

  @[hott] def aps_hconcat_eq' {p₂₁'} (f : A → B) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁) (r : p₂₁ = p₂₁') :
    aps f (s₁₁ ⬝hp r) = aps f s₁₁ ⬝hp ap02 f r :=
  by induction r; reflexivity

  @[hott] def aps_square_of_eq (f : A → B) (s : p₁₀ ⬝ p₂₁ = p₀₁ ⬝ p₁₂) :
    aps f (square_of_eq s) = square_of_eq ((ap_con f p₁₀ p₂₁)⁻¹ ⬝ ap02 f s ⬝ ap_con f p₀₁ p₁₂) :=
  by induction p₁₂; dsimp at *; induction s; induction p₂₁; induction p₁₀; reflexivity

  @[hott] def aps_eq_hconcat_eq {p₀₁' p₂₁'} (f : A → B) (q : p₀₁' = p₀₁) (s₁₁ : square p₁₀ p₁₂ p₀₁ p₂₁)
    (r : p₂₁' = p₂₁) : aps f (q ⬝ph s₁₁ ⬝hp r⁻¹) = ap02 f q ⬝ph aps f s₁₁ ⬝hp (ap02 f r)⁻¹ :=
  by induction q; induction r; reflexivity

end eq

end hott


/-
Copyright (c) 2020 Zhouhang Zhou. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Zhouhang Zhou, Yury Kudryashov
-/
import measure_theory.integral.integrable_on
import measure_theory.integral.bochner
import order.filter.indicator_function
import topology.metric_space.thickened_indicator

/-!
# Set integral

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

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

We use the property `integrable_on f s μ := integrable f (μ.restrict s)`, defined in
`measure_theory.integrable_on`. We also defined in that same file a predicate
`integrable_at_filter (f : α → E) (l : filter α) (μ : measure α)` saying that `f` is integrable at
some set `s ∈ l`.

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

## Notation

We provide the following notations for expressing the integral of a function on a set :
* `∫ a in s, f a ∂μ` is `measure_theory.integral (μ.restrict s) f`
* `∫ a in s, f a` is `∫ a in s, f a ∂volume`

Note that the set notations are defined in the file `measure_theory/integral/bochner`,
but we reference them here because all theorems about set integrals are in this file.

-/

noncomputable theory
open set filter topological_space measure_theory function
open_locale classical topological_space interval big_operators filter ennreal nnreal measure_theory

variables {α β E F : Type*} [measurable_space α]

namespace measure_theory

section normed_group

variables [normed_group E]  {f g : α → E} {s t : set α} {μ ν : measure α}
  {l l' : filter α}

variables [complete_space E] [normed_space ℝ E]

lemma set_integral_congr_ae (hs : measurable_set s) (h : ∀ᵐ x ∂μ, x ∈ s → f x = g x) :
  ∫ x in s, f x ∂μ = ∫ x in s, g x ∂μ :=
integral_congr_ae ((ae_restrict_iff' hs).2 h)

lemma set_integral_congr (hs : measurable_set s) (h : eq_on f g s) :
  ∫ x in s, f x ∂μ = ∫ x in s, g x ∂μ :=
set_integral_congr_ae hs $ eventually_of_forall h

lemma set_integral_congr_set_ae (hst : s =ᵐ[μ] t) :
  ∫ x in s, f x ∂μ = ∫ x in t, f x ∂μ :=
by rw measure.restrict_congr_set hst

lemma integral_union_ae (hst : ae_disjoint μ s t) (ht : null_measurable_set t μ)
  (hfs : integrable_on f s μ) (hft : integrable_on f t μ) :
  ∫ x in s ∪ t, f x ∂μ = ∫ x in s, f x ∂μ + ∫ x in t, f x ∂μ :=
by simp only [integrable_on, measure.restrict_union₀ hst ht, integral_add_measure hfs hft]

lemma integral_diff (ht : measurable_set t) (hfs : integrable_on f s μ)
  (hft : integrable_on f t μ) (hts : t ⊆ s) :
  ∫ x in s \ t, f x ∂μ = ∫ x in s, f x ∂μ - ∫ x in t, f x ∂μ :=
begin
  rw [eq_sub_iff_add_eq, ← integral_union, diff_union_of_subset hts],
  exacts [disjoint_diff.symm, ht, hfs.mono_set (diff_subset _ _), hft]
end

lemma integral_finset_bUnion {ι : Type*} (t : finset ι) {s : ι → set α}
  (hs : ∀ i ∈ t, measurable_set (s i)) (h's : set.pairwise ↑t (disjoint on s))
  (hf : ∀ i ∈ t, integrable_on f (s i) μ) :
  ∫ x in (⋃ i ∈ t, s i), f x ∂ μ = ∑ i in t, ∫ x in s i, f x ∂ μ :=
begin
  induction t using finset.induction_on with a t hat IH hs h's,
  { simp },
  { simp only [finset.coe_insert, finset.forall_mem_insert, set.pairwise_insert,
      finset.set_bUnion_insert] at hs hf h's ⊢,
    rw [integral_union _ _ hf.1 (integrable_on_finset_Union.2 hf.2)],
    { rw [finset.sum_insert hat, IH hs.2 h's.1 hf.2] },
    { simp only [disjoint_Union_right],
      exact (λ i hi, (h's.2 i hi (ne_of_mem_of_not_mem hi hat).symm).1) },
    { exact finset.measurable_set_bUnion _ hs.2 } }
end

lemma integral_fintype_Union {ι : Type*} [fintype ι] {s : ι → set α}
  (hs : ∀ i, measurable_set (s i)) (h's : pairwise (disjoint on s))
  (hf : ∀ i, integrable_on f (s i) μ) :
  ∫ x in (⋃ i, s i), f x ∂ μ = ∑ i, ∫ x in s i, f x ∂ μ :=
begin
  convert integral_finset_bUnion finset.univ (λ i hi, hs i) _ (λ i _, hf i),
  { simp },
  { simp [pairwise_univ, h's] }
end

lemma integral_empty : ∫ x in ∅, f x ∂μ = 0 := by rw [measure.restrict_empty, integral_zero_measure]

lemma integral_univ : ∫ x in univ, f x ∂μ = ∫ x, f x ∂μ := by rw [measure.restrict_univ]

lemma integral_add_compl (hs : measurable_set s) (hfi : integrable f μ) :
  ∫ x in s, f x ∂μ + ∫ x in sᶜ, f x ∂μ = ∫ x, f x ∂μ :=
by rw [← integral_union (@disjoint_compl_right (set α) _ _) hs.compl
    hfi.integrable_on hfi.integrable_on, union_compl_self, integral_univ]

/-- For a function `f` and a measurable set `s`, the integral of `indicator s f`
over the whole space is equal to `∫ x in s, f x ∂μ` defined as `∫ x, f x ∂(μ.restrict s)`. -/
lemma integral_indicator (hs : measurable_set s) :
  ∫ x, indicator s f x ∂μ = ∫ x in s, f x ∂μ :=
begin
  by_cases hfi : integrable_on f s μ, swap,
  { rwa [integral_undef, integral_undef],
    rwa integrable_indicator_iff hs },
  calc ∫ x, indicator s f x ∂μ = ∫ x in s, indicator s f x ∂μ + ∫ x in sᶜ, indicator s f x ∂μ :
    (integral_add_compl hs (hfi.indicator hs)).symm
  ... = ∫ x in s, f x ∂μ + ∫ x in sᶜ, 0 ∂μ :
    congr_arg2 (+) (integral_congr_ae (indicator_ae_eq_restrict hs))
      (integral_congr_ae (indicator_ae_eq_restrict_compl hs))
  ... = ∫ x in s, f x ∂μ : by simp
end

lemma of_real_set_integral_one_of_measure_ne_top {α : Type*} {m : measurable_space α}
  {μ : measure α} {s : set α} (hs : μ s ≠ ∞) :
  ennreal.of_real (∫ x in s, (1 : ℝ) ∂μ) = μ s :=
calc
ennreal.of_real (∫ x in s, (1 : ℝ) ∂μ)
    = ennreal.of_real (∫ x in s, ∥(1 : ℝ)∥ ∂μ) : by simp only [norm_one]
... = ∫⁻ x in s, 1 ∂μ :
begin
  rw of_real_integral_norm_eq_lintegral_nnnorm (integrable_on_const.2 (or.inr hs.lt_top)),
  simp only [nnnorm_one, ennreal.coe_one],
end
... = μ s : set_lintegral_one _

lemma of_real_set_integral_one {α : Type*} {m : measurable_space α} (μ : measure α)
  [is_finite_measure μ] (s : set α) :
  ennreal.of_real (∫ x in s, (1 : ℝ) ∂μ) = μ s :=
of_real_set_integral_one_of_measure_ne_top (measure_ne_top μ s)

lemma integral_piecewise [decidable_pred (∈ s)] (hs : measurable_set s)
  {f g : α → E} (hf : integrable_on f s μ) (hg : integrable_on g sᶜ μ) :
  ∫ x, s.piecewise f g x ∂μ = ∫ x in s, f x ∂μ + ∫ x in sᶜ, g x ∂μ :=
by rw [← set.indicator_add_compl_eq_piecewise,
  integral_add' (hf.indicator hs) (hg.indicator hs.compl),
  integral_indicator hs, integral_indicator hs.compl]

lemma tendsto_set_integral_of_monotone {ι : Type*} [encodable ι] [semilattice_sup ι]
  {s : ι → set α} {f : α → E} (hsm : ∀ i, measurable_set (s i))
  (h_mono : monotone s) (hfi : integrable_on f (⋃ n, s n) μ) :
  tendsto (λ i, ∫ a in s i, f a ∂μ) at_top (𝓝 (∫ a in (⋃ n, s n), f a ∂μ)) :=
begin
  have hfi' : ∫⁻ x in ⋃ n, s n, ∥f x∥₊ ∂μ < ∞ := hfi.2,
  set S := ⋃ i, s i,
  have hSm : measurable_set S := measurable_set.Union hsm,
  have hsub : ∀ {i}, s i ⊆ S, from subset_Union s,
  rw [← with_density_apply _ hSm] at hfi',
  set ν := μ.with_density (λ x, ∥f x∥₊) with hν,
  refine metric.nhds_basis_closed_ball.tendsto_right_iff.2 (λ ε ε0, _),
  lift ε to ℝ≥0 using ε0.le,
  have : ∀ᶠ i in at_top, ν (s i) ∈ Icc (ν S - ε) (ν S + ε),
    from tendsto_measure_Union h_mono (ennreal.Icc_mem_nhds hfi'.ne (ennreal.coe_pos.2 ε0).ne'),
  refine this.mono (λ i hi, _),
  rw [mem_closed_ball_iff_norm', ← integral_diff (hsm i) hfi (hfi.mono_set hsub) hsub,
    ← coe_nnnorm, nnreal.coe_le_coe, ← ennreal.coe_le_coe],
  refine (ennnorm_integral_le_lintegral_ennnorm _).trans _,
  rw [← with_density_apply _ (hSm.diff (hsm _)), ← hν, measure_diff hsub (hsm _)],
  exacts [tsub_le_iff_tsub_le.mp hi.1,
    (hi.2.trans_lt $ ennreal.add_lt_top.2 ⟨hfi', ennreal.coe_lt_top⟩).ne]
end

lemma has_sum_integral_Union_ae {ι : Type*} [encodable ι] {s : ι → set α} {f : α → E}
  (hm : ∀ i, null_measurable_set (s i) μ) (hd : pairwise (ae_disjoint μ on s))
  (hfi : integrable_on f (⋃ i, s i) μ) :
  has_sum (λ n, ∫ a in s n, f a ∂ μ) (∫ a in ⋃ n, s n, f a ∂μ) :=
begin
  simp only [integrable_on, measure.restrict_Union_ae hd hm] at hfi ⊢,
  exact has_sum_integral_measure hfi
end

lemma has_sum_integral_Union {ι : Type*} [encodable ι] {s : ι → set α} {f : α → E}
  (hm : ∀ i, measurable_set (s i)) (hd : pairwise (disjoint on s))
  (hfi : integrable_on f (⋃ i, s i) μ) :
  has_sum (λ n, ∫ a in s n, f a ∂ μ) (∫ a in ⋃ n, s n, f a ∂μ) :=
has_sum_integral_Union_ae (λ i, (hm i).null_measurable_set) (hd.mono (λ i j h, h.ae_disjoint)) hfi

lemma integral_Union {ι : Type*} [encodable ι] {s : ι → set α} {f : α → E}
  (hm : ∀ i, measurable_set (s i)) (hd : pairwise (disjoint on s))
  (hfi : integrable_on f (⋃ i, s i) μ) :
  (∫ a in (⋃ n, s n), f a ∂μ) = ∑' n, ∫ a in s n, f a ∂ μ :=
(has_sum.tsum_eq (has_sum_integral_Union hm hd hfi)).symm

lemma integral_Union_ae {ι : Type*} [encodable ι] {s : ι → set α} {f : α → E}
  (hm : ∀ i, null_measurable_set (s i) μ) (hd : pairwise (ae_disjoint μ on s))
  (hfi : integrable_on f (⋃ i, s i) μ) :
  (∫ a in (⋃ n, s n), f a ∂μ) = ∑' n, ∫ a in s n, f a ∂ μ :=
(has_sum.tsum_eq (has_sum_integral_Union_ae hm hd hfi)).symm

lemma set_integral_eq_zero_of_forall_eq_zero {f : α → E} (hf : strongly_measurable f)
  (ht_eq : ∀ x ∈ t, f x = 0) :
  ∫ x in t, f x ∂μ = 0 :=
begin
  refine integral_eq_zero_of_ae _,
  rw [eventually_eq, ae_restrict_iff (hf.measurable_set_eq_fun strongly_measurable_zero)],
  refine eventually_of_forall (λ x hx, _),
  rw pi.zero_apply,
  exact ht_eq x hx,
end

lemma set_integral_union_eq_left {f : α → E} (hf : strongly_measurable f) (hfi : integrable f μ)
  (hs : measurable_set s) (ht_eq : ∀ x ∈ t, f x = 0) :
  ∫ x in (s ∪ t), f x ∂μ = ∫ x in s, f x ∂μ :=
begin
  rw [← set.union_diff_self, union_comm, integral_union,
    set_integral_eq_zero_of_forall_eq_zero _ (λ x hx, ht_eq x (diff_subset _ _ hx)), zero_add],
  exacts [hf, disjoint_diff.symm, hs, hfi.integrable_on, hfi.integrable_on]
end

lemma set_integral_neg_eq_set_integral_nonpos [linear_order E] [order_closed_topology E]
  {f : α → E} (hf : strongly_measurable f) (hfi : integrable f μ) :
  ∫ x in {x | f x < 0}, f x ∂μ = ∫ x in {x | f x ≤ 0}, f x ∂μ :=
begin
  have h_union : {x | f x ≤ 0} = {x | f x < 0} ∪ {x | f x = 0},
    by { ext, simp_rw [set.mem_union_eq, set.mem_set_of_eq], exact le_iff_lt_or_eq, },
  rw h_union,
  exact (set_integral_union_eq_left hf hfi (hf.measurable_set_lt strongly_measurable_const)
    (λ x hx, hx)).symm,
end

lemma integral_norm_eq_pos_sub_neg {f : α → ℝ} (hf : strongly_measurable f)
  (hfi : integrable f μ) :
  ∫ x, ∥f x∥ ∂μ = ∫ x in {x | 0 ≤ f x}, f x ∂μ - ∫ x in {x | f x ≤ 0}, f x ∂μ :=
have h_meas : measurable_set {x | 0 ≤ f x}, from strongly_measurable_const.measurable_set_le hf,
calc ∫ x, ∥f x∥ ∂μ = ∫ x in {x | 0 ≤ f x}, ∥f x∥ ∂μ + ∫ x in {x | 0 ≤ f x}ᶜ, ∥f x∥ ∂μ :
  by rw ← integral_add_compl h_meas hfi.norm
... = ∫ x in {x | 0 ≤ f x}, f x ∂μ + ∫ x in {x | 0 ≤ f x}ᶜ, ∥f x∥ ∂μ :
begin
  congr' 1,
  refine set_integral_congr h_meas (λ x hx, _),
  dsimp only,
  rw [real.norm_eq_abs, abs_eq_self.mpr _],
  exact hx,
end
... = ∫ x in {x | 0 ≤ f x}, f x ∂μ - ∫ x in {x | 0 ≤ f x}ᶜ, f x ∂μ :
begin
  congr' 1,
  rw ← integral_neg,
  refine set_integral_congr h_meas.compl (λ x hx, _),
  dsimp only,
  rw [real.norm_eq_abs, abs_eq_neg_self.mpr _],
  rw [set.mem_compl_iff, set.nmem_set_of_eq] at hx,
  linarith,
end
... = ∫ x in {x | 0 ≤ f x}, f x ∂μ - ∫ x in {x | f x ≤ 0}, f x ∂μ :
by { rw ← set_integral_neg_eq_set_integral_nonpos hf hfi, congr, ext1 x, simp, }

lemma set_integral_const (c : E) : ∫ x in s, c ∂μ = (μ s).to_real • c :=
by rw [integral_const, measure.restrict_apply_univ]

@[simp]
lemma integral_indicator_const (e : E) ⦃s : set α⦄ (s_meas : measurable_set s) :
  ∫ (a : α), s.indicator (λ (x : α), e) a ∂μ = (μ s).to_real • e :=
by rw [integral_indicator s_meas, ← set_integral_const]

@[simp]
lemma integral_indicator_one ⦃s : set α⦄ (hs : measurable_set s) :
  ∫ a, s.indicator 1 a ∂μ = (μ s).to_real :=
(integral_indicator_const 1 hs).trans ((smul_eq_mul _).trans (mul_one _))

lemma set_integral_indicator_const_Lp {p : ℝ≥0∞} (hs : measurable_set s) (ht : measurable_set t)
  (hμt : μ t ≠ ∞) (x : E) :
  ∫ a in s, indicator_const_Lp p ht hμt x a ∂μ = (μ (t ∩ s)).to_real • x :=
calc ∫ a in s, indicator_const_Lp p ht hμt x a ∂μ
    = (∫ a in s, t.indicator (λ _, x) a ∂μ) :
  by rw set_integral_congr_ae hs (indicator_const_Lp_coe_fn.mono (λ x hx hxs, hx))
... = (μ (t ∩ s)).to_real • x : by rw [integral_indicator_const _ ht, measure.restrict_apply ht]

lemma integral_indicator_const_Lp {p : ℝ≥0∞} (ht : measurable_set t) (hμt : μ t ≠ ∞) (x : E) :
  ∫ a, indicator_const_Lp p ht hμt x a ∂μ = (μ t).to_real • x :=
calc ∫ a, indicator_const_Lp p ht hμt x a ∂μ
    = ∫ a in univ, indicator_const_Lp p ht hμt x a ∂μ : by rw integral_univ
... = (μ (t ∩ univ)).to_real • x : set_integral_indicator_const_Lp measurable_set.univ ht hμt x
... = (μ t).to_real • x : by rw inter_univ

lemma set_integral_map {β} [measurable_space β] {g : α → β} {f : β → E} {s : set β}
  (hs : measurable_set s)
  (hf : ae_strongly_measurable f (measure.map g μ)) (hg : ae_measurable g μ) :
  ∫ y in s, f y ∂(measure.map g μ) = ∫ x in g ⁻¹' s, f (g x) ∂μ :=
begin
  rw [measure.restrict_map_of_ae_measurable hg hs,
      integral_map (hg.mono_measure measure.restrict_le_self) (hf.mono_measure _)],
  exact measure.map_mono_of_ae_measurable measure.restrict_le_self hg
end

lemma _root_.measurable_embedding.set_integral_map {β} {_ : measurable_space β} {f : α → β}
  (hf : measurable_embedding f) (g : β → E) (s : set β) :
  ∫ y in s, g y ∂(measure.map f μ) = ∫ x in f ⁻¹' s, g (f x) ∂μ :=
by rw [hf.restrict_map, hf.integral_map]

lemma _root_.closed_embedding.set_integral_map [topological_space α] [borel_space α]
  {β} [measurable_space β] [topological_space β] [borel_space β]
  {g : α → β} {f : β → E} (s : set β) (hg : closed_embedding g) :
  ∫ y in s, f y ∂(measure.map g μ) = ∫ x in g ⁻¹' s, f (g x) ∂μ :=
hg.measurable_embedding.set_integral_map _ _

lemma measure_preserving.set_integral_preimage_emb {β} {_ : measurable_space β} {f : α → β} {ν}
  (h₁ : measure_preserving f μ ν) (h₂ : measurable_embedding f) (g : β → E) (s : set β) :
  ∫ x in f ⁻¹' s, g (f x) ∂μ = ∫ y in s, g y ∂ν :=
(h₁.restrict_preimage_emb h₂ s).integral_comp h₂ _

lemma measure_preserving.set_integral_image_emb {β} {_ : measurable_space β} {f : α → β} {ν}
  (h₁ : measure_preserving f μ ν) (h₂ : measurable_embedding f) (g : β → E) (s : set α) :
  ∫ y in f '' s, g y ∂ν = ∫ x in s, g (f x) ∂μ :=
eq.symm $ (h₁.restrict_image_emb h₂ s).integral_comp h₂ _

lemma set_integral_map_equiv {β} [measurable_space β] (e : α ≃ᵐ β) (f : β → E) (s : set β) :
  ∫ y in s, f y ∂(measure.map e μ) = ∫ x in e ⁻¹' s, f (e x) ∂μ :=
e.measurable_embedding.set_integral_map f s

lemma norm_set_integral_le_of_norm_le_const_ae {C : ℝ} (hs : μ s < ∞)
  (hC : ∀ᵐ x ∂μ.restrict s, ∥f x∥ ≤ C) :
  ∥∫ x in s, f x ∂μ∥ ≤ C * (μ s).to_real :=
begin
  rw ← measure.restrict_apply_univ at *,
  haveI : is_finite_measure (μ.restrict s) := ⟨‹_›⟩,
  exact norm_integral_le_of_norm_le_const hC
end

lemma norm_set_integral_le_of_norm_le_const_ae' {C : ℝ} (hs : μ s < ∞)
  (hC : ∀ᵐ x ∂μ, x ∈ s → ∥f x∥ ≤ C) (hfm : ae_strongly_measurable f (μ.restrict s)) :
  ∥∫ x in s, f x ∂μ∥ ≤ C * (μ s).to_real :=
begin
  apply norm_set_integral_le_of_norm_le_const_ae hs,
  have A : ∀ᵐ (x : α) ∂μ, x ∈ s → ∥ae_strongly_measurable.mk f hfm x∥ ≤ C,
  { filter_upwards [hC, hfm.ae_mem_imp_eq_mk] with _ h1 h2 h3,
    rw [← h2 h3],
    exact h1 h3 },
  have B : measurable_set {x | ∥(hfm.mk f) x∥ ≤ C} :=
    hfm.strongly_measurable_mk.norm.measurable measurable_set_Iic,
  filter_upwards [hfm.ae_eq_mk, (ae_restrict_iff B).2 A] with _ h1 _,
  rwa h1,
end

lemma norm_set_integral_le_of_norm_le_const_ae'' {C : ℝ} (hs : μ s < ∞) (hsm : measurable_set s)
  (hC : ∀ᵐ x ∂μ, x ∈ s → ∥f x∥ ≤ C) :
  ∥∫ x in s, f x ∂μ∥ ≤ C * (μ s).to_real :=
norm_set_integral_le_of_norm_le_const_ae hs $ by rwa [ae_restrict_eq hsm, eventually_inf_principal]

lemma norm_set_integral_le_of_norm_le_const {C : ℝ} (hs : μ s < ∞)
  (hC : ∀ x ∈ s, ∥f x∥ ≤ C) (hfm : ae_strongly_measurable f (μ.restrict s)) :
  ∥∫ x in s, f x ∂μ∥ ≤ C * (μ s).to_real :=
norm_set_integral_le_of_norm_le_const_ae' hs (eventually_of_forall hC) hfm

lemma norm_set_integral_le_of_norm_le_const' {C : ℝ} (hs : μ s < ∞) (hsm : measurable_set s)
  (hC : ∀ x ∈ s, ∥f x∥ ≤ C) :
  ∥∫ x in s, f x ∂μ∥ ≤ C * (μ s).to_real :=
norm_set_integral_le_of_norm_le_const_ae'' hs hsm $ eventually_of_forall hC

lemma set_integral_eq_zero_iff_of_nonneg_ae {f : α → ℝ} (hf : 0 ≤ᵐ[μ.restrict s] f)
  (hfi : integrable_on f s μ) :
  ∫ x in s, f x ∂μ = 0 ↔ f =ᵐ[μ.restrict s] 0 :=
integral_eq_zero_iff_of_nonneg_ae hf hfi

lemma set_integral_pos_iff_support_of_nonneg_ae {f : α → ℝ} (hf : 0 ≤ᵐ[μ.restrict s] f)
  (hfi : integrable_on f s μ) :
  0 < ∫ x in s, f x ∂μ ↔ 0 < μ (support f ∩ s) :=
begin
  rw [integral_pos_iff_support_of_nonneg_ae hf hfi, measure.restrict_apply₀],
  rw support_eq_preimage,
  exact hfi.ae_strongly_measurable.ae_measurable.null_measurable (measurable_set_singleton 0).compl
end

lemma set_integral_trim {α} {m m0 : measurable_space α} {μ : measure α} (hm : m ≤ m0) {f : α → E}
  (hf_meas : strongly_measurable[m] f) {s : set α} (hs : measurable_set[m] s) :
  ∫ x in s, f x ∂μ = ∫ x in s, f x ∂(μ.trim hm) :=
by rwa [integral_trim hm hf_meas, restrict_trim hm μ]

lemma integral_Icc_eq_integral_Ioc' [partial_order α] {f : α → E} {a b : α} (ha : μ {a} = 0) :
  ∫ t in Icc a b, f t ∂μ = ∫ t in Ioc a b, f t ∂μ :=
set_integral_congr_set_ae (Ioc_ae_eq_Icc' ha).symm

lemma integral_Ioc_eq_integral_Ioo' [partial_order α] {f : α → E} {a b : α} (hb : μ {b} = 0) :
  ∫ t in Ioc a b, f t ∂μ = ∫ t in Ioo a b, f t ∂μ :=
set_integral_congr_set_ae (Ioo_ae_eq_Ioc' hb).symm

lemma integral_Icc_eq_integral_Ioc [partial_order α] {f : α → E} {a b : α} [has_no_atoms μ] :
  ∫ t in Icc a b, f t ∂μ = ∫ t in Ioc a b, f t ∂μ :=
integral_Icc_eq_integral_Ioc' $ measure_singleton a

lemma integral_Ioc_eq_integral_Ioo [partial_order α] {f : α → E} {a b : α} [has_no_atoms μ] :
  ∫ t in Ioc a b, f t ∂μ = ∫ t in Ioo a b, f t ∂μ :=
integral_Ioc_eq_integral_Ioo' $ measure_singleton b

end normed_group

section mono

variables {μ : measure α} {f g : α → ℝ} {s t : set α}
  (hf : integrable_on f s μ) (hg : integrable_on g s μ)

lemma set_integral_mono_ae_restrict (h : f ≤ᵐ[μ.restrict s] g) :
  ∫ a in s, f a ∂μ ≤ ∫ a in s, g a ∂μ :=
integral_mono_ae hf hg h

lemma set_integral_mono_ae (h : f ≤ᵐ[μ] g) :
  ∫ a in s, f a ∂μ ≤ ∫ a in s, g a ∂μ :=
set_integral_mono_ae_restrict hf hg (ae_restrict_of_ae h)

lemma set_integral_mono_on (hs : measurable_set s) (h : ∀ x ∈ s, f x ≤ g x) :
  ∫ a in s, f a ∂μ ≤ ∫ a in s, g a ∂μ :=
set_integral_mono_ae_restrict hf hg
  (by simp [hs, eventually_le, eventually_inf_principal, ae_of_all _ h])

include hf hg  -- why do I need this include, but we don't need it in other lemmas?
lemma set_integral_mono_on_ae (hs : measurable_set s) (h : ∀ᵐ x ∂μ, x ∈ s → f x ≤ g x) :
  ∫ a in s, f a ∂μ ≤ ∫ a in s, g a ∂μ :=
by { refine set_integral_mono_ae_restrict hf hg _, rwa [eventually_le, ae_restrict_iff' hs], }
omit hf hg

lemma set_integral_mono (h : f ≤ g) :
  ∫ a in s, f a ∂μ ≤ ∫ a in s, g a ∂μ :=
integral_mono hf hg h

lemma set_integral_mono_set (hfi : integrable_on f t μ) (hf : 0 ≤ᵐ[μ.restrict t] f)
  (hst : s ≤ᵐ[μ] t) :
  ∫ x in s, f x ∂μ ≤ ∫ x in t, f x ∂μ :=
integral_mono_measure (measure.restrict_mono_ae hst) hf hfi

end mono

section nonneg

variables {μ : measure α} {f : α → ℝ} {s : set α}

lemma set_integral_nonneg_of_ae_restrict (hf : 0 ≤ᵐ[μ.restrict s] f) :
  0 ≤ ∫ a in s, f a ∂μ :=
integral_nonneg_of_ae hf

lemma set_integral_nonneg_of_ae (hf : 0 ≤ᵐ[μ] f) : 0 ≤ ∫ a in s, f a ∂μ :=
set_integral_nonneg_of_ae_restrict (ae_restrict_of_ae hf)

lemma set_integral_nonneg (hs : measurable_set s) (hf : ∀ a, a ∈ s → 0 ≤ f a) :
  0 ≤ ∫ a in s, f a ∂μ :=
set_integral_nonneg_of_ae_restrict ((ae_restrict_iff' hs).mpr (ae_of_all μ hf))

lemma set_integral_nonneg_ae (hs : measurable_set s) (hf : ∀ᵐ a ∂μ, a ∈ s → 0 ≤ f a) :
  0 ≤ ∫ a in s, f a ∂μ :=
set_integral_nonneg_of_ae_restrict $ by rwa [eventually_le, ae_restrict_iff' hs]

lemma set_integral_le_nonneg {s : set α} (hs : measurable_set s) (hf : strongly_measurable f)
  (hfi : integrable f μ) :
  ∫ x in s, f x ∂μ ≤ ∫ x in {y | 0 ≤ f y}, f x ∂μ :=
begin
  rw [← integral_indicator hs,
      ← integral_indicator (strongly_measurable_const.measurable_set_le hf)],
  exact integral_mono (hfi.indicator hs)
    (hfi.indicator (strongly_measurable_const.measurable_set_le hf))
    (indicator_le_indicator_nonneg s f),
end

lemma set_integral_nonpos_of_ae_restrict (hf : f ≤ᵐ[μ.restrict s] 0) :
  ∫ a in s, f a ∂μ ≤ 0 :=
integral_nonpos_of_ae hf

lemma set_integral_nonpos_of_ae (hf : f ≤ᵐ[μ] 0) : ∫ a in s, f a ∂μ ≤ 0 :=
set_integral_nonpos_of_ae_restrict (ae_restrict_of_ae hf)

lemma set_integral_nonpos (hs : measurable_set s) (hf : ∀ a, a ∈ s → f a ≤ 0) :
  ∫ a in s, f a ∂μ ≤ 0 :=
set_integral_nonpos_of_ae_restrict ((ae_restrict_iff' hs).mpr (ae_of_all μ hf))

lemma set_integral_nonpos_ae (hs : measurable_set s) (hf : ∀ᵐ a ∂μ, a ∈ s → f a ≤ 0) :
  ∫ a in s, f a ∂μ ≤ 0 :=
set_integral_nonpos_of_ae_restrict $ by rwa [eventually_le, ae_restrict_iff' hs]

lemma set_integral_nonpos_le {s : set α} (hs : measurable_set s) (hf : strongly_measurable f)
  (hfi : integrable f μ) :
  ∫ x in {y | f y ≤ 0}, f x ∂μ ≤ ∫ x in s, f x ∂μ :=
begin
  rw [← integral_indicator hs,
      ← integral_indicator (hf.measurable_set_le strongly_measurable_const)],
  exact integral_mono (hfi.indicator (hf.measurable_set_le strongly_measurable_const))
    (hfi.indicator hs) (indicator_nonpos_le_indicator s f),
end

end nonneg

section tendsto_mono

variables {μ : measure α} [normed_group E] [complete_space E] [normed_space ℝ E]
  {s : ℕ → set α} {f : α → E}

lemma _root_.antitone.tendsto_set_integral (hsm : ∀ i, measurable_set (s i))
  (h_anti : antitone s) (hfi : integrable_on f (s 0) μ) :
  tendsto (λi, ∫ a in s i, f a ∂μ) at_top (𝓝 (∫ a in (⋂ n, s n), f a ∂μ)) :=
begin
  let bound : α → ℝ := indicator (s 0) (λ a, ∥f a∥),
  have h_int_eq : (λ i, ∫ a in s i, f a ∂μ) = (λ i, ∫ a, (s i).indicator f a ∂μ),
    from funext (λ i, (integral_indicator (hsm i)).symm),
  rw h_int_eq,
  rw ← integral_indicator (measurable_set.Inter hsm),
  refine tendsto_integral_of_dominated_convergence bound _ _ _ _,
  { intro n,
    rw ae_strongly_measurable_indicator_iff (hsm n),
    exact (integrable_on.mono_set hfi (h_anti (zero_le n))).1 },
  { rw integrable_indicator_iff (hsm 0),
    exact hfi.norm, },
  { simp_rw norm_indicator_eq_indicator_norm,
    refine λ n, eventually_of_forall (λ x, _),
    exact indicator_le_indicator_of_subset (h_anti (zero_le n)) (λ a, norm_nonneg _) _ },
  { filter_upwards with a using le_trans (h_anti.tendsto_indicator _ _ _) (pure_le_nhds _), },
end

end tendsto_mono

/-! ### Continuity of the set integral

We prove that for any set `s`, the function `λ f : α →₁[μ] E, ∫ x in s, f x ∂μ` is continuous. -/

section continuous_set_integral
variables [normed_group E] {𝕜 : Type*} [normed_field 𝕜] [normed_group F] [normed_space 𝕜 F]
  {p : ℝ≥0∞} {μ : measure α}

/-- For `f : Lp E p μ`, we can define an element of `Lp E p (μ.restrict s)` by
`(Lp.mem_ℒp f).restrict s).to_Lp f`. This map is additive. -/
lemma Lp_to_Lp_restrict_add (f g : Lp E p μ) (s : set α) :
  ((Lp.mem_ℒp (f + g)).restrict s).to_Lp ⇑(f + g)
    = ((Lp.mem_ℒp f).restrict s).to_Lp f + ((Lp.mem_ℒp g).restrict s).to_Lp g :=
begin
  ext1,
  refine (ae_restrict_of_ae (Lp.coe_fn_add f g)).mp _,
  refine (Lp.coe_fn_add (mem_ℒp.to_Lp f ((Lp.mem_ℒp f).restrict s))
    (mem_ℒp.to_Lp g ((Lp.mem_ℒp g).restrict s))).mp _,
  refine (mem_ℒp.coe_fn_to_Lp ((Lp.mem_ℒp f).restrict s)).mp _,
  refine (mem_ℒp.coe_fn_to_Lp ((Lp.mem_ℒp g).restrict s)).mp _,
  refine (mem_ℒp.coe_fn_to_Lp ((Lp.mem_ℒp (f+g)).restrict s)).mono (λ x hx1 hx2 hx3 hx4 hx5, _),
  rw [hx4, hx1, pi.add_apply, hx2, hx3, hx5, pi.add_apply],
end

/-- For `f : Lp E p μ`, we can define an element of `Lp E p (μ.restrict s)` by
`(Lp.mem_ℒp f).restrict s).to_Lp f`. This map commutes with scalar multiplication. -/
lemma Lp_to_Lp_restrict_smul (c : 𝕜) (f : Lp F p μ) (s : set α) :
  ((Lp.mem_ℒp (c • f)).restrict s).to_Lp ⇑(c • f) = c • (((Lp.mem_ℒp f).restrict s).to_Lp f) :=
begin
  ext1,
  refine (ae_restrict_of_ae (Lp.coe_fn_smul c f)).mp _,
  refine (mem_ℒp.coe_fn_to_Lp ((Lp.mem_ℒp f).restrict s)).mp _,
  refine (mem_ℒp.coe_fn_to_Lp ((Lp.mem_ℒp (c • f)).restrict s)).mp _,
  refine (Lp.coe_fn_smul c (mem_ℒp.to_Lp f ((Lp.mem_ℒp f).restrict s))).mono
    (λ x hx1 hx2 hx3 hx4, _),
  rw [hx2, hx1, pi.smul_apply, hx3, hx4, pi.smul_apply],
end

/-- For `f : Lp E p μ`, we can define an element of `Lp E p (μ.restrict s)` by
`(Lp.mem_ℒp f).restrict s).to_Lp f`. This map is non-expansive. -/
lemma norm_Lp_to_Lp_restrict_le (s : set α) (f : Lp E p μ) :
  ∥((Lp.mem_ℒp f).restrict s).to_Lp f∥ ≤ ∥f∥ :=
begin
  rw [Lp.norm_def, Lp.norm_def, ennreal.to_real_le_to_real (Lp.snorm_ne_top _) (Lp.snorm_ne_top _)],
  refine (le_of_eq _).trans (snorm_mono_measure _ measure.restrict_le_self),
  { exact s, },
  exact snorm_congr_ae (mem_ℒp.coe_fn_to_Lp _),
end

variables (α F 𝕜)
/-- Continuous linear map sending a function of `Lp F p μ` to the same function in
`Lp F p (μ.restrict s)`. -/
def Lp_to_Lp_restrict_clm (μ : measure α) (p : ℝ≥0∞) [hp : fact (1 ≤ p)] (s : set α) :
  Lp F p μ →L[𝕜] Lp F p (μ.restrict s) :=
@linear_map.mk_continuous 𝕜 𝕜 (Lp F p μ) (Lp F p (μ.restrict s)) _ _ _ _ _ _ (ring_hom.id 𝕜)
  ⟨λ f, mem_ℒp.to_Lp f ((Lp.mem_ℒp f).restrict s), λ f g, Lp_to_Lp_restrict_add f g s,
    λ c f, Lp_to_Lp_restrict_smul c f s⟩
  1 (by { intro f, rw one_mul, exact norm_Lp_to_Lp_restrict_le s f, })

variables {α F 𝕜}

variables (𝕜)
lemma Lp_to_Lp_restrict_clm_coe_fn [hp : fact (1 ≤ p)] (s : set α) (f : Lp F p μ) :
  Lp_to_Lp_restrict_clm α F 𝕜 μ p s f =ᵐ[μ.restrict s] f :=
mem_ℒp.coe_fn_to_Lp ((Lp.mem_ℒp f).restrict s)
variables {𝕜}

@[continuity]
lemma continuous_set_integral [normed_space ℝ E] [complete_space E] (s : set α) :
  continuous (λ f : α →₁[μ] E, ∫ x in s, f x ∂μ) :=
begin
  haveI : fact ((1 : ℝ≥0∞) ≤ 1) := ⟨le_rfl⟩,
  have h_comp : (λ f : α →₁[μ] E, ∫ x in s, f x ∂μ)
    = (integral (μ.restrict s)) ∘ (λ f, Lp_to_Lp_restrict_clm α E ℝ μ 1 s f),
  { ext1 f,
    rw [function.comp_apply, integral_congr_ae (Lp_to_Lp_restrict_clm_coe_fn ℝ s f)], },
  rw h_comp,
  exact continuous_integral.comp (Lp_to_Lp_restrict_clm α E ℝ μ 1 s).continuous,
end

end continuous_set_integral

end measure_theory

open measure_theory asymptotics metric

variables {ι : Type*} [normed_group E]

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

Often there is a good formula for `(μ (s i)).to_real`, so the formalization can take an optional
argument `m` with this formula and a proof `of `(λ i, (μ (s i)).to_real) =ᶠ[li] m`. Without these
arguments, `m i = (μ (s i)).to_real` is used in the output. -/
lemma filter.tendsto.integral_sub_linear_is_o_ae
  [normed_space ℝ E] [complete_space E]
  {μ : measure α} {l : filter α} [l.is_measurably_generated]
  {f : α → E} {b : E} (h : tendsto f (l ⊓ μ.ae) (𝓝 b))
  (hfm : strongly_measurable_at_filter f l μ) (hμ : μ.finite_at_filter l)
  {s : ι → set α} {li : filter ι} (hs : tendsto s li l.small_sets)
  (m : ι → ℝ := λ i, (μ (s i)).to_real)
  (hsμ : (λ i, (μ (s i)).to_real) =ᶠ[li] m . tactic.interactive.refl) :
  (λ i, ∫ x in s i, f x ∂μ - m i • b) =o[li] m :=
begin
  suffices : (λ s, ∫ x in s, f x ∂μ - (μ s).to_real • b) =o[l.small_sets] (λ s, (μ s).to_real),
    from (this.comp_tendsto hs).congr' (hsμ.mono $ λ a ha, ha ▸ rfl) hsμ,
  refine is_o_iff.2 (λ ε ε₀, _),
  have : ∀ᶠ s in l.small_sets, ∀ᶠ x in μ.ae, x ∈ s → f x ∈ closed_ball b ε :=
    eventually_small_sets_eventually.2 (h.eventually $ closed_ball_mem_nhds _ ε₀),
  filter_upwards [hμ.eventually, (hμ.integrable_at_filter_of_tendsto_ae hfm h).eventually,
    hfm.eventually, this],
  simp only [mem_closed_ball, dist_eq_norm],
  intros s hμs h_integrable hfm h_norm,
  rw [← set_integral_const, ← integral_sub h_integrable (integrable_on_const.2 $ or.inr hμs),
    real.norm_eq_abs, abs_of_nonneg ennreal.to_real_nonneg],
  exact norm_set_integral_le_of_norm_le_const_ae' hμs h_norm (hfm.sub ae_strongly_measurable_const)
end

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

Often there is a good formula for `(μ (s i)).to_real`, so the formalization can take an optional
argument `m` with this formula and a proof `of `(λ i, (μ (s i)).to_real) =ᶠ[li] m`. Without these
arguments, `m i = (μ (s i)).to_real` is used in the output. -/
lemma continuous_within_at.integral_sub_linear_is_o_ae
  [topological_space α] [opens_measurable_space α]
  [normed_space ℝ E] [complete_space E]
  {μ : measure α} [is_locally_finite_measure μ] {a : α} {t : set α}
  {f : α → E} (ha : continuous_within_at f t a) (ht : measurable_set t)
  (hfm : strongly_measurable_at_filter f (𝓝[t] a) μ)
  {s : ι → set α} {li : filter ι} (hs : tendsto s li (𝓝[t] a).small_sets)
  (m : ι → ℝ := λ i, (μ (s i)).to_real)
  (hsμ : (λ i, (μ (s i)).to_real) =ᶠ[li] m . tactic.interactive.refl) :
  (λ i, ∫ x in s i, f x ∂μ - m i • f a) =o[li] m :=
by haveI : (𝓝[t] a).is_measurably_generated := ht.nhds_within_is_measurably_generated _;
exact (ha.mono_left inf_le_left).integral_sub_linear_is_o_ae
  hfm (μ.finite_at_nhds_within a t) hs m hsμ

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

Often there is a good formula for `(μ (s i)).to_real`, so the formalization can take an optional
argument `m` with this formula and a proof `of `(λ i, (μ (s i)).to_real) =ᶠ[li] m`. Without these
arguments, `m i = (μ (s i)).to_real` is used in the output. -/
lemma continuous_at.integral_sub_linear_is_o_ae
  [topological_space α] [opens_measurable_space α]
  [normed_space ℝ E] [complete_space E]
  {μ : measure α} [is_locally_finite_measure μ] {a : α}
  {f : α → E} (ha : continuous_at f a) (hfm : strongly_measurable_at_filter f (𝓝 a) μ)
  {s : ι → set α} {li : filter ι} (hs : tendsto s li (𝓝 a).small_sets)
  (m : ι → ℝ := λ i, (μ (s i)).to_real)
  (hsμ : (λ i, (μ (s i)).to_real) =ᶠ[li] m . tactic.interactive.refl) :
  (λ i, ∫ x in s i, f x ∂μ - m i • f a) =o[li] m :=
(ha.mono_left inf_le_left).integral_sub_linear_is_o_ae hfm (μ.finite_at_nhds a) hs m hsμ

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

Often there is a good formula for `(μ (s i)).to_real`, so the formalization can take an optional
argument `m` with this formula and a proof `of `(λ i, (μ (s i)).to_real) =ᶠ[li] m`. Without these
arguments, `m i = (μ (s i)).to_real` is used in the output. -/
lemma continuous_on.integral_sub_linear_is_o_ae
  [topological_space α] [opens_measurable_space α]
  [normed_space ℝ E] [complete_space E] [second_countable_topology_either α E]
  {μ : measure α} [is_locally_finite_measure μ] {a : α} {t : set α}
  {f : α → E} (hft : continuous_on f t) (ha : a ∈ t) (ht : measurable_set t)
  {s : ι → set α} {li : filter ι} (hs : tendsto s li (𝓝[t] a).small_sets)
  (m : ι → ℝ := λ i, (μ (s i)).to_real)
  (hsμ : (λ i, (μ (s i)).to_real) =ᶠ[li] m . tactic.interactive.refl) :
  (λ i, ∫ x in s i, f x ∂μ - m i • f a) =o[li] m :=
(hft a ha).integral_sub_linear_is_o_ae ht
  ⟨t, self_mem_nhds_within, hft.ae_strongly_measurable ht⟩ hs m hsμ

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

The goal of this section is to prove that integration commutes with continuous linear maps.
This holds for simple functions. The general result follows from the continuity of all involved
operations on the space `L¹`. Note that composition by a continuous linear map on `L¹` is not just
the composition, as we are dealing with classes of functions, but it has already been defined
as `continuous_linear_map.comp_Lp`. We take advantage of this construction here.
-/

open_locale complex_conjugate

variables {μ : measure α} {𝕜 : Type*} [is_R_or_C 𝕜] [normed_space 𝕜 E]
  [normed_group F] [normed_space 𝕜 F]
  {p : ennreal}

namespace continuous_linear_map

variables [complete_space F] [normed_space ℝ F]

lemma integral_comp_Lp (L : E →L[𝕜] F) (φ : Lp E p μ) :
  ∫ a, (L.comp_Lp φ) a ∂μ = ∫ a, L (φ a) ∂μ :=
integral_congr_ae $ coe_fn_comp_Lp _ _

lemma set_integral_comp_Lp (L : E →L[𝕜] F) (φ : Lp E p μ) {s : set α} (hs : measurable_set s) :
  ∫ a in s, (L.comp_Lp φ) a ∂μ = ∫ a in s, L (φ a) ∂μ :=
set_integral_congr_ae hs ((L.coe_fn_comp_Lp φ).mono (λ x hx hx2, hx))

lemma continuous_integral_comp_L1 (L : E →L[𝕜] F) :
  continuous (λ (φ : α →₁[μ] E), ∫ (a : α), L (φ a) ∂μ) :=
by { rw ← funext L.integral_comp_Lp, exact continuous_integral.comp (L.comp_LpL 1 μ).continuous, }

variables [complete_space E] [normed_space ℝ E]

lemma integral_comp_comm (L : E →L[𝕜] F) {φ : α → E} (φ_int : integrable φ μ) :
  ∫ a, L (φ a) ∂μ = L (∫ a, φ a ∂μ) :=
begin
  apply integrable.induction (λ φ, ∫ a, L (φ a) ∂μ = L (∫ a, φ a ∂μ)),
  { intros e s s_meas s_finite,
    rw [integral_indicator_const e s_meas, ← @smul_one_smul E ℝ 𝕜 _ _ _ _ _ (μ s).to_real e,
      continuous_linear_map.map_smul, @smul_one_smul F ℝ 𝕜 _ _ _ _ _ (μ s).to_real (L e),
      ← integral_indicator_const (L e) s_meas],
    congr' 1 with a,
    rw set.indicator_comp_of_zero L.map_zero },
  { intros f g H f_int g_int hf hg,
    simp [L.map_add, integral_add f_int g_int,
      integral_add (L.integrable_comp f_int) (L.integrable_comp g_int), hf, hg] },
  { exact is_closed_eq L.continuous_integral_comp_L1 (L.continuous.comp continuous_integral) },
  { intros f g hfg f_int hf,
    convert hf using 1 ; clear hf,
    { exact integral_congr_ae (hfg.fun_comp L).symm },
    { rw integral_congr_ae hfg.symm } },
  all_goals { assumption }
end

lemma integral_apply {H : Type*} [normed_group H] [normed_space 𝕜 H]
  {φ : α → H →L[𝕜] E} (φ_int : integrable φ μ) (v : H) :
  (∫ a, φ a ∂μ) v = ∫ a, φ a v ∂μ :=
((continuous_linear_map.apply 𝕜 E v).integral_comp_comm φ_int).symm

lemma integral_comp_comm' (L : E →L[𝕜] F) {K} (hL : antilipschitz_with K L) (φ : α → E) :
  ∫ a, L (φ a) ∂μ = L (∫ a, φ a ∂μ) :=
begin
  by_cases h : integrable φ μ,
  { exact integral_comp_comm L h },
  have : ¬ (integrable (L ∘ φ) μ),
    by rwa lipschitz_with.integrable_comp_iff_of_antilipschitz L.lipschitz hL (L.map_zero),
  simp [integral_undef, h, this]
end

lemma integral_comp_L1_comm (L : E →L[𝕜] F) (φ : α →₁[μ] E) : ∫ a, L (φ a) ∂μ = L (∫ a, φ a ∂μ) :=
L.integral_comp_comm (L1.integrable_coe_fn φ)

end continuous_linear_map

namespace linear_isometry

variables [complete_space F] [normed_space ℝ F] [complete_space E] [normed_space ℝ E]

lemma integral_comp_comm (L : E →ₗᵢ[𝕜] F) (φ : α → E) : ∫ a, L (φ a) ∂μ = L (∫ a, φ a ∂μ) :=
L.to_continuous_linear_map.integral_comp_comm' L.antilipschitz _

end linear_isometry

variables [complete_space E] [normed_space ℝ E] [complete_space F] [normed_space ℝ F]

@[norm_cast] lemma integral_of_real {f : α → ℝ} : ∫ a, (f a : 𝕜) ∂μ = ↑∫ a, f a ∂μ :=
(@is_R_or_C.of_real_li 𝕜 _).integral_comp_comm f

lemma integral_re {f : α → 𝕜} (hf : integrable f μ) :
  ∫ a, is_R_or_C.re (f a) ∂μ = is_R_or_C.re ∫ a, f a ∂μ :=
(@is_R_or_C.re_clm 𝕜 _).integral_comp_comm hf

lemma integral_im {f : α → 𝕜} (hf : integrable f μ) :
  ∫ a, is_R_or_C.im (f a) ∂μ = is_R_or_C.im ∫ a, f a ∂μ :=
(@is_R_or_C.im_clm 𝕜 _).integral_comp_comm hf

lemma integral_conj {f : α → 𝕜} : ∫ a, conj (f a) ∂μ = conj ∫ a, f a ∂μ :=
(@is_R_or_C.conj_lie 𝕜 _).to_linear_isometry.integral_comp_comm f

lemma integral_coe_re_add_coe_im {f : α → 𝕜} (hf : integrable f μ) :
  ∫ x, (is_R_or_C.re (f x) : 𝕜) ∂μ + ∫ x, is_R_or_C.im (f x) ∂μ * is_R_or_C.I = ∫ x, f x ∂μ :=
begin
  rw [mul_comm, ← smul_eq_mul, ← integral_smul, ← integral_add],
  { congr,
    ext1 x,
    rw [smul_eq_mul, mul_comm, is_R_or_C.re_add_im] },
  { exact hf.re.of_real },
  { exact hf.im.of_real.smul is_R_or_C.I }
end

lemma integral_re_add_im {f : α → 𝕜} (hf : integrable f μ) :
  ((∫ x, is_R_or_C.re (f x) ∂μ : ℝ) : 𝕜) + (∫ x, is_R_or_C.im (f x) ∂μ : ℝ) * is_R_or_C.I =
  ∫ x, f x ∂μ :=
by { rw [← integral_of_real, ← integral_of_real, integral_coe_re_add_coe_im hf] }

lemma set_integral_re_add_im {f : α → 𝕜} {i : set α} (hf : integrable_on f i μ) :
  ((∫ x in i, is_R_or_C.re (f x) ∂μ : ℝ) : 𝕜) +
  (∫ x in i, is_R_or_C.im (f x) ∂μ : ℝ) * is_R_or_C.I = ∫ x in i, f x ∂μ :=
integral_re_add_im hf

lemma fst_integral {f : α → E × F} (hf : integrable f μ) :
  (∫ x, f x ∂μ).1 = ∫ x, (f x).1 ∂μ :=
((continuous_linear_map.fst ℝ E F).integral_comp_comm hf).symm

lemma snd_integral {f : α → E × F} (hf : integrable f μ) :
  (∫ x, f x ∂μ).2 = ∫ x, (f x).2 ∂μ :=
((continuous_linear_map.snd ℝ E F).integral_comp_comm hf).symm

lemma integral_pair {f : α → E} {g : α → F} (hf : integrable f μ) (hg : integrable g μ) :
  ∫ x, (f x, g x) ∂μ = (∫ x, f x ∂μ, ∫ x, g x ∂μ) :=
have _ := hf.prod_mk hg, prod.ext (fst_integral this) (snd_integral this)

lemma integral_smul_const {𝕜 : Type*} [is_R_or_C 𝕜] [normed_space 𝕜 E] (f : α → 𝕜) (c : E) :
  ∫ x, f x • c ∂μ = (∫ x, f x ∂μ) • c :=
begin
  by_cases hf : integrable f μ,
  { exact ((1 : 𝕜 →L[𝕜] 𝕜).smul_right c).integral_comp_comm hf },
  { by_cases hc : c = 0,
    { simp only [hc, integral_zero, smul_zero] },
    rw [integral_undef hf, integral_undef, zero_smul],
    simp_rw [integrable_smul_const hc, hf, not_false_iff] }
end

section inner

variables {E' : Type*} [inner_product_space 𝕜 E'] [complete_space E'] [normed_space ℝ E']

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

lemma integral_inner {f : α → E'} (hf : integrable f μ) (c : E') :
  ∫ x, ⟪c, f x⟫ ∂μ = ⟪c, ∫ x, f x ∂μ⟫ :=
((@innerSL 𝕜 E' _ _ c).restrict_scalars ℝ).integral_comp_comm hf

lemma integral_eq_zero_of_forall_integral_inner_eq_zero (f : α → E') (hf : integrable f μ)
  (hf_int : ∀ (c : E'), ∫ x, ⟪c, f x⟫ ∂μ = 0) :
  ∫ x, f x ∂μ = 0 :=
by { specialize hf_int (∫ x, f x ∂μ), rwa [integral_inner hf, inner_self_eq_zero] at hf_int }

end inner

lemma integral_with_density_eq_integral_smul
  {f : α → ℝ≥0} (f_meas : measurable f) (g : α → E) :
  ∫ a, g a ∂(μ.with_density (λ x, f x)) = ∫ a, f a • g a ∂μ :=
begin
  by_cases hg : integrable g (μ.with_density (λ x, f x)), swap,
  { rw [integral_undef hg, integral_undef],
    rwa [← integrable_with_density_iff_integrable_smul f_meas];
    apply_instance },
  refine integrable.induction _ _ _ _ _ hg,
  { assume c s s_meas hs,
    rw integral_indicator s_meas,
    simp_rw [← indicator_smul_apply, integral_indicator s_meas],
    simp only [s_meas, integral_const, measure.restrict_apply', univ_inter, with_density_apply],
    rw [lintegral_coe_eq_integral, ennreal.to_real_of_real, ← integral_smul_const],
    { refl },
    { exact integral_nonneg (λ x, nnreal.coe_nonneg _) },
    { refine ⟨(f_meas.coe_nnreal_real).ae_measurable.ae_strongly_measurable, _⟩,
      rw with_density_apply _ s_meas at hs,
      rw has_finite_integral,
      convert hs,
      ext1 x,
      simp only [nnreal.nnnorm_eq] } },
  { assume u u' h_disj u_int u'_int h h',
    change ∫ (a : α), (u a + u' a) ∂μ.with_density (λ (x : α), ↑(f x)) =
      ∫ (a : α), f a • (u a + u' a) ∂μ,
    simp_rw [smul_add],
    rw [integral_add u_int u'_int, h, h', integral_add],
    { exact (integrable_with_density_iff_integrable_smul f_meas).1 u_int },
    { exact (integrable_with_density_iff_integrable_smul f_meas).1 u'_int } },
  { have C1 : continuous (λ (u : Lp E 1 (μ.with_density (λ x, f x))),
      ∫ x, u x ∂(μ.with_density (λ x, f x))) := continuous_integral,
    have C2 : continuous (λ (u : Lp E 1 (μ.with_density (λ x, f x))),
      ∫ x, f x • u x ∂μ),
    { have : continuous ((λ (u : Lp E 1 μ), ∫ x, u x ∂μ) ∘ (with_density_smul_li μ f_meas)) :=
        continuous_integral.comp (with_density_smul_li μ f_meas).continuous,
      convert this,
      ext1 u,
      simp only [function.comp_app, with_density_smul_li_apply],
      exact integral_congr_ae (mem_ℒ1_smul_of_L1_with_density f_meas u).coe_fn_to_Lp.symm },
    exact is_closed_eq C1 C2 },
  { assume u v huv u_int hu,
    rw [← integral_congr_ae huv, hu],
    apply integral_congr_ae,
    filter_upwards [(ae_with_density_iff f_meas.coe_nnreal_ennreal).1 huv] with x hx,
    rcases eq_or_ne (f x) 0 with h'x|h'x,
    { simp only [h'x, zero_smul]},
    { rw [hx _],
      simpa only [ne.def, ennreal.coe_eq_zero] using h'x } }
end

lemma integral_with_density_eq_integral_smul₀
  {f : α → ℝ≥0} (hf : ae_measurable f μ) (g : α → E) :
  ∫ a, g a ∂(μ.with_density (λ x, f x)) = ∫ a, f a • g a ∂μ :=
begin
  let f' := hf.mk _,
  calc ∫ a, g a ∂(μ.with_density (λ x, f x))
      = ∫ a, g a ∂(μ.with_density (λ x, f' x)) :
  begin
    congr' 1,
    apply with_density_congr_ae,
    filter_upwards [hf.ae_eq_mk] with x hx,
    rw hx,
  end
  ... = ∫ a, f' a • g a ∂μ : integral_with_density_eq_integral_smul hf.measurable_mk _
  ... = ∫ a, f a • g a ∂μ :
  begin
    apply integral_congr_ae,
    filter_upwards [hf.ae_eq_mk] with x hx,
    rw hx,
  end
end

lemma set_integral_with_density_eq_set_integral_smul
  {f : α → ℝ≥0} (f_meas : measurable f) (g : α → E) {s : set α} (hs : measurable_set s) :
  ∫ a in s, g a ∂(μ.with_density (λ x, f x)) = ∫ a in s, f a • g a ∂μ :=
by rw [restrict_with_density hs, integral_with_density_eq_integral_smul f_meas]

lemma set_integral_with_density_eq_set_integral_smul₀ {f : α → ℝ≥0} {s : set α}
  (hf : ae_measurable f (μ.restrict s)) (g : α → E) (hs : measurable_set s) :
  ∫ a in s, g a ∂(μ.with_density (λ x, f x)) = ∫ a in s, f a • g a ∂μ :=
by rw [restrict_with_density hs, integral_with_density_eq_integral_smul₀ hf]

end

section thickened_indicator

variables [pseudo_emetric_space α]

lemma measure_le_lintegral_thickened_indicator_aux
  (μ : measure α) {E : set α} (E_mble : measurable_set E) (δ : ℝ) :
  μ E ≤ ∫⁻ a, (thickened_indicator_aux δ E a : ℝ≥0∞) ∂μ :=
begin
  convert_to lintegral μ (E.indicator (λ _, (1 : ℝ≥0∞)))
              ≤ lintegral μ (thickened_indicator_aux δ E),
  { rw [lintegral_indicator _ E_mble],
    simp only [lintegral_one, measure.restrict_apply, measurable_set.univ, univ_inter], },
  { apply lintegral_mono,
    apply indicator_le_thickened_indicator_aux, },
end

lemma measure_le_lintegral_thickened_indicator
  (μ : measure α) {E : set α} (E_mble : measurable_set E) {δ : ℝ} (δ_pos : 0 < δ) :
  μ E ≤ ∫⁻ a, (thickened_indicator δ_pos E a : ℝ≥0∞) ∂μ :=
begin
  convert measure_le_lintegral_thickened_indicator_aux μ E_mble δ,
  dsimp,
  simp only [thickened_indicator_aux_lt_top.ne, ennreal.coe_to_nnreal, ne.def, not_false_iff],
end

end thickened_indicator