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

! This file was ported from Lean 3 source module topology.uniform_space.uniform_convergence_topology
! leanprover-community/mathlib commit ee05e9ce1322178f0c12004eb93c00d2c8c00ed2
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.UniformSpace.UniformConvergence
import Mathbin.Topology.UniformSpace.Pi
import Mathbin.Topology.UniformSpace.Equiv

/-!
# Topology and uniform structure of uniform convergence

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

This files endows `α → β` with the topologies / uniform structures of
- uniform convergence on `α`
- uniform convergence on a specified family `𝔖` of sets of `α`, also called `𝔖`-convergence

Since `α → β` is already endowed with the topologies and uniform structures of pointwise
convergence, we introduce type aliases `uniform_fun α β` (denoted `α →ᵤ β`) and
`uniform_on_fun α β 𝔖` (denoted `α →ᵤ[𝔖] β`) and we actually endow *these* with the structures
of uniform and `𝔖`-convergence respectively.

Usual examples of the second construction include :
- the topology of compact convergence, when `𝔖` is the set of compacts of `α`
- the strong topology on the dual of a topological vector space (TVS) `E`, when `𝔖` is the set of
  Von Neuman bounded subsets of `E`
- the weak-* topology on the dual of a TVS `E`, when `𝔖` is the set of singletons of `E`.

This file contains a lot of technical facts, so it is heavily commented, proofs included!

## Main definitions

* `uniform_fun.gen`: basis sets for the uniformity of uniform convergence. These are sets
  of the form `S(V) := {(f, g) | ∀ x : α, (f x, g x) ∈ V}` for some `V : set (β × β)`
* `uniform_fun.uniform_space`: uniform structure of uniform convergence. This is the
  `uniform_space` on `α →ᵤ β` whose uniformity is generated by the sets `S(V)` for `V ∈ 𝓤 β`.
  We will denote this uniform space as `𝒰(α, β, uβ)`, both in the comments and as a local notation
  in the Lean code, where `uβ` is the uniform space structure on `β`.
  This is declared as an instance on `α →ᵤ β`.
* `uniform_on_fun.uniform_space`: uniform structure of `𝔖`-convergence, where
  `𝔖 : set (set α)`. This is the infimum, for `S ∈ 𝔖`, of the pullback of `𝒰 S β` by the map of
  restriction to `S`. We will denote it `𝒱(α, β, 𝔖, uβ)`, where `uβ` is the uniform space structure
  on `β`.
  This is declared as an instance on `α →ᵤ[𝔖] β`.

## Main statements

### Basic properties

* `uniform_fun.uniform_continuous_eval`: evaluation is uniformly continuous on `α →ᵤ β`.
* `uniform_fun.t2_space`: the topology of uniform convergence on `α →ᵤ β` is T₂ if
  `β` is T₂.
* `uniform_fun.tendsto_iff_tendsto_uniformly`: `𝒰(α, β, uβ)` is
  indeed the uniform structure of uniform convergence
* `uniform_on_fun.uniform_continuous_eval_of_mem`: evaluation at a point contained in a
  set of `𝔖` is uniformly continuous on `α →ᵤ[𝔖] β`
* `uniform_on_fun.t2_space_of_covering`: the topology of `𝔖`-convergence on `α →ᵤ[𝔖] β` is T₂ if
  `β` is T₂ and `𝔖` covers `α`
* `uniform_on_fun.tendsto_iff_tendsto_uniformly_on`:
  `𝒱(α, β, 𝔖 uβ)` is indeed the uniform structure of `𝔖`-convergence

### Functoriality and compatibility with product of uniform spaces

In order to avoid the need for filter bases as much as possible when using these definitions,
we develop an extensive API for manipulating these structures abstractly. As usual in the topology
section of mathlib, we first state results about the complete lattices of `uniform_space`s on
fixed types, and then we use these to deduce categorical-like results about maps between two
uniform spaces.

We only describe these in the harder case of `𝔖`-convergence, as the names of the corresponding
results for uniform convergence can easily be guessed.

#### Order statements

* `uniform_on_fun.mono`: let `u₁`, `u₂` be two uniform structures on `γ` and
  `𝔖₁ 𝔖₂ : set (set α)`. If `u₁ ≤ u₂` and `𝔖₂ ⊆ 𝔖₁` then `𝒱(α, γ, 𝔖₁, u₁) ≤ 𝒱(α, γ, 𝔖₂, u₂)`.
* `uniform_on_fun.infi_eq`: if `u` is a family of uniform structures on `γ`, then
  `𝒱(α, γ, 𝔖, (⨅ i, u i)) = ⨅ i, 𝒱(α, γ, 𝔖, u i)`.
* `uniform_on_fun.comap_eq`: if `u` is a uniform structures on `β` and `f : γ → β`, then
  `𝒱(α, γ, 𝔖, comap f u) = comap (λ g, f ∘ g) 𝒱(α, γ, 𝔖, u₁)`.

An interesting note about these statements is that they are proved without ever unfolding the basis
definition of the uniform structure of uniform convergence! Instead, we build a
(not very interesting) Galois connection `uniform_convergence.gc` and then rely on the Galois
connection API to do most of the work.

#### Morphism statements (unbundled)

* `uniform_on_fun.postcomp_uniform_continuous`: if `f : γ → β` is uniformly
  continuous, then `(λ g, f ∘ g) : (α →ᵤ[𝔖] γ) → (α →ᵤ[𝔖] β)` is uniformly continuous.
* `uniform_on_fun.postcomp_uniform_inducing`: if `f : γ → β` is a uniform
  inducing, then `(λ g, f ∘ g) : (α →ᵤ[𝔖] γ) → (α →ᵤ[𝔖] β)` is a uniform inducing.
* `uniform_on_fun.precomp_uniform_continuous`: let `f : γ → α`, `𝔖 : set (set α)`,
  `𝔗 : set (set γ)`, and assume that `∀ T ∈ 𝔗, f '' T ∈ 𝔖`. Then, the function
  `(λ g, g ∘ f) : (α →ᵤ[𝔖] β) → (γ →ᵤ[𝔗] β)` is uniformly continuous.

#### Isomorphism statements (bundled)

* `uniform_on_fun.congr_right`: turn a uniform isomorphism `γ ≃ᵤ β` into a uniform isomorphism
  `(α →ᵤ[𝔖] γ) ≃ᵤ (α →ᵤ[𝔖] β)` by post-composing.
* `uniform_on_fun.congr_left`: turn a bijection `e : γ ≃ α` such that we have both
  `∀ T ∈ 𝔗, e '' T ∈ 𝔖` and `∀ S ∈ 𝔖, e ⁻¹' S ∈ 𝔗` into a uniform isomorphism
  `(γ →ᵤ[𝔗] β) ≃ᵤ (α →ᵤ[𝔖] β)` by pre-composing.
* `uniform_on_fun.uniform_equiv_Pi_comm`: the natural bijection between `α → Π i, δ i`
  and `Π i, α → δ i`, upgraded to a uniform isomorphism between `α →ᵤ[𝔖] (Π i, δ i)` and
  `Π i, α →ᵤ[𝔖] δ i`.

#### Important use cases

* If `G` is a uniform group, then `α →ᵤ[𝔖] G` is a uniform group: since `(/) : G × G → G` is
  uniformly continuous, `uniform_convergence_on.postcomp_uniform_continuous` tells us that
  `((/) ∘ —) : (α →ᵤ[𝔖] G × G) → (α →ᵤ[𝔖] G)` is uniformly continuous. By precomposing with
  `uniform_convergence_on.uniform_equiv_prod_arrow`, this gives that
  `(/) : (α →ᵤ[𝔖] G) × (α →ᵤ[𝔖] G) → (α →ᵤ[𝔖] G)` is also uniformly continuous
* The transpose of a continuous linear map is continuous for the strong topologies: since
  continuous linear maps are uniformly continuous and map bounded sets to bounded sets,
  this is just a special case of `uniform_convergence_on.precomp_uniform_continuous`.

## TODO

* Show that the uniform structure of `𝔖`-convergence is exactly the structure of `𝔖'`-convergence,
  where `𝔖'` is the ***noncovering*** bornology (i.e ***not*** what `bornology` currently refers
  to in mathlib) generated by `𝔖`.

## References

* [N. Bourbaki, *General Topology, Chapter X*][bourbaki1966]

## Tags

uniform convergence
-/

noncomputable section

open Topology Classical uniformity Filter

open Set Filter

section TypeAlias

#print UniformFun /-
/-- The type of functions from `α` to `β` equipped with the uniform structure and topology of
uniform convergence. We denote it `α →ᵤ β`. -/
def UniformFun (α β : Type _) :=
  α → β
#align uniform_fun UniformFun
-/

#print UniformOnFun /-
/-- The type of functions from `α` to `β` equipped with the uniform structure and topology of
uniform convergence on some family `𝔖` of subsets of `α`. We denote it `α →ᵤ[𝔖] β`. -/
@[nolint unused_arguments]
def UniformOnFun (α β : Type _) (𝔖 : Set (Set α)) :=
  α → β
#align uniform_on_fun UniformOnFun
-/

-- mathport name: «expr →ᵤ »
scoped[UniformConvergence] notation:25 α " →ᵤ " β:0 => UniformFun α β

-- mathport name: «expr →ᵤ[ ] »
scoped[UniformConvergence] notation:25 α " →ᵤ[" 𝔖 "] " β:0 => UniformOnFun α β 𝔖

-- mathport name: «exprλᵘ , »
scoped[UniformConvergence] notation3"λᵘ "(...)", "r:(scoped p => UniformFun.ofFun p) => r

-- mathport name: «exprλᵘ[ ] , »
scoped[UniformConvergence] notation3"λᵘ["𝔖"] "(...)", "r:(scoped p => UniformFun.ofFun p) => r

instance {α β} [Nonempty β] : Nonempty (α →ᵤ β) :=
  Pi.nonempty

instance {α β 𝔖} [Nonempty β] : Nonempty (α →ᵤ[𝔖] β) :=
  Pi.nonempty

#print UniformFun.ofFun /-
/-- Reinterpret `f : α → β` as an element of `α →ᵤ β`. -/
def UniformFun.ofFun {α β} : (α → β) ≃ (α →ᵤ β) :=
  ⟨fun x => x, fun x => x, fun x => rfl, fun x => rfl⟩
#align uniform_fun.of_fun UniformFun.ofFun
-/

#print UniformOnFun.ofFun /-
/-- Reinterpret `f : α → β` as an element of `α →ᵤ[𝔖] β`. -/
def UniformOnFun.ofFun {α β} (𝔖) : (α → β) ≃ (α →ᵤ[𝔖] β) :=
  ⟨fun x => x, fun x => x, fun x => rfl, fun x => rfl⟩
#align uniform_on_fun.of_fun UniformOnFun.ofFun
-/

#print UniformFun.toFun /-
/-- Reinterpret `f : α →ᵤ β` as an element of `α → β`. -/
def UniformFun.toFun {α β} : (α →ᵤ β) ≃ (α → β) :=
  UniformFun.ofFun.symm
#align uniform_fun.to_fun UniformFun.toFun
-/

#print UniformOnFun.toFun /-
/-- Reinterpret `f : α →ᵤ[𝔖] β` as an element of `α → β`. -/
def UniformOnFun.toFun {α β} (𝔖) : (α →ᵤ[𝔖] β) ≃ (α → β) :=
  (UniformOnFun.ofFun 𝔖).symm
#align uniform_on_fun.to_fun UniformOnFun.toFun
-/

-- Note: we don't declare a `has_coe_to_fun` instance because Lean wouldn't insert it when writing
-- `f x` (because of definitional equality with `α → β`).
end TypeAlias

open UniformConvergence

namespace UniformFun

variable (α β : Type _) {γ ι : Type _}

variable {s s' : Set α} {x : α} {p : Filter ι} {g : ι → α}

#print UniformFun.gen /-
/-- Basis sets for the uniformity of uniform convergence: `gen α β V` is the set of pairs `(f, g)`
of functions `α →ᵤ β` such that `∀ x, (f x, g x) ∈ V`. -/
protected def gen (V : Set (β × β)) : Set ((α →ᵤ β) × (α →ᵤ β)) :=
  { uv : (α →ᵤ β) × (α →ᵤ β) | ∀ x, (uv.1 x, uv.2 x) ∈ V }
#align uniform_fun.gen UniformFun.gen
-/

#print UniformFun.isBasis_gen /-
/-- If `𝓕` is a filter on `β × β`, then the set of all `uniform_convergence.gen α β V` for
`V ∈ 𝓕` is a filter basis on `(α →ᵤ β) × (α →ᵤ β)`. This will only be applied to `𝓕 = 𝓤 β` when
`β` is equipped with a `uniform_space` structure, but it is useful to define it for any filter in
order to be able to state that it has a lower adjoint (see `uniform_convergence.gc`). -/
protected theorem isBasis_gen (𝓑 : Filter <| β × β) :
    IsBasis (fun V : Set (β × β) => V ∈ 𝓑) (UniformFun.gen α β) :=
  ⟨⟨univ, univ_mem⟩, fun U V hU hV =>
    ⟨U ∩ V, inter_mem hU hV, fun uv huv => ⟨fun x => (huv x).left, fun x => (huv x).right⟩⟩⟩
#align uniform_fun.is_basis_gen UniformFun.isBasis_gen
-/

#print UniformFun.basis /-
/-- For `𝓕 : filter (β × β)`, this is the set of all `uniform_convergence.gen α β V` for
`V ∈ 𝓕` as a bundled `filter_basis` over `(α →ᵤ β) × (α →ᵤ β)`. This will only be applied to
`𝓕 = 𝓤 β` when `β` is equipped with a `uniform_space` structure, but it is useful to define it for
any filter in order to be able to state that it has a lower adjoint
(see `uniform_convergence.gc`). -/
protected def basis (𝓕 : Filter <| β × β) : FilterBasis ((α →ᵤ β) × (α →ᵤ β)) :=
  (UniformFun.isBasis_gen α β 𝓕).FilterBasis
#align uniform_fun.basis UniformFun.basis
-/

#print UniformFun.filter /-
/-- For `𝓕 : filter (β × β)`, this is the filter generated by the filter basis
`uniform_convergence.basis α β 𝓕`. For `𝓕 = 𝓤 β`, this will be the uniformity of uniform
convergence on `α`. -/
protected def filter (𝓕 : Filter <| β × β) : Filter ((α →ᵤ β) × (α →ᵤ β)) :=
  (UniformFun.basis α β 𝓕).filterₓ
#align uniform_fun.filter UniformFun.filter
-/

-- mathport name: exprΦ
local notation "Φ" => fun (α β : Type _) (uvx : ((α →ᵤ β) × (α →ᵤ β)) × α) =>
  (uvx.1.1 uvx.2, uvx.1.2 uvx.2)

-- mathport name: exprlower_adjoint
/- This is a lower adjoint to `uniform_convergence.filter` (see `uniform_convergence.gc`).
The exact definition of the lower adjoint `l` is not interesting; we will only use that it exists
(in `uniform_convergence.mono` and `uniform_convergence.infi_eq`) and that
`l (filter.map (prod.map f f) 𝓕) = filter.map (prod.map ((∘) f) ((∘) f)) (l 𝓕)` for each
`𝓕 : filter (γ × γ)` and `f : γ → α` (in `uniform_convergence.comap_eq`). -/
local notation "lower_adjoint" => fun 𝓐 => map (Φ α β) (𝓐 ×ᶠ ⊤)

/- warning: uniform_fun.gc -> UniformFun.gc is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}), GaloisConnection.{max u1 u2, u2} (Filter.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β))) (Filter.{u2} (Prod.{u2, u2} β β)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β))) (Filter.partialOrder.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β)))) (PartialOrder.toPreorder.{u2} (Filter.{u2} (Prod.{u2, u2} β β)) (Filter.partialOrder.{u2} (Prod.{u2, u2} β β))) (fun (𝓐 : Filter.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β))) => Filter.map.{max u1 u2, u2} (Prod.{max u1 u2, u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β)) α) (Prod.{u2, u2} β β) ((fun (α : Type.{u1}) (β : Type.{u2}) (uvx : Prod.{max u1 u2, u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β)) α) => Prod.mk.{u2, u2} β β (Prod.fst.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β) (Prod.fst.{max u1 u2, u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β)) α uvx) (Prod.snd.{max u1 u2, u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β)) α uvx)) (Prod.snd.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β) (Prod.fst.{max u1 u2, u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β)) α uvx) (Prod.snd.{max u1 u2, u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β)) α uvx))) α β) (Filter.prod.{max u1 u2, u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β)) α 𝓐 (Top.top.{u1} (Filter.{u1} α) (Filter.hasTop.{u1} α)))) (fun (𝓕 : Filter.{u2} (Prod.{u2, u2} β β)) => UniformFun.filter.{u1, u2} α β 𝓕)
but is expected to have type
  forall (α : Type.{u2}) (β : Type.{u1}), GaloisConnection.{max u2 u1, u1} (Filter.{max u2 u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β))) (Filter.{u1} (Prod.{u1, u1} β β)) (PartialOrder.toPreorder.{max u2 u1} (Filter.{max u2 u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β))) (Filter.instPartialOrderFilter.{max u2 u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β)))) (PartialOrder.toPreorder.{u1} (Filter.{u1} (Prod.{u1, u1} β β)) (Filter.instPartialOrderFilter.{u1} (Prod.{u1, u1} β β))) (fun (𝓐 : Filter.{max u2 u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β))) => Filter.map.{max u2 u1, u1} (Prod.{max u1 u2, u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β)) α) (Prod.{u1, u1} β β) (UniformFun.phi.{u2, u1} α β) (Filter.prod.{max u2 u1, u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β)) α 𝓐 (Top.top.{u2} (Filter.{u2} α) (Filter.instTopFilter.{u2} α)))) (fun (𝓕 : Filter.{u1} (Prod.{u1, u1} β β)) => UniformFun.filter.{u2, u1} α β 𝓕)
