/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Mario Carneiro

! This file was ported from Lean 3 source module topology.algebra.monoid
! leanprover-community/mathlib commit 6efec6bb9fcaed3cf1baaddb2eaadd8a2a06679c
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Algebra.BigOperators.Finprod
import Mathlib.Order.Filter.Pointwise
import Mathlib.Topology.Algebra.MulAction
import Mathlib.Algebra.BigOperators.Pi
import Mathlib.Topology.ContinuousFunction.Basic

/-!
# Theory of topological monoids

In this file we define mixin classes `ContinuousMul` and `ContinuousAdd`. While in many
applications the underlying type is a monoid (multiplicative or additive), we do not require this in
the definitions.
-/

universe u v

open Classical Set Filter TopologicalSpace

open Classical Topology BigOperators Pointwise

variable {ι α X M N : Type _} [TopologicalSpace X]

@[to_additive (attr := continuity)]
theorem continuous_one [TopologicalSpace M] [One M] : Continuous (1 : X → M) :=
  @continuous_const _ _ _ _ 1
#align continuous_one continuous_one
#align continuous_zero continuous_zero

/-- Basic hypothesis to talk about a topological additive monoid or a topological additive
semigroup. A topological additive monoid over `M`, for example, is obtained by requiring both the
instances `AddMonoid M` and `ContinuousAdd M`.

Continuity in only the left/right argument can be stated using
`ContinuousConstVAdd α α`/`ContinuousConstVAdd αᵐᵒᵖ α`. -/
class ContinuousAdd (M : Type u) [TopologicalSpace M] [Add M] : Prop where
  continuous_add : Continuous fun p : M × M => p.1 + p.2
#align has_continuous_add ContinuousAdd

/-- Basic hypothesis to talk about a topological monoid or a topological semigroup.
A topological monoid over `M`, for example, is obtained by requiring both the instances `Monoid M`
and `ContinuousMul M`.

Continuity in only the left/right argument can be stated using
`ContinuousConstSMul α α`/`ContinuousConstSMul αᵐᵒᵖ α`. -/
@[to_additive]
class ContinuousMul (M : Type u) [TopologicalSpace M] [Mul M] : Prop where
  continuous_mul : Continuous fun p : M × M => p.1 * p.2
#align has_continuous_mul ContinuousMul
-- #align has_continuous_add ContinuousAdd

section ContinuousMul

variable [TopologicalSpace M] [Mul M] [ContinuousMul M]

@[to_additive]
instance : ContinuousMul Mᵒᵈ :=
  ‹ContinuousMul M›

@[to_additive (attr := continuity)]
theorem continuous_mul : Continuous fun p : M × M => p.1 * p.2 :=
  ContinuousMul.continuous_mul
#align continuous_mul continuous_mul
#align continuous_add continuous_add

@[to_additive]
instance ContinuousMul.to_continuousSMul : ContinuousSMul M M :=
  ⟨continuous_mul⟩
#align has_continuous_mul.to_has_continuous_smul ContinuousMul.to_continuousSMul
#align has_continuous_add.to_has_continuous_vadd ContinuousAdd.to_continuousVAdd

@[to_additive]
instance ContinuousMul.to_continuousSMul_op : ContinuousSMul Mᵐᵒᵖ M :=
  ⟨show Continuous ((fun p : M × M => p.1 * p.2) ∘ Prod.swap ∘ Prod.map MulOpposite.unop id) from
      continuous_mul.comp <|
        continuous_swap.comp <| Continuous.prod_map MulOpposite.continuous_unop continuous_id⟩
#align has_continuous_mul.to_has_continuous_smul_op ContinuousMul.to_continuousSMul_op
#align has_continuous_add.to_has_continuous_vadd_op ContinuousAdd.to_continuousVAdd_op

@[to_additive (attr := continuity)]
theorem Continuous.mul {f g : X → M} (hf : Continuous f) (hg : Continuous g) :
    Continuous fun x => f x * g x :=
  continuous_mul.comp (hf.prod_mk hg : _)
#align continuous.mul Continuous.mul
#align continuous.add Continuous.add

@[to_additive (attr := continuity)]
theorem continuous_mul_left (a : M) : Continuous fun b : M => a * b :=
  continuous_const.mul continuous_id
#align continuous_mul_left continuous_mul_left
#align continuous_add_left continuous_add_left

@[to_additive (attr := continuity)]
theorem continuous_mul_right (a : M) : Continuous fun b : M => b * a :=
  continuous_id.mul continuous_const
#align continuous_mul_right continuous_mul_right
#align continuous_add_right continuous_add_right

@[to_additive]
theorem ContinuousOn.mul {f g : X → M} {s : Set X} (hf : ContinuousOn f s) (hg : ContinuousOn g s) :
    ContinuousOn (fun x => f x * g x) s :=
  (continuous_mul.comp_continuousOn (hf.prod hg) : _)
#align continuous_on.mul ContinuousOn.mul
#align continuous_on.add ContinuousOn.add

@[to_additive]
theorem tendsto_mul {a b : M} : Tendsto (fun p : M × M => p.fst * p.snd) (𝓝 (a, b)) (𝓝 (a * b)) :=
  continuous_iff_continuousAt.mp ContinuousMul.continuous_mul (a, b)
#align tendsto_mul tendsto_mul
#align tendsto_add tendsto_add

@[to_additive]
theorem Filter.Tendsto.mul {f g : α → M} {x : Filter α} {a b : M} (hf : Tendsto f x (𝓝 a))
    (hg : Tendsto g x (𝓝 b)) : Tendsto (fun x => f x * g x) x (𝓝 (a * b)) :=
  tendsto_mul.comp (hf.prod_mk_nhds hg)
#align filter.tendsto.mul Filter.Tendsto.mul
#align filter.tendsto.add Filter.Tendsto.add

@[to_additive]
theorem Filter.Tendsto.const_mul (b : M) {c : M} {f : α → M} {l : Filter α}
    (h : Tendsto (fun k : α => f k) l (𝓝 c)) : Tendsto (fun k : α => b * f k) l (𝓝 (b * c)) :=
  tendsto_const_nhds.mul h
#align filter.tendsto.const_mul Filter.Tendsto.const_mul
#align filter.tendsto.const_add Filter.Tendsto.const_add

@[to_additive]
theorem Filter.Tendsto.mul_const (b : M) {c : M} {f : α → M} {l : Filter α}
    (h : Tendsto (fun k : α => f k) l (𝓝 c)) : Tendsto (fun k : α => f k * b) l (𝓝 (c * b)) :=
  h.mul tendsto_const_nhds
#align filter.tendsto.mul_const Filter.Tendsto.mul_const
#align filter.tendsto.add_const Filter.Tendsto.add_const

@[to_additive]
theorem le_nhds_mul (a b : M) : 𝓝 a * 𝓝 b ≤ 𝓝 (a * b) := by
  rw [← map₂_mul, ← map_uncurry_prod, ← nhds_prod_eq]
  exact continuous_mul.tendsto _
#align le_nhds_mul le_nhds_mul
#align le_nhds_add le_nhds_add

@[to_additive (attr := simp)]
theorem nhds_one_mul_nhds {M} [MulOneClass M] [TopologicalSpace M] [ContinuousMul M] (a : M) :
    𝓝 (1 : M) * 𝓝 a = 𝓝 a :=
  ((le_nhds_mul _ _).trans_eq <| congr_arg _ (one_mul a)).antisymm <|
    le_mul_of_one_le_left' <| pure_le_nhds 1
#align nhds_one_mul_nhds nhds_one_mul_nhds
#align nhds_zero_add_nhds nhds_zero_add_nhds

@[to_additive (attr := simp)]
theorem nhds_mul_nhds_one {M} [MulOneClass M] [TopologicalSpace M] [ContinuousMul M] (a : M) :
    𝓝 a * 𝓝 1 = 𝓝 a :=
  ((le_nhds_mul _ _).trans_eq <| congr_arg _ (mul_one a)).antisymm <|
    le_mul_of_one_le_right' <| pure_le_nhds 1
#align nhds_mul_nhds_one nhds_mul_nhds_one
#align nhds_add_nhds_zero nhds_add_nhds_zero

section tendsto_nhds

variable {𝕜 : Type _} [Preorder 𝕜] [Zero 𝕜] [Mul 𝕜] [TopologicalSpace 𝕜] [ContinuousMul 𝕜]
  {l : Filter α} {f : α → 𝕜} {b c : 𝕜} (hb : 0 < b)

theorem Filter.TendstoNhdsWithinIoi.const_mul [PosMulStrictMono 𝕜] [PosMulReflectLT 𝕜]
    (h : Tendsto f l (𝓝[>] c)) : Tendsto (fun a => b * f a) l (𝓝[>] (b * c)) :=
  tendsto_nhdsWithin_of_tendsto_nhds_of_eventually_within _
      ((tendsto_nhds_of_tendsto_nhdsWithin h).const_mul b) <|
    (tendsto_nhdsWithin_iff.mp h).2.mono fun _ => (mul_lt_mul_left hb).mpr
#align filter.tendsto_nhds_within_Ioi.const_mul Filter.TendstoNhdsWithinIoi.const_mul

theorem Filter.TendstoNhdsWithinIio.const_mul [PosMulStrictMono 𝕜] [PosMulReflectLT 𝕜]
    (h : Tendsto f l (𝓝[<] c)) : Tendsto (fun a => b * f a) l (𝓝[<] (b * c)) :=
  tendsto_nhdsWithin_of_tendsto_nhds_of_eventually_within _
      ((tendsto_nhds_of_tendsto_nhdsWithin h).const_mul b) <|
    (tendsto_nhdsWithin_iff.mp h).2.mono fun _ => (mul_lt_mul_left hb).mpr
#align filter.tendsto_nhds_within_Iio.const_mul Filter.TendstoNhdsWithinIio.const_mul

theorem Filter.TendstoNhdsWithinIoi.mul_const [MulPosStrictMono 𝕜] [MulPosReflectLT 𝕜]
    (h : Tendsto f l (𝓝[>] c)) : Tendsto (fun a => f a * b) l (𝓝[>] (c * b)) :=
  tendsto_nhdsWithin_of_tendsto_nhds_of_eventually_within _
      ((tendsto_nhds_of_tendsto_nhdsWithin h).mul_const b) <|
    (tendsto_nhdsWithin_iff.mp h).2.mono fun _ => (mul_lt_mul_right hb).mpr
