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

! This file was ported from Lean 3 source module analysis.schwartz_space
! leanprover-community/mathlib commit b31173ee05c911d61ad6a05bd2196835c932e0ec
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Calculus.ContDiff
import Mathbin.Analysis.LocallyConvex.WithSeminorms
import Mathbin.Topology.Algebra.UniformFilterBasis
import Mathbin.Topology.ContinuousFunction.Bounded
import Mathbin.Tactic.Positivity
import Mathbin.Analysis.SpecialFunctions.Pow

/-!
# Schwartz space

This file defines the Schwartz space. Usually, the Schwartz space is defined as the set of smooth
functions $f : ℝ^n → ℂ$ such that there exists $C_{αβ} > 0$ with $$|x^α ∂^β f(x)| < C_{αβ}$$ for
all $x ∈ ℝ^n$ and for all multiindices $α, β$.
In mathlib, we use a slightly different approach and define define the Schwartz space as all
smooth functions `f : E → F`, where `E` and `F` are real normed vector spaces such that for all
natural numbers `k` and `n` we have uniform bounds `‖x‖^k * ‖iterated_fderiv ℝ n f x‖ < C`.
This approach completely avoids using partial derivatives as well as polynomials.
We construct the topology on the Schwartz space by a family of seminorms, which are the best
constants in the above estimates, which is by abstract theory from
`seminorm_family.module_filter_basis` and `with_seminorms.to_locally_convex_space` turns the
Schwartz space into a locally convex topological vector space.

## Main definitions

* `schwartz_map`: The Schwartz space is the space of smooth functions such that all derivatives
decay faster than any power of `‖x‖`.
* `schwartz_map.seminorm`: The family of seminorms as described above
* `schwartz_map.fderiv_clm`: The differential as a continuous linear map
`𝓢(E, F) →L[𝕜] 𝓢(E, E →L[ℝ] F)`

## Main statements

* `schwartz_map.uniform_add_group` and `schwartz_map.locally_convex`: The Schwartz space is a
locally convex topological vector space.

## Implementation details

The implementation of the seminorms is taken almost literally from `continuous_linear_map.op_norm`.

## Notation

* `𝓢(E, F)`: The Schwartz space `schwartz_map E F` localized in `schwartz_space`

## Tags

Schwartz space, tempered distributions
-/

noncomputable section

