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

! This file was ported from Lean 3 source module analysis.special_functions.compare_exp
! leanprover-community/mathlib commit f2ce6086713c78a7f880485f7917ea547a215982
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Analysis.SpecialFunctions.Pow
import Mathbin.Analysis.Asymptotics.AsymptoticEquivalent
import Mathbin.Analysis.Asymptotics.SpecificAsymptotics

/-!
# Growth estimates on `x ^ y` for complex `x`, `y`

Let `l` be a filter on `ℂ` such that `complex.re` tends to infinity along `l` and `complex.im z`
grows at a subexponential rate compared to `complex.re z`. Then

- `complex.is_o_log_abs_re_of_subexponential_im_re`: `real.log ∘ complex.abs` is `o`-small of
  `complex.re` along `l`;

- `complex.is_o_cpow_mul_exp`: $z^{a_1}e^{b_1 * z} = o\left(z^{a_1}e^{b_1 * z}\right)$ along `l`
  for any complex `a₁`, `a₂` and real `b₁ < b₂`.

We use these assumptions on `l` for two reasons. First, these are the assumptions that naturally
appear in the proof. Second, in some applications (e.g., in Ilyashenko's proof of the individual
finiteness theorem for limit cycles of polynomial ODEs with hyperbolic singularities only) natural
stronger assumptions (e.g., `im z` is bounded from below and from above) are not available.

-/

open Asymptotics Filter Function

open Topology

namespace Complex

/-- We say that `l : filter ℂ` is an *exponential comparison filter* if the real part tends to
infinity along `l` and the imaginary part grows subexponentially compared to the real part. These
properties guarantee that `(λ z, z ^ a₁ * exp (b₁ * z)) =o[l] (λ z, z ^ a₂ * exp (b₂ * z))` for any
complex `a₁`, `a₂` and real `b₁ < b₂`.

In particular, the second property is automatically satisfied if the imaginary part is bounded along
`l`. -/
structure IsExpCmpFilter (l : Filter ℂ) : Prop where
  tendsto_re : Tendsto re l atTop
  isO_im_pow_re : ∀ n : ℕ, (fun z : ℂ => z.im ^ n) =O[l] fun z => Real.exp z.re
#align complex.is_exp_cmp_filter Complex.IsExpCmpFilter

namespace IsExpCmpFilter

variable {l : Filter ℂ}

/-!
### Alternative constructors
-/

theorem of_isO_im_re_rpow (hre : Tendsto re l atTop) (r : ℝ) (hr : im =O[l] fun z => z.re ^ r) :
    IsExpCmpFilter l :=
  ⟨hre, fun n =>
    IsOCat.isO <|
      calc
        (fun z : ℂ => z.im ^ n) =O[l] fun z => (z.re ^ r) ^ n := hr.pow n
        _ =ᶠ[l] fun z => z.re ^ (r * n) :=
          ((hre.eventually_ge_atTop 0).mono fun z hz => by
            simp only [Real.rpow_mul hz r n, Real.rpow_nat_cast])
        _ =o[l] fun z => Real.exp z.re := (isOCat_rpow_exp_atTop _).comp_tendsto hre
        ⟩
#align complex.is_exp_cmp_filter.of_is_O_im_re_rpow Complex.IsExpCmpFilter.of_isO_im_re_rpow

theorem of_isO_im_re_pow (hre : Tendsto re l atTop) (n : ℕ) (hr : im =O[l] fun z => z.re ^ n) :
    IsExpCmpFilter l :=
  of_isO_im_re_rpow hre n <| by simpa only [Real.rpow_nat_cast]
#align complex.is_exp_cmp_filter.of_is_O_im_re_pow Complex.IsExpCmpFilter.of_isO_im_re_pow

theorem of_bounded_under_abs_im (hre : Tendsto re l atTop)
    (him : IsBoundedUnder (· ≤ ·) l fun z => |z.im|) : IsExpCmpFilter l :=
  of_isO_im_re_pow hre 0 <| by
    simpa only [pow_zero] using @is_bounded_under.is_O_const ℂ ℝ ℝ _ _ _ l him 1 one_ne_zero
#align complex.is_exp_cmp_filter.of_bounded_under_abs_im Complex.IsExpCmpFilter.of_bounded_under_abs_im

theorem of_bounded_under_im (hre : Tendsto re l atTop) (him_le : IsBoundedUnder (· ≤ ·) l im)
    (him_ge : IsBoundedUnder (· ≥ ·) l im) : IsExpCmpFilter l :=
  of_bounded_under_abs_im hre <| isBoundedUnder_le_abs.2 ⟨him_le, him_ge⟩
#align complex.is_exp_cmp_filter.of_bounded_under_im Complex.IsExpCmpFilter.of_bounded_under_im

/-!
### Preliminary lemmas
-/

theorem eventually_ne (hl : IsExpCmpFilter l) : ∀ᶠ w : ℂ in l, w ≠ 0 :=
  hl.tendsto_re.eventually_ne_atTop' _
#align complex.is_exp_cmp_filter.eventually_ne Complex.IsExpCmpFilter.eventually_ne

theorem tendsto_abs_re (hl : IsExpCmpFilter l) : Tendsto (fun z : ℂ => |z.re|) l atTop :=
  tendsto_abs_atTop_atTop.comp hl.tendsto_re
#align complex.is_exp_cmp_filter.tendsto_abs_re Complex.IsExpCmpFilter.tendsto_abs_re

theorem tendsto_abs (hl : IsExpCmpFilter l) : Tendsto abs l atTop :=
  tendsto_atTop_mono abs_re_le_abs hl.tendsto_abs_re
#align complex.is_exp_cmp_filter.tendsto_abs Complex.IsExpCmpFilter.tendsto_abs

theorem isOCat_log_re_re (hl : IsExpCmpFilter l) : (fun z => Real.log z.re) =o[l] re :=
  Real.isOCat_log_id_atTop.comp_tendsto hl.tendsto_re
#align complex.is_exp_cmp_filter.is_o_log_re_re Complex.IsExpCmpFilter.isOCat_log_re_re