Case conversion may be inaccurate. Consider using '#align uniform_fun.gc UniformFun.gcₓ'. -/
/-- The function `uniform_convergence.filter α β : filter (β × β) → filter ((α →ᵤ β) × (α →ᵤ β))`
has a lower adjoint `l` (in the sense of `galois_connection`). The exact definition of `l` is not
interesting; we will only use that it exists (in `uniform_convergence.mono` and
`uniform_convergence.infi_eq`) and that
`l (filter.map (prod.map f f) 𝓕) = filter.map (prod.map ((∘) f) ((∘) f)) (l 𝓕)` for each
`𝓕 : filter (γ × γ)` and `f : γ → α` (in `uniform_convergence.comap_eq`). -/
protected theorem gc : GaloisConnection lower_adjoint fun 𝓕 => UniformFun.filter α β 𝓕 :=
  by
  intro 𝓐 𝓕
  symm
  calc
    𝓐 ≤ UniformFun.filter α β 𝓕 ↔ (UniformFun.basis α β 𝓕).sets ⊆ 𝓐.sets := by
      rw [UniformFun.filter, ← FilterBasis.generate, sets_iff_generate]
    _ ↔ ∀ U ∈ 𝓕, UniformFun.gen α β U ∈ 𝓐 := image_subset_iff
    _ ↔
        ∀ U ∈ 𝓕,
          { uv | ∀ x, (uv, x) ∈ { t : ((α →ᵤ β) × (α →ᵤ β)) × α | (t.1.1 t.2, t.1.2 t.2) ∈ U } } ∈
            𝓐 :=
      Iff.rfl
    _ ↔
        ∀ U ∈ 𝓕,
          { uvx : ((α →ᵤ β) × (α →ᵤ β)) × α | (uvx.1.1 uvx.2, uvx.1.2 uvx.2) ∈ U } ∈
            𝓐 ×ᶠ (⊤ : Filter α) :=
      (forall₂_congr fun U hU => mem_prod_top.symm)
    _ ↔ lower_adjoint 𝓐 ≤ 𝓕 := Iff.rfl

#align uniform_fun.gc UniformFun.gc

variable [UniformSpace β]

#print UniformFun.uniformCore /-
/-- Core of the uniform structure of uniform convergence. -/
protected def uniformCore : UniformSpace.Core (α →ᵤ β) :=
  UniformSpace.Core.mkOfBasis (UniformFun.basis α β (𝓤 β))
    (fun U ⟨V, hV, hVU⟩ f => hVU ▸ fun x => refl_mem_uniformity hV)
    (fun U ⟨V, hV, hVU⟩ =>
      hVU ▸
        ⟨UniformFun.gen α β (Prod.swap ⁻¹' V), ⟨Prod.swap ⁻¹' V, tendsto_swap_uniformity hV, rfl⟩,
          fun uv huv x => huv x⟩)
    fun U ⟨V, hV, hVU⟩ =>
    hVU ▸
      let ⟨W, hW, hWV⟩ := comp_mem_uniformity_sets hV
      ⟨UniformFun.gen α β W, ⟨W, hW, rfl⟩, fun uv ⟨w, huw, hwv⟩ x => hWV ⟨w x, ⟨huw x, hwv x⟩⟩⟩
#align uniform_fun.uniform_core UniformFun.uniformCore
-/

/-- Uniform structure of uniform convergence, declared as an instance on `α →ᵤ β`.
We will denote it `𝒰(α, β, uβ)` in the rest of this file. -/
instance : UniformSpace (α →ᵤ β) :=
  UniformSpace.ofCore (UniformFun.uniformCore α β)

/-- Topology of uniform convergence, declared as an instance on `α →ᵤ β`. -/
instance : TopologicalSpace (α →ᵤ β) :=
  inferInstance

-- mathport name: «expr𝒰( , , )»
local notation "𝒰(" α ", " β ", " u ")" => @UniformFun.uniformSpace α β u

/- warning: uniform_fun.has_basis_uniformity -> UniformFun.hasBasis_uniformity is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}) [_inst_1 : UniformSpace.{u2} β], Filter.HasBasis.{max u1 u2, succ u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β)) (Set.{u2} (Prod.{u2, u2} β β)) (uniformity.{max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.uniformSpace.{u1, u2} α β _inst_1)) (fun (V : Set.{u2} (Prod.{u2, u2} β β)) => Membership.Mem.{u2, u2} (Set.{u2} (Prod.{u2, u2} β β)) (Filter.{u2} (Prod.{u2, u2} β β)) (Filter.hasMem.{u2} (Prod.{u2, u2} β β)) V (uniformity.{u2} β _inst_1)) (UniformFun.gen.{u1, u2} α β)
but is expected to have type
  forall (α : Type.{u2}) (β : Type.{u1}) [_inst_1 : UniformSpace.{u1} β], Filter.HasBasis.{max u2 u1, succ u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β)) (Set.{u1} (Prod.{u1, u1} β β)) (uniformity.{max u1 u2} (UniformFun.{u2, u1} α β) (UniformFun.uniformSpace.{u2, u1} α β _inst_1)) (fun (V : Set.{u1} (Prod.{u1, u1} β β)) => Membership.mem.{u1, u1} (Set.{u1} (Prod.{u1, u1} β β)) (Filter.{u1} (Prod.{u1, u1} β β)) (instMembershipSetFilter.{u1} (Prod.{u1, u1} β β)) V (uniformity.{u1} β _inst_1)) (UniformFun.gen.{u2, u1} α β)
Case conversion may be inaccurate. Consider using '#align uniform_fun.has_basis_uniformity UniformFun.hasBasis_uniformityₓ'. -/
/-- By definition, the uniformity of `α →ᵤ β` admits the family `{(f, g) | ∀ x, (f x, g x) ∈ V}`
for `V ∈ 𝓤 β` as a filter basis. -/
protected theorem hasBasis_uniformity :
    (𝓤 (α →ᵤ β)).HasBasis (fun V => V ∈ 𝓤 β) (UniformFun.gen α β) :=
  (UniformFun.isBasis_gen α β (𝓤 β)).HasBasis
#align uniform_fun.has_basis_uniformity UniformFun.hasBasis_uniformity

#print UniformFun.hasBasis_uniformity_of_basis /-
/-- The uniformity of `α →ᵤ β` admits the family `{(f, g) | ∀ x, (f x, g x) ∈ V}` for `V ∈ 𝓑` as
a filter basis, for any basis `𝓑` of `𝓤 β` (in the case `𝓑 = (𝓤 β).as_basis` this is true by
definition). -/
protected theorem hasBasis_uniformity_of_basis {ι : Sort _} {p : ι → Prop} {s : ι → Set (β × β)}
    (h : (𝓤 β).HasBasis p s) : (𝓤 (α →ᵤ β)).HasBasis p (UniformFun.gen α β ∘ s) :=
  (UniformFun.hasBasis_uniformity α β).to_hasBasis
    (fun U hU =>
      let ⟨i, hi, hiU⟩ := h.mem_iff.mp hU
      ⟨i, hi, fun uv huv x => hiU (huv x)⟩)
    fun i hi => ⟨s i, h.mem_of_mem hi, subset_refl _⟩
#align uniform_fun.has_basis_uniformity_of_basis UniformFun.hasBasis_uniformity_of_basis
-/

/- warning: uniform_fun.has_basis_nhds_of_basis -> UniformFun.hasBasis_nhds_of_basis is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}) {ι : Type.{u3}} [_inst_1 : UniformSpace.{u2} β] (f : UniformFun.{u1, u2} α β) {p : ι -> Prop} {s : ι -> (Set.{u2} (Prod.{u2, u2} β β))}, (Filter.HasBasis.{u2, succ u3} (Prod.{u2, u2} β β) ι (uniformity.{u2} β _inst_1) p s) -> (Filter.HasBasis.{max u1 u2, succ u3} (UniformFun.{u1, u2} α β) ι (nhds.{max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.topologicalSpace.{u1, u2} α β _inst_1) f) p (fun (i : ι) => setOf.{max u1 u2} (UniformFun.{u1, u2} α β) (fun (g : UniformFun.{u1, u2} α β) => Membership.Mem.{max u1 u2, max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β)) (Set.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β))) (Set.hasMem.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β))) (Prod.mk.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β) f g) (UniformFun.gen.{u1, u2} α β (s i)))))
but is expected to have type
  forall (α : Type.{u3}) (β : Type.{u2}) {ι : Type.{u1}} [_inst_1 : UniformSpace.{u2} β] (f : UniformFun.{u3, u2} α β) {p : ι -> Prop} {s : ι -> (Set.{u2} (Prod.{u2, u2} β β))}, (Filter.HasBasis.{u2, succ u1} (Prod.{u2, u2} β β) ι (uniformity.{u2} β _inst_1) p s) -> (Filter.HasBasis.{max u3 u2, succ u1} (UniformFun.{u3, u2} α β) ι (nhds.{max u3 u2} (UniformFun.{u3, u2} α β) (UniformFun.topologicalSpace.{u3, u2} α β _inst_1) f) p (fun (i : ι) => setOf.{max u3 u2} (UniformFun.{u3, u2} α β) (fun (g : UniformFun.{u3, u2} α β) => Membership.mem.{max u3 u2, max u3 u2} (Prod.{max u3 u2, max u3 u2} (UniformFun.{u3, u2} α β) (UniformFun.{u3, u2} α β)) (Set.{max u2 u3} (Prod.{max u2 u3, max u2 u3} (UniformFun.{u3, u2} α β) (UniformFun.{u3, u2} α β))) (Set.instMembershipSet.{max u3 u2} (Prod.{max u2 u3, max u2 u3} (UniformFun.{u3, u2} α β) (UniformFun.{u3, u2} α β))) (Prod.mk.{max u3 u2, max u3 u2} (UniformFun.{u3, u2} α β) (UniformFun.{u3, u2} α β) f g) (UniformFun.gen.{u3, u2} α β (s i)))))
Case conversion may be inaccurate. Consider using '#align uniform_fun.has_basis_nhds_of_basis UniformFun.hasBasis_nhds_of_basisₓ'. -/
/-- For `f : α →ᵤ β`, `𝓝 f` admits the family `{g | ∀ x, (f x, g x) ∈ V}` for `V ∈ 𝓑` as a filter
basis, for any basis `𝓑` of `𝓤 β`. -/
protected theorem hasBasis_nhds_of_basis (f) {p : ι → Prop} {s : ι → Set (β × β)}
    (h : HasBasis (𝓤 β) p s) :
    (𝓝 f).HasBasis p fun i => { g | (f, g) ∈ UniformFun.gen α β (s i) } :=
  nhds_basis_uniformity' (UniformFun.hasBasis_uniformity_of_basis α β h)
#align uniform_fun.has_basis_nhds_of_basis UniformFun.hasBasis_nhds_of_basis

/- warning: uniform_fun.has_basis_nhds -> UniformFun.hasBasis_nhds is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}) [_inst_1 : UniformSpace.{u2} β] (f : UniformFun.{u1, u2} α β), Filter.HasBasis.{max u1 u2, succ u2} (UniformFun.{u1, u2} α β) (Set.{u2} (Prod.{u2, u2} β β)) (nhds.{max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.topologicalSpace.{u1, u2} α β _inst_1) f) (fun (V : Set.{u2} (Prod.{u2, u2} β β)) => Membership.Mem.{u2, u2} (Set.{u2} (Prod.{u2, u2} β β)) (Filter.{u2} (Prod.{u2, u2} β β)) (Filter.hasMem.{u2} (Prod.{u2, u2} β β)) V (uniformity.{u2} β _inst_1)) (fun (V : Set.{u2} (Prod.{u2, u2} β β)) => setOf.{max u1 u2} (UniformFun.{u1, u2} α β) (fun (g : UniformFun.{u1, u2} α β) => Membership.Mem.{max u1 u2, max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β)) (Set.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β))) (Set.hasMem.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β))) (Prod.mk.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u2} α β) f g) (UniformFun.gen.{u1, u2} α β V)))
but is expected to have type
  forall (α : Type.{u2}) (β : Type.{u1}) [_inst_1 : UniformSpace.{u1} β] (f : UniformFun.{u2, u1} α β), Filter.HasBasis.{max u2 u1, succ u1} (UniformFun.{u2, u1} α β) (Set.{u1} (Prod.{u1, u1} β β)) (nhds.{max u2 u1} (UniformFun.{u2, u1} α β) (UniformFun.topologicalSpace.{u2, u1} α β _inst_1) f) (fun (V : Set.{u1} (Prod.{u1, u1} β β)) => Membership.mem.{u1, u1} (Set.{u1} (Prod.{u1, u1} β β)) (Filter.{u1} (Prod.{u1, u1} β β)) (instMembershipSetFilter.{u1} (Prod.{u1, u1} β β)) V (uniformity.{u1} β _inst_1)) (fun (V : Set.{u1} (Prod.{u1, u1} β β)) => setOf.{max u2 u1} (UniformFun.{u2, u1} α β) (fun (g : UniformFun.{u2, u1} α β) => Membership.mem.{max u2 u1, max u2 u1} (Prod.{max u2 u1, max u2 u1} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β)) (Set.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β))) (Set.instMembershipSet.{max u2 u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β))) (Prod.mk.{max u2 u1, max u2 u1} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β) f g) (UniformFun.gen.{u2, u1} α β V)))
Case conversion may be inaccurate. Consider using '#align uniform_fun.has_basis_nhds UniformFun.hasBasis_nhdsₓ'. -/
/-- For `f : α →ᵤ β`, `𝓝 f` admits the family `{g | ∀ x, (f x, g x) ∈ V}` for `V ∈ 𝓤 β` as a
filter basis. -/
protected theorem hasBasis_nhds (f) :
    (𝓝 f).HasBasis (fun V => V ∈ 𝓤 β) fun V => { g | (f, g) ∈ UniformFun.gen α β V } :=
  UniformFun.hasBasis_nhds_of_basis α β f (Filter.basis_sets _)
#align uniform_fun.has_basis_nhds UniformFun.hasBasis_nhds

variable {α}

/- warning: uniform_fun.uniform_continuous_eval -> UniformFun.uniformContinuous_eval is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (β : Type.{u2}) [_inst_1 : UniformSpace.{u2} β] (x : α), UniformContinuous.{max u1 u2, u2} (UniformFun.{u1, u2} α β) β (UniformFun.uniformSpace.{u1, u2} α β _inst_1) _inst_1 (Function.comp.{max (succ u1) (succ u2), max (succ u1) (succ u2), succ u2} (UniformFun.{u1, u2} α β) (α -> β) β (Function.eval.{succ u1, succ u2} α (fun (x : α) => β) x) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u1, u2} α β) (α -> β)) (fun (_x : Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u1, u2} α β) (α -> β)) => (UniformFun.{u1, u2} α β) -> α -> β) (Equiv.hasCoeToFun.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u1, u2} α β) (α -> β)) (UniformFun.toFun.{u1, u2} α β)))
but is expected to have type
  forall {α : Type.{u2}} (β : Type.{u1}) [_inst_1 : UniformSpace.{u1} β] (x : α), UniformContinuous.{max u2 u1, u1} (UniformFun.{u2, u1} α β) β (UniformFun.uniformSpace.{u2, u1} α β _inst_1) _inst_1 (Function.comp.{max (succ u2) (succ u1), max (succ u2) (succ u1), succ u1} (UniformFun.{u2, u1} α β) (α -> β) β (Function.eval.{succ u2, succ u1} α (fun (x : α) => β) x) (FunLike.coe.{max (succ u1) (succ u2), max (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u2, u1} α β) (α -> β)) (UniformFun.{u2, u1} α β) (fun (_x : UniformFun.{u2, u1} α β) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : UniformFun.{u2, u1} α β) => α -> β) _x) (Equiv.instFunLikeEquiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u2, u1} α β) (α -> β)) (UniformFun.toFun.{u2, u1} α β)))
Case conversion may be inaccurate. Consider using '#align uniform_fun.uniform_continuous_eval UniformFun.uniformContinuous_evalₓ'. -/
/-- Evaluation at a fixed point is uniformly continuous on `α →ᵤ β`. -/
theorem uniformContinuous_eval (x : α) :
    UniformContinuous (Function.eval x ∘ toFun : (α →ᵤ β) → β) :=
  by
  change _ ≤ _
  rw [map_le_iff_le_comap,
    (UniformFun.hasBasis_uniformity α β).le_basis_iffₓ ((𝓤 _).basis_sets.comap _)]
  exact fun U hU => ⟨U, hU, fun uv huv => huv x⟩
#align uniform_fun.uniform_continuous_eval UniformFun.uniformContinuous_eval

variable {β}

/- warning: uniform_fun.mono -> UniformFun.mono is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {γ : Type.{u2}}, Monotone.{u2, max u1 u2} (UniformSpace.{u2} γ) (UniformSpace.{max u1 u2} (UniformFun.{u1, u2} α γ)) (PartialOrder.toPreorder.{u2} (UniformSpace.{u2} γ) (UniformSpace.partialOrder.{u2} γ)) (PartialOrder.toPreorder.{max u1 u2} (UniformSpace.{max u1 u2} (UniformFun.{u1, u2} α γ)) (UniformSpace.partialOrder.{max u1 u2} (UniformFun.{u1, u2} α γ))) (UniformFun.uniformSpace.{u1, u2} α γ)
but is expected to have type
  forall {α : Type.{u1}} {γ : Type.{u2}}, Monotone.{u2, max u1 u2} (UniformSpace.{u2} γ) (UniformSpace.{max u2 u1} (UniformFun.{u1, u2} α γ)) (PartialOrder.toPreorder.{u2} (UniformSpace.{u2} γ) (instPartialOrderUniformSpace.{u2} γ)) (PartialOrder.toPreorder.{max u1 u2} (UniformSpace.{max u2 u1} (UniformFun.{u1, u2} α γ)) (instPartialOrderUniformSpace.{max u1 u2} (UniformFun.{u1, u2} α γ))) (UniformFun.uniformSpace.{u1, u2} α γ)
Case conversion may be inaccurate. Consider using '#align uniform_fun.mono UniformFun.monoₓ'. -/
/-- If `u₁` and `u₂` are two uniform structures on `γ` and `u₁ ≤ u₂`, then
`𝒰(α, γ, u₁) ≤ 𝒰(α, γ, u₂)`. -/
protected theorem mono : Monotone (@UniformFun.uniformSpace α γ) := fun u₁ u₂ hu =>
  (UniformFun.gc α γ).monotone_u hu
#align uniform_fun.mono UniformFun.mono