#align filter.tendsto_nhds_within_Ioi.mul_const Filter.TendstoNhdsWithinIoi.mul_const

theorem Filter.TendstoNhdsWithinIio.mul_const [MulPosStrictMono 𝕜] [MulPosReflectLT 𝕜]
    (h : Tendsto f l (𝓝[<] c)) : Tendsto (fun a => f a * b) l (𝓝[<] (c * b)) :=
  tendsto_nhdsWithin_of_tendsto_nhds_of_eventually_within _
      ((tendsto_nhds_of_tendsto_nhdsWithin h).mul_const b) <|
    (tendsto_nhdsWithin_iff.mp h).2.mono fun _ => (mul_lt_mul_right hb).mpr
#align filter.tendsto_nhds_within_Iio.mul_const Filter.TendstoNhdsWithinIio.mul_const

end tendsto_nhds

/-- Construct a unit from limits of units and their inverses. -/
@[to_additive (attr := simps)
  "Construct an additive unit from limits of additive units and their negatives."]
def Filter.Tendsto.units [TopologicalSpace N] [Monoid N] [ContinuousMul N] [T2Space N]
    {f : ι → Nˣ} {r₁ r₂ : N} {l : Filter ι} [l.NeBot] (h₁ : Tendsto (fun x => ↑(f x)) l (𝓝 r₁))
    (h₂ : Tendsto (fun x => ↑(f x)⁻¹) l (𝓝 r₂)) : Nˣ
    where
  val := r₁
  inv := r₂
  val_inv := by
    symm
    simpa using h₁.mul h₂
  inv_val := by
    symm
    simpa using h₂.mul h₁
#align filter.tendsto.units Filter.Tendsto.units
#align filter.tendsto.add_units Filter.Tendsto.addUnits

@[to_additive]
theorem ContinuousAt.mul {f g : X → M} {x : X} (hf : ContinuousAt f x) (hg : ContinuousAt g x) :
    ContinuousAt (fun x => f x * g x) x :=
  Filter.Tendsto.mul hf hg
#align continuous_at.mul ContinuousAt.mul
#align continuous_at.add ContinuousAt.add

@[to_additive]
theorem ContinuousWithinAt.mul {f g : X → M} {s : Set X} {x : X} (hf : ContinuousWithinAt f s x)
    (hg : ContinuousWithinAt g s x) : ContinuousWithinAt (fun x => f x * g x) s x :=
  Filter.Tendsto.mul hf hg
#align continuous_within_at.mul ContinuousWithinAt.mul
#align continuous_within_at.add ContinuousWithinAt.add

@[to_additive]
instance [TopologicalSpace N] [Mul N] [ContinuousMul N] : ContinuousMul (M × N) :=
  ⟨(continuous_fst.fst'.mul continuous_fst.snd').prod_mk
      (continuous_snd.fst'.mul continuous_snd.snd')⟩

@[to_additive]
instance Pi.continuousMul {C : ι → Type _} [∀ i, TopologicalSpace (C i)] [∀ i, Mul (C i)]
    [∀ i, ContinuousMul (C i)] : ContinuousMul (∀ i, C i)
    where continuous_mul :=
    continuous_pi fun i => (continuous_apply i).fst'.mul (continuous_apply i).snd'
#align pi.has_continuous_mul Pi.continuousMul
#align pi.has_continuous_add Pi.continuousAdd