theorem isOCat_im_pow_exp_re (hl : IsExpCmpFilter l) (n : ℕ) :
    (fun z : ℂ => z.im ^ n) =o[l] fun z => Real.exp z.re :=
  flip IsOCat.of_pow two_ne_zero <|
    calc
      (fun z : ℂ => (z.im ^ n) ^ 2) = fun z => z.im ^ (2 * n) := by simp only [pow_mul']
      _ =O[l] fun z => Real.exp z.re := (hl.isO_im_pow_re _)
      _ = fun z => Real.exp z.re ^ 1 := by simp only [pow_one]
      _ =o[l] fun z => Real.exp z.re ^ 2 :=
        (isOCat_pow_pow_atTop_of_lt one_lt_two).comp_tendsto <|
          Real.tendsto_exp_atTop.comp hl.tendsto_re

#align complex.is_exp_cmp_filter.is_o_im_pow_exp_re Complex.IsExpCmpFilter.isOCat_im_pow_exp_re

theorem abs_im_pow_eventuallyLE_exp_re (hl : IsExpCmpFilter l) (n : ℕ) :
    (fun z : ℂ => |z.im| ^ n) ≤ᶠ[l] fun z => Real.exp z.re := by
  simpa using (hl.is_o_im_pow_exp_re n).bound zero_lt_one
#align complex.is_exp_cmp_filter.abs_im_pow_eventually_le_exp_re Complex.IsExpCmpFilter.abs_im_pow_eventuallyLE_exp_re

/-- If `l : filter ℂ` is an "exponential comparison filter", then $\log |z| =o(ℜ z)$ along `l`.
This is the main lemma in the proof of `complex.is_exp_cmp_filter.is_o_cpow_exp` below.
-/
theorem isOCat_log_abs_re (hl : IsExpCmpFilter l) : (fun z => Real.log (abs z)) =o[l] re :=
  calc
    (fun z => Real.log (abs z)) =O[l] fun z =>
        Real.log (Real.sqrt 2) + Real.log (max z.re (|z.im|)) :=
      IsO.of_bound 1 <|
        (hl.tendsto_re.eventually_ge_atTop 1).mono fun z hz =>
          by
          have h2 : 0 < Real.sqrt 2 := by simp
          have hz' : 1 ≤ abs z := hz.trans (re_le_abs z)
          have hz₀ : 0 < abs z := one_pos.trans_le hz'
          have hm₀ : 0 < max z.re (|z.im|) := lt_max_iff.2 (Or.inl <| one_pos.trans_le hz)
          rw [one_mul, Real.norm_eq_abs, _root_.abs_of_nonneg (Real.log_nonneg hz')]
          refine' le_trans _ (le_abs_self _)
          rw [← Real.log_mul, Real.log_le_log, ← _root_.abs_of_nonneg (le_trans zero_le_one hz)]
          exacts[abs_le_sqrt_two_mul_max z, one_pos.trans_le hz', mul_pos h2 hm₀, h2.ne', hm₀.ne']
    _ =o[l] re :=
      IsOCat.add (isOCat_const_left.2 <| Or.inr <| hl.tendsto_abs_re) <|
        isOCat_iff_nat_mul_le.2 fun n =>
          by
          filter_upwards [is_o_iff_nat_mul_le.1 hl.is_o_log_re_re n,
            hl.abs_im_pow_eventually_le_exp_re n,
            hl.tendsto_re.eventually_gt_at_top 1]with z hre him h₁
          cases' le_total (|z.im|) z.re with hle hle
          · rwa [max_eq_left hle]
          · have H : 1 < |z.im| := h₁.trans_le hle
            rwa [max_eq_right hle, Real.norm_eq_abs, Real.norm_eq_abs, abs_of_pos (Real.log_pos H),
              ← Real.log_pow, Real.log_le_iff_le_exp (pow_pos (one_pos.trans H) _),
              abs_of_pos (one_pos.trans h₁)]

#align complex.is_exp_cmp_filter.is_o_log_abs_re Complex.IsExpCmpFilter.isOCat_log_abs_re

/-!
### Main results
-/

/-- If `l : filter ℂ` is an "exponential comparison filter", then for any complex `a` and any
positive real `b`, we have `(λ z, z ^ a) =o[l] (λ z, exp (b * z))`. -/
theorem isOCat_cpow_exp (hl : IsExpCmpFilter l) (a : ℂ) {b : ℝ} (hb : 0 < b) :
    (fun z => z ^ a) =o[l] fun z => exp (b * z) :=
  calc
    (fun z => z ^ a) =Θ[l] fun z => abs z ^ re a :=
      isTheta_cpow_const_rpow fun _ _ => hl.eventually_ne
    _ =ᶠ[l] fun z => Real.exp (re a * Real.log (abs z)) :=
      (hl.eventually_ne.mono fun z hz => by simp only [Real.rpow_def_of_pos, abs.pos hz, mul_comm])
    _ =o[l] fun z => exp (b * z) :=
      IsOCat.of_norm_right <|
        by
        simp only [norm_eq_abs, abs_exp, of_real_mul_re, Real.isOCat_exp_comp_exp_comp]
        refine'
          (is_equivalent.refl.sub_is_o _).symm.tendsto_atTop (hl.tendsto_re.const_mul_at_top hb)
        exact (hl.is_o_log_abs_re.const_mul_left _).const_mul_right hb.ne'

#align complex.is_exp_cmp_filter.is_o_cpow_exp Complex.IsExpCmpFilter.isOCat_cpow_exp

/-- If `l : filter ℂ` is an "exponential comparison filter", then for any complex `a₁`, `a₂` and any
real `b₁ < b₂`, we have `(λ z, z ^ a₁ * exp (b₁ * z)) =o[l] (λ z, z ^ a₂ * exp (b₂ * z))`. -/
theorem isOCat_cpow_mul_exp {b₁ b₂ : ℝ} (hl : IsExpCmpFilter l) (hb : b₁ < b₂) (a₁ a₂ : ℂ) :
    (fun z => z ^ a₁ * exp (b₁ * z)) =o[l] fun z => z ^ a₂ * exp (b₂ * z) :=
  calc
    (fun z => z ^ a₁ * exp (b₁ * z)) =ᶠ[l] fun z => z ^ a₂ * exp (b₁ * z) * z ^ (a₁ - a₂) :=
      hl.eventually_ne.mono fun z hz => by
        simp only
        rw [mul_right_comm, ← cpow_add _ _ hz, add_sub_cancel'_right]
    _ =o[l] fun z => z ^ a₂ * exp (b₁ * z) * exp (↑(b₂ - b₁) * z) :=
      ((isO_refl (fun z => z ^ a₂ * exp (b₁ * z)) l).mul_isOCat <|
        hl.isOCat_cpow_exp _ (sub_pos.2 hb))
    _ =ᶠ[l] fun z => z ^ a₂ * exp (b₂ * z) := by
      simp only [of_real_sub, sub_mul, mul_assoc, ← exp_add, add_sub_cancel'_right]

#align complex.is_exp_cmp_filter.is_o_cpow_mul_exp Complex.IsExpCmpFilter.isOCat_cpow_mul_exp

/-- If `l : filter ℂ` is an "exponential comparison filter", then for any complex `a` and any
negative real `b`, we have `(λ z, exp (b * z)) =o[l] (λ z, z ^ a)`. -/
theorem isOCat_exp_cpow (hl : IsExpCmpFilter l) (a : ℂ) {b : ℝ} (hb : b < 0) :
    (fun z => exp (b * z)) =o[l] fun z => z ^ a := by simpa using hl.is_o_cpow_mul_exp hb 0 a
#align complex.is_exp_cmp_filter.is_o_exp_cpow Complex.IsExpCmpFilter.isOCat_exp_cpow

/-- If `l : filter ℂ` is an "exponential comparison filter", then for any complex `a₁`, `a₂` and any
natural `b₁ < b₂`, we have `(λ z, z ^ a₁ * exp (b₁ * z)) =o[l] (λ z, z ^ a₂ * exp (b₂ * z))`. -/
theorem isOCat_pow_mul_exp {b₁ b₂ : ℝ} (hl : IsExpCmpFilter l) (hb : b₁ < b₂) (m n : ℕ) :
    (fun z => z ^ m * exp (b₁ * z)) =o[l] fun z => z ^ n * exp (b₂ * z) := by
  simpa only [cpow_nat_cast] using hl.is_o_cpow_mul_exp hb m n
#align complex.is_exp_cmp_filter.is_o_pow_mul_exp Complex.IsExpCmpFilter.isOCat_pow_mul_exp

/-- If `l : filter ℂ` is an "exponential comparison filter", then for any complex `a₁`, `a₂` and any
integer `b₁ < b₂`, we have `(λ z, z ^ a₁ * exp (b₁ * z)) =o[l] (λ z, z ^ a₂ * exp (b₂ * z))`. -/
theorem isOCat_zpow_mul_exp {b₁ b₂ : ℝ} (hl : IsExpCmpFilter l) (hb : b₁ < b₂) (m n : ℤ) :
    (fun z => z ^ m * exp (b₁ * z)) =o[l] fun z => z ^ n * exp (b₂ * z) := by
  simpa only [cpow_int_cast] using hl.is_o_cpow_mul_exp hb m n
#align complex.is_exp_cmp_filter.is_o_zpow_mul_exp Complex.IsExpCmpFilter.isOCat_zpow_mul_exp

end IsExpCmpFilter

end Complex


/-
Copyright (c) 2020 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel
-/
import analysis.mean_inequalities

/-!
# `L^p` distance on finite products of metric spaces
Given finitely many metric spaces, one can put the max distance on their product, but there is also
a whole family of natural distances, indexed by a real parameter `p ∈ [1, ∞)`, that also induce
the product topology. We define them in this file. The distance on `Π i, α i` is given by
$$
d(x, y) = \left(\sum d(x_i, y_i)^p\right)^{1/p}.
$$

We give instances of this construction for emetric spaces, metric spaces, normed groups and normed
spaces.

To avoid conflicting instances, all these are defined on a copy of the original Pi type, named
`pi_Lp p α`. The assumpion `[fact (1 ≤ p)]` is required for the metric and normed space instances.

We ensure that the topology and uniform structure on `pi_Lp p α` are (defeq to) the product
topology and product uniformity, to be able to use freely continuity statements for the coordinate
functions, for instance.

## Implementation notes

We only deal with the `L^p` distance on a product of finitely many metric spaces, which may be
distinct. A closely related construction is `lp`, the `L^p` norm on a product of (possibly
infinitely many) normed spaces, where the norm is
$$
\left(\sum ∥f (x)∥^p \right)^{1/p}.
$$
However, the topology induced by this construction is not the product topology, and some functions
have infinite `L^p` norm. These subtleties are not present in the case of finitely many metric
spaces, hence it is worth devoting a file to this specific case which is particularly well behaved.

Another related construction is `measure_theory.Lp`, the `L^p` norm on the space of functions from
a measure space to a normed space, where the norm is
$$
\left(\int ∥f (x)∥^p dμ\right)^{1/p}.
$$
This has all the same subtleties as `lp`, and the further subtlety that this only
defines a seminorm (as almost everywhere zero functions have zero `L^p` norm).
The construction `pi_Lp` corresponds to the special case of `measure_theory.Lp` in which the basis
is a finite space equipped with the counting measure.

To prove that the topology (and the uniform structure) on a finite product with the `L^p` distance
are the same as those coming from the `L^∞` distance, we could argue that the `L^p` and `L^∞` norms
are equivalent on `ℝ^n` for abstract (norm equivalence) reasons. Instead, we give a more explicit
(easy) proof which provides a comparison between these two norms with explicit constants.

We also set up the theory for `pseudo_emetric_space` and `pseudo_metric_space`.
-/

open real set filter is_R_or_C
open_locale big_operators uniformity topological_space nnreal ennreal

noncomputable theory

variables {ι : Type*}

/-- A copy of a Pi type, on which we will put the `L^p` distance. Since the Pi type itself is
already endowed with the `L^∞` distance, we need the type synonym to avoid confusing typeclass
resolution. Also, we let it depend on `p`, to get a whole family of type on which we can put
different distances. -/
@[nolint unused_arguments]
def pi_Lp {ι : Type*} (p : ℝ) (α : ι → Type*) : Type* := Π (i : ι), α i

instance {ι : Type*} (p : ℝ) (α : ι → Type*) [∀ i, inhabited (α i)] : inhabited (pi_Lp p α) :=
⟨λ i, default⟩

instance fact_one_le_one_real : fact ((1:ℝ) ≤ 1) := ⟨rfl.le⟩
instance fact_one_le_two_real : fact ((1:ℝ) ≤ 2) := ⟨one_le_two⟩

namespace pi_Lp

variables (p : ℝ) [fact_one_le_p : fact (1 ≤ p)] (α : ι → Type*) (β : ι → Type*)

/-- Canonical bijection between `pi_Lp p α` and the original Pi type. We introduce it to be able
to compare the `L^p` and `L^∞` distances through it. -/
protected def equiv : pi_Lp p α ≃ Π (i : ι), α i :=
equiv.refl _

section
/-!
### The uniformity on finite `L^p` products is the product uniformity

In this section, we put the `L^p` edistance on `pi_Lp p α`, and we check that the uniformity
coming from this edistance coincides with the product uniformity, by showing that the canonical
map to the Pi type (with the `L^∞` distance) is a uniform embedding, as it is both Lipschitz and
antiLipschitz.

We only register this emetric space structure as a temporary instance, as the true instance (to be
registered later) will have as uniformity exactly the product uniformity, instead of the one coming
from the edistance (which is equal to it, but not defeq). See Note [forgetful inheritance]
explaining why having definitionally the right uniformity is often important.
-/

variables [∀ i, emetric_space (α i)] [∀ i, pseudo_emetric_space (β i)] [fintype ι]
include fact_one_le_p

/-- Endowing the space `pi_Lp p β` with the `L^p` pseudoedistance. This definition is not
satisfactory, as it does not register the fact that the topology and the uniform structure coincide
with the product one. Therefore, we do not register it as an instance. Using this as a temporary
pseudoemetric space instance, we will show that the uniform structure is equal (but not defeq) to
the product one, and then register an instance in which we replace the uniform structure by the
product one using this pseudoemetric space and `pseudo_emetric_space.replace_uniformity`. -/
def pseudo_emetric_aux : pseudo_emetric_space (pi_Lp p β) :=
have pos : 0 < p := lt_of_lt_of_le zero_lt_one fact_one_le_p.out,
{ edist          := λ f g, (∑ (i : ι), (edist (f i) (g i)) ^ p) ^ (1/p),
  edist_self     := λ f, by simp [edist, ennreal.zero_rpow_of_pos pos,
                                  ennreal.zero_rpow_of_pos (inv_pos.2 pos)],
  edist_comm     := λ f g, by simp [edist, edist_comm],
  edist_triangle := λ f g h, calc
    (∑ (i : ι), edist (f i) (h i) ^ p) ^ (1 / p) ≤
    (∑ (i : ι), (edist (f i) (g i) + edist (g i) (h i)) ^ p) ^ (1 / p) :
    begin
      apply ennreal.rpow_le_rpow _ (one_div_nonneg.2 $ le_of_lt pos),
      refine finset.sum_le_sum (λ i hi, _),
      exact ennreal.rpow_le_rpow (edist_triangle _ _ _) (le_trans zero_le_one fact_one_le_p.out)
    end
    ... ≤
    (∑ (i : ι), edist (f i) (g i) ^ p) ^ (1 / p) + (∑ (i : ι), edist (g i) (h i) ^ p) ^ (1 / p) :
      ennreal.Lp_add_le _ _ _ fact_one_le_p.out }

/-- Endowing the space `pi_Lp p α` with the `L^p` edistance. This definition is not satisfactory,
as it does not register the fact that the topology and the uniform structure coincide with the
product one. Therefore, we do not register it as an instance. Using this as a temporary emetric
space instance, we will show that the uniform structure is equal (but not defeq) to the product
one, and then register an instance in which we replace the uniform structure by the product one
using this emetric space and `emetric_space.replace_uniformity`. -/
def emetric_aux : emetric_space (pi_Lp p α) :=
{ eq_of_edist_eq_zero := λ f g hfg,
  begin
    have pos : 0 < p := lt_of_lt_of_le zero_lt_one fact_one_le_p.out,
    letI h := pseudo_emetric_aux p α,
    have h : edist f g = (∑ (i : ι), (edist (f i) (g i)) ^ p) ^ (1/p) := rfl,
    simp [h, ennreal.rpow_eq_zero_iff, pos, asymm pos, finset.sum_eq_zero_iff_of_nonneg] at hfg,
    exact funext hfg
  end,
  ..pseudo_emetric_aux p α }

local attribute [instance] pi_Lp.emetric_aux pi_Lp.pseudo_emetric_aux

lemma lipschitz_with_equiv : lipschitz_with 1 (pi_Lp.equiv p β) :=
begin
  have pos : 0 < p := lt_of_lt_of_le zero_lt_one fact_one_le_p.out,
  have cancel : p * (1/p) = 1 := mul_div_cancel' 1 (ne_of_gt pos),
  assume x y,
  simp only [edist, forall_prop_of_true, one_mul, finset.mem_univ, finset.sup_le_iff,
             ennreal.coe_one],
  assume i,
  calc
  edist (x i) (y i) = (edist (x i) (y i) ^ p) ^ (1/p) :
    by simp [← ennreal.rpow_mul, cancel, -one_div]
  ... ≤ (∑ (i : ι), edist (x i) (y i) ^ p) ^ (1 / p) :
  begin
    apply ennreal.rpow_le_rpow _ (one_div_nonneg.2 $ le_of_lt pos),
    exact finset.single_le_sum (λ i hi, (bot_le : (0 : ℝ≥0∞) ≤ _)) (finset.mem_univ i)
  end
end

lemma antilipschitz_with_equiv :
  antilipschitz_with ((fintype.card ι : ℝ≥0) ^ (1/p)) (pi_Lp.equiv p β) :=
begin
  have pos : 0 < p := lt_of_lt_of_le zero_lt_one fact_one_le_p.out,
  have nonneg : 0 ≤ 1 / p := one_div_nonneg.2 (le_of_lt pos),
  have cancel : p * (1/p) = 1 := mul_div_cancel' 1 (ne_of_gt pos),
  assume x y,
  simp [edist, -one_div],
  calc (∑ (i : ι), edist (x i) (y i) ^ p) ^ (1 / p) ≤
  (∑ (i : ι), edist (pi_Lp.equiv p β x) (pi_Lp.equiv p β y) ^ p) ^ (1 / p) :
  begin
    apply ennreal.rpow_le_rpow _ nonneg,
    apply finset.sum_le_sum (λ i hi, _),
    apply ennreal.rpow_le_rpow _ (le_of_lt pos),
    exact finset.le_sup (finset.mem_univ i)
  end
  ... = (((fintype.card ι : ℝ≥0)) ^ (1/p) : ℝ≥0) *
    edist (pi_Lp.equiv p β x) (pi_Lp.equiv p β y) :
  begin
    simp only [nsmul_eq_mul, finset.card_univ, ennreal.rpow_one, finset.sum_const,
      ennreal.mul_rpow_of_nonneg _ _ nonneg, ←ennreal.rpow_mul, cancel],
    have : (fintype.card ι : ℝ≥0∞) = (fintype.card ι : ℝ≥0) :=
      (ennreal.coe_nat (fintype.card ι)).symm,
    rw [this, ennreal.coe_rpow_of_nonneg _ nonneg]
  end
end

lemma aux_uniformity_eq :
  𝓤 (pi_Lp p β) = @uniformity _ (Pi.uniform_space _) :=
begin
  have A : uniform_inducing (pi_Lp.equiv p β) :=
    (antilipschitz_with_equiv p β).uniform_inducing
    (lipschitz_with_equiv p β).uniform_continuous,
  have : (λ (x : pi_Lp p β × pi_Lp p β),
    ((pi_Lp.equiv p β) x.fst, (pi_Lp.equiv p β) x.snd)) = id,
    by ext i; refl,
  rw [← A.comap_uniformity, this, comap_id]
end

end

/-! ### Instances on finite `L^p` products -/

instance uniform_space [∀ i, uniform_space (β i)] : uniform_space (pi_Lp p β) :=
Pi.uniform_space _

variable [fintype ι]
include fact_one_le_p

/-- pseudoemetric space instance on the product of finitely many pseudoemetric spaces, using the
`L^p` pseudoedistance, and having as uniformity the product uniformity. -/
instance [∀ i, pseudo_emetric_space (β i)] : pseudo_emetric_space (pi_Lp p β) :=
(pseudo_emetric_aux p β).replace_uniformity (aux_uniformity_eq p β).symm

/-- emetric space instance on the product of finitely many emetric spaces, using the `L^p`
edistance, and having as uniformity the product uniformity. -/
instance [∀ i, emetric_space (α i)] : emetric_space (pi_Lp p α) :=
(emetric_aux p α).replace_uniformity (aux_uniformity_eq p α).symm

omit fact_one_le_p
protected lemma edist {p : ℝ} [fact (1 ≤ p)] {β : ι → Type*}
  [∀ i, pseudo_emetric_space (β i)] (x y : pi_Lp p β) :
  edist x y = (∑ (i : ι), (edist (x i) (y i)) ^ p) ^ (1/p) := rfl
include fact_one_le_p

/-- pseudometric space instance on the product of finitely many psuedometric spaces, using the
`L^p` distance, and having as uniformity the product uniformity. -/
instance [∀ i, pseudo_metric_space (β i)] : pseudo_metric_space (pi_Lp p β) :=
begin
  /- we construct the instance from the pseudo emetric space instance to avoid checking again that
  the uniformity is the same as the product uniformity, but we register nevertheless a nice formula
  for the distance -/
  have pos : 0 < p := lt_of_lt_of_le zero_lt_one fact_one_le_p.out,
  refine pseudo_emetric_space.to_pseudo_metric_space_of_dist
    (λf g, (∑ (i : ι), (dist (f i) (g i)) ^ p) ^ (1/p)) (λ f g, _) (λ f g, _),
  { simp [pi_Lp.edist, ennreal.rpow_eq_top_iff, asymm pos, pos,
          ennreal.sum_eq_top_iff, edist_ne_top] },
  { have A : ∀ (i : ι), i ∈ (finset.univ : finset ι) → edist (f i) (g i) ^ p ≠ ⊤ :=
      λ i hi, by simp [lt_top_iff_ne_top, edist_ne_top, le_of_lt pos],
    simp [dist, -one_div, pi_Lp.edist, ← ennreal.to_real_rpow,
          ennreal.to_real_sum A, dist_edist] }
end

/-- metric space instance on the product of finitely many metric spaces, using the `L^p` distance,
and having as uniformity the product uniformity. -/
instance [∀ i, metric_space (α i)] : metric_space (pi_Lp p α) :=
begin
  /- we construct the instance from the emetric space instance to avoid checking again that the
  uniformity is the same as the product uniformity, but we register nevertheless a nice formula
  for the distance -/
  have pos : 0 < p := lt_of_lt_of_le zero_lt_one fact_one_le_p.out,
  refine emetric_space.to_metric_space_of_dist
    (λf g, (∑ (i : ι), (dist (f i) (g i)) ^ p) ^ (1/p)) (λ f g, _) (λ f g, _),
  { simp [pi_Lp.edist, ennreal.rpow_eq_top_iff, asymm pos, pos,
          ennreal.sum_eq_top_iff, edist_ne_top] },
  { have A : ∀ (i : ι), i ∈ (finset.univ : finset ι) → edist (f i) (g i) ^ p ≠ ⊤ :=
      λ i hi, by simp [edist_ne_top, pos.le],
    simp [dist, -one_div, pi_Lp.edist, ← ennreal.to_real_rpow,
          ennreal.to_real_sum A, dist_edist] }
end

omit fact_one_le_p
protected lemma dist {p : ℝ} [fact (1 ≤ p)] {β : ι → Type*}
  [∀ i, pseudo_metric_space (β i)] (x y : pi_Lp p β) :
  dist x y = (∑ (i : ι), (dist (x i) (y i)) ^ p) ^ (1/p) := rfl
include fact_one_le_p

/-- seminormed group instance on the product of finitely many normed groups, using the `L^p`
norm. -/
instance semi_normed_group [∀i, semi_normed_group (β i)] : semi_normed_group (pi_Lp p β) :=
{ norm := λf, (∑ (i : ι), norm (f i) ^ p) ^ (1/p),
  dist_eq := λ x y, by { simp [pi_Lp.dist, dist_eq_norm, sub_eq_add_neg] },
  .. pi.add_comm_group }

/-- normed group instance on the product of finitely many normed groups, using the `L^p` norm. -/
instance normed_group [∀i, normed_group (α i)] : normed_group (pi_Lp p α) :=
{ ..pi_Lp.semi_normed_group p α }

omit fact_one_le_p
lemma norm_eq {p : ℝ} [fact (1 ≤ p)] {β : ι → Type*}
  [∀i, semi_normed_group (β i)] (f : pi_Lp p β) :
  ∥f∥ = (∑ (i : ι), ∥f i∥ ^ p) ^ (1/p) := rfl

lemma norm_eq_of_nat {p : ℝ} [fact (1 ≤ p)] {β : ι → Type*}
  [∀i, semi_normed_group (β i)] (n : ℕ) (h : p = n) (f : pi_Lp p β) :
  ∥f∥ = (∑ (i : ι), ∥f i∥ ^ n) ^ (1/(n : ℝ)) :=
by simp [norm_eq, h, real.sqrt_eq_rpow, ←real.rpow_nat_cast]
include fact_one_le_p

variables (𝕜 : Type*) [normed_field 𝕜]

/-- The product of finitely many normed spaces is a normed space, with the `L^p` norm. -/
instance normed_space [∀i, semi_normed_group (β i)] [∀i, normed_space 𝕜 (β i)] :
  normed_space 𝕜 (pi_Lp p β) :=
{ norm_smul_le :=
  begin
    assume c f,
    have : p * (1 / p) = 1 := mul_div_cancel' 1 (lt_of_lt_of_le zero_lt_one fact_one_le_p.out).ne',
    simp only [pi_Lp.norm_eq, norm_smul, mul_rpow, norm_nonneg, ←finset.mul_sum, pi.smul_apply],
    rw [mul_rpow (rpow_nonneg_of_nonneg (norm_nonneg _) _), ← rpow_mul (norm_nonneg _),
        this, rpow_one],
    exact finset.sum_nonneg (λ i hi, rpow_nonneg_of_nonneg (norm_nonneg _) _)
  end,
  .. pi.module ι β 𝕜 }

/- Register simplification lemmas for the applications of `pi_Lp` elements, as the usual lemmas
for Pi types will not trigger. -/
variables {𝕜 p α}
[∀i, semi_normed_group (β i)] [∀i, normed_space 𝕜 (β i)] (c : 𝕜) (x y : pi_Lp p β) (i : ι)

@[simp] lemma add_apply : (x + y) i = x i + y i := rfl
@[simp] lemma sub_apply : (x - y) i = x i - y i := rfl
@[simp] lemma smul_apply : (c • x) i = c • x i := rfl
@[simp] lemma neg_apply : (-x) i = - (x i) := rfl

end pi_Lp


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

/-!
# Comparison

This file provides basic results about orderings and comparison in linear orders.

## Definitions

* `cmp_le`: An `ordering` from `≤`.
* `ordering.compares`: Turns an `ordering` into `<` and `=` propositions.
* `linear_order_of_compares`: Constructs a `linear_order` instance from the fact that any two
  elements that are not one strictly less than the other either way are equal.
-/

variables {α : Type*}

/-- Like `cmp`, but uses a `≤` on the type instead of `<`. Given two elements `x` and `y`, returns a
three-way comparison result `ordering`. -/
def cmp_le {α} [has_le α] [@decidable_rel α (≤)] (x y : α) : ordering :=
if x ≤ y then
  if y ≤ x then ordering.eq else ordering.lt
else ordering.gt

lemma cmp_le_swap {α} [has_le α] [is_total α (≤)] [@decidable_rel α (≤)] (x y : α) :
  (cmp_le x y).swap = cmp_le y x :=
begin
  by_cases xy : x ≤ y; by_cases yx : y ≤ x; simp [cmp_le, *, ordering.swap],
  cases not_or xy yx (total_of _ _ _)
end

lemma cmp_le_eq_cmp {α} [preorder α] [is_total α (≤)]
  [@decidable_rel α (≤)] [@decidable_rel α (<)] (x y : α) : cmp_le x y = cmp x y :=
begin
  by_cases xy : x ≤ y; by_cases yx : y ≤ x;
    simp [cmp_le, lt_iff_le_not_le, *, cmp, cmp_using],
  cases not_or xy yx (total_of _ _ _)
end

namespace ordering

/-- `compares o a b` means that `a` and `b` have the ordering relation `o` between them, assuming
that the relation `a < b` is defined. -/
@[simp] def compares [has_lt α] : ordering → α → α → Prop
| lt a b := a < b
| eq a b := a = b
| gt a b := a > b

lemma compares_swap [has_lt α] {a b : α} {o : ordering} :
  o.swap.compares a b ↔ o.compares b a :=
by { cases o, exacts [iff.rfl, eq_comm, iff.rfl] }

alias compares_swap ↔ compares.of_swap compares.swap

lemma swap_eq_iff_eq_swap {o o' : ordering} : o.swap = o' ↔ o = o'.swap :=
⟨λ h, by rw [← swap_swap o, h], λ h, by rw [← swap_swap o', h]⟩

lemma compares.eq_lt [preorder α] :
  ∀ {o} {a b : α}, compares o a b → (o = lt ↔ a < b)
| lt a b h := ⟨λ _, h, λ _, rfl⟩
| eq a b h := ⟨λ h, by injection h, λ h', (ne_of_lt h' h).elim⟩
| gt a b h := ⟨λ h, by injection h, λ h', (lt_asymm h h').elim⟩

lemma compares.ne_lt [preorder α] :
  ∀ {o} {a b : α}, compares o a b → (o ≠ lt ↔ b ≤ a)
| lt a b h := ⟨absurd rfl, λ h', (not_le_of_lt h h').elim⟩
| eq a b h := ⟨λ _, ge_of_eq h, λ _ h, by injection h⟩
| gt a b h := ⟨λ _, le_of_lt h, λ _ h, by injection h⟩

lemma compares.eq_eq [preorder α] :
  ∀ {o} {a b : α}, compares o a b → (o = eq ↔ a = b)
| lt a b h := ⟨λ h, by injection h, λ h', (ne_of_lt h h').elim⟩
| eq a b h := ⟨λ _, h, λ _, rfl⟩
| gt a b h := ⟨λ h, by injection h, λ h', (ne_of_gt h h').elim⟩

lemma compares.eq_gt [preorder α] {o} {a b : α} (h : compares o a b) : (o = gt ↔ b < a) :=
swap_eq_iff_eq_swap.symm.trans h.swap.eq_lt

lemma compares.ne_gt [preorder α] {o} {a b : α} (h : compares o a b) : (o ≠ gt ↔ a ≤ b) :=
(not_congr swap_eq_iff_eq_swap.symm).trans h.swap.ne_lt

lemma compares.le_total [preorder α] {a b : α} :
  ∀ {o}, compares o a b → a ≤ b ∨ b ≤ a
| lt h := or.inl (le_of_lt h)
| eq h := or.inl (le_of_eq h)
| gt h := or.inr (le_of_lt h)

lemma compares.le_antisymm [preorder α] {a b : α} :
  ∀ {o}, compares o a b → a ≤ b → b ≤ a → a = b
| lt h _ hba := (not_le_of_lt h hba).elim
| eq h _ _   := h
| gt h hab _ := (not_le_of_lt h hab).elim

lemma compares.inj [preorder α] {o₁} :
  ∀ {o₂} {a b : α}, compares o₁ a b → compares o₂ a b → o₁ = o₂
| lt a b h₁ h₂ := h₁.eq_lt.2 h₂
| eq a b h₁ h₂ := h₁.eq_eq.2 h₂
| gt a b h₁ h₂ := h₁.eq_gt.2 h₂

lemma compares_iff_of_compares_impl {β : Type*} [linear_order α] [preorder β] {a b : α}
  {a' b' : β} (h : ∀ {o}, compares o a b → compares o a' b') (o) :
  compares o a b ↔ compares o a' b' :=
begin
  refine ⟨h, λ ho, _⟩,
  cases lt_trichotomy a b with hab hab,
  { change compares ordering.lt a b at hab,
    rwa [ho.inj (h hab)] },
  { cases hab with hab hab,
    { change compares ordering.eq a b at hab,
      rwa [ho.inj (h hab)] },
    { change compares ordering.gt a b at hab,
      rwa [ho.inj (h hab)] } }
end

lemma swap_or_else (o₁ o₂) : (or_else o₁ o₂).swap = or_else o₁.swap o₂.swap :=
by cases o₁; try {refl}; cases o₂; refl

lemma or_else_eq_lt (o₁ o₂) : or_else o₁ o₂ = lt ↔ o₁ = lt ∨ (o₁ = eq ∧ o₂ = lt) :=
by cases o₁; cases o₂; exact dec_trivial

end ordering

open ordering order_dual

@[simp] lemma to_dual_compares_to_dual [has_lt α] {a b : α} {o : ordering} :
  compares o (to_dual a) (to_dual b) ↔ compares o b a :=
by { cases o, exacts [iff.rfl, eq_comm, iff.rfl] }

@[simp] lemma of_dual_compares_of_dual [has_lt α] {a b : αᵒᵈ} {o : ordering} :
  compares o (of_dual a) (of_dual b) ↔ compares o b a :=
by { cases o, exacts [iff.rfl, eq_comm, iff.rfl] }

lemma cmp_compares [linear_order α] (a b : α) : (cmp a b).compares a b :=
by obtain h | h | h := lt_trichotomy a b; simp [cmp, cmp_using, h, h.not_lt]

lemma ordering.compares.cmp_eq [linear_order α] {a b : α} {o : ordering} (h : o.compares a b) :
  cmp a b = o :=
(cmp_compares a b).inj h

lemma cmp_swap [preorder α] [@decidable_rel α (<)] (a b : α) : (cmp a b).swap = cmp b a :=
begin
  unfold cmp cmp_using,
  by_cases a < b; by_cases h₂ : b < a; simp [h, h₂, ordering.swap],
  exact lt_asymm h h₂
end

@[simp] lemma cmp_le_to_dual [has_le α] [@decidable_rel α (≤)] (x y : α) :
  cmp_le (to_dual x) (to_dual y) = cmp_le y x := rfl

@[simp] lemma cmp_le_of_dual [has_le α] [@decidable_rel α (≤)] (x y : αᵒᵈ) :
  cmp_le (of_dual x) (of_dual y) = cmp_le y x := rfl

@[simp] lemma cmp_to_dual [has_lt α] [@decidable_rel α (<)] (x y : α) :
  cmp (to_dual x) (to_dual y) = cmp y x := rfl

@[simp] lemma cmp_of_dual [has_lt α] [@decidable_rel α (<)] (x y : αᵒᵈ) :
  cmp (of_dual x) (of_dual y) = cmp y x := rfl

/-- Generate a linear order structure from a preorder and `cmp` function. -/
def linear_order_of_compares [preorder α] (cmp : α → α → ordering)
  (h : ∀ a b, (cmp a b).compares a b) :
  linear_order α :=
{ le_antisymm := λ a b, (h a b).le_antisymm,
  le_total := λ a b, (h a b).le_total,
  decidable_le := λ a b, decidable_of_iff _ (h a b).ne_gt,
  decidable_lt := λ a b, decidable_of_iff _ (h a b).eq_lt,
  decidable_eq := λ a b, decidable_of_iff _ (h a b).eq_eq,
  .. ‹preorder α› }

variables [linear_order α] (x y : α)

@[simp] lemma cmp_eq_lt_iff : cmp x y = ordering.lt ↔ x < y :=
ordering.compares.eq_lt (cmp_compares x y)

@[simp] lemma cmp_eq_eq_iff : cmp x y = ordering.eq ↔ x = y :=
ordering.compares.eq_eq (cmp_compares x y)

@[simp] lemma cmp_eq_gt_iff : cmp x y = ordering.gt ↔ y < x :=
ordering.compares.eq_gt (cmp_compares x y)

@[simp] lemma cmp_self_eq_eq : cmp x x = ordering.eq :=
by rw cmp_eq_eq_iff

variables {x y} {β : Type*} [linear_order β] {x' y' : β}

lemma cmp_eq_cmp_symm : cmp x y = cmp x' y' ↔ cmp y x = cmp y' x' :=
by { split, rw [←cmp_swap _ y, ←cmp_swap _ y'], cc,
  rw [←cmp_swap _ x, ←cmp_swap _ x'], cc, }

lemma lt_iff_lt_of_cmp_eq_cmp (h : cmp x y = cmp x' y') : x < y ↔ x' < y' :=
by rw [←cmp_eq_lt_iff, ←cmp_eq_lt_iff, h]

lemma le_iff_le_of_cmp_eq_cmp (h : cmp x y = cmp x' y') : x ≤ y ↔ x' ≤ y' :=
by { rw [←not_lt, ←not_lt], apply not_congr,
  apply lt_iff_lt_of_cmp_eq_cmp, rwa cmp_eq_cmp_symm }

lemma eq_iff_eq_of_cmp_eq_cmp (h : cmp x y = cmp x' y') : x = y ↔ x' = y' :=
by rw [le_antisymm_iff, le_antisymm_iff, le_iff_le_of_cmp_eq_cmp h,
  le_iff_le_of_cmp_eq_cmp (cmp_eq_cmp_symm.1 h)]

lemma has_lt.lt.cmp_eq_lt (h : x < y) : cmp x y = ordering.lt := (cmp_eq_lt_iff _ _).2 h
lemma has_lt.lt.cmp_eq_gt (h : x < y) : cmp y x = ordering.gt := (cmp_eq_gt_iff _ _).2 h
lemma eq.cmp_eq_eq (h : x = y) : cmp x y = ordering.eq := (cmp_eq_eq_iff _ _).2 h
lemma eq.cmp_eq_eq' (h : x = y) : cmp y x = ordering.eq := h.symm.cmp_eq_eq


inductive Exp
  | hole
  | var (i : UInt32)
  | app (a b : Exp)
with
  /-- Computes the hash -/ @[simp, computed_field] protected hash : Exp → UInt64
    | .var i => Hashable.hash i
    | .app a b => mixHash a.hash b.hash
    | .hole => 32

def dagLikeTerm : Nat → Exp
  | 0 => .app (.var 42) .hole
  | n+1 => .app (dagLikeTerm n) (dagLikeTerm n)

#eval (dagLikeTerm 1000).hash -- memoized

def varNum? : Exp → Option UInt32
  | .var i => i
  | _ => none

example : varNum? (.var 32) = some 32 := by native_decide

namespace WithIndices

inductive B.C (α : Type u) : Nat → Type u
  | a : C α 0
  | b (c : C α n) {d : C α (n-1)} : C α (n+1)
with
  @[computed_field] hash : ∀ α i, C α i → UInt64
    | _, _, .a => 1
    | _, _, .b c => 42 + c.hash

#eval (B.C.b (α := Nat) (.a) (d := .a)).hash

end WithIndices

namespace Mutual

mutual
  inductive A
    | a (b : B)
    | b (b : B)
  with
    @[computed_field] f : A → Nat
      | .a c => 32 + c.f
      | .b c => 42 + 2*c.f

  inductive B
    | c (a : A)
    | d
  with
    @[computed_field] f : B → Nat
      | .c a => a.f
      | .d => 0
end

#eval (B.c (.a .d)).f

end Mutual


/-
Copyright (c) 2022 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn, Patrick Massot

! This file was ported from Lean 3 source module topology.support
! leanprover-community/mathlib commit 0ebfdb71919ac6ca5d7fbc61a082fa2519556818
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.Separation

/-!
# The topological support of a function

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

In this file we define the topological support of a function `f`, `tsupport f`,
as the closure of the support of `f`.

Furthermore, we say that `f` has compact support if the topological support of `f` is compact.

## Main definitions

* `function.mul_tsupport` & `function.tsupport`
* `function.has_compact_mul_support` & `function.has_compact_support`

## Implementation Notes

* We write all lemmas for multiplicative functions, and use `@[to_additive]` to get the more common
  additive versions.
* We do not put the definitions in the `function` namespace, following many other topological
  definitions that are in the root namespace (compare `embedding` vs `function.embedding`).
-/

open Function Set Filter

open Topology

variable {X α α' β γ δ M E R : Type _}

section One

variable [One α]

variable [TopologicalSpace X]

#print mulTSupport /-
/-- The topological support of a function is the closure of its support, i.e. the closure of the
  set of all elements where the function is not equal to 1. -/
@[to_additive
      " The topological support of a function is the closure of its support. i.e. the closure of the\n  set of all elements where the function is nonzero. "]
def mulTSupport (f : X → α) : Set X :=
  closure (mulSupport f)
#align mul_tsupport mulTSupport
#align tsupport tsupport
-/

/- warning: subset_mul_tsupport -> subset_mulTSupport is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} {α : Type.{u2}} [_inst_1 : One.{u2} α] [_inst_2 : TopologicalSpace.{u1} X] (f : X -> α), HasSubset.Subset.{u1} (Set.{u1} X) (Set.hasSubset.{u1} X) (Function.mulSupport.{u1, u2} X α _inst_1 f) (mulTSupport.{u1, u2} X α _inst_1 _inst_2 f)
but is expected to have type
  forall {X : Type.{u2}} {α : Type.{u1}} [_inst_1 : One.{u1} α] [_inst_2 : TopologicalSpace.{u2} X] (f : X -> α), HasSubset.Subset.{u2} (Set.{u2} X) (Set.instHasSubsetSet.{u2} X) (Function.mulSupport.{u2, u1} X α _inst_1 f) (mulTSupport.{u2, u1} X α _inst_1 _inst_2 f)
Case conversion may be inaccurate. Consider using '#align subset_mul_tsupport subset_mulTSupportₓ'. -/
@[to_additive]
theorem subset_mulTSupport (f : X → α) : mulSupport f ⊆ mulTSupport f :=
  subset_closure
#align subset_mul_tsupport subset_mulTSupport
#align subset_tsupport subset_tsupport

/- warning: is_closed_mul_tsupport -> isClosed_mulTSupport is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} {α : Type.{u2}} [_inst_1 : One.{u2} α] [_inst_2 : TopologicalSpace.{u1} X] (f : X -> α), IsClosed.{u1} X _inst_2 (mulTSupport.{u1, u2} X α _inst_1 _inst_2 f)
but is expected to have type
  forall {X : Type.{u2}} {α : Type.{u1}} [_inst_1 : One.{u1} α] [_inst_2 : TopologicalSpace.{u2} X] (f : X -> α), IsClosed.{u2} X _inst_2 (mulTSupport.{u2, u1} X α _inst_1 _inst_2 f)
Case conversion may be inaccurate. Consider using '#align is_closed_mul_tsupport isClosed_mulTSupportₓ'. -/
@[to_additive]
theorem isClosed_mulTSupport (f : X → α) : IsClosed (mulTSupport f) :=
  isClosed_closure
#align is_closed_mul_tsupport isClosed_mulTSupport
#align is_closed_tsupport isClosed_tsupport

/- warning: mul_tsupport_eq_empty_iff -> mulTSupport_eq_empty_iff is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} {α : Type.{u2}} [_inst_1 : One.{u2} α] [_inst_2 : TopologicalSpace.{u1} X] {f : X -> α}, Iff (Eq.{succ u1} (Set.{u1} X) (mulTSupport.{u1, u2} X α _inst_1 _inst_2 f) (EmptyCollection.emptyCollection.{u1} (Set.{u1} X) (Set.hasEmptyc.{u1} X))) (Eq.{max (succ u1) (succ u2)} (X -> α) f (OfNat.ofNat.{max u1 u2} (X -> α) 1 (OfNat.mk.{max u1 u2} (X -> α) 1 (One.one.{max u1 u2} (X -> α) (Pi.instOne.{u1, u2} X (fun (ᾰ : X) => α) (fun (i : X) => _inst_1))))))
but is expected to have type
  forall {X : Type.{u2}} {α : Type.{u1}} [_inst_1 : One.{u1} α] [_inst_2 : TopologicalSpace.{u2} X] {f : X -> α}, Iff (Eq.{succ u2} (Set.{u2} X) (mulTSupport.{u2, u1} X α _inst_1 _inst_2 f) (EmptyCollection.emptyCollection.{u2} (Set.{u2} X) (Set.instEmptyCollectionSet.{u2} X))) (Eq.{max (succ u2) (succ u1)} (X -> α) f (OfNat.ofNat.{max u2 u1} (X -> α) 1 (One.toOfNat1.{max u2 u1} (X -> α) (Pi.instOne.{u2, u1} X (fun (a._@.Mathlib.Topology.Support._hyg.142 : X) => α) (fun (i : X) => _inst_1)))))
Case conversion may be inaccurate. Consider using '#align mul_tsupport_eq_empty_iff mulTSupport_eq_empty_iffₓ'. -/
@[to_additive]
theorem mulTSupport_eq_empty_iff {f : X → α} : mulTSupport f = ∅ ↔ f = 1 := by
  rw [mulTSupport, closure_empty_iff, mul_support_eq_empty_iff]
#align mul_tsupport_eq_empty_iff mulTSupport_eq_empty_iff
#align tsupport_eq_empty_iff tsupport_eq_empty_iff

/- warning: image_eq_one_of_nmem_mul_tsupport -> image_eq_one_of_nmem_mulTSupport is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} {α : Type.{u2}} [_inst_1 : One.{u2} α] [_inst_2 : TopologicalSpace.{u1} X] {f : X -> α} {x : X}, (Not (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) x (mulTSupport.{u1, u2} X α _inst_1 _inst_2 f))) -> (Eq.{succ u2} α (f x) (OfNat.ofNat.{u2} α 1 (OfNat.mk.{u2} α 1 (One.one.{u2} α _inst_1))))
but is expected to have type
  forall {X : Type.{u2}} {α : Type.{u1}} [_inst_1 : One.{u1} α] [_inst_2 : TopologicalSpace.{u2} X] {f : X -> α} {x : X}, (Not (Membership.mem.{u2, u2} X (Set.{u2} X) (Set.instMembershipSet.{u2} X) x (mulTSupport.{u2, u1} X α _inst_1 _inst_2 f))) -> (Eq.{succ u1} α (f x) (OfNat.ofNat.{u1} α 1 (One.toOfNat1.{u1} α _inst_1)))
Case conversion may be inaccurate. Consider using '#align image_eq_one_of_nmem_mul_tsupport image_eq_one_of_nmem_mulTSupportₓ'. -/
@[to_additive]
theorem image_eq_one_of_nmem_mulTSupport {f : X → α} {x : X} (hx : x ∉ mulTSupport f) : f x = 1 :=
  mulSupport_subset_iff'.mp (subset_mulTSupport f) x hx
#align image_eq_one_of_nmem_mul_tsupport image_eq_one_of_nmem_mulTSupport
#align image_eq_zero_of_nmem_tsupport image_eq_zero_of_nmem_tsupport

#print range_subset_insert_image_mulTSupport /-
@[to_additive]
theorem range_subset_insert_image_mulTSupport (f : X → α) :
    range f ⊆ insert 1 (f '' mulTSupport f) :=
  (range_subset_insert_image_mulSupport f).trans <|
    insert_subset_insert <| image_subset _ subset_closure
#align range_subset_insert_image_mul_tsupport range_subset_insert_image_mulTSupport
#align range_subset_insert_image_tsupport range_subset_insert_image_tsupport
-/

#print range_eq_image_mulTSupport_or /-
@[to_additive]
theorem range_eq_image_mulTSupport_or (f : X → α) :
    range f = f '' mulTSupport f ∨ range f = insert 1 (f '' mulTSupport f) :=
  (wcovby_insert _ _).eq_or_eq (image_subset_range _ _) (range_subset_insert_image_mulTSupport f)
#align range_eq_image_mul_tsupport_or range_eq_image_mulTSupport_or
#align range_eq_image_tsupport_or range_eq_image_tsupport_or
-/

/- warning: tsupport_mul_subset_left -> tsupport_mul_subset_left is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_2 : TopologicalSpace.{u1} X] {α : Type.{u2}} [_inst_3 : MulZeroClass.{u2} α] {f : X -> α} {g : X -> α}, HasSubset.Subset.{u1} (Set.{u1} X) (Set.hasSubset.{u1} X) (tsupport.{u1, u2} X α (MulZeroClass.toHasZero.{u2} α _inst_3) _inst_2 (fun (x : X) => HMul.hMul.{u2, u2, u2} α α α (instHMul.{u2} α (MulZeroClass.toHasMul.{u2} α _inst_3)) (f x) (g x))) (tsupport.{u1, u2} X α (MulZeroClass.toHasZero.{u2} α _inst_3) _inst_2 f)
but is expected to have type
  forall {X : Type.{u1}} [_inst_2 : TopologicalSpace.{u1} X] {α : Type.{u2}} [_inst_3 : MulZeroClass.{u2} α] {f : X -> α} {g : X -> α}, HasSubset.Subset.{u1} (Set.{u1} X) (Set.instHasSubsetSet.{u1} X) (tsupport.{u1, u2} X α (MulZeroClass.toZero.{u2} α _inst_3) _inst_2 (fun (x : X) => HMul.hMul.{u2, u2, u2} α α α (instHMul.{u2} α (MulZeroClass.toMul.{u2} α _inst_3)) (f x) (g x))) (tsupport.{u1, u2} X α (MulZeroClass.toZero.{u2} α _inst_3) _inst_2 f)
Case conversion may be inaccurate. Consider using '#align tsupport_mul_subset_left tsupport_mul_subset_leftₓ'. -/
theorem tsupport_mul_subset_left {α : Type _} [MulZeroClass α] {f g : X → α} :
    (tsupport fun x => f x * g x) ⊆ tsupport f :=
  closure_mono (support_mul_subset_left _ _)
#align tsupport_mul_subset_left tsupport_mul_subset_left

/- warning: tsupport_mul_subset_right -> tsupport_mul_subset_right is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} [_inst_2 : TopologicalSpace.{u1} X] {α : Type.{u2}} [_inst_3 : MulZeroClass.{u2} α] {f : X -> α} {g : X -> α}, HasSubset.Subset.{u1} (Set.{u1} X) (Set.hasSubset.{u1} X) (tsupport.{u1, u2} X α (MulZeroClass.toHasZero.{u2} α _inst_3) _inst_2 (fun (x : X) => HMul.hMul.{u2, u2, u2} α α α (instHMul.{u2} α (MulZeroClass.toHasMul.{u2} α _inst_3)) (f x) (g x))) (tsupport.{u1, u2} X α (MulZeroClass.toHasZero.{u2} α _inst_3) _inst_2 g)
but is expected to have type
  forall {X : Type.{u1}} [_inst_2 : TopologicalSpace.{u1} X] {α : Type.{u2}} [_inst_3 : MulZeroClass.{u2} α] {f : X -> α} {g : X -> α}, HasSubset.Subset.{u1} (Set.{u1} X) (Set.instHasSubsetSet.{u1} X) (tsupport.{u1, u2} X α (MulZeroClass.toZero.{u2} α _inst_3) _inst_2 (fun (x : X) => HMul.hMul.{u2, u2, u2} α α α (instHMul.{u2} α (MulZeroClass.toMul.{u2} α _inst_3)) (f x) (g x))) (tsupport.{u1, u2} X α (MulZeroClass.toZero.{u2} α _inst_3) _inst_2 g)