/- warning: uniform_fun.infi_eq -> UniformFun.infᵢ_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {γ : Type.{u2}} {ι : Type.{u3}} {u : ι -> (UniformSpace.{u2} γ)}, Eq.{succ (max u1 u2)} (UniformSpace.{max u1 u2} (UniformFun.{u1, u2} α γ)) (UniformFun.uniformSpace.{u1, u2} α γ (infᵢ.{u2, succ u3} (UniformSpace.{u2} γ) (UniformSpace.hasInf.{u2} γ) ι (fun (i : ι) => u i))) (infᵢ.{max u1 u2, succ u3} (UniformSpace.{max u1 u2} (UniformFun.{u1, u2} α γ)) (UniformSpace.hasInf.{max u1 u2} (UniformFun.{u1, u2} α γ)) ι (fun (i : ι) => UniformFun.uniformSpace.{u1, u2} α γ (u i)))
but is expected to have type
  forall {α : Type.{u2}} {γ : Type.{u3}} {ι : Type.{u1}} {u : ι -> (UniformSpace.{u3} γ)}, Eq.{max (succ u2) (succ u3)} (UniformSpace.{max u3 u2} (UniformFun.{u2, u3} α γ)) (UniformFun.uniformSpace.{u2, u3} α γ (infᵢ.{u3, succ u1} (UniformSpace.{u3} γ) (instInfSetUniformSpace.{u3} γ) ι (fun (i : ι) => u i))) (infᵢ.{max u2 u3, succ u1} (UniformSpace.{max u3 u2} (UniformFun.{u2, u3} α γ)) (instInfSetUniformSpace.{max u2 u3} (UniformFun.{u2, u3} α γ)) ι (fun (i : ι) => UniformFun.uniformSpace.{u2, u3} α γ (u i)))
Case conversion may be inaccurate. Consider using '#align uniform_fun.infi_eq UniformFun.infᵢ_eqₓ'. -/
/-- If `u` is a family of uniform structures on `γ`, then
`𝒰(α, γ, (⨅ i, u i)) = ⨅ i, 𝒰(α, γ, u i)`. -/
protected theorem infᵢ_eq {u : ι → UniformSpace γ} : 𝒰(α, γ, ⨅ i, u i) = ⨅ i, 𝒰(α, γ, u i) :=
  by
  -- This follows directly from the fact that the upper adjoint in a Galois connection maps
  -- infimas to infimas.
  ext : 1
  change UniformFun.filter α γ 𝓤[⨅ i, u i] = 𝓤[⨅ i, 𝒰(α, γ, u i)]
  rw [infᵢ_uniformity, infᵢ_uniformity]
  exact (UniformFun.gc α γ).u_infᵢ
#align uniform_fun.infi_eq UniformFun.infᵢ_eq

#print UniformFun.inf_eq /-
/-- If `u₁` and `u₂` are two uniform structures on `γ`, then
`𝒰(α, γ, u₁ ⊓ u₂) = 𝒰(α, γ, u₁) ⊓ 𝒰(α, γ, u₂)`. -/
protected theorem inf_eq {u₁ u₂ : UniformSpace γ} : 𝒰(α, γ, u₁ ⊓ u₂) = 𝒰(α, γ, u₁) ⊓ 𝒰(α, γ, u₂) :=
  by
  -- This follows directly from the fact that the upper adjoint in a Galois connection maps
  -- infimas to infimas.
  rw [inf_eq_infᵢ, inf_eq_infᵢ, UniformFun.infᵢ_eq]
  refine' infᵢ_congr fun i => _
  cases i <;> rfl
#align uniform_fun.inf_eq UniformFun.inf_eq
-/

/- warning: uniform_fun.comap_eq -> UniformFun.comap_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : UniformSpace.{u2} β] {f : γ -> β}, Eq.{succ (max u1 u3)} (UniformSpace.{max u1 u3} (UniformFun.{u1, u3} α γ)) (UniformFun.uniformSpace.{u1, u3} α γ (UniformSpace.comap.{u3, u2} γ β f _inst_1)) (UniformSpace.comap.{max u1 u3, max u1 u2} (UniformFun.{u1, u3} α γ) (UniformFun.{u1, u2} α β) (Function.comp.{succ u1, succ u3, succ u2} α γ β f) (UniformFun.uniformSpace.{u1, u2} α β _inst_1))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u1}} {γ : Type.{u2}} [_inst_1 : UniformSpace.{u1} β] {f : γ -> β}, Eq.{max (succ u3) (succ u2)} (UniformSpace.{max u2 u3} (UniformFun.{u3, u2} α γ)) (UniformFun.uniformSpace.{u3, u2} α γ (UniformSpace.comap.{u2, u1} γ β f _inst_1)) (UniformSpace.comap.{max u3 u2, max u3 u1} (α -> γ) (α -> β) (fun (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.4219 : α -> γ) => Function.comp.{succ u3, succ u2, succ u1} α γ β f x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.4219) (UniformFun.uniformSpace.{u3, u1} α β _inst_1))
Case conversion may be inaccurate. Consider using '#align uniform_fun.comap_eq UniformFun.comap_eqₓ'. -/
/-- If `u` is a uniform structures on `β` and `f : γ → β`, then
`𝒰(α, γ, comap f u) = comap (λ g, f ∘ g) 𝒰(α, γ, u₁)`. -/
protected theorem comap_eq {f : γ → β} :
    𝒰(α, γ, ‹UniformSpace β›.comap f) = 𝒰(α, β, _).comap ((· ∘ ·) f) :=
  by
  letI : UniformSpace γ := ‹UniformSpace β›.comap f
  ext : 1
  change UniformFun.filter α γ ((𝓤 β).comap _) = (UniformFun.filter α β (𝓤 β)).comap _
  -- We have the following four Galois connection which form a square diagram, and we want
  -- to show that the square of upper adjoints is commutative. The trick then is to use
  -- `galois_connection.u_comm_of_l_comm` to reduce it to commutativity of the lower adjoints,
  -- which is way easier to prove.
  have h₁ := Filter.gc_map_comap (Prod.map ((· ∘ ·) f) ((· ∘ ·) f))
  have h₂ := Filter.gc_map_comap (Prod.map f f)
  have h₃ := UniformFun.gc α β
  have h₄ := UniformFun.gc α γ
  refine' GaloisConnection.u_comm_of_l_comm h₁ h₂ h₃ h₄ fun 𝓐 => _
  have : Prod.map f f ∘ Φ α γ = Φ α β ∘ Prod.map (Prod.map ((· ∘ ·) f) ((· ∘ ·) f)) id := by
    ext <;> rfl
  rw [map_comm this, ← prod_map_map_eq']
  rfl
#align uniform_fun.comap_eq UniformFun.comap_eq

#print UniformFun.postcomp_uniformContinuous /-
/-- Post-composition by a uniformly continuous function is uniformly continuous on `α →ᵤ β`.

More precisely, if `f : γ → β` is uniformly continuous, then `(λ g, f ∘ g) : (α →ᵤ γ) → (α →ᵤ β)`
is uniformly continuous. -/
protected theorem postcomp_uniformContinuous [UniformSpace γ] {f : γ → β}
    (hf : UniformContinuous f) :
    UniformContinuous (ofFun ∘ (· ∘ ·) f ∘ toFun : (α →ᵤ γ) → α →ᵤ β) :=
  -- This is a direct consequence of `uniform_convergence.comap_eq`
      uniformContinuous_iff.mpr <|
    calc
      𝒰(α, γ, _) ≤ 𝒰(α, γ, ‹UniformSpace β›.comap f) :=
        UniformFun.mono (uniformContinuous_iff.mp hf)
      _ = 𝒰(α, β, _).comap ((· ∘ ·) f) := UniformFun.comap_eq

#align uniform_fun.postcomp_uniform_continuous UniformFun.postcomp_uniformContinuous
-/

#print UniformFun.postcomp_uniformInducing /-
/-- Post-composition by a uniform inducing is a uniform inducing for the
uniform structures of uniform convergence.

More precisely, if `f : γ → β` is a uniform inducing, then `(λ g, f ∘ g) : (α →ᵤ γ) → (α →ᵤ β)` is
a uniform inducing. -/
protected theorem postcomp_uniformInducing [UniformSpace γ] {f : γ → β} (hf : UniformInducing f) :
    UniformInducing (ofFun ∘ (· ∘ ·) f ∘ toFun : (α →ᵤ γ) → α →ᵤ β) :=
  by
  -- This is a direct consequence of `uniform_convergence.comap_eq`
  constructor
  replace hf : (𝓤 β).comap (Prod.map f f) = _ := hf.comap_uniformity
  change comap (Prod.map (of_fun ∘ (· ∘ ·) f ∘ to_fun) (of_fun ∘ (· ∘ ·) f ∘ to_fun)) _ = _
  rw [← uniformity_comap] at hf⊢
  congr
  rw [← uniformSpace_eq hf, UniformFun.comap_eq]
  rfl
#align uniform_fun.postcomp_uniform_inducing UniformFun.postcomp_uniformInducing
-/

#print UniformFun.congrRight /-
/-- Turn a uniform isomorphism `γ ≃ᵤ β` into a uniform isomorphism `(α →ᵤ γ) ≃ᵤ (α →ᵤ β)` by
post-composing. -/
protected def congrRight [UniformSpace γ] (e : γ ≃ᵤ β) : (α →ᵤ γ) ≃ᵤ (α →ᵤ β) :=
  {
    Equiv.piCongrRight fun a =>
      e.toEquiv with
    uniformContinuous_toFun := UniformFun.postcomp_uniformContinuous e.UniformContinuous
    uniformContinuous_invFun := UniformFun.postcomp_uniformContinuous e.symm.UniformContinuous }
#align uniform_fun.congr_right UniformFun.congrRight
-/

/- warning: uniform_fun.precomp_uniform_continuous -> UniformFun.precomp_uniformContinuous is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : UniformSpace.{u2} β] {f : γ -> α}, UniformContinuous.{max u1 u2, max u3 u2} (UniformFun.{u1, u2} α β) (UniformFun.{u3, u2} γ β) (UniformFun.uniformSpace.{u1, u2} α β _inst_1) (UniformFun.uniformSpace.{u3, u2} γ β _inst_1) (fun (g : UniformFun.{u1, u2} α β) => coeFn.{max 1 (succ u3) (succ u2), max (succ u3) (succ u2)} (Equiv.{max (succ u3) (succ u2), max (succ u3) (succ u2)} (γ -> β) (UniformFun.{u3, u2} γ β)) (fun (_x : Equiv.{max (succ u3) (succ u2), max (succ u3) (succ u2)} (γ -> β) (UniformFun.{u3, u2} γ β)) => (γ -> β) -> (UniformFun.{u3, u2} γ β)) (Equiv.hasCoeToFun.{max (succ u3) (succ u2), max (succ u3) (succ u2)} (γ -> β) (UniformFun.{u3, u2} γ β)) (UniformFun.ofFun.{u3, u2} γ β) (Function.comp.{succ u3, succ u1, succ u2} γ α β g f))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} [_inst_1 : UniformSpace.{u2} β] {f : γ -> α}, UniformContinuous.{max u3 u2, max u2 u1} (UniformFun.{u3, u2} α β) (UniformFun.{u1, u2} γ β) (UniformFun.uniformSpace.{u3, u2} α β _inst_1) (UniformFun.uniformSpace.{u1, u2} γ β _inst_1) (fun (g : UniformFun.{u3, u2} α β) => FunLike.coe.{max (succ u2) (succ u1), max (succ u2) (succ u1), max (succ u2) (succ u1)} (Equiv.{max (succ u1) (succ u2), max (succ u2) (succ u1)} (γ -> β) (UniformFun.{u1, u2} γ β)) (γ -> β) (fun (_x : γ -> β) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : γ -> β) => UniformFun.{u1, u2} γ β) _x) (Equiv.instFunLikeEquiv.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (γ -> β) (UniformFun.{u1, u2} γ β)) (UniformFun.ofFun.{u1, u2} γ β) (Function.comp.{succ u1, succ u3, succ u2} γ α β g f))
Case conversion may be inaccurate. Consider using '#align uniform_fun.precomp_uniform_continuous UniformFun.precomp_uniformContinuousₓ'. -/
/-- Pre-composition by a any function is uniformly continuous for the uniform structures of
uniform convergence.

More precisely, for any `f : γ → α`, the function `(λ g, g ∘ f) : (α →ᵤ β) → (γ →ᵤ β)` is uniformly
continuous. -/
protected theorem precomp_uniformContinuous {f : γ → α} :
    UniformContinuous fun g : α →ᵤ β => ofFun (g ∘ f) :=
  by
  -- Here we simply go back to filter bases.
  rw [uniformContinuous_iff]
  change
    𝓤 (α →ᵤ β) ≤ (𝓤 (γ →ᵤ β)).comap (Prod.map (fun g : α →ᵤ β => g ∘ f) fun g : α →ᵤ β => g ∘ f)
  rw [(UniformFun.hasBasis_uniformity α β).le_basis_iffₓ
      ((UniformFun.hasBasis_uniformity γ β).comap _)]
  exact fun U hU => ⟨U, hU, fun uv huv x => huv (f x)⟩
#align uniform_fun.precomp_uniform_continuous UniformFun.precomp_uniformContinuous

#print UniformFun.congrLeft /-
/-- Turn a bijection `γ ≃ α` into a uniform isomorphism
`(γ →ᵤ β) ≃ᵤ (α →ᵤ β)` by pre-composing. -/
protected def congrLeft (e : γ ≃ α) : (γ →ᵤ β) ≃ᵤ (α →ᵤ β) :=
  {
    Equiv.arrowCongr e
      (Equiv.refl
        _) with
    uniformContinuous_toFun := UniformFun.precomp_uniformContinuous
    uniformContinuous_invFun := UniformFun.precomp_uniformContinuous }
#align uniform_fun.congr_left UniformFun.congrLeft
-/

/-- The topology of uniform convergence is T₂. -/
instance [T2Space β] : T2Space (α →ᵤ β)
    where t2 := by
    intro f g h
    obtain ⟨x, hx⟩ := not_forall.mp (mt funext h)
    exact separated_by_continuous (uniform_continuous_eval β x).Continuous hx

/- warning: uniform_fun.uniform_continuous_to_fun -> UniformFun.uniformContinuous_toFun is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : UniformSpace.{u2} β], UniformContinuous.{max u1 u2, max u1 u2} (UniformFun.{u1, u2} α β) (α -> β) (UniformFun.uniformSpace.{u1, u2} α β _inst_1) (Pi.uniformSpace.{u2, u1} α (fun (ᾰ : α) => β) (fun (i : α) => _inst_1)) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u1, u2} α β) (α -> β)) (fun (_x : Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u1, u2} α β) (α -> β)) => (UniformFun.{u1, u2} α β) -> α -> β) (Equiv.hasCoeToFun.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u1, u2} α β) (α -> β)) (UniformFun.toFun.{u1, u2} α β))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : UniformSpace.{u1} β], UniformContinuous.{max u2 u1, max u2 u1} (UniformFun.{u2, u1} α β) (α -> β) (UniformFun.uniformSpace.{u2, u1} α β _inst_1) (Pi.uniformSpace.{u1, u2} α (fun (ᾰ : α) => β) (fun (i : α) => _inst_1)) (FunLike.coe.{max (succ u1) (succ u2), max (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u2, u1} α β) (α -> β)) (UniformFun.{u2, u1} α β) (fun (_x : UniformFun.{u2, u1} α β) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : UniformFun.{u2, u1} α β) => α -> β) _x) (Equiv.instFunLikeEquiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u2, u1} α β) (α -> β)) (UniformFun.toFun.{u2, u1} α β))
Case conversion may be inaccurate. Consider using '#align uniform_fun.uniform_continuous_to_fun UniformFun.uniformContinuous_toFunₓ'. -/
/-- The natural map `uniform_fun.to_fun` from `α →ᵤ β` to `α → β` is uniformly continuous.

In other words, the uniform structure of uniform convergence is finer than that of pointwise
convergence, aka the product uniform structure. -/
protected theorem uniformContinuous_toFun : UniformContinuous (toFun : (α →ᵤ β) → α → β) :=
  by
  -- By definition of the product uniform structure, this is just `uniform_continuous_eval`.
  rw [uniformContinuous_pi]
  intro x
  exact uniform_continuous_eval β x
#align uniform_fun.uniform_continuous_to_fun UniformFun.uniformContinuous_toFun

/- warning: uniform_fun.tendsto_iff_tendsto_uniformly -> UniformFun.tendsto_iff_tendstoUniformly is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {ι : Type.{u3}} {p : Filter.{u3} ι} [_inst_1 : UniformSpace.{u2} β] {F : ι -> (UniformFun.{u1, u2} α β)} {f : UniformFun.{u1, u2} α β}, Iff (Filter.Tendsto.{u3, max u1 u2} ι (UniformFun.{u1, u2} α β) F p (nhds.{max u1 u2} (UniformFun.{u1, u2} α β) (UniformFun.topologicalSpace.{u1, u2} α β _inst_1) f)) (TendstoUniformly.{u1, u2, u3} α β ι _inst_1 F f p)
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {ι : Type.{u1}} {p : Filter.{u1} ι} [_inst_1 : UniformSpace.{u2} β] {F : ι -> (UniformFun.{u3, u2} α β)} {f : UniformFun.{u3, u2} α β}, Iff (Filter.Tendsto.{u1, max u3 u2} ι (UniformFun.{u3, u2} α β) F p (nhds.{max u3 u2} (UniformFun.{u3, u2} α β) (UniformFun.topologicalSpace.{u3, u2} α β _inst_1) f)) (TendstoUniformly.{u3, u2, u1} α β ι _inst_1 F f p)
Case conversion may be inaccurate. Consider using '#align uniform_fun.tendsto_iff_tendsto_uniformly UniformFun.tendsto_iff_tendstoUniformlyₓ'. -/
/-- The topology of uniform convergence indeed gives the same notion of convergence as
`tendsto_uniformly`. -/
protected theorem tendsto_iff_tendstoUniformly {F : ι → α →ᵤ β} {f : α →ᵤ β} :
    Tendsto F p (𝓝 f) ↔ TendstoUniformly F f p :=
  by
  rw [(UniformFun.hasBasis_nhds α β f).tendsto_right_iff, TendstoUniformly]
  exact Iff.rfl
#align uniform_fun.tendsto_iff_tendsto_uniformly UniformFun.tendsto_iff_tendstoUniformly

