import inner_product_spaces.real_ip.basic

noncomputable theory

variables {α : Type*}
variables [decidable_eq α] [add_comm_group α] [vector_space ℝ α] [ℝ_inner_product_space α]

open real

instance ip_space_has_dist : has_dist α := ⟨λ x y, sqrt (norm_sq (x-y))⟩

lemma ip_dist_self (x : α) : dist x x = 0 :=
by {dsimp [dist],
    rw [add_right_neg, norm_sq_zero, sqrt_zero]}

lemma ip_eq_of_dist_eq_zero (x y : α) (h : dist x y = 0) : x = y :=
begin
    dsimp [dist] at h,
    rw [real.sqrt_eq_zero (norm_sq_nonneg (x+-y)), zero_iff_norm_sq_zero] at h,
    exact (eq_of_sub_eq_zero h),
end

lemma ip_dist_comm (x y : α) : dist x y = dist y x :=
by {dsimp [dist],
    rw [←sub_eq_add_neg, ←sub_eq_add_neg, real.sqrt_inj (norm_sq_nonneg (x-y)) (norm_sq_nonneg (y-x)),
    ←neg_sub x y, norm_sq_neg_eq, sub_eq_add_neg]}

instance ip_space_has_norm : has_norm α := ⟨λ x, sqrt ((norm_sq x))⟩

@[simp] lemma sqr_norm {x : α} : ∥x∥^2 = (norm_sq x) := real.sqr_sqrt (norm_sq_nonneg x)

lemma ip_norm_nonneg {x : α} : ∥x∥ ≥ 0 := real.sqrt_nonneg (norm_sq x)

@[reducible] def orthog (x y : α) := ⟪x ∥ y⟫ = 0

infix `⊥` := orthog

lemma orthog_symm {x y : α} (h : x ⊥ y) : y ⊥ x :=
by {dsimp [orthog] at *,
    rw [←conj_symm x y],
    exact h}

lemma zero_of_orthog_self {x : α} : x ⊥ x → x = 0 :=
(zero_iff_norm_sq_zero x).1

lemma add_orthog {x y z : α} (hx : x ⊥ z) (hy : y ⊥ z) : (x+y)⊥z :=
by {dsimp [orthog] at *, rw [add_left, hx, hy, add_zero]}

lemma mul_orthog (x y : α) (a b : ℝ) (h : x ⊥ y) : (a•x) ⊥ (b•y) :=
by {simp [orthog], repeat {right}, exact h}

theorem pythagoras {x y : α} (h : x ⊥ y) : ∥x+y∥^2 = ∥x∥^2+∥y∥^2 :=
by {dsimp [orthog] at h, simp [sqr_norm, norm_sq], rw [←conj_symm x y, h, zero_add, zero_add]}

lemma orthog_of_pythagoras {x y : α} (h : ∥x+y∥^2 = ∥x∥^2 + ∥y∥^2) : x ⊥ y :=
begin
    rw [sqr_norm, sqr_norm, sqr_norm, norm_sq_add, add_assoc] at h,
    conv at h {to_rhs, rw [←add_zero (norm_sq y)]},
    have w := (add_left_inj _).mp h,
    have k := congr_arg (λ (r : ℝ), 1/2 * r) w,
    simp at k,
    rw [left_distrib, ←mul_assoc, inv_mul_cancel two_ne_zero, one_mul] at k,
    conv at k {to_rhs, rw [←add_zero (2⁻¹ * norm_sq y)]},
    exact (add_left_inj _).mp k,
end

lemma pythagoras_iff_orthog {x y : α} : ∥x+y∥^2 = ∥x∥^2 + ∥y∥^2 ↔ x ⊥ y :=
⟨orthog_of_pythagoras, pythagoras⟩

-- Scott: maybe even provide the instance of `normed_group`?
-- I wonder where in mathlib this belongs. Possibly even `data.real.basic`.
instance : has_norm ℝ := ⟨abs⟩

instance ℝ_normed_space : normed_space ℝ ℝ := by apply_instance

lemma norm_leq_of_norm_sq_leq (x y : α) (h : ∥⟪x ∥ y⟫∥^2≤∥x∥^2*∥y∥^2) : ∥⟪x ∥ y⟫∥≤∥x∥*∥y∥ :=
by {have w := sqrt_le_sqrt h,
        dsimp [norm] at *,
        rw [sqrt_mul (pow_two_nonneg _), sqrt_sqr (abs_nonneg _), sqr_sqrt (norm_sq_nonneg _), sqr_sqrt (norm_sq_nonneg _)] at w,
        exact w}

lemma norm_sq_ip_eq_ip_sqr (x y : α) : ∥⟪x ∥ y⟫∥^2 = ⟪x ∥ y⟫^2 :=
by {dsimp [norm], rw [←sqrt_sqr_eq_abs, sqr_sqrt (pow_two_nonneg _)]}

theorem cauchy_schwarz (x y : α) : ∥⟪x ∥ y⟫∥≤∥x∥*∥y∥ :=
begin
    by_cases (y=0),

    { dsimp [norm],
      rw [h],
      simp },
    apply norm_leq_of_norm_sq_leq,
    let c := ⟪x ∥ y⟫/∥y∥^2,
    have w := pow_two_nonneg (∥x-c•y∥),
    rw [sqr_norm, sqr_norm] at *,
    rw [norm_sq_ip_eq_ip_sqr],
    dsimp [norm_sq] at *,
    simp at w,
    repeat {rw [←neg_one_smul ℝ (c•y)] at w},
    repeat {rw [mul_left] at w},
    repeat {rw [mul_right] at w},
    rw [conj_symm y x] at w,
    simp at w,
    have k₁ : c = ⟪x ∥ y⟫/∥y∥^2 := by refl,
    rw [k₁] at w,
    have k₂ := (neq_zero_iff_norm_sq_neq_zero y).2 h,
    simp only [sqr_norm] at w,
    have w₁ := div_mul_cancel ⟪x ∥ y⟫ k₂,
    dsimp [norm_sq] at *,
    rw [w₁] at w,
    simp at w,
    have w₂ := le_of_sub_nonneg w,
    have w₃ : ⟪x ∥ y⟫/⟪y ∥ y⟫ = ⟪x ∥ y⟫*⟪y ∥ y⟫⁻¹ := by refl,
    rw [mul_comm, w₃, ←mul_assoc, ←pow_two] at w₂,
    have w₄ := norm_sq_nonneg y,
    dsimp [norm_sq] at w₄,
    have w₅ := mul_le_mul_of_nonneg_right w₂ w₄,
    rw [mul_assoc, inv_mul_cancel k₂, mul_one] at w₅,
    exact w₅,
end

lemma sqr_nonneg (r : ℝ) : r^2 ≥ 0 :=
by {rw [pow_two], exact mul_self_nonneg r}

lemma norm_sqr_eq_sqr (r : ℝ) : ∥r^2∥ = r^2 := abs_of_nonneg (sqr_nonneg r)

lemma sqr_pos_iff_neq_zero (r : ℝ) : r^2 > 0 ↔ r ≠ 0 :=
begin
    constructor,

    rw [awesome_mt],
    simp,
    intros k,
    rw [k, pow_two, mul_zero],

    rw [awesome_mt],
    simp,
    intros k,
    rw [le_iff_eq_or_lt] at k,
    cases k,
    exact pow_eq_zero k,

    have l := sqr_nonneg r,
    dsimp [(≥)] at l,
    rw [←not_lt] at l,
    exact absurd k l,
end

lemma norm_add_leq_of_norm_add_sqr_leq (x y : α) (h : ∥x+y∥^2≤(∥x∥+∥y∥)^2) : ∥x+y∥≤∥x∥+∥y∥ :=
by {rw [←sqrt_le (sqr_nonneg _) (sqr_nonneg _),
            sqrt_sqr (ip_norm_nonneg),
            sqrt_sqr (add_nonneg ip_norm_nonneg ip_norm_nonneg)] at h,
        exact h}

theorem triangle_ineq (x y : α) : ∥x+y∥≤∥x∥+∥y∥ :=
begin
    apply norm_add_leq_of_norm_add_sqr_leq,
    rw [sqr_norm, pow_two, left_distrib, right_distrib, right_distrib, ←pow_two,
    ←pow_two, sqr_norm, sqr_norm, norm_sq_add, add_assoc, add_assoc, add_le_add_iff_left,
    ←add_assoc, add_le_add_iff_right (norm_sq y), mul_comm ∥y∥, ←mul_two, mul_comm],
    apply mul_le_mul_of_nonneg_right,
    apply le_trans (le_abs_self ⟪x ∥ y⟫),
    exact cauchy_schwarz x y,

    rw [le_iff_eq_or_lt],
    right,
    exact two_pos,
end

lemma ip_dist_eq (x y : α) : dist x y = norm (x - y) := rfl

lemma ip_dist_triangle (x y z : α) : dist x z ≤ dist x y + dist y z :=
begin
    repeat {rw [ip_dist_eq]},
    have w : x - z = (x-y) + (y-z) := by simp,
    rw [w],
    exact triangle_ineq (x-y) (y-z),
end

def ip_space_is_metric_space : metric_space α :=
{dist_self := ip_dist_self,
 eq_of_dist_eq_zero := ip_eq_of_dist_eq_zero,
 dist_comm := ip_dist_comm,
 dist_triangle := ip_dist_triangle}

def ip_space_is_normed_group : normed_group α :=
{dist_eq := ip_dist_eq,
..ip_space_is_metric_space}

lemma sqr_abs (r : ℝ) : r^2 = (abs r)^2 :=
by rw [←sqrt_sqr_eq_abs, sqr_sqrt (pow_two_nonneg r)]

lemma ip_norm_smul (a : ℝ) (x : α) : ∥a • x∥ = ∥a∥*∥x∥:=
begin
    dsimp [norm],
    have h₁ := real.sqrt_sqr (abs_nonneg a),
    have h₂ := pow_two_nonneg (abs a),
    rw [←h₁, ←real.sqrt_mul h₂],
    have h₃ := mul_nonneg h₂ (norm_sq_nonneg x),
    rw [real.sqrt_inj (norm_sq_nonneg (a•x)) h₃],
    simp only [norm_sq, mul_left, mul_right],
    rw [←mul_assoc, ←pow_two, sqr_abs],
end

def ip_space_is_normed_space : normed_space ℝ α :=
{norm_smul := ip_norm_smul,
 .. ip_space_is_normed_group}

lemma norm_neq_zero_iff_neq_zero {β : Type*} [normed_space ℝ β] (x : β) : ∥x∥ ≠ (0 : ℝ) ↔ x ≠ (0 : β) :=
⟨by {apply mt, exact (norm_eq_zero x).2}, by {apply mt, exact (norm_eq_zero x).1}⟩

lemma norm_eq_iff_norm_sq_eq {β : Type*} [normed_space ℝ β] {x y : β} : ∥x∥=∥y∥ ↔ ∥x∥^2 = ∥y∥^2 :=
⟨by {apply congr_arg (λ (r : ℝ), r^2)},
 by {intros h, have w := congr_arg (λ (r : ℝ), sqrt r) h, simp at w, exact w}⟩

lemma norm_sqr_leq_iff_norm_leq {β : Type*} [normed_space ℝ β] {x : β} {a : ℝ} {k : a ≥ 0}: ∥x∥^2 ≤ a^2 ↔ ∥x∥ ≤ a :=
begin
    have w := sqrt_le (mul_nonneg (norm_nonneg x) (norm_nonneg x)) (mul_nonneg k k),
    rw [←pow_two, ←pow_two, sqrt_sqr (norm_nonneg _), sqrt_sqr k] at w,
    exact w.symm,
end

-- Scott: are these abandoned? Maybe move them closer to the point of use?

lemma four_ne_zero : (4 : ℝ) ≠ 0 :=
ne.symm (ne_of_lt four_pos)

lemma leq_of_add_nonneg {a b c : ℝ} {ha : a ≥ 0} {hb : b ≥ 0} {hc : c ≥ 0} : a = b + c → b ≤ a :=
by {intros h, linarith}


/-
Copyright (c) 2021 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris Van Doorn, Yury Kudryashov

! This file was ported from Lean 3 source module measure_theory.measure.regular
! leanprover-community/mathlib commit 57ac39bd365c2f80589a700f9fbb664d3a1a30c2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.MeasureTheory.Constructions.BorelSpace

/-!
# Regular measures

A measure is `outer_regular` if the measure of any measurable set `A` is the infimum of `μ U` over
all open sets `U` containing `A`.

A measure is `regular` if it satisfies the following properties:
* it is finite on compact sets;
* it is outer regular;
* it is inner regular for open sets with respect to compacts sets: the measure of any open set `U`
  is the supremum of `μ K` over all compact sets `K` contained in `U`.

A measure is `weakly_regular` if it satisfies the following properties:
* it is outer regular;
* it is inner regular for open sets with respect to closed sets: the measure of any open set `U`
  is the supremum of `μ F` over all closed sets `F` contained in `U`.

In a Hausdorff topological space, regularity implies weak regularity. These three conditions are
registered as typeclasses for a measure `μ`, and this implication is recorded as an instance.

In order to avoid code duplication, we also define a measure `μ` to be `inner_regular` for sets
satisfying a predicate `q` with respect to sets satisfying a predicate `p` if for any set
`U ∈ {U | q U}` and a number `r < μ U` there exists `F ⊆ U` such that `p F` and `r < μ F`.

We prove that inner regularity for open sets with respect to compact sets or closed sets implies
inner regularity for all measurable sets of finite measure (with respect to
compact sets or closed sets respectively), and register some corollaries for (weakly) regular
measures.

Note that a similar statement for measurable sets of infinite mass can fail. For a counterexample,
consider the group `ℝ × ℝ` where the first factor has the discrete topology and the second one the
usual topology. It is a locally compact Hausdorff topological group, with Haar measure equal to
Lebesgue measure on each vertical fiber. The set `ℝ × {0}` has infinite measure (by outer
regularity), but any compact set it contains has zero measure (as it is finite).

Several authors require as a definition of regularity that all measurable sets are inner regular.
We have opted for the slightly weaker definition above as it holds for all Haar measures, it is
enough for essentially all applications, and it is equivalent to the other definition when the
measure is finite.

The interest of the notion of weak regularity is that it is enough for many applications, and it
is automatically satisfied by any finite measure on a metric space.

## Main definitions

* `measure_theory.measure.outer_regular μ`: a typeclass registering that a measure `μ` on a
  topological space is outer regular.
* `measure_theory.measure.regular μ`: a typeclass registering that a measure `μ` on a topological
  space is regular.
* `measure_theory.measure.weakly_regular μ`: a typeclass registering that a measure `μ` on a
  topological space is weakly regular.
* `measure_theory.measure.inner_regular μ p q`: a non-typeclass predicate saying that a measure `μ`
  is inner regular for sets satisfying `q` with respect to sets satisfying `p`.

## Main results

### Outer regular measures

* `set.measure_eq_infi_is_open` asserts that, when `μ` is outer regular, the measure of a
  set is the infimum of the measure of open sets containing it.
* `set.exists_is_open_lt_of_lt` asserts that, when `μ` is outer regular, for every set `s`
  and `r > μ s` there exists an open superset `U ⊇ s` of measure less than `r`.
* push forward of an outer regular measure is outer regular, and scalar multiplication of a regular
  measure by a finite number is outer regular.

### Weakly regular measures

* `is_open.measure_eq_supr_is_closed` asserts that the measure of an open set is the supremum of
  the measure of closed sets it contains.
* `is_open.exists_lt_is_closed`: for an open set `U` and `r < μ U`, there exists a closed `F ⊆ U`
  of measure greater than `r`;
* `measurable_set.measure_eq_supr_is_closed_of_ne_top` asserts that the measure of a measurable set
  of finite measure is the supremum of the measure of closed sets it contains.