Case conversion may be inaccurate. Consider using '#align tsupport_mul_subset_right tsupport_mul_subset_rightₓ'. -/
theorem tsupport_mul_subset_right {α : Type _} [MulZeroClass α] {f g : X → α} :
    (tsupport fun x => f x * g x) ⊆ tsupport g :=
  closure_mono (support_mul_subset_right _ _)
#align tsupport_mul_subset_right tsupport_mul_subset_right

end One

/- warning: tsupport_smul_subset_left -> tsupport_smul_subset_left is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} {M : Type.{u2}} {α : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : Zero.{u2} M] [_inst_3 : Zero.{u3} α] [_inst_4 : SMulWithZero.{u2, u3} M α _inst_2 _inst_3] (f : X -> M) (g : X -> α), HasSubset.Subset.{u1} (Set.{u1} X) (Set.hasSubset.{u1} X) (tsupport.{u1, u3} X α _inst_3 _inst_1 (fun (x : X) => SMul.smul.{u2, u3} M α (SMulZeroClass.toHasSmul.{u2, u3} M α _inst_3 (SMulWithZero.toSmulZeroClass.{u2, u3} M α _inst_2 _inst_3 _inst_4)) (f x) (g x))) (tsupport.{u1, u2} X M _inst_2 _inst_1 f)
but is expected to have type
  forall {X : Type.{u1}} {M : Type.{u3}} {α : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : Zero.{u3} M] [_inst_3 : Zero.{u2} α] [_inst_4 : SMulWithZero.{u3, u2} M α _inst_2 _inst_3] (f : X -> M) (g : X -> α), HasSubset.Subset.{u1} (Set.{u1} X) (Set.instHasSubsetSet.{u1} X) (tsupport.{u1, u2} X α _inst_3 _inst_1 (fun (x : X) => HSMul.hSMul.{u3, u2, u2} M α α (instHSMul.{u3, u2} M α (SMulZeroClass.toSMul.{u3, u2} M α _inst_3 (SMulWithZero.toSMulZeroClass.{u3, u2} M α _inst_2 _inst_3 _inst_4))) (f x) (g x))) (tsupport.{u1, u3} X M _inst_2 _inst_1 f)