/- warning: uniform_fun.uniform_equiv_prod_arrow -> UniformFun.uniformEquivProdArrow is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : UniformSpace.{u2} β] [_inst_2 : UniformSpace.{u3} γ], UniformEquiv.{max u1 u2 u3, max (max u1 u2) u1 u3} (UniformFun.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (Prod.{max u1 u2, max u1 u3} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u3} α γ)) (UniformFun.uniformSpace.{u1, max u2 u3} α (Prod.{u2, u3} β γ) (Prod.uniformSpace.{u2, u3} β γ _inst_1 _inst_2)) (Prod.uniformSpace.{max u1 u2, max u1 u3} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u3} α γ) (UniformFun.uniformSpace.{u1, u2} α β _inst_1) (UniformFun.uniformSpace.{u1, u3} α γ _inst_2))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : UniformSpace.{u2} β] [_inst_2 : UniformSpace.{u3} γ], UniformEquiv.{max (max u3 u2) u1, max (max u3 u1) u2 u1} (UniformFun.{u1, max u3 u2} α (Prod.{u2, u3} β γ)) (Prod.{max u2 u1, max u3 u1} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u3} α γ)) (UniformFun.uniformSpace.{u1, max u2 u3} α (Prod.{u2, u3} β γ) (instUniformSpaceProd.{u2, u3} β γ _inst_1 _inst_2)) (instUniformSpaceProd.{max u1 u2, max u1 u3} (UniformFun.{u1, u2} α β) (UniformFun.{u1, u3} α γ) (UniformFun.uniformSpace.{u1, u2} α β _inst_1) (UniformFun.uniformSpace.{u1, u3} α γ _inst_2))
Case conversion may be inaccurate. Consider using '#align uniform_fun.uniform_equiv_prod_arrow UniformFun.uniformEquivProdArrowₓ'. -/
/-- The natural bijection between `α → β × γ` and `(α → β) × (α → γ)`, upgraded to a uniform
isomorphism between `α →ᵤ β × γ` and `(α →ᵤ β) × (α →ᵤ γ)`. -/
protected def uniformEquivProdArrow [UniformSpace γ] : (α →ᵤ β × γ) ≃ᵤ (α →ᵤ β) × (α →ᵤ γ) :=
  (-- Denote `φ` this bijection. We want to show that
        -- `comap φ (𝒰(α, β, uβ) × 𝒰(α, γ, uγ)) = 𝒰(α, β × γ, uβ × uγ)`.
        -- But `uβ × uγ` is defined as `comap fst uβ ⊓ comap snd uγ`, so we just have to apply
        -- `uniform_convergence.inf_eq` and `uniform_convergence.comap_eq`, which leaves us to check
        -- that some square commutes.
        Equiv.arrowProdEquivProdArrow
        _ _ _).toUniformEquivOfUniformInducing
    (by
      constructor
      change
        comap (Prod.map (Equiv.arrowProdEquivProdArrow _ _ _) (Equiv.arrowProdEquivProdArrow _ _ _))
            _ =
          _
      rw [← uniformity_comap]
      congr
      rw [Prod.uniformSpace, Prod.uniformSpace, UniformSpace.comap_inf, UniformFun.inf_eq]
      congr <;> rw [← UniformSpace.comap_comap, UniformFun.comap_eq] <;> rfl)
#align uniform_fun.uniform_equiv_prod_arrow UniformFun.uniformEquivProdArrow

-- the relevant diagram commutes by definition
variable (α) (δ : ι → Type _) [∀ i, UniformSpace (δ i)]

#print UniformFun.uniformEquivPiComm /-
/-- The natural bijection between `α → Π i, δ i` and `Π i, α → δ i`, upgraded to a uniform
isomorphism between `α →ᵤ (Π i, δ i)` and `Π i, α →ᵤ δ i`. -/
protected def uniformEquivPiComm : UniformEquiv (α →ᵤ ∀ i, δ i) (∀ i, α →ᵤ δ i) :=
  -- Denote `φ` this bijection. We want to show that
    -- `comap φ (Π i, 𝒰(α, δ i, uδ i)) = 𝒰(α, (Π i, δ i), (Π i, uδ i))`.
    -- But `Π i, uδ i` is defined as `⨅ i, comap (eval i) (uδ i)`, so we just have to apply
    -- `uniform_convergence.infi_eq` and `uniform_convergence.comap_eq`, which leaves us to check
    -- that some square commutes.
    @Equiv.toUniformEquivOfUniformInducing
    _ _ 𝒰(α, ∀ i, δ i, Pi.uniformSpace δ)
    (@Pi.uniformSpace ι (fun i => α → δ i) fun i => 𝒰(α, δ i, _)) (Equiv.piComm _)
    (by
      constructor
      change comap (Prod.map Function.swap Function.swap) _ = _
      rw [← uniformity_comap]
      congr
      rw [Pi.uniformSpace, UniformSpace.ofCoreEq_toCore, Pi.uniformSpace,
        UniformSpace.ofCoreEq_toCore, UniformSpace.comap_infᵢ, UniformFun.infᵢ_eq]
      refine' infᵢ_congr fun i => _
      rw [← UniformSpace.comap_comap, UniformFun.comap_eq])
#align uniform_fun.uniform_equiv_Pi_comm UniformFun.uniformEquivPiComm
-/

-- Like in the previous lemma, the diagram actually commutes by definition
end UniformFun

namespace UniformOnFun

variable {α β : Type _} {γ ι : Type _}

variable {s s' : Set α} {x : α} {p : Filter ι} {g : ι → α}

-- mathport name: «expr𝒰( , , )»
local notation "𝒰(" α ", " β ", " u ")" => @UniformFun.uniformSpace α β u

#print UniformOnFun.gen /-
/-- Basis sets for the uniformity of `𝔖`-convergence: for `S : set α` and `V : set (β × β)`,
`gen 𝔖 S V` is the set of pairs `(f, g)` of functions `α →ᵤ[𝔖] β` such that
`∀ x ∈ S, (f x, g x) ∈ V`. Note that the family `𝔖 : set (set α)` is only used to specify which
type alias of `α → β` to use here. -/
protected def gen (𝔖) (S : Set α) (V : Set (β × β)) : Set ((α →ᵤ[𝔖] β) × (α →ᵤ[𝔖] β)) :=
  { uv : (α →ᵤ[𝔖] β) × (α →ᵤ[𝔖] β) | ∀ x ∈ S, (uv.1 x, uv.2 x) ∈ V }
#align uniform_on_fun.gen UniformOnFun.gen
-/

/- warning: uniform_on_fun.gen_eq_preimage_restrict -> UniformOnFun.gen_eq_preimage_restrict is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {𝔖 : Set.{u1} (Set.{u1} α)} (S : Set.{u1} α) (V : Set.{u2} (Prod.{u2, u2} β β)), Eq.{succ (max u1 u2)} (Set.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖))) (UniformOnFun.gen.{u1, u2} α β 𝔖 S V) (Set.preimage.{max u1 u2, max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖)) (Prod.{max u1 u2, max u1 u2} ((coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) S) -> β) ((coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) S) -> β)) (Prod.map.{max u1 u2, max u1 u2, max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) ((coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) S) -> β) (UniformOnFun.{u1, u2} α β 𝔖) ((coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) S) -> β) (Set.restrict.{u1, u2} α (fun (ᾰ : α) => β) S) (Set.restrict.{u1, u2} α (fun (ᾰ : α) => β) S)) (UniformFun.gen.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) S) β V))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {𝔖 : Set.{u2} (Set.{u2} α)} (S : Set.{u2} α) (V : Set.{u1} (Prod.{u1, u1} β β)), Eq.{max (succ u2) (succ u1)} (Set.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.{u2, u1} α β 𝔖))) (UniformOnFun.gen.{u2, u1} α β 𝔖 S V) (Set.preimage.{max u1 u2, max u2 u1} (Prod.{max u1 u2, max u1 u2} (UniformFun.{u2, u1} α β) (UniformFun.{u2, u1} α β)) (Prod.{max u2 u1, max u2 u1} ((Set.Elem.{u2} α S) -> β) ((Set.Elem.{u2} α S) -> β)) (Prod.map.{max u1 u2, max u2 u1, max u1 u2, max u2 u1} (UniformFun.{u2, u1} α β) ((Set.Elem.{u2} α S) -> β) (UniformFun.{u2, u1} α β) ((Set.Elem.{u2} α S) -> β) (Function.comp.{succ (max u1 u2), max (succ u2) (succ u1), succ (max u2 u1)} (UniformFun.{u2, u1} α β) (α -> β) ((Set.Elem.{u2} α S) -> β) (Set.restrict.{u2, u1} α (fun (a._@.Mathlib.Data.Set.Function._hyg.24 : α) => β) S) (FunLike.coe.{max (succ u1) (succ u2), max (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u2, u1} α β) (α -> β)) (UniformFun.{u2, u1} α β) (fun (a : UniformFun.{u2, u1} α β) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : UniformFun.{u2, u1} α β) => α -> β) a) (Equiv.instFunLikeEquiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u2, u1} α β) (α -> β)) (UniformFun.toFun.{u2, u1} α β))) (Function.comp.{succ (max u1 u2), max (succ u2) (succ u1), succ (max u2 u1)} (UniformFun.{u2, u1} α β) (α -> β) ((Set.Elem.{u2} α S) -> β) (Set.restrict.{u2, u1} α (fun (a._@.Mathlib.Data.Set.Function._hyg.24 : α) => β) S) (FunLike.coe.{max (succ u1) (succ u2), max (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u2, u1} α β) (α -> β)) (UniformFun.{u2, u1} α β) (fun (a : UniformFun.{u2, u1} α β) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : UniformFun.{u2, u1} α β) => α -> β) a) (Equiv.instFunLikeEquiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u2, u1} α β) (α -> β)) (UniformFun.toFun.{u2, u1} α β)))) (UniformFun.gen.{u2, u1} (Set.Elem.{u2} α S) β V))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.gen_eq_preimage_restrict UniformOnFun.gen_eq_preimage_restrictₓ'. -/
/-- For `S : set α` and `V : set (β × β)`, we have
`uniform_on_fun.gen 𝔖 S V = (S.restrict × S.restrict) ⁻¹' (uniform_fun.gen S β V)`.
This is the crucial fact for proving that the family `uniform_on_fun.gen S V` for `S ∈ 𝔖` and
`V ∈ 𝓤 β` is indeed a basis for the uniformity `α →ᵤ[𝔖] β` endowed with `𝒱(α, β, 𝔖, uβ)`
the uniform structure of `𝔖`-convergence, as defined in `uniform_on_fun.uniform_space`. -/
protected theorem gen_eq_preimage_restrict {𝔖} (S : Set α) (V : Set (β × β)) :
    UniformOnFun.gen 𝔖 S V = Prod.map S.restrict S.restrict ⁻¹' UniformFun.gen S β V :=
  by
  ext uv
  exact ⟨fun h ⟨x, hx⟩ => h x hx, fun h x hx => h ⟨x, hx⟩⟩
#align uniform_on_fun.gen_eq_preimage_restrict UniformOnFun.gen_eq_preimage_restrict

/- warning: uniform_on_fun.gen_mono -> UniformOnFun.gen_mono is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {𝔖 : Set.{u1} (Set.{u1} α)} {S : Set.{u1} α} {S' : Set.{u1} α} {V : Set.{u2} (Prod.{u2, u2} β β)} {V' : Set.{u2} (Prod.{u2, u2} β β)}, (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α) S' S) -> (HasSubset.Subset.{u2} (Set.{u2} (Prod.{u2, u2} β β)) (Set.hasSubset.{u2} (Prod.{u2, u2} β β)) V V') -> (HasSubset.Subset.{max u1 u2} (Set.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖))) (Set.hasSubset.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖))) (UniformOnFun.gen.{u1, u2} α β 𝔖 S V) (UniformOnFun.gen.{u1, u2} α β 𝔖 S' V'))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {𝔖 : Set.{u2} (Set.{u2} α)} {S : Set.{u2} α} {S' : Set.{u2} α} {V : Set.{u1} (Prod.{u1, u1} β β)} {V' : Set.{u1} (Prod.{u1, u1} β β)}, (HasSubset.Subset.{u2} (Set.{u2} α) (Set.instHasSubsetSet.{u2} α) S' S) -> (HasSubset.Subset.{u1} (Set.{u1} (Prod.{u1, u1} β β)) (Set.instHasSubsetSet.{u1} (Prod.{u1, u1} β β)) V V') -> (HasSubset.Subset.{max u1 u2} (Set.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.{u2, u1} α β 𝔖))) (Set.instHasSubsetSet.{max u2 u1} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.{u2, u1} α β 𝔖))) (UniformOnFun.gen.{u2, u1} α β 𝔖 S V) (UniformOnFun.gen.{u2, u1} α β 𝔖 S' V'))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.gen_mono UniformOnFun.gen_monoₓ'. -/
/-- `uniform_on_fun.gen` is antitone in the first argument and monotone in the second. -/
protected theorem gen_mono {𝔖} {S S' : Set α} {V V' : Set (β × β)} (hS : S' ⊆ S) (hV : V ⊆ V') :
    UniformOnFun.gen 𝔖 S V ⊆ UniformOnFun.gen 𝔖 S' V' := fun uv h x hx => hV (h x <| hS hx)
#align uniform_on_fun.gen_mono UniformOnFun.gen_mono

/- warning: uniform_on_fun.is_basis_gen -> UniformOnFun.isBasis_gen is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (𝔖 : Set.{u1} (Set.{u1} α)), (Set.Nonempty.{u1} (Set.{u1} α) 𝔖) -> (DirectedOn.{u1} (Set.{u1} α) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α)) 𝔖) -> (forall (𝓑 : FilterBasis.{u2} (Prod.{u2, u2} β β)), Filter.IsBasis.{max u1 u2, max (succ u1) (succ u2)} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖)) (Prod.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β))) (fun (SV : Prod.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β))) => And (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) (Prod.fst.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV) 𝔖) (Membership.Mem.{u2, u2} (Set.{u2} (Prod.{u2, u2} β β)) (FilterBasis.{u2} (Prod.{u2, u2} β β)) (FilterBasis.hasMem.{u2} (Prod.{u2, u2} β β)) (Prod.snd.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV) 𝓑)) (fun (SV : Prod.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β))) => UniformOnFun.gen.{u1, u2} α β 𝔖 (Prod.fst.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV) (Prod.snd.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (𝔖 : Set.{u2} (Set.{u2} α)), (Set.Nonempty.{u2} (Set.{u2} α) 𝔖) -> (DirectedOn.{u2} (Set.{u2} α) (fun (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.6675 : Set.{u2} α) (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.6677 : Set.{u2} α) => HasSubset.Subset.{u2} (Set.{u2} α) (Set.instHasSubsetSet.{u2} α) x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.6675 x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.6677) 𝔖) -> (forall (𝓑 : FilterBasis.{u1} (Prod.{u1, u1} β β)), Filter.IsBasis.{max u2 u1, max (succ u2) (succ u1)} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.{u2, u1} α β 𝔖)) (Prod.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β))) (fun (SV : Prod.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β))) => And (Membership.mem.{u2, u2} (Set.{u2} α) (Set.{u2} (Set.{u2} α)) (Set.instMembershipSet.{u2} (Set.{u2} α)) (Prod.fst.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV) 𝔖) (Membership.mem.{u1, u1} (Set.{u1} (Prod.{u1, u1} β β)) (FilterBasis.{u1} (Prod.{u1, u1} β β)) (instMembershipSetFilterBasis.{u1} (Prod.{u1, u1} β β)) (Prod.snd.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV) 𝓑)) (fun (SV : Prod.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β))) => UniformOnFun.gen.{u2, u1} α β 𝔖 (Prod.fst.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV) (Prod.snd.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV)))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.is_basis_gen UniformOnFun.isBasis_genₓ'. -/
/-- If `𝔖 : set (set α)` is nonempty and directed and `𝓑` is a filter basis on `β × β`, then the
family `uniform_on_fun.gen 𝔖 S V` for `S ∈ 𝔖` and `V ∈ 𝓑` is a filter basis on
`(α →ᵤ[𝔖] β) × (α →ᵤ[𝔖] β)`.
We will show in `has_basis_uniformity_of_basis` that, if `𝓑` is a basis for `𝓤 β`, then the
corresponding filter is the uniformity of `α →ᵤ[𝔖] β`. -/
protected theorem isBasis_gen (𝔖 : Set (Set α)) (h : 𝔖.Nonempty) (h' : DirectedOn (· ⊆ ·) 𝔖)
    (𝓑 : FilterBasis <| β × β) :
    IsBasis (fun SV : Set α × Set (β × β) => SV.1 ∈ 𝔖 ∧ SV.2 ∈ 𝓑) fun SV =>
      UniformOnFun.gen 𝔖 SV.1 SV.2 :=
  ⟨h.Prod 𝓑.Nonempty, fun U₁V₁ U₂V₂ h₁ h₂ =>
    let ⟨U₃, hU₃, hU₁₃, hU₂₃⟩ := h' U₁V₁.1 h₁.1 U₂V₂.1 h₂.1
    let ⟨V₃, hV₃, hV₁₂₃⟩ := 𝓑.inter_sets h₁.2 h₂.2
    ⟨⟨U₃, V₃⟩,
      ⟨⟨hU₃, hV₃⟩, fun uv huv =>
        ⟨fun x hx => (hV₁₂₃ <| huv x <| hU₁₃ hx).1, fun x hx => (hV₁₂₃ <| huv x <| hU₂₃ hx).2⟩⟩⟩⟩
#align uniform_on_fun.is_basis_gen UniformOnFun.isBasis_gen

variable (α β) [UniformSpace β] (𝔖 : Set (Set α))

/-- Uniform structure of `𝔖`-convergence, i.e uniform convergence on the elements of `𝔖`,
declared as an instance on `α →ᵤ[𝔖] β`. It is defined as the infimum, for `S ∈ 𝔖`, of the pullback
by `S.restrict`, the map of restriction to `S`, of the uniform structure `𝒰(s, β, uβ)` on
`↥S →ᵤ β`. We will denote it `𝒱(α, β, 𝔖, uβ)`, where `uβ` is the uniform structure on `β`. -/
instance : UniformSpace (α →ᵤ[𝔖] β) :=
  ⨅ (s : Set α) (hs : s ∈ 𝔖), UniformSpace.comap s.restrict 𝒰(s, β, _)

-- mathport name: «expr𝒱( , , , )»
local notation "𝒱(" α ", " β ", " 𝔖 ", " u ")" => @UniformOnFun.uniformSpace α β u 𝔖

/-- Topology of `𝔖`-convergence, i.e uniform convergence on the elements of `𝔖`, declared as an
instance on `α →ᵤ[𝔖] β`. -/
instance : TopologicalSpace (α →ᵤ[𝔖] β) :=
  𝒱(α, β, 𝔖, _).toTopologicalSpace

/- warning: uniform_on_fun.topological_space_eq -> UniformOnFun.topologicalSpace_eq is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}) [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u1} (Set.{u1} α)), Eq.{succ (max u1 u2)} (TopologicalSpace.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖)) (UniformOnFun.topologicalSpace.{u1, u2} α β _inst_1 𝔖) (infᵢ.{max u1 u2, succ u1} (TopologicalSpace.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖)) (ConditionallyCompleteLattice.toHasInf.{max u1 u2} (TopologicalSpace.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖)) (CompleteLattice.toConditionallyCompleteLattice.{max u1 u2} (TopologicalSpace.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖)) (TopologicalSpace.completeLattice.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖)))) (Set.{u1} α) (fun (s : Set.{u1} α) => infᵢ.{max u1 u2, 0} (TopologicalSpace.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖)) (ConditionallyCompleteLattice.toHasInf.{max u1 u2} (TopologicalSpace.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖)) (CompleteLattice.toConditionallyCompleteLattice.{max u1 u2} (TopologicalSpace.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖)) (TopologicalSpace.completeLattice.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖)))) (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) s 𝔖) (fun (hs : Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) s 𝔖) => TopologicalSpace.induced.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) ((coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) -> β) (Set.restrict.{u1, u2} α (fun (ᾰ : α) => β) s) (UniformFun.topologicalSpace.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β _inst_1))))
but is expected to have type
  forall (α : Type.{u2}) (β : Type.{u1}) [_inst_1 : UniformSpace.{u1} β] (𝔖 : Set.{u2} (Set.{u2} α)), Eq.{max (succ u2) (succ u1)} (TopologicalSpace.{max u1 u2} (UniformOnFun.{u2, u1} α β 𝔖)) (UniformOnFun.topologicalSpace.{u2, u1} α β _inst_1 𝔖) (infᵢ.{max u1 u2, succ u2} (TopologicalSpace.{max u1 u2} (UniformFun.{u2, u1} α β)) (ConditionallyCompleteLattice.toInfSet.{max u2 u1} (TopologicalSpace.{max u1 u2} (UniformFun.{u2, u1} α β)) (CompleteLattice.toConditionallyCompleteLattice.{max u2 u1} (TopologicalSpace.{max u1 u2} (UniformFun.{u2, u1} α β)) (TopologicalSpace.instCompleteLatticeTopologicalSpace.{max u2 u1} (UniformFun.{u2, u1} α β)))) (Set.{u2} α) (fun (s : Set.{u2} α) => infᵢ.{max u1 u2, 0} (TopologicalSpace.{max u1 u2} (UniformFun.{u2, u1} α β)) (ConditionallyCompleteLattice.toInfSet.{max u2 u1} (TopologicalSpace.{max u1 u2} (UniformFun.{u2, u1} α β)) (CompleteLattice.toConditionallyCompleteLattice.{max u2 u1} (TopologicalSpace.{max u1 u2} (UniformFun.{u2, u1} α β)) (TopologicalSpace.instCompleteLatticeTopologicalSpace.{max u2 u1} (UniformFun.{u2, u1} α β)))) (Membership.mem.{u2, u2} (Set.{u2} α) (Set.{u2} (Set.{u2} α)) (Set.instMembershipSet.{u2} (Set.{u2} α)) s 𝔖) (fun (hs : Membership.mem.{u2, u2} (Set.{u2} α) (Set.{u2} (Set.{u2} α)) (Set.instMembershipSet.{u2} (Set.{u2} α)) s 𝔖) => TopologicalSpace.induced.{max u1 u2, max u2 u1} (UniformFun.{u2, u1} α β) ((Set.Elem.{u2} α s) -> β) (Function.comp.{succ (max u1 u2), max (succ u2) (succ u1), succ (max u2 u1)} (UniformFun.{u2, u1} α β) (α -> β) ((Set.Elem.{u2} α s) -> β) (Set.restrict.{u2, u1} α (fun (a._@.Mathlib.Data.Set.Function._hyg.24 : α) => β) s) (FunLike.coe.{max (succ u1) (succ u2), max (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u2, u1} α β) (α -> β)) (UniformFun.{u2, u1} α β) (fun (a : UniformFun.{u2, u1} α β) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : UniformFun.{u2, u1} α β) => α -> β) a) (Equiv.instFunLikeEquiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformFun.{u2, u1} α β) (α -> β)) (UniformFun.toFun.{u2, u1} α β))) (UniformFun.topologicalSpace.{u2, u1} (Set.Elem.{u2} α s) β _inst_1))))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.topological_space_eq UniformOnFun.topologicalSpace_eqₓ'. -/