*  `measurable_set.exists_lt_is_closed_of_ne_top` and `measurable_set.exists_is_closed_lt_add`:
  a measurable set of finite measure can be approximated by a closed subset (stated as
  `r < μ F` and `μ s < μ F + ε`, respectively).
* `measure_theory.measure.weakly_regular.of_pseudo_emetric_space_of_is_finite_measure` is an
  instance registering that a finite measure on a metric space is weakly regular (in fact, a pseudo
  emetric space is enough);
* `measure_theory.measure.weakly_regular.of_pseudo_emetric_second_countable_of_locally_finite`
  is an instance registering that a locally finite measure on a second countable metric space (or
  even a pseudo emetric space) is weakly regular.

### Regular measures

* `is_open.measure_eq_supr_is_compact` asserts that the measure of an open set is the supremum of
  the measure of compact sets it contains.
* `is_open.exists_lt_is_compact`: for an open set `U` and `r < μ U`, there exists a compact `K ⊆ U`
  of measure greater than `r`;
* `measurable_set.measure_eq_supr_is_compact_of_ne_top` asserts that the measure of a measurable set
  of finite measure is the supremum of the measure of compact sets it contains.
*  `measurable_set.exists_lt_is_compact_of_ne_top` and `measurable_set.exists_is_compact_lt_add`:
  a measurable set of finite measure can be approximated by a compact subset (stated as
  `r < μ K` and `μ s < μ K + ε`, respectively).
* `measure_theory.measure.regular.of_sigma_compact_space_of_is_locally_finite_measure` is an
  instance registering that a locally finite measure on a `σ`-compact metric space is regular (in
  fact, an emetric space is enough).

## Implementation notes

The main nontrivial statement is `measure_theory.measure.inner_regular.weakly_regular_of_finite`,
expressing that in a finite measure space, if every open set can be approximated from inside by
closed sets, then the measure is in fact weakly regular. To prove that we show that any measurable
set can be approximated from inside by closed sets and from outside by open sets. This statement is
proved by measurable induction, starting from open sets and checking that it is stable by taking
complements (this is the point of this condition, being symmetrical between inside and outside) and
countable disjoint unions.

Once this statement is proved, one deduces results for `σ`-finite measures from this statement, by
restricting them to finite measure sets (and proving that this restriction is weakly regular, using
again the same statement).

## References

[Halmos, Measure Theory, §52][halmos1950measure]. Note that Halmos uses an unusual definition of
Borel sets (for him, they are elements of the `σ`-algebra generated by compact sets!), so his
proofs or statements do not apply directly.

[Billingsley, Convergence of Probability Measures][billingsley1999]
-/

open Set Filter

open ENNReal Topology NNReal BigOperators

namespace MeasureTheory

namespace Measure

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (K «expr ⊆ » U) -/
/-- We say that a measure `μ` is *inner regular* with respect to predicates `p q : set α → Prop`,
if for every `U` such that `q U` and `r < μ U`, there exists a subset `K ⊆ U` satisfying `p K`
of measure greater than `r`.

This definition is used to prove some facts about regular and weakly regular measures without
repeating the proofs. -/
def InnerRegular {α} {m : MeasurableSpace α} (μ : Measure α) (p q : Set α → Prop) :=
  ∀ ⦃U⦄, q U → ∀ r < μ U, ∃ (K : _)(_ : K ⊆ U), p K ∧ r < μ K
#align measure_theory.measure.inner_regular MeasureTheory.Measure.InnerRegular

namespace InnerRegular

variable {α : Type _} {m : MeasurableSpace α} {μ : Measure α} {p q : Set α → Prop} {U : Set α}
  {ε : ℝ≥0∞}

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (K «expr ⊆ » U) -/
theorem measure_eq_supᵢ (H : InnerRegular μ p q) (hU : q U) :
    μ U = ⨆ (K) (_ : K ⊆ U) (hK : p K), μ K :=
  by
  refine'
    le_antisymm (le_of_forall_lt fun r hr => _) (supᵢ₂_le fun K hK => supᵢ_le fun _ => μ.mono hK)
  simpa only [lt_supᵢ_iff, exists_prop] using H hU r hr
#align measure_theory.measure.inner_regular.measure_eq_supr MeasureTheory.Measure.InnerRegular.measure_eq_supᵢ

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (K «expr ⊆ » U) -/
theorem exists_subset_lt_add (H : InnerRegular μ p q) (h0 : p ∅) (hU : q U) (hμU : μ U ≠ ∞)
    (hε : ε ≠ 0) : ∃ (K : _)(_ : K ⊆ U), p K ∧ μ U < μ K + ε :=
  by
  cases' eq_or_ne (μ U) 0 with h₀ h₀
  · refine' ⟨∅, empty_subset _, h0, _⟩
    rwa [measure_empty, h₀, zero_add, pos_iff_ne_zero]
  · rcases H hU _ (ENNReal.sub_lt_self hμU h₀ hε) with ⟨K, hKU, hKc, hrK⟩
    exact ⟨K, hKU, hKc, ENNReal.lt_add_of_sub_lt_right (Or.inl hμU) hrK⟩
#align measure_theory.measure.inner_regular.exists_subset_lt_add MeasureTheory.Measure.InnerRegular.exists_subset_lt_add