Case conversion may be inaccurate. Consider using '#align tsupport_smul_subset_left tsupport_smul_subset_leftₓ'. -/
theorem tsupport_smul_subset_left {M α} [TopologicalSpace X] [Zero M] [Zero α] [SMulWithZero M α]
    (f : X → M) (g : X → α) : (tsupport fun x => f x • g x) ⊆ tsupport f :=
  closure_mono <| support_smul_subset_left f g
#align tsupport_smul_subset_left tsupport_smul_subset_left

section

variable [TopologicalSpace α] [TopologicalSpace α']

variable [One β] [One γ] [One δ]

variable {g : β → γ} {f : α → β} {f₂ : α → γ} {m : β → γ → δ} {x : α}

/- warning: not_mem_mul_tsupport_iff_eventually_eq -> not_mem_mulTSupport_iff_eventuallyEq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : One.{u2} β] {f : α -> β} {x : α}, Iff (Not (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x (mulTSupport.{u1, u2} α β _inst_3 _inst_1 f))) (Filter.EventuallyEq.{u1, u2} α β (nhds.{u1} α _inst_1 x) f (OfNat.ofNat.{max u1 u2} (α -> β) 1 (OfNat.mk.{max u1 u2} (α -> β) 1 (One.one.{max u1 u2} (α -> β) (Pi.instOne.{u1, u2} α (fun (ᾰ : α) => β) (fun (i : α) => _inst_3))))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_3 : One.{u1} β] {f : α -> β} {x : α}, Iff (Not (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x (mulTSupport.{u2, u1} α β _inst_3 _inst_1 f))) (Filter.EventuallyEq.{u2, u1} α β (nhds.{u2} α _inst_1 x) f (OfNat.ofNat.{max u2 u1} (α -> β) 1 (One.toOfNat1.{max u2 u1} (α -> β) (Pi.instOne.{u2, u1} α (fun (a._@.Mathlib.Order.Filter.Basic._hyg.19139 : α) => β) (fun (i : α) => _inst_3)))))
Case conversion may be inaccurate. Consider using '#align not_mem_mul_tsupport_iff_eventually_eq not_mem_mulTSupport_iff_eventuallyEqₓ'. -/
@[to_additive]
theorem not_mem_mulTSupport_iff_eventuallyEq : x ∉ mulTSupport f ↔ f =ᶠ[𝓝 x] 1 := by
  simp_rw [mulTSupport, mem_closure_iff_nhds, not_forall, not_nonempty_iff_eq_empty, ←
    disjoint_iff_inter_eq_empty, disjoint_mul_support_iff, eventually_eq_iff_exists_mem]
#align not_mem_mul_tsupport_iff_eventually_eq not_mem_mulTSupport_iff_eventuallyEq
#align not_mem_tsupport_iff_eventually_eq not_mem_tsupport_iff_eventuallyEq

#print continuous_of_mulTSupport /-
@[to_additive]
theorem continuous_of_mulTSupport [TopologicalSpace β] {f : α → β}
    (hf : ∀ x ∈ mulTSupport f, ContinuousAt f x) : Continuous f :=
  continuous_iff_continuousAt.2 fun x =>
    (em _).elim (hf x) fun hx =>
      (@continuousAt_const _ _ _ _ _ 1).congr (not_mem_mulTSupport_iff_eventuallyEq.mp hx).symm
#align continuous_of_mul_tsupport continuous_of_mulTSupport
#align continuous_of_tsupport continuous_of_tsupport
-/

#print HasCompactMulSupport /-
/-- A function `f` *has compact multiplicative support* or is *compactly supported* if the closure
of the multiplicative support of `f` is compact. In a T₂ space this is equivalent to `f` being equal
to `1` outside a compact set. -/
@[to_additive
      " A function `f` *has compact support* or is *compactly supported* if the closure of the support\nof `f` is compact. In a T₂ space this is equivalent to `f` being equal to `0` outside a compact\nset. "]
def HasCompactMulSupport (f : α → β) : Prop :=
  IsCompact (mulTSupport f)
#align has_compact_mul_support HasCompactMulSupport
#align has_compact_support HasCompactSupport
-/

/- warning: has_compact_mul_support_def -> hasCompactMulSupport_def is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : One.{u2} β] {f : α -> β}, Iff (HasCompactMulSupport.{u1, u2} α β _inst_1 _inst_3 f) (IsCompact.{u1} α _inst_1 (closure.{u1} α _inst_1 (Function.mulSupport.{u1, u2} α β _inst_3 f)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_3 : One.{u1} β] {f : α -> β}, Iff (HasCompactMulSupport.{u2, u1} α β _inst_1 _inst_3 f) (IsCompact.{u2} α _inst_1 (closure.{u2} α _inst_1 (Function.mulSupport.{u2, u1} α β _inst_3 f)))
Case conversion may be inaccurate. Consider using '#align has_compact_mul_support_def hasCompactMulSupport_defₓ'. -/
@[to_additive]
theorem hasCompactMulSupport_def : HasCompactMulSupport f ↔ IsCompact (closure (mulSupport f)) := by
  rfl
#align has_compact_mul_support_def hasCompactMulSupport_def
#align has_compact_support_def hasCompactSupport_def

/- warning: exists_compact_iff_has_compact_mul_support -> exists_compact_iff_hasCompactMulSupport is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : One.{u2} β] {f : α -> β} [_inst_6 : T2Space.{u1} α _inst_1], Iff (Exists.{succ u1} (Set.{u1} α) (fun (K : Set.{u1} α) => And (IsCompact.{u1} α _inst_1 K) (forall (x : α), (Not (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x K)) -> (Eq.{succ u2} β (f x) (OfNat.ofNat.{u2} β 1 (OfNat.mk.{u2} β 1 (One.one.{u2} β _inst_3))))))) (HasCompactMulSupport.{u1, u2} α β _inst_1 _inst_3 f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_3 : One.{u1} β] {f : α -> β} [_inst_6 : T2Space.{u2} α _inst_1], Iff (Exists.{succ u2} (Set.{u2} α) (fun (K : Set.{u2} α) => And (IsCompact.{u2} α _inst_1 K) (forall (x : α), (Not (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x K)) -> (Eq.{succ u1} β (f x) (OfNat.ofNat.{u1} β 1 (One.toOfNat1.{u1} β _inst_3)))))) (HasCompactMulSupport.{u2, u1} α β _inst_1 _inst_3 f)
Case conversion may be inaccurate. Consider using '#align exists_compact_iff_has_compact_mul_support exists_compact_iff_hasCompactMulSupportₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (x «expr ∉ » K) -/
@[to_additive]
theorem exists_compact_iff_hasCompactMulSupport [T2Space α] :
    (∃ K : Set α, IsCompact K ∧ ∀ (x) (_ : x ∉ K), f x = 1) ↔ HasCompactMulSupport f := by
  simp_rw [← nmem_mul_support, ← mem_compl_iff, ← subset_def, compl_subset_compl,
    hasCompactMulSupport_def, exists_compact_superset_iff]