/-- The topology of `𝔖`-convergence is the infimum, for `S ∈ 𝔖`, of topology induced by the map
of `S.restrict : (α →ᵤ[𝔖] β) → (↥S →ᵤ β)` of restriction to `S`, where `↥S →ᵤ β` is endowed with
the topology of uniform convergence. -/
protected theorem topologicalSpace_eq :
    UniformOnFun.topologicalSpace α β 𝔖 =
      ⨅ (s : Set α) (hs : s ∈ 𝔖),
        TopologicalSpace.induced s.restrict (UniformFun.topologicalSpace s β) :=
  by
  simp only [UniformOnFun.topologicalSpace, toTopologicalSpace_infᵢ, toTopologicalSpace_infᵢ,
    toTopologicalSpace_comap]
  rfl
#align uniform_on_fun.topological_space_eq UniformOnFun.topologicalSpace_eq

/- warning: uniform_on_fun.has_basis_uniformity_of_basis_aux₁ -> UniformOnFun.hasBasis_uniformity_of_basis_aux₁ is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}) {ι : Type.{u3}} [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u1} (Set.{u1} α)) {p : ι -> Prop} {s : ι -> (Set.{u2} (Prod.{u2, u2} β β))}, (Filter.HasBasis.{u2, succ u3} (Prod.{u2, u2} β β) ι (uniformity.{u2} β _inst_1) p s) -> (forall (S : Set.{u1} α), Filter.HasBasis.{max u1 u2, succ u3} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖)) ι (uniformity.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformSpace.comap.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformFun.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) S) β) (Set.restrict.{u1, u2} α (fun (ᾰ : α) => β) S) (UniformFun.uniformSpace.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) S) β _inst_1))) p (fun (i : ι) => UniformOnFun.gen.{u1, u2} α β 𝔖 S (s i)))
but is expected to have type
  forall (α : Type.{u1}) (β : Type.{u3}) {ι : Type.{u2}} [_inst_1 : UniformSpace.{u3} β] (𝔖 : Set.{u1} (Set.{u1} α)) {p : ι -> Prop} {s : ι -> (Set.{u3} (Prod.{u3, u3} β β))}, (Filter.HasBasis.{u3, succ u2} (Prod.{u3, u3} β β) ι (uniformity.{u3} β _inst_1) p s) -> (forall (S : Set.{u1} α), Filter.HasBasis.{max u1 u3, succ u2} (Prod.{max u3 u1, max u3 u1} (UniformOnFun.{u1, u3} α β 𝔖) (UniformOnFun.{u1, u3} α β 𝔖)) ι (uniformity.{max u3 u1} (UniformOnFun.{u1, u3} α β 𝔖) (UniformSpace.comap.{max u1 u3, max u1 u3} (UniformOnFun.{u1, u3} α β 𝔖) ((Set.Elem.{u1} α S) -> β) (Set.restrict.{u1, u3} α (fun (ᾰ : α) => β) S) (UniformFun.uniformSpace.{u1, u3} (Set.Elem.{u1} α S) β _inst_1))) p (fun (i : ι) => UniformOnFun.gen.{u1, u3} α β 𝔖 S (s i)))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.has_basis_uniformity_of_basis_aux₁ UniformOnFun.hasBasis_uniformity_of_basis_aux₁ₓ'. -/
protected theorem hasBasis_uniformity_of_basis_aux₁ {p : ι → Prop} {s : ι → Set (β × β)}
    (hb : HasBasis (𝓤 β) p s) (S : Set α) :
    (@uniformity (α →ᵤ[𝔖] β) ((UniformFun.uniformSpace S β).comap S.restrict)).HasBasis p fun i =>
      UniformOnFun.gen 𝔖 S (s i) :=
  by
  simp_rw [UniformOnFun.gen_eq_preimage_restrict, uniformity_comap]
  exact (UniformFun.hasBasis_uniformity_of_basis S β hb).comap _
#align uniform_on_fun.has_basis_uniformity_of_basis_aux₁ UniformOnFun.hasBasis_uniformity_of_basis_aux₁

/- warning: uniform_on_fun.has_basis_uniformity_of_basis_aux₂ -> UniformOnFun.hasBasis_uniformity_of_basis_aux₂ is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}) {ι : Type.{u3}} [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u1} (Set.{u1} α)), (DirectedOn.{u1} (Set.{u1} α) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α)) 𝔖) -> (forall {p : ι -> Prop} {s : ι -> (Set.{u2} (Prod.{u2, u2} β β))}, (Filter.HasBasis.{u2, succ u3} (Prod.{u2, u2} β β) ι (uniformity.{u2} β _inst_1) p s) -> (DirectedOn.{u1} (Set.{u1} α) (Order.Preimage.{succ u1, succ (max u1 u2)} (Set.{u1} α) (UniformSpace.{max u1 u2} (α -> β)) (fun (s : Set.{u1} α) => UniformSpace.comap.{max u1 u2, max u1 u2} (α -> β) (UniformFun.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β) (Set.restrict.{u1, u2} α (fun (ᾰ : α) => β) s) (UniformFun.uniformSpace.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β _inst_1)) (GE.ge.{max u1 u2} (UniformSpace.{max u1 u2} (α -> β)) (Preorder.toLE.{max u1 u2} (UniformSpace.{max u1 u2} (α -> β)) (PartialOrder.toPreorder.{max u1 u2} (UniformSpace.{max u1 u2} (α -> β)) (UniformSpace.partialOrder.{max u1 u2} (α -> β)))))) 𝔖))
but is expected to have type
  forall (α : Type.{u3}) (β : Type.{u2}) {ι : Type.{u1}} [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u3} (Set.{u3} α)), (DirectedOn.{u3} (Set.{u3} α) (fun (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7583 : Set.{u3} α) (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7585 : Set.{u3} α) => HasSubset.Subset.{u3} (Set.{u3} α) (Set.instHasSubsetSet.{u3} α) x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7583 x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7585) 𝔖) -> (forall {p : ι -> Prop} {s : ι -> (Set.{u2} (Prod.{u2, u2} β β))}, (Filter.HasBasis.{u2, succ u1} (Prod.{u2, u2} β β) ι (uniformity.{u2} β _inst_1) p s) -> (DirectedOn.{u3} (Set.{u3} α) (Order.Preimage.{succ u3, succ (max u3 u2)} (Set.{u3} α) (UniformSpace.{max u3 u2} (α -> β)) (fun (s : Set.{u3} α) => UniformSpace.comap.{max u3 u2, max u3 u2} (α -> β) ((Set.Elem.{u3} α s) -> β) (Set.restrict.{u3, u2} α (fun (ᾰ : α) => β) s) (UniformFun.uniformSpace.{u3, u2} (Set.Elem.{u3} α s) β _inst_1)) (GE.ge.{max u3 u2} (UniformSpace.{max u3 u2} (α -> β)) (Preorder.toLE.{max u3 u2} (UniformSpace.{max u3 u2} (α -> β)) (PartialOrder.toPreorder.{max u3 u2} (UniformSpace.{max u3 u2} (α -> β)) (instPartialOrderUniformSpace.{max u3 u2} (α -> β)))))) 𝔖))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.has_basis_uniformity_of_basis_aux₂ UniformOnFun.hasBasis_uniformity_of_basis_aux₂ₓ'. -/
protected theorem hasBasis_uniformity_of_basis_aux₂ (h : DirectedOn (· ⊆ ·) 𝔖) {p : ι → Prop}
    {s : ι → Set (β × β)} (hb : HasBasis (𝓤 β) p s) :
    DirectedOn
      ((fun s : Set α => (UniformFun.uniformSpace s β).comap (s.restrict : (α →ᵤ β) → s →ᵤ β)) ⁻¹'o
        GE.ge)
      𝔖 :=
  h.mono fun s t hst =>
    ((UniformOnFun.hasBasis_uniformity_of_basis_aux₁ α β 𝔖 hb _).le_basis_iffₓ
          (UniformOnFun.hasBasis_uniformity_of_basis_aux₁ α β 𝔖 hb _)).mpr
      fun V hV => ⟨V, hV, UniformOnFun.gen_mono hst subset_rfl⟩
#align uniform_on_fun.has_basis_uniformity_of_basis_aux₂ UniformOnFun.hasBasis_uniformity_of_basis_aux₂

/- warning: uniform_on_fun.has_basis_uniformity_of_basis -> UniformOnFun.hasBasis_uniformity_of_basis is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}) {ι : Type.{u3}} [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u1} (Set.{u1} α)), (Set.Nonempty.{u1} (Set.{u1} α) 𝔖) -> (DirectedOn.{u1} (Set.{u1} α) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α)) 𝔖) -> (forall {p : ι -> Prop} {s : ι -> (Set.{u2} (Prod.{u2, u2} β β))}, (Filter.HasBasis.{u2, succ u3} (Prod.{u2, u2} β β) ι (uniformity.{u2} β _inst_1) p s) -> (Filter.HasBasis.{max u1 u2, max (succ u1) (succ u3)} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖)) (Prod.{u1, u3} (Set.{u1} α) ι) (uniformity.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.uniformSpace.{u1, u2} α β _inst_1 𝔖)) (fun (Si : Prod.{u1, u3} (Set.{u1} α) ι) => And (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) (Prod.fst.{u1, u3} (Set.{u1} α) ι Si) 𝔖) (p (Prod.snd.{u1, u3} (Set.{u1} α) ι Si))) (fun (Si : Prod.{u1, u3} (Set.{u1} α) ι) => UniformOnFun.gen.{u1, u2} α β 𝔖 (Prod.fst.{u1, u3} (Set.{u1} α) ι Si) (s (Prod.snd.{u1, u3} (Set.{u1} α) ι Si)))))
but is expected to have type
  forall (α : Type.{u3}) (β : Type.{u2}) {ι : Type.{u1}} [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u3} (Set.{u3} α)), (Set.Nonempty.{u3} (Set.{u3} α) 𝔖) -> (DirectedOn.{u3} (Set.{u3} α) (fun (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7725 : Set.{u3} α) (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7727 : Set.{u3} α) => HasSubset.Subset.{u3} (Set.{u3} α) (Set.instHasSubsetSet.{u3} α) x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7725 x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7727) 𝔖) -> (forall {p : ι -> Prop} {s : ι -> (Set.{u2} (Prod.{u2, u2} β β))}, (Filter.HasBasis.{u2, succ u1} (Prod.{u2, u2} β β) ι (uniformity.{u2} β _inst_1) p s) -> (Filter.HasBasis.{max u3 u2, max (succ u3) (succ u1)} (Prod.{max u2 u3, max u2 u3} (UniformOnFun.{u3, u2} α β 𝔖) (UniformOnFun.{u3, u2} α β 𝔖)) (Prod.{u3, u1} (Set.{u3} α) ι) (uniformity.{max u2 u3} (UniformOnFun.{u3, u2} α β 𝔖) (UniformOnFun.uniformSpace.{u3, u2} α β _inst_1 𝔖)) (fun (Si : Prod.{u3, u1} (Set.{u3} α) ι) => And (Membership.mem.{u3, u3} (Set.{u3} α) (Set.{u3} (Set.{u3} α)) (Set.instMembershipSet.{u3} (Set.{u3} α)) (Prod.fst.{u3, u1} (Set.{u3} α) ι Si) 𝔖) (p (Prod.snd.{u3, u1} (Set.{u3} α) ι Si))) (fun (Si : Prod.{u3, u1} (Set.{u3} α) ι) => UniformOnFun.gen.{u3, u2} α β 𝔖 (Prod.fst.{u3, u1} (Set.{u3} α) ι Si) (s (Prod.snd.{u3, u1} (Set.{u3} α) ι Si)))))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.has_basis_uniformity_of_basis UniformOnFun.hasBasis_uniformity_of_basisₓ'. -/
/-- If `𝔖 : set (set α)` is nonempty and directed and `𝓑` is a filter basis of `𝓤 β`, then the
uniformity of `α →ᵤ[𝔖] β` admits the family `{(f, g) | ∀ x ∈ S, (f x, g x) ∈ V}` for `S ∈ 𝔖` and
`V ∈ 𝓑` as a filter basis. -/
protected theorem hasBasis_uniformity_of_basis (h : 𝔖.Nonempty) (h' : DirectedOn (· ⊆ ·) 𝔖)
    {p : ι → Prop} {s : ι → Set (β × β)} (hb : HasBasis (𝓤 β) p s) :
    (𝓤 (α →ᵤ[𝔖] β)).HasBasis (fun Si : Set α × ι => Si.1 ∈ 𝔖 ∧ p Si.2) fun Si =>
      UniformOnFun.gen 𝔖 Si.1 (s Si.2) :=
  by
  simp only [infᵢ_uniformity]
  exact
    has_basis_binfi_of_directed h (fun S => UniformOnFun.gen 𝔖 S ∘ s) _
      (fun S hS => UniformOnFun.hasBasis_uniformity_of_basis_aux₁ α β 𝔖 hb S)
      (UniformOnFun.hasBasis_uniformity_of_basis_aux₂ α β 𝔖 h' hb)
#align uniform_on_fun.has_basis_uniformity_of_basis UniformOnFun.hasBasis_uniformity_of_basis

/- warning: uniform_on_fun.has_basis_uniformity -> UniformOnFun.hasBasis_uniformity is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}) [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u1} (Set.{u1} α)), (Set.Nonempty.{u1} (Set.{u1} α) 𝔖) -> (DirectedOn.{u1} (Set.{u1} α) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α)) 𝔖) -> (Filter.HasBasis.{max u1 u2, max (succ u1) (succ u2)} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖)) (Prod.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β))) (uniformity.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.uniformSpace.{u1, u2} α β _inst_1 𝔖)) (fun (SV : Prod.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β))) => And (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) (Prod.fst.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV) 𝔖) (Membership.Mem.{u2, u2} (Set.{u2} (Prod.{u2, u2} β β)) (Filter.{u2} (Prod.{u2, u2} β β)) (Filter.hasMem.{u2} (Prod.{u2, u2} β β)) (Prod.snd.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV) (uniformity.{u2} β _inst_1))) (fun (SV : Prod.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β))) => UniformOnFun.gen.{u1, u2} α β 𝔖 (Prod.fst.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV) (Prod.snd.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV)))
but is expected to have type
  forall (α : Type.{u2}) (β : Type.{u1}) [_inst_1 : UniformSpace.{u1} β] (𝔖 : Set.{u2} (Set.{u2} α)), (Set.Nonempty.{u2} (Set.{u2} α) 𝔖) -> (DirectedOn.{u2} (Set.{u2} α) (fun (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7877 : Set.{u2} α) (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7879 : Set.{u2} α) => HasSubset.Subset.{u2} (Set.{u2} α) (Set.instHasSubsetSet.{u2} α) x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7877 x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7879) 𝔖) -> (Filter.HasBasis.{max u2 u1, max (succ u2) (succ u1)} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.{u2, u1} α β 𝔖)) (Prod.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β))) (uniformity.{max u1 u2} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.uniformSpace.{u2, u1} α β _inst_1 𝔖)) (fun (SV : Prod.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β))) => And (Membership.mem.{u2, u2} (Set.{u2} α) (Set.{u2} (Set.{u2} α)) (Set.instMembershipSet.{u2} (Set.{u2} α)) (Prod.fst.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV) 𝔖) (Membership.mem.{u1, u1} (Set.{u1} (Prod.{u1, u1} β β)) (Filter.{u1} (Prod.{u1, u1} β β)) (instMembershipSetFilter.{u1} (Prod.{u1, u1} β β)) (Prod.snd.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV) (uniformity.{u1} β _inst_1))) (fun (SV : Prod.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β))) => UniformOnFun.gen.{u2, u1} α β 𝔖 (Prod.fst.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV) (Prod.snd.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV)))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.has_basis_uniformity UniformOnFun.hasBasis_uniformityₓ'. -/