variable {𝕜 𝕜' E F : Type _}

variable [NormedAddCommGroup E] [NormedSpace ℝ E]

variable [NormedAddCommGroup F] [NormedSpace ℝ F]

variable (E F)

/-- A function is a Schwartz function if it is smooth and all derivatives decay faster than
  any power of `‖x‖`. -/
structure SchwartzMap where
  toFun : E → F
  smooth' : ContDiff ℝ ⊤ to_fun
  decay' : ∀ k n : ℕ, ∃ C : ℝ, ∀ x, ‖x‖ ^ k * ‖iteratedFderiv ℝ n to_fun x‖ ≤ C
#align schwartz_map SchwartzMap

-- mathport name: «expr𝓢( , )»
scoped[SchwartzSpace] notation "𝓢(" E ", " F ")" => SchwartzMap E F

variable {E F}

namespace SchwartzMap

instance : Coe 𝓢(E, F) (E → F) :=
  ⟨toFun⟩

instance funLike : FunLike 𝓢(E, F) E fun _ => F
    where
  coe f := f.toFun
  coe_injective' f g h := by cases f <;> cases g <;> congr
#align schwartz_map.fun_like SchwartzMap.funLike

/-- Helper instance for when there's too many metavariables to apply `fun_like.has_coe_to_fun`. -/
instance : CoeFun 𝓢(E, F) fun _ => E → F :=
  ⟨fun p => p.toFun⟩

/-- All derivatives of a Schwartz function are rapidly decaying. -/
theorem decay (f : 𝓢(E, F)) (k n : ℕ) :
    ∃ (C : ℝ)(hC : 0 < C), ∀ x, ‖x‖ ^ k * ‖iteratedFderiv ℝ n f x‖ ≤ C :=
  by
  rcases f.decay' k n with ⟨C, hC⟩
  exact ⟨max C 1, by positivity, fun x => (hC x).trans (le_max_left _ _)⟩
#align schwartz_map.decay SchwartzMap.decay

/-- Every Schwartz function is smooth. -/
theorem smooth (f : 𝓢(E, F)) (n : ℕ∞) : ContDiff ℝ n f :=
  f.smooth'.of_le le_top
#align schwartz_map.smooth SchwartzMap.smooth

/-- Every Schwartz function is continuous. -/
@[continuity, protected]
theorem continuous (f : 𝓢(E, F)) : Continuous f :=
  (f.smooth 0).Continuous
#align schwartz_map.continuous SchwartzMap.continuous

/-- Every Schwartz function is differentiable. -/
@[protected]
theorem differentiable (f : 𝓢(E, F)) : Differentiable ℝ f :=
  (f.smooth 1).Differentiable rfl.le
#align schwartz_map.differentiable SchwartzMap.differentiable

@[ext]
theorem ext {f g : 𝓢(E, F)} (h : ∀ x, (f : E → F) x = g x) : f = g :=
  FunLike.ext f g h
#align schwartz_map.ext SchwartzMap.ext

section IsO

variable (f : 𝓢(E, F))

/-- Auxiliary lemma, used in proving the more general result `is_O_cocompact_zpow`. -/
theorem isO_cocompact_zpow_neg_nat (k : ℕ) :
    Asymptotics.IsO (Filter.cocompact E) f fun x => ‖x‖ ^ (-k : ℤ) :=
  by
  obtain ⟨d, hd, hd'⟩ := f.decay k 0
  simp_rw [norm_iteratedFderiv_zero] at hd'
  simp_rw [Asymptotics.IsO, Asymptotics.IsOWith]
  refine' ⟨d, Filter.Eventually.filter_mono Filter.cocompact_le_cofinite _⟩
  refine' (Filter.eventually_cofinite_ne 0).mp (Filter.eventually_of_forall fun x hx => _)
  rwa [Real.norm_of_nonneg (zpow_nonneg (norm_nonneg _) _), zpow_neg, ← div_eq_mul_inv, le_div_iff']
  exacts[hd' x, zpow_pos_of_pos (norm_pos_iff.mpr hx) _]
#align schwartz_map.is_O_cocompact_zpow_neg_nat SchwartzMap.isO_cocompact_zpow_neg_nat

theorem isO_cocompact_rpow [ProperSpace E] (s : ℝ) :
    Asymptotics.IsO (Filter.cocompact E) f fun x => ‖x‖ ^ s :=
  by
  let k := ⌈-s⌉₊
  have hk : -(k : ℝ) ≤ s := neg_le.mp (Nat.le_ceil (-s))
  refine' (is_O_cocompact_zpow_neg_nat f k).trans _
  refine'
    (_ : Asymptotics.IsO Filter.atTop (fun x : ℝ => x ^ (-k : ℤ)) fun x : ℝ => x ^ s).comp_tendsto
      tendsto_norm_cocompact_atTop
  simp_rw [Asymptotics.IsO, Asymptotics.IsOWith]
  refine' ⟨1, Filter.eventually_of_mem (Filter.eventually_ge_atTop 1) fun x hx => _⟩
  rw [one_mul, Real.norm_of_nonneg (Real.rpow_nonneg_of_nonneg (zero_le_one.trans hx) _),
    Real.norm_of_nonneg (zpow_nonneg (zero_le_one.trans hx) _), ← Real.rpow_int_cast, Int.cast_neg,
    Int.cast_ofNat]
  exact Real.rpow_le_rpow_of_exponent_le hx hk
#align schwartz_map.is_O_cocompact_rpow SchwartzMap.isO_cocompact_rpow

theorem isO_cocompact_zpow [ProperSpace E] (k : ℤ) :
    Asymptotics.IsO (Filter.cocompact E) f fun x => ‖x‖ ^ k := by
  simpa only [Real.rpow_int_cast] using is_O_cocompact_rpow f k
#align schwartz_map.is_O_cocompact_zpow SchwartzMap.isO_cocompact_zpow

end IsO

section Aux

theorem bounds_nonempty (k n : ℕ) (f : 𝓢(E, F)) :
    ∃ c : ℝ, c ∈ { c : ℝ | 0 ≤ c ∧ ∀ x : E, ‖x‖ ^ k * ‖iteratedFderiv ℝ n f x‖ ≤ c } :=
  let ⟨M, hMp, hMb⟩ := f.decay k n
  ⟨M, le_of_lt hMp, hMb⟩
#align schwartz_map.bounds_nonempty SchwartzMap.bounds_nonempty

theorem bounds_bddBelow (k n : ℕ) (f : 𝓢(E, F)) :
    BddBelow { c | 0 ≤ c ∧ ∀ x, ‖x‖ ^ k * ‖iteratedFderiv ℝ n f x‖ ≤ c } :=
  ⟨0, fun _ ⟨hn, _⟩ => hn⟩
#align schwartz_map.bounds_bdd_below SchwartzMap.bounds_bddBelow

theorem decay_add_le_aux (k n : ℕ) (f g : 𝓢(E, F)) (x : E) :
    ‖x‖ ^ k * ‖iteratedFderiv ℝ n (f + g) x‖ ≤
      ‖x‖ ^ k * ‖iteratedFderiv ℝ n f x‖ + ‖x‖ ^ k * ‖iteratedFderiv ℝ n g x‖ :=
  by
  rw [← mul_add]
  refine' mul_le_mul_of_nonneg_left _ (by positivity)
  convert norm_add_le _ _
  exact iteratedFderiv_add_apply (f.smooth _) (g.smooth _)
#align schwartz_map.decay_add_le_aux SchwartzMap.decay_add_le_aux

theorem decay_neg_aux (k n : ℕ) (f : 𝓢(E, F)) (x : E) :
    ‖x‖ ^ k * ‖iteratedFderiv ℝ n (-f) x‖ = ‖x‖ ^ k * ‖iteratedFderiv ℝ n f x‖ :=
  by
  nth_rw 4 [← norm_neg]
  congr
  exact iteratedFderiv_neg_apply
#align schwartz_map.decay_neg_aux SchwartzMap.decay_neg_aux

variable [NormedField 𝕜] [NormedSpace 𝕜 F] [SMulCommClass ℝ 𝕜 F]

theorem decay_smul_aux (k n : ℕ) (f : 𝓢(E, F)) (c : 𝕜) (x : E) :
    ‖x‖ ^ k * ‖iteratedFderiv ℝ n (c • f) x‖ = ‖c‖ * ‖x‖ ^ k * ‖iteratedFderiv ℝ n f x‖ := by
  rw [mul_comm ‖c‖, mul_assoc, iteratedFderiv_const_smul_apply (f.smooth _), norm_smul]
#align schwartz_map.decay_smul_aux SchwartzMap.decay_smul_aux

end Aux

section SeminormAux

/-- Helper definition for the seminorms of the Schwartz space. -/
@[protected]
def seminormAux (k n : ℕ) (f : 𝓢(E, F)) : ℝ :=
  infₛ { c | 0 ≤ c ∧ ∀ x, ‖x‖ ^ k * ‖iteratedFderiv ℝ n f x‖ ≤ c }
#align schwartz_map.seminorm_aux SchwartzMap.seminormAux

theorem seminormAux_nonneg (k n : ℕ) (f : 𝓢(E, F)) : 0 ≤ f.seminormAux k n :=
  le_cinfₛ (bounds_nonempty k n f) fun _ ⟨hx, _⟩ => hx
#align schwartz_map.seminorm_aux_nonneg SchwartzMap.seminormAux_nonneg

theorem le_seminormAux (k n : ℕ) (f : 𝓢(E, F)) (x : E) :
    ‖x‖ ^ k * ‖iteratedFderiv ℝ n (⇑f) x‖ ≤ f.seminormAux k n :=
  le_cinfₛ (bounds_nonempty k n f) fun y ⟨_, h⟩ => h x
#align schwartz_map.le_seminorm_aux SchwartzMap.le_seminormAux

/-- If one controls the norm of every `A x`, then one controls the norm of `A`. -/
theorem seminormAux_le_bound (k n : ℕ) (f : 𝓢(E, F)) {M : ℝ} (hMp : 0 ≤ M)
    (hM : ∀ x, ‖x‖ ^ k * ‖iteratedFderiv ℝ n f x‖ ≤ M) : f.seminormAux k n ≤ M :=
  cinfₛ_le (bounds_bddBelow k n f) ⟨hMp, hM⟩
#align schwartz_map.seminorm_aux_le_bound SchwartzMap.seminormAux_le_bound

end SeminormAux

/-! ### Algebraic properties -/

section Smul

variable [NormedField 𝕜] [NormedSpace 𝕜 F] [SMulCommClass ℝ 𝕜 F] [NormedField 𝕜'] [NormedSpace 𝕜' F]
  [SMulCommClass ℝ 𝕜' F]

instance : SMul 𝕜 𝓢(E, F) :=
  ⟨fun c f =>
    { toFun := c • f
      smooth' := (f.smooth _).const_smul c
      decay' := fun k n =>
        by
        refine' ⟨f.seminorm_aux k n * (‖c‖ + 1), fun x => _⟩
        have hc : 0 ≤ ‖c‖ := by positivity
        refine' le_trans _ ((mul_le_mul_of_nonneg_right (f.le_seminorm_aux k n x) hc).trans _)
        · apply Eq.le
          rw [mul_comm _ ‖c‖, ← mul_assoc]
          exact decay_smul_aux k n f c x
        · apply mul_le_mul_of_nonneg_left _ (f.seminorm_aux_nonneg k n)
          linarith }⟩

@[simp]
theorem smul_apply {f : 𝓢(E, F)} {c : 𝕜} {x : E} : (c • f) x = c • f x :=
  rfl
#align schwartz_map.smul_apply SchwartzMap.smul_apply

instance [SMul 𝕜 𝕜'] [IsScalarTower 𝕜 𝕜' F] : IsScalarTower 𝕜 𝕜' 𝓢(E, F) :=
  ⟨fun a b f => ext fun x => smul_assoc a b (f x)⟩

instance [SMulCommClass 𝕜 𝕜' F] : SMulCommClass 𝕜 𝕜' 𝓢(E, F) :=
  ⟨fun a b f => ext fun x => smul_comm a b (f x)⟩

theorem seminormAux_smul_le (k n : ℕ) (c : 𝕜) (f : 𝓢(E, F)) :
    (c • f).seminormAux k n ≤ ‖c‖ * f.seminormAux k n :=
  by
  refine'
    (c • f).seminormAux_le_bound k n (mul_nonneg (norm_nonneg _) (seminorm_aux_nonneg _ _ _))
      fun x => (decay_smul_aux k n f c x).le.trans _
  rw [mul_assoc]
  exact mul_le_mul_of_nonneg_left (f.le_seminorm_aux k n x) (norm_nonneg _)
#align schwartz_map.seminorm_aux_smul_le SchwartzMap.seminormAux_smul_le

instance hasNsmul : SMul ℕ 𝓢(E, F) :=
  ⟨fun c f =>
    { toFun := c • f
      smooth' := (f.smooth _).const_smul c
      decay' :=
        by
        have : c • (f : E → F) = (c : ℝ) • f := by
          ext x
          simp only [Pi.smul_apply, ← nsmul_eq_smul_cast]
        simp only [this]
        exact ((c : ℝ) • f).decay' }⟩
#align schwartz_map.has_nsmul SchwartzMap.hasNsmul

instance hasZsmul : SMul ℤ 𝓢(E, F) :=
  ⟨fun c f =>
    { toFun := c • f
      smooth' := (f.smooth _).const_smul c
      decay' :=
        by
        have : c • (f : E → F) = (c : ℝ) • f := by
          ext x
          simp only [Pi.smul_apply, ← zsmul_eq_smul_cast]
        simp only [this]
        exact ((c : ℝ) • f).decay' }⟩
#align schwartz_map.has_zsmul SchwartzMap.hasZsmul

end Smul

section Zero

instance : Zero 𝓢(E, F) :=
  ⟨{  toFun := fun _ => 0
      smooth' := contDiff_const
      decay' := fun _ _ => ⟨1, fun _ => by simp⟩ }⟩

instance : Inhabited 𝓢(E, F) :=
  ⟨0⟩

theorem coe_zero : ↑(0 : 𝓢(E, F)) = (0 : E → F) :=
  rfl
#align schwartz_map.coe_zero SchwartzMap.coe_zero

@[simp]
theorem coeFn_zero : coeFn (0 : 𝓢(E, F)) = (0 : E → F) :=
  rfl
#align schwartz_map.coe_fn_zero SchwartzMap.coeFn_zero

@[simp]
theorem zero_apply {x : E} : (0 : 𝓢(E, F)) x = 0 :=
  rfl
#align schwartz_map.zero_apply SchwartzMap.zero_apply

theorem seminormAux_zero (k n : ℕ) : (0 : 𝓢(E, F)).seminormAux k n = 0 :=
  le_antisymm (seminormAux_le_bound k n _ rfl.le fun _ => by simp [Pi.zero_def])
    (seminormAux_nonneg _ _ _)
#align schwartz_map.seminorm_aux_zero SchwartzMap.seminormAux_zero

end Zero

section Neg

instance : Neg 𝓢(E, F) :=
  ⟨fun f =>
    ⟨-f, (f.smooth _).neg, fun k n =>
      ⟨f.seminormAux k n, fun x => (decay_neg_aux k n f x).le.trans (f.le_seminormAux k n x)⟩⟩⟩

end Neg

section Add

instance : Add 𝓢(E, F) :=
  ⟨fun f g =>
    ⟨f + g, (f.smooth _).add (g.smooth _), fun k n =>
      ⟨f.seminormAux k n + g.seminormAux k n, fun x =>
        (decay_add_le_aux k n f g x).trans
          (add_le_add (f.le_seminormAux k n x) (g.le_seminormAux k n x))⟩⟩⟩

@[simp]
theorem add_apply {f g : 𝓢(E, F)} {x : E} : (f + g) x = f x + g x :=
  rfl
#align schwartz_map.add_apply SchwartzMap.add_apply

theorem seminormAux_add_le (k n : ℕ) (f g : 𝓢(E, F)) :
    (f + g).seminormAux k n ≤ f.seminormAux k n + g.seminormAux k n :=
  (f + g).seminormAux_le_bound k n
    (add_nonneg (seminormAux_nonneg _ _ _) (seminormAux_nonneg _ _ _)) fun x =>
    (decay_add_le_aux k n f g x).trans <|
      add_le_add (f.le_seminormAux k n x) (g.le_seminormAux k n x)
#align schwartz_map.seminorm_aux_add_le SchwartzMap.seminormAux_add_le

end Add

section Sub

instance : Sub 𝓢(E, F) :=
  ⟨fun f g =>
    ⟨f - g, (f.smooth _).sub (g.smooth _), by
      intro k n
      refine' ⟨f.seminorm_aux k n + g.seminorm_aux k n, fun x => _⟩
      refine' le_trans _ (add_le_add (f.le_seminorm_aux k n x) (g.le_seminorm_aux k n x))
      rw [sub_eq_add_neg]
      rw [← decay_neg_aux k n g x]
      convert decay_add_le_aux k n f (-g) x⟩⟩

-- exact fails with deterministic timeout
@[simp]
theorem sub_apply {f g : 𝓢(E, F)} {x : E} : (f - g) x = f x - g x :=
  rfl
#align schwartz_map.sub_apply SchwartzMap.sub_apply

end Sub

section AddCommGroup

instance : AddCommGroup 𝓢(E, F) :=
  FunLike.coe_injective.AddCommGroup _ rfl (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl)
    (fun _ _ => rfl) fun _ _ => rfl

variable (E F)

/-- Coercion as an additive homomorphism. -/
def coeHom : 𝓢(E, F) →+ E → F where
  toFun f := f
  map_zero' := coe_zero
  map_add' _ _ := rfl
#align schwartz_map.coe_hom SchwartzMap.coeHom

variable {E F}

theorem coe_coeHom : (coeHom E F : 𝓢(E, F) → E → F) = coeFn :=
  rfl
#align schwartz_map.coe_coe_hom SchwartzMap.coe_coeHom

theorem coeHom_injective : Function.Injective (coeHom E F) :=
  by
  rw [coe_coe_hom]
  exact FunLike.coe_injective
#align schwartz_map.coe_hom_injective SchwartzMap.coeHom_injective

end AddCommGroup

section Module

variable [NormedField 𝕜] [NormedSpace 𝕜 F] [SMulCommClass ℝ 𝕜 F]

instance : Module 𝕜 𝓢(E, F) :=
  coeHom_injective.Module 𝕜 (coeHom E F) fun _ _ => rfl

end Module

section Seminorms

/-! ### Seminorms on Schwartz space-/

variable [NormedField 𝕜] [NormedSpace 𝕜 F] [SMulCommClass ℝ 𝕜 F]

variable (𝕜)

/-- The seminorms of the Schwartz space given by the best constants in the definition of
`𝓢(E, F)`. -/
@[protected]
def seminorm (k n : ℕ) : Seminorm 𝕜 𝓢(E, F) :=
  Seminorm.ofSmulLe (seminormAux k n) (seminormAux_zero k n) (seminormAux_add_le k n)
    (seminormAux_smul_le k n)
#align schwartz_map.seminorm SchwartzMap.seminorm

/-- If one controls the seminorm for every `x`, then one controls the seminorm. -/
theorem seminorm_le_bound (k n : ℕ) (f : 𝓢(E, F)) {M : ℝ} (hMp : 0 ≤ M)
    (hM : ∀ x, ‖x‖ ^ k * ‖iteratedFderiv ℝ n f x‖ ≤ M) : Seminorm 𝕜 k n f ≤ M :=
  f.seminormAux_le_bound k n hMp hM
#align schwartz_map.seminorm_le_bound SchwartzMap.seminorm_le_bound

/-- The seminorm controls the Schwartz estimate for any fixed `x`. -/
theorem le_seminorm (k n : ℕ) (f : 𝓢(E, F)) (x : E) :
    ‖x‖ ^ k * ‖iteratedFderiv ℝ n f x‖ ≤ Seminorm 𝕜 k n f :=
  f.le_seminormAux k n x
#align schwartz_map.le_seminorm SchwartzMap.le_seminorm

theorem norm_iteratedFderiv_le_seminorm (f : 𝓢(E, F)) (n : ℕ) (x₀ : E) :
    ‖iteratedFderiv ℝ n f x₀‖ ≤ (SchwartzMap.seminorm 𝕜 0 n) f :=
  by
  have := SchwartzMap.le_seminorm 𝕜 0 n f x₀
  rwa [pow_zero, one_mul] at this
#align schwartz_map.norm_iterated_fderiv_le_seminorm SchwartzMap.norm_iteratedFderiv_le_seminorm

theorem norm_pow_mul_le_seminorm (f : 𝓢(E, F)) (k : ℕ) (x₀ : E) :
    ‖x₀‖ ^ k * ‖f x₀‖ ≤ (SchwartzMap.seminorm 𝕜 k 0) f :=
  by
  have := SchwartzMap.le_seminorm 𝕜 k 0 f x₀
  rwa [norm_iteratedFderiv_zero] at this
#align schwartz_map.norm_pow_mul_le_seminorm SchwartzMap.norm_pow_mul_le_seminorm

theorem norm_le_seminorm (f : 𝓢(E, F)) (x₀ : E) : ‖f x₀‖ ≤ (SchwartzMap.seminorm 𝕜 0 0) f :=
  by
  have := norm_pow_mul_le_seminorm 𝕜 f 0 x₀
  rwa [pow_zero, one_mul] at this
#align schwartz_map.norm_le_seminorm SchwartzMap.norm_le_seminorm

end Seminorms

section Topology

/-! ### The topology on the Schwartz space-/

variable [NormedField 𝕜] [NormedSpace 𝕜 F] [SMulCommClass ℝ 𝕜 F]

variable (𝕜 E F)

/-- The family of Schwartz seminorms. -/
def schwartzSeminormFamily : SeminormFamily 𝕜 𝓢(E, F) (ℕ × ℕ) := fun n => Seminorm 𝕜 n.1 n.2
#align schwartz_seminorm_family schwartzSeminormFamily

@[simp]
theorem schwartzSeminormFamily_apply (n k : ℕ) :
    schwartzSeminormFamily 𝕜 E F (n, k) = SchwartzMap.seminorm 𝕜 n k :=
  rfl
#align schwartz_map.schwartz_seminorm_family_apply SchwartzMap.schwartzSeminormFamily_apply

@[simp]
theorem schwartzSeminormFamily_apply_zero :
    schwartzSeminormFamily 𝕜 E F 0 = SchwartzMap.seminorm 𝕜 0 0 :=
  rfl
#align schwartz_map.schwartz_seminorm_family_apply_zero SchwartzMap.schwartzSeminormFamily_apply_zero

instance : TopologicalSpace 𝓢(E, F) :=
  (schwartzSeminormFamily ℝ E F).ModuleFilterBasis.topology'

theorem schwartzWithSeminorms : WithSeminorms (schwartzSeminormFamily 𝕜 E F) :=
  by
  have A : WithSeminorms (schwartzSeminormFamily ℝ E F) := ⟨rfl⟩
  rw [SeminormFamily.withSeminorms_iff_nhds_eq_infᵢ] at A⊢
  rw [A]
  rfl
#align schwartz_with_seminorms schwartzWithSeminorms

variable {𝕜 E F}

instance : ContinuousSMul 𝕜 𝓢(E, F) :=
  by
  rw [(schwartzWithSeminorms 𝕜 E F).withSeminorms_eq]
  exact (schwartzSeminormFamily 𝕜 E F).ModuleFilterBasis.ContinuousSMul

instance : TopologicalAddGroup 𝓢(E, F) :=
  (schwartzSeminormFamily ℝ E F).AddGroupFilterBasis.is_topological_add_group

instance : UniformSpace 𝓢(E, F) :=
  (schwartzSeminormFamily ℝ E F).AddGroupFilterBasis.UniformSpace

instance : UniformAddGroup 𝓢(E, F) :=
  (schwartzSeminormFamily ℝ E F).AddGroupFilterBasis.UniformAddGroup

instance : LocallyConvexSpace ℝ 𝓢(E, F) :=
  (schwartzWithSeminorms ℝ E F).toLocallyConvexSpace

instance : TopologicalSpace.FirstCountableTopology 𝓢(E, F) :=
  (schwartzWithSeminorms ℝ E F).first_countable

end Topology

section fderiv

/-! ### Derivatives of Schwartz functions -/

variable {E F}

/-- The derivative of a Schwartz function as a Schwartz function with values in the
continuous linear maps `E→L[ℝ] F`. -/
@[protected]
def fderiv (f : 𝓢(E, F)) : 𝓢(E, E →L[ℝ] F)
    where
  toFun := fderiv ℝ f
  smooth' := (contDiff_top_iff_fderiv.mp f.smooth').2
  decay' := by
    intro k n
    cases' f.decay' k (n + 1) with C hC
    use C
    intro x
    rw [norm_iteratedFderiv_fderiv]
    exact hC x
#align schwartz_map.fderiv SchwartzMap.fderiv

@[simp, norm_cast]
theorem coe_fderiv (f : 𝓢(E, F)) : ⇑f.fderiv = fderiv ℝ f :=
  rfl
#align schwartz_map.coe_fderiv SchwartzMap.coe_fderiv

@[simp]
theorem fderiv_apply (f : 𝓢(E, F)) (x : E) : f.fderiv x = fderiv ℝ f x :=
  rfl
#align schwartz_map.fderiv_apply SchwartzMap.fderiv_apply

variable (𝕜)

variable [IsROrC 𝕜] [NormedSpace 𝕜 F] [SMulCommClass ℝ 𝕜 F]

/-- The derivative on Schwartz space as a linear map. -/
def fderivLm : 𝓢(E, F) →ₗ[𝕜] 𝓢(E, E →L[ℝ] F)
    where
  toFun := SchwartzMap.fderiv
  map_add' f g :=
    ext fun _ => fderiv_add f.Differentiable.DifferentiableAt g.Differentiable.DifferentiableAt
  map_smul' a f := ext fun _ => fderiv_const_smul f.Differentiable.DifferentiableAt a
#align schwartz_map.fderiv_lm SchwartzMap.fderivLm

@[simp, norm_cast]
theorem fderivLm_apply (f : 𝓢(E, F)) : fderivLm 𝕜 f = SchwartzMap.fderiv f :=
  rfl
#align schwartz_map.fderiv_lm_apply SchwartzMap.fderivLm_apply

/-- The derivative on Schwartz space as a continuous linear map. -/
def fderivClm : 𝓢(E, F) →L[𝕜] 𝓢(E, E →L[ℝ] F)
    where
  cont := by
    change Continuous (fderiv_lm 𝕜 : 𝓢(E, F) →ₗ[𝕜] 𝓢(E, E →L[ℝ] F))
    refine'
      Seminorm.continuous_from_bounded (schwartzWithSeminorms 𝕜 E F)
        (schwartzWithSeminorms 𝕜 E (E →L[ℝ] F)) _ _
    rintro ⟨k, n⟩
    use {⟨k, n + 1⟩}, 1
    intro f
    simp only [schwartz_seminorm_family_apply, Seminorm.comp_apply, Finset.sup_singleton, one_smul]
    refine' (fderiv_lm 𝕜 f).seminorm_le_bound 𝕜 k n (by positivity) _
    intro x
    rw [fderiv_lm_apply, coe_fderiv, norm_iteratedFderiv_fderiv]
    exact f.le_seminorm 𝕜 k (n + 1) x
  toLinearMap := fderivLm 𝕜
#align schwartz_map.fderiv_clm SchwartzMap.fderivClm

@[simp, norm_cast]
theorem fderivClm_apply (f : 𝓢(E, F)) : fderivClm 𝕜 f = SchwartzMap.fderiv f :=
  rfl
#align schwartz_map.fderiv_clm_apply SchwartzMap.fderivClm_apply

end fderiv

section BoundedContinuousFunction

/-! ### Inclusion into the space of bounded continuous functions -/

open BoundedContinuousFunction

/-- Schwartz functions as bounded continuous functions -/
def toBoundedContinuousFunction (f : 𝓢(E, F)) : E →ᵇ F :=
  BoundedContinuousFunction.ofNormedAddCommGroup f (SchwartzMap.continuous f)
    (SchwartzMap.seminorm ℝ 0 0 f) (norm_le_seminorm ℝ f)
#align schwartz_map.to_bounded_continuous_function SchwartzMap.toBoundedContinuousFunction

@[simp]
theorem toBoundedContinuousFunction_apply (f : 𝓢(E, F)) (x : E) :
    f.toBoundedContinuousFunction x = f x :=
  rfl
#align schwartz_map.to_bounded_continuous_function_apply SchwartzMap.toBoundedContinuousFunction_apply

/-- Schwartz functions as continuous functions -/
def toContinuousMap (f : 𝓢(E, F)) : C(E, F) :=
  f.toBoundedContinuousFunction.toContinuousMap
#align schwartz_map.to_continuous_map SchwartzMap.toContinuousMap

variable (𝕜 E F)

variable [IsROrC 𝕜] [NormedSpace 𝕜 F] [SMulCommClass ℝ 𝕜 F]

/-- The inclusion map from Schwartz functions to bounded continuous functions as a linear map. -/
def toBoundedContinuousFunctionLm : 𝓢(E, F) →ₗ[𝕜] E →ᵇ F
    where
  toFun f := f.toBoundedContinuousFunction
  map_add' f g := by
    ext
    exact add_apply
  map_smul' a f := by
    ext
    exact smul_apply
#align schwartz_map.to_bounded_continuous_function_lm SchwartzMap.toBoundedContinuousFunctionLm

@[simp]
theorem toBoundedContinuousFunctionLm_apply (f : 𝓢(E, F)) (x : E) :
    toBoundedContinuousFunctionLm 𝕜 E F f x = f x :=
  rfl
#align schwartz_map.to_bounded_continuous_function_lm_apply SchwartzMap.toBoundedContinuousFunctionLm_apply

/-- The inclusion map from Schwartz functions to bounded continuous functions as a continuous linear
map. -/
def toBoundedContinuousFunctionClm : 𝓢(E, F) →L[𝕜] E →ᵇ F :=
  { toBoundedContinuousFunctionLm 𝕜 E F with
    cont := by
      change Continuous (to_bounded_continuous_function_lm 𝕜 E F)
      refine'
        Seminorm.continuous_from_bounded (schwartzWithSeminorms 𝕜 E F)
          (normWithSeminorms 𝕜 (E →ᵇ F)) _ fun i => ⟨{0}, 1, fun f => _⟩
      rw [Finset.sup_singleton, one_smul, Seminorm.comp_apply, coe_normSeminorm,
        schwartz_seminorm_family_apply_zero, BoundedContinuousFunction.norm_le (map_nonneg _ _)]
      intro x
      exact norm_le_seminorm 𝕜 _ _ }
#align schwartz_map.to_bounded_continuous_function_clm SchwartzMap.toBoundedContinuousFunctionClm

@[simp]
theorem toBoundedContinuousFunctionClm_apply (f : 𝓢(E, F)) (x : E) :
    toBoundedContinuousFunctionClm 𝕜 E F f x = f x :=
  rfl
#align schwartz_map.to_bounded_continuous_function_clm_apply SchwartzMap.toBoundedContinuousFunctionClm_apply

variable {E}

/-- The Dirac delta distribution -/
def delta (x : E) : 𝓢(E, F) →L[𝕜] F :=
  (BoundedContinuousFunction.evalClm 𝕜 x).comp (toBoundedContinuousFunctionClm 𝕜 E F)
#align schwartz_map.delta SchwartzMap.delta

@[simp]
theorem delta_apply (x₀ : E) (f : 𝓢(E, F)) : delta 𝕜 F x₀ f = f x₀ :=
  rfl
#align schwartz_map.delta_apply SchwartzMap.delta_apply

end BoundedContinuousFunction

end SchwartzMap


/- Natural Number Game using Lean 4 -/
import MyNat.lemma

inductive MyNat where
  | zero : MyNat
  | succ : MyNat -> MyNat
  deriving Repr

open MyNat

notation "ℕ" => MyNat

def nat_to_mynat (n : Nat): ℕ :=
  match n with
  | Nat.zero => MyNat.zero
  | Nat.succ n' => MyNat.succ (nat_to_mynat n')

instance : OfNat ℕ n where
 ofNat := nat_to_mynat n

theorem zero_equal_numeral : 0 = zero := by rfl

def mynat_to_nat (n : ℕ): Nat :=
  match n with
  | MyNat.zero => Nat.zero
  | MyNat.succ n' => Nat.succ (mynat_to_nat n')

def add (m : ℕ) (n : ℕ) : ℕ :=
  match n with
  | zero => m
  | succ n' => succ (add m n') -- n' is n - 1

instance : Add ℕ where
  add := add

example : add 7 8 = 15 := rfl
example : add 0 1 = 1 := rfl
example : (0 : ℕ) + 1 = 1 := rfl

def mul (m n : ℕ) : ℕ :=
  match n with
  | zero => zero
  -- 4 * 3 => 4 + 4 * 2 => 4 + 4 + 4 * 1 => 4 + 4 + 4 => 12
  | succ n' => add m (mul m n')

instance : Mul ℕ where
  mul := mul

example : mul 4 3 = 12 := rfl
example : mul 4 0 = 0 := rfl
example : zero * 4 = 0 := rfl

def pow (m n : ℕ) : ℕ :=
  match n with
  | zero => 1
  -- m ^ (n' + 1) = m * (m ^ n')
  | succ n' => mul m (pow m n')

instance : Pow ℕ ℕ where
  pow := pow

example : pow 1 0 = 1 := rfl
example : pow 2 3 = 8 := rfl
example : pow 3 2 = 9 := rfl
example : pow 0 2 = 0 := rfl
example : zero ^ 0 = 1 := rfl


import data.real.basic

theorem exo (f: real -> real) :
     (forall x, f x > 0)
  -> (forall x y z w: real, x > 0 /\ y > 0 /\ z > 0 /\ w > 0 /\ w*x = y*z ->
        ((f w)^2 + (f x)^2) / (f y^2 + f z^2) = (w^2 + x^2) / (y^2 + z^2)
     )
  -> (forall x > 0, f(x) = x) \/ (forall x > 0, f(x) = 1/x)
  := sorry


import game.world10.level5 -- hide
namespace mynat -- hide
/-

# Inequality world.

## Level 6: `le_antisymm`

In Advanced Addition World you proved

`eq_zero_of_add_right_eq_self (a b : mynat) : a + b = a → b = 0`.

This might be useful in this level.

Another tip: if you want to create a new hypothesis, you can use the `have` tactic.
For example, if you have a hypothesis `hd : a + (c + d) = a` and you want
a hypothesis `h : c + d = 0` then you can write

`have h := eq_zero_of_add_right_eq_self hd,`

-/

/- Lemma
$\le$ is antisymmetric. In other words, if $a\le b$ and $b\le a$ then $a = b$.
-/
theorem le_antisymm (a b : mynat) (hab : a ≤ b) (hba : b ≤ a) : a = b :=
begin [nat_num_game]
  cases hab with c hc,
  cases hba with d hd,
  rw hc at hd,
  rw add_assoc at hd,
  symmetry at hd,
  have h := eq_zero_of_add_right_eq_self hd,
  have h2 := add_right_eq_zero h,
  rw h2 at hc,
  rw hc,
  exact add_zero a,

end
/-
Congratulations -- you just proved that the natural numbers are a partial order!
-/

instance : partial_order mynat := by structure_helper
end mynat -- hide


/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Mario Carneiro
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.multiset.finset_ops
import Mathlib.data.multiset.fold
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# Lattice operations on multisets
-/

namespace multiset

/-! ### sup -/

/-- Supremum of a multiset: `sup {a, b, c} = a ⊔ b ⊔ c` -/
def sup {α : Type u_1} [semilattice_sup_bot α] (s : multiset α) : α :=
  fold has_sup.sup ⊥ s

@[simp] theorem sup_zero {α : Type u_1} [semilattice_sup_bot α] : sup 0 = ⊥ :=
  fold_zero has_sup.sup ⊥

@[simp] theorem sup_cons {α : Type u_1} [semilattice_sup_bot α] (a : α) (s : multiset α) : sup (a ::ₘ s) = a ⊔ sup s :=
  fold_cons_left has_sup.sup ⊥ a s

@[simp] theorem sup_singleton {α : Type u_1} [semilattice_sup_bot α] {a : α} : sup (a ::ₘ 0) = a := sorry

@[simp] theorem sup_add {α : Type u_1} [semilattice_sup_bot α] (s₁ : multiset α) (s₂ : multiset α) : sup (s₁ + s₂) = sup s₁ ⊔ sup s₂ := sorry

theorem sup_le {α : Type u_1} [semilattice_sup_bot α] {s : multiset α} {a : α} : sup s ≤ a ↔ ∀ (b : α), b ∈ s → b ≤ a := sorry

theorem le_sup {α : Type u_1} [semilattice_sup_bot α] {s : multiset α} {a : α} (h : a ∈ s) : a ≤ sup s :=
  iff.mp sup_le (le_refl (sup s)) a h

theorem sup_mono {α : Type u_1} [semilattice_sup_bot α] {s₁ : multiset α} {s₂ : multiset α} (h : s₁ ⊆ s₂) : sup s₁ ≤ sup s₂ :=
  iff.mpr sup_le fun (b : α) (hb : b ∈ s₁) => le_sup (h hb)

@[simp] theorem sup_erase_dup {α : Type u_1} [semilattice_sup_bot α] [DecidableEq α] (s : multiset α) : sup (erase_dup s) = sup s :=
  fold_erase_dup_idem has_sup.sup s ⊥

@[simp] theorem sup_ndunion {α : Type u_1} [semilattice_sup_bot α] [DecidableEq α] (s₁ : multiset α) (s₂ : multiset α) : sup (ndunion s₁ s₂) = sup s₁ ⊔ sup s₂ := sorry

@[simp] theorem sup_union {α : Type u_1} [semilattice_sup_bot α] [DecidableEq α] (s₁ : multiset α) (s₂ : multiset α) : sup (s₁ ∪ s₂) = sup s₁ ⊔ sup s₂ := sorry

@[simp] theorem sup_ndinsert {α : Type u_1} [semilattice_sup_bot α] [DecidableEq α] (a : α) (s : multiset α) : sup (ndinsert a s) = a ⊔ sup s := sorry

/-! ### inf -/

/-- Infimum of a multiset: `inf {a, b, c} = a ⊓ b ⊓ c` -/
def inf {α : Type u_1} [semilattice_inf_top α] (s : multiset α) : α :=
  fold has_inf.inf ⊤ s

@[simp] theorem inf_zero {α : Type u_1} [semilattice_inf_top α] : inf 0 = ⊤ :=
  fold_zero has_inf.inf ⊤

@[simp] theorem inf_cons {α : Type u_1} [semilattice_inf_top α] (a : α) (s : multiset α) : inf (a ::ₘ s) = a ⊓ inf s :=
  fold_cons_left has_inf.inf ⊤ a s

@[simp] theorem inf_singleton {α : Type u_1} [semilattice_inf_top α] {a : α} : inf (a ::ₘ 0) = a := sorry

@[simp] theorem inf_add {α : Type u_1} [semilattice_inf_top α] (s₁ : multiset α) (s₂ : multiset α) : inf (s₁ + s₂) = inf s₁ ⊓ inf s₂ := sorry

theorem le_inf {α : Type u_1} [semilattice_inf_top α] {s : multiset α} {a : α} : a ≤ inf s ↔ ∀ (b : α), b ∈ s → a ≤ b := sorry

theorem inf_le {α : Type u_1} [semilattice_inf_top α] {s : multiset α} {a : α} (h : a ∈ s) : inf s ≤ a :=
  iff.mp le_inf (le_refl (inf s)) a h

theorem inf_mono {α : Type u_1} [semilattice_inf_top α] {s₁ : multiset α} {s₂ : multiset α} (h : s₁ ⊆ s₂) : inf s₂ ≤ inf s₁ :=
  iff.mpr le_inf fun (b : α) (hb : b ∈ s₁) => inf_le (h hb)

@[simp] theorem inf_erase_dup {α : Type u_1} [semilattice_inf_top α] [DecidableEq α] (s : multiset α) : inf (erase_dup s) = inf s :=
  fold_erase_dup_idem has_inf.inf s ⊤

@[simp] theorem inf_ndunion {α : Type u_1} [semilattice_inf_top α] [DecidableEq α] (s₁ : multiset α) (s₂ : multiset α) : inf (ndunion s₁ s₂) = inf s₁ ⊓ inf s₂ := sorry

@[simp] theorem inf_union {α : Type u_1} [semilattice_inf_top α] [DecidableEq α] (s₁ : multiset α) (s₂ : multiset α) : inf (s₁ ∪ s₂) = inf s₁ ⊓ inf s₂ := sorry

@[simp] theorem inf_ndinsert {α : Type u_1} [semilattice_inf_top α] [DecidableEq α] (a : α) (s : multiset α) : inf (ndinsert a s) = a ⊓ inf s := sorry


import game.world3.level8 -- hide
import mynat.mul -- hide
namespace mynat -- hide

/-
# Multiplication World

## Level 9: `mul_left_comm`

You are equipped with

* `mul_assoc (a b c : mynat) : (a * b) * c = a * (b * c)`
* `mul_comm (a b : mynat) : a * b = b * a`

Re-read the docs for `rw` so you know all the tricks.
You can see them in the "tactics" drop-down menu on the left.
-/

/- Lemma
For all natural numbers $a$ $b$ and $c$, we have
$$a(bc)=b(ac)$$
-/
lemma mul_left_comm (a b c : mynat) : a * (b * c) = b * (a * c) :=
begin [nat_num_game]
  rw ←mul_assoc,
  rw mul_comm a,
  rw mul_assoc,
  refl,

end

/-
And now I whisper a magic incantation
-/
attribute [simp] mul_assoc mul_comm mul_left_comm
/-
and all of a sudden Lean can automatically do levels which are
very boring for a human, for example
-/
example (a b c d e : mynat) :
(((a*b)*c)*d)*e=(c*((b*e)*a))*d :=
begin
  simp,
end

/-
If you feel like attempting Advanced Multiplication world
you'll have to do Function World and the Proposition Worlds first.
These worlds assume a certain amount of mathematical maturity
(perhaps 1st year undergraduate level).
Your other possibility is Power World, with the "final boss".
-/

end mynat -- hide


import tactic.nth_rewrite
import data.vector

structure F :=
(a : ℕ)
(v : vector ℕ a)
(p : v.val = [])

example (f : F) : f.v.val = [] := by nth_rewrite 0 [f.p]

structure cat :=
  (O : Type) -- Type of objects
  (H : O → O → Type) -- morphisms (arrows) between objects
  (i : Π o : O, H o o) -- identity morphism
  (c : Π {X Y Z : O} (f : H X Y) (g: H Y Z), H X Z)
  -- if exists X → Y and Y → Z then exists X → Z
  (li : Π {X Y : O} (f : H X Y), c (i X) f = f) -- id ∘ f = f
  (ri : Π {X Y : O} (f : H X Y), c f (i Y) = f) -- f ∘ id = f
  (a : Π {W X Y Z : O} (f : H W X) (g : H X Y) (h : H Y Z), c (c f g) h = c f (c g h))
  -- associativity of morphisms composition: (f ∘ g) ∘ h = f ∘ (g ∘ h)

open tactic

example (C : cat) (W X Y Z : C.O) (f : C.H X Y) (g : C.H W X) (h k : C.H Y Z) :
  C.c (C.c g f) h = C.c g (C.c f h) := by nth_rewrite 0 [C.a] -- rw C.a, OK

example (C : cat) (X Y : C.O) (f : C.H X Y) : C.c f (C.i Y) = f := by nth_rewrite 0 [C.ri]

example (a b : ℕ) (h : b = a) (H : a * a = b * b) :
  b * a = a * a ∧ a * b = a * a :=
begin
  have h₀ := H, have h₁ := H, have h₂ := H, have h₃ := H,
  nth_rewrite 0 h at h₀,     nth_rewrite 1 h at h₁,
  rw h₁,
  split; simp [h₀, h₁] {fail_if_unchanged := ff},
  simp [h₀, ←h₁],
end

axiom foo : [1] = [2]

example : [[1], [1], [1]] = [[2], [1], [2]] := by {
  nth_rewrite_lhs 0 [foo], -- lhs means left part of an equation
  nth_rewrite_lhs 1 [foo], -- rhs means right part of an equation
}

axiom foo' : [6] = [7]
axiom bar' : [[5], [5]] = [[6], [6]]

lemma ex1 : [[7], [6]] = [[5], [5]] := by {
  nth_rewrite_lhs 0 foo',
  nth_rewrite_rhs 0 bar',
  nth_rewrite_lhs 0 ←foo',
  nth_rewrite_lhs 0 ←foo',
}

#print ex1

-- example backtracking
example (a b c : ℕ) : c + a + b = a + c + b := by {
  -- nth_rewrite_lhs 0 add_comm, -- not solved next try
  -- nth_rewrite_lhs 1 add_comm, -- solved, but we want to find all solutions
  -- start
  nth_rewrite_lhs 0 add_comm,
  nth_rewrite_lhs 0 add_comm, -- this two lines doing nothing => delete branch
  -- start
  nth_rewrite_lhs 0 add_comm,
  nth_rewrite_lhs 1 add_comm,
  -- nth_rewrite 1 add_comm, -- do nothing => delete branch
  nth_rewrite_lhs 0 add_comm, -- solved with 3 rewrites
}


/-
Copyright (c) 2023 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Kevin Buzzard
-/

import tactic -- imports all the Lean tactics
import measure_theory.measurable_space

/-

# Measure theory

## Sigma algebras.

A σ-algebra on a type `X` is a collection of subsets of `X` satisfying some
axioms, and in Lean you write it like this:

-/

-- let X be a set
variable (X : Type)
-- ...and let 𝓐 be a sigma-algebra on X
variable (𝓐 : measurable_space X)

/-

Note that `measurable_space` is a *class*, so really we should be writing `[measurable_space X]`,
meaning "let `X` be equipped once and for all with a sigma algebra which we won't give a name to".
But in this sheet we'll consider making them explicitly.

Let's do the following exercise. Show that if `A` is a subset of `X` then `{0,A,Aᶜ,X}`
is a sigma algebra on `X`.

-/

def gen_by (A : set X) : measurable_space X :=
{ measurable_set' := λ S, S = ∅ ∨ S = A ∨ S = Aᶜ ∨ S = ⊤,
  measurable_set_empty := begin
    left, refl,
  end,
  measurable_set_compl := begin
    rintro s (h | h | h | h),
    { right, right, right, simp [h], },
    { right, right, left, rw [h], },
    { right, left, rw h, simp, },
    { left, rw h, simp, },
  end,
  measurable_set_Union := begin
    intros f hf,
    by_cases h1 : ∃ j, f j = ⊤,
    { right, right, right,
      rw eq_top_iff,
      rintro x -,
      rw set.mem_Union,
      cases h1 with j hj,
      use j,
      rw hj,
      triv, },
    push_neg at h1,
    by_cases h2 : ∃ j k, f j = A ∧ f k = Aᶜ,
    { right, right, right, rw eq_top_iff, rintro x -,
    rw set.mem_Union,
    rcases h2 with ⟨j, k, hj, hk⟩,
    by_cases hxA : x ∈ A,
    { use j,
      rwa hj, },
    { use k,
      rwa hk, }, },
    push_neg at h2,
    by_cases h3 : ∃ j, f j = A,
    { right, left,
      ext x,
      rw set.mem_Union,
      cases h3 with j hj,
      split,
      { rintro ⟨i, hi⟩,
        suffices : f i ⊆ A,
        { exact this hi, },
        rcases hf i with (h|h|h|h),
        { rw h, simp, },
        { rw h, },
        { cases h2 j i hj h, },
        { cases h1 i h, }, },
      { intro hx,
        use j,
        rwa hj, }, },
    by_cases h4 : ∃ j, f j = Aᶜ,
    { right, right, left,
      ext x,
      rw set.mem_Union,
      cases h4 with j hj,
      split,
      { rintro ⟨i, hi⟩,
        suffices : f i ⊆ Aᶜ,
        { exact this hi, },
        rcases hf i with (h|h|h|h),
        { rw h, simp, },
        { cases h2 i j h hj, },
        { rw h, simp, },
        { cases h1 i h, }, },
      { intro hx,
        use j,
        rwa hj, }, },
    push_neg at h3 h4,
    left,
    apply set.eq_empty_of_subset_empty,
    intros x hx,
    rw set.mem_Union at hx,
    cases hx with i hi,
    rcases hf i with (h|h|h|h),
    { rwa h at hi, },
    { cases h3 _ h, },
    { cases h4 _ h, },
    { cases h1 _ h, },
  end }

-- An alternative approach to defining the sigma algebra generated by `{A}` is just
-- to use `measurable_space.generate_from`:

example (A : set X) : measurable_space X := measurable_space.generate_from {A}

-- But the problem with that approach is that you don't get the actual sets
-- in the sigma algebra for free. Try this, to see what I mean!
example (A : set X) : (measurable_space.generate_from {A}).measurable_set' = ({∅,A,Aᶜ,⊤} : set (set X)) :=
begin
  ext B,
  simp,
  unfold measurable_space.generate_from,
  dsimp,
  split,
  { intro h,
    induction h with A' hA' C hC1 hC2 f hf1 hf2,
    { simp at hA', right, left, assumption, },
    { left, refl, },
    { rcases hC2 with (rfl|rfl|rfl|rfl);
      finish, },
    { dsimp at hf2,
      exact (gen_by X A).measurable_set_Union f hf2, }, },
  { rintro (rfl|rfl|rfl|rfl),
    { apply measurable_space.generate_measurable.empty, },
    { apply measurable_space.generate_measurable.basic, simp, },
    { apply measurable_space.generate_measurable.compl, apply measurable_space.generate_measurable.basic, simp, },
    { rw (show (set.univ : set X) = ∅ᶜ,by simp),
      apply measurable_space.generate_measurable.compl, apply measurable_space.generate_measurable.empty, }, },
end


-- #3

variables (men : Type*) (barber : men)
variable  (shaves : men → men → Prop)

open classical

example (h : ∀ x : men, shaves barber x ↔ ¬ shaves x x) :
  false :=
  have hb: shaves barber barber ↔ ¬ shaves barber barber := h barber,
  have hbb: shaves barber barber → ¬ shaves barber barber := iff.elim_left hb,
  have hnbb: ¬ shaves barber barber → shaves barber barber := iff.elim_right hb,
  or.elim (em (shaves barber barber))
  (assume hsbb: shaves barber barber, absurd hsbb (hbb hsbb))
  (assume hnsbb: ¬ shaves barber barber, absurd (hnbb hnsbb) hnsbb)


/-
Copyright (c) 2021 Yury Kudriashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudriashov, Malo Jaffré

! This file was ported from Lean 3 source module analysis.convex.slope
! leanprover-community/mathlib commit 78261225eb5cedc61c5c74ecb44e5b385d13b733
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.Convex.Function
import Mathbin.Tactic.FieldSimp
import Mathbin.Tactic.Linarith.Default

/-!
# Slopes of convex functions

This file relates convexity/concavity of functions in a linearly ordered field and the monotonicity
of their slopes.

The main use is to show convexity/concavity from monotonicity of the derivative.
-/

variable {𝕜 : Type _} [LinearOrderedField 𝕜] {s : Set 𝕜} {f : 𝕜 → 𝕜}

/-- If `f : 𝕜 → 𝕜` is convex, then for any three points `x < y < z` the slope of the secant line of
`f` on `[x, y]` is less than the slope of the secant line of `f` on `[x, z]`. -/
theorem ConvexOn.slope_mono_adjacent (hf : ConvexOn 𝕜 s f) {x y z : 𝕜} (hx : x ∈ s) (hz : z ∈ s)
    (hxy : x < y) (hyz : y < z) : (f y - f x) / (y - x) ≤ (f z - f y) / (z - y) :=
  by
  have hxz := hxy.trans hyz
  rw [← sub_pos] at hxy hxz hyz
  suffices f y / (y - x) + f y / (z - y) ≤ f x / (y - x) + f z / (z - y)
    by
    ring_nf  at this⊢
    linarith
  set a := (z - y) / (z - x)
  set b := (y - x) / (z - x)
  have hy : a • x + b • z = y := by
    field_simp
    rw [div_eq_iff] <;> [ring, linarith]
  have key :=
    hf.2 hx hz (show 0 ≤ a by apply div_nonneg <;> linarith)
      (show 0 ≤ b by apply div_nonneg <;> linarith)
      (show a + b = 1 by
        field_simp
        rw [div_eq_iff] <;> [ring, linarith])
  rw [hy] at key
  replace key := mul_le_mul_of_nonneg_left key hxz.le
  field_simp [hxy.ne', hyz.ne', hxz.ne', mul_comm (z - x) _]  at key⊢
  rw [div_le_div_right]
  · linarith
  · nlinarith
#align convex_on.slope_mono_adjacent ConvexOn.slope_mono_adjacent

/-- If `f : 𝕜 → 𝕜` is concave, then for any three points `x < y < z` the slope of the secant line of
`f` on `[x, y]` is greater than the slope of the secant line of `f` on `[x, z]`. -/
theorem ConcaveOn.slope_anti_adjacent (hf : ConcaveOn 𝕜 s f) {x y z : 𝕜} (hx : x ∈ s) (hz : z ∈ s)
    (hxy : x < y) (hyz : y < z) : (f z - f y) / (z - y) ≤ (f y - f x) / (y - x) :=
  by
  rw [← neg_le_neg_iff, ← neg_sub_neg (f x), ← neg_sub_neg (f y)]
  simp_rw [← Pi.neg_apply, ← neg_div, neg_sub]
  exact ConvexOn.slope_mono_adjacent hf.neg hx hz hxy hyz
#align concave_on.slope_anti_adjacent ConcaveOn.slope_anti_adjacent

/-- If `f : 𝕜 → 𝕜` is strictly convex, then for any three points `x < y < z` the slope of the
secant line of `f` on `[x, y]` is strictly less than the slope of the secant line of `f` on
`[x, z]`. -/
theorem StrictConvexOn.slope_strict_mono_adjacent (hf : StrictConvexOn 𝕜 s f) {x y z : 𝕜}
    (hx : x ∈ s) (hz : z ∈ s) (hxy : x < y) (hyz : y < z) :
    (f y - f x) / (y - x) < (f z - f y) / (z - y) :=
  by
  have hxz := hxy.trans hyz
  have hxz' := hxz.ne
  rw [← sub_pos] at hxy hxz hyz
  suffices f y / (y - x) + f y / (z - y) < f x / (y - x) + f z / (z - y)
    by
    ring_nf  at this⊢
    linarith
  set a := (z - y) / (z - x)
  set b := (y - x) / (z - x)
  have hy : a • x + b • z = y := by
    field_simp
    rw [div_eq_iff] <;> [ring, linarith]
  have key :=
    hf.2 hx hz hxz' (div_pos hyz hxz) (div_pos hxy hxz)
      (show a + b = 1 by
        field_simp
        rw [div_eq_iff] <;> [ring, linarith])
  rw [hy] at key
  replace key := mul_lt_mul_of_pos_left key hxz
  field_simp [hxy.ne', hyz.ne', hxz.ne', mul_comm (z - x) _]  at key⊢
  rw [div_lt_div_right]
  · linarith
  · nlinarith
#align strict_convex_on.slope_strict_mono_adjacent StrictConvexOn.slope_strict_mono_adjacent

/-- If `f : 𝕜 → 𝕜` is strictly concave, then for any three points `x < y < z` the slope of the
secant line of `f` on `[x, y]` is strictly greater than the slope of the secant line of `f` on
`[x, z]`. -/
theorem StrictConcaveOn.slope_anti_adjacent (hf : StrictConcaveOn 𝕜 s f) {x y z : 𝕜} (hx : x ∈ s)
    (hz : z ∈ s) (hxy : x < y) (hyz : y < z) : (f z - f y) / (z - y) < (f y - f x) / (y - x) :=
  by
  rw [← neg_lt_neg_iff, ← neg_sub_neg (f x), ← neg_sub_neg (f y)]
  simp_rw [← Pi.neg_apply, ← neg_div, neg_sub]
  exact StrictConvexOn.slope_strict_mono_adjacent hf.neg hx hz hxy hyz
#align strict_concave_on.slope_anti_adjacent StrictConcaveOn.slope_anti_adjacent

/-- If for any three points `x < y < z`, the slope of the secant line of `f : 𝕜 → 𝕜` on `[x, y]` is
less than the slope of the secant line of `f` on `[x, z]`, then `f` is convex. -/
theorem convexOn_of_slope_mono_adjacent (hs : Convex 𝕜 s)
    (hf :
      ∀ {x y z : 𝕜},
        x ∈ s → z ∈ s → x < y → y < z → (f y - f x) / (y - x) ≤ (f z - f y) / (z - y)) :
    ConvexOn 𝕜 s f :=
  LinearOrder.convexOn_of_lt hs fun x hx z hz hxz a b ha hb hab =>
    by
    let y := a * x + b * z
    have hxy : x < y := by
      rw [← one_mul x, ← hab, add_mul]
      exact add_lt_add_left ((mul_lt_mul_left hb).2 hxz) _
    have hyz : y < z := by
      rw [← one_mul z, ← hab, add_mul]
      exact add_lt_add_right ((mul_lt_mul_left ha).2 hxz) _
    have : (f y - f x) * (z - y) ≤ (f z - f y) * (y - x) :=
      (div_le_div_iff (sub_pos.2 hxy) (sub_pos.2 hyz)).1 (hf hx hz hxy hyz)
    have hxz : 0 < z - x := sub_pos.2 (hxy.trans hyz)
    have ha : (z - y) / (z - x) = a :=
      by
      rw [eq_comm, ← sub_eq_iff_eq_add'] at hab
      simp_rw [div_eq_iff hxz.ne', y, ← hab]
      ring
    have hb : (y - x) / (z - x) = b :=
      by
      rw [eq_comm, ← sub_eq_iff_eq_add] at hab
      simp_rw [div_eq_iff hxz.ne', y, ← hab]
      ring
    rwa [sub_mul, sub_mul, sub_le_iff_le_add', ← add_sub_assoc, le_sub_iff_add_le, ← mul_add,
      sub_add_sub_cancel, ← le_div_iff hxz, add_div, mul_div_assoc, mul_div_assoc, mul_comm (f x),
      mul_comm (f z), ha, hb] at this
#align convex_on_of_slope_mono_adjacent convexOn_of_slope_mono_adjacent

/-- If for any three points `x < y < z`, the slope of the secant line of `f : 𝕜 → 𝕜` on `[x, y]` is
greater than the slope of the secant line of `f` on `[x, z]`, then `f` is concave. -/
theorem concaveOn_of_slope_anti_adjacent (hs : Convex 𝕜 s)
    (hf :
      ∀ {x y z : 𝕜},
        x ∈ s → z ∈ s → x < y → y < z → (f z - f y) / (z - y) ≤ (f y - f x) / (y - x)) :
    ConcaveOn 𝕜 s f := by
  rw [← neg_convexOn_iff]
  refine' convexOn_of_slope_mono_adjacent hs fun x y z hx hz hxy hyz => _
  rw [← neg_le_neg_iff]
  simp_rw [← neg_div, neg_sub, Pi.neg_apply, neg_sub_neg]
  exact hf hx hz hxy hyz
#align concave_on_of_slope_anti_adjacent concaveOn_of_slope_anti_adjacent

/-- If for any three points `x < y < z`, the slope of the secant line of `f : 𝕜 → 𝕜` on `[x, y]` is
strictly less than the slope of the secant line of `f` on `[x, z]`, then `f` is strictly convex. -/
theorem strictConvexOn_of_slope_strict_mono_adjacent (hs : Convex 𝕜 s)
    (hf :
      ∀ {x y z : 𝕜},
        x ∈ s → z ∈ s → x < y → y < z → (f y - f x) / (y - x) < (f z - f y) / (z - y)) :
    StrictConvexOn 𝕜 s f :=
  LinearOrder.strictConvexOn_of_lt hs fun x hx z hz hxz a b ha hb hab =>
    by
    let y := a * x + b * z
    have hxy : x < y := by
      rw [← one_mul x, ← hab, add_mul]
      exact add_lt_add_left ((mul_lt_mul_left hb).2 hxz) _
    have hyz : y < z := by
      rw [← one_mul z, ← hab, add_mul]
      exact add_lt_add_right ((mul_lt_mul_left ha).2 hxz) _
    have : (f y - f x) * (z - y) < (f z - f y) * (y - x) :=
      (div_lt_div_iff (sub_pos.2 hxy) (sub_pos.2 hyz)).1 (hf hx hz hxy hyz)
    have hxz : 0 < z - x := sub_pos.2 (hxy.trans hyz)
    have ha : (z - y) / (z - x) = a :=
      by
      rw [eq_comm, ← sub_eq_iff_eq_add'] at hab
      simp_rw [div_eq_iff hxz.ne', y, ← hab]
      ring
    have hb : (y - x) / (z - x) = b :=
      by
      rw [eq_comm, ← sub_eq_iff_eq_add] at hab
      simp_rw [div_eq_iff hxz.ne', y, ← hab]
      ring
    rwa [sub_mul, sub_mul, sub_lt_iff_lt_add', ← add_sub_assoc, lt_sub_iff_add_lt, ← mul_add,
      sub_add_sub_cancel, ← lt_div_iff hxz, add_div, mul_div_assoc, mul_div_assoc, mul_comm (f x),
      mul_comm (f z), ha, hb] at this
#align strict_convex_on_of_slope_strict_mono_adjacent strictConvexOn_of_slope_strict_mono_adjacent

/-- If for any three points `x < y < z`, the slope of the secant line of `f : 𝕜 → 𝕜` on `[x, y]` is
strictly greater than the slope of the secant line of `f` on `[x, z]`, then `f` is strictly concave.
-/
theorem strictConcaveOn_of_slope_strict_anti_adjacent (hs : Convex 𝕜 s)
    (hf :
      ∀ {x y z : 𝕜},
        x ∈ s → z ∈ s → x < y → y < z → (f z - f y) / (z - y) < (f y - f x) / (y - x)) :
    StrictConcaveOn 𝕜 s f := by
  rw [← neg_strictConvexOn_iff]
  refine' strictConvexOn_of_slope_strict_mono_adjacent hs fun x y z hx hz hxy hyz => _
  rw [← neg_lt_neg_iff]
  simp_rw [← neg_div, neg_sub, Pi.neg_apply, neg_sub_neg]
  exact hf hx hz hxy hyz
#align strict_concave_on_of_slope_strict_anti_adjacent strictConcaveOn_of_slope_strict_anti_adjacent

/-- A function `f : 𝕜 → 𝕜` is convex iff for any three points `x < y < z` the slope of the secant
line of `f` on `[x, y]` is less than the slope of the secant line of `f` on `[x, z]`. -/
theorem convexOn_iff_slope_mono_adjacent :
    ConvexOn 𝕜 s f ↔
      Convex 𝕜 s ∧
        ∀ ⦃x y z : 𝕜⦄,
          x ∈ s → z ∈ s → x < y → y < z → (f y - f x) / (y - x) ≤ (f z - f y) / (z - y) :=
  ⟨fun h => ⟨h.1, fun x y z => h.slope_mono_adjacent⟩, fun h =>
    convexOn_of_slope_mono_adjacent h.1 h.2⟩
#align convex_on_iff_slope_mono_adjacent convexOn_iff_slope_mono_adjacent

/-- A function `f : 𝕜 → 𝕜` is concave iff for any three points `x < y < z` the slope of the secant
line of `f` on `[x, y]` is greater than the slope of the secant line of `f` on `[x, z]`. -/
theorem concaveOn_iff_slope_anti_adjacent :
    ConcaveOn 𝕜 s f ↔
      Convex 𝕜 s ∧
        ∀ ⦃x y z : 𝕜⦄,
          x ∈ s → z ∈ s → x < y → y < z → (f z - f y) / (z - y) ≤ (f y - f x) / (y - x) :=
  ⟨fun h => ⟨h.1, fun x y z => h.slope_anti_adjacent⟩, fun h =>
    concaveOn_of_slope_anti_adjacent h.1 h.2⟩
#align concave_on_iff_slope_anti_adjacent concaveOn_iff_slope_anti_adjacent

/-- A function `f : 𝕜 → 𝕜` is strictly convex iff for any three points `x < y < z` the slope of
the secant line of `f` on `[x, y]` is strictly less than the slope of the secant line of `f` on
`[x, z]`. -/
theorem strictConvexOn_iff_slope_strict_mono_adjacent :
    StrictConvexOn 𝕜 s f ↔
      Convex 𝕜 s ∧
        ∀ ⦃x y z : 𝕜⦄,
          x ∈ s → z ∈ s → x < y → y < z → (f y - f x) / (y - x) < (f z - f y) / (z - y) :=
  ⟨fun h => ⟨h.1, fun x y z => h.slope_strict_mono_adjacent⟩, fun h =>
    strictConvexOn_of_slope_strict_mono_adjacent h.1 h.2⟩
#align strict_convex_on_iff_slope_strict_mono_adjacent strictConvexOn_iff_slope_strict_mono_adjacent

/-- A function `f : 𝕜 → 𝕜` is strictly concave iff for any three points `x < y < z` the slope of
the secant line of `f` on `[x, y]` is strictly greater than the slope of the secant line of `f` on
`[x, z]`. -/
theorem strictConcaveOn_iff_slope_strict_anti_adjacent :
    StrictConcaveOn 𝕜 s f ↔
      Convex 𝕜 s ∧
        ∀ ⦃x y z : 𝕜⦄,
          x ∈ s → z ∈ s → x < y → y < z → (f z - f y) / (z - y) < (f y - f x) / (y - x) :=
  ⟨fun h => ⟨h.1, fun x y z => h.slope_anti_adjacent⟩, fun h =>
    strictConcaveOn_of_slope_strict_anti_adjacent h.1 h.2⟩
#align strict_concave_on_iff_slope_strict_anti_adjacent strictConcaveOn_iff_slope_strict_anti_adjacent

/-- If `f` is convex on a set `s` in a linearly ordered field, and `f x < f y` for two points
`x < y` in `s`, then `f` is strictly monotone on `s ∩ [y, ∞)`. -/
theorem ConvexOn.strict_mono_of_lt (hf : ConvexOn 𝕜 s f) {x y : 𝕜} (hx : x ∈ s) (hxy : x < y)
    (hxy' : f x < f y) : StrictMonoOn f (s ∩ Set.Ici y) :=
  by
  intro u hu v hv huv
  have step1 : ∀ {z : 𝕜}, z ∈ s ∩ Set.Ioi y → f y < f z :=
    by
    refine' fun z hz => hf.lt_right_of_left_lt hx hz.1 _ hxy'
    rw [openSegment_eq_Ioo (hxy.trans hz.2)]
    exact ⟨hxy, hz.2⟩
  rcases eq_or_lt_of_le hu.2 with (rfl | hu2)
  · exact step1 ⟨hv.1, huv⟩
  · refine' hf.lt_right_of_left_lt _ hv.1 _ (step1 ⟨hu.1, hu2⟩)
    · apply hf.1.segment_subset hx hu.1
      rw [segment_eq_Icc (hxy.le.trans hu.2)]
      exact ⟨hxy.le, hu.2⟩
    · rw [openSegment_eq_Ioo (hu2.trans huv)]
      exact ⟨hu2, huv⟩
#align convex_on.strict_mono_of_lt ConvexOn.strict_mono_of_lt


constant U : Type
constant p : U → U → Prop
constants A B C : U → Prop

theorem ex1 : (∃ x, ∀ y, p x y) → (∀ y, ∃ x, p x y) :=
  begin
    intros h1,
    intros a,
    cases h1 with b h2,
    existsi b,
    have h3, from h2 a,
    assumption
  end

theorem ex2 (h1 : ∀ x, A x ∨ B x) (h2 : ∀ y, ¬ A y) :
∀ x, B x :=
  begin
    intro a,
    cases h1 a,
    have h3, from h2 a, contradiction,
    assumption
  end

constants even odd : U → Prop
constant s : U → U

theorem ex3 (h1 : ∀ x, even x ∨ odd x)
(h2 : ∀ x, odd x → even (s x)) :
∀ x, even x ∨ even (s x) :=
  begin
    intro a,
    cases h1 a,
    exact (or.inl h),
    --have h3, from h2 a h,
    exact (or.inr $ h2 a h)
  end

theorem ex4 : (∃ x, A x) ∨ (∃ x, B x) → (∃ x, A x ∨ B x) :=
  begin
    intro h,
    cases h,
    cases h,
    existsi h_w,
    exact (or.inl h_h),
    cases h,
    existsi h_w,
    exact (or.inr h_h),
end