#align exists_compact_iff_has_compact_mul_support exists_compact_iff_hasCompactMulSupport
#align exists_compact_iff_has_compact_support exists_compact_iff_hasCompactSupport

/- warning: has_compact_mul_support.intro -> HasCompactMulSupport.intro is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : One.{u2} β] {f : α -> β} [_inst_6 : T2Space.{u1} α _inst_1] {K : Set.{u1} α}, (IsCompact.{u1} α _inst_1 K) -> (forall (x : α), (Not (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x K)) -> (Eq.{succ u2} β (f x) (OfNat.ofNat.{u2} β 1 (OfNat.mk.{u2} β 1 (One.one.{u2} β _inst_3))))) -> (HasCompactMulSupport.{u1, u2} α β _inst_1 _inst_3 f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_3 : One.{u1} β] {f : α -> β} [_inst_6 : T2Space.{u2} α _inst_1] {K : Set.{u2} α}, (IsCompact.{u2} α _inst_1 K) -> (forall (x : α), (Not (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x K)) -> (Eq.{succ u1} β (f x) (OfNat.ofNat.{u1} β 1 (One.toOfNat1.{u1} β _inst_3)))) -> (HasCompactMulSupport.{u2, u1} α β _inst_1 _inst_3 f)
Case conversion may be inaccurate. Consider using '#align has_compact_mul_support.intro HasCompactMulSupport.introₓ'. -/
/- ./././Mathport/Syntax/Translate/Basic.lean:635:2: warning: expanding binder collection (x «expr ∉ » K) -/
@[to_additive]
theorem HasCompactMulSupport.intro [T2Space α] {K : Set α} (hK : IsCompact K)
    (hfK : ∀ (x) (_ : x ∉ K), f x = 1) : HasCompactMulSupport f :=
  exists_compact_iff_hasCompactMulSupport.mp ⟨K, hK, hfK⟩
#align has_compact_mul_support.intro HasCompactMulSupport.intro
#align has_compact_support.intro HasCompactSupport.intro

/- warning: has_compact_mul_support.is_compact -> HasCompactMulSupport.isCompact is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : One.{u2} β] {f : α -> β}, (HasCompactMulSupport.{u1, u2} α β _inst_1 _inst_3 f) -> (IsCompact.{u1} α _inst_1 (mulTSupport.{u1, u2} α β _inst_3 _inst_1 f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_3 : One.{u1} β] {f : α -> β}, (HasCompactMulSupport.{u2, u1} α β _inst_1 _inst_3 f) -> (IsCompact.{u2} α _inst_1 (mulTSupport.{u2, u1} α β _inst_3 _inst_1 f))
Case conversion may be inaccurate. Consider using '#align has_compact_mul_support.is_compact HasCompactMulSupport.isCompactₓ'. -/
@[to_additive]
theorem HasCompactMulSupport.isCompact (hf : HasCompactMulSupport f) : IsCompact (mulTSupport f) :=
  hf
#align has_compact_mul_support.is_compact HasCompactMulSupport.isCompact
#align has_compact_support.is_compact HasCompactSupport.isCompact

/- warning: has_compact_mul_support_iff_eventually_eq -> hasCompactMulSupport_iff_eventuallyEq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : One.{u2} β] {f : α -> β}, Iff (HasCompactMulSupport.{u1, u2} α β _inst_1 _inst_3 f) (Filter.EventuallyEq.{u1, u2} α β (Filter.coclosedCompact.{u1} α _inst_1) f (OfNat.ofNat.{max u1 u2} (α -> β) 1 (OfNat.mk.{max u1 u2} (α -> β) 1 (One.one.{max u1 u2} (α -> β) (Pi.instOne.{u1, u2} α (fun (ᾰ : α) => β) (fun (i : α) => _inst_3))))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_3 : One.{u1} β] {f : α -> β}, Iff (HasCompactMulSupport.{u2, u1} α β _inst_1 _inst_3 f) (Filter.EventuallyEq.{u2, u1} α β (Filter.coclosedCompact.{u2} α _inst_1) f (OfNat.ofNat.{max u2 u1} (α -> β) 1 (One.toOfNat1.{max u2 u1} (α -> β) (Pi.instOne.{u2, u1} α (fun (a._@.Mathlib.Order.Filter.Basic._hyg.19139 : α) => β) (fun (i : α) => _inst_3)))))
Case conversion may be inaccurate. Consider using '#align has_compact_mul_support_iff_eventually_eq hasCompactMulSupport_iff_eventuallyEqₓ'. -/
@[to_additive]
theorem hasCompactMulSupport_iff_eventuallyEq :
    HasCompactMulSupport f ↔ f =ᶠ[coclosedCompact α] 1 :=
  ⟨fun h =>
    mem_coclosedCompact.mpr
      ⟨mulTSupport f, isClosed_mulTSupport _, h, fun x =>
        not_imp_comm.mpr fun hx => subset_mulTSupport f hx⟩,
    fun h =>
    let ⟨C, hC⟩ := mem_coclosed_compact'.mp h
    isCompact_of_isClosed_subset hC.2.1 (isClosed_mulTSupport _) (closure_minimal hC.2.2 hC.1)⟩
#align has_compact_mul_support_iff_eventually_eq hasCompactMulSupport_iff_eventuallyEq
#align has_compact_support_iff_eventually_eq hasCompactSupport_iff_eventuallyEq

#print HasCompactMulSupport.isCompact_range /-
@[to_additive]
theorem HasCompactMulSupport.isCompact_range [TopologicalSpace β] (h : HasCompactMulSupport f)
    (hf : Continuous f) : IsCompact (range f) :=
  by
  cases' range_eq_image_mulTSupport_or f with h2 h2 <;> rw [h2]
  exacts[h.image hf, (h.image hf).insert 1]
#align has_compact_mul_support.is_compact_range HasCompactMulSupport.isCompact_range
#align has_compact_support.is_compact_range HasCompactSupport.isCompact_range
-/

/- warning: has_compact_mul_support.mono' -> HasCompactMulSupport.mono' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : One.{u2} β] [_inst_4 : One.{u3} γ] {f : α -> β} {f' : α -> γ}, (HasCompactMulSupport.{u1, u2} α β _inst_1 _inst_3 f) -> (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (Function.mulSupport.{u1, u3} α γ _inst_4 f') (mulTSupport.{u1, u2} α β _inst_3 _inst_1 f)) -> (HasCompactMulSupport.{u1, u3} α γ _inst_1 _inst_4 f')
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} [_inst_1 : TopologicalSpace.{u3} α] [_inst_3 : One.{u2} β] [_inst_4 : One.{u1} γ] {f : α -> β} {f' : α -> γ}, (HasCompactMulSupport.{u3, u2} α β _inst_1 _inst_3 f) -> (HasSubset.Subset.{u3} (Set.{u3} α) (Set.instHasSubsetSet.{u3} α) (Function.mulSupport.{u3, u1} α γ _inst_4 f') (mulTSupport.{u3, u2} α β _inst_3 _inst_1 f)) -> (HasCompactMulSupport.{u3, u1} α γ _inst_1 _inst_4 f')
Case conversion may be inaccurate. Consider using '#align has_compact_mul_support.mono' HasCompactMulSupport.mono'ₓ'. -/
@[to_additive]
theorem HasCompactMulSupport.mono' {f' : α → γ} (hf : HasCompactMulSupport f)
    (hff' : mulSupport f' ⊆ mulTSupport f) : HasCompactMulSupport f' :=
  isCompact_of_isClosed_subset hf isClosed_closure <| closure_minimal hff' isClosed_closure
#align has_compact_mul_support.mono' HasCompactMulSupport.mono'
#align has_compact_support.mono' HasCompactSupport.mono'

/- warning: has_compact_mul_support.mono -> HasCompactMulSupport.mono is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : One.{u2} β] [_inst_4 : One.{u3} γ] {f : α -> β} {f' : α -> γ}, (HasCompactMulSupport.{u1, u2} α β _inst_1 _inst_3 f) -> (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) (Function.mulSupport.{u1, u3} α γ _inst_4 f') (Function.mulSupport.{u1, u2} α β _inst_3 f)) -> (HasCompactMulSupport.{u1, u3} α γ _inst_1 _inst_4 f')
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} [_inst_1 : TopologicalSpace.{u3} α] [_inst_3 : One.{u2} β] [_inst_4 : One.{u1} γ] {f : α -> β} {f' : α -> γ}, (HasCompactMulSupport.{u3, u2} α β _inst_1 _inst_3 f) -> (HasSubset.Subset.{u3} (Set.{u3} α) (Set.instHasSubsetSet.{u3} α) (Function.mulSupport.{u3, u1} α γ _inst_4 f') (Function.mulSupport.{u3, u2} α β _inst_3 f)) -> (HasCompactMulSupport.{u3, u1} α γ _inst_1 _inst_4 f')
Case conversion may be inaccurate. Consider using '#align has_compact_mul_support.mono HasCompactMulSupport.monoₓ'. -/
@[to_additive]
theorem HasCompactMulSupport.mono {f' : α → γ} (hf : HasCompactMulSupport f)
    (hff' : mulSupport f' ⊆ mulSupport f) : HasCompactMulSupport f' :=
  hf.mono' <| hff'.trans subset_closure
#align has_compact_mul_support.mono HasCompactMulSupport.mono
#align has_compact_support.mono HasCompactSupport.mono

/- warning: has_compact_mul_support.comp_left -> HasCompactMulSupport.comp_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : One.{u2} β] [_inst_4 : One.{u3} γ] {g : β -> γ} {f : α -> β}, (HasCompactMulSupport.{u1, u2} α β _inst_1 _inst_3 f) -> (Eq.{succ u3} γ (g (OfNat.ofNat.{u2} β 1 (OfNat.mk.{u2} β 1 (One.one.{u2} β _inst_3)))) (OfNat.ofNat.{u3} γ 1 (OfNat.mk.{u3} γ 1 (One.one.{u3} γ _inst_4)))) -> (HasCompactMulSupport.{u1, u3} α γ _inst_1 _inst_4 (Function.comp.{succ u1, succ u2, succ u3} α β γ g f))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} [_inst_1 : TopologicalSpace.{u3} α] [_inst_3 : One.{u2} β] [_inst_4 : One.{u1} γ] {g : β -> γ} {f : α -> β}, (HasCompactMulSupport.{u3, u2} α β _inst_1 _inst_3 f) -> (Eq.{succ u1} γ (g (OfNat.ofNat.{u2} β 1 (One.toOfNat1.{u2} β _inst_3))) (OfNat.ofNat.{u1} γ 1 (One.toOfNat1.{u1} γ _inst_4))) -> (HasCompactMulSupport.{u3, u1} α γ _inst_1 _inst_4 (Function.comp.{succ u3, succ u2, succ u1} α β γ g f))
Case conversion may be inaccurate. Consider using '#align has_compact_mul_support.comp_left HasCompactMulSupport.comp_leftₓ'. -/
@[to_additive]
theorem HasCompactMulSupport.comp_left (hf : HasCompactMulSupport f) (hg : g 1 = 1) :
    HasCompactMulSupport (g ∘ f) :=
  hf.mono <| mulSupport_comp_subset hg f