/-- If `𝔖 : set (set α)` is nonempty and directed, then the uniformity of `α →ᵤ[𝔖] β` admits the
family `{(f, g) | ∀ x ∈ S, (f x, g x) ∈ V}` for `S ∈ 𝔖` and `V ∈ 𝓤 β` as a filter basis. -/
protected theorem hasBasis_uniformity (h : 𝔖.Nonempty) (h' : DirectedOn (· ⊆ ·) 𝔖) :
    (𝓤 (α →ᵤ[𝔖] β)).HasBasis (fun SV : Set α × Set (β × β) => SV.1 ∈ 𝔖 ∧ SV.2 ∈ 𝓤 β) fun SV =>
      UniformOnFun.gen 𝔖 SV.1 SV.2 :=
  UniformOnFun.hasBasis_uniformity_of_basis α β 𝔖 h h' (𝓤 β).basis_sets
#align uniform_on_fun.has_basis_uniformity UniformOnFun.hasBasis_uniformity

/- warning: uniform_on_fun.has_basis_nhds_of_basis -> UniformOnFun.hasBasis_nhds_of_basis is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}) {ι : Type.{u3}} [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u1} (Set.{u1} α)) (f : UniformOnFun.{u1, u2} α β 𝔖), (Set.Nonempty.{u1} (Set.{u1} α) 𝔖) -> (DirectedOn.{u1} (Set.{u1} α) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α)) 𝔖) -> (forall {p : ι -> Prop} {s : ι -> (Set.{u2} (Prod.{u2, u2} β β))}, (Filter.HasBasis.{u2, succ u3} (Prod.{u2, u2} β β) ι (uniformity.{u2} β _inst_1) p s) -> (Filter.HasBasis.{max u1 u2, max (succ u1) (succ u3)} (UniformOnFun.{u1, u2} α β 𝔖) (Prod.{u1, u3} (Set.{u1} α) ι) (nhds.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.topologicalSpace.{u1, u2} α β _inst_1 𝔖) f) (fun (Si : Prod.{u1, u3} (Set.{u1} α) ι) => And (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) (Prod.fst.{u1, u3} (Set.{u1} α) ι Si) 𝔖) (p (Prod.snd.{u1, u3} (Set.{u1} α) ι Si))) (fun (Si : Prod.{u1, u3} (Set.{u1} α) ι) => setOf.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (fun (g : UniformOnFun.{u1, u2} α β 𝔖) => Membership.Mem.{max u1 u2, max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖)) (Set.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖))) (Set.hasMem.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖))) (Prod.mk.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖) g f) (UniformOnFun.gen.{u1, u2} α β 𝔖 (Prod.fst.{u1, u3} (Set.{u1} α) ι Si) (s (Prod.snd.{u1, u3} (Set.{u1} α) ι Si)))))))
but is expected to have type
  forall (α : Type.{u3}) (β : Type.{u2}) {ι : Type.{u1}} [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u3} (Set.{u3} α)) (f : UniformOnFun.{u3, u2} α β 𝔖), (Set.Nonempty.{u3} (Set.{u3} α) 𝔖) -> (DirectedOn.{u3} (Set.{u3} α) (fun (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7997 : Set.{u3} α) (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7999 : Set.{u3} α) => HasSubset.Subset.{u3} (Set.{u3} α) (Set.instHasSubsetSet.{u3} α) x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7997 x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.7999) 𝔖) -> (forall {p : ι -> Prop} {s : ι -> (Set.{u2} (Prod.{u2, u2} β β))}, (Filter.HasBasis.{u2, succ u1} (Prod.{u2, u2} β β) ι (uniformity.{u2} β _inst_1) p s) -> (Filter.HasBasis.{max u3 u2, max (succ u3) (succ u1)} (UniformOnFun.{u3, u2} α β 𝔖) (Prod.{u3, u1} (Set.{u3} α) ι) (nhds.{max u3 u2} (UniformOnFun.{u3, u2} α β 𝔖) (UniformOnFun.topologicalSpace.{u3, u2} α β _inst_1 𝔖) f) (fun (Si : Prod.{u3, u1} (Set.{u3} α) ι) => And (Membership.mem.{u3, u3} (Set.{u3} α) (Set.{u3} (Set.{u3} α)) (Set.instMembershipSet.{u3} (Set.{u3} α)) (Prod.fst.{u3, u1} (Set.{u3} α) ι Si) 𝔖) (p (Prod.snd.{u3, u1} (Set.{u3} α) ι Si))) (fun (Si : Prod.{u3, u1} (Set.{u3} α) ι) => setOf.{max u3 u2} (UniformOnFun.{u3, u2} α β 𝔖) (fun (g : UniformOnFun.{u3, u2} α β 𝔖) => Membership.mem.{max u3 u2, max u3 u2} (Prod.{max u3 u2, max u3 u2} (UniformOnFun.{u3, u2} α β 𝔖) (UniformOnFun.{u3, u2} α β 𝔖)) (Set.{max u2 u3} (Prod.{max u2 u3, max u2 u3} (UniformOnFun.{u3, u2} α β 𝔖) (UniformOnFun.{u3, u2} α β 𝔖))) (Set.instMembershipSet.{max u3 u2} (Prod.{max u2 u3, max u2 u3} (UniformOnFun.{u3, u2} α β 𝔖) (UniformOnFun.{u3, u2} α β 𝔖))) (Prod.mk.{max u3 u2, max u3 u2} (UniformOnFun.{u3, u2} α β 𝔖) (UniformOnFun.{u3, u2} α β 𝔖) g f) (UniformOnFun.gen.{u3, u2} α β 𝔖 (Prod.fst.{u3, u1} (Set.{u3} α) ι Si) (s (Prod.snd.{u3, u1} (Set.{u3} α) ι Si)))))))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.has_basis_nhds_of_basis UniformOnFun.hasBasis_nhds_of_basisₓ'. -/
/-- For `f : α →ᵤ[𝔖] β`, where `𝔖 : set (set α)` is nonempty and directed, `𝓝 f` admits the
family `{g | ∀ x ∈ S, (f x, g x) ∈ V}` for `S ∈ 𝔖` and `V ∈ 𝓑` as a filter basis, for any basis
`𝓑` of `𝓤 β`. -/
protected theorem hasBasis_nhds_of_basis (f : α →ᵤ[𝔖] β) (h : 𝔖.Nonempty)
    (h' : DirectedOn (· ⊆ ·) 𝔖) {p : ι → Prop} {s : ι → Set (β × β)} (hb : HasBasis (𝓤 β) p s) :
    (𝓝 f).HasBasis (fun Si : Set α × ι => Si.1 ∈ 𝔖 ∧ p Si.2) fun Si =>
      { g | (g, f) ∈ UniformOnFun.gen 𝔖 Si.1 (s Si.2) } :=
  letI : UniformSpace (α → β) := UniformOnFun.uniformSpace α β 𝔖
  nhds_basis_uniformity (UniformOnFun.hasBasis_uniformity_of_basis α β 𝔖 h h' hb)
#align uniform_on_fun.has_basis_nhds_of_basis UniformOnFun.hasBasis_nhds_of_basis

/- warning: uniform_on_fun.has_basis_nhds -> UniformOnFun.hasBasis_nhds is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}) [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u1} (Set.{u1} α)) (f : UniformOnFun.{u1, u2} α β 𝔖), (Set.Nonempty.{u1} (Set.{u1} α) 𝔖) -> (DirectedOn.{u1} (Set.{u1} α) (HasSubset.Subset.{u1} (Set.{u1} α) (Set.hasSubset.{u1} α)) 𝔖) -> (Filter.HasBasis.{max u1 u2, max (succ u1) (succ u2)} (UniformOnFun.{u1, u2} α β 𝔖) (Prod.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β))) (nhds.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.topologicalSpace.{u1, u2} α β _inst_1 𝔖) f) (fun (SV : Prod.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β))) => And (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) (Prod.fst.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV) 𝔖) (Membership.Mem.{u2, u2} (Set.{u2} (Prod.{u2, u2} β β)) (Filter.{u2} (Prod.{u2, u2} β β)) (Filter.hasMem.{u2} (Prod.{u2, u2} β β)) (Prod.snd.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV) (uniformity.{u2} β _inst_1))) (fun (SV : Prod.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β))) => setOf.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (fun (g : UniformOnFun.{u1, u2} α β 𝔖) => Membership.Mem.{max u1 u2, max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖)) (Set.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖))) (Set.hasMem.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖))) (Prod.mk.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u1, u2} α β 𝔖) g f) (UniformOnFun.gen.{u1, u2} α β 𝔖 (Prod.fst.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV) (Prod.snd.{u1, u2} (Set.{u1} α) (Set.{u2} (Prod.{u2, u2} β β)) SV)))))
but is expected to have type
  forall (α : Type.{u2}) (β : Type.{u1}) [_inst_1 : UniformSpace.{u1} β] (𝔖 : Set.{u2} (Set.{u2} α)) (f : UniformOnFun.{u2, u1} α β 𝔖), (Set.Nonempty.{u2} (Set.{u2} α) 𝔖) -> (DirectedOn.{u2} (Set.{u2} α) (fun (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.8146 : Set.{u2} α) (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.8148 : Set.{u2} α) => HasSubset.Subset.{u2} (Set.{u2} α) (Set.instHasSubsetSet.{u2} α) x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.8146 x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.8148) 𝔖) -> (Filter.HasBasis.{max u2 u1, max (succ u2) (succ u1)} (UniformOnFun.{u2, u1} α β 𝔖) (Prod.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β))) (nhds.{max u2 u1} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.topologicalSpace.{u2, u1} α β _inst_1 𝔖) f) (fun (SV : Prod.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β))) => And (Membership.mem.{u2, u2} (Set.{u2} α) (Set.{u2} (Set.{u2} α)) (Set.instMembershipSet.{u2} (Set.{u2} α)) (Prod.fst.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV) 𝔖) (Membership.mem.{u1, u1} (Set.{u1} (Prod.{u1, u1} β β)) (Filter.{u1} (Prod.{u1, u1} β β)) (instMembershipSetFilter.{u1} (Prod.{u1, u1} β β)) (Prod.snd.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV) (uniformity.{u1} β _inst_1))) (fun (SV : Prod.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β))) => setOf.{max u2 u1} (UniformOnFun.{u2, u1} α β 𝔖) (fun (g : UniformOnFun.{u2, u1} α β 𝔖) => Membership.mem.{max u2 u1, max u2 u1} (Prod.{max u2 u1, max u2 u1} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.{u2, u1} α β 𝔖)) (Set.{max u1 u2} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.{u2, u1} α β 𝔖))) (Set.instMembershipSet.{max u2 u1} (Prod.{max u1 u2, max u1 u2} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.{u2, u1} α β 𝔖))) (Prod.mk.{max u2 u1, max u2 u1} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.{u2, u1} α β 𝔖) g f) (UniformOnFun.gen.{u2, u1} α β 𝔖 (Prod.fst.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV) (Prod.snd.{u2, u1} (Set.{u2} α) (Set.{u1} (Prod.{u1, u1} β β)) SV)))))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.has_basis_nhds UniformOnFun.hasBasis_nhdsₓ'. -/
/-- For `f : α →ᵤ[𝔖] β`, where `𝔖 : set (set α)` is nonempty and directed, `𝓝 f` admits the
family `{g | ∀ x ∈ S, (f x, g x) ∈ V}` for `S ∈ 𝔖` and `V ∈ 𝓤 β` as a filter basis. -/
protected theorem hasBasis_nhds (f : α →ᵤ[𝔖] β) (h : 𝔖.Nonempty) (h' : DirectedOn (· ⊆ ·) 𝔖) :
    (𝓝 f).HasBasis (fun SV : Set α × Set (β × β) => SV.1 ∈ 𝔖 ∧ SV.2 ∈ 𝓤 β) fun SV =>
      { g | (g, f) ∈ UniformOnFun.gen 𝔖 SV.1 SV.2 } :=
  UniformOnFun.hasBasis_nhds_of_basis α β 𝔖 f h h' (Filter.basis_sets _)
#align uniform_on_fun.has_basis_nhds UniformOnFun.hasBasis_nhds

/- warning: uniform_on_fun.uniform_continuous_restrict -> UniformOnFun.uniformContinuous_restrict is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) (β : Type.{u2}) {s : Set.{u1} α} [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u1} (Set.{u1} α)), (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) s 𝔖) -> (UniformContinuous.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformFun.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β) (UniformOnFun.uniformSpace.{u1, u2} α β _inst_1 𝔖) (UniformFun.uniformSpace.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β _inst_1) (Function.comp.{succ (max u1 u2), max (succ u1) (succ u2), succ (max u1 u2)} (UniformOnFun.{u1, u2} α β 𝔖) ((coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) -> β) (UniformFun.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} ((coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) -> β) (UniformFun.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β)) (fun (_x : Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} ((coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) -> β) (UniformFun.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β)) => ((coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) -> β) -> (UniformFun.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β)) (Equiv.hasCoeToFun.{max (succ u1) (succ u2), max (succ u1) (succ u2)} ((coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) -> β) (UniformFun.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β)) (UniformFun.ofFun.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) β)) (Function.comp.{succ (max u1 u2), max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β) ((coeSort.{succ u1, succ (succ u1)} (Set.{u1} α) Type.{u1} (Set.hasCoeToSort.{u1} α) s) -> β) (Set.restrict.{u1, u2} α (fun (ᾰ : α) => β) s) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β)) (fun (_x : Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β)) => (UniformOnFun.{u1, u2} α β 𝔖) -> α -> β) (Equiv.hasCoeToFun.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β)) (UniformOnFun.toFun.{u1, u2} α β 𝔖)))))
but is expected to have type
  forall (α : Type.{u2}) (β : Type.{u1}) {s : Set.{u2} α} [_inst_1 : UniformSpace.{u1} β] (𝔖 : Set.{u2} (Set.{u2} α)), (Membership.mem.{u2, u2} (Set.{u2} α) (Set.{u2} (Set.{u2} α)) (Set.instMembershipSet.{u2} (Set.{u2} α)) s 𝔖) -> (UniformContinuous.{max u2 u1, max u1 u2} (UniformOnFun.{u2, u1} α β 𝔖) (UniformFun.{u2, u1} (Set.Elem.{u2} α s) β) (UniformOnFun.uniformSpace.{u2, u1} α β _inst_1 𝔖) (UniformFun.uniformSpace.{u2, u1} (Set.Elem.{u2} α s) β _inst_1) (Function.comp.{succ (max u2 u1), max (succ u1) (succ u2), succ (max u1 u2)} (UniformOnFun.{u2, u1} α β 𝔖) ((Set.Elem.{u2} α s) -> β) (UniformFun.{u2, u1} (Set.Elem.{u2} α s) β) (FunLike.coe.{max (succ u1) (succ u2), max (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u2) (succ u1), max (succ u1) (succ u2)} ((Set.Elem.{u2} α s) -> β) (UniformFun.{u2, u1} (Set.Elem.{u2} α s) β)) ((Set.Elem.{u2} α s) -> β) (fun (_x : (Set.Elem.{u2} α s) -> β) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : (Set.Elem.{u2} α s) -> β) => UniformFun.{u2, u1} (Set.Elem.{u2} α s) β) _x) (Equiv.instFunLikeEquiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} ((Set.Elem.{u2} α s) -> β) (UniformFun.{u2, u1} (Set.Elem.{u2} α s) β)) (UniformFun.ofFun.{u2, u1} (Set.Elem.{u2} α s) β)) (Function.comp.{succ (max u2 u1), max (succ u2) (succ u1), max (succ u1) (succ u2)} (UniformOnFun.{u2, u1} α β 𝔖) (α -> β) ((Set.Elem.{u2} α s) -> β) (Set.restrict.{u2, u1} α (fun (ᾰ : α) => β) s) (FunLike.coe.{max (succ u2) (succ u1), max (succ u2) (succ u1), max (succ u2) (succ u1)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u2, u1} α β 𝔖) (α -> β)) (UniformOnFun.{u2, u1} α β 𝔖) (fun (_x : UniformOnFun.{u2, u1} α β 𝔖) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : UniformOnFun.{u2, u1} α β 𝔖) => α -> β) _x) (Equiv.instFunLikeEquiv.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (UniformOnFun.{u2, u1} α β 𝔖) (α -> β)) (UniformOnFun.toFun.{u2, u1} α β 𝔖)))))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.uniform_continuous_restrict UniformOnFun.uniformContinuous_restrictₓ'. -/
/-- If `S ∈ 𝔖`, then the restriction to `S` is a uniformly continuous map from `α →ᵤ[𝔖] β` to
`↥S →ᵤ β`. -/
protected theorem uniformContinuous_restrict (h : s ∈ 𝔖) :
    UniformContinuous (UniformFun.ofFun ∘ (s.restrict : (α → β) → s → β) ∘ toFun 𝔖) :=
  by
  change _ ≤ _
  simp only [UniformOnFun.uniformSpace, map_le_iff_le_comap, infᵢ_uniformity]
  exact infᵢ₂_le s h