/-- A version of `pi.continuousMul` for non-dependent functions. It is needed because sometimes
Lean 3 fails to use `pi.continuousMul` for non-dependent functions. -/
@[to_additive "A version of `pi.continuousAdd` for non-dependent functions. It is needed
because sometimes Lean fails to use `pi.continuousAdd` for non-dependent functions."]
instance Pi.continuousMul' : ContinuousMul (ι → M) :=
  Pi.continuousMul
#align pi.has_continuous_mul' Pi.continuousMul'
#align pi.has_continuous_add' Pi.continuousAdd'

@[to_additive]
instance (priority := 100) continuousMul_of_discreteTopology [TopologicalSpace N] [Mul N]
    [DiscreteTopology N] : ContinuousMul N :=
  ⟨continuous_of_discreteTopology⟩
#align has_continuous_mul_of_discrete_topology continuousMul_of_discreteTopology
#align has_continuous_add_of_discrete_topology continuousAdd_of_discreteTopology

open Filter

open Function

@[to_additive]
theorem ContinuousMul.of_nhds_one {M : Type u} [Monoid M] [TopologicalSpace M]
    (hmul : Tendsto (uncurry ((· * ·) : M → M → M)) (𝓝 1 ×ᶠ 𝓝 1) <| 𝓝 1)
    (hleft : ∀ x₀ : M, 𝓝 x₀ = map (fun x => x₀ * x) (𝓝 1))
    (hright : ∀ x₀ : M, 𝓝 x₀ = map (fun x => x * x₀) (𝓝 1)) : ContinuousMul M :=
  ⟨by
    rw [continuous_iff_continuousAt]
    rintro ⟨x₀, y₀⟩
    have key :
      (fun p : M × M => x₀ * p.1 * (p.2 * y₀)) =
        ((fun x => x₀ * x) ∘ fun x => x * y₀) ∘ uncurry (· * ·) :=
      by
      ext p
      simp [uncurry, mul_assoc]
    have key₂ : ((fun x => x₀ * x) ∘ fun x => y₀ * x) = fun x => x₀ * y₀ * x :=
      by
      ext x
      simp [mul_assoc]
    calc
      map (uncurry (· * ·)) (𝓝 (x₀, y₀)) = map (uncurry (· * ·)) (𝓝 x₀ ×ᶠ 𝓝 y₀) :=
        by rw [nhds_prod_eq]
      _ = map (fun p : M × M => x₀ * p.1 * (p.2 * y₀)) (𝓝 1 ×ᶠ 𝓝 1) :=
        by simp_rw [uncurry, hleft x₀, hright y₀, prod_map_map_eq, Filter.map_map, Function.comp]
        -- Porting note: `rw` was able to prove this
      _ = map ((fun x => x₀ * x) ∘ fun x => x * y₀) (map (uncurry (· * ·)) (𝓝 1 ×ᶠ 𝓝 1)) :=
        by rw [key, ← Filter.map_map]
      _ ≤ map ((fun x : M => x₀ * x) ∘ fun x => x * y₀) (𝓝 1) := map_mono hmul
      _ = 𝓝 (x₀ * y₀) := by rw [← Filter.map_map, ← hright, hleft y₀, Filter.map_map, key₂, ← hleft]
      ⟩
#align has_continuous_mul.of_nhds_one ContinuousMul.of_nhds_one
#align has_continuous_add.of_nhds_zero ContinuousAdd.of_nhds_zero

@[to_additive]
theorem continuousMul_of_comm_of_nhds_one (M : Type u) [CommMonoid M] [TopologicalSpace M]
    (hmul : Tendsto (uncurry ((· * ·) : M → M → M)) (𝓝 1 ×ᶠ 𝓝 1) (𝓝 1))
    (hleft : ∀ x₀ : M, 𝓝 x₀ = map (fun x => x₀ * x) (𝓝 1)) : ContinuousMul M := by
  apply ContinuousMul.of_nhds_one hmul hleft
  intro x₀
  simp_rw [mul_comm, hleft x₀]
#align has_continuous_mul_of_comm_of_nhds_one continuousMul_of_comm_of_nhds_one
#align has_continuous_add_of_comm_of_nhds_zero continuousAdd_of_comm_of_nhds_zero

end ContinuousMul

section PointwiseLimits

variable (M₁ M₂ : Type _) [TopologicalSpace M₂] [T2Space M₂]

@[to_additive]
theorem isClosed_setOf_map_one [One M₁] [One M₂] : IsClosed { f : M₁ → M₂ | f 1 = 1 } :=
  isClosed_eq (continuous_apply 1) continuous_const
#align is_closed_set_of_map_one isClosed_setOf_map_one
#align is_closed_set_of_map_zero isClosed_setOf_map_zero

@[to_additive]
theorem isClosed_setOf_map_mul [Mul M₁] [Mul M₂] [ContinuousMul M₂] :
    IsClosed { f : M₁ → M₂ | ∀ x y, f (x * y) = f x * f y } := by
  simp only [setOf_forall]
  exact
    isClosed_interᵢ fun x =>
      isClosed_interᵢ fun y =>
        isClosed_eq (continuous_apply _)
          -- Porting note: proof was:
          -- `((continuous_apply _).mul (continuous_apply _))`
          (by continuity)
#align is_closed_set_of_map_mul isClosed_setOf_map_mul
#align is_closed_set_of_map_add isClosed_setOf_map_add

-- porting note: split variables command over two lines, can't change explicitness at the same time
-- as declaring new variables.
variable {M₁ M₂}
variable [MulOneClass M₁] [MulOneClass M₂] [ContinuousMul M₂]
  {F : Type _} [MonoidHomClass F M₁ M₂] {l : Filter α}

/-- Construct a bundled monoid homomorphism `M₁ →* M₂` from a function `f` and a proof that it
belongs to the closure of the range of the coercion from `M₁ →* M₂` (or another type of bundled
homomorphisms that has a `MonoidHomClass` instance) to `M₁ → M₂`. -/
@[to_additive (attr := simps (config := .asFn))
  "Construct a bundled additive monoid homomorphism `M₁ →+ M₂` from a function `f`
and a proof that it belongs to the closure of the range of the coercion from `M₁ →+ M₂` (or another
type of bundled homomorphisms that has a `add_monoid_hom_class` instance) to `M₁ → M₂`."]
def monoidHomOfMemClosureRangeCoe (f : M₁ → M₂)
    (hf : f ∈ closure (range fun (f : F) (x : M₁) => f x)) : M₁ →* M₂
    where
  toFun := f
  map_one' := (isClosed_setOf_map_one M₁ M₂).closure_subset_iff.2 (range_subset_iff.2 map_one) hf
  map_mul' := (isClosed_setOf_map_mul M₁ M₂).closure_subset_iff.2 (range_subset_iff.2 map_mul) hf
#align monoid_hom_of_mem_closure_range_coe monoidHomOfMemClosureRangeCoe
#align add_monoid_hom_of_mem_closure_range_coe addMonoidHomOfMemClosureRangeCoe

/-- Construct a bundled monoid homomorphism from a pointwise limit of monoid homomorphisms. -/
@[to_additive (attr := simps! (config := .asFn))
  "Construct a bundled additive monoid homomorphism from a pointwise limit of additive
monoid homomorphisms"]
def monoidHomOfTendsto (f : M₁ → M₂) (g : α → F) [l.NeBot]
    (h : Tendsto (fun a x => g a x) l (𝓝 f)) : M₁ →* M₂ :=
  monoidHomOfMemClosureRangeCoe f <|
    mem_closure_of_tendsto h <| eventually_of_forall fun _ => mem_range_self _
#align monoid_hom_of_tendsto monoidHomOfTendsto
#align add_monoid_hom_of_tendsto addMonoidHomOfTendsto

variable (M₁ M₂)

@[to_additive]
theorem MonoidHom.isClosed_range_coe : IsClosed (Set.range ((↑) : (M₁ →* M₂) → M₁ → M₂)) :=
  isClosed_of_closure_subset fun f hf => ⟨monoidHomOfMemClosureRangeCoe f hf, rfl⟩
#align monoid_hom.is_closed_range_coe MonoidHom.isClosed_range_coe
#align add_monoid_hom.is_closed_range_coe AddMonoidHom.isClosed_range_coe

end PointwiseLimits

@[to_additive]
theorem Inducing.continuousMul {M N F : Type _} [Mul M] [Mul N] [MulHomClass F M N]
    [TopologicalSpace M] [TopologicalSpace N] [ContinuousMul N] (f : F) (hf : Inducing f) :
    ContinuousMul M :=
  ⟨hf.continuous_iff.2 <| by
      simpa only [(· ∘ ·), map_mul f] using hf.continuous.fst'.mul hf.continuous.snd'⟩
#align inducing.has_continuous_mul Inducing.continuousMul
#align inducing.has_continuous_add Inducing.continuousAdd

@[to_additive]
theorem continuousMul_induced {M N F : Type _} [Mul M] [Mul N] [MulHomClass F M N]
    [TopologicalSpace N] [ContinuousMul N] (f : F) : @ContinuousMul M (induced f ‹_›) _ :=
  letI := induced f ‹_›
  Inducing.continuousMul f ⟨rfl⟩
#align has_continuous_mul_induced continuousMul_induced
#align has_continuous_add_induced continuousAdd_induced

@[to_additive]
instance Subsemigroup.continuousMul [TopologicalSpace M] [Semigroup M] [ContinuousMul M]
    (S : Subsemigroup M) : ContinuousMul S :=
  Inducing.continuousMul (⟨(↑), fun _ _ => rfl⟩ : MulHom S M) ⟨rfl⟩
#align subsemigroup.has_continuous_mul Subsemigroup.continuousMul
#align add_subsemigroup.has_continuous_add AddSubsemigroup.continuousAdd

@[to_additive]
instance Submonoid.continuousMul [TopologicalSpace M] [Monoid M] [ContinuousMul M]
    (S : Submonoid M) : ContinuousMul S :=
  S.toSubsemigroup.continuousMul
#align submonoid.has_continuous_mul Submonoid.continuousMul
#align add_submonoid.has_continuous_add AddSubmonoid.continuousAdd

section ContinuousMul

variable [TopologicalSpace M] [Monoid M] [ContinuousMul M]

@[to_additive]
theorem Submonoid.top_closure_mul_self_subset (s : Submonoid M) :
    _root_.closure (s : Set M) * _root_.closure s ⊆ _root_.closure s :=
  image2_subset_iff.2 fun _ hx _ hy =>
    map_mem_closure₂ continuous_mul hx hy fun _ ha _ hb => s.mul_mem ha hb
#align submonoid.top_closure_mul_self_subset Submonoid.top_closure_mul_self_subset
#align add_submonoid.top_closure_add_self_subset AddSubmonoid.top_closure_add_self_subset

@[to_additive]
theorem Submonoid.top_closure_mul_self_eq (s : Submonoid M) :
    _root_.closure (s : Set M) * _root_.closure s = _root_.closure s :=
  Subset.antisymm s.top_closure_mul_self_subset fun x hx =>
    ⟨x, 1, hx, _root_.subset_closure s.one_mem, mul_one _⟩
#align submonoid.top_closure_mul_self_eq Submonoid.top_closure_mul_self_eq
#align add_submonoid.top_closure_add_self_eq AddSubmonoid.top_closure_add_self_eq

/-- The (topological-space) closure of a submonoid of a space `M` with `ContinuousMul` is
itself a submonoid. -/
@[to_additive "The (topological-space) closure of an additive submonoid of a space `M` with
`ContinuousAdd` is itself an additive submonoid."]
def Submonoid.topologicalClosure (s : Submonoid M) : Submonoid M
    where
  carrier := _root_.closure (s : Set M)
  one_mem' := _root_.subset_closure s.one_mem
  mul_mem' ha hb := s.top_closure_mul_self_subset ⟨_, _, ha, hb, rfl⟩
#align submonoid.topological_closure Submonoid.topologicalClosure
#align add_submonoid.topological_closure AddSubmonoid.topologicalClosure

-- Porting note: new lemma
@[to_additive]
theorem Submonoid.coe_topologicalClosure (s : Submonoid M) :
  (s.topologicalClosure : Set M) = _root_.closure (s : Set M) := by rfl

@[to_additive]
theorem Submonoid.le_topologicalClosure (s : Submonoid M) : s ≤ s.topologicalClosure :=
  _root_.subset_closure
#align submonoid.le_topological_closure Submonoid.le_topologicalClosure
#align add_submonoid.le_topological_closure AddSubmonoid.le_topologicalClosure

@[to_additive]
theorem Submonoid.isClosed_topologicalClosure (s : Submonoid M) :
    IsClosed (s.topologicalClosure : Set M) := isClosed_closure
#align submonoid.is_closed_topological_closure Submonoid.isClosed_topologicalClosure
#align add_submonoid.is_closed_topological_closure AddSubmonoid.isClosed_topologicalClosure

@[to_additive]
theorem Submonoid.topologicalClosure_minimal (s : Submonoid M) {t : Submonoid M} (h : s ≤ t)
    (ht : IsClosed (t : Set M)) : s.topologicalClosure ≤ t := closure_minimal h ht
#align submonoid.topological_closure_minimal Submonoid.topologicalClosure_minimal
#align add_submonoid.topological_closure_minimal AddSubmonoid.topologicalClosure_minimal

/-- If a submonoid of a topological monoid is commutative, then so is its topological closure. -/
@[to_additive "If a submonoid of an additive topological monoid is commutative, then so is its
topological closure."]
def Submonoid.commMonoidTopologicalClosure [T2Space M] (s : Submonoid M)
    (hs : ∀ x y : s, x * y = y * x) : CommMonoid s.topologicalClosure :=
  { s.topologicalClosure.toMonoid with
    mul_comm :=
      have : ∀ x ∈ s, ∀ y ∈ s, x * y = y * x := fun x hx y hy =>
        congr_arg Subtype.val (hs ⟨x, hx⟩ ⟨y, hy⟩)
      fun ⟨x, hx⟩ ⟨y, hy⟩ =>
      Subtype.ext <|
        eqOn_closure₂ this continuous_mul (continuous_snd.mul continuous_fst) x hx y hy }
#align submonoid.comm_monoid_topological_closure Submonoid.commMonoidTopologicalClosure
#align add_submonoid.add_comm_monoid_topological_closure AddSubmonoid.addCommMonoidTopologicalClosure

@[to_additive exists_open_nhds_zero_half]
theorem exists_open_nhds_one_split {s : Set M} (hs : s ∈ 𝓝 (1 : M)) :
    ∃ V : Set M, IsOpen V ∧ (1 : M) ∈ V ∧ ∀ v ∈ V, ∀ w ∈ V, v * w ∈ s := by
  have : (fun a : M × M => a.1 * a.2) ⁻¹' s ∈ 𝓝 ((1, 1) : M × M) :=
    tendsto_mul (by simpa only [one_mul] using hs)
  simpa only [prod_subset_iff] using exists_nhds_square this
#align exists_open_nhds_one_split exists_open_nhds_one_split
#align exists_open_nhds_zero_half exists_open_nhds_zero_half

@[to_additive exists_nhds_zero_half]
theorem exists_nhds_one_split {s : Set M} (hs : s ∈ 𝓝 (1 : M)) :
    ∃ V ∈ 𝓝 (1 : M), ∀ v ∈ V, ∀ w ∈ V, v * w ∈ s :=
  let ⟨V, Vo, V1, hV⟩ := exists_open_nhds_one_split hs
  ⟨V, IsOpen.mem_nhds Vo V1, hV⟩
#align exists_nhds_one_split exists_nhds_one_split
#align exists_nhds_zero_half exists_nhds_zero_half

@[to_additive exists_nhds_zero_quarter]
theorem exists_nhds_one_split4 {u : Set M} (hu : u ∈ 𝓝 (1 : M)) :
    ∃ V ∈ 𝓝 (1 : M), ∀ {v w s t}, v ∈ V → w ∈ V → s ∈ V → t ∈ V → v * w * s * t ∈ u := by
  rcases exists_nhds_one_split hu with ⟨W, W1, h⟩
  rcases exists_nhds_one_split W1 with ⟨V, V1, h'⟩
  use V, V1
  intro v w s t v_in w_in s_in t_in
  simpa only [mul_assoc] using h _ (h' v v_in w w_in) _ (h' s s_in t t_in)
#align exists_nhds_one_split4 exists_nhds_one_split4
#align exists_nhds_zero_quarter exists_nhds_zero_quarter

/-- Given a neighborhood `U` of `1` there is an open neighborhood `V` of `1`
such that `VV ⊆ U`. -/
@[to_additive "Given a open neighborhood `U` of `0` there is a open neighborhood `V` of `0`
  such that `V + V ⊆ U`."]
theorem exists_open_nhds_one_mul_subset {U : Set M} (hU : U ∈ 𝓝 (1 : M)) :
    ∃ V : Set M, IsOpen V ∧ (1 : M) ∈ V ∧ V * V ⊆ U := by
  rcases exists_open_nhds_one_split hU with ⟨V, Vo, V1, hV⟩
  use V, Vo, V1
  rintro _ ⟨x, y, hx, hy, rfl⟩
  exact hV _ hx _ hy
#align exists_open_nhds_one_mul_subset exists_open_nhds_one_mul_subset
#align exists_open_nhds_zero_add_subset exists_open_nhds_zero_add_subset

@[to_additive]
theorem IsCompact.mul {s t : Set M} (hs : IsCompact s) (ht : IsCompact t) : IsCompact (s * t) := by
  rw [← image_mul_prod]
  exact (hs.prod ht).image continuous_mul
#align is_compact.mul IsCompact.mul
#align is_compact.add IsCompact.add

@[to_additive]
theorem tendsto_list_prod {f : ι → α → M} {x : Filter α} {a : ι → M} :
    ∀ l : List ι,
      (∀ i ∈ l, Tendsto (f i) x (𝓝 (a i))) →
        Tendsto (fun b => (l.map fun c => f c b).prod) x (𝓝 (l.map a).prod)
  | [], _ => by simp [tendsto_const_nhds]
  | f::l, h => by
    simp only [List.map_cons, List.prod_cons]
    exact
      (h f (List.mem_cons_self _ _)).mul
        (tendsto_list_prod l fun c hc => h c (List.mem_cons_of_mem _ hc))
#align tendsto_list_prod tendsto_list_prod
#align tendsto_list_sum tendsto_list_sum

@[to_additive (attr := continuity)]
theorem continuous_list_prod {f : ι → X → M} (l : List ι) (h : ∀ i ∈ l, Continuous (f i)) :
    Continuous fun a => (l.map fun i => f i a).prod :=
  continuous_iff_continuousAt.2 fun x =>
    tendsto_list_prod l fun c hc => continuous_iff_continuousAt.1 (h c hc) x
#align continuous_list_prod continuous_list_prod
#align continuous_list_sum continuous_list_sum

@[to_additive]
theorem continuousOn_list_prod {f : ι → X → M} (l : List ι) {t : Set X}
    (h : ∀ i ∈ l, ContinuousOn (f i) t) : ContinuousOn (fun a => (l.map fun i => f i a).prod) t :=
  by
  intro x hx
  rw [continuousWithinAt_iff_continuousAt_restrict _ hx]
  refine' tendsto_list_prod _ fun i hi => _
  specialize h i hi x hx
  rw [continuousWithinAt_iff_continuousAt_restrict _ hx] at h
  exact h
#align continuous_on_list_prod continuousOn_list_prod
#align continuous_on_list_sum continuousOn_list_sum

@[to_additive (attr := continuity)]
theorem continuous_pow : ∀ n : ℕ, Continuous fun a : M => a ^ n
  | 0 => by simpa using continuous_const
  | k + 1 => by
    simp only [pow_succ]
    exact continuous_id.mul (continuous_pow _)
#align continuous_pow continuous_pow
#align continuous_nsmul continuous_nsmul

instance AddMonoid.continuousConstSMul_nat {A} [AddMonoid A] [TopologicalSpace A]
    [ContinuousAdd A] : ContinuousConstSMul ℕ A :=
  ⟨continuous_nsmul⟩
#align add_monoid.has_continuous_const_smul_nat AddMonoid.continuousConstSMul_nat

instance AddMonoid.continuousSMul_nat {A} [AddMonoid A] [TopologicalSpace A]
    [ContinuousAdd A] : ContinuousSMul ℕ A :=
  ⟨continuous_uncurry_of_discreteTopology continuous_nsmul⟩
#align add_monoid.has_continuous_smul_nat AddMonoid.continuousSMul_nat

@[to_additive (attr := continuity)]
theorem Continuous.pow {f : X → M} (h : Continuous f) (n : ℕ) : Continuous fun b => f b ^ n :=
  (continuous_pow n).comp h
#align continuous.pow Continuous.pow
#align continuous.nsmul Continuous.nsmul

@[to_additive]
theorem continuousOn_pow {s : Set M} (n : ℕ) : ContinuousOn (fun (x : M) => x ^ n) s :=
  (continuous_pow n).continuousOn
#align continuous_on_pow continuousOn_pow
#align continuous_on_nsmul continuousOn_nsmul

@[to_additive]
theorem continuousAt_pow (x : M) (n : ℕ) : ContinuousAt (fun (x : M) => x ^ n) x :=
  (continuous_pow n).continuousAt
#align continuous_at_pow continuousAt_pow
#align continuous_at_nsmul continuousAt_nsmul

@[to_additive]
theorem Filter.Tendsto.pow {l : Filter α} {f : α → M} {x : M} (hf : Tendsto f l (𝓝 x)) (n : ℕ) :
    Tendsto (fun x => f x ^ n) l (𝓝 (x ^ n)) :=
  (continuousAt_pow _ _).tendsto.comp hf
#align filter.tendsto.pow Filter.Tendsto.pow
#align filter.tendsto.nsmul Filter.Tendsto.nsmul

@[to_additive]
theorem ContinuousWithinAt.pow {f : X → M} {x : X} {s : Set X} (hf : ContinuousWithinAt f s x)
    (n : ℕ) : ContinuousWithinAt (fun x => f x ^ n) s x :=
  Filter.Tendsto.pow hf n
#align continuous_within_at.pow ContinuousWithinAt.pow
#align continuous_within_at.nsmul ContinuousWithinAt.nsmul

@[to_additive]
theorem ContinuousAt.pow {f : X → M} {x : X} (hf : ContinuousAt f x) (n : ℕ) :
    ContinuousAt (fun x => f x ^ n) x :=
  Filter.Tendsto.pow hf n
#align continuous_at.pow ContinuousAt.pow
#align continuous_at.nsmul ContinuousAt.nsmul

@[to_additive]
theorem ContinuousOn.pow {f : X → M} {s : Set X} (hf : ContinuousOn f s) (n : ℕ) :
    ContinuousOn (fun x => f x ^ n) s := fun x hx => (hf x hx).pow n
#align continuous_on.pow ContinuousOn.pow
#align continuous_on.nsmul ContinuousOn.nsmul

/-- Left-multiplication by a left-invertible element of a topological monoid is proper, i.e.,
inverse images of compact sets are compact. -/
theorem Filter.tendsto_cocompact_mul_left {a b : M} (ha : b * a = 1) :
    Filter.Tendsto (fun x : M => a * x) (Filter.cocompact M) (Filter.cocompact M) := by
  refine Filter.Tendsto.of_tendsto_comp ?_ (Filter.comap_cocompact_le (continuous_mul_left b))
  simp only [comp_mul_left, ha, one_mul]
  exact Filter.tendsto_id
  -- Porting note: changed proof, original proof was:
  /-refine' Filter.Tendsto.of_tendsto_comp _ (Filter.comap_cocompact_le (continuous_mul_left b))
  convert Filter.tendsto_id
  ext x
  simp [ha]-/
#align filter.tendsto_cocompact_mul_left Filter.tendsto_cocompact_mul_left

/-- Right-multiplication by a right-invertible element of a topological monoid is proper, i.e.,
inverse images of compact sets are compact. -/
theorem Filter.tendsto_cocompact_mul_right {a b : M} (ha : a * b = 1) :
    Filter.Tendsto (fun x : M => x * a) (Filter.cocompact M) (Filter.cocompact M) := by
  refine Filter.Tendsto.of_tendsto_comp ?_ (Filter.comap_cocompact_le (continuous_mul_right b))
  simp only [comp_mul_right, ha, mul_one]
  exact Filter.tendsto_id
  -- Porting note: changed proof
#align filter.tendsto_cocompact_mul_right Filter.tendsto_cocompact_mul_right

/-- If `R` acts on `A` via `A`, then continuous multiplication implies continuous scalar
multiplication by constants.

Notably, this instances applies when `R = A`, or when `[Algebra R A]` is available. -/
@[to_additive "If `R` acts on `A` via `A`, then continuous addition implies
continuous affine addition by constants."]
instance (priority := 100) IsScalarTower.continuousConstSMul {R A : Type _} [Monoid A] [SMul R A]
    [IsScalarTower R A A] [TopologicalSpace A] [ContinuousMul A] : ContinuousConstSMul R A where
    continuous_const_smul q := by
      simp (config := { singlePass := true }) only [← smul_one_mul q (_ : A)]
      exact continuous_const.mul continuous_id
#align is_scalar_tower.has_continuous_const_smul IsScalarTower.continuousConstSMul
#align vadd_assoc_class.has_continuous_const_vadd VAddAssocClass.continuousConstVAdd

/-- If the action of `R` on `A` commutes with left-multiplication, then continuous multiplication
implies continuous scalar multiplication by constants.

Notably, this instances applies when `R = Aᵐᵒᵖ`.-/
@[to_additive "If the action of `R` on `A` commutes with left-addition, then
continuous addition implies continuous affine addition by constants.

Notably, this instances applies when `R = Aᵃᵒᵖ`."]
instance (priority := 100) SMulCommClass.continuousConstSMul {R A : Type _} [Monoid A] [SMul R A]
    [SMulCommClass R A A] [TopologicalSpace A] [ContinuousMul A] : ContinuousConstSMul R A where
    continuous_const_smul q := by
      simp (config := { singlePass := true }) only [← mul_smul_one q (_ : A)]
      exact continuous_id.mul continuous_const
#align smul_comm_class.has_continuous_const_smul SMulCommClass.continuousConstSMul
#align vadd_comm_class.has_continuous_const_vadd VAddCommClass.continuousConstVAdd

end ContinuousMul

namespace MulOpposite

/-- If multiplication is continuous in `α`, then it also is in `αᵐᵒᵖ`. -/
@[to_additive "If addition is continuous in `α`, then it also is in `αᵃᵒᵖ`."]
instance [TopologicalSpace α] [Mul α] [ContinuousMul α] : ContinuousMul αᵐᵒᵖ :=
  ⟨continuous_op.comp (continuous_unop.snd'.mul continuous_unop.fst')⟩

end MulOpposite

namespace Units

open MulOpposite

variable [TopologicalSpace α] [Monoid α] [ContinuousMul α]

/-- If multiplication on a monoid is continuous, then multiplication on the units of the monoid,
with respect to the induced topology, is continuous.

Inversion is also continuous, but we register this in a later file, `Topology.Algebra.Group`,
because the predicate `ContinuousInv` has not yet been defined. -/
@[to_additive "If addition on an additive monoid is continuous, then addition on the additive units
of the monoid, with respect to the induced topology, is continuous.

Negation is also continuous, but we register this in a later file, `topology.algebra.group`, because
the predicate `has_continuous_neg` has not yet been defined."]
instance : ContinuousMul αˣ :=
  inducing_embedProduct.continuousMul (embedProduct α)

end Units

@[to_additive]
theorem Continuous.units_map [Monoid M] [Monoid N] [TopologicalSpace M] [TopologicalSpace N]
    (f : M →* N) (hf : Continuous f) : Continuous (Units.map f) :=
  Units.continuous_iff.2 ⟨hf.comp Units.continuous_val, hf.comp Units.continuous_coe_inv⟩
#align continuous.units_map Continuous.units_map
#align continuous.add_units_map Continuous.addUnits_map

section

variable [TopologicalSpace M] [CommMonoid M]

@[to_additive]
theorem Submonoid.mem_nhds_one (S : Submonoid M) (oS : IsOpen (S : Set M)) :
    (S : Set M) ∈ 𝓝 (1 : M) :=
  IsOpen.mem_nhds oS S.one_mem
#align submonoid.mem_nhds_one Submonoid.mem_nhds_one
#align add_submonoid.mem_nhds_zero AddSubmonoid.mem_nhds_zero

variable [ContinuousMul M]

@[to_additive]
theorem tendsto_multiset_prod {f : ι → α → M} {x : Filter α} {a : ι → M} (s : Multiset ι) :
    (∀ i ∈ s, Tendsto (f i) x (𝓝 (a i))) →
      Tendsto (fun b => (s.map fun c => f c b).prod) x (𝓝 (s.map a).prod) := by
  rcases s with ⟨l⟩
  simpa using tendsto_list_prod l
#align tendsto_multiset_prod tendsto_multiset_prod
#align tendsto_multiset_sum tendsto_multiset_sum

@[to_additive]
theorem tendsto_finset_prod {f : ι → α → M} {x : Filter α} {a : ι → M} (s : Finset ι) :
    (∀ i ∈ s, Tendsto (f i) x (𝓝 (a i))) →
      Tendsto (fun b => ∏ c in s, f c b) x (𝓝 (∏ c in s, a c)) :=
  tendsto_multiset_prod _
#align tendsto_finset_prod tendsto_finset_prod
#align tendsto_finset_sum tendsto_finset_sum

@[to_additive (attr := continuity)]
theorem continuous_multiset_prod {f : ι → X → M} (s : Multiset ι) :
    (∀ i ∈ s, Continuous (f i)) → Continuous fun a => (s.map fun i => f i a).prod := by
  rcases s with ⟨l⟩
  simpa using continuous_list_prod l
#align continuous_multiset_prod continuous_multiset_prod
#align continuous_multiset_sum continuous_multiset_sum

@[to_additive]
theorem continuousOn_multiset_prod {f : ι → X → M} (s : Multiset ι) {t : Set X} :
    (∀ i ∈ s, ContinuousOn (f i) t) → ContinuousOn (fun a => (s.map fun i => f i a).prod) t := by
  rcases s with ⟨l⟩
  simpa using continuousOn_list_prod l
#align continuous_on_multiset_prod continuousOn_multiset_prod
#align continuous_on_multiset_sum continuousOn_multiset_sum

@[to_additive (attr := continuity)]
theorem continuous_finset_prod {f : ι → X → M} (s : Finset ι) :
    (∀ i ∈ s, Continuous (f i)) → Continuous fun a => ∏ i in s, f i a :=
  continuous_multiset_prod _
#align continuous_finset_prod continuous_finset_prod
#align continuous_finset_sum continuous_finset_sum

@[to_additive]
theorem continuousOn_finset_prod {f : ι → X → M} (s : Finset ι) {t : Set X} :
    (∀ i ∈ s, ContinuousOn (f i) t) → ContinuousOn (fun a => ∏ i in s, f i a) t :=
  continuousOn_multiset_prod _
#align continuous_on_finset_prod continuousOn_finset_prod
#align continuous_on_finset_sum continuousOn_finset_sum

@[to_additive]
theorem eventuallyEq_prod {X M : Type _} [CommMonoid M] {s : Finset ι} {l : Filter X}
    {f g : ι → X → M} (hs : ∀ i ∈ s, f i =ᶠ[l] g i) : (∏ i in s, f i) =ᶠ[l] ∏ i in s, g i := by
  replace hs : ∀ᶠ x in l, ∀ i ∈ s, f i x = g i x
  · rwa [eventually_all_finset]
  filter_upwards [hs]with x hx
  simp only [Finset.prod_apply, Finset.prod_congr rfl hx]
#align eventually_eq_prod eventuallyEq_prod
#align eventually_eq_sum eventuallyEq_sum

open Function

@[to_additive]
theorem LocallyFinite.exists_finset_mulSupport {M : Type _} [CommMonoid M] {f : ι → X → M}
    (hf : LocallyFinite fun i => mulSupport <| f i) (x₀ : X) :
    ∃ I : Finset ι, ∀ᶠ x in 𝓝 x₀, (mulSupport fun i => f i x) ⊆ I := by
  rcases hf x₀ with ⟨U, hxU, hUf⟩
  refine' ⟨hUf.toFinset, mem_of_superset hxU fun y hy i hi => _⟩
  rw [hUf.coe_toFinset]
  exact ⟨y, hi, hy⟩
#align locally_finite.exists_finset_mul_support LocallyFinite.exists_finset_mulSupport
#align locally_finite.exists_finset_support LocallyFinite.exists_finset_support

@[to_additive]
theorem finprod_eventually_eq_prod {M : Type _} [CommMonoid M] {f : ι → X → M}
    (hf : LocallyFinite fun i => mulSupport (f i)) (x : X) :
    ∃ s : Finset ι, ∀ᶠ y in 𝓝 x, (∏ᶠ i, f i y) = ∏ i in s, f i y :=
  let ⟨I, hI⟩ := hf.exists_finset_mulSupport x
  ⟨I, hI.mono fun _ hy => finprod_eq_prod_of_mulSupport_subset _ fun _ hi => hy hi⟩
#align finprod_eventually_eq_prod finprod_eventually_eq_prod
#align finsum_eventually_eq_sum finsum_eventually_eq_sum

@[to_additive]
theorem continuous_finprod {f : ι → X → M} (hc : ∀ i, Continuous (f i))
    (hf : LocallyFinite fun i => mulSupport (f i)) : Continuous fun x => ∏ᶠ i, f i x := by
  refine' continuous_iff_continuousAt.2 fun x => _
  rcases finprod_eventually_eq_prod hf x with ⟨s, hs⟩
  refine' ContinuousAt.congr _ (EventuallyEq.symm hs)
  exact tendsto_finset_prod _ fun i _ => (hc i).continuousAt
#align continuous_finprod continuous_finprod
#align continuous_finsum continuous_finsum

@[to_additive]
theorem continuous_finprod_cond {f : ι → X → M} {p : ι → Prop} (hc : ∀ i, p i → Continuous (f i))
    (hf : LocallyFinite fun i => mulSupport (f i)) : Continuous fun x => ∏ᶠ (i) (_h : p i), f i x :=
  by
  simp only [← finprod_subtype_eq_finprod_cond]
  exact continuous_finprod (fun i => hc i i.2) (hf.comp_injective Subtype.coe_injective)
#align continuous_finprod_cond continuous_finprod_cond
#align continuous_finsum_cond continuous_finsum_cond

end

instance [TopologicalSpace M] [Mul M] [ContinuousMul M] : ContinuousAdd (Additive M)
    where continuous_add := @continuous_mul M _ _ _

instance [TopologicalSpace M] [Add M] [ContinuousAdd M] : ContinuousMul (Multiplicative M)
    where continuous_mul := @continuous_add M _ _ _

section LatticeOps

variable {ι' : Sort _} [Mul M]

@[to_additive]
theorem continuousMul_infₛ {ts : Set (TopologicalSpace M)}
    (h : ∀ t ∈ ts, @ContinuousMul M t _) : @ContinuousMul M (infₛ ts) _ :=
  letI := infₛ ts
  { continuous_mul :=
      continuous_infₛ_rng.2 fun t ht =>
        continuous_infₛ_dom₂ ht ht (@ContinuousMul.continuous_mul M t _ (h t ht)) }
#align has_continuous_mul_Inf continuousMul_infₛ
#align has_continuous_add_Inf continuousAdd_infₛ

@[to_additive]
theorem continuousMul_infᵢ {ts : ι' → TopologicalSpace M}
    (h' : ∀ i, @ContinuousMul M (ts i) _) : @ContinuousMul M (⨅ i, ts i) _ := by
  rw [← infₛ_range]
  exact continuousMul_infₛ (Set.forall_range_iff.mpr h')
#align has_continuous_mul_infi continuousMul_infᵢ
#align has_continuous_add_infi continuousAdd_infᵢ

@[to_additive]
theorem continuousMul_inf {t₁ t₂ : TopologicalSpace M} (h₁ : @ContinuousMul M t₁ _)
    (h₂ : @ContinuousMul M t₂ _) : @ContinuousMul M (t₁ ⊓ t₂) _ := by
  rw [inf_eq_infᵢ]
  refine' continuousMul_infᵢ fun b => _
  cases b <;> assumption
#align has_continuous_mul_inf continuousMul_inf
#align has_continuous_add_inf continuousAdd_inf

end LatticeOps

namespace ContinuousMap

variable [Mul X] [ContinuousMul X]

/-- The continuous map `fun y => y * x` -/
@[to_additive "The continuous map `fun y => y + x"]
protected def mulRight (x : X) : C(X, X) :=
  mk _ (continuous_mul_right x)
#align continuous_map.mul_right ContinuousMap.mulRight
#align continuous_map.add_right ContinuousMap.addRight

@[to_additive, simp]
theorem coe_mulRight (x : X) : ⇑(ContinuousMap.mulRight x) = fun y => y * x :=
  rfl
#align continuous_map.coe_mul_right ContinuousMap.coe_mulRight
#align continuous_map.coe_add_right ContinuousMap.coe_addRight

/-- The continuous map `fun y => x * y` -/
@[to_additive "The continuous map `fun y => x + y"]
protected def mulLeft (x : X) : C(X, X) :=
  mk _ (continuous_mul_left x)
#align continuous_map.mul_left ContinuousMap.mulLeft
#align continuous_map.add_left ContinuousMap.addLeft

@[to_additive, simp]
theorem coe_mulLeft (x : X) : ⇑(ContinuousMap.mulLeft x) = fun y => x * y :=
  rfl
#align continuous_map.coe_mul_left ContinuousMap.coe_mulLeft
#align continuous_map.coe_add_left ContinuousMap.coe_addLeft

end ContinuousMap


/-
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

/-!
# 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.lift' powerset`, i.e. for any `ε>0` there exists `t ∈ l` such that
`∥∫ x in s, f x ∂μ - μ s • c∥ ≤ ε * μ s` whenever `s ⊆ t`. We also formulate a version of this
theorem for a locally finite measure `μ` and a function `f` continuous at a point `a`.

## Notation

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] [measurable_space E] {f g : α → E} {s t : set α} {μ ν : measure α}
  {l l' : filter α} [borel_space E] [second_countable_topology E]

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 : (s ∩ t : set α) =ᵐ[μ] (∅ : set α)) (hs : measurable_set s)
  (ht : 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 ∂μ :=
begin
  have : s =ᵐ[μ] s \ t,
  { refine (hst.mem_iff.mono _).set_eq, simp },
  rw [← diff_union_self, integral_union disjoint_diff.symm, set_integral_congr_set_ae this],
  exacts [hs.diff ht, ht, hfs.mono_set (diff_subset _ _), hft]
end

lemma integral_diff (hs : measurable_set s) (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, hs.diff ht, 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 _ hs.1 _ 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 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 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 hsm 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 (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 (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 {ι : 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 ∂μ) :=
begin
  have hfi' : ∀ i, integrable_on f (s i) μ, from λ i, hfi.mono_set (subset_Union _ _),
  simp only [has_sum, ← integral_finset_bUnion _ (λ i _, hm i) (hd.set_pairwise _) (λ i _, hfi' i)],
  rw Union_eq_Union_finset at hfi ⊢,
  exact tendsto_set_integral_of_monotone (λ t, t.measurable_set_bUnion (λ i _, hm i))
    (λ t₁ t₂ h, bUnion_subset_bUnion_left h) hfi
end

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 has_sum_integral_Union_of_null_inter {ι : Type*} [encodable ι] {s : ι → set α} {f : α → E}
  (hm : ∀ i, measurable_set (s i)) (hd : pairwise (λ i j, μ (s i ∩ s j) = 0))
  (hfi : integrable_on f (⋃ i, s i) μ) :
  has_sum (λ n, ∫ a in s n, f a ∂ μ) (∫ a in ⋃ n, s n, f a ∂μ) :=
begin
  rcases exists_subordinate_pairwise_disjoint hm hd with ⟨t, ht_sub, ht_eq, htm, htd⟩,
  have htU_eq : (⋃ i, s i) =ᵐ[μ] ⋃ i, t i := eventually_eq.countable_Union ht_eq,
  simp only [set_integral_congr_set_ae (ht_eq _), set_integral_congr_set_ae htU_eq, htU_eq],
  exact has_sum_integral_Union htm htd (hfi.congr_set_ae htU_eq.symm)
end

lemma integral_Union_of_null_inter {ι : Type*} [encodable ι] {s : ι → set α} {f : α → E}
  (hm : ∀ i, measurable_set (s i)) (hd : pairwise (λ i j, μ (s i ∩ s j) = 0))
  (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_of_null_inter hm hd hfi)).symm

lemma set_integral_eq_zero_of_forall_eq_zero {f : α → E} (hf : 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 (measurable_set_eq_fun hf measurable_zero)],
  refine eventually_of_forall (λ x hx, _),
  rw pi.zero_apply,
  exact ht_eq x hx,
end

private lemma set_integral_union_eq_left_of_disjoint {f : α → E} (hf : measurable f)
  (hfi : integrable f μ) (hs : measurable_set s) (ht : measurable_set t) (ht_eq : ∀ x ∈ t, f x = 0)
  (hs_disj : disjoint s t) :
  ∫ x in (s ∪ t), f x ∂μ = ∫ x in s, f x ∂μ :=
by rw [integral_union hs_disj hs ht hfi.integrable_on hfi.integrable_on,
  set_integral_eq_zero_of_forall_eq_zero hf ht_eq, add_zero]

lemma set_integral_union_eq_left {f : α → E} (hf : measurable f) (hfi : integrable f μ)
  (hs : measurable_set s) (ht : measurable_set t) (ht_eq : ∀ x ∈ t, f x = 0) :
  ∫ x in (s ∪ t), f x ∂μ = ∫ x in s, f x ∂μ :=
begin
  rw [← set.union_diff_self, integral_union,
    set_integral_eq_zero_of_forall_eq_zero _ (λ x hx, ht_eq x (diff_subset _ _ hx)), add_zero],
  exacts [hf, disjoint_diff, hs, ht.diff 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 : 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 (measurable_set_lt hf measurable_const)
    (measurable_set_eq_fun hf measurable_const) (λ x hx, hx)).symm,
end

lemma integral_norm_eq_pos_sub_neg {f : α → ℝ} (hf : 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 measurable_set_le measurable_const 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]

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_measurable f (measure.map g μ)) (hg : measurable g) :
  ∫ y in s, f y ∂(measure.map g μ) = ∫ x in g ⁻¹' s, f (g x) ∂μ :=
begin
  rw [measure.restrict_map hg hs, integral_map hg (hf.mono_measure _)],
  exact measure.map_mono g measure.restrict_le_self
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_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_measurable.mk f hfm x∥ ≤ C,
  { filter_upwards [hC, hfm.ae_mem_imp_eq_mk],
    assume a h1 h2 h3,
    rw [← h2 h3],
    exact h1 h3 },
  have B : measurable_set {x | ∥(hfm.mk f) x∥ ≤ C} := hfm.measurable_mk.norm measurable_set_Iic,
  filter_upwards [hfm.ae_eq_mk, (ae_restrict_iff B).2 A],
  assume a h1 h2,
  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_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_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 : @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 μ]

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 f μ) (hf : 0 ≤ᵐ[μ] f) (hst : s ≤ᵐ[μ] t) :
  ∫ x in s, f x ∂μ ≤ ∫ x in t, f x ∂μ :=
begin
  repeat { rw integral_eq_lintegral_of_nonneg_ae (ae_restrict_of_ae hf)
            (hfi.1.mono_measure measure.restrict_le_self) },
  rw ennreal.to_real_le_to_real
    (ne_of_lt $ (has_finite_integral_iff_of_real (ae_restrict_of_ae hf)).mp hfi.integrable_on.2)
    (ne_of_lt $ (has_finite_integral_iff_of_real (ae_restrict_of_ae hf)).mp hfi.integrable_on.2),
  exact (lintegral_mono_set' hst),
end

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 : 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 (measurable_set_le measurable_const hf)],
  exact integral_mono (hfi.indicator hs) (hfi.indicator (measurable_set_le measurable_const 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) {f : α → ℝ} (hf : 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 (measurable_set_le hf measurable_const)],
  exact integral_mono (hfi.indicator (measurable_set_le hf measurable_const)) (hfi.indicator hs)
    (indicator_nonpos_le_indicator s f),
end

end nonneg

section tendsto_mono

variables {μ : measure α}
  [measurable_space E] [normed_group E] [borel_space E] [complete_space E] [normed_space ℝ E]
  [second_countable_topology 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_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 [] λ a, 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] [measurable_space E] [second_countable_topology E] [borel_space E]
  {𝕜 : Type*} [is_R_or_C 𝕜]
  [normed_group F] [measurable_space F] [second_countable_topology F] [borel_space 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*} [measurable_space E] [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.lift'
powerset` 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] [second_countable_topology E] [complete_space E] [borel_space E]
  {μ : measure α} {l : filter α} [l.is_measurably_generated]
  {f : α → E} {b : E} (h : tendsto f (l ⊓ μ.ae) (𝓝 b))
  (hfm : measurable_at_filter f l μ) (hμ : μ.finite_at_filter l)
  {s : ι → set α} {li : filter ι} (hs : tendsto s li (l.lift' powerset))
  (m : ι → ℝ := λ i, (μ (s i)).to_real)
  (hsμ : (λ i, (μ (s i)).to_real) =ᶠ[li] m . tactic.interactive.refl) :
  is_o (λ i, ∫ x in s i, f x ∂μ - m i • b) m li :=
begin
  suffices : is_o (λ s, ∫ x in s, f x ∂μ - (μ s).to_real • b) (λ s, (μ s).to_real)
    (l.lift' powerset),
    from (this.comp_tendsto hs).congr' (hsμ.mono $ λ a ha, ha ▸ rfl) hsμ,
  refine is_o_iff.2 (λ ε ε₀, _),
  have : ∀ᶠ s in l.lift' powerset, ∀ᶠ x in μ.ae, x ∈ s → f x ∈ closed_ball b ε :=
    eventually_lift'_powerset_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_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).lift' powerset` 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] [second_countable_topology E] [complete_space E] [borel_space E]
  {μ : measure α} [is_locally_finite_measure μ] {a : α} {t : set α}
  {f : α → E} (ha : continuous_within_at f t a) (ht : measurable_set t)
  (hfm : measurable_at_filter f (𝓝[t] a) μ)
  {s : ι → set α} {li : filter ι} (hs : tendsto s li ((𝓝[t] a).lift' powerset))
  (m : ι → ℝ := λ i, (μ (s i)).to_real)
  (hsμ : (λ i, (μ (s i)).to_real) =ᶠ[li] m . tactic.interactive.refl) :
  is_o (λ i, ∫ x in s i, f x ∂μ - m i • f a) m li :=
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).lift'
powerset` 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] [second_countable_topology E] [complete_space E] [borel_space E]
  {μ : measure α} [is_locally_finite_measure μ] {a : α}
  {f : α → E} (ha : continuous_at f a) (hfm : measurable_at_filter f (𝓝 a) μ)
  {s : ι → set α} {li : filter ι} (hs : tendsto s li ((𝓝 a).lift' powerset))
  (m : ι → ℝ := λ i, (μ (s i)).to_real)
  (hsμ : (λ i, (μ (s i)).to_real) =ᶠ[li] m . tactic.interactive.refl) :
  is_o (λ i, ∫ x in s i, f x ∂μ - m i • f a) m li :=
(ha.mono_left inf_le_left).integral_sub_linear_is_o_ae hfm (μ.finite_at_nhds a) hs m hsμ

/-- If a function is continuous on an open set `s`, then it is measurable at the filter `𝓝 x` for
  all `x ∈ s`. -/
lemma continuous_on.measurable_at_filter
  [topological_space α] [opens_measurable_space α] [measurable_space β] [topological_space β]
  [borel_space β]
  {f : α → β} {s : set α} {μ : measure α} (hs : is_open s) (hf : continuous_on f s) :
  ∀ x ∈ s, measurable_at_filter f (𝓝 x) μ :=
λ x hx, ⟨s, is_open.mem_nhds hs hx, hf.ae_measurable hs.measurable_set⟩

lemma continuous_at.measurable_at_filter
  [topological_space α] [opens_measurable_space α] [borel_space E]
  {f : α → E} {s : set α} {μ : measure α} (hs : is_open s) (hf : ∀ x ∈ s, continuous_at f x) :
  ∀ x ∈ s, measurable_at_filter f (𝓝 x) μ :=
continuous_on.measurable_at_filter hs $ continuous_at.continuous_on hf

lemma continuous.measurable_at_filter [topological_space α] [opens_measurable_space α]
  [measurable_space β] [topological_space β] [borel_space β] {f : α → β} (hf : continuous f)
  (μ : measure α) (l : filter α) :
  measurable_at_filter f l μ :=
hf.measurable.measurable_at_filter

/-- If a function is continuous on a measurable set `s`, then it is measurable at the filter
  `𝓝[s] x` for all `x`. -/
lemma continuous_on.measurable_at_filter_nhds_within {α β : Type*} [measurable_space α]
  [topological_space α] [opens_measurable_space α] [measurable_space β] [topological_space β]
  [borel_space β] {f : α → β} {s : set α} {μ : measure α}
  (hf : continuous_on f s) (hs : measurable_set s) (x : α) :
  measurable_at_filter f (𝓝[s] x) μ :=
⟨s, self_mem_nhds_within, hf.ae_measurable 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).lift' powerset` 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] [second_countable_topology E] [complete_space E] [borel_space 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).lift' powerset))
  (m : ι → ℝ := λ i, (μ (s i)).to_real)
  (hsμ : (λ i, (μ (s i)).to_real) =ᶠ[li] m . tactic.interactive.refl) :
  is_o (λ i, ∫ x in s i, f x ∂μ - m i • f a) m li :=
(hft a ha).integral_sub_linear_is_o_ae ht ⟨t, self_mem_nhds_within, hft.ae_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}

local attribute [instance] fact_one_le_one_ennreal

namespace continuous_linear_map

variables [measurable_space F] [borel_space F]

variables [second_countable_topology F] [complete_space F]
  [borel_space E] [second_countable_topology E] [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] [is_scalar_tower ℝ 𝕜 E] [is_scalar_tower ℝ 𝕜 F]

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]
  [second_countable_topology $ H →L[𝕜] E] {φ : α → 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 [measurable_space F] [borel_space F] [second_countable_topology F] [complete_space F]
  [normed_space ℝ F] [is_scalar_tower ℝ 𝕜 F]
  [borel_space E] [second_countable_topology E] [complete_space E] [normed_space ℝ E]
  [is_scalar_tower ℝ 𝕜 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 [borel_space E] [second_countable_topology E] [complete_space E] [normed_space ℝ E]
  [measurable_space F] [borel_space F] [second_countable_topology F] [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] [is_scalar_tower ℝ 𝕜 E]
  [measurable_space 𝕜] [borel_space 𝕜] (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'] [measurable_space E'] [borel_space E']
  [second_countable_topology E'] [complete_space E'] [normed_space ℝ E'] [is_scalar_tower ℝ 𝕜 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 ∂μ⟫ :=
((@inner_right 𝕜 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

end


-- Notes for 10/31/2019: boooooolean

inductive fuckitwe (α : Type) : Type
| fuckitwe : α → fuckitwe

def harsh := fuckitwe bool

/-
Propositions can be formalized as types, and proofs are just values of those types
-/

/-
Review we define day to be a type.
-/

inductive day : Type
| mon
| tue
| wed
| thu
| fri
| sat
| sun

/-
This is a *computational*, its constructoras define the possible values that we can build of this type
-/

/-
Introducing the begin-end pair
The begin-end pair allows us to incrementally build values
-/
def d : day :=
begin
    -- lean is in "scripting mode"
    exact day.mon, -- exact must be given an argument which is *exactly* the type of
end

/-
The defenition d is an assertion that there exists a value of type day
-/

/-
Propositions are formalized as types deriving from Prop.
Constuctors define the values, which we accept as proofs of the proposition
Proofs in Lean are values of logical types
-/
inductive emily_is_from_cville : Prop
| drivers_license
| passport
| utility_bill

inductive false_prop : Prop

open emily_is_from_cville

theorem proof: emily_is_from_cville :=
begin
    exact utility_bill,
end

/-
We can generalize from a specific proposition,
such as the prop that emily is from charlottesville, to
one that allows us to assert that any given person is from
Charlottesville by adding a parameter.
-/

inductive person
| mari
| jose
| jane
| bill

open person

inductive is_from_cville : person → Prop
| proof_for :
    ∀ (p : person), -- for any person p,
        (p = mari) → is_from_cville mari -- if p is mari, then a proof that the person is from charlottesville exists

/-
Proposition with a parameter: a function that returns a proposition.

is_from_cville is a PREDICATE.

Predicates can be thought of as defining properties that apply to the arguments
-/

#check is_from_cville -- function from person → Prop
#check (is_from_cville mari) -- Prop
#check is_from_cville jane
#check is_from_cville bill

/-
Let's create a proof that mari is from cville.
-/
#check is_from_cville.proof_for mari
#check is_from_cville.proof_for jose

theorem mari_is_from_cville: is_from_cville mari :=
begin
    apply is_from_cville.proof_for _ _,
    exact mari,
    exact eq.refl mari -- reflexive equality proof (x = x)
end

/-
Our proof_for constructor effectively allows us to derive a proof that someone is from charlottesville if we have a proof that p = mari
-/

/-
Relations
- can be reflexive (a = a, so an equality relation is reflexive)
- can be symmetric (if a = b, then b = a, so the equality relation is reflexive)

"If I accept a 'likes request' on Facebook, I have to 'like' you back"
"That doesn't exist"
-/

/-
Predicaates are often used to express properties of objects.
-/

inductive is_zero : ℕ → Prop
| zmk : ∀ (n : ℕ), n = 0 → is_zero n

#check is_zero.zmk

theorem zero_is_zero : is_zero 0 :=
begin
    apply is_zero.zmk 0 (eq.refl 0)
end

/-
Inductively defined proof
-/
inductive is_even : ℕ → Prop
| pf_zero_is_even : (is_even 0)
| pf_n_minus_two_is_even :
    ∀ (n : ℕ), is_even n → is_even (nat.succ (nat.succ n))

open is_even

theorem zero_is_even : is_even 0 := pf_zero_is_even

theorem two_even : is_even 2 :=
begin
end


import fol
import Rings.Notation
import Rings.Rings
import field_theory.algebraic_closure
notation h :: t  := dvector.cons h t
notation `[` l:(foldr `, ` (h t, dvector.cons h t) dvector.nil `]`) := l

universe u

namespace Fields
open fol
open Rings

def MulInv : sentence RingSignature :=
∀' (x_ 1 ≃ 0) ⊔ (∃' x_ 1 * x_ 0 ≃ 1)

def NonTriv : sentence RingSignature := 0 ≄ 1

def FieldTheory : Theory RingSignature :=  RingTheory ∪ {MulInv , NonTriv}

lemma MulInvInFieldTheory : MulInv ∈ FieldTheory := begin right, left, refl end

lemma NonTrivInFieldTheory : NonTriv ∈ FieldTheory :=
begin right, right, exact set.mem_singleton _ end

lemma RingTheorySubFieldTheory : RingTheory ⊆ FieldTheory :=
begin intros f hf, left, exact hf end

namespace field_to
  -- open classical
  -- local attribute [instance] prop_decidable
  variables
    {K : Type u} [field K]

  lemma K_is_field : is_field K := field.to_is_field K

  lemma RealizeFieldTheory :
    (structureTo.Structure K) ⊨ FieldTheory := -- squeeze_simp, val_zero
  begin
    intros ϕ h,
    cases h,
    {apply (comm_ring_to.RealizeRingTheory K h)},
    repeat {cases h},
     {
       intro,
       unfold fol.bd_or,
       simp only [ModelTo.realize_one, mul, structureTo.FuncMap,
         fin.val_zero', realize_bounded_formula_not, structureTo.BinariesMap,
         fin.val_eq_coe, dvector.last, realize_bounded_formula_ex, realize_bounded_term_bd_app,
         realize_bounded_formula, realize_bounded_term, fin.val_one, dvector.nth,
         ModelTo.realize_zero],
       apply is_field.mul_inv_cancel,
       apply K_is_field,
     },
    {unfold fol.realize_sentence, simp},
  end

  def ModelOfFieldTheory : Model FieldTheory.{u} :=
  ⟨ structureTo.Structure K ,  RealizeFieldTheory ⟩

end field_to

namespace FieldModelTo

  variables {M : Structure RingSignature} [h : fact (M ⊨ FieldTheory)]
  -- M inherits instances of 0 1 - + * from Rings.ModelTo

  include h

  instance RingModel : fact (M ⊨ RingTheory) :=
  ⟨ begin intros f hf, apply h.elim, apply RingTheorySubFieldTheory, exact hf end ⟩

  instance CommRing : comm_ring M := ModelTo.CommRing

  instance Ring : ring M := comm_ring.to_ring M

  lemma zero_ne_one : (0 : M) ≠ 1 := by simpa using (h.elim NonTrivInFieldTheory)

  lemma mul_inv (a : M) (ha : a ≠ 0) : (∃ (b : M), a * b = 1) :=
  let hmulinv := h.elim MulInvInFieldTheory in by simpa using hmulinv a ha

  instance is_field : fact (is_field M) :=
  ⟨⟨
    ⟨ 0 , 1 , zero_ne_one ⟩,
    Rings.ModelTo.mul_comm,
    mul_inv
  ⟩⟩

  noncomputable instance field : field M := is_field.to_field M FieldModelTo.is_field.elim

end FieldModelTo

-- GenPoly n is the polynomial aₙ₊₁ xⁿ + ⋯ + a₂ x + a₁
-- where aₖ = x_ k and x = x_ 0, i.e. we are seeing the variables x_ k with 0 < k as coefficients
-- and x_ 0 as the indeterminate
@[simp] def GenPoly : Π (n : ℕ), bounded_term RingSignature (n + 2)
| 0       := x_ 1
| (n + 1) := (x_ (fin.succ n.succ)) * ((x_ 0) ^ (n + 1)) +
  bounded_preterm.lift_succ (GenPoly n)

-- Adds a monic term at the beginning of GenPoly.
-- We will require that these polynomials have solutions.
-- We cannot just use GenPoly as we need the polynomials to have 0 < deg
@[simp] def GenMonicPoly (n : ℕ) : bounded_term RingSignature (n + 2) :=
(x_ 0) ^ (n + 2) + GenPoly n

-- lemma comeone {A : Type u} [ring A] {p q : polynomial A} : (p.degree) ≤ (p + q).degree := by
-- hint

-- #check @nat.lt_add_one_iff

--   #check @polynomial.degree_add_eq_left_of_degree_lt

-- #check polynomial.degree
-- #check with_bot ℕ

-- lemma TermToPolyBaseIdk {A : Type u} [comm_ring A] (a : A) :
--   comm_ring_to.TermToPoly ([a]) x_ 1 = polynomial.C a := rfl

-- lemma TermToPolyInd {A : Type u} [comm_ring A] {n} (as : dvector A (n + 1)) (k : fin (n + 1)) :
--   comm_ring_to.TermToPoly as x_(k + 1) = polynomial.C (dvector.nth' as k) :=
-- begin
--   unfold comm_ring_to.TermToPoly,
--   unfold bounded_term.rec,
--   simp,
--   unfold fin.cases,
--   unfold fin.induction,
--   simp,
--   sorry,
-- end

-- lemma TermToPolyMonomial

-- lemma GenPolyDeg {A : Type u} [comm_ring A] [decidable_eq A]:
--   Π {n} {a_ : dvector A (n + 1)},
--   (comm_ring_to.TermToPoly.{u u} a_ (GenPoly n)).degree ≤ n
-- | nat.zero (a :: []) :=
-- begin
--   unfold GenPoly,
--   rw TermToPolyBaseIdk a,
--   by_cases ha : a = 0,
--   {rw ha, simp},
--   {rw polynomial.degree_C ha, simp},
-- end
-- | (nat.succ n) as :=
-- begin
--   unfold GenPoly,
--   rw comm_ring_to.TermToPolyAdd,
--   have hq : (comm_ring_to.TermToPoly as (x_(n + 2) * x_ 0 ^ (n + 1))).degree ≤ n.succ,
--   {
--     rw comm_ring_to.TermToPolyMul,

--     -- by_cases ha : as.last = 0,
--     -- {
--     --   rw TermToPolyInd as ⟨ n + 1 , _ ⟩,

--     --   unfold pow,
--     --   simp,
--     --   -- rw ha,

--     -- },
--     -- sorry,
--   },
--   have hle : (comm_ring_to.TermToPoly (a :: as) (lift_bounded_term1 (GenPoly n))).degree <
--   (comm_ring_to.TermToPoly (a :: as) (x_(n + 2) * x_ 0 ^ (n + 1))).degree,
--   sorry,
--   rw (polynomial.degree_add_eq_left_of_degree_lt hle),
--   exact hq,
-- end

--   sorry,
--   have hle :
--   sorry,
-- end

-- should be in the library
lemma with_bot.succ_lt_succ_succ {n : ℕ} : (n + 1 : with_bot ℕ) < ↑n.succ + 1 := by tidy

lemma DegPow {A : Type u} [comm_ring A] [nontrivial A] {n m : ℕ} {as : dvector A (n + 1)} :
(polynomial.TermEvaluatedAtCoeffs as (x_ 0 ^ m)).degree = m :=
begin
  rw polynomial.TermEvaluatedAtCoeffsPow,
  apply polynomial.degree_X_pow,
end

section PolyLemmas

variables {A : Type u} [hcomm : comm_ring A] [hnt : nontrivial A] [hde : decidable_eq A]

include hcomm hnt hde

lemma GenPolyDegree : Π {n} {as : dvector A (n + 1)},
  (polynomial.TermEvaluatedAtCoeffs as (GenPoly n)).degree < n.succ
| nat.zero as :=
begin
  simp only [GenPoly],
  have h : polynomial.TermEvaluatedAtCoeffs as x_ 1 = polynomial.C (as.nth' 0),
  {rw ← polynomial.TermEvaluatedAtCoeffsCoeff, simp,},
  rw h,
  by_cases h0 : as.nth' 0 = 0,
  {
    rw h0,
    rw polynomial.C_0,
    rw polynomial.degree_zero,
    exact dec_trivial,
  },
  {
    rw polynomial.degree_C h0,
    exact dec_trivial,
  }
end
| (nat.succ n) as :=
begin
  simp only [GenPoly, polynomial.TermEvaluatedAtCoeffsAdd],
  rw polynomial.TermEvaluatedAtCoeffsMonomial,
  rw polynomial.lift_succ_remove_last,
  -- have am : dvector A n.succ := dvector.remove_mth (n + 2) as,
  apply has_le.le.trans_lt (polynomial.degree_add_le _ _),
  apply max_lt,
  {
    apply has_le.le.trans_lt (polynomial.degree_monomial_le _ _),
    exact with_bot.succ_lt_succ_succ,
  },
  {
    apply lt_trans (@GenPolyDegree n (dvector.remove_mth (n.succ + 2) as)),
    exact with_bot.succ_lt_succ_succ,
  },
end

lemma GenMonicPolyNonConst {n} {as : dvector A (n + 1)} :
  polynomial.degree (polynomial.TermEvaluatedAtCoeffs as (GenMonicPoly n)) ≠ 0 :=
begin
  unfold GenMonicPoly,
  rw polynomial.TermEvaluatedAtCoeffsAdd,
  apply ne_of_gt,
  have hp : (polynomial.TermEvaluatedAtCoeffs as (x_ 0 ^ (n + 2))).degree = n + 2,
  {apply DegPow},
  have hq : (polynomial.TermEvaluatedAtCoeffs as (GenPoly n)).degree < n + 2,
  {
    apply lt_trans GenPolyDegree,
    exact with_bot.succ_lt_succ_succ,
    exact hnt, -- why??
    exact hde, -- why??
  },
  have hle : (polynomial.TermEvaluatedAtCoeffs as (GenPoly n)).degree
    < (polynomial.TermEvaluatedAtCoeffs as (x_ 0 ^ (n + 2))).degree,
  { rw hp, exact hq },
  rw (polynomial.degree_add_eq_left_of_degree_lt hle),
  rw hp,
  exact dec_trivial,
end

end PolyLemmas
/-
From fol - takes a formula with n free variables and sticks n many ∀'s in front
@[simp] def bd_alls : Π n : ℕ, bounded_formula L n → sentence L
| 0     f := f
| (n+1) f := bd_alls n (∀' f) -- bd_alls' (n+1) 0 (f.cast_eqr (zero_add (n+1)))
-/

-- @[simp] lemma bd_alls_0 {L} {ϕ} : @bd_alls L 0 ϕ = ϕ := rfl

@[simp] def AllGenMonicPolyHasRoot (n : ℕ) : sentence RingSignature :=
fol.bd_alls (n + 1) (∃' GenMonicPoly n ≃ 0)

-- #reduce GenMonicPolyHasSolution 1

def ACF : Theory RingSignature := FieldTheory ∪ (set.range AllGenMonicPolyHasRoot)
-- the latter stands for {GenMonicPolyHasSolution n | n : ℕ}

-- ACF with p = 0 chucked in
def ACFₚ (p : ℕ) : Theory RingSignature := set.insert (p ≃ 0) ACF

-- ACF with p ≠ 0 chucked in for every natural p
def ACF₀ : Theory RingSignature := ACF ∪ (set.range (λ p : ℕ, p ≄ 0))

namespace is_alg_closed_to

  variables {K : Type u} [field K] [is_alg_closed K] [decidable_eq K]

  -- #check is_alg_closed.of_exists_root

  -- should be in the library
  theorem is_alg_closed.exists_root (f : polynomial K) (h : f.degree ≠ 0) :
    ∃ x : K, f.eval x = 0 := polynomial.exists_root_of_splits _ (is_alg_closed.splits f) h

  -- lemma ExistsRoot {n} (a_ : dvector K (n + 1)) :
  --   ∃ x : K, polynomial.eval x (ToPoly (GenMonicPoly n)) = 0 :=
  -- sorry

  -- @[simp] lemma notsure {A : Type u} [has_one A] [has_mul A] {x : M} {n m : ℕ} {a_ : dvector M n} :
  --   realize_bounded_term (dvector.cons x a_) (x_ 0 ^ m) dvector.nil = x ^ m := _

  lemma RealizeACF : structureTo.Structure K ⊨ ACF :=
  begin
    intros ϕ h,
    cases h,
    {apply field_to.RealizeFieldTheory h},
    {
      cases h with n hϕ,
      rw ← hϕ,
      unfold AllGenMonicPolyHasRoot,
      rw realize_sentence_bd_alls,
      simp only [realize_bounded_formula_ex, realize_bounded_formula, ModelTo.realize_zero, zero],
      intro as,
      have root := is_alg_closed.exists_root
        (polynomial.TermEvaluatedAtCoeffs as (GenMonicPoly n)) GenMonicPolyNonConst,
      cases root with x hx,
      use x,
      rw polynomial.eval_eq_realize_bounded_term at hx,
      exact hx,
    },
  end

end is_alg_closed_to

end Fields

-- #lint