#align has_compact_mul_support.comp_left HasCompactMulSupport.comp_left
#align has_compact_support.comp_left HasCompactSupport.comp_left

#print hasCompactMulSupport_comp_left /-
@[to_additive]
theorem hasCompactMulSupport_comp_left (hg : ∀ {x}, g x = 1 ↔ x = 1) :
    HasCompactMulSupport (g ∘ f) ↔ HasCompactMulSupport f := by
  simp_rw [hasCompactMulSupport_def, mul_support_comp_eq g (@hg) f]
#align has_compact_mul_support_comp_left hasCompactMulSupport_comp_left
#align has_compact_support_comp_left hasCompactSupport_comp_left
-/

/- warning: has_compact_mul_support.comp_closed_embedding -> HasCompactMulSupport.comp_closedEmbedding is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {α' : Type.{u2}} {β : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : TopologicalSpace.{u2} α'] [_inst_3 : One.{u3} β] {f : α -> β}, (HasCompactMulSupport.{u1, u3} α β _inst_1 _inst_3 f) -> (forall {g : α' -> α}, (ClosedEmbedding.{u2, u1} α' α _inst_2 _inst_1 g) -> (HasCompactMulSupport.{u2, u3} α' β _inst_2 _inst_3 (Function.comp.{succ u2, succ u1, succ u3} α' α β f g)))
but is expected to have type
  forall {α : Type.{u3}} {α' : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u3} α] [_inst_2 : TopologicalSpace.{u1} α'] [_inst_3 : One.{u2} β] {f : α -> β}, (HasCompactMulSupport.{u3, u2} α β _inst_1 _inst_3 f) -> (forall {g : α' -> α}, (ClosedEmbedding.{u1, u3} α' α _inst_2 _inst_1 g) -> (HasCompactMulSupport.{u1, u2} α' β _inst_2 _inst_3 (Function.comp.{succ u1, succ u3, succ u2} α' α β f g)))
Case conversion may be inaccurate. Consider using '#align has_compact_mul_support.comp_closed_embedding HasCompactMulSupport.comp_closedEmbeddingₓ'. -/
@[to_additive]
theorem HasCompactMulSupport.comp_closedEmbedding (hf : HasCompactMulSupport f) {g : α' → α}
    (hg : ClosedEmbedding g) : HasCompactMulSupport (f ∘ g) :=
  by
  rw [hasCompactMulSupport_def, Function.mulSupport_comp_eq_preimage]
  refine' isCompact_of_isClosed_subset (hg.is_compact_preimage hf) isClosed_closure _
  rw [hg.to_embedding.closure_eq_preimage_closure_image]
  exact preimage_mono (closure_mono <| image_preimage_subset _ _)
#align has_compact_mul_support.comp_closed_embedding HasCompactMulSupport.comp_closedEmbedding
#align has_compact_support.comp_closed_embedding HasCompactSupport.comp_closedEmbedding

/- warning: has_compact_mul_support.comp₂_left -> HasCompactMulSupport.comp₂_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_3 : One.{u2} β] [_inst_4 : One.{u3} γ] [_inst_5 : One.{u4} δ] {f : α -> β} {f₂ : α -> γ} {m : β -> γ -> δ}, (HasCompactMulSupport.{u1, u2} α β _inst_1 _inst_3 f) -> (HasCompactMulSupport.{u1, u3} α γ _inst_1 _inst_4 f₂) -> (Eq.{succ u4} δ (m (OfNat.ofNat.{u2} β 1 (OfNat.mk.{u2} β 1 (One.one.{u2} β _inst_3))) (OfNat.ofNat.{u3} γ 1 (OfNat.mk.{u3} γ 1 (One.one.{u3} γ _inst_4)))) (OfNat.ofNat.{u4} δ 1 (OfNat.mk.{u4} δ 1 (One.one.{u4} δ _inst_5)))) -> (HasCompactMulSupport.{u1, u4} α δ _inst_1 _inst_5 (fun (x : α) => m (f x) (f₂ x)))
but is expected to have type
  forall {α : Type.{u4}} {β : Type.{u3}} {γ : Type.{u2}} {δ : Type.{u1}} [_inst_1 : TopologicalSpace.{u4} α] [_inst_3 : One.{u3} β] [_inst_4 : One.{u2} γ] [_inst_5 : One.{u1} δ] {f : α -> β} {f₂ : α -> γ} {m : β -> γ -> δ}, (HasCompactMulSupport.{u4, u3} α β _inst_1 _inst_3 f) -> (HasCompactMulSupport.{u4, u2} α γ _inst_1 _inst_4 f₂) -> (Eq.{succ u1} δ (m (OfNat.ofNat.{u3} β 1 (One.toOfNat1.{u3} β _inst_3)) (OfNat.ofNat.{u2} γ 1 (One.toOfNat1.{u2} γ _inst_4))) (OfNat.ofNat.{u1} δ 1 (One.toOfNat1.{u1} δ _inst_5))) -> (HasCompactMulSupport.{u4, u1} α δ _inst_1 _inst_5 (fun (x : α) => m (f x) (f₂ x)))
Case conversion may be inaccurate. Consider using '#align has_compact_mul_support.comp₂_left HasCompactMulSupport.comp₂_leftₓ'. -/
@[to_additive]
theorem HasCompactMulSupport.comp₂_left (hf : HasCompactMulSupport f)
    (hf₂ : HasCompactMulSupport f₂) (hm : m 1 1 = 1) :
    HasCompactMulSupport fun x => m (f x) (f₂ x) :=
  by
  rw [hasCompactMulSupport_iff_eventuallyEq] at hf hf₂⊢
  filter_upwards [hf, hf₂]using fun x hx hx₂ => by simp_rw [hx, hx₂, Pi.one_apply, hm]
#align has_compact_mul_support.comp₂_left HasCompactMulSupport.comp₂_left
#align has_compact_support.comp₂_left HasCompactSupport.comp₂_left

end

section Monoid

variable [TopologicalSpace α] [Monoid β]