#align uniform_on_fun.uniform_continuous_restrict UniformOnFun.uniformContinuous_restrict

variable {α}

/- warning: uniform_on_fun.mono -> UniformOnFun.mono is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {γ : Type.{u2}} {{u₁ : UniformSpace.{u2} γ}} {{u₂ : UniformSpace.{u2} γ}}, (LE.le.{u2} (UniformSpace.{u2} γ) (Preorder.toLE.{u2} (UniformSpace.{u2} γ) (PartialOrder.toPreorder.{u2} (UniformSpace.{u2} γ) (UniformSpace.partialOrder.{u2} γ))) u₁ u₂) -> (forall {{𝔖₁ : Set.{u1} (Set.{u1} α)}} {{𝔖₂ : Set.{u1} (Set.{u1} α)}}, (HasSubset.Subset.{u1} (Set.{u1} (Set.{u1} α)) (Set.hasSubset.{u1} (Set.{u1} α)) 𝔖₂ 𝔖₁) -> (LE.le.{max u1 u2} (UniformSpace.{max u1 u2} (UniformOnFun.{u1, u2} α γ 𝔖₁)) (Preorder.toLE.{max u1 u2} (UniformSpace.{max u1 u2} (UniformOnFun.{u1, u2} α γ 𝔖₁)) (PartialOrder.toPreorder.{max u1 u2} (UniformSpace.{max u1 u2} (UniformOnFun.{u1, u2} α γ 𝔖₁)) (UniformSpace.partialOrder.{max u1 u2} (UniformOnFun.{u1, u2} α γ 𝔖₁)))) (UniformOnFun.uniformSpace.{u1, u2} α γ u₁ 𝔖₁) (UniformOnFun.uniformSpace.{u1, u2} α γ u₂ 𝔖₂)))
but is expected to have type
  forall {α : Type.{u1}} {γ : Type.{u2}} {{u₁ : UniformSpace.{u2} γ}} {{u₂ : UniformSpace.{u2} γ}}, (LE.le.{u2} (UniformSpace.{u2} γ) (Preorder.toLE.{u2} (UniformSpace.{u2} γ) (PartialOrder.toPreorder.{u2} (UniformSpace.{u2} γ) (instPartialOrderUniformSpace.{u2} γ))) u₁ u₂) -> (forall {{𝔖₁ : Set.{u1} (Set.{u1} α)}} {{𝔖₂ : Set.{u1} (Set.{u1} α)}}, (HasSubset.Subset.{u1} (Set.{u1} (Set.{u1} α)) (Set.instHasSubsetSet.{u1} (Set.{u1} α)) 𝔖₂ 𝔖₁) -> (LE.le.{max u1 u2} (UniformSpace.{max u2 u1} (UniformOnFun.{u1, u2} α γ 𝔖₁)) (Preorder.toLE.{max u1 u2} (UniformSpace.{max u2 u1} (UniformOnFun.{u1, u2} α γ 𝔖₁)) (PartialOrder.toPreorder.{max u1 u2} (UniformSpace.{max u2 u1} (UniformOnFun.{u1, u2} α γ 𝔖₁)) (instPartialOrderUniformSpace.{max u1 u2} (UniformOnFun.{u1, u2} α γ 𝔖₁)))) (UniformOnFun.uniformSpace.{u1, u2} α γ u₁ 𝔖₁) (UniformOnFun.uniformSpace.{u1, u2} α γ u₂ 𝔖₂)))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.mono UniformOnFun.monoₓ'. -/
/-- Let `u₁`, `u₂` be two uniform structures on `γ` and `𝔖₁ 𝔖₂ : set (set α)`. If `u₁ ≤ u₂` and
`𝔖₂ ⊆ 𝔖₁` then `𝒱(α, γ, 𝔖₁, u₁) ≤ 𝒱(α, γ, 𝔖₂, u₂)`. -/
protected theorem mono ⦃u₁ u₂ : UniformSpace γ⦄ (hu : u₁ ≤ u₂) ⦃𝔖₁ 𝔖₂ : Set (Set α)⦄
    (h𝔖 : 𝔖₂ ⊆ 𝔖₁) : 𝒱(α, γ, 𝔖₁, u₁) ≤ 𝒱(α, γ, 𝔖₂, u₂) :=
  calc
    𝒱(α, γ, 𝔖₁, u₁) ≤ 𝒱(α, γ, 𝔖₂, u₁) := infᵢ_le_infᵢ_of_subset h𝔖
    _ ≤ 𝒱(α, γ, 𝔖₂, u₂) := infᵢ₂_mono fun i hi => UniformSpace.comap_mono <| UniformFun.mono hu

#align uniform_on_fun.mono UniformOnFun.mono

/- warning: uniform_on_fun.uniform_continuous_eval_of_mem -> UniformOnFun.uniformContinuous_eval_of_mem is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} (β : Type.{u2}) {s : Set.{u1} α} [_inst_1 : UniformSpace.{u2} β] (𝔖 : Set.{u1} (Set.{u1} α)) {x : α}, (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x s) -> (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) s 𝔖) -> (UniformContinuous.{max u1 u2, u2} (UniformOnFun.{u1, u2} α β 𝔖) β (UniformOnFun.uniformSpace.{u1, u2} α β _inst_1 𝔖) _inst_1 (Function.comp.{succ (max u1 u2), max (succ u1) (succ u2), succ u2} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β) β (Function.eval.{succ u1, succ u2} α (fun (ᾰ : α) => β) x) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β)) (fun (_x : Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β)) => (UniformOnFun.{u1, u2} α β 𝔖) -> α -> β) (Equiv.hasCoeToFun.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β)) (UniformOnFun.toFun.{u1, u2} α β 𝔖))))
but is expected to have type
  forall {α : Type.{u2}} (β : Type.{u1}) {s : Set.{u2} α} [_inst_1 : UniformSpace.{u1} β] (𝔖 : Set.{u2} (Set.{u2} α)) {x : α}, (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) x s) -> (Membership.mem.{u2, u2} (Set.{u2} α) (Set.{u2} (Set.{u2} α)) (Set.instMembershipSet.{u2} (Set.{u2} α)) s 𝔖) -> (UniformContinuous.{max u2 u1, u1} (UniformOnFun.{u2, u1} α β 𝔖) β (UniformOnFun.uniformSpace.{u2, u1} α β _inst_1 𝔖) _inst_1 (Function.comp.{succ (max u2 u1), max (succ u2) (succ u1), succ u1} (UniformOnFun.{u2, u1} α β 𝔖) (α -> β) β (Function.eval.{succ u2, succ u1} α (fun (ᾰ : α) => β) x) (FunLike.coe.{max (succ u2) (succ u1), max (succ u2) (succ u1), max (succ u2) (succ u1)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u2, u1} α β 𝔖) (α -> β)) (UniformOnFun.{u2, u1} α β 𝔖) (fun (_x : UniformOnFun.{u2, u1} α β 𝔖) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : UniformOnFun.{u2, u1} α β 𝔖) => α -> β) _x) (Equiv.instFunLikeEquiv.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (UniformOnFun.{u2, u1} α β 𝔖) (α -> β)) (UniformOnFun.toFun.{u2, u1} α β 𝔖))))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.uniform_continuous_eval_of_mem UniformOnFun.uniformContinuous_eval_of_memₓ'. -/
/-- If `x : α` is in some `S ∈ 𝔖`, then evaluation at `x` is uniformly continuous on
`α →ᵤ[𝔖] β`. -/
theorem uniformContinuous_eval_of_mem {x : α} (hxs : x ∈ s) (hs : s ∈ 𝔖) :
    UniformContinuous ((Function.eval x : (α → β) → β) ∘ toFun 𝔖) :=
  (UniformFun.uniformContinuous_eval β (⟨x, hxs⟩ : s)).comp
    (UniformOnFun.uniformContinuous_restrict α β 𝔖 hs)
#align uniform_on_fun.uniform_continuous_eval_of_mem UniformOnFun.uniformContinuous_eval_of_mem

variable {β} {𝔖}

/- warning: uniform_on_fun.infi_eq -> UniformOnFun.infᵢ_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {γ : Type.{u2}} {ι : Type.{u3}} {𝔖 : Set.{u1} (Set.{u1} α)} {u : ι -> (UniformSpace.{u2} γ)}, Eq.{succ (max u1 u2)} (UniformSpace.{max u1 u2} (UniformOnFun.{u1, u2} α γ 𝔖)) (UniformOnFun.uniformSpace.{u1, u2} α γ (infᵢ.{u2, succ u3} (UniformSpace.{u2} γ) (UniformSpace.hasInf.{u2} γ) ι (fun (i : ι) => u i)) 𝔖) (infᵢ.{max u1 u2, succ u3} (UniformSpace.{max u1 u2} (UniformOnFun.{u1, u2} α γ 𝔖)) (UniformSpace.hasInf.{max u1 u2} (UniformOnFun.{u1, u2} α γ 𝔖)) ι (fun (i : ι) => UniformOnFun.uniformSpace.{u1, u2} α γ (u i) 𝔖))
but is expected to have type
  forall {α : Type.{u2}} {γ : Type.{u3}} {ι : Type.{u1}} {𝔖 : Set.{u2} (Set.{u2} α)} {u : ι -> (UniformSpace.{u3} γ)}, Eq.{max (succ u2) (succ u3)} (UniformSpace.{max u3 u2} (UniformOnFun.{u2, u3} α γ 𝔖)) (UniformOnFun.uniformSpace.{u2, u3} α γ (infᵢ.{u3, succ u1} (UniformSpace.{u3} γ) (instInfSetUniformSpace.{u3} γ) ι (fun (i : ι) => u i)) 𝔖) (infᵢ.{max u2 u3, succ u1} (UniformSpace.{max u3 u2} (UniformOnFun.{u2, u3} α γ 𝔖)) (instInfSetUniformSpace.{max u2 u3} (UniformOnFun.{u2, u3} α γ 𝔖)) ι (fun (i : ι) => UniformOnFun.uniformSpace.{u2, u3} α γ (u i) 𝔖))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.infi_eq UniformOnFun.infᵢ_eqₓ'. -/
/-- If `u` is a family of uniform structures on `γ`, then
`𝒱(α, γ, 𝔖, (⨅ i, u i)) = ⨅ i, 𝒱(α, γ, 𝔖, u i)`. -/
protected theorem infᵢ_eq {u : ι → UniformSpace γ} : 𝒱(α, γ, 𝔖, ⨅ i, u i) = ⨅ i, 𝒱(α, γ, 𝔖, u i) :=
  by
  simp_rw [UniformOnFun.uniformSpace, UniformFun.infᵢ_eq, UniformSpace.comap_infᵢ]
  rw [infᵢ_comm]
  exact infᵢ_congr fun s => infᵢ_comm
#align uniform_on_fun.infi_eq UniformOnFun.infᵢ_eq

#print UniformOnFun.inf_eq /-
/-- If `u₁` and `u₂` are two uniform structures on `γ`, then
`𝒱(α, γ, 𝔖, u₁ ⊓ u₂) = 𝒱(α, γ, 𝔖, u₁) ⊓ 𝒱(α, γ, 𝔖, u₂)`. -/
protected theorem inf_eq {u₁ u₂ : UniformSpace γ} :
    𝒱(α, γ, 𝔖, u₁ ⊓ u₂) = 𝒱(α, γ, 𝔖, u₁) ⊓ 𝒱(α, γ, 𝔖, u₂) :=
  by
  rw [inf_eq_infᵢ, inf_eq_infᵢ, UniformOnFun.infᵢ_eq]
  refine' infᵢ_congr fun i => _
  cases i <;> rfl
#align uniform_on_fun.inf_eq UniformOnFun.inf_eq
-/

/- warning: uniform_on_fun.comap_eq -> UniformOnFun.comap_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : UniformSpace.{u2} β] {𝔖 : Set.{u1} (Set.{u1} α)} {f : γ -> β}, Eq.{succ (max u1 u3)} (UniformSpace.{max u1 u3} (UniformOnFun.{u1, u3} α γ 𝔖)) (UniformOnFun.uniformSpace.{u1, u3} α γ (UniformSpace.comap.{u3, u2} γ β f _inst_1) 𝔖) (UniformSpace.comap.{max u1 u3, max u1 u2} (UniformOnFun.{u1, u3} α γ 𝔖) (UniformOnFun.{u1, u2} α β 𝔖) (Function.comp.{succ u1, succ u3, succ u2} α γ β f) (UniformOnFun.uniformSpace.{u1, u2} α β _inst_1 𝔖))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u1}} {γ : Type.{u2}} [_inst_1 : UniformSpace.{u1} β] {𝔖 : Set.{u3} (Set.{u3} α)} {f : γ -> β}, Eq.{max (succ u3) (succ u2)} (UniformSpace.{max u2 u3} (UniformOnFun.{u3, u2} α γ 𝔖)) (UniformOnFun.uniformSpace.{u3, u2} α γ (UniformSpace.comap.{u2, u1} γ β f _inst_1) 𝔖) (UniformSpace.comap.{max u3 u2, max u3 u1} (α -> γ) (α -> β) ((fun (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.8859 : γ -> β) (x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.8861 : α -> γ) => Function.comp.{succ u3, succ u2, succ u1} α γ β x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.8859 x._@.Mathlib.Topology.UniformSpace.UniformConvergenceTopology._hyg.8861) f) (UniformOnFun.uniformSpace.{u3, u1} α β _inst_1 𝔖))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.comap_eq UniformOnFun.comap_eqₓ'. -/
/-- If `u` is a uniform structures on `β` and `f : γ → β`, then
`𝒱(α, γ, 𝔖, comap f u) = comap (λ g, f ∘ g) 𝒱(α, γ, 𝔖, u₁)`. -/
protected theorem comap_eq {f : γ → β} :
    𝒱(α, γ, 𝔖, ‹UniformSpace β›.comap f) = 𝒱(α, β, 𝔖, _).comap ((· ∘ ·) f) :=
  by
  -- We reduce this to `uniform_convergence.comap_eq` using the fact that `comap` distributes
  -- on `infi`.
  simp_rw [UniformOnFun.uniformSpace, UniformSpace.comap_infᵢ, UniformFun.comap_eq, ←
    UniformSpace.comap_comap]
  rfl
#align uniform_on_fun.comap_eq UniformOnFun.comap_eq

#print UniformOnFun.postcomp_uniformContinuous /-
-- by definition, `∀ S ∈ 𝔖, (f ∘ —) ∘ S.restrict = S.restrict ∘ (f ∘ —)`.
/-- Post-composition by a uniformly continuous function is uniformly continuous for the
uniform structures of `𝔖`-convergence.

More precisely, if `f : γ → β` is uniformly continuous, then
`(λ g, f ∘ g) : (α →ᵤ[𝔖] γ) → (α →ᵤ[𝔖] β)` is uniformly continuous. -/
protected theorem postcomp_uniformContinuous [UniformSpace γ] {f : γ → β}
    (hf : UniformContinuous f) : UniformContinuous (ofFun 𝔖 ∘ (· ∘ ·) f ∘ toFun 𝔖) :=
  by
  -- This is a direct consequence of `uniform_convergence.comap_eq`
  rw [uniformContinuous_iff]
  calc
    𝒱(α, γ, 𝔖, _) ≤ 𝒱(α, γ, 𝔖, ‹UniformSpace β›.comap f) :=
      UniformOnFun.mono (uniform_continuous_iff.mp hf) subset_rfl
    _ = 𝒱(α, β, 𝔖, _).comap ((· ∘ ·) f) := UniformOnFun.comap_eq

#align uniform_on_fun.postcomp_uniform_continuous UniformOnFun.postcomp_uniformContinuous
-/

#print UniformOnFun.postcomp_uniformInducing /-
/-- Post-composition by a uniform inducing is a uniform inducing for the
uniform structures of `𝔖`-convergence.

More precisely, if `f : γ → β` is a uniform inducing, then
`(λ g, f ∘ g) : (α →ᵤ[𝔖] γ) → (α →ᵤ[𝔖] β)` is a uniform inducing. -/
protected theorem postcomp_uniformInducing [UniformSpace γ] {f : γ → β} (hf : UniformInducing f) :
    UniformInducing (ofFun 𝔖 ∘ (· ∘ ·) f ∘ toFun 𝔖) :=
  by
  -- This is a direct consequence of `uniform_convergence.comap_eq`
  constructor
  replace hf : (𝓤 β).comap (Prod.map f f) = _ := hf.comap_uniformity
  change comap (Prod.map (of_fun 𝔖 ∘ (· ∘ ·) f ∘ to_fun 𝔖) (of_fun 𝔖 ∘ (· ∘ ·) f ∘ to_fun 𝔖)) _ = _
  rw [← uniformity_comap] at hf⊢
  congr
  rw [← uniformSpace_eq hf, UniformOnFun.comap_eq]
  rfl
#align uniform_on_fun.postcomp_uniform_inducing UniformOnFun.postcomp_uniformInducing
-/

#print UniformOnFun.congrRight /-
/-- Turn a uniform isomorphism `γ ≃ᵤ β` into a uniform isomorphism `(α →ᵤ[𝔖] γ) ≃ᵤ (α →ᵤ[𝔖] β)`
by post-composing. -/
protected def congrRight [UniformSpace γ] (e : γ ≃ᵤ β) : (α →ᵤ[𝔖] γ) ≃ᵤ (α →ᵤ[𝔖] β) :=
  {
    Equiv.piCongrRight fun a =>
      e.toEquiv with
    uniformContinuous_toFun := UniformOnFun.postcomp_uniformContinuous e.UniformContinuous
    uniformContinuous_invFun := UniformOnFun.postcomp_uniformContinuous e.symm.UniformContinuous }
#align uniform_on_fun.congr_right UniformOnFun.congrRight
-/