theorem map {α β} [MeasurableSpace α] [MeasurableSpace β] {μ : Measure α} {pa qa : Set α → Prop}
    (H : InnerRegular μ pa qa) (f : α ≃ β) (hf : AeMeasurable f μ) {pb qb : Set β → Prop}
    (hAB : ∀ U, qb U → qa (f ⁻¹' U)) (hAB' : ∀ K, pa K → pb (f '' K))
    (hB₁ : ∀ K, pb K → MeasurableSet K) (hB₂ : ∀ U, qb U → MeasurableSet U) :
    InnerRegular (map f μ) pb qb := by
  intro U hU r hr
  rw [map_apply_of_ae_measurable hf (hB₂ _ hU)] at hr
  rcases H (hAB U hU) r hr with ⟨K, hKU, hKc, hK⟩
  refine' ⟨f '' K, image_subset_iff.2 hKU, hAB' _ hKc, _⟩
  rwa [map_apply_of_ae_measurable hf (hB₁ _ <| hAB' _ hKc), f.preimage_image]
#align measure_theory.measure.inner_regular.map MeasureTheory.Measure.InnerRegular.map

theorem smul (H : InnerRegular μ p q) (c : ℝ≥0∞) : InnerRegular (c • μ) p q :=
  by
  intro U hU r hr
  rw [smul_apply, H.measure_eq_supr hU, smul_eq_mul] at hr
  simpa only [ENNReal.mul_supᵢ, lt_supᵢ_iff, exists_prop] using hr
#align measure_theory.measure.inner_regular.smul MeasureTheory.Measure.InnerRegular.smul

theorem trans {q' : Set α → Prop} (H : InnerRegular μ p q) (H' : InnerRegular μ q q') :
    InnerRegular μ p q' := by
  intro U hU r hr
  rcases H' hU r hr with ⟨F, hFU, hqF, hF⟩; rcases H hqF _ hF with ⟨K, hKF, hpK, hrK⟩
  exact ⟨K, hKF.trans hFU, hpK, hrK⟩
#align measure_theory.measure.inner_regular.trans MeasureTheory.Measure.InnerRegular.trans

end InnerRegular

variable {α β : Type _} [MeasurableSpace α] [TopologicalSpace α] {μ : Measure α}

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (U «expr ⊇ » A) -/
/-- A measure `μ` is outer regular if `μ(A) = inf {μ(U) | A ⊆ U open}` for a measurable set `A`.

This definition implies the same equality for any (not necessarily measurable) set, see
`set.measure_eq_infi_is_open`. -/
@[protect_proj]
class OuterRegular (μ : Measure α) : Prop where
  OuterRegular :
    ∀ ⦃A : Set α⦄, MeasurableSet A → ∀ r > μ A, ∃ (U : _)(_ : U ⊇ A), IsOpen U ∧ μ U < r
#align measure_theory.measure.outer_regular MeasureTheory.Measure.OuterRegular

/-- A measure `μ` is regular if
  - it is finite on all compact sets;
  - it is outer regular: `μ(A) = inf {μ(U) | A ⊆ U open}` for `A` measurable;
  - it is inner regular for open sets, using compact sets:
    `μ(U) = sup {μ(K) | K ⊆ U compact}` for `U` open. -/
@[protect_proj]
class Regular (μ : Measure α) extends IsFiniteMeasureOnCompacts μ, OuterRegular μ : Prop where
  InnerRegular : InnerRegular μ IsCompact IsOpen
#align measure_theory.measure.regular MeasureTheory.Measure.Regular

/-- A measure `μ` is weakly regular if
  - it is outer regular: `μ(A) = inf {μ(U) | A ⊆ U open}` for `A` measurable;
  - it is inner regular for open sets, using closed sets:
    `μ(U) = sup {μ(F) | F ⊆ U compact}` for `U` open. -/
@[protect_proj]
class WeaklyRegular (μ : Measure α) extends OuterRegular μ : Prop where
  InnerRegular : InnerRegular μ IsClosed IsOpen
#align measure_theory.measure.weakly_regular MeasureTheory.Measure.WeaklyRegular

-- see Note [lower instance priority]
/-- A regular measure is weakly regular. -/
instance (priority := 100) Regular.weaklyRegular [T2Space α] [Regular μ] : WeaklyRegular μ
    where InnerRegular U hU r hr :=
    let ⟨K, hKU, hcK, hK⟩ := Regular.innerRegular hU r hr
    ⟨K, hKU, hcK.IsClosed, hK⟩
#align measure_theory.measure.regular.weakly_regular MeasureTheory.Measure.Regular.weaklyRegular

namespace OuterRegular

instance zero : OuterRegular (0 : Measure α) :=
  ⟨fun A hA r hr => ⟨univ, subset_univ A, isOpen_univ, hr⟩⟩
#align measure_theory.measure.outer_regular.zero MeasureTheory.Measure.OuterRegular.zero

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (U «expr ⊇ » A) -/
/-- Given `r` larger than the measure of a set `A`, there exists an open superset of `A` with
measure less than `r`. -/
theorem Set.exists_isOpen_lt_of_lt [OuterRegular μ] (A : Set α) (r : ℝ≥0∞) (hr : μ A < r) :
    ∃ (U : _)(_ : U ⊇ A), IsOpen U ∧ μ U < r :=
  by
  rcases outer_regular.outer_regular (measurable_set_to_measurable μ A) r
      (by rwa [measure_to_measurable]) with
    ⟨U, hAU, hUo, hU⟩
  exact ⟨U, (subset_to_measurable _ _).trans hAU, hUo, hU⟩
#align set.exists_is_open_lt_of_lt Set.exists_isOpen_lt_of_lt

/-- For an outer regular measure, the measure of a set is the infimum of the measures of open sets
containing it. -/
theorem Set.measure_eq_infᵢ_isOpen (A : Set α) (μ : Measure α) [OuterRegular μ] :
    μ A = ⨅ (U : Set α) (h : A ⊆ U) (h2 : IsOpen U), μ U :=
  by
  refine' le_antisymm (le_infᵢ₂ fun s hs => le_infᵢ fun h2s => μ.mono hs) _
  refine' le_of_forall_lt' fun r hr => _
  simpa only [infᵢ_lt_iff, exists_prop] using A.exists_is_open_lt_of_lt r hr
#align set.measure_eq_infi_is_open Set.measure_eq_infᵢ_isOpen

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (U «expr ⊇ » A) -/
theorem Set.exists_isOpen_lt_add [OuterRegular μ] (A : Set α) (hA : μ A ≠ ∞) {ε : ℝ≥0∞}
    (hε : ε ≠ 0) : ∃ (U : _)(_ : U ⊇ A), IsOpen U ∧ μ U < μ A + ε :=
  A.exists_isOpen_lt_of_lt _ (ENNReal.lt_add_right hA hε)
#align set.exists_is_open_lt_add Set.exists_isOpen_lt_add

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (U «expr ⊇ » A) -/
theorem Set.exists_isOpen_le_add (A : Set α) (μ : Measure α) [OuterRegular μ] {ε : ℝ≥0∞}
    (hε : ε ≠ 0) : ∃ (U : _)(_ : U ⊇ A), IsOpen U ∧ μ U ≤ μ A + ε :=
  by
  rcases eq_or_ne (μ A) ∞ with (H | H)
  · exact ⟨univ, subset_univ _, isOpen_univ, by simp only [H, _root_.top_add, le_top]⟩
  · rcases A.exists_is_open_lt_add H hε with ⟨U, AU, U_open, hU⟩
    exact ⟨U, AU, U_open, hU.le⟩
#align set.exists_is_open_le_add Set.exists_isOpen_le_add

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (U «expr ⊇ » A) -/
theorem MeasurableSet.exists_isOpen_diff_lt [OuterRegular μ] {A : Set α} (hA : MeasurableSet A)
    (hA' : μ A ≠ ∞) {ε : ℝ≥0∞} (hε : ε ≠ 0) :
    ∃ (U : _)(_ : U ⊇ A), IsOpen U ∧ μ U < ∞ ∧ μ (U \ A) < ε :=
  by
  rcases A.exists_is_open_lt_add hA' hε with ⟨U, hAU, hUo, hU⟩
  use U, hAU, hUo, hU.trans_le le_top
  exact measure_diff_lt_of_lt_add hA hAU hA' hU
#align measurable_set.exists_is_open_diff_lt MeasurableSet.exists_isOpen_diff_lt

protected theorem map [OpensMeasurableSpace α] [MeasurableSpace β] [TopologicalSpace β]
    [BorelSpace β] (f : α ≃ₜ β) (μ : Measure α) [OuterRegular μ] : (Measure.map f μ).OuterRegular :=
  by
  refine' ⟨fun A hA r hr => _⟩
  rw [map_apply f.measurable hA, ← f.image_symm] at hr
  rcases Set.exists_isOpen_lt_of_lt _ r hr with ⟨U, hAU, hUo, hU⟩
  have : IsOpen (f.symm ⁻¹' U) := hUo.preimage f.symm.continuous
  refine' ⟨f.symm ⁻¹' U, image_subset_iff.1 hAU, this, _⟩
  rwa [map_apply f.measurable this.measurable_set, f.preimage_symm, f.preimage_image]
#align measure_theory.measure.outer_regular.map MeasureTheory.Measure.OuterRegular.map

protected theorem smul (μ : Measure α) [OuterRegular μ] {x : ℝ≥0∞} (hx : x ≠ ∞) :
    (x • μ).OuterRegular := by
  rcases eq_or_ne x 0 with (rfl | h0)
  · rw [zero_smul]
    exact outer_regular.zero
  · refine' ⟨fun A hA r hr => _⟩
    rw [smul_apply, A.measure_eq_infi_is_open, smul_eq_mul] at hr
    simpa only [ENNReal.mul_infᵢ_of_ne h0 hx, gt_iff_lt, infᵢ_lt_iff, exists_prop] using hr
#align measure_theory.measure.outer_regular.smul MeasureTheory.Measure.OuterRegular.smul

end OuterRegular

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (U «expr ⊇ » A n) -/
/-- If a measure `μ` admits finite spanning open sets such that the restriction of `μ` to each set
is outer regular, then the original measure is outer regular as well. -/
protected theorem FiniteSpanningSetsIn.outerRegular [OpensMeasurableSpace α] {μ : Measure α}
    (s : μ.FiniteSpanningSetsIn { U | IsOpen U ∧ OuterRegular (μ.restrict U) }) : OuterRegular μ :=
  by
  refine' ⟨fun A hA r hr => _⟩
  have hm : ∀ n, MeasurableSet (s.set n) := fun n => (s.set_mem n).1.MeasurableSet
  haveI : ∀ n, outer_regular (μ.restrict (s.set n)) := fun n => (s.set_mem n).2
  -- Note that `A = ⋃ n, A ∩ disjointed s n`. We replace `A` with this sequence.
  obtain ⟨A, hAm, hAs, hAd, rfl⟩ :
    ∃ A' : ℕ → Set α,
      (∀ n, MeasurableSet (A' n)) ∧
        (∀ n, A' n ⊆ s.set n) ∧ Pairwise (Disjoint on A') ∧ A = ⋃ n, A' n :=
    by
    refine'
      ⟨fun n => A ∩ disjointed s.set n, fun n => hA.inter (MeasurableSet.disjointed hm _), fun n =>
        (inter_subset_right _ _).trans (disjointed_subset _ _),
        (disjoint_disjointed s.set).mono fun k l hkl => hkl.mono inf_le_right inf_le_right, _⟩
    rw [← inter_Union, unionᵢ_disjointed, s.spanning, inter_univ]
  rcases ENNReal.exists_pos_sum_of_countable' (tsub_pos_iff_lt.2 hr).ne' ℕ with ⟨δ, δ0, hδε⟩
  rw [lt_tsub_iff_right, add_comm] at hδε
  have : ∀ n, ∃ (U : _)(_ : U ⊇ A n), IsOpen U ∧ μ U < μ (A n) + δ n :=
    by
    intro n
    have H₁ : ∀ t, μ.restrict (s.set n) t = μ (t ∩ s.set n) := fun t => restrict_apply' (hm n)
    have Ht : μ.restrict (s.set n) (A n) ≠ ⊤ := by
      rw [H₁]
      exact ((measure_mono <| inter_subset_right _ _).trans_lt (s.finite n)).Ne
    rcases(A n).exists_isOpen_lt_add Ht (δ0 n).ne' with ⟨U, hAU, hUo, hU⟩
    rw [H₁, H₁, inter_eq_self_of_subset_left (hAs _)] at hU
    exact ⟨U ∩ s.set n, subset_inter hAU (hAs _), hUo.inter (s.set_mem n).1, hU⟩
  choose U hAU hUo hU
  refine' ⟨⋃ n, U n, Union_mono hAU, isOpen_unionᵢ hUo, _⟩
  calc
    μ (⋃ n, U n) ≤ ∑' n, μ (U n) := measure_Union_le _
    _ ≤ ∑' n, μ (A n) + δ n := (ENNReal.tsum_le_tsum fun n => (hU n).le)
    _ = (∑' n, μ (A n)) + ∑' n, δ n := ENNReal.tsum_add
    _ = μ (⋃ n, A n) + ∑' n, δ n := (congr_arg₂ (· + ·) (measure_Union hAd hAm).symm rfl)
    _ < r := hδε

#align measure_theory.measure.finite_spanning_sets_in.outer_regular MeasureTheory.Measure.FiniteSpanningSetsIn.outerRegular

namespace InnerRegular

variable {p q : Set α → Prop} {U s : Set α} {ε r : ℝ≥0∞}

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (ε «expr ≠ » 0) -/
/-- If a measure is inner regular (using closed or compact sets), then every measurable set of
finite measure can by approximated by a (closed or compact) subset. -/
theorem measurableSetOfOpen [OuterRegular μ] (H : InnerRegular μ p IsOpen) (h0 : p ∅)
    (hd : ∀ ⦃s U⦄, p s → IsOpen U → p (s \ U)) :
    InnerRegular μ p fun s => MeasurableSet s ∧ μ s ≠ ∞ :=
  by
  rintro s ⟨hs, hμs⟩ r hr
  obtain ⟨ε, hε, hεs, rfl⟩ : ∃ (ε : _)(_ : ε ≠ 0), ε + ε ≤ μ s ∧ r = μ s - (ε + ε) :=
    by
    use (μ s - r) / 2
    simp [*, hr.le, ENNReal.add_halves, ENNReal.sub_sub_cancel, le_add_right]
  rcases hs.exists_is_open_diff_lt hμs hε with ⟨U, hsU, hUo, hUt, hμU⟩
  rcases(U \ s).exists_isOpen_lt_of_lt _ hμU with ⟨U', hsU', hU'o, hμU'⟩
  replace hsU' := diff_subset_comm.1 hsU'
  rcases H.exists_subset_lt_add h0 hUo hUt.ne hε with ⟨K, hKU, hKc, hKr⟩
  refine' ⟨K \ U', fun x hx => hsU' ⟨hKU hx.1, hx.2⟩, hd hKc hU'o, ENNReal.sub_lt_of_lt_add hεs _⟩
  calc
    μ s ≤ μ U := μ.mono hsU
    _ < μ K + ε := hKr
    _ ≤ μ (K \ U') + μ U' + ε := (add_le_add_right (tsub_le_iff_right.1 le_measure_diff) _)
    _ ≤ μ (K \ U') + ε + ε := by
      mono*
      exacts[hμU'.le, le_rfl]
    _ = μ (K \ U') + (ε + ε) := add_assoc _ _ _

#align measure_theory.measure.inner_regular.measurable_set_of_open MeasureTheory.Measure.InnerRegular.measurableSetOfOpen

open Finset

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (ε «expr ≠ » 0) -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (F «expr ⊆ » s) -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (U «expr ⊇ » s) -/
/-- In a finite measure space, assume that any open set can be approximated from inside by closed
sets. Then the measure is weakly regular. -/
theorem weaklyRegularOfFinite [BorelSpace α] (μ : Measure α) [IsFiniteMeasure μ]
    (H : InnerRegular μ IsClosed IsOpen) : WeaklyRegular μ :=
  by
  have hfin : ∀ {s}, μ s ≠ ⊤ := measure_ne_top μ
  suffices
    ∀ s,
      MeasurableSet s →
        ∀ (ε) (_ : ε ≠ 0),
          ∃ (F : _)(_ : F ⊆ s)(U : _)(_ : U ⊇ s),
            IsClosed F ∧ IsOpen U ∧ μ s ≤ μ F + ε ∧ μ U ≤ μ s + ε
    by
    refine'
      { OuterRegular := fun s hs r hr => _
        InnerRegular := H }
    rcases exists_between hr with ⟨r', hsr', hr'r⟩
    rcases this s hs _ (tsub_pos_iff_lt.2 hsr').ne' with ⟨-, -, U, hsU, -, hUo, -, H⟩
    refine' ⟨U, hsU, hUo, _⟩
    rw [add_tsub_cancel_of_le hsr'.le] at H
    exact H.trans_lt hr'r
  refine' MeasurableSet.induction_on_open _ _ _
  /- The proof is by measurable induction: we should check that the property is true for the empty
    set, for open sets, and is stable by taking the complement and by taking countable disjoint
    unions. The point of the property we are proving is that it is stable by taking complements
    (exchanging the roles of closed and open sets and thanks to the finiteness of the measure). -/
  -- check for open set
  · intro U hU ε hε
    rcases H.exists_subset_lt_add isClosed_empty hU hfin hε with ⟨F, hsF, hFc, hF⟩
    exact ⟨F, hsF, U, subset.rfl, hFc, hU, hF.le, le_self_add⟩
  -- check for complements
  · rintro s hs H ε hε
    rcases H ε hε with ⟨F, hFs, U, hsU, hFc, hUo, hF, hU⟩
    refine'
      ⟨Uᶜ, compl_subset_compl.2 hsU, Fᶜ, compl_subset_compl.2 hFs, hUo.is_closed_compl,
        hFc.is_open_compl, _⟩
    simp only [measure_compl_le_add_iff, *, hUo.measurable_set, hFc.measurable_set, true_and_iff]
  -- check for disjoint unions
  · intro s hsd hsm H ε ε0
    have ε0' : ε / 2 ≠ 0 := (ENNReal.half_pos ε0).ne'
    rcases ENNReal.exists_pos_sum_of_countable' ε0' ℕ with ⟨δ, δ0, hδε⟩
    choose F hFs U hsU hFc hUo hF hU using fun n => H n (δ n) (δ0 n).ne'
    -- the approximating closed set is constructed by considering finitely many sets `s i`, which
    -- cover all the measure up to `ε/2`, approximating each of these by a closed set `F i`, and
    -- taking the union of these (finitely many) `F i`.
    have : tendsto (fun t => (∑ k in t, μ (s k)) + ε / 2) at_top (𝓝 <| μ (⋃ n, s n) + ε / 2) :=
      by
      rw [measure_Union hsd hsm]
      exact tendsto.add ennreal.summable.has_sum tendsto_const_nhds
    rcases(this.eventually <| lt_mem_nhds <| ENNReal.lt_add_right hfin ε0').exists with ⟨t, ht⟩
    -- the approximating open set is constructed by taking for each `s n` an approximating open set
    -- `U n` with measure at most `μ (s n) + δ n` for a summable `δ`, and taking the union of these.
    refine'
      ⟨⋃ k ∈ t, F k, Union_mono fun k => Union_subset fun _ => hFs _, ⋃ n, U n, Union_mono hsU,
        isClosed_bunionᵢ t.finite_to_set fun k _ => hFc k, isOpen_unionᵢ hUo, ht.le.trans _, _⟩
    · calc
        (∑ k in t, μ (s k)) + ε / 2 ≤ ((∑ k in t, μ (F k)) + ∑ k in t, δ k) + ε / 2 :=
          by
          rw [← sum_add_distrib]
          exact add_le_add_right (sum_le_sum fun k hk => hF k) _
        _ ≤ (∑ k in t, μ (F k)) + ε / 2 + ε / 2 :=
          (add_le_add_right (add_le_add_left ((ENNReal.sum_le_tsum _).trans hδε.le) _) _)
        _ = μ (⋃ k ∈ t, F k) + ε := _

      rw [measure_bUnion_finset, add_assoc, ENNReal.add_halves]
      exacts[fun k _ n _ hkn => (hsd hkn).mono (hFs k) (hFs n), fun k hk => (hFc k).MeasurableSet]
    ·
      calc
        μ (⋃ n, U n) ≤ ∑' n, μ (U n) := measure_Union_le _
        _ ≤ ∑' n, μ (s n) + δ n := (ENNReal.tsum_le_tsum hU)
        _ = μ (⋃ n, s n) + ∑' n, δ n := by rw [measure_Union hsd hsm, ENNReal.tsum_add]
        _ ≤ μ (⋃ n, s n) + ε := add_le_add_left (hδε.le.trans ENNReal.half_le_self) _

#align measure_theory.measure.inner_regular.weakly_regular_of_finite MeasureTheory.Measure.InnerRegular.weaklyRegularOfFinite

/-- In a metric space (or even a pseudo emetric space), an open set can be approximated from inside
by closed sets. -/
theorem ofPseudoEmetricSpace {X : Type _} [PseudoEMetricSpace X] [MeasurableSpace X]
    (μ : Measure X) : InnerRegular μ IsClosed IsOpen :=
  by
  intro U hU r hr
  rcases hU.exists_Union_is_closed with ⟨F, F_closed, -, rfl, F_mono⟩
  rw [measure_Union_eq_supr F_mono.directed_le] at hr
  rcases lt_supᵢ_iff.1 hr with ⟨n, hn⟩
  exact ⟨F n, subset_Union _ _, F_closed n, hn⟩
#align measure_theory.measure.inner_regular.of_pseudo_emetric_space MeasureTheory.Measure.InnerRegular.ofPseudoEmetricSpace

/-- In a `σ`-compact space, any closed set can be approximated by a compact subset. -/
theorem isCompactIsClosed {X : Type _} [TopologicalSpace X] [SigmaCompactSpace X]
    [MeasurableSpace X] (μ : Measure X) : InnerRegular μ IsCompact IsClosed :=
  by
  intro F hF r hr
  set B : ℕ → Set X := compactCovering X
  have hBc : ∀ n, IsCompact (F ∩ B n) := fun n => (isCompact_compactCovering X n).inter_left hF
  have hBU : (⋃ n, F ∩ B n) = F := by rw [← inter_Union, unionᵢ_compactCovering, Set.inter_univ]
  have : μ F = ⨆ n, μ (F ∩ B n) :=
    by
    rw [← measure_Union_eq_supr, hBU]
    exact Monotone.directed_le fun m n h => inter_subset_inter_right _ (compactCovering_subset _ h)
  rw [this] at hr
  rcases lt_supᵢ_iff.1 hr with ⟨n, hn⟩
  exact ⟨_, inter_subset_left _ _, hBc n, hn⟩
#align measure_theory.measure.inner_regular.is_compact_is_closed MeasureTheory.Measure.InnerRegular.isCompactIsClosed

end InnerRegular

namespace Regular

instance zero : Regular (0 : Measure α) :=
  ⟨fun U hU r hr => ⟨∅, empty_subset _, isCompact_empty, hr⟩⟩
#align measure_theory.measure.regular.zero MeasureTheory.Measure.Regular.zero

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (K «expr ⊆ » U) -/
/-- If `μ` is a regular measure, then any open set can be approximated by a compact subset. -/
theorem IsOpen.exists_lt_isCompact [Regular μ] ⦃U : Set α⦄ (hU : IsOpen U) {r : ℝ≥0∞}
    (hr : r < μ U) : ∃ (K : _)(_ : K ⊆ U), IsCompact K ∧ r < μ K :=
  Regular.innerRegular hU r hr
#align is_open.exists_lt_is_compact IsOpen.exists_lt_isCompact

/-- The measure of an open set is the supremum of the measures of compact sets it contains. -/
theorem IsOpen.measure_eq_supᵢ_isCompact ⦃U : Set α⦄ (hU : IsOpen U) (μ : Measure α) [Regular μ] :
    μ U = ⨆ (K : Set α) (h : K ⊆ U) (h2 : IsCompact K), μ K :=
  Regular.innerRegular.measure_eq_supᵢ hU
#align is_open.measure_eq_supr_is_compact IsOpen.measure_eq_supᵢ_isCompact

theorem exists_compact_not_null [Regular μ] : (∃ K, IsCompact K ∧ μ K ≠ 0) ↔ μ ≠ 0 := by
  simp_rw [Ne.def, ← measure_univ_eq_zero, is_open_univ.measure_eq_supr_is_compact,
    ENNReal.supᵢ_eq_zero, not_forall, exists_prop, subset_univ, true_and_iff]
#align measure_theory.measure.regular.exists_compact_not_null MeasureTheory.Measure.Regular.exists_compact_not_null

/-- If `μ` is a regular measure, then any measurable set of finite measure can be approximated by a
compact subset. See also `measurable_set.exists_is_compact_lt_add` and
`measurable_set.exists_lt_is_compact_of_ne_top`. -/
theorem innerRegularMeasurable [Regular μ] :
    InnerRegular μ IsCompact fun s => MeasurableSet s ∧ μ s ≠ ∞ :=
  Regular.innerRegular.measurableSetOfOpen isCompact_empty fun _ _ => IsCompact.diff
#align measure_theory.measure.regular.inner_regular_measurable MeasureTheory.Measure.Regular.innerRegularMeasurable

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (K «expr ⊆ » A) -/
/-- If `μ` is a regular measure, then any measurable set of finite measure can be approximated by a
compact subset. See also `measurable_set.exists_lt_is_compact_of_ne_top`. -/
theorem MeasurableSet.exists_isCompact_lt_add [Regular μ] ⦃A : Set α⦄ (hA : MeasurableSet A)
    (h'A : μ A ≠ ∞) {ε : ℝ≥0∞} (hε : ε ≠ 0) : ∃ (K : _)(_ : K ⊆ A), IsCompact K ∧ μ A < μ K + ε :=
  Regular.innerRegularMeasurable.exists_subset_lt_add isCompact_empty ⟨hA, h'A⟩ h'A hε
#align measurable_set.exists_is_compact_lt_add MeasurableSet.exists_isCompact_lt_add

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (K «expr ⊆ » A) -/
/-- If `μ` is a regular measure, then any measurable set of finite measure can be approximated by a
compact subset. See also `measurable_set.exists_is_compact_lt_add` and
`measurable_set.exists_lt_is_compact_of_ne_top`. -/
theorem MeasurableSet.exists_isCompact_diff_lt [OpensMeasurableSpace α] [T2Space α] [Regular μ]
    ⦃A : Set α⦄ (hA : MeasurableSet A) (h'A : μ A ≠ ∞) {ε : ℝ≥0∞} (hε : ε ≠ 0) :
    ∃ (K : _)(_ : K ⊆ A), IsCompact K ∧ μ (A \ K) < ε :=
  by
  rcases hA.exists_is_compact_lt_add h'A hε with ⟨K, hKA, hKc, hK⟩
  exact
    ⟨K, hKA, hKc,
      measure_diff_lt_of_lt_add hKc.measurable_set hKA (ne_top_of_le_ne_top h'A <| measure_mono hKA)
        hK⟩
#align measurable_set.exists_is_compact_diff_lt MeasurableSet.exists_isCompact_diff_lt

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (K «expr ⊆ » A) -/
/-- If `μ` is a regular measure, then any measurable set of finite measure can be approximated by a
compact subset. See also `measurable_set.exists_is_compact_lt_add`. -/
theorem MeasurableSet.exists_lt_isCompact_of_ne_top [Regular μ] ⦃A : Set α⦄ (hA : MeasurableSet A)
    (h'A : μ A ≠ ∞) {r : ℝ≥0∞} (hr : r < μ A) : ∃ (K : _)(_ : K ⊆ A), IsCompact K ∧ r < μ K :=
  Regular.innerRegularMeasurable ⟨hA, h'A⟩ _ hr
#align measurable_set.exists_lt_is_compact_of_ne_top MeasurableSet.exists_lt_isCompact_of_ne_top

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (K «expr ⊆ » A) -/
/-- Given a regular measure, any measurable set of finite mass can be approximated from
inside by compact sets. -/
theorem MeasurableSet.measure_eq_supᵢ_isCompact_of_ne_top [Regular μ] ⦃A : Set α⦄
    (hA : MeasurableSet A) (h'A : μ A ≠ ∞) : μ A = ⨆ (K) (_ : K ⊆ A) (h : IsCompact K), μ K :=
  Regular.innerRegularMeasurable.measure_eq_supᵢ ⟨hA, h'A⟩
#align measurable_set.measure_eq_supr_is_compact_of_ne_top MeasurableSet.measure_eq_supᵢ_isCompact_of_ne_top

protected theorem map [OpensMeasurableSpace α] [MeasurableSpace β] [TopologicalSpace β] [T2Space β]
    [BorelSpace β] [Regular μ] (f : α ≃ₜ β) : (Measure.map f μ).regular :=
  by
  haveI := outer_regular.map f μ
  haveI := IsFiniteMeasureOnCompacts.map μ f
  exact
    ⟨regular.inner_regular.map f.to_equiv f.measurable.ae_measurable
        (fun U hU => hU.Preimage f.continuous) (fun K hK => hK.image f.continuous)
        (fun K hK => hK.MeasurableSet) fun U hU => hU.MeasurableSet⟩
#align measure_theory.measure.regular.map MeasureTheory.Measure.Regular.map

protected theorem smul [Regular μ] {x : ℝ≥0∞} (hx : x ≠ ∞) : (x • μ).regular :=
  by
  haveI := outer_regular.smul μ hx
  haveI := is_finite_measure_on_compacts.smul μ hx
  exact ⟨regular.inner_regular.smul x⟩
#align measure_theory.measure.regular.smul MeasureTheory.Measure.Regular.smul

-- see Note [lower instance priority]
/-- A regular measure in a σ-compact space is σ-finite. -/
instance (priority := 100) sigmaFinite [SigmaCompactSpace α] [Regular μ] : SigmaFinite μ :=
  ⟨⟨{   Set := compactCovering α
        set_mem := fun n => trivial
        Finite := fun n => (isCompact_compactCovering α n).measure_lt_top
        spanning := unionᵢ_compactCovering α }⟩⟩
#align measure_theory.measure.regular.sigma_finite MeasureTheory.Measure.Regular.sigmaFinite

end Regular

namespace WeaklyRegular

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (F «expr ⊆ » U) -/
/-- If `μ` is a weakly regular measure, then any open set can be approximated by a closed subset. -/
theorem IsOpen.exists_lt_isClosed [WeaklyRegular μ] ⦃U : Set α⦄ (hU : IsOpen U) {r : ℝ≥0∞}
    (hr : r < μ U) : ∃ (F : _)(_ : F ⊆ U), IsClosed F ∧ r < μ F :=
  WeaklyRegular.innerRegular hU r hr
#align is_open.exists_lt_is_closed IsOpen.exists_lt_isClosed

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (F «expr ⊆ » U) -/
/-- If `μ` is a weakly regular measure, then any open set can be approximated by a closed subset. -/
theorem IsOpen.measure_eq_supᵢ_isClosed ⦃U : Set α⦄ (hU : IsOpen U) (μ : Measure α)
    [WeaklyRegular μ] : μ U = ⨆ (F) (_ : F ⊆ U) (h : IsClosed F), μ F :=
  WeaklyRegular.innerRegular.measure_eq_supᵢ hU
#align is_open.measure_eq_supr_is_closed IsOpen.measure_eq_supᵢ_isClosed

theorem innerRegularMeasurable [WeaklyRegular μ] :
    InnerRegular μ IsClosed fun s => MeasurableSet s ∧ μ s ≠ ∞ :=
  WeaklyRegular.innerRegular.measurableSetOfOpen isClosed_empty fun _ _ h₁ h₂ =>
    h₁.inter h₂.isClosed_compl
#align measure_theory.measure.weakly_regular.inner_regular_measurable MeasureTheory.Measure.WeaklyRegular.innerRegularMeasurable

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (K «expr ⊆ » s) -/
/-- If `s` is a measurable set, a weakly regular measure `μ` is finite on `s`, and `ε` is a positive
number, then there exist a closed set `K ⊆ s` such that `μ s < μ K + ε`. -/
theorem MeasurableSet.exists_isClosed_lt_add [WeaklyRegular μ] {s : Set α} (hs : MeasurableSet s)
    (hμs : μ s ≠ ∞) {ε : ℝ≥0∞} (hε : ε ≠ 0) : ∃ (K : _)(_ : K ⊆ s), IsClosed K ∧ μ s < μ K + ε :=
  innerRegularMeasurable.exists_subset_lt_add isClosed_empty ⟨hs, hμs⟩ hμs hε
#align measurable_set.exists_is_closed_lt_add MeasurableSet.exists_isClosed_lt_add

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (F «expr ⊆ » A) -/
theorem MeasurableSet.exists_isClosed_diff_lt [OpensMeasurableSpace α] [WeaklyRegular μ] ⦃A : Set α⦄
    (hA : MeasurableSet A) (h'A : μ A ≠ ∞) {ε : ℝ≥0∞} (hε : ε ≠ 0) :
    ∃ (F : _)(_ : F ⊆ A), IsClosed F ∧ μ (A \ F) < ε :=
  by
  rcases hA.exists_is_closed_lt_add h'A hε with ⟨F, hFA, hFc, hF⟩
  exact
    ⟨F, hFA, hFc,
      measure_diff_lt_of_lt_add hFc.measurable_set hFA (ne_top_of_le_ne_top h'A <| measure_mono hFA)
        hF⟩
#align measurable_set.exists_is_closed_diff_lt MeasurableSet.exists_isClosed_diff_lt

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (K «expr ⊆ » A) -/
/-- Given a weakly regular measure, any measurable set of finite mass can be approximated from
inside by closed sets. -/
theorem MeasurableSet.exists_lt_isClosed_of_ne_top [WeaklyRegular μ] ⦃A : Set α⦄
    (hA : MeasurableSet A) (h'A : μ A ≠ ∞) {r : ℝ≥0∞} (hr : r < μ A) :
    ∃ (K : _)(_ : K ⊆ A), IsClosed K ∧ r < μ K :=
  innerRegularMeasurable ⟨hA, h'A⟩ _ hr
#align measurable_set.exists_lt_is_closed_of_ne_top MeasurableSet.exists_lt_isClosed_of_ne_top

/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (K «expr ⊆ » A) -/
/-- Given a weakly regular measure, any measurable set of finite mass can be approximated from
inside by closed sets. -/
theorem MeasurableSet.measure_eq_supᵢ_isClosed_of_ne_top [WeaklyRegular μ] ⦃A : Set α⦄
    (hA : MeasurableSet A) (h'A : μ A ≠ ∞) : μ A = ⨆ (K) (_ : K ⊆ A) (h : IsClosed K), μ K :=
  innerRegularMeasurable.measure_eq_supᵢ ⟨hA, h'A⟩
#align measurable_set.measure_eq_supr_is_closed_of_ne_top MeasurableSet.measure_eq_supᵢ_isClosed_of_ne_top

/-- The restriction of a weakly regular measure to a measurable set of finite measure is
weakly regular. -/
theorem restrictOfMeasurableSet [BorelSpace α] [WeaklyRegular μ] (A : Set α) (hA : MeasurableSet A)
    (h'A : μ A ≠ ∞) : WeaklyRegular (μ.restrict A) :=
  by
  haveI : Fact (μ A < ∞) := ⟨h'A.lt_top⟩
  refine' inner_regular.weakly_regular_of_finite _ fun V V_open => _
  simp only [restrict_apply' hA]
  intro r hr
  have : μ (V ∩ A) ≠ ∞ := ne_top_of_le_ne_top h'A (measure_mono <| inter_subset_right _ _)
  rcases(V_open.measurable_set.inter hA).exists_lt_isClosed_of_ne_top this hr with
    ⟨F, hFVA, hFc, hF⟩
  refine' ⟨F, hFVA.trans (inter_subset_left _ _), hFc, _⟩
  rwa [inter_eq_self_of_subset_left (hFVA.trans <| inter_subset_right _ _)]
#align measure_theory.measure.weakly_regular.restrict_of_measurable_set MeasureTheory.Measure.WeaklyRegular.restrictOfMeasurableSet

-- see Note [lower instance priority]
/-- Any finite measure on a metric space (or even a pseudo emetric space) is weakly regular. -/
instance (priority := 100) ofPseudoEmetricSpaceOfIsFiniteMeasure {X : Type _} [PseudoEMetricSpace X]
    [MeasurableSpace X] [BorelSpace X] (μ : Measure X) [IsFiniteMeasure μ] : WeaklyRegular μ :=
  (InnerRegular.ofPseudoEmetricSpace μ).weaklyRegularOfFinite μ
#align measure_theory.measure.weakly_regular.of_pseudo_emetric_space_of_is_finite_measure MeasureTheory.Measure.WeaklyRegular.ofPseudoEmetricSpaceOfIsFiniteMeasure

-- see Note [lower instance priority]
/-- Any locally finite measure on a second countable metric space (or even a pseudo emetric space)
is weakly regular. -/
instance (priority := 100) ofPseudoEmetricSecondCountableOfLocallyFinite {X : Type _}
    [PseudoEMetricSpace X] [TopologicalSpace.SecondCountableTopology X] [MeasurableSpace X]
    [BorelSpace X] (μ : Measure X) [IsLocallyFiniteMeasure μ] : WeaklyRegular μ :=
  haveI : outer_regular μ :=
    by
    refine' (μ.finite_spanning_sets_in_open'.mono' fun U hU => _).OuterRegular
    have : Fact (μ U < ∞) := ⟨hU.2⟩
    exact ⟨hU.1, inferInstance⟩
  ⟨inner_regular.of_pseudo_emetric_space μ⟩
#align measure_theory.measure.weakly_regular.of_pseudo_emetric_second_countable_of_locally_finite MeasureTheory.Measure.WeaklyRegular.ofPseudoEmetricSecondCountableOfLocallyFinite

end WeaklyRegular

attribute [local instance] EMetric.secondCountable_of_sigmaCompact

-- see Note [lower instance priority]
/-- Any locally finite measure on a `σ`-compact (e)metric space is regular. -/
instance (priority := 100) Regular.ofSigmaCompactSpaceOfIsLocallyFiniteMeasure {X : Type _}
    [EMetricSpace X] [SigmaCompactSpace X] [MeasurableSpace X] [BorelSpace X] (μ : Measure X)
    [IsLocallyFiniteMeasure μ] : Regular μ
    where
  lt_top_of_isCompact K hK := hK.measure_lt_top
  InnerRegular := (InnerRegular.isCompactIsClosed μ).trans (InnerRegular.ofPseudoEmetricSpace μ)
#align measure_theory.measure.regular.of_sigma_compact_space_of_is_locally_finite_measure MeasureTheory.Measure.Regular.ofSigmaCompactSpaceOfIsLocallyFiniteMeasure

end Measure

end MeasureTheory


/-
Copyright (c) 2020 Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Damiano Testa

! This file was ported from Lean 3 source module data.polynomial.reverse
! leanprover-community/mathlib commit 44de64f183393284a16016dfb2a48ac97382f2bd
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Polynomial.Degree.TrailingDegree
import Mathlib.Data.Polynomial.EraseLead
import Mathlib.Data.Polynomial.Eval

/-!
# Reverse of a univariate polynomial

The main definition is `reverse`.  Applying `reverse` to a polynomial `f : R[X]` produces
the polynomial with a reversed list of coefficients, equivalent to `X^f.natDegree * f(1/X)`.

The main result is that `reverse (f * g) = reverse f * reverse g`, provided the leading
coefficients of `f` and `g` do not multiply to zero.
-/

namespace Polynomial

open Polynomial Finsupp Finset

open Classical Polynomial

section Semiring

variable {R : Type _} [Semiring R] {f : R[X]}

/-- If `i ≤ N`, then `revAtFun N i` returns `N - i`, otherwise it returns `i`.
This is the map used by the embedding `revAt`.
-/
def revAtFun (N i : ℕ) : ℕ :=
  ite (i ≤ N) (N - i) i
#align polynomial.rev_at_fun Polynomial.revAtFun

theorem revAtFun_invol {N i : ℕ} : revAtFun N (revAtFun N i) = i := by
  unfold revAtFun
  split_ifs with h j
  · exact tsub_tsub_cancel_of_le h
  · exfalso
    apply j
    exact Nat.sub_le N i
  · rfl
#align polynomial.rev_at_fun_invol Polynomial.revAtFun_invol

theorem revAtFun_inj {N : ℕ} : Function.Injective (revAtFun N) := by
  intro a b hab
  rw [← @revAtFun_invol N a, hab, revAtFun_invol]
#align polynomial.rev_at_fun_inj Polynomial.revAtFun_inj

/-- If `i ≤ N`, then `revAt N i` returns `N - i`, otherwise it returns `i`.
Essentially, this embedding is only used for `i ≤ N`.
The advantage of `revAt N i` over `N - i` is that `revAt` is an involution.
-/
def revAt (N : ℕ) : Function.Embedding ℕ ℕ
    where
  toFun i := ite (i ≤ N) (N - i) i
  inj' := revAtFun_inj
#align polynomial.rev_at Polynomial.revAt

/-- We prefer to use the bundled `revAt` over unbundled `revAtfun`. -/
@[simp]
theorem revAtFun_eq (N i : ℕ) : revAtFun N i = revAt N i :=
  rfl
#align polynomial.rev_at_fun_eq Polynomial.revAtFun_eq

@[simp]
theorem revAt_invol {N i : ℕ} : (revAt N) (revAt N i) = i :=
  revAtFun_invol
#align polynomial.rev_at_invol Polynomial.revAt_invol

@[simp]
theorem revAt_le {N i : ℕ} (H : i ≤ N) : revAt N i = N - i :=
  if_pos H
#align polynomial.rev_at_le Polynomial.revAt_le

theorem revAt_add {N O n o : ℕ} (hn : n ≤ N) (ho : o ≤ O) :
    revAt (N + O) (n + o) = revAt N n + revAt O o := by
  rcases Nat.le.dest hn with ⟨n', rfl⟩
  rcases Nat.le.dest ho with ⟨o', rfl⟩
  repeat' rw [revAt_le (le_add_right rfl.le)]
  rw [add_assoc, add_left_comm n' o, ← add_assoc, revAt_le (le_add_right rfl.le)]
  repeat' rw [add_tsub_cancel_left]
#align polynomial.rev_at_add Polynomial.revAt_add

-- @[simp] -- Porting note: simp can prove this
theorem revAt_zero (N : ℕ) : revAt N 0 = N := by simp
#align polynomial.rev_at_zero Polynomial.revAt_zero

/-- `reflect N f` is the polynomial such that `(reflect N f).coeff i = f.coeff (revAt N i)`.
In other words, the terms with exponent `[0, ..., N]` now have exponent `[N, ..., 0]`.

In practice, `reflect` is only used when `N` is at least as large as the degree of `f`.

Eventually, it will be used with `N` exactly equal to the degree of `f`.  -/
noncomputable def reflect (N : ℕ) : R[X] → R[X]
  | ⟨f⟩ => ⟨Finsupp.embDomain (revAt N) f⟩
#align polynomial.reflect Polynomial.reflect

theorem reflect_support (N : ℕ) (f : R[X]) :
    (reflect N f).support = Finset.image (revAt N) f.support := by
  rcases f with ⟨⟩
  ext1
  simp only [reflect, support_ofFinsupp, support_embDomain, Finset.mem_map, Finset.mem_image]
#align polynomial.reflect_support Polynomial.reflect_support

@[simp]
theorem coeff_reflect (N : ℕ) (f : R[X]) (i : ℕ) : coeff (reflect N f) i = f.coeff (revAt N i) := by
  rcases f with ⟨f⟩
  simp only [reflect, coeff]
  calc
    Finsupp.embDomain (revAt N) f i = Finsupp.embDomain (revAt N) f (revAt N (revAt N i)) := by
      rw [revAt_invol]
    _ = f (revAt N i) := Finsupp.embDomain_apply _ _ _

#align polynomial.coeff_reflect Polynomial.coeff_reflect

@[simp]
theorem reflect_zero {N : ℕ} : reflect N (0 : R[X]) = 0 :=
  rfl
#align polynomial.reflect_zero Polynomial.reflect_zero

@[simp]
theorem reflect_eq_zero_iff {N : ℕ} {f : R[X]} : reflect N (f : R[X]) = 0 ↔ f = 0 := by
  rw [ofFinsupp_eq_zero, reflect, embDomain_eq_zero, ofFinsupp_eq_zero]
#align polynomial.reflect_eq_zero_iff Polynomial.reflect_eq_zero_iff

@[simp]
theorem reflect_add (f g : R[X]) (N : ℕ) : reflect N (f + g) = reflect N f + reflect N g := by
  ext
  simp only [coeff_add, coeff_reflect]
#align polynomial.reflect_add Polynomial.reflect_add

@[simp]
theorem reflect_C_mul (f : R[X]) (r : R) (N : ℕ) : reflect N (C r * f) = C r * reflect N f := by
  ext
  simp only [coeff_reflect, coeff_C_mul]
set_option linter.uppercaseLean3 false in
#align polynomial.reflect_C_mul Polynomial.reflect_C_mul

-- @[simp] -- Porting note: simp can prove this (once `reflect_monomial` is in simp scope)
theorem reflect_C_mul_X_pow (N n : ℕ) {c : R} : reflect N (C c * X ^ n) = C c * X ^ revAt N n := by
  ext
  rw [reflect_C_mul, coeff_C_mul, coeff_C_mul, coeff_X_pow, coeff_reflect]
  split_ifs with h
  · rw [h, revAt_invol, coeff_X_pow_self]
  · rw [not_mem_support_iff.mp]
    intro a
    rw [← one_mul (X ^ n), ← C_1] at a
    apply h
    rw [← mem_support_C_mul_X_pow a, revAt_invol]
set_option linter.uppercaseLean3 false in
#align polynomial.reflect_C_mul_X_pow Polynomial.reflect_C_mul_X_pow

@[simp]
theorem reflect_C (r : R) (N : ℕ) : reflect N (C r) = C r * X ^ N := by
  conv_lhs => rw [← mul_one (C r), ← pow_zero X, reflect_C_mul_X_pow, revAt_zero]
set_option linter.uppercaseLean3 false in
#align polynomial.reflect_C Polynomial.reflect_C

@[simp]
theorem reflect_monomial (N n : ℕ) : reflect N ((X : R[X]) ^ n) = X ^ revAt N n := by
  rw [← one_mul (X ^ n), ← one_mul (X ^ revAt N n), ← C_1, reflect_C_mul_X_pow]
#align polynomial.reflect_monomial Polynomial.reflect_monomial

theorem reflect_mul_induction (cf cg : ℕ) :
    ∀ N O : ℕ,
      ∀ f g : R[X],
        f.support.card ≤ cf.succ →
          g.support.card ≤ cg.succ →
            f.natDegree ≤ N →
              g.natDegree ≤ O → reflect (N + O) (f * g) = reflect N f * reflect O g := by
  induction' cf with cf hcf
  --first induction (left): base case
  · induction' cg with cg hcg
    -- second induction (right): base case
    · intro N O f g Cf Cg Nf Og
      rw [← C_mul_X_pow_eq_self Cf, ← C_mul_X_pow_eq_self Cg]
      simp_rw [mul_assoc, X_pow_mul, mul_assoc, ← pow_add (X : R[X]), reflect_C_mul,
        reflect_monomial, add_comm, revAt_add Nf Og, mul_assoc, X_pow_mul, mul_assoc, ←
        pow_add (X : R[X]), add_comm]
    -- second induction (right): induction step
    · intro N O f g Cf Cg Nf Og
      by_cases g0 : g = 0
      · rw [g0, reflect_zero, mul_zero, mul_zero, reflect_zero]
      rw [← eraseLead_add_C_mul_X_pow g, mul_add, reflect_add, reflect_add, mul_add, hcg, hcg] <;>
        try assumption
      · exact le_add_left card_support_C_mul_X_pow_le_one
      · exact le_trans (natDegree_C_mul_X_pow_le g.leadingCoeff g.natDegree) Og
      · exact Nat.lt_succ_iff.mp (gt_of_ge_of_gt Cg (eraseLead_support_card_lt g0))
      · exact le_trans eraseLead_natDegree_le_aux Og
  --first induction (left): induction step
  · intro N O f g Cf Cg Nf Og
    by_cases f0 : f = 0
    · rw [f0, reflect_zero, zero_mul, zero_mul, reflect_zero]
    rw [← eraseLead_add_C_mul_X_pow f, add_mul, reflect_add, reflect_add, add_mul, hcf, hcf] <;>
      try assumption
    · exact le_add_left card_support_C_mul_X_pow_le_one
    · exact le_trans (natDegree_C_mul_X_pow_le f.leadingCoeff f.natDegree) Nf
    · exact Nat.lt_succ_iff.mp (gt_of_ge_of_gt Cf (eraseLead_support_card_lt f0))
    · exact le_trans eraseLead_natDegree_le_aux Nf
#align polynomial.reflect_mul_induction Polynomial.reflect_mul_induction

@[simp]
theorem reflect_mul (f g : R[X]) {F G : ℕ} (Ff : f.natDegree ≤ F) (Gg : g.natDegree ≤ G) :
    reflect (F + G) (f * g) = reflect F f * reflect G g :=
  reflect_mul_induction _ _ F G f g f.support.card.le_succ g.support.card.le_succ Ff Gg
#align polynomial.reflect_mul Polynomial.reflect_mul

section Eval₂

variable {S : Type _} [CommSemiring S]

theorem eval₂_reflect_mul_pow (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f := by
  refine'
    induction_with_natDegree_le (fun f => eval₂ i (⅟ x) (reflect N f) * x ^ N = eval₂ i x f) _ _ _
      _ f hf
  · simp
  · intro n r _ hnN
    simp only [revAt_le hnN, reflect_C_mul_X_pow, eval₂_X_pow, eval₂_C, eval₂_mul]
    conv in x ^ N => rw [← Nat.sub_add_cancel hnN]
    rw [pow_add, ← mul_assoc, mul_assoc (i r), ← mul_pow, invOf_mul_self, one_pow, mul_one]
  · intros
    simp [*, add_mul]
#align polynomial.eval₂_reflect_mul_pow Polynomial.eval₂_reflect_mul_pow

theorem eval₂_reflect_eq_zero_iff (i : R →+* S) (x : S) [Invertible x] (N : ℕ) (f : R[X])
    (hf : f.natDegree ≤ N) : eval₂ i (⅟ x) (reflect N f) = 0 ↔ eval₂ i x f = 0 := by
  conv_rhs => rw [← eval₂_reflect_mul_pow i x N f hf]
  constructor
  · intro h
    rw [h, zero_mul]
  · intro h
    rw [← mul_one (eval₂ i (⅟ x) _), ← one_pow N, ← mul_invOf_self x, mul_pow, ← mul_assoc, h,
      zero_mul]
#align polynomial.eval₂_reflect_eq_zero_iff Polynomial.eval₂_reflect_eq_zero_iff

end Eval₂

/-- The reverse of a polynomial f is the polynomial obtained by "reading f backwards".
Even though this is not the actual definition, `reverse f = f (1/X) * X ^ f.natDegree`. -/
noncomputable def reverse (f : R[X]) : R[X] :=
  reflect f.natDegree f
#align polynomial.reverse Polynomial.reverse

theorem coeff_reverse (f : R[X]) (n : ℕ) : f.reverse.coeff n = f.coeff (revAt f.natDegree n) := by
  rw [reverse, coeff_reflect]
#align polynomial.coeff_reverse Polynomial.coeff_reverse

@[simp]
theorem coeff_zero_reverse (f : R[X]) : coeff (reverse f) 0 = leadingCoeff f := by
  rw [coeff_reverse, revAt_le (zero_le f.natDegree), tsub_zero, leadingCoeff]
#align polynomial.coeff_zero_reverse Polynomial.coeff_zero_reverse

@[simp]
theorem reverse_zero : reverse (0 : R[X]) = 0 :=
  rfl
#align polynomial.reverse_zero Polynomial.reverse_zero

@[simp]
theorem reverse_eq_zero : f.reverse = 0 ↔ f = 0 := by simp [reverse]
#align polynomial.reverse_eq_zero Polynomial.reverse_eq_zero

theorem natDegree_eq_reverse_natDegree_add_natTrailingDegree (f : R[X]) :
    f.natDegree = f.reverse.natDegree + f.natTrailingDegree := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natDegree_zero, natTrailingDegree_zero]
  apply le_antisymm
  · refine' tsub_le_iff_right.mp _
    apply le_natDegree_of_ne_zero
    rw [reverse, coeff_reflect, ← revAt_le f.natTrailingDegree_le_natDegree, revAt_invol]
    exact trailingCoeff_nonzero_iff_nonzero.mpr hf
  · rw [← le_tsub_iff_left f.reverse_natDegree_le]
    apply natTrailingDegree_le_of_ne_zero
    have key := mt leadingCoeff_eq_zero.mp (mt reverse_eq_zero.mp hf)
    rwa [leadingCoeff, coeff_reverse, revAt_le f.reverse_natDegree_le] at key
#align polynomial.nat_degree_eq_reverse_nat_degree_add_nat_trailing_degree Polynomial.natDegree_eq_reverse_natDegree_add_natTrailingDegree

theorem reverse_natDegree (f : R[X]) : f.reverse.natDegree = f.natDegree - f.natTrailingDegree := by
  rw [f.natDegree_eq_reverse_natDegree_add_natTrailingDegree, add_tsub_cancel_right]
#align polynomial.reverse_nat_degree Polynomial.reverse_natDegree

theorem reverse_leadingCoeff (f : R[X]) : f.reverse.leadingCoeff = f.trailingCoeff := by
  rw [leadingCoeff, reverse_natDegree, ← revAt_le f.natTrailingDegree_le_natDegree,
    coeff_reverse, revAt_invol, trailingCoeff]
#align polynomial.reverse_leading_coeff Polynomial.reverse_leadingCoeff

theorem reverse_natTrailingDegree (f : R[X]) : f.reverse.natTrailingDegree = 0 := by
  by_cases hf : f = 0
  · rw [hf, reverse_zero, natTrailingDegree_zero]
  · rw [← le_zero_iff]
    apply natTrailingDegree_le_of_ne_zero
    rw [coeff_zero_reverse]
    exact mt leadingCoeff_eq_zero.mp hf
#align polynomial.reverse_nat_trailing_degree Polynomial.reverse_natTrailingDegree

theorem reverse_trailingCoeff (f : R[X]) : f.reverse.trailingCoeff = f.leadingCoeff := by
  rw [trailingCoeff, reverse_natTrailingDegree, coeff_zero_reverse]
#align polynomial.reverse_trailing_coeff Polynomial.reverse_trailingCoeff

theorem reverse_mul {f g : R[X]} (fg : f.leadingCoeff * g.leadingCoeff ≠ 0) :
    reverse (f * g) = reverse f * reverse g := by
  unfold reverse
  rw [natDegree_mul' fg, reflect_mul f g rfl.le rfl.le]
#align polynomial.reverse_mul Polynomial.reverse_mul

@[simp]
theorem reverse_mul_of_domain {R : Type _} [Ring R] [NoZeroDivisors R] (f g : R[X]) :
    reverse (f * g) = reverse f * reverse g := by
  by_cases f0 : f = 0
  · simp only [f0, zero_mul, reverse_zero]
  by_cases g0 : g = 0
  · rw [g0, mul_zero, reverse_zero, mul_zero]
  simp [reverse_mul, *]
#align polynomial.reverse_mul_of_domain Polynomial.reverse_mul_of_domain

theorem trailingCoeff_mul {R : Type _} [Ring R] [NoZeroDivisors R] (p q : R[X]) :
    (p * q).trailingCoeff = p.trailingCoeff * q.trailingCoeff := by
  rw [← reverse_leadingCoeff, reverse_mul_of_domain, leadingCoeff_mul, reverse_leadingCoeff,
    reverse_leadingCoeff]
#align polynomial.trailing_coeff_mul Polynomial.trailingCoeff_mul

@[simp]
theorem coeff_one_reverse (f : R[X]) : coeff (reverse f) 1 = nextCoeff f := by
  rw [coeff_reverse, nextCoeff]
  split_ifs with hf
  · have : coeff f 1 = 0 := coeff_eq_zero_of_natDegree_lt (by simp only [hf, zero_lt_one])
    simp [*, revAt]
  · rw [revAt_le]
    exact Nat.succ_le_iff.2 (pos_iff_ne_zero.2 hf)
#align polynomial.coeff_one_reverse Polynomial.coeff_one_reverse

section Eval₂

variable {S : Type _} [CommSemiring S]

theorem eval₂_reverse_mul_pow (i : R →+* S) (x : S) [Invertible x] (f : R[X]) :
    eval₂ i (⅟ x) (reverse f) * x ^ f.natDegree = eval₂ i x f :=
  eval₂_reflect_mul_pow i _ _ f le_rfl
#align polynomial.eval₂_reverse_mul_pow Polynomial.eval₂_reverse_mul_pow

@[simp]
theorem eval₂_reverse_eq_zero_iff (i : R →+* S) (x : S) [Invertible x] (f : R[X]) :
    eval₂ i (⅟ x) (reverse f) = 0 ↔ eval₂ i x f = 0 :=
  eval₂_reflect_eq_zero_iff i x _ _ le_rfl
#align polynomial.eval₂_reverse_eq_zero_iff Polynomial.eval₂_reverse_eq_zero_iff

end Eval₂

end Semiring

section Ring

variable {R : Type _} [Ring R]

@[simp]
theorem reflect_neg (f : R[X]) (N : ℕ) : reflect N (-f) = -reflect N f := by
  rw [neg_eq_neg_one_mul, ← C_1, ← C_neg, reflect_C_mul, C_neg, C_1, ← neg_eq_neg_one_mul]
#align polynomial.reflect_neg Polynomial.reflect_neg

@[simp]
theorem reflect_sub (f g : R[X]) (N : ℕ) : reflect N (f - g) = reflect N f - reflect N g := by
  rw [sub_eq_add_neg, sub_eq_add_neg, reflect_add, reflect_neg]
#align polynomial.reflect_sub Polynomial.reflect_sub

@[simp]
theorem reverse_neg (f : R[X]) : reverse (-f) = -reverse f := by
  rw [reverse, reverse, reflect_neg, natDegree_neg]
#align polynomial.reverse_neg Polynomial.reverse_neg

end Ring

end Polynomial


/-
Copyright (c) 2022 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import order.monotone.union
import algebra.order.group.instances

/-!
# Monotonicity of odd functions

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

An odd function on a linear ordered additive commutative group `G` is monotone on the whole group
provided that is is monotone on `set.Ici 0`, see `monotone_of_odd_of_monotone_on_nonneg`. We also
prove versions of this lemma for `antitone`, `strict_mono`, and `strict_anti`.
-/

open set
variables {G H : Type*} [linear_ordered_add_comm_group G] [ordered_add_comm_group H]

/-- An odd function on a linear ordered additive commutative group is strictly monotone on the whole
group provided that it is strictly monotone on `set.Ici 0`. -/
lemma strict_mono_of_odd_strict_mono_on_nonneg {f : G → H} (h₁ : ∀ x, f (-x) = -f x)
  (h₂ : strict_mono_on f (Ici 0)) :
  strict_mono f :=
begin
  refine strict_mono_on.Iic_union_Ici (λ x hx y hy hxy, neg_lt_neg_iff.1 _) h₂,
  rw [← h₁, ← h₁],
  exact h₂ (neg_nonneg.2 hy) (neg_nonneg.2 hx) (neg_lt_neg hxy)
end

/-- An odd function on a linear ordered additive commutative group is strictly antitone on the whole
group provided that it is strictly antitone on `set.Ici 0`. -/
lemma strict_anti_of_odd_strict_anti_on_nonneg {f : G → H} (h₁ : ∀ x, f (-x) = -f x)
  (h₂ : strict_anti_on f (Ici 0)) :
  strict_anti f :=
@strict_mono_of_odd_strict_mono_on_nonneg G Hᵒᵈ _ _ _ h₁ h₂

/-- An odd function on a linear ordered additive commutative group is monotone on the whole group
provided that it is monotone on `set.Ici 0`. -/
lemma monotone_of_odd_of_monotone_on_nonneg {f : G → H} (h₁ : ∀ x, f (-x) = -f x)
  (h₂ : monotone_on f (Ici 0)) : monotone f :=
begin
  refine monotone_on.Iic_union_Ici (λ x hx y hy hxy, neg_le_neg_iff.1 _) h₂,
  rw [← h₁, ← h₁],
  exact h₂ (neg_nonneg.2 hy) (neg_nonneg.2 hx) (neg_le_neg hxy)
end

/-- An odd function on a linear ordered additive commutative group is antitone on the whole group
provided that it is monotone on `set.Ici 0`. -/
lemma antitone_of_odd_of_monotone_on_nonneg {f : G → H} (h₁ : ∀ x, f (-x) = -f x)
  (h₂ : antitone_on f (Ici 0)) : antitone f :=
@monotone_of_odd_of_monotone_on_nonneg G Hᵒᵈ _ _ _ h₁ h₂


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

! This file was ported from Lean 3 source module data.fintype.lattice
! leanprover-community/mathlib commit 509de852e1de55e1efa8eacfa11df0823f26f226
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Fintype.Card
import Mathlib.Data.Finset.Lattice

/-!
# Lemmas relating fintypes and order/lattice structure.
-/

open Function

open Nat

universe u v

variable {α β : Type _}

namespace Finset

variable [Fintype α] {s : Finset α}

/-- A special case of `Finset.sup_eq_supᵢ` that omits the useless `x ∈ univ` binder. -/
theorem sup_univ_eq_supᵢ [CompleteLattice β] (f : α → β) : Finset.univ.sup f = supᵢ f :=
  (sup_eq_supᵢ _ f).trans <| congr_arg _ <| funext fun _ => supᵢ_pos (mem_univ _)
#align finset.sup_univ_eq_supr Finset.sup_univ_eq_supᵢ

/-- A special case of `Finset.inf_eq_infᵢ` that omits the useless `x ∈ univ` binder. -/
theorem inf_univ_eq_infᵢ [CompleteLattice β] (f : α → β) : Finset.univ.inf f = infᵢ f :=
  @sup_univ_eq_supᵢ _ βᵒᵈ _ _ (f : α → βᵒᵈ)
#align finset.inf_univ_eq_infi Finset.inf_univ_eq_infᵢ

@[simp]
theorem fold_inf_univ [SemilatticeInf α] [OrderBot α] (a : α) :
    -- Porting note: added `haveI`
    haveI : IsCommutative α (· ⊓ ·) := inferInstance
    (Finset.univ.fold (· ⊓ ·) a fun x => x) = ⊥ :=
  eq_bot_iff.2 <|
    ((Finset.fold_op_rel_iff_and <| @le_inf_iff α _).1 le_rfl).2 ⊥ <| Finset.mem_univ _
#align finset.fold_inf_univ Finset.fold_inf_univ

@[simp]
theorem fold_sup_univ [SemilatticeSup α] [OrderTop α] (a : α) :
    -- Porting note: added `haveI`
    haveI : IsCommutative α (· ⊔ ·) := inferInstance
    (Finset.univ.fold (· ⊔ ·) a fun x => x) = ⊤ :=
  @fold_inf_univ αᵒᵈ _ _ _ _
#align finset.fold_sup_univ Finset.fold_sup_univ

end Finset

open Finset Function

theorem Finite.exists_max [Finite α] [Nonempty α] [LinearOrder β] (f : α → β) :
    ∃ x₀ : α, ∀ x, f x ≤ f x₀ := by
  cases nonempty_fintype α
  simpa using exists_max_image univ f univ_nonempty
#align finite.exists_max Finite.exists_max

theorem Finite.exists_min [Finite α] [Nonempty α] [LinearOrder β] (f : α → β) :
    ∃ x₀ : α, ∀ x, f x₀ ≤ f x := by
  cases nonempty_fintype α
  simpa using exists_min_image univ f univ_nonempty
#align finite.exists_min Finite.exists_min


/-
Copyright (c) 2020 Fox Thomson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Fox Thomson
-/
import data.list.join
import data.set.lattice

/-!
# Languages

This file contains the definition and operations on formal languages over an alphabet. Note strings
are implemented as lists over the alphabet.
The operations in this file define a [Kleene algebra](https://en.wikipedia.org/wiki/Kleene_algebra)
over the languages.
-/

universes u v

variables {α : Type u}

/-- A language is a set of strings over an alphabet. -/
@[derive [has_mem (list α), has_singleton (list α), has_insert (list α), complete_boolean_algebra]]
def language (α) := set (list α)

namespace language

local attribute [reducible] language

/-- Zero language has no elements. -/
instance : has_zero (language α) := ⟨(∅ : set _)⟩
/-- `1 : language α` contains only one element `[]`. -/
instance : has_one (language α) := ⟨{[]}⟩

instance : inhabited (language α) := ⟨0⟩

/-- The sum of two languages is the union of  -/
instance : has_add (language α) := ⟨set.union⟩
instance : has_mul (language α) := ⟨set.image2 (++)⟩

lemma zero_def : (0 : language α) = (∅ : set _) := rfl
lemma one_def : (1 : language α) = {[]} := rfl

lemma add_def (l m : language α) : l + m = l ∪ m := rfl
lemma mul_def (l m : language α) : l * m = set.image2 (++) l m := rfl

/-- The star of a language `L` is the set of all strings which can be written by concatenating
  strings from `L`. -/
def star (l : language α) : language α :=
{ x | ∃ S : list (list α), x = S.join ∧ ∀ y ∈ S, y ∈ l}

lemma star_def (l : language α) :
  l.star = { x | ∃ S : list (list α), x = S.join ∧ ∀ y ∈ S, y ∈ l} := rfl

@[simp] lemma mem_one (x : list α) : x ∈ (1 : language α) ↔ x = [] := by refl
@[simp] lemma mem_add (l m : language α) (x : list α) : x ∈ l + m ↔ x ∈ l ∨ x ∈ m :=
by simp [add_def]
lemma mem_mul (l m : language α) (x : list α) : x ∈ l * m ↔ ∃ a b, a ∈ l ∧ b ∈ m ∧ a ++ b = x :=
by simp [mul_def]
lemma mem_star (l : language α) (x : list α) :
  x ∈ l.star ↔ ∃ S : list (list α), x = S.join ∧ ∀ y ∈ S, y ∈ l :=
iff.rfl

instance : semiring (language α) :=
{ add := (+),
  add_assoc := set.union_assoc,
  zero := 0,
  zero_add := set.empty_union,
  add_zero := set.union_empty,
  add_comm := set.union_comm,
  mul := (*),
  mul_assoc := λ l m n,
    by simp only [mul_def, set.image2_image2_left, set.image2_image2_right, list.append_assoc],
  zero_mul := by simp [zero_def, mul_def],
  mul_zero := by simp [zero_def, mul_def],
  one := 1,
  one_mul := λ l, by simp [mul_def, one_def],
  mul_one := λ l, by simp [mul_def, one_def],
  left_distrib := λ l m n, by simp only [mul_def, add_def, set.image2_union_right],
  right_distrib := λ l m n, by simp only [mul_def, add_def, set.image2_union_left] }

@[simp] lemma add_self (l : language α) : l + l = l := sup_idem

lemma star_def_nonempty (l : language α) :
  l.star = {x | ∃ S : list (list α), x = S.join ∧ ∀ y ∈ S, y ∈ l ∧ y ≠ []} :=
begin
  ext x,
  split,
  { rintro ⟨S, rfl, h⟩,
    refine ⟨S.filter (λ l, ¬list.empty l), by simp, λ y hy, _⟩,
    rw [list.mem_filter, list.empty_iff_eq_nil] at hy,
    exact ⟨h y hy.1, hy.2⟩ },
  { rintro ⟨S, hx, h⟩,
    exact ⟨S, hx, λ y hy, (h y hy).1⟩ }
end

lemma le_mul_congr {l₁ l₂ m₁ m₂ : language α} : l₁ ≤ m₁ → l₂ ≤ m₂ → l₁ * l₂ ≤ m₁ * m₂ :=
begin
  intros h₁ h₂ x hx,
  simp only [mul_def, exists_and_distrib_left, set.mem_image2, set.image_prod] at hx ⊢,
  tauto
end

lemma le_add_congr {l₁ l₂ m₁ m₂ : language α} : l₁ ≤ m₁ → l₂ ≤ m₂ → l₁ + l₂ ≤ m₁ + m₂ := sup_le_sup

lemma mem_supr {ι : Sort v} {l : ι → language α} {x : list α} :
  x ∈ (⨆ i, l i) ↔ ∃ i, x ∈ l i :=
set.mem_Union

lemma supr_mul {ι : Sort v} (l : ι → language α) (m : language α) :
  (⨆ i, l i) * m = ⨆ i, l i * m :=
set.image2_Union_left _ _ _

lemma mul_supr {ι : Sort v} (l : ι → language α) (m : language α) :
  m * (⨆ i, l i) = ⨆ i, m * l i :=
set.image2_Union_right _ _ _

lemma supr_add {ι : Sort v} [nonempty ι] (l : ι → language α) (m : language α) :
  (⨆ i, l i) + m = ⨆ i, l i + m := supr_sup

lemma add_supr {ι : Sort v} [nonempty ι] (l : ι → language α) (m : language α) :
  m + (⨆ i, l i) = ⨆ i, m + l i := sup_supr

lemma mem_pow {l : language α} {x : list α} {n : ℕ} :
  x ∈ l ^ n ↔ ∃ S : list (list α), x = S.join ∧ S.length = n ∧ ∀ y ∈ S, y ∈ l :=
begin
  induction n with n ihn generalizing x,
  { simp only [mem_one, pow_zero, list.length_eq_zero],
    split,
    { rintro rfl, exact ⟨[], rfl, rfl, λ y h, h.elim⟩ },
    { rintro ⟨_, rfl, rfl, _⟩, refl } },
  { simp only [pow_succ, mem_mul, ihn],
    split,
    { rintro ⟨a, b, ha, ⟨S, rfl, rfl, hS⟩, rfl⟩,
      exact ⟨a :: S, rfl, rfl, list.forall_mem_cons.2 ⟨ha, hS⟩⟩ },
    { rintro ⟨_|⟨a, S⟩, rfl, hn, hS⟩; cases hn,
      rw list.forall_mem_cons at hS,
      exact ⟨a, _, hS.1, ⟨S, rfl, rfl, hS.2⟩, rfl⟩ } }
end

lemma star_eq_supr_pow (l : language α) : l.star = ⨆ i : ℕ, l ^ i :=
begin
  ext x,
  simp only [mem_star, mem_supr, mem_pow],
  split,
  { rintro ⟨S, rfl, hS⟩, exact ⟨_, S, rfl, rfl, hS⟩ },
  { rintro ⟨_, S, rfl, rfl, hS⟩, exact ⟨S, rfl, hS⟩ }
end

lemma mul_self_star_comm (l : language α) : l.star * l = l * l.star :=
by simp only [star_eq_supr_pow, mul_supr, supr_mul, ← pow_succ, ← pow_succ']

@[simp] lemma one_add_self_mul_star_eq_star (l : language α) : 1 + l * l.star = l.star :=
begin
  simp only [star_eq_supr_pow, mul_supr, ← pow_succ, ← pow_zero l],
  exact sup_supr_nat_succ _
end

@[simp] lemma one_add_star_mul_self_eq_star (l : language α) : 1 + l.star * l = l.star :=
by rw [mul_self_star_comm, one_add_self_mul_star_eq_star]

lemma star_mul_le_right_of_mul_le_right (l m : language α) : l * m ≤ m → l.star * m ≤ m :=
begin
  intro h,
  rw [star_eq_supr_pow, supr_mul],
  refine supr_le _,
  intro n,
  induction n with n ih,
  { simp },
  rw [pow_succ', mul_assoc (l^n) l m],
  exact le_trans (le_mul_congr (le_refl _) h) ih,
end

lemma star_mul_le_left_of_mul_le_left (l m : language α) : m * l ≤ m → m * l.star ≤ m :=
begin
  intro h,
  rw [star_eq_supr_pow, mul_supr],
  refine supr_le _,
  intro n,
  induction n with n ih,
  { simp },
  rw [pow_succ, ←mul_assoc m l (l^n)],
  exact le_trans (le_mul_congr h (le_refl _)) ih
end

end language


/-
Copyright (c) 2021 Henry Swanson. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Henry Swanson, Patrick Massot
-/
import analysis.special_functions.exponential
import combinatorics.derangements.finite
import order.filter.basic

/-!
# Derangement exponential series

This file proves that the probability of a permutation on n elements being a derangement is 1/e.
The specific lemma is `num_derangements_tendsto_inv_e`.
-/
open filter

open_locale big_operators
open_locale topological_space

theorem num_derangements_tendsto_inv_e :
  tendsto (λ n, (num_derangements n : ℝ) / n.factorial) at_top
  (𝓝 (real.exp (-1))) :=
begin
  -- we show that d(n)/n! is the partial sum of exp(-1), but offset by 1.
  -- this isn't entirely obvious, since we have to ensure that asc_factorial and
  -- factorial interact in the right way, e.g., that k ≤ n always
  let s : ℕ → ℝ := λ n, ∑ k in finset.range n, (-1 : ℝ)^k / k.factorial,
  suffices : ∀ n : ℕ, (num_derangements n : ℝ) / n.factorial = s(n+1),
  { simp_rw this,
    -- shift the function by 1, and then use the fact that the partial sums
    -- converge to the infinite sum
    rw tendsto_add_at_top_iff_nat 1,
    apply has_sum.tendsto_sum_nat,
    -- there's no specific lemma for ℝ that ∑ x^k/k! sums to exp(x), but it's
    -- true in more general fields, so use that lemma
    rw real.exp_eq_exp_ℝ,
    exact exp_series_div_has_sum_exp ℝ (-1 : ℝ) },
  intro n,
  rw [← int.cast_coe_nat, num_derangements_sum],
  push_cast,
  rw finset.sum_div,
  -- get down to individual terms
  refine finset.sum_congr (refl _) _,
  intros k hk,
  have h_le : k ≤ n := finset.mem_range_succ_iff.mp hk,
  rw [nat.asc_factorial_eq_div, add_tsub_cancel_of_le h_le],
  push_cast [nat.factorial_dvd_factorial h_le],
  field_simp [nat.factorial_ne_zero],
  ring,
end


universe u
variables (α : Type u) (a b c d : α)
variables (hab : a = b) (hcb : c = b) (hcd : c = d)

example : a = d := eq.trans (eq.trans hab (eq.symm hcb)) hcd


theorem and_commutative (p q : Prop) : p ∧ q → q ∧ p :=
  fun hpq : p ∧ q =>
  have hp : p := And.left hpq
  have hq : q := And.right hpq
  show q ∧ p from And.intro hq hp


import tactic
import data.real.basic
import data.complex.exponential
import analysis.special_functions.pow

 import wonky_sq.basic

/-!
# Defining the functions at zero

## Wonky Square GTFs

Let us show that these functions follow general trigonometric convention. We
are going to show here that they have have values at 0 apart from cscₘ, but
thats ∞ -/

noncomputable theory
open_locale classical

open real

/- 008
Here we prove that sinₘ 0 = 0.
-/
lemma sinm_zero (m : ℝ): sinm 0 m = 0 :=
begin
  unfold sinm,
  rw sin_zero,
  rw zero_mul,
end

-- we know that p q > 0 because the negative values get funny and annoying.

-- #print instances has_pow

/- 009
Here we prove a lemma similar to 008, but for cosₘ
-/
lemma cosm_zero (m : ℝ) (hm : m ≠ 0) : cosm 0 m = 1 :=
begin
  unfold cosm,
  unfold radius,
  rw [cos_zero, one_mul, sin_zero, abs_zero, abs_one],
  simp,
  rw [zero_rpow, zero_add, one_rpow],
  simp, exact hm,
  end

/- 010
Similar to 008, but for tanₘ
-/
lemma tanm_zero (m : ℝ) : tanm 0 m = 0 :=
begin
  unfold tanm,
  rw [sinm_zero, zero_div],
end

/- 011
Similar to 008, but for secₘ
-/
lemma secm_zero (m : ℝ) (hm : m ≠ 0) : secm 0 m = 1 :=
begin
  unfold secm,
  rw cosm_zero,
  norm_num,
  exact hm,
end

/-!
## p-GTFs
nothing yet
-/


/-
Copyright (c) 2021 Yaël Dillies. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies
-/
import algebra.order.module
import algebra.pointwise
import data.real.basic

/-!
# Pointwise operations on sets of reals

This file relates `Inf (a • s)`/`Sup (a • s)` with `a • Inf s`/`a • Sup s` for `s : set ℝ`.

# TODO

This is true more generally for conditionally complete linear order whose default value is `0`. We
don't have those yet.
-/

open set
open_locale pointwise

variables {α : Type*} [linear_ordered_field α]

section mul_action_with_zero
variables [mul_action_with_zero α ℝ] [ordered_smul α ℝ] {a : α}

lemma real.Inf_smul_of_nonneg (ha : 0 ≤ a) (s : set ℝ) : Inf (a • s) = a • Inf s :=
begin
  obtain rfl | hs := s.eq_empty_or_nonempty,
  { rw [smul_set_empty, real.Inf_empty, smul_zero'] },
  obtain rfl | ha' := ha.eq_or_lt,
  { rw [zero_smul_set hs, zero_smul],
    exact cInf_singleton 0 },
  by_cases bdd_below s,
  { exact ((order_iso.smul_left ℝ ha').map_cInf' hs h).symm },
  { rw [real.Inf_of_not_bdd_below (mt (bdd_below_smul_iff_of_pos ha').1 h),
      real.Inf_of_not_bdd_below h, smul_zero'] }
end

lemma real.Sup_smul_of_nonneg (ha : 0 ≤ a) (s : set ℝ) : Sup (a • s) = a • Sup s :=
begin
  obtain rfl | hs := s.eq_empty_or_nonempty,
  { rw [smul_set_empty, real.Sup_empty, smul_zero'] },
  obtain rfl | ha' := ha.eq_or_lt,
  { rw [zero_smul_set hs, zero_smul],
    exact cSup_singleton 0 },
  by_cases bdd_above s,
  { exact ((order_iso.smul_left ℝ ha').map_cSup' hs h).symm },
  { rw [real.Sup_of_not_bdd_above (mt (bdd_above_smul_iff_of_pos ha').1 h),
      real.Sup_of_not_bdd_above h, smul_zero'] }
end

end mul_action_with_zero

section module
variables [module α ℝ] [ordered_smul α ℝ] {a : α}

lemma real.Inf_smul_of_nonpos (ha : a ≤ 0) (s : set ℝ) : Inf (a • s) = a • Sup s :=
begin
  obtain rfl | hs := s.eq_empty_or_nonempty,
  { rw [smul_set_empty, real.Inf_empty, real.Sup_empty, smul_zero'] },
  obtain rfl | ha' := ha.eq_or_lt,
  { rw [zero_smul_set hs, zero_smul],
    exact cInf_singleton 0 },
  by_cases bdd_above s,
  { exact ((order_iso.smul_left_dual ℝ ha').map_cSup' hs h).symm },
  { rw [real.Inf_of_not_bdd_below (mt (bdd_below_smul_iff_of_neg ha').1 h),
      real.Sup_of_not_bdd_above h, smul_zero'] }
end

lemma real.Sup_smul_of_nonpos (ha : a ≤ 0) (s : set ℝ) : Sup (a • s) = a • Inf s :=
begin
  obtain rfl | hs := s.eq_empty_or_nonempty,
  { rw [smul_set_empty, real.Sup_empty, real.Inf_empty, smul_zero] },
  obtain rfl | ha' := ha.eq_or_lt,
  { rw [zero_smul_set hs, zero_smul],
    exact cSup_singleton 0 },
  by_cases bdd_below s,
  { exact ((order_iso.smul_left_dual ℝ ha').map_cInf' hs h).symm },
  { rw [real.Sup_of_not_bdd_above (mt (bdd_above_smul_iff_of_neg ha').1 h),
      real.Inf_of_not_bdd_below h, smul_zero] }
end

end module


/-
Copyright (c) 2022 Alex J. Best. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alex J. Best
-/
import data.finsupp.order
import order.well_founded_set

/-!
# Partial well ordering on finsupps

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

This file contains the fact that finitely supported functions from a fintype are
partially well ordered when the codomain is a linear order that is well ordered.
It is in a separate file for now so as to not add imports to the file `order.well_founded_set`.

## Main statements

* `finsupp.is_pwo` - finitely supported functions from a fintype are partially well ordered when
  the codomain is a linear order that is well ordered

## Tags

Dickson, order, partial well order
-/

/-- A version of **Dickson's lemma** any subset of functions `σ →₀ α` is partially well
ordered, when `σ` is `finite` and `α` is a linear well order.
This version uses finsupps on a finite type as it is intended for use with `mv_power_series`.
-/
lemma finsupp.is_pwo {α σ : Type*} [has_zero α] [linear_order α] [is_well_order α (<)] [finite σ]
  (S : set (σ →₀ α)) : S.is_pwo :=
finsupp.equiv_fun_on_finite.symm_image_image S ▸
  set.partially_well_ordered_on.image_of_monotone_on (pi.is_pwo _) (λ a b ha hb, id)


-- begin header
import tactic.linarith
import data.real.basic
import algebra.pi_instances
import data.set.function
noncomputable theory
local attribute [instance, priority 0] classical.prop_decidable

-- We introduce the usual mathematical notation for absolute value
local notation `|` x `|` := abs x
-- end header

/- Section
Limits of sequences
-/

/- Sub-section
Basic definitions
-/

/-
In this file, we introduce limits of sequences of real numbers.

We model a sequence $a₀, a₁, a₂, \dots$ of real numbers as a function
from $ℕ := \{0,1,2,\dots\}$ to $ℝ$, sending $n$ to $a_n$. So in the below
definition of the limit of a sequence, $a : ℕ → ℝ$ is the sequence.
-/

/- Definition
A sequence $a$ converges to a real number $l$ if, for all positive
$ε$, there is some $N$ such that, for every $n ≥ N$, $|a_n - l| < ε$.
-/
definition is_limit (a : ℕ → ℝ) (l : ℝ) : Prop :=
∀ ε > 0, ∃ N, ∀ n ≥ N, | a n - l | < ε

/- Definition
A sequence converges if and only if it has a limit.
-/
definition has_limit (a : ℕ → ℝ) : Prop := ∃ l : ℝ, is_limit a l

/-
The difference between the above definition and the preceding one is that we
don't specify the limit, we just claim that it exists.
-/

/- Sub-section
Basic lemmas
-/

/- Lemma
The constant sequence with value $a$ converges to $a$.
-/
lemma tendsto_const (a : ℝ) : is_limit (λ n, a) a :=
begin
  -- Let $ε$ be any positive real number.
  intros ε εpos,
  -- We choose $N = 0$ in the definition of a limit,
  use 0,
  -- and observe that, for every $n ≥ N$, |a - a| < ε
  intros n _,
  simpa [sub_self] using εpos
end

/-
We will need an easy reformulation of the limit definition
-/
/- Lemma
A sequence $a_n$ converges to a number $l$ if and only if the sequence
$a_n - l$ converges to zero.
-/
lemma tendsto_iff_sub_tendsto_zero {a : ℕ → ℝ} {l : ℝ} :
  is_limit (λ n, a n - l) 0 ↔ is_limit a l :=
begin
  -- We need to prove both implications, but both proofs are the same.
  split ;
  { -- We assume the premise, and consider any positive $ε$.
    intros h ε εpos,
    -- By the premise specialized to our $ε$, we get some $N$,
    rcases h ε εpos with ⟨N, H⟩,
    -- and use that $N$ to prove the other condition
    use N,
    -- which is immediate.
    intros n hn,
    simpa using H n hn }
end

/-
In the definition of a limit, the final ε can be replaced
by a constant multiple of ε. We could assume this constant is positive
but we don't want to deal with this when applying the lemma.
-/
/- Lemma
Let $a$ be a sequence. In order to prove that $a$ converges to some limit
$l$, it is sufficient to find some number $K$ such that,
for all $ε > 0$, there is some $N$ such that, for all $n ≥ N$,
$|a_n - l| < Kε$.
-/
lemma tendsto_of_mul_eps (a : ℕ → ℝ) (l : ℝ) (K : ℝ)
  (h : ∀ ε > 0, ∃ N, ∀ n ≥ N, | a n - l | < K*ε) : is_limit a l :=
begin
  -- Let $ε$ be any positive number.
  intros ε εpos,
  -- $K$ is either non positive or positive
  cases le_or_gt K 0 with Knonpos Kpos,
  { -- If $K$ is non positive then our assumed bound quickly
    -- gives a contradiction.
    exfalso,
    -- Indeed we can apply our assumption to $ε = 1$ to get $N$ such that
    -- for all $n ≥ N$, $|a n - l| < K * 1$
    rcases h 1 (by linarith) with ⟨N, H⟩,
    -- in particular this holds when $n = N$
    specialize H N (by linarith),
    -- but $|a N - l| ≥ 0$ so we get a contradiction.
    have : |a N - l| ≥ 0, from abs_nonneg _,
    linarith },
  { -- Now assume $K$ is positive. Our assumption gives $N$ such that,
    -- for all $n ≥ N$, $|a n - l| < K * (ε / K)$
    rcases h (ε/K) (div_pos εpos Kpos) with ⟨N, H⟩,
    -- we can simplify that $K (ε / K)$ and we are done.
    rw mul_div_cancel' _ (ne_of_gt Kpos) at H,
    tauto }
end


/- Problem 1: Programming in Lean -/

/-
Define the following list functions. Example uses are given via
example. Once you have defined the function (replaced the _ with
an implementation), the examples will work (the red highlighting
will go away).
-/

-- part p1-a
def nonzeros : List Nat -> List Nat := _
example : nonzeros [0,1,0,2,3,0,0] = [1,2,3] := by rfl

def oddmembers : List Nat -> List Nat := _
example : oddmembers [0,1,0,2,3,0,0] = [1,3] := by rfl

def countoddmembers : List Nat -> Nat := _
example : countoddmembers [1,0,3,1,4,5] = 4 := by rfl
example : countoddmembers [0,2,4] = 0 := by rfl
example : countoddmembers [] = 0 := by rfl

-- part p1-a

/- A bag (or multiset) is like a set, except that each element
can appear multiple times rather than just once. One possible
representation for a bag of numbers is as a list.
-/

-- part p1-b
def Bag := List Nat
-- part p1-b

/- Complete the following definitions for the functions count,
union, add, and member for bags.
-/

-- part p1-c
def count : Nat -> Bag -> Nat := _
example : count 1 [1,2,3,1,4,1] = 3 := by rfl
example : count 6 [1,2,3,1,4,1] = 0 := by rfl

def union : Bag -> Bag -> Bag := _
example : count 1 (union [1,2,3] [1,4,1]) = 3 := by rfl

def add : Nat -> Bag -> Bag := _
example : count 1 (add 1 [1,4,1]) = 3 := by rfl
example : count 5 (add 1 [1,4,1]) = 0 := by rfl

def member : Nat -> Bag -> Bool := _
example : member 1 [1,4,1] = true := by rfl
example : member 2 [1,4,1] = false := by rfl

def remove_one : Nat -> Bag -> Bag := _
example : count 5 (remove_one 5 [2,1,5,4,1]) = 0 := by rfl
example : count 5 (remove_one 5 [2,1,4,1]) = 0 := by rfl
example : count 4 (remove_one 5 [2,1,4,5,1,4]) = 2 := by rfl
example : count 5 (remove_one 5 [2,1,5,4,5,1,4]) = 1 := by rfl

def remove_all : Nat -> Bag -> Bag := _
example : count 5 (remove_all 5 [2,1,5,4,1]) = 0 := by rfl
example : count 5 (remove_all 5 [2,1,4,1]) = 0 := by rfl
example : count 4 (remove_all 5 [2,1,4,5,1,4]) = 2 := by rfl
example : count 5 (remove_all 5 [2,1,5,4,5,1,4,5,1,4]) = 0 := by rfl

def subset : Bag -> Bag -> Bool := _
example : subset [1,2] [2,1,4,1] = true := by rfl
example : subset [1,2,2] [2,1,4,1] = false := by rfl

-- part p1-c

/- Proofs in minimal propositional logic -/

-- part p1-d

variable (P Q R S : Prop)

theorem t1 : P -> P := _

theorem t2 : P -> Q -> P := _

theorem t3 : (P -> Q) -> (Q -> R) -> P -> R := _

theorem t4 : P -> Q -> (Q -> P -> R) -> R := _

theorem t5 : (P -> Q) -> (P -> R) -> (R -> Q -> S) -> P -> S := _

theorem t6 : (P -> Q -> R) -> (P -> Q) -> P -> R := _

-- part p1-d

/- Proofs in propositional logic -/

-- part p1-e

theorem p1 : P ∧ Q -> Q ∧ P := _

theorem p2 : P ∧ Q -> P := _

theorem p3 : P ∧ Q -> (Q -> R) -> R ∧ P := _

theorem p4 : P ∨ Q -> (P -> R) -> (Q -> R) -> R := _

theorem p5 : P ∨ Q -> (P -> R) -> R ∨ Q := _

theorem p6 : ¬ Q -> (R -> Q) -> (R ∨ ¬ S) -> S -> False := _

-- part p1-e


/-
Copyright (c) 2021 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser
-/
import linear_algebra.matrix.determinant
import data.mv_polynomial.basic
import data.mv_polynomial.comm_ring

/-!
# Matrices of multivariate polynomials

In this file, we prove results about matrices over an mv_polynomial ring.
In particular, we provide `matrix.mv_polynomial_X` which associates every entry of a matrix with a
unique variable.

## Tags

matrix determinant, multivariate polynomial
-/
variables {m n R S : Type*}

namespace matrix

variables (m n R)

/-- The matrix with variable `X (i,j)` at location `(i,j)`. -/
@[simp] noncomputable def mv_polynomial_X [comm_semiring R] : matrix m n (mv_polynomial (m × n) R)
| i j := mv_polynomial.X (i, j)

variables {m n R S}

/-- Any matrix `A` can be expressed as the evaluation of `matrix.mv_polynomial_X`.

This is of particular use when `mv_polynomial (m × n) R` is an integral domain but `S` is
not, as if the `mv_polynomial.eval₂` can be pulled to the outside of a goal, it can be solved in
under cancellative assumptions. -/
lemma mv_polynomial_X_map_eval₂ [comm_semiring R] [comm_semiring S]
  (f : R →+* S) (A : matrix m n S) :
  (mv_polynomial_X m n R).map (mv_polynomial.eval₂ f $ λ p : m × n, A p.1 p.2) = A :=
ext $ λ i j, mv_polynomial.eval₂_X _ (λ p : m × n, A p.1 p.2) (i, j)

/-- A variant of `matrix.mv_polynomial_X_map_eval₂` with a bundled `ring_hom` on the LHS. -/
lemma mv_polynomial_X_map_matrix_eval [fintype m] [decidable_eq m]
  [comm_semiring R] (A : matrix m m R) :
  (mv_polynomial.eval $ λ p : m × m, A p.1 p.2).map_matrix (mv_polynomial_X m m R) = A :=
mv_polynomial_X_map_eval₂ _ A

variables (R)

/-- A variant of `matrix.mv_polynomial_X_map_eval₂` with a bundled `alg_hom` on the LHS. -/
lemma mv_polynomial_X_map_matrix_aeval [fintype m] [decidable_eq m]
  [comm_semiring R] [comm_semiring S] [algebra R S] (A : matrix m m S) :
  (mv_polynomial.aeval $ λ p : m × m, A p.1 p.2).map_matrix (mv_polynomial_X m m R) = A :=
mv_polynomial_X_map_eval₂ _ A

variables (m R)

/-- In a nontrivial ring, `matrix.mv_polynomial_X m m R` has non-zero determinant. -/
lemma det_mv_polynomial_X_ne_zero [decidable_eq m] [fintype m] [comm_ring R] [nontrivial R] :
  det (mv_polynomial_X m m R) ≠ 0 :=
begin
  intro h_det,
  have := congr_arg matrix.det (mv_polynomial_X_map_matrix_eval (1 : matrix m m R)),
  rw [det_one, ←ring_hom.map_det, h_det, ring_hom.map_zero] at this,
  exact zero_ne_one this,
end

end matrix


/-
Copyright (c) 2021 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser

! This file was ported from Lean 3 source module algebra.module.submodule.pointwise
! leanprover-community/mathlib commit 48085f140e684306f9e7da907cd5932056d1aded
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.GroupTheory.Subgroup.Pointwise
import Mathlib.LinearAlgebra.Span

/-! # Pointwise instances on `Submodule`s

This file provides:

* `Submodule.pointwiseNeg`

and the actions

* `Submodule.pointwiseDistribMulAction`
* `Submodule.pointwiseMulActionWithZero`

which matches the action of `Set.mulActionSet`.

These actions are available in the `Pointwise` locale.

## Implementation notes

Most of the lemmas in this file are direct copies of lemmas from
`GroupTheory/Submonoid/Pointwise.lean`.
-/

variable {α : Type _} {R : Type _} {M : Type _}

open Pointwise

namespace Submodule

section Neg

section Semiring

variable [Semiring R] [AddCommGroup M] [Module R M]

/-- The submodule with every element negated. Note if `R` is a ring and not just a semiring, this
is a no-op, as shown by `Submodule.neg_eq_self`.

Recall that When `R` is the semiring corresponding to the nonnegative elements of `R'`,
`Submodule R' M` is the type of cones of `M`. This instance reflects such cones about `0`.

This is available as an instance in the `Pointwise` locale. -/
protected def pointwiseNeg : Neg (Submodule R M)
    where neg p :=
    { -p.toAddSubmonoid with
      carrier := -(p : Set M)
      smul_mem' := fun r m hm => Set.mem_neg.2 <| smul_neg r m ▸ p.smul_mem r <| Set.mem_neg.1 hm }
#align submodule.has_pointwise_neg Submodule.pointwiseNeg

scoped[Pointwise] attribute [instance] Submodule.pointwiseNeg

open Pointwise

@[simp]
theorem coe_set_neg (S : Submodule R M) : ↑(-S) = -(S : Set M) :=
  rfl
#align submodule.coe_set_neg Submodule.coe_set_neg

@[simp]
theorem neg_toAddSubmonoid (S : Submodule R M) : (-S).toAddSubmonoid = -S.toAddSubmonoid :=
  rfl
#align submodule.neg_to_add_submonoid Submodule.neg_toAddSubmonoid

@[simp]
theorem mem_neg {g : M} {S : Submodule R M} : g ∈ -S ↔ -g ∈ S :=
  Iff.rfl
#align submodule.mem_neg Submodule.mem_neg

/-- `Submodule.pointwiseNeg` is involutive.

This is available as an instance in the `Pointwise` locale. -/
protected def involutivePointwiseNeg : InvolutiveNeg (Submodule R M)
    where
  neg := Neg.neg
  neg_neg _S := SetLike.coe_injective <| neg_neg _
#align submodule.has_involutive_pointwise_neg Submodule.involutivePointwiseNeg

scoped[Pointwise] attribute [instance] Submodule.involutivePointwiseNeg

@[simp]
theorem neg_le_neg (S T : Submodule R M) : -S ≤ -T ↔ S ≤ T :=
  SetLike.coe_subset_coe.symm.trans Set.neg_subset_neg
#align submodule.neg_le_neg Submodule.neg_le_neg

theorem neg_le (S T : Submodule R M) : -S ≤ T ↔ S ≤ -T :=
  SetLike.coe_subset_coe.symm.trans Set.neg_subset
#align submodule.neg_le Submodule.neg_le

/-- `Submodule.pointwiseNeg` as an order isomorphism. -/
def negOrderIso : Submodule R M ≃o Submodule R M
    where
  toEquiv := Equiv.neg _
  map_rel_iff' := @neg_le_neg _ _ _ _ _
#align submodule.neg_order_iso Submodule.negOrderIso

theorem closure_neg (s : Set M) : span R (-s) = -span R s := by
  apply le_antisymm
  · rw [span_le, coe_set_neg, ← Set.neg_subset, neg_neg]
    exact subset_span
  · rw [neg_le, span_le, coe_set_neg, ← Set.neg_subset]
    exact subset_span
#align submodule.closure_neg Submodule.closure_neg

@[simp]
theorem neg_inf (S T : Submodule R M) : -(S ⊓ T) = -S ⊓ -T :=
  SetLike.coe_injective Set.inter_neg
#align submodule.neg_inf Submodule.neg_inf

@[simp]
theorem neg_sup (S T : Submodule R M) : -(S ⊔ T) = -S ⊔ -T :=
  (negOrderIso : Submodule R M ≃o Submodule R M).map_sup S T
#align submodule.neg_sup Submodule.neg_sup

@[simp]
theorem neg_bot : -(⊥ : Submodule R M) = ⊥ :=
  SetLike.coe_injective <| (Set.neg_singleton 0).trans <| congr_arg _ neg_zero
#align submodule.neg_bot Submodule.neg_bot

@[simp]
theorem neg_top : -(⊤ : Submodule R M) = ⊤ :=
  SetLike.coe_injective <| Set.neg_univ
#align submodule.neg_top Submodule.neg_top

@[simp]
theorem neg_infᵢ {ι : Sort _} (S : ι → Submodule R M) : (-⨅ i, S i) = ⨅ i, -S i :=
  (negOrderIso : Submodule R M ≃o Submodule R M).map_infᵢ _
#align submodule.neg_infi Submodule.neg_infᵢ

@[simp]
theorem neg_supᵢ {ι : Sort _} (S : ι → Submodule R M) : (-⨆ i, S i) = ⨆ i, -S i :=
  (negOrderIso : Submodule R M ≃o Submodule R M).map_supᵢ _
#align submodule.neg_supr Submodule.neg_supᵢ

end Semiring

open Pointwise

@[simp]
theorem neg_eq_self [Ring R] [AddCommGroup M] [Module R M] (p : Submodule R M) : -p = p :=
  ext fun _ => p.neg_mem_iff
#align submodule.neg_eq_self Submodule.neg_eq_self

end Neg

variable [Semiring R] [AddCommMonoid M] [Module R M]

instance pointwiseAddCommMonoid : AddCommMonoid (Submodule R M)
    where
  add := (· ⊔ ·)
  add_assoc _ _ _ := sup_assoc
  zero := ⊥
  zero_add _ := bot_sup_eq
  add_zero _ := sup_bot_eq
  add_comm _ _ := sup_comm
#align submodule.pointwise_add_comm_monoid Submodule.pointwiseAddCommMonoid

@[simp]
theorem add_eq_sup (p q : Submodule R M) : p + q = p ⊔ q :=
  rfl
#align submodule.add_eq_sup Submodule.add_eq_sup

@[simp]
theorem zero_eq_bot : (0 : Submodule R M) = ⊥ :=
  rfl
#align submodule.zero_eq_bot Submodule.zero_eq_bot

instance : CanonicallyOrderedAddMonoid (Submodule R M) :=
  { Submodule.pointwiseAddCommMonoid,
    Submodule.completeLattice with
    zero := 0
    bot := ⊥
    add := (· + ·)
    add_le_add_left := fun _a _b => sup_le_sup_left
    exists_add_of_le := @fun _a b h => ⟨b, (sup_eq_right.2 h).symm⟩
    le_self_add := fun _a _b => le_sup_left }

section

variable [Monoid α] [DistribMulAction α M] [SMulCommClass α R M]

/-- The action on a submodule corresponding to applying the action to every element.

This is available as an instance in the `Pointwise` locale. -/
protected def pointwiseDistribMulAction : DistribMulAction α (Submodule R M)
    where
  smul a S := S.map (DistribMulAction.toLinearMap R M a : M →ₗ[R] M)
  one_smul S :=
    (congr_arg (fun f : Module.End R M => S.map f) (LinearMap.ext <| one_smul α)).trans S.map_id
  mul_smul _a₁ _a₂ S :=
    (congr_arg (fun f : Module.End R M => S.map f) (LinearMap.ext <| mul_smul _ _)).trans
      (S.map_comp _ _)
  smul_zero _a := map_bot _
  smul_add _a _S₁ _S₂ := map_sup _ _ _
#align submodule.pointwise_distrib_mul_action Submodule.pointwiseDistribMulAction

scoped[Pointwise] attribute [instance] Submodule.pointwiseDistribMulAction

open Pointwise

@[simp]
theorem coe_pointwise_smul (a : α) (S : Submodule R M) : ↑(a • S) = a • (S : Set M) :=
  rfl
#align submodule.coe_pointwise_smul Submodule.coe_pointwise_smul

@[simp]
theorem pointwise_smul_toAddSubmonoid (a : α) (S : Submodule R M) :
    (a • S).toAddSubmonoid = a • S.toAddSubmonoid :=
  rfl
#align submodule.pointwise_smul_to_add_submonoid Submodule.pointwise_smul_toAddSubmonoid

@[simp]
theorem pointwise_smul_toAddSubgroup {R M : Type _} [Ring R] [AddCommGroup M] [DistribMulAction α M]
    [Module R M] [SMulCommClass α R M] (a : α) (S : Submodule R M) :
    (a • S).toAddSubgroup = a • S.toAddSubgroup :=
  rfl
#align submodule.pointwise_smul_to_add_subgroup Submodule.pointwise_smul_toAddSubgroup

theorem smul_mem_pointwise_smul (m : M) (a : α) (S : Submodule R M) : m ∈ S → a • m ∈ a • S :=
  (Set.smul_mem_smul_set : _ → _ ∈ a • (S : Set M))
#align submodule.smul_mem_pointwise_smul Submodule.smul_mem_pointwise_smul

/-- See also `Submodule.smul_bot`. -/
@[simp]
theorem smul_bot' (a : α) : a • (⊥ : Submodule R M) = ⊥ :=
  map_bot _
#align submodule.smul_bot' Submodule.smul_bot'

/-- See also `Submodule.smul_sup`. -/
theorem smul_sup' (a : α) (S T : Submodule R M) : a • (S ⊔ T) = a • S ⊔ a • T :=
  map_sup _ _ _
#align submodule.smul_sup' Submodule.smul_sup'

theorem smul_span (a : α) (s : Set M) : a • span R s = span R (a • s) :=
  map_span _ _
#align submodule.smul_span Submodule.smul_span

theorem span_smul (a : α) (s : Set M) : span R (a • s) = a • span R s :=
  Eq.symm (span_image _).symm
#align submodule.span_smul Submodule.span_smul

instance pointwiseCentralScalar [DistribMulAction αᵐᵒᵖ M] [SMulCommClass αᵐᵒᵖ R M]
    [IsCentralScalar α M] : IsCentralScalar α (Submodule R M) :=
  ⟨fun _a S => (congr_arg fun f : Module.End R M => S.map f) <| LinearMap.ext <| op_smul_eq_smul _⟩
#align submodule.pointwise_central_scalar Submodule.pointwiseCentralScalar

@[simp]
theorem smul_le_self_of_tower {α : Type _} [Semiring α] [Module α R] [Module α M]
    [SMulCommClass α R M] [IsScalarTower α R M] (a : α) (S : Submodule R M) : a • S ≤ S := by
  rintro y ⟨x, hx, rfl⟩
  exact smul_of_tower_mem _ a hx
#align submodule.smul_le_self_of_tower Submodule.smul_le_self_of_tower

end

section

variable [Semiring α] [Module α M] [SMulCommClass α R M]

/-- The action on a submodule corresponding to applying the action to every element.

This is available as an instance in the `Pointwise` locale.

This is a stronger version of `Submodule.pointwiseDistribMulAction`. Note that `add_smul` does
not hold so this cannot be stated as a `Module`. -/
protected def pointwiseMulActionWithZero : MulActionWithZero α (Submodule R M) :=
  { Submodule.pointwiseDistribMulAction with
    zero_smul := fun S =>
      (congr_arg (fun f : M →ₗ[R] M => S.map f) (LinearMap.ext <| zero_smul α)).trans S.map_zero }
#align submodule.pointwise_mul_action_with_zero Submodule.pointwiseMulActionWithZero

scoped[Pointwise] attribute [instance] Submodule.pointwiseMulActionWithZero

end

end Submodule