variable {f f' : α → β} {x : α}

/- warning: has_compact_mul_support.mul -> HasCompactMulSupport.mul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : Monoid.{u2} β] {f : α -> β} {f' : α -> β}, (HasCompactMulSupport.{u1, u2} α β _inst_1 (MulOneClass.toHasOne.{u2} β (Monoid.toMulOneClass.{u2} β _inst_2)) f) -> (HasCompactMulSupport.{u1, u2} α β _inst_1 (MulOneClass.toHasOne.{u2} β (Monoid.toMulOneClass.{u2} β _inst_2)) f') -> (HasCompactMulSupport.{u1, u2} α β _inst_1 (MulOneClass.toHasOne.{u2} β (Monoid.toMulOneClass.{u2} β _inst_2)) (HMul.hMul.{max u1 u2, max u1 u2, max u1 u2} (α -> β) (α -> β) (α -> β) (instHMul.{max u1 u2} (α -> β) (Pi.instMul.{u1, u2} α (fun (ᾰ : α) => β) (fun (i : α) => MulOneClass.toHasMul.{u2} β (Monoid.toMulOneClass.{u2} β _inst_2)))) f f'))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : Monoid.{u1} β] {f : α -> β} {f' : α -> β}, (HasCompactMulSupport.{u2, u1} α β _inst_1 (Monoid.toOne.{u1} β _inst_2) f) -> (HasCompactMulSupport.{u2, u1} α β _inst_1 (Monoid.toOne.{u1} β _inst_2) f') -> (HasCompactMulSupport.{u2, u1} α β _inst_1 (Monoid.toOne.{u1} β _inst_2) (HMul.hMul.{max u2 u1, max u2 u1, max u2 u1} (α -> β) (α -> β) (α -> β) (instHMul.{max u2 u1} (α -> β) (Pi.instMul.{u2, u1} α (fun (ᾰ : α) => β) (fun (i : α) => MulOneClass.toMul.{u1} β (Monoid.toMulOneClass.{u1} β _inst_2)))) f f'))
Case conversion may be inaccurate. Consider using '#align has_compact_mul_support.mul HasCompactMulSupport.mulₓ'. -/
@[to_additive]
theorem HasCompactMulSupport.mul (hf : HasCompactMulSupport f) (hf' : HasCompactMulSupport f') :
    HasCompactMulSupport (f * f') := by apply hf.comp₂_left hf' (mul_one 1)
#align has_compact_mul_support.mul HasCompactMulSupport.mul
#align has_compact_support.add HasCompactSupport.add

-- `by apply` speeds up elaboration
end Monoid

section DistribMulAction

variable [TopologicalSpace α] [MonoidWithZero R] [AddMonoid M] [DistribMulAction R M]

variable {f : α → R} {f' : α → M} {x : α}

/- warning: has_compact_support.smul_left -> HasCompactSupport.smul_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {M : Type.{u2}} {R : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : MonoidWithZero.{u3} R] [_inst_3 : AddMonoid.{u2} M] [_inst_4 : DistribMulAction.{u3, u2} R M (MonoidWithZero.toMonoid.{u3} R _inst_2) _inst_3] {f : α -> R} {f' : α -> M}, (HasCompactSupport.{u1, u2} α M _inst_1 (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_3)) f') -> (HasCompactSupport.{u1, u2} α M _inst_1 (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_3)) (SMul.smul.{max u1 u3, max u1 u2} (α -> R) (α -> M) (Pi.smul'.{u1, u3, u2} α (fun (ᾰ : α) => R) (fun (ᾰ : α) => M) (fun (i : α) => SMulZeroClass.toHasSmul.{u3, u2} R M (AddZeroClass.toHasZero.{u2} M (AddMonoid.toAddZeroClass.{u2} M _inst_3)) (DistribSMul.toSmulZeroClass.{u3, u2} R M (AddMonoid.toAddZeroClass.{u2} M _inst_3) (DistribMulAction.toDistribSMul.{u3, u2} R M (MonoidWithZero.toMonoid.{u3} R _inst_2) _inst_3 _inst_4)))) f f'))
but is expected to have type
  forall {α : Type.{u3}} {M : Type.{u2}} {R : Type.{u1}} [_inst_1 : TopologicalSpace.{u3} α] [_inst_2 : MonoidWithZero.{u1} R] [_inst_3 : AddMonoid.{u2} M] [_inst_4 : DistribMulAction.{u1, u2} R M (MonoidWithZero.toMonoid.{u1} R _inst_2) _inst_3] {f : α -> R} {f' : α -> M}, (HasCompactSupport.{u3, u2} α M _inst_1 (AddMonoid.toZero.{u2} M _inst_3) f') -> (HasCompactSupport.{u3, u2} α M _inst_1 (AddMonoid.toZero.{u2} M _inst_3) (HSMul.hSMul.{max u3 u1, max u3 u2, max u3 u2} (α -> R) (α -> M) (α -> M) (instHSMul.{max u3 u1, max u3 u2} (α -> R) (α -> M) (Pi.smul'.{u3, u1, u2} α (fun (a._@.Mathlib.Topology.Support._hyg.2143 : α) => R) (fun (a._@.Mathlib.Topology.Support._hyg.2146 : α) => M) (fun (i : α) => SMulZeroClass.toSMul.{u1, u2} R M (AddMonoid.toZero.{u2} M _inst_3) (DistribSMul.toSMulZeroClass.{u1, u2} R M (AddMonoid.toAddZeroClass.{u2} M _inst_3) (DistribMulAction.toDistribSMul.{u1, u2} R M (MonoidWithZero.toMonoid.{u1} R _inst_2) _inst_3 _inst_4))))) f f'))
Case conversion may be inaccurate. Consider using '#align has_compact_support.smul_left HasCompactSupport.smul_leftₓ'. -/
theorem HasCompactSupport.smul_left (hf : HasCompactSupport f') : HasCompactSupport (f • f') :=
  by
  rw [hasCompactSupport_iff_eventuallyEq] at hf⊢
  refine' hf.mono fun x hx => by simp_rw [Pi.smul_apply', hx, Pi.zero_apply, smul_zero]
#align has_compact_support.smul_left HasCompactSupport.smul_left

end DistribMulAction

section SMulWithZero

variable [TopologicalSpace α] [Zero R] [Zero M] [SMulWithZero R M]

variable {f : α → R} {f' : α → M} {x : α}

/- warning: has_compact_support.smul_right -> HasCompactSupport.smul_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {M : Type.{u2}} {R : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : Zero.{u3} R] [_inst_3 : Zero.{u2} M] [_inst_4 : SMulWithZero.{u3, u2} R M _inst_2 _inst_3] {f : α -> R} {f' : α -> M}, (HasCompactSupport.{u1, u3} α R _inst_1 _inst_2 f) -> (HasCompactSupport.{u1, u2} α M _inst_1 _inst_3 (SMul.smul.{max u1 u3, max u1 u2} (α -> R) (α -> M) (Pi.smul'.{u1, u3, u2} α (fun (ᾰ : α) => R) (fun (ᾰ : α) => M) (fun (i : α) => SMulZeroClass.toHasSmul.{u3, u2} R M _inst_3 (SMulWithZero.toSmulZeroClass.{u3, u2} R M _inst_2 _inst_3 _inst_4))) f f'))
but is expected to have type
  forall {α : Type.{u3}} {M : Type.{u1}} {R : Type.{u2}} [_inst_1 : TopologicalSpace.{u3} α] [_inst_2 : Zero.{u2} R] [_inst_3 : Zero.{u1} M] [_inst_4 : SMulWithZero.{u2, u1} R M _inst_2 _inst_3] {f : α -> R} {f' : α -> M}, (HasCompactSupport.{u3, u2} α R _inst_1 _inst_2 f) -> (HasCompactSupport.{u3, u1} α M _inst_1 _inst_3 (HSMul.hSMul.{max u3 u2, max u3 u1, max u3 u1} (α -> R) (α -> M) (α -> M) (instHSMul.{max u3 u2, max u3 u1} (α -> R) (α -> M) (Pi.smul'.{u3, u2, u1} α (fun (a._@.Mathlib.Topology.Support._hyg.2300 : α) => R) (fun (a._@.Mathlib.Topology.Support._hyg.2303 : α) => M) (fun (i : α) => SMulZeroClass.toSMul.{u2, u1} R M _inst_3 (SMulWithZero.toSMulZeroClass.{u2, u1} R M _inst_2 _inst_3 _inst_4)))) f f'))
Case conversion may be inaccurate. Consider using '#align has_compact_support.smul_right HasCompactSupport.smul_rightₓ'. -/
theorem HasCompactSupport.smul_right (hf : HasCompactSupport f) : HasCompactSupport (f • f') :=
  by
  rw [hasCompactSupport_iff_eventuallyEq] at hf⊢
  refine' hf.mono fun x hx => by simp_rw [Pi.smul_apply', hx, Pi.zero_apply, zero_smul]
#align has_compact_support.smul_right HasCompactSupport.smul_right

/- warning: has_compact_support.smul_left' -> HasCompactSupport.smul_left' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {M : Type.{u2}} {R : Type.{u3}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : Zero.{u3} R] [_inst_3 : Zero.{u2} M] [_inst_4 : SMulWithZero.{u3, u2} R M _inst_2 _inst_3] {f : α -> R} {f' : α -> M}, (HasCompactSupport.{u1, u2} α M _inst_1 _inst_3 f') -> (HasCompactSupport.{u1, u2} α M _inst_1 _inst_3 (SMul.smul.{max u1 u3, max u1 u2} (α -> R) (α -> M) (Pi.smul'.{u1, u3, u2} α (fun (ᾰ : α) => R) (fun (ᾰ : α) => M) (fun (i : α) => SMulZeroClass.toHasSmul.{u3, u2} R M _inst_3 (SMulWithZero.toSmulZeroClass.{u3, u2} R M _inst_2 _inst_3 _inst_4))) f f'))
but is expected to have type
  forall {α : Type.{u3}} {M : Type.{u2}} {R : Type.{u1}} [_inst_1 : TopologicalSpace.{u3} α] [_inst_2 : Zero.{u1} R] [_inst_3 : Zero.{u2} M] [_inst_4 : SMulWithZero.{u1, u2} R M _inst_2 _inst_3] {f : α -> R} {f' : α -> M}, (HasCompactSupport.{u3, u2} α M _inst_1 _inst_3 f') -> (HasCompactSupport.{u3, u2} α M _inst_1 _inst_3 (HSMul.hSMul.{max u3 u1, max u3 u2, max u3 u2} (α -> R) (α -> M) (α -> M) (instHSMul.{max u3 u1, max u3 u2} (α -> R) (α -> M) (Pi.smul'.{u3, u1, u2} α (fun (a._@.Mathlib.Topology.Support._hyg.2395 : α) => R) (fun (a._@.Mathlib.Topology.Support._hyg.2398 : α) => M) (fun (i : α) => SMulZeroClass.toSMul.{u1, u2} R M _inst_3 (SMulWithZero.toSMulZeroClass.{u1, u2} R M _inst_2 _inst_3 _inst_4)))) f f'))
Case conversion may be inaccurate. Consider using '#align has_compact_support.smul_left' HasCompactSupport.smul_left'ₓ'. -/
theorem HasCompactSupport.smul_left' (hf : HasCompactSupport f') : HasCompactSupport (f • f') :=
  by
  rw [hasCompactSupport_iff_eventuallyEq] at hf⊢
  refine' hf.mono fun x hx => by simp_rw [Pi.smul_apply', hx, Pi.zero_apply, smul_zero]
#align has_compact_support.smul_left' HasCompactSupport.smul_left'

end SMulWithZero

section MulZeroClass

variable [TopologicalSpace α] [MulZeroClass β]

variable {f f' : α → β} {x : α}

/- warning: has_compact_support.mul_right -> HasCompactSupport.mul_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : MulZeroClass.{u2} β] {f : α -> β} {f' : α -> β}, (HasCompactSupport.{u1, u2} α β _inst_1 (MulZeroClass.toHasZero.{u2} β _inst_2) f) -> (HasCompactSupport.{u1, u2} α β _inst_1 (MulZeroClass.toHasZero.{u2} β _inst_2) (HMul.hMul.{max u1 u2, max u1 u2, max u1 u2} (α -> β) (α -> β) (α -> β) (instHMul.{max u1 u2} (α -> β) (Pi.instMul.{u1, u2} α (fun (ᾰ : α) => β) (fun (i : α) => MulZeroClass.toHasMul.{u2} β _inst_2))) f f'))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : MulZeroClass.{u1} β] {f : α -> β} {f' : α -> β}, (HasCompactSupport.{u2, u1} α β _inst_1 (MulZeroClass.toZero.{u1} β _inst_2) f) -> (HasCompactSupport.{u2, u1} α β _inst_1 (MulZeroClass.toZero.{u1} β _inst_2) (HMul.hMul.{max u2 u1, max u2 u1, max u2 u1} (α -> β) (α -> β) (α -> β) (instHMul.{max u2 u1} (α -> β) (Pi.instMul.{u2, u1} α (fun (ᾰ : α) => β) (fun (i : α) => MulZeroClass.toMul.{u1} β _inst_2))) f f'))
Case conversion may be inaccurate. Consider using '#align has_compact_support.mul_right HasCompactSupport.mul_rightₓ'. -/
theorem HasCompactSupport.mul_right (hf : HasCompactSupport f) : HasCompactSupport (f * f') :=
  by
  rw [hasCompactSupport_iff_eventuallyEq] at hf⊢
  refine' hf.mono fun x hx => by simp_rw [Pi.mul_apply, hx, Pi.zero_apply, MulZeroClass.zero_mul]
#align has_compact_support.mul_right HasCompactSupport.mul_right

/- warning: has_compact_support.mul_left -> HasCompactSupport.mul_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : TopologicalSpace.{u1} α] [_inst_2 : MulZeroClass.{u2} β] {f : α -> β} {f' : α -> β}, (HasCompactSupport.{u1, u2} α β _inst_1 (MulZeroClass.toHasZero.{u2} β _inst_2) f') -> (HasCompactSupport.{u1, u2} α β _inst_1 (MulZeroClass.toHasZero.{u2} β _inst_2) (HMul.hMul.{max u1 u2, max u1 u2, max u1 u2} (α -> β) (α -> β) (α -> β) (instHMul.{max u1 u2} (α -> β) (Pi.instMul.{u1, u2} α (fun (ᾰ : α) => β) (fun (i : α) => MulZeroClass.toHasMul.{u2} β _inst_2))) f f'))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : TopologicalSpace.{u2} α] [_inst_2 : MulZeroClass.{u1} β] {f : α -> β} {f' : α -> β}, (HasCompactSupport.{u2, u1} α β _inst_1 (MulZeroClass.toZero.{u1} β _inst_2) f') -> (HasCompactSupport.{u2, u1} α β _inst_1 (MulZeroClass.toZero.{u1} β _inst_2) (HMul.hMul.{max u2 u1, max u2 u1, max u2 u1} (α -> β) (α -> β) (α -> β) (instHMul.{max u2 u1} (α -> β) (Pi.instMul.{u2, u1} α (fun (ᾰ : α) => β) (fun (i : α) => MulZeroClass.toMul.{u1} β _inst_2))) f f'))
Case conversion may be inaccurate. Consider using '#align has_compact_support.mul_left HasCompactSupport.mul_leftₓ'. -/
theorem HasCompactSupport.mul_left (hf : HasCompactSupport f') : HasCompactSupport (f * f') :=
  by
  rw [hasCompactSupport_iff_eventuallyEq] at hf⊢
  refine' hf.mono fun x hx => by simp_rw [Pi.mul_apply, hx, Pi.zero_apply, MulZeroClass.mul_zero]
#align has_compact_support.mul_left HasCompactSupport.mul_left

end MulZeroClass

namespace LocallyFinite

variable {ι : Type _} {U : ι → Set X} [TopologicalSpace X] [One R]

/- warning: locally_finite.exists_finset_nhd_mul_support_subset -> LocallyFinite.exists_finset_nhd_mulSupport_subset is a dubious translation:
lean 3 declaration is
  forall {X : Type.{u1}} {R : Type.{u2}} {ι : Type.{u3}} {U : ι -> (Set.{u1} X)} [_inst_1 : TopologicalSpace.{u1} X] [_inst_2 : One.{u2} R] {f : ι -> X -> R}, (LocallyFinite.{u3, u1} ι X _inst_1 (fun (i : ι) => Function.mulSupport.{u1, u2} X R _inst_2 (f i))) -> (forall (i : ι), HasSubset.Subset.{u1} (Set.{u1} X) (Set.hasSubset.{u1} X) (mulTSupport.{u1, u2} X R _inst_2 _inst_1 (f i)) (U i)) -> (forall (i : ι), IsOpen.{u1} X _inst_1 (U i)) -> (forall (x : X), Exists.{succ u3} (Finset.{u3} ι) (fun (is : Finset.{u3} ι) => Exists.{succ u1} (Set.{u1} X) (fun {n : Set.{u1} X} => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} X) (Filter.{u1} X) (Filter.hasMem.{u1} X) n (nhds.{u1} X _inst_1 x)) (fun (hn₁ : Membership.Mem.{u1, u1} (Set.{u1} X) (Filter.{u1} X) (Filter.hasMem.{u1} X) n (nhds.{u1} X _inst_1 x)) => Exists.{0} (HasSubset.Subset.{u1} (Set.{u1} X) (Set.hasSubset.{u1} X) n (Set.interᵢ.{u1, succ u3} X ι (fun (i : ι) => Set.interᵢ.{u1, 0} X (Membership.Mem.{u3, u3} ι (Finset.{u3} ι) (Finset.hasMem.{u3} ι) i is) (fun (H : Membership.Mem.{u3, u3} ι (Finset.{u3} ι) (Finset.hasMem.{u3} ι) i is) => U i)))) (fun (hn₂ : HasSubset.Subset.{u1} (Set.{u1} X) (Set.hasSubset.{u1} X) n (Set.interᵢ.{u1, succ u3} X ι (fun (i : ι) => Set.interᵢ.{u1, 0} X (Membership.Mem.{u3, u3} ι (Finset.{u3} ι) (Finset.hasMem.{u3} ι) i is) (fun (H : Membership.Mem.{u3, u3} ι (Finset.{u3} ι) (Finset.hasMem.{u3} ι) i is) => U i)))) => forall (z : X), (Membership.Mem.{u1, u1} X (Set.{u1} X) (Set.hasMem.{u1} X) z n) -> (HasSubset.Subset.{u3} (Set.{u3} ι) (Set.hasSubset.{u3} ι) (Function.mulSupport.{u3, u2} ι R _inst_2 (fun (i : ι) => f i z)) ((fun (a : Type.{u3}) (b : Type.{u3}) [self : HasLiftT.{succ u3, succ u3} a b] => self.0) (Finset.{u3} ι) (Set.{u3} ι) (HasLiftT.mk.{succ u3, succ u3} (Finset.{u3} ι) (Set.{u3} ι) (CoeTCₓ.coe.{succ u3, succ u3} (Finset.{u3} ι) (Set.{u3} ι) (Finset.Set.hasCoeT.{u3} ι))) is)))))))
but is expected to have type
  forall {X : Type.{u2}} {R : Type.{u1}} {ι : Type.{u3}} {U : ι -> (Set.{u2} X)} [_inst_1 : TopologicalSpace.{u2} X] [_inst_2 : One.{u1} R] {f : ι -> X -> R}, (LocallyFinite.{u3, u2} ι X _inst_1 (fun (i : ι) => Function.mulSupport.{u2, u1} X R _inst_2 (f i))) -> (forall (i : ι), HasSubset.Subset.{u2} (Set.{u2} X) (Set.instHasSubsetSet.{u2} X) (mulTSupport.{u2, u1} X R _inst_2 _inst_1 (f i)) (U i)) -> (forall (i : ι), IsOpen.{u2} X _inst_1 (U i)) -> (forall (x : X), Exists.{succ u3} (Finset.{u3} ι) (fun (is : Finset.{u3} ι) => Exists.{succ u2} (Set.{u2} X) (fun (n : Set.{u2} X) => And (Membership.mem.{u2, u2} (Set.{u2} X) (Filter.{u2} X) (instMembershipSetFilter.{u2} X) n (nhds.{u2} X _inst_1 x)) (And (HasSubset.Subset.{u2} (Set.{u2} X) (Set.instHasSubsetSet.{u2} X) n (Set.interᵢ.{u2, succ u3} X ι (fun (i : ι) => Set.interᵢ.{u2, 0} X (Membership.mem.{u3, u3} ι (Finset.{u3} ι) (Finset.instMembershipFinset.{u3} ι) i is) (fun (h._@.Mathlib.Topology.Support._hyg.2812 : Membership.mem.{u3, u3} ι (Finset.{u3} ι) (Finset.instMembershipFinset.{u3} ι) i is) => U i)))) (forall (z : X), (Membership.mem.{u2, u2} X (Set.{u2} X) (Set.instMembershipSet.{u2} X) z n) -> (HasSubset.Subset.{u3} (Set.{u3} ι) (Set.instHasSubsetSet.{u3} ι) (Function.mulSupport.{u3, u1} ι R _inst_2 (fun (i : ι) => f i z)) (Finset.toSet.{u3} ι is)))))))
Case conversion may be inaccurate. Consider using '#align locally_finite.exists_finset_nhd_mul_support_subset LocallyFinite.exists_finset_nhd_mulSupport_subsetₓ'. -/
/-- If a family of functions `f` has locally-finite multiplicative support, subordinate to a family
of open sets, then for any point we can find a neighbourhood on which only finitely-many members of
`f` are not equal to 1. -/
@[to_additive
      " If a family of functions `f` has locally-finite support, subordinate to a family of open sets,\nthen for any point we can find a neighbourhood on which only finitely-many members of `f` are\nnon-zero. "]