/- warning: uniform_on_fun.precomp_uniform_continuous -> UniformOnFun.precomp_uniformContinuous is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} [_inst_1 : UniformSpace.{u2} β] {𝔖 : Set.{u1} (Set.{u1} α)} {𝔗 : Set.{u3} (Set.{u3} γ)} {f : γ -> α}, (HasSubset.Subset.{u3} (Set.{u3} (Set.{u3} γ)) (Set.hasSubset.{u3} (Set.{u3} γ)) 𝔗 (Set.preimage.{u3, u1} (Set.{u3} γ) (Set.{u1} α) (Set.image.{u3, u1} γ α f) 𝔖)) -> (UniformContinuous.{max u1 u2, max u3 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.{u3, u2} γ β 𝔗) (UniformOnFun.uniformSpace.{u1, u2} α β _inst_1 𝔖) (UniformOnFun.uniformSpace.{u3, u2} γ β _inst_1 𝔗) (fun (g : UniformOnFun.{u1, u2} α β 𝔖) => coeFn.{max 1 (succ u3) (succ u2), max (succ u3) (succ u2)} (Equiv.{max (succ u3) (succ u2), max (succ u3) (succ u2)} (γ -> β) (UniformOnFun.{u3, u2} γ β 𝔗)) (fun (_x : Equiv.{max (succ u3) (succ u2), max (succ u3) (succ u2)} (γ -> β) (UniformOnFun.{u3, u2} γ β 𝔗)) => (γ -> β) -> (UniformOnFun.{u3, u2} γ β 𝔗)) (Equiv.hasCoeToFun.{max (succ u3) (succ u2), max (succ u3) (succ u2)} (γ -> β) (UniformOnFun.{u3, u2} γ β 𝔗)) (UniformOnFun.ofFun.{u3, u2} γ β 𝔗) (Function.comp.{succ u3, succ u1, succ u2} γ α β g f)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} [_inst_1 : UniformSpace.{u1} β] {𝔖 : Set.{u2} (Set.{u2} α)} {𝔗 : Set.{u3} (Set.{u3} γ)} {f : γ -> α}, (HasSubset.Subset.{u3} (Set.{u3} (Set.{u3} γ)) (Set.instHasSubsetSet.{u3} (Set.{u3} γ)) 𝔗 (Set.preimage.{u3, u2} (Set.{u3} γ) (Set.{u2} α) (Set.image.{u3, u2} γ α f) 𝔖)) -> (UniformContinuous.{max u2 u1, max u1 u3} (UniformOnFun.{u2, u1} α β 𝔖) (UniformOnFun.{u3, u1} γ β 𝔗) (UniformOnFun.uniformSpace.{u2, u1} α β _inst_1 𝔖) (UniformOnFun.uniformSpace.{u3, u1} γ β _inst_1 𝔗) (fun (g : UniformOnFun.{u2, u1} α β 𝔖) => FunLike.coe.{max (succ u3) (succ u1), max (succ u3) (succ u1), max (succ u3) (succ u1)} (Equiv.{max (succ u3) (succ u1), max (succ u1) (succ u3)} (γ -> β) (UniformOnFun.{u3, u1} γ β 𝔗)) (γ -> β) (fun (_x : γ -> β) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : γ -> β) => UniformOnFun.{u3, u1} γ β 𝔗) _x) (Equiv.instFunLikeEquiv.{max (succ u3) (succ u1), max (succ u3) (succ u1)} (γ -> β) (UniformOnFun.{u3, u1} γ β 𝔗)) (UniformOnFun.ofFun.{u3, u1} γ β 𝔗) (Function.comp.{succ u3, succ u2, succ u1} γ α β g f)))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.precomp_uniform_continuous UniformOnFun.precomp_uniformContinuousₓ'. -/
/-- Let `f : γ → α`, `𝔖 : set (set α)`, `𝔗 : set (set γ)`, and assume that `∀ T ∈ 𝔗, f '' T ∈ 𝔖`.
Then, the function `(λ g, g ∘ f) : (α →ᵤ[𝔖] β) → (γ →ᵤ[𝔗] β)` is uniformly continuous.

Note that one can easily see that assuming `∀ T ∈ 𝔗, ∃ S ∈ 𝔖, f '' T ⊆ S` would work too, but
we will get this for free when we prove that `𝒱(α, β, 𝔖, uβ) = 𝒱(α, β, 𝔖', uβ)` where `𝔖'` is the
***noncovering*** bornology generated by `𝔖`. -/
protected theorem precomp_uniformContinuous {𝔗 : Set (Set γ)} {f : γ → α} (hf : 𝔗 ⊆ image f ⁻¹' 𝔖) :
    UniformContinuous fun g : α →ᵤ[𝔖] β => ofFun 𝔗 (g ∘ f) :=
  by
  -- Since `comap` distributes on `infi`, it suffices to prove that
  -- `⨅ s ∈ 𝔖, comap s.restrict 𝒰(↥s, β, uβ) ≤ ⨅ t ∈ 𝔗, comap (t.restrict ∘ (— ∘ f)) 𝒰(↥t, β, uβ)`.
  simp_rw [uniformContinuous_iff, UniformOnFun.uniformSpace, UniformSpace.comap_infᵢ, ←
    UniformSpace.comap_comap]
  -- For any `t ∈ 𝔗`, note `s := f '' t ∈ 𝔖`.
  -- We will show that `comap s.restrict 𝒰(↥s, β, uβ) ≤ comap (t.restrict ∘ (— ∘ f)) 𝒰(↥t, β, uβ)`.
  refine' le_infᵢ₂ fun t ht => infᵢ_le_of_le (f '' t) <| infᵢ_le_of_le (hf ht) _
  -- Let `f'` be the map from `t` to `f '' t` induced by `f`.
  let f' : t → f '' t := (maps_to_image f t).restrict f t (f '' t)
  -- By definition `t.restrict ∘ (— ∘ f) = (— ∘ f') ∘ (f '' t).restrict`.
  have :
    (t.restrict ∘ fun g : α →ᵤ[𝔖] β => of_fun 𝔗 (g ∘ f)) =
      (fun g : f '' t → β => g ∘ f') ∘ (f '' t).restrict :=
    rfl
  -- Thus, we have to show `comap (f '' t).restrict 𝒰(↥(f '' t), β, uβ) ≤`
  -- `comap (f '' t).restrict (comap (— ∘ f') 𝒰(↥t, β, uβ))`.
  rw [this, @UniformSpace.comap_comap (α →ᵤ[𝔖] β) (f '' t →ᵤ β)]
  -- But this is exactly monotonicity of `comap` applied to
  -- `uniform_convergence.precomp_continuous`.
  refine' UniformSpace.comap_mono _
  rw [← uniformContinuous_iff]
  exact UniformFun.precomp_uniformContinuous
#align uniform_on_fun.precomp_uniform_continuous UniformOnFun.precomp_uniformContinuous

#print UniformOnFun.congrLeft /-
/-- Turn a bijection `e : γ ≃ α` such that we have both `∀ T ∈ 𝔗, e '' T ∈ 𝔖` and
`∀ S ∈ 𝔖, e ⁻¹' S ∈ 𝔗` into a uniform isomorphism `(γ →ᵤ[𝔗] β) ≃ᵤ (α →ᵤ[𝔖] β)` by pre-composing. -/
protected def congrLeft {𝔗 : Set (Set γ)} (e : γ ≃ α) (he : 𝔗 ⊆ image e ⁻¹' 𝔖)
    (he' : 𝔖 ⊆ preimage e ⁻¹' 𝔗) : (γ →ᵤ[𝔗] β) ≃ᵤ (α →ᵤ[𝔖] β) :=
  {
    Equiv.arrowCongr e
      (Equiv.refl
        _) with
    uniformContinuous_toFun :=
      UniformOnFun.precomp_uniformContinuous
        (by
          intro s hs
          change e.symm '' s ∈ 𝔗
          rw [← preimage_equiv_eq_image_symm]
          exact he' hs)
    uniformContinuous_invFun := UniformOnFun.precomp_uniformContinuous he }
#align uniform_on_fun.congr_left UniformOnFun.congrLeft
-/

#print UniformOnFun.t2Space_of_covering /-
/-- If `𝔖` covers `α`, then the topology of `𝔖`-convergence is T₂. -/
theorem t2Space_of_covering [T2Space β] (h : ⋃₀ 𝔖 = univ) : T2Space (α →ᵤ[𝔖] β) :=
  {
    t2 := by
      intro f g hfg
      obtain ⟨x, hx⟩ := not_forall.mp (mt funext hfg)
      obtain ⟨s, hs, hxs⟩ : ∃ s ∈ 𝔖, x ∈ s := mem_sUnion.mp (h.symm ▸ True.intro)
      exact separated_by_continuous (uniform_continuous_eval_of_mem β 𝔖 hxs hs).Continuous hx }
#align uniform_on_fun.t2_space_of_covering UniformOnFun.t2Space_of_covering
-/

/- warning: uniform_on_fun.uniform_continuous_to_fun -> UniformOnFun.uniformContinuous_toFun is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : UniformSpace.{u2} β] {𝔖 : Set.{u1} (Set.{u1} α)}, (Eq.{succ u1} (Set.{u1} α) (Set.unionₛ.{u1} α 𝔖) (Set.univ.{u1} α)) -> (UniformContinuous.{max u1 u2, max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β) (UniformOnFun.uniformSpace.{u1, u2} α β _inst_1 𝔖) (Pi.uniformSpace.{u2, u1} α (fun (ᾰ : α) => β) (fun (i : α) => _inst_1)) (coeFn.{max 1 (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β)) (fun (_x : Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β)) => (UniformOnFun.{u1, u2} α β 𝔖) -> α -> β) (Equiv.hasCoeToFun.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u1, u2} α β 𝔖) (α -> β)) (UniformOnFun.toFun.{u1, u2} α β 𝔖)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : UniformSpace.{u1} β] {𝔖 : Set.{u2} (Set.{u2} α)}, (Eq.{succ u2} (Set.{u2} α) (Set.unionₛ.{u2} α 𝔖) (Set.univ.{u2} α)) -> (UniformContinuous.{max u2 u1, max u2 u1} (UniformOnFun.{u2, u1} α β 𝔖) (α -> β) (UniformOnFun.uniformSpace.{u2, u1} α β _inst_1 𝔖) (Pi.uniformSpace.{u1, u2} α (fun (ᾰ : α) => β) (fun (i : α) => _inst_1)) (FunLike.coe.{max (succ u2) (succ u1), max (succ u2) (succ u1), max (succ u2) (succ u1)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (UniformOnFun.{u2, u1} α β 𝔖) (α -> β)) (UniformOnFun.{u2, u1} α β 𝔖) (fun (_x : UniformOnFun.{u2, u1} α β 𝔖) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : UniformOnFun.{u2, u1} α β 𝔖) => α -> β) _x) (Equiv.instFunLikeEquiv.{max (succ u2) (succ u1), max (succ u2) (succ u1)} (UniformOnFun.{u2, u1} α β 𝔖) (α -> β)) (UniformOnFun.toFun.{u2, u1} α β 𝔖)))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.uniform_continuous_to_fun UniformOnFun.uniformContinuous_toFunₓ'. -/
/-- If `𝔖` covers `α`, the natural map `uniform_on_fun.to_fun` from `α →ᵤ[𝔖] β` to `α → β` is
uniformly continuous.

In other words, if `𝔖` covers `α`, then the uniform structure of `𝔖`-convergence is finer than
that of pointwise convergence. -/
protected theorem uniformContinuous_toFun (h : ⋃₀ 𝔖 = univ) :
    UniformContinuous (toFun 𝔖 : (α →ᵤ[𝔖] β) → α → β) :=
  by
  rw [uniformContinuous_pi]
  intro x
  obtain ⟨s : Set α, hs : s ∈ 𝔖, hxs : x ∈ s⟩ := sUnion_eq_univ_iff.mp h x
  exact uniform_continuous_eval_of_mem β 𝔖 hxs hs
#align uniform_on_fun.uniform_continuous_to_fun UniformOnFun.uniformContinuous_toFun

/- warning: uniform_on_fun.tendsto_iff_tendsto_uniformly_on -> UniformOnFun.tendsto_iff_tendstoUniformlyOn is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {ι : Type.{u3}} {p : Filter.{u3} ι} [_inst_1 : UniformSpace.{u2} β] {𝔖 : Set.{u1} (Set.{u1} α)} {F : ι -> (UniformOnFun.{u1, u2} α β 𝔖)} {f : UniformOnFun.{u1, u2} α β 𝔖}, Iff (Filter.Tendsto.{u3, max u1 u2} ι (UniformOnFun.{u1, u2} α β 𝔖) F p (nhds.{max u1 u2} (UniformOnFun.{u1, u2} α β 𝔖) (UniformOnFun.topologicalSpace.{u1, u2} α β _inst_1 𝔖) f)) (forall (s : Set.{u1} α), (Membership.Mem.{u1, u1} (Set.{u1} α) (Set.{u1} (Set.{u1} α)) (Set.hasMem.{u1} (Set.{u1} α)) s 𝔖) -> (TendstoUniformlyOn.{u1, u2, u3} α β ι _inst_1 F f p s))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {ι : Type.{u1}} {p : Filter.{u1} ι} [_inst_1 : UniformSpace.{u2} β] {𝔖 : Set.{u3} (Set.{u3} α)} {F : ι -> (UniformOnFun.{u3, u2} α β 𝔖)} {f : UniformOnFun.{u3, u2} α β 𝔖}, Iff (Filter.Tendsto.{u1, max u3 u2} ι (UniformOnFun.{u3, u2} α β 𝔖) F p (nhds.{max u3 u2} (UniformOnFun.{u3, u2} α β 𝔖) (UniformOnFun.topologicalSpace.{u3, u2} α β _inst_1 𝔖) f)) (forall (s : Set.{u3} α), (Membership.mem.{u3, u3} (Set.{u3} α) (Set.{u3} (Set.{u3} α)) (Set.instMembershipSet.{u3} (Set.{u3} α)) s 𝔖) -> (TendstoUniformlyOn.{u3, u2, u1} α β ι _inst_1 F f p s))
Case conversion may be inaccurate. Consider using '#align uniform_on_fun.tendsto_iff_tendsto_uniformly_on UniformOnFun.tendsto_iff_tendstoUniformlyOnₓ'. -/
/-- Convergence in the topology of `𝔖`-convergence means uniform convergence on `S` (in the sense
of `tendsto_uniformly_on`) for all `S ∈ 𝔖`. -/
protected theorem tendsto_iff_tendstoUniformlyOn {F : ι → α →ᵤ[𝔖] β} {f : α →ᵤ[𝔖] β} :
    Tendsto F p (𝓝 f) ↔ ∀ s ∈ 𝔖, TendstoUniformlyOn F f p s :=
  by
  rw [UniformOnFun.topologicalSpace_eq, nhds_infᵢ, tendsto_infi]
  refine' forall_congr' fun s => _
  rw [nhds_infᵢ, tendsto_infi]
  refine' forall_congr' fun hs => _
  rw [nhds_induced, tendsto_comap_iff, tendstoUniformlyOn_iff_tendstoUniformly_comp_coe,
    UniformFun.tendsto_iff_tendstoUniformly]
  rfl
#align uniform_on_fun.tendsto_iff_tendsto_uniformly_on UniformOnFun.tendsto_iff_tendstoUniformlyOn

#print UniformOnFun.uniformEquivProdArrow /-
/-- The natural bijection between `α → β × γ` and `(α → β) × (α → γ)`, upgraded to a uniform
isomorphism between `α →ᵤ[𝔖] β × γ` and `(α →ᵤ[𝔖] β) × (α →ᵤ[𝔖] γ)`. -/
protected def uniformEquivProdArrow [UniformSpace γ] :
    (α →ᵤ[𝔖] β × γ) ≃ᵤ (α →ᵤ[𝔖] β) × (α →ᵤ[𝔖] γ) :=
  ((-- Denote `φ` this bijection. We want to show that
              -- `comap φ (𝒱(α, β, 𝔖, uβ) × 𝒱(α, γ, 𝔖, uγ)) = 𝒱(α, β × γ, 𝔖, uβ × uγ)`.
              -- But `uβ × uγ` is defined as `comap fst uβ ⊓ comap snd uγ`, so we just have to apply
              -- `uniform_convergence_on.inf_eq` and `uniform_convergence_on.comap_eq`, which leaves us to check
              -- that some square commutes.
              -- We could also deduce this from `uniform_convergence.uniform_equiv_prod_arrow`, but it turns out
              -- to be more annoying.
              UniformOnFun.ofFun
              𝔖).symm.trans <|
        (Equiv.arrowProdEquivProdArrow _ _ _).trans <|
          (UniformOnFun.ofFun 𝔖).prodCongr (UniformOnFun.ofFun 𝔖)).toUniformEquivOfUniformInducing
    (by
      constructor
      rw [uniformity_prod, comap_inf, comap_comap, comap_comap, UniformOnFun.inf_eq, inf_uniformity,
        UniformOnFun.comap_eq, UniformOnFun.comap_eq, uniformity_comap, uniformity_comap]
      rfl)
#align uniform_on_fun.uniform_equiv_prod_arrow UniformOnFun.uniformEquivProdArrow
-/

-- the relevant diagram commutes by definition
variable (𝔖) (δ : ι → Type _) [∀ i, UniformSpace (δ i)]

#print UniformOnFun.uniformEquivPiComm /-
/-- The natural bijection between `α → Π i, δ i` and `Π i, α → δ i`, upgraded to a uniform
isomorphism between `α →ᵤ[𝔖] (Π i, δ i)` and `Π i, α →ᵤ[𝔖] δ i`. -/
protected def uniformEquivPiComm : (α →ᵤ[𝔖] ∀ i, δ i) ≃ᵤ ∀ i, α →ᵤ[𝔖] δ i :=
  (-- Denote `φ` this bijection. We want to show that
        -- `comap φ (Π i, 𝒱(α, δ i, 𝔖, uδ i)) = 𝒱(α, (Π i, δ i), 𝔖, (Π i, uδ i))`.
        -- But `Π i, uδ i` is defined as `⨅ i, comap (eval i) (uδ i)`, so we just have to apply
        -- `uniform_convergence_on.infi_eq` and `uniform_convergence_on.comap_eq`, which leaves us to check
        -- that some square commutes.
        -- We could also deduce this from `uniform_convergence.uniform_equiv_Pi_comm`, but it turns out
        -- to be more annoying.
        Equiv.piComm
        _).toUniformEquivOfUniformInducing
    (by
      constructor
      change comap (Prod.map Function.swap Function.swap) _ = _
      rw [← uniformity_comap]
      congr
      rw [Pi.uniformSpace, UniformSpace.ofCoreEq_toCore, Pi.uniformSpace,
        UniformSpace.ofCoreEq_toCore, UniformSpace.comap_infᵢ, UniformOnFun.infᵢ_eq]
      refine' infᵢ_congr fun i => _
      rw [← UniformSpace.comap_comap, UniformOnFun.comap_eq])
#align uniform_on_fun.uniform_equiv_Pi_comm UniformOnFun.uniformEquivPiComm
-/

-- Like in the previous lemma, the diagram actually commutes by definition
end UniformOnFun