theorem exists_finset_nhd_mulSupport_subset {f : ι → X → R}
    (hlf : LocallyFinite fun i => mulSupport (f i)) (hso : ∀ i, mulTSupport (f i) ⊆ U i)
    (ho : ∀ i, IsOpen (U i)) (x : X) :
    ∃ (is : Finset ι)(n : Set X)(hn₁ : n ∈ 𝓝 x)(hn₂ : n ⊆ ⋂ i ∈ is, U i),
      ∀ z ∈ n, (mulSupport fun i => f i z) ⊆ is :=
  by
  obtain ⟨n, hn, hnf⟩ := hlf x
  classical
    let is := hnf.to_finset.filter fun i => x ∈ U i
    let js := hnf.to_finset.filter fun j => x ∉ U j
    refine'
      ⟨is, (n ∩ ⋂ j ∈ js, mulTSupport (f j)ᶜ) ∩ ⋂ i ∈ is, U i, inter_mem (inter_mem hn _) _,
        inter_subset_right _ _, fun z hz => _⟩
    ·
      exact
        (bInter_finset_mem js).mpr fun j hj =>
          IsClosed.compl_mem_nhds (isClosed_mulTSupport _)
            (Set.not_mem_subset (hso j) (finset.mem_filter.mp hj).2)
    · exact (bInter_finset_mem is).mpr fun i hi => (ho i).mem_nhds (finset.mem_filter.mp hi).2
    · have hzn : z ∈ n := by
        rw [inter_assoc] at hz
        exact mem_of_mem_inter_left hz
      replace hz := mem_of_mem_inter_right (mem_of_mem_inter_left hz)
      simp only [Finset.mem_filter, finite.mem_to_finset, mem_set_of_eq, mem_Inter, and_imp] at hz
      suffices (mul_support fun i => f i z) ⊆ hnf.to_finset
        by
        refine' hnf.to_finset.subset_coe_filter_of_subset_forall _ this fun i hi => _
        specialize hz i ⟨z, ⟨hi, hzn⟩⟩
        contrapose hz
        simp [hz, subset_mulTSupport (f i) hi]
      intro i hi
      simp only [finite.coe_to_finset, mem_set_of_eq]
      exact ⟨z, ⟨hi, hzn⟩⟩
#align locally_finite.exists_finset_nhd_mul_support_subset LocallyFinite.exists_finset_nhd_mulSupport_subset
#align locally_finite.exists_finset_nhd_support_subset LocallyFinite.exists_finset_nhd_support_subset

end LocallyFinite


/-
Copyright (c) 2021 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot, Yury Kudryashov
-/
import topology.algebra.ordered.intermediate_value

/-!
# Compactness of a closed interval

In this file we prove that a closed interval in a conditionally complete linear ordered type with
order topology (or a product of such types) is compact. We also prove the extreme value theorem
(`is_compact.exists_forall_le`, `is_compact.exists_forall_ge`): a continuous function on a compact
set takes its minimum and maximum values.

We also prove that the image of a closed interval under a continuous map is a closed interval, see
`continuous_on.image_Icc`.

## Tags

compact, extreme value theorem
-/

open classical filter order_dual topological_space function set

/-!
### Compactness of a closed interval

In this section we define a typeclass `compact_Icc_space α` saying that all closed intervals in `α`
are compact. Then we provide an instance for a `conditionally_complete_linear_order` and prove that
the product (both `α × β` and an indexed product) of spaces with this property inherits the
property.

We also prove some simple lemmas about spaces with this property.
-/

/-- This typeclass says that all closed intervals in `α` are compact. This is true for all
conditionally complete linear orders with order topology and products (finite or infinite)
of such spaces. -/
class compact_Icc_space (α : Type*) [topological_space α] [preorder α] : Prop :=
(is_compact_Icc : ∀ {a b : α}, is_compact (Icc a b))

export compact_Icc_space (is_compact_Icc)

/-- A closed interval in a conditionally complete linear order is compact. -/
@[priority 100]
instance conditionally_complete_linear_order.to_compact_Icc_space
  (α : Type*) [conditionally_complete_linear_order α] [topological_space α] [order_topology α] :
  compact_Icc_space α :=
begin
  refine ⟨λ a b, _⟩,
  cases le_or_lt a b with hab hab, swap, { simp [hab] },
  refine is_compact_iff_ultrafilter_le_nhds.2 (λ f hf, _),
  contrapose! hf,
  rw [le_principal_iff],
  have hpt : ∀ x ∈ Icc a b, {x} ∉ f,
    from λ x hx hxf, hf x hx ((le_pure_iff.2 hxf).trans (pure_le_nhds x)),
  set s := {x ∈ Icc a b | Icc a x ∉ f},
  have hsb : b ∈ upper_bounds s, from λ x hx, hx.1.2,
  have sbd : bdd_above s, from ⟨b, hsb⟩,
  have ha : a ∈ s, by simp [hpt, hab],
  rcases hab.eq_or_lt with rfl|hlt, { exact ha.2 },
  set c := Sup s,
  have hsc : is_lub s c, from is_lub_cSup ⟨a, ha⟩ sbd,
  have hc : c ∈ Icc a b, from ⟨hsc.1 ha, hsc.2 hsb⟩,
  specialize hf c hc,
  have hcs : c ∈ s,
  { cases hc.1.eq_or_lt with heq hlt, { rwa ← heq },
    refine ⟨hc, λ hcf, hf (λ U hU, _)⟩,
    rcases (mem_nhds_within_Iic_iff_exists_Ioc_subset' hlt).1 (mem_nhds_within_of_mem_nhds hU)
      with ⟨x, hxc, hxU⟩,
    rcases ((hsc.frequently_mem ⟨a, ha⟩).and_eventually
      (Ioc_mem_nhds_within_Iic ⟨hxc, le_rfl⟩)).exists
      with ⟨y, ⟨hyab, hyf⟩, hy⟩,
    refine mem_of_superset(f.diff_mem_iff.2 ⟨hcf, hyf⟩) (subset.trans _ hxU),
    rw diff_subset_iff,
    exact subset.trans Icc_subset_Icc_union_Ioc
      (union_subset_union subset.rfl $ Ioc_subset_Ioc_left hy.1.le) },
  cases hc.2.eq_or_lt with heq hlt, { rw ← heq, exact hcs.2 },
  contrapose! hf,
  intros U hU,
  rcases (mem_nhds_within_Ici_iff_exists_mem_Ioc_Ico_subset hlt).1 (mem_nhds_within_of_mem_nhds hU)
    with ⟨y, hxy, hyU⟩,
  refine mem_of_superset _ hyU, clear_dependent U,
  have hy : y ∈ Icc a b, from ⟨hc.1.trans hxy.1.le, hxy.2⟩,
  by_cases hay : Icc a y ∈ f,
  { refine mem_of_superset (f.diff_mem_iff.2 ⟨f.diff_mem_iff.2 ⟨hay, hcs.2⟩, hpt y hy⟩) _,
    rw [diff_subset_iff, union_comm, Ico_union_right hxy.1.le, diff_subset_iff],
    exact Icc_subset_Icc_union_Icc },
  { exact ((hsc.1 ⟨hy, hay⟩).not_lt hxy.1).elim },
end

instance {ι : Type*} {α : ι → Type*} [Π i, preorder (α i)] [Π i, topological_space (α i)]
  [Π i, compact_Icc_space (α i)] : compact_Icc_space (Π i, α i) :=
⟨λ a b, pi_univ_Icc a b ▸ is_compact_univ_pi $ λ i, is_compact_Icc⟩

instance pi.compact_Icc_space' {α β : Type*} [preorder β] [topological_space β]
  [compact_Icc_space β] : compact_Icc_space (α → β) :=
pi.compact_Icc_space

instance {α β : Type*} [preorder α] [topological_space α] [compact_Icc_space α]
  [preorder β] [topological_space β] [compact_Icc_space β] :
  compact_Icc_space (α × β) :=
⟨λ a b, (Icc_prod_eq a b).symm ▸ is_compact_Icc.prod is_compact_Icc⟩

/-- An unordered closed interval in a conditionally complete linear order is compact. -/
lemma is_compact_interval {α : Type*} [conditionally_complete_linear_order α]
  [topological_space α] [order_topology α]{a b : α} : is_compact (interval a b) :=
is_compact_Icc

/-- A complete linear order is a compact space.

We do not register an instance for a `[compact_Icc_space α]` because this would only add instances
for products (indexed or not) of complete linear orders, and we have instances with higher priority
that cover these cases. -/
@[priority 100] -- See note [lower instance priority]
instance compact_space_of_complete_linear_order {α : Type*} [complete_linear_order α]
  [topological_space α] [order_topology α] :
  compact_space α :=
⟨by simp only [← Icc_bot_top, is_compact_Icc]⟩

section

variables {α : Type*} [preorder α] [topological_space α] [compact_Icc_space α]

instance compact_space_Icc (a b : α) : compact_space (Icc a b) :=
is_compact_iff_compact_space.mp is_compact_Icc

end

/-!
### Min and max elements of a compact set
-/

variables {α β : Type*} [conditionally_complete_linear_order α] [topological_space α]
  [order_topology α] [topological_space β]

lemma is_compact.Inf_mem {s : set α} (hs : is_compact s) (ne_s : s.nonempty) :
  Inf s ∈ s :=
hs.is_closed.cInf_mem ne_s hs.bdd_below

lemma is_compact.Sup_mem {s : set α} (hs : is_compact s) (ne_s : s.nonempty) :
  Sup s ∈ s :=
@is_compact.Inf_mem (order_dual α) _ _ _ _ hs ne_s

lemma is_compact.is_glb_Inf {s : set α} (hs : is_compact s) (ne_s : s.nonempty) :
  is_glb s (Inf s) :=
is_glb_cInf ne_s hs.bdd_below

lemma is_compact.is_lub_Sup {s : set α} (hs : is_compact s) (ne_s : s.nonempty) :
  is_lub s (Sup s) :=
@is_compact.is_glb_Inf (order_dual α) _ _ _ _ hs ne_s

lemma is_compact.is_least_Inf {s : set α} (hs : is_compact s) (ne_s : s.nonempty) :
  is_least s (Inf s) :=
⟨hs.Inf_mem ne_s, (hs.is_glb_Inf ne_s).1⟩

lemma is_compact.is_greatest_Sup {s : set α} (hs : is_compact s) (ne_s : s.nonempty) :
  is_greatest s (Sup s) :=
@is_compact.is_least_Inf (order_dual α) _ _ _ _ hs ne_s

lemma is_compact.exists_is_least {s : set α} (hs : is_compact s) (ne_s : s.nonempty) :
  ∃ x, is_least s x :=
⟨_, hs.is_least_Inf ne_s⟩

lemma is_compact.exists_is_greatest {s : set α} (hs : is_compact s) (ne_s : s.nonempty) :
  ∃ x, is_greatest s x :=
⟨_, hs.is_greatest_Sup ne_s⟩

lemma is_compact.exists_is_glb {s : set α} (hs : is_compact s) (ne_s : s.nonempty) :
  ∃ x ∈ s, is_glb s x :=
⟨_, hs.Inf_mem ne_s, hs.is_glb_Inf ne_s⟩

lemma is_compact.exists_is_lub {s : set α} (hs : is_compact s) (ne_s : s.nonempty) :
  ∃ x ∈ s, is_lub s x :=
⟨_, hs.Sup_mem ne_s, hs.is_lub_Sup ne_s⟩

lemma is_compact.exists_Inf_image_eq {s : set β} (hs : is_compact s) (ne_s : s.nonempty)
  {f : β → α} (hf : continuous_on f s) :
  ∃ x ∈ s,  Inf (f '' s) = f x :=
let ⟨x, hxs, hx⟩ := (hs.image_of_continuous_on hf).Inf_mem (ne_s.image f)
in ⟨x, hxs, hx.symm⟩

lemma is_compact.exists_Sup_image_eq :
  ∀ {s : set β}, is_compact s → s.nonempty → ∀ {f : β → α}, continuous_on f s →
  ∃ x ∈ s,  Sup (f '' s) = f x :=
@is_compact.exists_Inf_image_eq (order_dual α) _ _ _ _ _

lemma eq_Icc_of_connected_compact {s : set α} (h₁ : is_connected s) (h₂ : is_compact s) :
  s = Icc (Inf s) (Sup s) :=
eq_Icc_cInf_cSup_of_connected_bdd_closed h₁ h₂.bdd_below h₂.bdd_above h₂.is_closed

/-!
### Extreme value theorem
-/

/-- The **extreme value theorem**: a continuous function realizes its minimum on a compact set. -/
lemma is_compact.exists_forall_le {s : set β} (hs : is_compact s) (ne_s : s.nonempty)
  {f : β → α} (hf : continuous_on f s) :
  ∃x∈s, ∀y∈s, f x ≤ f y :=
begin
  rcases (hs.image_of_continuous_on hf).exists_is_least (ne_s.image f)
    with ⟨_, ⟨x, hxs, rfl⟩, hx⟩,
  exact ⟨x, hxs, ball_image_iff.1 hx⟩
end

/-- The **extreme value theorem**: a continuous function realizes its maximum on a compact set. -/
lemma is_compact.exists_forall_ge :
  ∀ {s : set β}, is_compact s → s.nonempty → ∀ {f : β → α}, continuous_on f s →
  ∃x∈s, ∀y∈s, f y ≤ f x :=
@is_compact.exists_forall_le (order_dual α) _ _ _ _ _

/-- The **extreme value theorem**: if a continuous function `f` tends to infinity away from compact
sets, then it has a global minimum. -/
lemma continuous.exists_forall_le [nonempty β] {f : β → α}
  (hf : continuous f) (hlim : tendsto f (cocompact β) at_top) :
  ∃ x, ∀ y, f x ≤ f y :=
begin
  inhabit β,
  obtain ⟨s : set β, hsc : is_compact s, hsf : ∀ x ∉ s, f (default β) ≤ f x⟩ :=
    (has_basis_cocompact.tendsto_iff at_top_basis).1 hlim (f $ default β) trivial,
  obtain ⟨x, -, hx⟩ : ∃ x ∈ insert (default β) s, ∀ y ∈ insert (default β) s, f x ≤ f y :=
    (hsc.insert (default β)).exists_forall_le (nonempty_insert _ _) hf.continuous_on,
  refine ⟨x, λ y, _⟩,
  by_cases hy : y ∈ s,
  exacts [hx y (or.inr hy), (hx _ (or.inl rfl)).trans (hsf y hy)]
end

/-- The **extreme value theorem**: if a continuous function `f` tends to negative infinity away from
compact sets, then it has a global maximum. -/
lemma continuous.exists_forall_ge [nonempty β] {f : β → α}
  (hf : continuous f) (hlim : tendsto f (cocompact β) at_bot) :
  ∃ x, ∀ y, f y ≤ f x :=
@continuous.exists_forall_le (order_dual α) _ _ _ _ _ _ _ hf hlim

/-!
### Image of a closed interval
-/

variables [densely_ordered α] [conditionally_complete_linear_order β] [order_topology β]
  {f : α → β} {a b x y : α}

open_locale interval

lemma continuous_on.image_Icc (hab : a ≤ b) (h : continuous_on f $ Icc a b) :
  f '' Icc a b = Icc (Inf $ f '' Icc a b) (Sup $ f '' Icc a b) :=
eq_Icc_of_connected_compact ⟨(nonempty_Icc.2 hab).image f, is_preconnected_Icc.image f h⟩
  (is_compact_Icc.image_of_continuous_on h)

lemma continuous_on.image_interval_eq_Icc (h : continuous_on f $ [a, b]) :
  f '' [a, b] = Icc (Inf (f '' [a, b])) (Sup (f '' [a, b])) :=
begin
  cases le_total a b with h2 h2,
  { simp_rw [interval_of_le h2] at h ⊢, exact h.image_Icc h2 },
  { simp_rw [interval_of_ge h2] at h ⊢, exact h.image_Icc h2 },
end

lemma continuous_on.image_interval (h : continuous_on f $ [a, b]) :
  f '' [a, b] = [Inf (f '' [a, b]), Sup (f '' [a, b])] :=
begin
  refine h.image_interval_eq_Icc.trans (interval_of_le _).symm,
  refine cInf_le_cSup _ _ (nonempty_interval.image _); rw h.image_interval_eq_Icc,
  exacts [bdd_below_Icc, bdd_above_Icc]
end