/-
Copyright © 2020 Nicolò Cavalleri. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nicolò Cavalleri, Sebastien Gouezel, Heather Macbeth, Patrick Massot
-/

import analysis.normed_space.bounded_linear_maps
import topology.fiber_bundle

/-!
# Topological vector bundles

In this file we define topological vector bundles.

Let `B` be the base space. In our formalism, a topological vector bundle is by definition the type
`bundle.total_space E` where `E : B → Type*` is a function associating to
`x : B` the fiber over `x`. This type `bundle.total_space E` is just a type synonym for
`Σ (x : B), E x`, with the interest that one can put another topology than on `Σ (x : B), E x`
which has the disjoint union topology.

To have a topological vector bundle structure on `bundle.total_space E`, one should
additionally have the following data:

* `F` should be a normed space over a normed field `R`;
* There should be a topology on `bundle.total_space E`, for which the projection to `B` is
a topological fiber bundle with fiber `F` (in particular, each fiber `E x` is homeomorphic to `F`);
* For each `x`, the fiber `E x` should be a topological vector space over `R`, and the injection
from `E x` to `bundle.total_space F E` should be an embedding;
* There should be a distinguished set of bundle trivializations (which are continuous linear equivs
in the fibres), the "trivialization atlas"
* There should be a choice of bundle trivialization at each point, which belongs to this atlas.

If all these conditions are satisfied, and if moreover for any two trivializations `e`, `e'` in the
atlas the transition function considered as a map from `B` into `F →L[R] F` is continuous on
`e.base_set ∩ e'.base_set` with respect to the operator norm topology on `F →L[R] F`, we register
the typeclass `topological_vector_bundle R F E`.

If `E₁ : B → Type*` and `E₂ : B → Type*` define two topological vector bundles over `R` with fiber
models `F₁` and `F₂`, denote by `E₁ ×ᵇ E₂` the sigma type of direct sums, with fiber
`E x := (E₁ x × E₂ x)`. We can endow `bundle.total_space (E₁ ×ᵇ E₂)` with a topological vector
bundle structure, `bundle.prod.topological_vector_bundle`.

A similar construction (which is yet to be formalized) can be done for the vector bundle of
continuous linear maps from `E₁ x` to `E₂ x` with fiber a type synonym
`vector_bundle_continuous_linear_map R F₁ E₁ F₂ E₂ x := (E₁ x →L[R] E₂ x)` (and with the
topology inherited from the norm-topology on `F₁ →L[R] F₂`, without the need to define the strong
topology on continuous linear maps between general topological vector spaces).  Likewise for tensor
products of topological vector bundles, exterior algebras, and so on, where the topology can be
defined using a norm on the fiber model if this helps.

## Tags
Vector bundle
-/

noncomputable theory

open bundle set

variables (R : Type*) {B : Type*} (F : Type*) (E : B → Type*)

section topological_vector_space
variables [semiring R] [∀ x, add_comm_monoid (E x)] [∀ x, module R (E x)]
  [topological_space F] [add_comm_monoid F] [module R F] [topological_space B]

/-- Local pretrivialization for vector prebundles. -/
@[nolint has_inhabited_instance]
structure topological_vector_bundle.pretrivialization extends to_fiber_bundle_pretrivialization :
  topological_fiber_bundle.pretrivialization F (proj E) :=
(linear : ∀ x ∈ base_set, is_linear_map R (λ y : (E x), (to_fun y).2))

instance : has_coe_to_fun (topological_vector_bundle.pretrivialization R F E) _ := ⟨λ e, e.to_fun⟩

instance : has_coe (topological_vector_bundle.pretrivialization R F E)
  (topological_fiber_bundle.pretrivialization F (proj E)) :=
⟨topological_vector_bundle.pretrivialization.to_fiber_bundle_pretrivialization⟩

variable [topological_space (total_space E)]

/-- Local trivialization for vector bundles. -/
@[nolint has_inhabited_instance]
structure topological_vector_bundle.trivialization extends to_fiber_bundle_trivialization :
  topological_fiber_bundle.trivialization F (proj E) :=
(linear : ∀ x ∈ base_set, is_linear_map R (λ y : (E x), (to_fun y).2))

open topological_vector_bundle

instance : has_coe_to_fun (trivialization R F E) (λ _, total_space E → B × F) := ⟨λ e, e.to_fun⟩

instance : has_coe (trivialization R F E) (topological_fiber_bundle.trivialization F (proj E)) :=
⟨topological_vector_bundle.trivialization.to_fiber_bundle_trivialization⟩

namespace topological_vector_bundle

variables {R F E}

/-- Natural identification as `topological_vector_bundle.pretrivialization`. -/
def trivialization.to_pretrivialization (e : trivialization R F E) :
  topological_vector_bundle.pretrivialization R F E := { ..e }

lemma trivialization.mem_source (e : trivialization R F E)
  {x : total_space E} : x ∈ e.source ↔ proj E x ∈ e.base_set :=
topological_fiber_bundle.trivialization.mem_source e

@[simp, mfld_simps] lemma trivialization.coe_coe (e : trivialization R F E) :
  ⇑e.to_local_homeomorph = e := rfl

@[simp, mfld_simps] lemma trivialization.coe_fst (e : trivialization R F E) {x : total_space E}
  (ex : x ∈ e.source) : (e x).1 = (proj E) x := e.proj_to_fun x ex

end topological_vector_bundle

end topological_vector_space

section
open topological_vector_bundle

variables (B)
variables [nondiscrete_normed_field R] [∀ x, add_comm_monoid (E x)] [∀ x, module R (E x)]
  [normed_group F] [normed_space R F] [topological_space B]
  [topological_space (total_space E)] [∀ x, topological_space (E x)]

/-- The valid transition functions for a topological vector bundle over `B` modelled on
a normed space `F`: a transition function must be a local homeomorphism of `B × F` with source and
target both `s ×ˢ univ`, which on this set is of the form `λ (b, v), (b, ε b v)` for some continuous
map `ε` from `s` to `F ≃L[R] F`.  Here continuity is with respect to the operator norm on
`F →L[R] F`. -/
def continuous_transitions (e : local_equiv (B × F) (B × F)) : Prop :=
∃ s : set B, e.source = s ×ˢ (univ : set F) ∧ e.target = s ×ˢ (univ : set F)
    ∧ ∃ ε : B → (F ≃L[R] F), continuous_on (λ b, (ε b : F →L[R] F)) s
      ∧ ∀ b ∈ s, ∀ v : F, e (b, v) = (b, ε b v)

variables {B}

/-- The space `total_space E` (for `E : B → Type*` such that each `E x` is a topological vector
space) has a topological vector space structure with fiber `F` (denoted with
`topological_vector_bundle R F E`) if around every point there is a fiber bundle trivialization
which is linear in the fibers. -/
class topological_vector_bundle :=
(total_space_mk_inducing [] : ∀ (b : B), inducing (total_space_mk E b))
(trivialization_atlas [] : set (trivialization R F E))
(trivialization_at [] : B → trivialization R F E)
(mem_base_set_trivialization_at [] : ∀ b : B, b ∈ (trivialization_at b).base_set)
(trivialization_mem_atlas [] : ∀ b : B, trivialization_at b ∈ trivialization_atlas)
(continuous_coord_change : ∀ e e' ∈ trivialization_atlas,
  continuous_transitions R B F (e.to_local_equiv.symm.trans e'.to_local_equiv : _))

export topological_vector_bundle (trivialization_atlas trivialization_at
  mem_base_set_trivialization_at trivialization_mem_atlas)

variable [topological_vector_bundle R F E]

namespace topological_vector_bundle

@[simp, mfld_simps] lemma mem_source_trivialization_at (z : total_space E) :
  z ∈ (trivialization_at R F E z.1).source :=
by { rw topological_fiber_bundle.trivialization.mem_source, apply mem_base_set_trivialization_at }

variables {R F E}

/-- The co-ordinate change (transition function) between two trivializations of a vector bundle
over `B` modelled on `F`: this is a function from `B` to `F ≃L[R] F` (of course, only meaningful
on the intersection of the domains of definition of the two trivializations). -/
def coord_change {e e' : trivialization R F E} (he : e ∈ trivialization_atlas R F E)
  (he' : e' ∈ trivialization_atlas R F E) :
  B → F ≃L[R] F :=
(topological_vector_bundle.continuous_coord_change e he e' he').some_spec.2.2.some

lemma continuous_on_coord_change {e e' : trivialization R F E} (he : e ∈ trivialization_atlas R F E)
  (he' : e' ∈ trivialization_atlas R F E) :
  continuous_on (λ b, (coord_change he he' b : F →L[R] F)) (e.base_set ∩ e'.base_set) :=
begin
  let s := (continuous_coord_change e he e' he').some,
  let hs := (continuous_coord_change e he e' he').some_spec.1,
  have hs : s = e.base_set ∩ e'.base_set,
  { have : s ×ˢ (univ : set F) = (e.base_set ∩ e'.base_set) ×ˢ (univ : set F) :=
      hs.symm.trans (topological_fiber_bundle.trivialization.symm_trans_source_eq e e'),
    have hF : (univ : set F).nonempty := univ_nonempty,
      rwa prod_eq_iff_eq hF at this },
  rw ← hs,
  exact (continuous_coord_change e he e' he').some_spec.2.2.some_spec.1
end

lemma trans_eq_coord_change {e e' : trivialization R F E} (he : e ∈ trivialization_atlas R F E)
  (he' : e' ∈ trivialization_atlas R F E) {b : B} (hb : b ∈ e.base_set ∩ e'.base_set) (v : F) :
  e' (e.to_local_homeomorph.symm (b, v)) = (b, coord_change he he' b v) :=
begin
  let s := (continuous_coord_change e he e' he').some,
  let hs := (continuous_coord_change e he e' he').some_spec.1,
  have hs : s = e.base_set ∩ e'.base_set,
  { have : s ×ˢ (univ : set F) = (e.base_set ∩ e'.base_set) ×ˢ (univ : set F) :=
      hs.symm.trans (topological_fiber_bundle.trivialization.symm_trans_source_eq e e'),
    have hF : (univ : set F).nonempty := univ_nonempty,
      rwa prod_eq_iff_eq hF at this },
  rw ← hs at hb,
  exact (continuous_coord_change e he e' he').some_spec.2.2.some_spec.2 b hb v
end

attribute [irreducible] coord_change

namespace trivialization

/-- In a topological vector bundle, a trivialization in the fiber (which is a priori only linear)
is in fact a continuous linear equiv between the fibers and the model fiber. -/
def continuous_linear_equiv_at (e : trivialization R F E) (b : B)
  (hb : b ∈ e.base_set) : E b ≃L[R] F :=
{ to_fun := λ y, (e ⟨b, y⟩).2,
  inv_fun := λ z, begin
    have : ((e.to_local_homeomorph.symm) (b, z)).fst = b :=
      topological_fiber_bundle.trivialization.proj_symm_apply' _ hb,
    have C : E ((e.to_local_homeomorph.symm) (b, z)).fst = E b, by rw this,
    exact cast C (e.to_local_homeomorph.symm (b, z)).2
  end,
  left_inv := begin
    assume v,
    rw [← heq_iff_eq],
    apply (cast_heq _ _).trans,
    have A : (b, (e ⟨b, v⟩).snd) = e ⟨b, v⟩,
    { refine prod.ext _ rfl,
      symmetry,
      exact topological_fiber_bundle.trivialization.coe_fst' _ hb },
    have B : e.to_local_homeomorph.symm (e ⟨b, v⟩) = ⟨b, v⟩,
    { apply local_homeomorph.left_inv_on,
      rw topological_fiber_bundle.trivialization.mem_source,
      exact hb },
    rw [A, B],
  end,
  right_inv := begin
    assume v,
    have B : e (e.to_local_homeomorph.symm (b, v)) = (b, v),
    { apply local_homeomorph.right_inv_on,
      rw topological_fiber_bundle.trivialization.mem_target,
      exact hb },
    have C : (e (e.to_local_homeomorph.symm (b, v))).2 = v, by rw [B],
    conv_rhs { rw ← C },
    dsimp,
    congr,
    ext,
    { exact (topological_fiber_bundle.trivialization.proj_symm_apply' _ hb).symm },
    { exact (cast_heq _ _).trans (by refl) },
  end,
  map_add' := λ v w, (e.linear _ hb).map_add v w,
  map_smul' := λ c v, (e.linear _ hb).map_smul c v,
  continuous_to_fun := begin
    refine continuous_snd.comp _,
    apply continuous_on.comp_continuous e.to_local_homeomorph.continuous_on
      (topological_vector_bundle.total_space_mk_inducing R F E b).continuous (λ x, _),
    rw topological_fiber_bundle.trivialization.mem_source,
    exact hb,
  end,
  continuous_inv_fun := begin
    rw (topological_vector_bundle.total_space_mk_inducing R F E b).continuous_iff,
    dsimp,
    have : continuous (λ (z : F), e.to_fiber_bundle_trivialization.to_local_homeomorph.symm (b, z)),
    { apply e.to_local_homeomorph.symm.continuous_on.comp_continuous
        (continuous_const.prod_mk continuous_id') (λ z, _),
      simp only [topological_fiber_bundle.trivialization.mem_target, hb, local_equiv.symm_source,
        local_homeomorph.symm_to_local_equiv] },
    convert this,
    ext z,
    { exact (topological_fiber_bundle.trivialization.proj_symm_apply' _ hb).symm },
    { exact cast_heq _ _ },
  end }

@[simp] lemma continuous_linear_equiv_at_apply (e : trivialization R F E) (b : B)
  (hb : b ∈ e.base_set) (y : E b) : e.continuous_linear_equiv_at b hb y = (e ⟨b, y⟩).2 := rfl

@[simp] lemma continuous_linear_equiv_at_apply' (e : trivialization R F E)
  (x : total_space E) (hx : x ∈ e.source) :
  e.continuous_linear_equiv_at (proj E x) (e.mem_source.1 hx) x.2 = (e x).2 := by { cases x, refl }

lemma apply_eq_prod_continuous_linear_equiv_at (e : trivialization R F E) (b : B)
  (hb : b ∈ e.base_set) (z : E b) :
  e.to_local_homeomorph ⟨b, z⟩ = (b, e.continuous_linear_equiv_at b hb z) :=
begin
  ext,
  { convert e.coe_fst _,
    rw e.source_eq,
    exact hb },
  { simp }
end

lemma symm_apply_eq_mk_continuous_linear_equiv_at_symm (e : trivialization R F E) (b : B)
  (hb : b ∈ e.base_set) (z : F) :
  e.to_local_homeomorph.symm ⟨b, z⟩
  = total_space_mk E b ((e.continuous_linear_equiv_at b hb).symm z) :=
begin
  have h : (b, z) ∈ e.to_local_homeomorph.target,
  { rw e.target_eq,
    exact ⟨hb, mem_univ _⟩ },
  apply e.to_local_homeomorph.inj_on (e.to_local_homeomorph.map_target h),
  { simp [e.source_eq, hb] },
  simp [-continuous_linear_equiv_at_apply, e.apply_eq_prod_continuous_linear_equiv_at b hb,
    e.to_local_homeomorph.right_inv h],
end

lemma comp_continuous_linear_equiv_at_eq_coord_change {e e' : trivialization R F E}
  (he : e ∈ trivialization_atlas R F E) (he' : e' ∈ trivialization_atlas R F E) {b : B}
  (hb : b ∈ e.base_set ∩ e'.base_set) :
  (e.continuous_linear_equiv_at b hb.1).symm.trans (e'.continuous_linear_equiv_at b hb.2)
  = coord_change he he' b :=
begin
  ext v,
  suffices :
    (b, e'.continuous_linear_equiv_at b hb.2 ((e.continuous_linear_equiv_at b hb.1).symm v))
    = (b, coord_change he he' b v),
  { simpa using this },
  rw [← trans_eq_coord_change he he' hb, ← apply_eq_prod_continuous_linear_equiv_at,
    symm_apply_eq_mk_continuous_linear_equiv_at_symm],
  refl,
end

end trivialization

section
local attribute [reducible] bundle.trivial

instance {B : Type*} {F : Type*} [add_comm_monoid F] (b : B) :
  add_comm_monoid (bundle.trivial B F b) := ‹add_comm_monoid F›

instance {B : Type*} {F : Type*} [add_comm_group F] (b : B) :
  add_comm_group (bundle.trivial B F b) := ‹add_comm_group F›

instance {B : Type*} {F : Type*} [add_comm_monoid F] [module R F] (b : B) :
  module R (bundle.trivial B F b) := ‹module R F›

end

variables (R B F)
/-- Local trivialization for trivial bundle. -/
def trivial_topological_vector_bundle.trivialization : trivialization R F (bundle.trivial B F) :=
{ to_fun := λ x, (x.fst, x.snd),
  inv_fun := λ y, ⟨y.fst, y.snd⟩,
  source := univ,
  target := univ,
  map_source' := λ x h, mem_univ (x.fst, x.snd),
  map_target' :=λ y h,  mem_univ ⟨y.fst, y.snd⟩,
  left_inv' := λ x h, sigma.eq rfl rfl,
  right_inv' := λ x h, prod.ext rfl rfl,
  open_source := is_open_univ,
  open_target := is_open_univ,
  continuous_to_fun := by { rw [←continuous_iff_continuous_on_univ, continuous_iff_le_induced],
    simp only [prod.topological_space, induced_inf, induced_compose], exact le_rfl, },
  continuous_inv_fun := by { rw [←continuous_iff_continuous_on_univ, continuous_iff_le_induced],
    simp only [bundle.total_space.topological_space, induced_inf, induced_compose],
    exact le_rfl, },
  base_set := univ,
  open_base_set := is_open_univ,
  source_eq := rfl,
  target_eq := by simp only [univ_prod_univ],
  proj_to_fun := λ y hy, rfl,
  linear := λ x hx, ⟨λ y z, rfl, λ c y, rfl⟩ }

@[simp]
lemma trivial_topological_vector_bundle.trivialization_source :
  (trivial_topological_vector_bundle.trivialization R B F).source = univ := rfl

@[simp]
lemma trivial_topological_vector_bundle.trivialization_target :
  (trivial_topological_vector_bundle.trivialization R B F).target = univ := rfl

instance trivial_bundle.topological_vector_bundle :
  topological_vector_bundle R F (bundle.trivial B F) :=
{ trivialization_atlas := {trivial_topological_vector_bundle.trivialization R B F},
  trivialization_at := λ x, trivial_topological_vector_bundle.trivialization R B F,
  mem_base_set_trivialization_at := mem_univ,
  trivialization_mem_atlas := λ x, mem_singleton _,
  total_space_mk_inducing := λ b, ⟨begin
    have : (λ (x : trivial B F b), x) = @id F, by { ext x, refl },
    simp only [total_space.topological_space, induced_inf, induced_compose, function.comp, proj,
      induced_const, top_inf_eq, trivial.proj_snd, id.def, trivial.topological_space, this,
      induced_id],
  end⟩,
  continuous_coord_change := begin
    intros e he e' he',
    rw [mem_singleton_iff.mp he, mem_singleton_iff.mp he'],
    exact ⟨univ, by simp, by simp, λb, continuous_linear_equiv.refl R F,
           continuous_const.continuous_on, λ b hb v, rfl⟩
  end }

variables {R B F}

/- Not registered as an instance because of a metavariable. -/
lemma is_topological_vector_bundle_is_topological_fiber_bundle :
  is_topological_fiber_bundle F (proj E) :=
λ x, ⟨(trivialization_at R F E x).to_fiber_bundle_trivialization,
  mem_base_set_trivialization_at R F E x⟩

variables (R B F)
include R F
@[continuity] lemma continuous_proj : continuous (proj E) :=
begin
  apply @is_topological_fiber_bundle.continuous_proj B F,
  apply @is_topological_vector_bundle_is_topological_fiber_bundle R,
end

end topological_vector_bundle

/-! ### Constructing topological vector bundles -/

variables (B)

/-- Analogous construction of `topological_fiber_bundle_core` for vector bundles. This
construction gives a way to construct vector bundles from a structure registering how
trivialization changes act on fibers. -/
structure topological_vector_bundle_core (ι : Type*) :=
(base_set          : ι → set B)
(is_open_base_set  : ∀ i, is_open (base_set i))
(index_at          : B → ι)
(mem_base_set_at   : ∀ x, x ∈ base_set (index_at x))
(coord_change      : ι → ι → B → (F →L[R] F))
(coord_change_self : ∀ i, ∀ x ∈ base_set i, ∀ v, coord_change i i x v = v)
(coord_change_continuous : ∀ i j, continuous_on (coord_change i j) (base_set i ∩ base_set j))
(coord_change_comp : ∀ i j k, ∀ x ∈ (base_set i) ∩ (base_set j) ∩ (base_set k), ∀ v,
  (coord_change j k x) (coord_change i j x v) = coord_change i k x v)

/-- The trivial topological vector bundle core, in which all the changes of coordinates are the
identity. -/
def trivial_topological_vector_bundle_core (ι : Type*) [inhabited ι] :
  topological_vector_bundle_core R B F ι :=
{ base_set := λ ι, univ,
  is_open_base_set := λ i, is_open_univ,
  index_at := λ x, default,
  mem_base_set_at := λ x, mem_univ x,
  coord_change := λ i j x, continuous_linear_map.id R F,
  coord_change_self := λ i x hx v, rfl,
  coord_change_comp := λ i j k x hx v, rfl,
  coord_change_continuous := λ i j, continuous_on_const, }

instance (ι : Type*) [inhabited ι] : inhabited (topological_vector_bundle_core R B F ι) :=
⟨trivial_topological_vector_bundle_core R B F ι⟩

namespace topological_vector_bundle_core

variables {R B F} {ι : Type*} (Z : topological_vector_bundle_core R B F ι)

/-- Natural identification to a `topological_fiber_bundle_core`. -/
def to_topological_vector_bundle_core : topological_fiber_bundle_core ι B F :=
{ coord_change := λ i j b, Z.coord_change i j b,
  coord_change_continuous := λ i j, is_bounded_bilinear_map_apply.continuous.comp_continuous_on
      ((Z.coord_change_continuous i j).prod_map continuous_on_id),
  ..Z }

instance to_topological_vector_bundle_core_coe : has_coe (topological_vector_bundle_core R B F ι)
  (topological_fiber_bundle_core ι B F) := ⟨to_topological_vector_bundle_core⟩

include Z

lemma coord_change_linear_comp (i j k : ι): ∀ x ∈ (Z.base_set i) ∩ (Z.base_set j) ∩ (Z.base_set k),
  (Z.coord_change j k x).comp (Z.coord_change i j x) = Z.coord_change i k x :=
λ x hx, by { ext v, exact Z.coord_change_comp i j k x hx v }

/-- The index set of a topological vector bundle core, as a convenience function for dot notation -/
@[nolint unused_arguments has_inhabited_instance]
def index := ι

/-- The base space of a topological vector bundle core, as a convenience function for dot notation-/
@[nolint unused_arguments, reducible]
def base := B

/-- The fiber of a topological vector bundle core, as a convenience function for dot notation and
typeclass inference -/
@[nolint unused_arguments has_inhabited_instance]
def fiber (x : B) := F

section fiber_instances

local attribute [reducible] fiber --just to record instances

instance topological_space_fiber (x : B) : topological_space (Z.fiber x) := by apply_instance
instance add_comm_monoid_fiber : ∀ (x : B), add_comm_monoid (Z.fiber x) := λ x, by apply_instance
instance module_fiber : ∀ (x : B), module R (Z.fiber x) := λ x, by apply_instance

variable [add_comm_group F]

instance add_comm_group_fiber : ∀ (x : B), add_comm_group (Z.fiber x) := λ x, by apply_instance

end fiber_instances

/-- The projection from the total space of a topological fiber bundle core, on its base. -/
@[reducible, simp, mfld_simps] def proj : total_space Z.fiber → B := bundle.proj Z.fiber

/-- The total space of the topological vector bundle, as a convenience function for dot notation.
It is by definition equal to `bundle.total_space Z.fiber`, a.k.a. `Σ x, Z.fiber x` but with a
different name for typeclass inference. -/
@[nolint unused_arguments, reducible]
def total_space := bundle.total_space Z.fiber

/-- Local homeomorphism version of the trivialization change. -/
def triv_change (i j : ι) : local_homeomorph (B × F) (B × F) :=
topological_fiber_bundle_core.triv_change ↑Z i j

@[simp, mfld_simps] lemma mem_triv_change_source (i j : ι) (p : B × F) :
  p ∈ (Z.triv_change i j).source ↔ p.1 ∈ Z.base_set i ∩ Z.base_set j :=
topological_fiber_bundle_core.mem_triv_change_source ↑Z i j p

variable (ι)

/-- Topological structure on the total space of a topological bundle created from core, designed so
that all the local trivialization are continuous. -/
instance to_topological_space : topological_space (Z.total_space) :=
topological_fiber_bundle_core.to_topological_space ι ↑Z

variables {ι} (b : B) (a : F)

@[simp, mfld_simps] lemma coe_coord_change (i j : ι) :
  topological_fiber_bundle_core.coord_change ↑Z i j b = Z.coord_change i j b := rfl

/-- Extended version of the local trivialization of a fiber bundle constructed from core,
registering additionally in its type that it is a local bundle trivialization. -/
def local_triv (i : ι) : topological_vector_bundle.trivialization R F Z.fiber :=
{ linear := λ x hx,
  { map_add := λ v w, by simp only [continuous_linear_map.map_add] with mfld_simps,
    map_smul := λ r v, by simp only [continuous_linear_map.map_smul] with mfld_simps},
  ..topological_fiber_bundle_core.local_triv ↑Z i }

variable (i : ι)

@[simp, mfld_simps] lemma mem_local_triv_source (p : Z.total_space) :
  p ∈ (Z.local_triv i).source ↔ p.1 ∈ Z.base_set i := iff.rfl

@[simp, mfld_simps] lemma base_set_at : Z.base_set i = (Z.local_triv i).base_set := rfl

@[simp, mfld_simps] lemma local_triv_apply (p : Z.total_space) :
  (Z.local_triv i) p = ⟨p.1, Z.coord_change (Z.index_at p.1) i p.1 p.2⟩ := rfl

@[simp, mfld_simps] lemma mem_local_triv_target (p : B × F) :
  p ∈ (Z.local_triv i).target ↔ p.1 ∈ (Z.local_triv i).base_set :=
topological_fiber_bundle_core.mem_local_triv_target Z i p

@[simp, mfld_simps] lemma local_triv_symm_fst (p : B × F) :
  (Z.local_triv i).to_local_homeomorph.symm p =
    ⟨p.1, Z.coord_change i (Z.index_at p.1) p.1 p.2⟩ := rfl

/-- Preferred local trivialization of a vector bundle constructed from core, at a given point, as
a bundle trivialization -/
def local_triv_at (b : B) : topological_vector_bundle.trivialization R F Z.fiber :=
Z.local_triv (Z.index_at b)

@[simp, mfld_simps] lemma local_triv_at_def :
  Z.local_triv (Z.index_at b) = Z.local_triv_at b := rfl

@[simp, mfld_simps] lemma mem_source_at : (⟨b, a⟩ : Z.total_space) ∈ (Z.local_triv_at b).source :=
by { rw [local_triv_at, mem_local_triv_source], exact Z.mem_base_set_at b }

@[simp, mfld_simps] lemma local_triv_at_apply : ((Z.local_triv_at b) ⟨b, a⟩) = ⟨b, a⟩ :=
topological_fiber_bundle_core.local_triv_at_apply Z b a

@[simp, mfld_simps] lemma mem_local_triv_at_base_set :
  b ∈ (Z.local_triv_at b).base_set :=
topological_fiber_bundle_core.mem_local_triv_at_base_set Z b

instance : topological_vector_bundle R F Z.fiber :=
{ total_space_mk_inducing := λ b, ⟨ begin refine le_antisymm _ (λ s h, _),
    { rw ←continuous_iff_le_induced,
      exact topological_fiber_bundle_core.continuous_total_space_mk ↑Z b, },
    { refine is_open_induced_iff.mpr ⟨(Z.local_triv_at b).source ∩ (Z.local_triv_at b) ⁻¹'
        ((Z.local_triv_at b).base_set ×ˢ s), (continuous_on_open_iff
        (Z.local_triv_at b).open_source).mp (Z.local_triv_at b).continuous_to_fun _
        ((Z.local_triv_at b).open_base_set.prod h), _⟩,
      rw [preimage_inter, ←preimage_comp, function.comp],
      simp only [total_space_mk],
      refine ext_iff.mpr (λ a, ⟨λ ha, _, λ ha, ⟨Z.mem_base_set_at b, _⟩⟩),
      { simp only [mem_prod, mem_preimage, mem_inter_eq, local_triv_at_apply] at ha,
        exact ha.2.2, },
      { simp only [mem_prod, mem_preimage, mem_inter_eq, local_triv_at_apply],
        exact ⟨Z.mem_base_set_at b, ha⟩, } } end⟩,
  trivialization_atlas := set.range Z.local_triv,
  trivialization_at := Z.local_triv_at,
  mem_base_set_trivialization_at := Z.mem_base_set_at,
  trivialization_mem_atlas := λ b, ⟨Z.index_at b, rfl⟩,
  continuous_coord_change := begin
    classical,
    rintros _ ⟨i, rfl⟩ _ ⟨i', rfl⟩,
    refine ⟨Z.base_set i ∩ Z.base_set i', _, _,
      λ b, if h : b ∈ Z.base_set i ∩ Z.base_set i' then continuous_linear_equiv.equiv_of_inverse
        (Z.coord_change i i' b) (Z.coord_change i' i b) _ _ else continuous_linear_equiv.refl R F,
      _, _⟩,
    { ext ⟨b, f⟩,
      simp },
    { ext ⟨b, f⟩,
      simp [and_comm] },
    { intro f,
      rw [Z.coord_change_comp _ _ _ _ ⟨h, h.1⟩, Z.coord_change_self _ _ h.1] },
    { intro f,
      rw [Z.coord_change_comp _ _ _ _ ⟨⟨h.2, h.1⟩, h.2⟩, Z.coord_change_self _ _ h.2] },
    { apply continuous_on.congr (Z.coord_change_continuous i i'),
      intros b hb,
      simp [hb],
      ext v,
      refl },
    { intros b hb v,
      have : b ∈ Z.base_set i ∩ Z.base_set (Z.index_at b) ∩ Z.base_set i',
      { simp only [base_set_at, local_triv_at_def, mem_inter_eq, mem_local_triv_at_base_set] at *,
        tauto },
      simp [hb, Z.coord_change_comp _ _ _ _ this] }
  end }

/-- The projection on the base of a topological vector bundle created from core is continuous -/
@[continuity] lemma continuous_proj : continuous Z.proj :=
topological_fiber_bundle_core.continuous_proj Z

/-- The projection on the base of a topological vector bundle created from core is an open map -/
lemma is_open_map_proj : is_open_map Z.proj :=
topological_fiber_bundle_core.is_open_map_proj Z

end topological_vector_bundle_core

end

/-! ### Topological vector prebundle -/

section
variables [nondiscrete_normed_field R] [∀ x, add_comm_monoid (E x)] [∀ x, module R (E x)]
  [normed_group F] [normed_space R F] [topological_space B]
  [∀ x, topological_space (E x)]

open topological_space

/-- This structure permits to define a vector bundle when trivializations are given as local
equivalences but there is not yet a topology on the total space. The total space is hence given a
topology in such a way that there is a fiber bundle structure for which the local equivalences
are also local homeomorphisms and hence vector bundle trivializations. -/
@[nolint has_inhabited_instance]
structure topological_vector_prebundle :=
(pretrivialization_atlas : set (topological_vector_bundle.pretrivialization R F E))
(pretrivialization_at : B → topological_vector_bundle.pretrivialization R F E)
(mem_base_pretrivialization_at : ∀ x : B, x ∈ (pretrivialization_at x).base_set)
(pretrivialization_mem_atlas : ∀ x : B, pretrivialization_at x ∈ pretrivialization_atlas)
(continuous_coord_change : ∀ e e' ∈ pretrivialization_atlas,
  continuous_transitions R B F (e'.to_local_equiv.symm.trans e.to_local_equiv : _))
(total_space_mk_inducing : ∀ (b : B), inducing ((pretrivialization_at b) ∘ (total_space_mk E b)))

namespace topological_vector_prebundle

variables {R E F}

/-- Natural identification of `topological_vector_prebundle` as a `topological_fiber_prebundle`. -/
def to_topological_fiber_prebundle (a : topological_vector_prebundle R F E) :
  topological_fiber_prebundle F (proj E) :=
{ pretrivialization_atlas :=
    pretrivialization.to_fiber_bundle_pretrivialization '' a.pretrivialization_atlas,
  pretrivialization_at := λ x, (a.pretrivialization_at x).to_fiber_bundle_pretrivialization,
  pretrivialization_mem_atlas := λ x, ⟨_, a.pretrivialization_mem_atlas x, rfl⟩,
  continuous_triv_change := begin
    rintros _ ⟨e, he, rfl⟩ _ ⟨e', he', rfl⟩,
    obtain ⟨s, hs, hs', ε, hε, heε⟩ := a.continuous_coord_change e he e' he',
    have H : e'.to_fiber_bundle_pretrivialization.to_local_equiv.target ∩
      (e'.to_fiber_bundle_pretrivialization.to_local_equiv.symm) ⁻¹'
      e.to_fiber_bundle_pretrivialization.to_local_equiv.source = s ×ˢ (univ : set F),
    { simpa using hs },
    rw H,
    have : continuous_on (λ p : B × F, (p.1, (ε p.1) p.2)) (s ×ˢ (univ : set F)),
    { apply continuous_on_fst.prod,
      exact is_bounded_bilinear_map_apply.continuous.comp_continuous_on
        (hε.prod_map continuous_on_id) },
    apply this.congr,
    rintros ⟨b, f⟩ ⟨hb : b ∈ s, -⟩,
    exact heε _ hb _,
  end,
  .. a }

/-- Topology on the total space that will make the prebundle into a bundle. -/
def total_space_topology (a : topological_vector_prebundle R F E) :
  topological_space (total_space E) :=
a.to_topological_fiber_prebundle.total_space_topology

/-- Promotion from a `topologial_vector_prebundle.trivialization` to a
  `topological_vector_bundle.trivialization`. -/
def trivialization_of_mem_pretrivialization_atlas (a : topological_vector_prebundle R F E)
  {e : topological_vector_bundle.pretrivialization R F E} (he : e ∈ a.pretrivialization_atlas) :
  @topological_vector_bundle.trivialization R _ F E _ _ _ _ _ _ _ a.total_space_topology :=
begin
  letI := a.total_space_topology,
  exact { linear := e.linear,
  ..a.to_topological_fiber_prebundle.trivialization_of_mem_pretrivialization_atlas ⟨e, he, rfl⟩ }
end

variable (a : topological_vector_prebundle R F E)

lemma mem_trivialization_at_source (b : B) (x : E b) :
  total_space_mk E b x ∈ (a.pretrivialization_at b).source :=
begin
  simp only [(a.pretrivialization_at b).source_eq, mem_preimage, proj],
  exact a.mem_base_pretrivialization_at b,
end

@[simp] lemma total_space_mk_preimage_source (b : B) :
  (total_space_mk E b) ⁻¹' (a.pretrivialization_at b).source = univ :=
begin
  apply eq_univ_of_univ_subset,
  rw [(a.pretrivialization_at b).source_eq, ←preimage_comp, function.comp],
  simp only [proj],
  rw preimage_const_of_mem _,
  exact a.mem_base_pretrivialization_at b,
end

@[continuity] lemma continuous_total_space_mk (b : B) :
  @continuous _ _ _ a.total_space_topology (total_space_mk E b) :=
begin
  letI := a.total_space_topology,
  let e := a.trivialization_of_mem_pretrivialization_atlas (a.pretrivialization_mem_atlas b),
  rw e.to_local_homeomorph.continuous_iff_continuous_comp_left
    (a.total_space_mk_preimage_source b),
  exact continuous_iff_le_induced.mpr (le_antisymm_iff.mp (a.total_space_mk_inducing b).induced).1,
end

lemma inducing_total_space_mk_of_inducing_comp (b : B)
  (h : inducing ((a.pretrivialization_at b) ∘ (total_space_mk E b))) :
  @inducing _ _ _ a.total_space_topology (total_space_mk E b) :=
begin
  letI := a.total_space_topology,
  rw ←restrict_comp_cod_restrict (a.mem_trivialization_at_source b) at h,
  apply inducing.of_cod_restrict (a.mem_trivialization_at_source b),
  refine inducing_of_inducing_compose _ (continuous_on_iff_continuous_restrict.mp
    (a.trivialization_of_mem_pretrivialization_atlas
    (a.pretrivialization_mem_atlas b)).continuous_to_fun) h,
  exact (a.continuous_total_space_mk b).cod_restrict (a.mem_trivialization_at_source b),
end

/-- Make a `topological_vector_bundle` from a `topological_vector_prebundle`.  Concretely this means
that, given a `topological_vector_prebundle` structure for a sigma-type `E` -- which consists of a
number of "pretrivializations" identifying parts of `E` with product spaces `U × F` -- one
establishes that for the topology constructed on the sigma-type using
`topological_vector_prebundle.total_space_topology`, these "pretrivializations" are actually
"trivializations" (i.e., homeomorphisms with respect to the constructed topology). -/
def to_topological_vector_bundle :
  @topological_vector_bundle R _ F E _ _ _ _ _ _ a.total_space_topology _ :=
{ total_space_mk_inducing := λ b, a.inducing_total_space_mk_of_inducing_comp b
    (a.total_space_mk_inducing b),
  trivialization_atlas := {e | ∃ e₀ (he₀ : e₀ ∈ a.pretrivialization_atlas),
    e = a.trivialization_of_mem_pretrivialization_atlas he₀},
  trivialization_at := λ x, a.trivialization_of_mem_pretrivialization_atlas
    (a.pretrivialization_mem_atlas x),
  mem_base_set_trivialization_at := a.mem_base_pretrivialization_at,
  trivialization_mem_atlas := λ x, ⟨_, a.pretrivialization_mem_atlas x, rfl⟩,
  continuous_coord_change := begin
    rintros _ ⟨e, he, rfl⟩ _ ⟨e', he', rfl⟩,
    exact a.continuous_coord_change e' he' e he,
  end }

end topological_vector_prebundle

end

/-! ### Direct sum of two vector bundles over the same base -/

namespace topological_vector_bundle

section defs
variables (E₁ : B → Type*) (E₂ : B → Type*)
variables [topological_space (total_space E₁)] [topological_space (total_space E₂)]

/-- Equip the total space of the fibrewise product of two topological vector bundles `E₁`, `E₂` with
the induced topology from the diagonal embedding into `(total_space E₁) × (total_space E₂)`. -/
instance prod.topological_space :
  topological_space (total_space (E₁ ×ᵇ E₂)) :=
topological_space.induced
  (λ p, ((⟨p.1, p.2.1⟩ : total_space E₁), (⟨p.1, p.2.2⟩ : total_space E₂)))
  (by apply_instance : topological_space ((total_space E₁) × (total_space E₂)))

/-- The diagonal map from the total space of the fibrewise product of two topological vector bundles
`E₁`, `E₂` into `(total_space E₁) × (total_space E₂)` is `inducing`. -/
lemma prod.inducing_diag : inducing
  (λ p, (⟨p.1, p.2.1⟩, ⟨p.1, p.2.2⟩) :
    total_space (E₁ ×ᵇ E₂) → (total_space E₁) × (total_space E₂)) :=
⟨rfl⟩

end defs

variables [nondiscrete_normed_field R] [topological_space B]

variables (F₁ : Type*) [normed_group F₁] [normed_space R F₁]
  (E₁ : B → Type*) [topological_space (total_space E₁)]
  [Π x, add_comm_monoid (E₁ x)] [Π x, module R (E₁ x)]

variables (F₂ : Type*) [normed_group F₂] [normed_space R F₂]
  (E₂ : B → Type*) [topological_space (total_space E₂)]
  [Π x, add_comm_monoid (E₂ x)] [Π x, module R (E₂ x)]

namespace trivialization
variables (e₁ : trivialization R F₁ E₁) (e₂ : trivialization R F₂ E₂)
include e₁ e₂
variables {R F₁ E₁ F₂ E₂}

/-- Given trivializations `e₁`, `e₂` for vector bundles `E₁`, `E₂` over a base `B`, the forward
function for the construction `topological_vector_bundle.trivialization.prod`, the induced
trivialization for the direct sum of `E₁` and `E₂`. -/
def prod.to_fun' : total_space (E₁ ×ᵇ E₂) → B × (F₁ × F₂) :=
λ ⟨x, v₁, v₂⟩, ⟨x, (e₁ ⟨x, v₁⟩).2, (e₂ ⟨x, v₂⟩).2⟩

variables {e₁ e₂}

lemma prod.continuous_to_fun :
  continuous_on (prod.to_fun' e₁ e₂) (proj (E₁ ×ᵇ E₂) ⁻¹' (e₁.base_set ∩ e₂.base_set)) :=
begin
  let f₁ : total_space (E₁ ×ᵇ E₂) → total_space E₁ × total_space E₂ :=
    λ p, ((⟨p.1, p.2.1⟩ : total_space E₁), (⟨p.1, p.2.2⟩ : total_space E₂)),
  let f₂ : total_space E₁ × total_space E₂ → (B × F₁) × (B × F₂) := λ p, ⟨e₁ p.1, e₂ p.2⟩,
  let f₃ : (B × F₁) × (B × F₂) → B × F₁ × F₂ := λ p, ⟨p.1.1, p.1.2, p.2.2⟩,
  have hf₁ : continuous f₁ := (prod.inducing_diag E₁ E₂).continuous,
  have hf₂ : continuous_on f₂ (e₁.source ×ˢ e₂.source) :=
    e₁.to_local_homeomorph.continuous_on.prod_map e₂.to_local_homeomorph.continuous_on,
  have hf₃ : continuous f₃ :=
    (continuous_fst.comp continuous_fst).prod_mk (continuous_snd.prod_map continuous_snd),
  refine ((hf₃.comp_continuous_on hf₂).comp hf₁.continuous_on _).congr _,
  { rw [e₁.source_eq, e₂.source_eq],
    exact maps_to_preimage _ _ },
  rintros ⟨b, v₁, v₂⟩ ⟨hb₁, hb₂⟩,
  simp only [prod.to_fun', prod.mk.inj_iff, eq_self_iff_true, and_true],
  rw e₁.coe_fst,
  rw [e₁.source_eq, mem_preimage],
  exact hb₁,
end

variables (e₁ e₂)

variables [Π x : B, topological_space (E₁ x)] [Π x : B, topological_space (E₂ x)]
  [topological_vector_bundle R F₁ E₁] [topological_vector_bundle R F₂ E₂]

/-- Given trivializations `e₁`, `e₂` for vector bundles `E₁`, `E₂` over a base `B`, the inverse
function for the construction `topological_vector_bundle.trivialization.prod`, the induced
trivialization for the direct sum of `E₁` and `E₂`. -/
def prod.inv_fun' (p : B × (F₁ × F₂)) : total_space (E₁ ×ᵇ E₂) :=
begin
  obtain ⟨x, w₁, w₂⟩ := p,
  refine ⟨x, _, _⟩,
  { by_cases h : x ∈ e₁.base_set,
    { exact (e₁.continuous_linear_equiv_at x h).symm w₁ },
    { exact 0 } },
  { by_cases h : x ∈ e₂.base_set,
    { exact (e₂.continuous_linear_equiv_at x h).symm w₂ },
    { exact 0 } },
end

variables {e₁ e₂}

lemma prod.inv_fun'_apply {x : B} (hx₁ : x ∈ e₁.base_set) (hx₂ : x ∈ e₂.base_set)
  (w₁ : F₁) (w₂ : F₂) :
  prod.inv_fun' e₁ e₂ ⟨x, w₁, w₂⟩
  = ⟨x, ((e₁.continuous_linear_equiv_at x hx₁).symm w₁,
    (e₂.continuous_linear_equiv_at x hx₂).symm w₂)⟩ :=
begin
  dsimp [prod.inv_fun'],
  rw [dif_pos, dif_pos],
end

lemma prod.left_inv {x : total_space (E₁ ×ᵇ E₂)}
  (h : x ∈ proj (E₁ ×ᵇ E₂) ⁻¹' (e₁.base_set ∩ e₂.base_set)) :
  prod.inv_fun' e₁ e₂ (prod.to_fun' e₁ e₂ x) = x :=
begin
  obtain ⟨x, v₁, v₂⟩ := x,
  simp only [prod.to_fun', prod.inv_fun', sigma.mk.inj_iff, true_and, eq_self_iff_true,
    prod.mk.inj_iff, heq_iff_eq],
  split,
  { rw [dif_pos, ← e₁.continuous_linear_equiv_at_apply x h.1,
      continuous_linear_equiv.symm_apply_apply] },
  { rw [dif_pos, ← e₂.continuous_linear_equiv_at_apply x h.2,
      continuous_linear_equiv.symm_apply_apply] },
end

lemma prod.right_inv {x : B × F₁ × F₂}
  (h : x ∈ (e₁.base_set ∩ e₂.base_set) ×ˢ (univ : set (F₁ × F₂))) :
  prod.to_fun' e₁ e₂ (prod.inv_fun' e₁ e₂ x) = x :=
begin
  obtain ⟨x, w₁, w₂⟩ := x,
  obtain ⟨h, -⟩ := h,
  dsimp only [prod.to_fun', prod.inv_fun'],
  simp only [prod.mk.inj_iff, eq_self_iff_true, true_and],
  split,
  { rw [dif_pos, ← e₁.continuous_linear_equiv_at_apply x h.1,
      continuous_linear_equiv.apply_symm_apply] },
  { rw [dif_pos, ← e₂.continuous_linear_equiv_at_apply x h.2,
      continuous_linear_equiv.apply_symm_apply] },
end

lemma prod.continuous_inv_fun :
  continuous_on (prod.inv_fun' e₁ e₂) ((e₁.base_set ∩ e₂.base_set) ×ˢ (univ : set (F₁ × F₂))) :=
begin
  rw (prod.inducing_diag E₁ E₂).continuous_on_iff,
  suffices : continuous_on (λ p : B × F₁ × F₂,
    (e₁.to_local_homeomorph.symm ⟨p.1, p.2.1⟩, e₂.to_local_homeomorph.symm ⟨p.1, p.2.2⟩))
    ((e₁.base_set ∩ e₂.base_set) ×ˢ (univ : set (F₁ × F₂))),
  { refine this.congr _,
    rintros ⟨b, v₁, v₂⟩ ⟨⟨h₁, h₂⟩, _⟩,
    dsimp at ⊢ h₁ h₂,
    rw [prod.inv_fun'_apply h₁ h₂, e₁.symm_apply_eq_mk_continuous_linear_equiv_at_symm b h₁,
      e₂.symm_apply_eq_mk_continuous_linear_equiv_at_symm b h₂] },
  have H₁ : continuous (λ p : B × F₁ × F₂, ((p.1, p.2.1), (p.1, p.2.2))) :=
    (continuous_id.prod_map continuous_fst).prod_mk (continuous_id.prod_map continuous_snd),
  have H₂ := e₁.to_local_homeomorph.symm.continuous_on.prod_map
    e₂.to_local_homeomorph.symm.continuous_on,
  refine H₂.comp H₁.continuous_on (λ x h, ⟨_, _⟩),
  { dsimp,
    rw e₁.target_eq,
    exact ⟨h.1.1, mem_univ _⟩ },
  { dsimp,
    rw e₂.target_eq,
    exact ⟨h.1.2, mem_univ _⟩ }
end

variables (e₁ e₂)

/-- Given trivializations `e₁`, `e₂` for vector bundles `E₁`, `E₂` over a base `B`, the induced
trivialization for the direct sum of `E₁` and `E₂`, whose base set is `e₁.base_set ∩ e₂.base_set`.
-/
def prod : trivialization R (F₁ × F₂) (E₁ ×ᵇ E₂) :=
{ to_fun := prod.to_fun' e₁ e₂,
  inv_fun := prod.inv_fun' e₁ e₂,
  source := (proj (λ x, E₁ x × E₂ x)) ⁻¹' (e₁.base_set ∩ e₂.base_set),
  target := (e₁.base_set ∩ e₂.base_set) ×ˢ (set.univ : set (F₁ × F₂)),
  map_source' := λ ⟨x, v₁, v₂⟩ h, ⟨h, set.mem_univ _⟩,
  map_target' := λ ⟨x, w₁, w₂⟩ h, h.1,
  left_inv' := λ x, prod.left_inv,
  right_inv' := λ x, prod.right_inv,
  open_source := begin
    refine (e₁.open_base_set.inter e₂.open_base_set).preimage _,
    have : continuous (proj E₁) := continuous_proj R B F₁,
    exact this.comp (continuous_fst.comp (prod.inducing_diag E₁ E₂).continuous),
  end,
  open_target := (e₁.open_base_set.inter e₂.open_base_set).prod is_open_univ,
  continuous_to_fun := prod.continuous_to_fun,
  continuous_inv_fun := prod.continuous_inv_fun,
  base_set := e₁.base_set ∩ e₂.base_set,
  open_base_set := e₁.open_base_set.inter e₂.open_base_set,
  source_eq := rfl,
  target_eq := rfl,
  proj_to_fun := λ ⟨x, v₁, v₂⟩ h, rfl,
  linear := λ x ⟨h₁, h₂⟩,
  { map_add := λ ⟨v₁, v₂⟩ ⟨v₁', v₂'⟩,
      congr_arg2 prod.mk ((e₁.linear x h₁).map_add v₁ v₁') ((e₂.linear x h₂).map_add v₂ v₂'),
    map_smul := λ c ⟨v₁, v₂⟩,
      congr_arg2 prod.mk ((e₁.linear x h₁).map_smul c v₁) ((e₂.linear x h₂).map_smul c v₂), } }

@[simp] lemma base_set_prod : (prod e₁ e₂).base_set = e₁.base_set ∩ e₂.base_set :=
rfl

variables {e₁ e₂}

lemma prod_apply {x : B} (hx₁ : x ∈ e₁.base_set) (hx₂ : x ∈ e₂.base_set) (v₁ : E₁ x)
  (v₂ : E₂ x) :
  prod e₁ e₂ ⟨x, (v₁, v₂)⟩
  = ⟨x, e₁.continuous_linear_equiv_at x hx₁ v₁, e₂.continuous_linear_equiv_at x hx₂ v₂⟩ :=
rfl

lemma prod_symm_apply {x : B} (hx₁ : x ∈ e₁.base_set) (hx₂ : x ∈ e₂.base_set) (w₁ : F₁) (w₂ : F₂) :
  (prod e₁ e₂).to_local_equiv.symm (x, (w₁, w₂))
  = ⟨x, ((e₁.continuous_linear_equiv_at x hx₁).symm w₁,
      (e₂.continuous_linear_equiv_at x hx₂).symm w₂)⟩ :=
prod.inv_fun'_apply hx₁ hx₂ w₁ w₂

end trivialization

open trivialization

variables [Π x : B, topological_space (E₁ x)] [Π x : B, topological_space (E₂ x)]
  [topological_vector_bundle R F₁ E₁] [topological_vector_bundle R F₂ E₂]

/-- The product of two vector bundles is a vector bundle. -/
instance _root_.bundle.prod.topological_vector_bundle :
  topological_vector_bundle R (F₁ × F₂) (E₁ ×ᵇ E₂) :=
{ total_space_mk_inducing := λ b,
  begin
    rw (prod.inducing_diag E₁ E₂).inducing_iff,
    exact (total_space_mk_inducing R F₁ E₁ b).prod_mk (total_space_mk_inducing R F₂ E₂ b),
  end,
  trivialization_atlas := (λ (p : trivialization R F₁ E₁ × trivialization R F₂ E₂), p.1.prod p.2) ''
    (trivialization_atlas R F₁ E₁ ×ˢ trivialization_atlas R F₂ E₂),
  trivialization_at := λ b, (trivialization_at R F₁ E₁ b).prod (trivialization_at R F₂ E₂ b),
  mem_base_set_trivialization_at :=
    λ b, ⟨mem_base_set_trivialization_at R F₁ E₁ b, mem_base_set_trivialization_at R F₂ E₂ b⟩,
  trivialization_mem_atlas := λ b,
    ⟨(_, _), ⟨trivialization_mem_atlas R F₁ E₁ b, trivialization_mem_atlas R F₂ E₂ b⟩, rfl⟩,
  continuous_coord_change := begin
    rintros _ ⟨⟨e₁, e₂⟩, ⟨he₁, he₂⟩, rfl⟩ _ ⟨⟨e'₁, e'₂⟩, ⟨he'₁, he'₂⟩, rfl⟩,
    let s := e₁.base_set ∩ e'₁.base_set,
    let t := e₂.base_set ∩ e'₂.base_set,
    let ε := coord_change he₁ he'₁,
    let η := coord_change he₂ he'₂,
    have fact : (s ∩ t) ×ˢ (univ : set $ F₁ × F₂) =
        (e₁.base_set ∩ e₂.base_set ∩  (e'₁.base_set ∩ e'₂.base_set)) ×ˢ (univ : set $ F₁ × F₂),
      by mfld_set_tac,
    refine ⟨s ∩ t, _, _, λ b, (ε b).prod (η b), _, _⟩,
    { rw fact,
      apply topological_fiber_bundle.trivialization.symm_trans_source_eq },
    { rw fact,
      apply topological_fiber_bundle.trivialization.symm_trans_target_eq },
    { have hε := (continuous_on_coord_change he₁ he'₁).mono (inter_subset_left s t),
      have hη := (continuous_on_coord_change he₂ he'₂).mono (inter_subset_right s t),
      exact hε.prod_map_equivL R hη },
    { rintros b ⟨hbs, hbt⟩ ⟨u, v⟩,
      have h : (e₁.prod e₂).to_local_homeomorph.symm _ = _ := prod_symm_apply hbs.1 hbt.1 u v,
      simp only [ε, η, h, prod_apply hbs.2 hbt.2,
        ← comp_continuous_linear_equiv_at_eq_coord_change he₁ he'₁ hbs,
        ← comp_continuous_linear_equiv_at_eq_coord_change he₂ he'₂ hbt,
        eq_self_iff_true, function.comp_app, local_equiv.coe_trans, local_homeomorph.coe_coe,
        local_homeomorph.coe_coe_symm, prod.mk.inj_iff,
        topological_vector_bundle.trivialization.coe_coe, true_and,
        continuous_linear_equiv.prod_apply, continuous_linear_equiv.trans_apply] },
  end }

variables {R F₁ E₁ F₂ E₂}

@[simp] lemma trivialization.continuous_linear_equiv_at_prod {e₁ : trivialization R F₁ E₁}
  {e₂ : trivialization R F₂ E₂} {x : B} (hx₁ : x ∈ e₁.base_set) (hx₂ : x ∈ e₂.base_set) :
  (e₁.prod e₂).continuous_linear_equiv_at x ⟨hx₁, hx₂⟩
  = (e₁.continuous_linear_equiv_at x hx₁).prod (e₂.continuous_linear_equiv_at x hx₂) :=
begin
  ext1,
  funext v,
  obtain ⟨v₁, v₂⟩ := v,
  rw [(e₁.prod e₂).continuous_linear_equiv_at_apply, trivialization.prod],
  exact congr_arg prod.snd (prod_apply hx₁ hx₂ v₁ v₂),
end

end topological_vector_bundle


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

! This file was ported from Lean 3 source module order.filter.pi
! leanprover-community/mathlib commit 4d392a6c9c4539cbeca399b3ee0afea398fbd2eb
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Order.Filter.Bases

/-!
# (Co)product of a family of filters

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

In this file we define two filters on `Π i, α i` and prove some basic properties of these filters.

* `filter.pi (f : Π i, filter (α i))` to be the maximal filter on `Π i, α i` such that
  `∀ i, filter.tendsto (function.eval i) (filter.pi f) (f i)`. It is defined as
  `Π i, filter.comap (function.eval i) (f i)`. This is a generalization of `filter.prod` to indexed
  products.

* `filter.Coprod (f : Π i, filter (α i))`: a generalization of `filter.coprod`; it is the supremum
  of `comap (eval i) (f i)`.
-/

open Set Function

open Classical Filter

namespace Filter

variable {ι : Type _} {α : ι → Type _} {f f₁ f₂ : ∀ i, Filter (α i)} {s : ∀ i, Set (α i)}

section Pi

#print Filter.pi /-
/-- The product of an indexed family of filters. -/
def pi (f : ∀ i, Filter (α i)) : Filter (∀ i, α i) :=
  ⨅ i, comap (eval i) (f i)
#align filter.pi Filter.pi
-/

#print Filter.pi.isCountablyGenerated /-
instance pi.isCountablyGenerated [Countable ι] [∀ i, IsCountablyGenerated (f i)] :
    IsCountablyGenerated (pi f) :=
  infᵢ.isCountablyGenerated _
#align filter.pi.is_countably_generated Filter.pi.isCountablyGenerated
-/

#print Filter.tendsto_eval_pi /-
theorem tendsto_eval_pi (f : ∀ i, Filter (α i)) (i : ι) : Tendsto (eval i) (pi f) (f i) :=
  tendsto_infᵢ' i tendsto_comap
#align filter.tendsto_eval_pi Filter.tendsto_eval_pi
-/

/- warning: filter.tendsto_pi -> Filter.tendsto_pi is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {β : Type.{u3}} {m : β -> (forall (i : ι), α i)} {l : Filter.{u3} β}, Iff (Filter.Tendsto.{u3, max u1 u2} β (forall (i : ι), α i) m l (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f)) (forall (i : ι), Filter.Tendsto.{u3, u2} β (α i) (fun (x : β) => m x i) l (f i))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} {f : forall (i : ι), Filter.{u1} (α i)} {β : Type.{u3}} {m : β -> (forall (i : ι), α i)} {l : Filter.{u3} β}, Iff (Filter.Tendsto.{u3, max u2 u1} β (forall (i : ι), α i) m l (Filter.pi.{u2, u1} ι (fun (i : ι) => α i) f)) (forall (i : ι), Filter.Tendsto.{u3, u1} β (α i) (fun (x : β) => m x i) l (f i))
Case conversion may be inaccurate. Consider using '#align filter.tendsto_pi Filter.tendsto_piₓ'. -/
theorem tendsto_pi {β : Type _} {m : β → ∀ i, α i} {l : Filter β} :
    Tendsto m l (pi f) ↔ ∀ i, Tendsto (fun x => m x i) l (f i) := by
  simp only [pi, tendsto_infi, tendsto_comap_iff]
#align filter.tendsto_pi Filter.tendsto_pi

/- warning: filter.le_pi -> Filter.le_pi is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {g : Filter.{max u1 u2} (forall (i : ι), α i)}, Iff (LE.le.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.partialOrder.{max u1 u2} (forall (i : ι), α i)))) g (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f)) (forall (i : ι), Filter.Tendsto.{max u1 u2, u2} (forall (x : ι), α x) (α i) (Function.eval.{succ u1, succ u2} ι (fun (i : ι) => α i) i) g (f i))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} {f : forall (i : ι), Filter.{u1} (α i)} {g : Filter.{max u2 u1} (forall (i : ι), α i)}, Iff (LE.le.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (Preorder.toLE.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (PartialOrder.toPreorder.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.instPartialOrderFilter.{max u2 u1} (forall (i : ι), α i)))) g (Filter.pi.{u2, u1} ι (fun (i : ι) => α i) f)) (forall (i : ι), Filter.Tendsto.{max u2 u1, u1} (forall (x : ι), α x) (α i) (Function.eval.{succ u2, succ u1} ι (fun (i : ι) => α i) i) g (f i))
Case conversion may be inaccurate. Consider using '#align filter.le_pi Filter.le_piₓ'. -/
theorem le_pi {g : Filter (∀ i, α i)} : g ≤ pi f ↔ ∀ i, Tendsto (eval i) g (f i) :=
  tendsto_pi
#align filter.le_pi Filter.le_pi

/- warning: filter.pi_mono -> Filter.pi_mono is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f₁ : forall (i : ι), Filter.{u2} (α i)} {f₂ : forall (i : ι), Filter.{u2} (α i)}, (forall (i : ι), LE.le.{u2} (Filter.{u2} (α i)) (Preorder.toLE.{u2} (Filter.{u2} (α i)) (PartialOrder.toPreorder.{u2} (Filter.{u2} (α i)) (Filter.partialOrder.{u2} (α i)))) (f₁ i) (f₂ i)) -> (LE.le.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.partialOrder.{max u1 u2} (forall (i : ι), α i)))) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f₁) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f₂))
but is expected to have type
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f₁ : forall (i : ι), Filter.{u2} (α i)} {f₂ : forall (i : ι), Filter.{u2} (α i)}, (forall (i : ι), LE.le.{u2} (Filter.{u2} (α i)) (Preorder.toLE.{u2} (Filter.{u2} (α i)) (PartialOrder.toPreorder.{u2} (Filter.{u2} (α i)) (Filter.instPartialOrderFilter.{u2} (α i)))) (f₁ i) (f₂ i)) -> (LE.le.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.instPartialOrderFilter.{max u1 u2} (forall (i : ι), α i)))) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f₁) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f₂))
Case conversion may be inaccurate. Consider using '#align filter.pi_mono Filter.pi_monoₓ'. -/
@[mono]
theorem pi_mono (h : ∀ i, f₁ i ≤ f₂ i) : pi f₁ ≤ pi f₂ :=
  infᵢ_mono fun i => comap_mono <| h i
#align filter.pi_mono Filter.pi_mono

#print Filter.mem_pi_of_mem /-
theorem mem_pi_of_mem (i : ι) {s : Set (α i)} (hs : s ∈ f i) : eval i ⁻¹' s ∈ pi f :=
  mem_infᵢ_of_mem i <| preimage_mem_comap hs
#align filter.mem_pi_of_mem Filter.mem_pi_of_mem
-/

/- warning: filter.pi_mem_pi -> Filter.pi_mem_pi is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : forall (i : ι), Set.{u2} (α i)} {I : Set.{u1} ι}, (Set.Finite.{u1} ι I) -> (forall (i : ι), (Membership.Mem.{u1, u1} ι (Set.{u1} ι) (Set.hasMem.{u1} ι) i I) -> (Membership.Mem.{u2, u2} (Set.{u2} (α i)) (Filter.{u2} (α i)) (Filter.hasMem.{u2} (α i)) (s i) (f i))) -> (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (forall (i : ι), α i)) (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.hasMem.{max u1 u2} (forall (i : ι), α i)) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) I s) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} {f : forall (i : ι), Filter.{u1} (α i)} {s : forall (i : ι), Set.{u1} (α i)} {I : Set.{u2} ι}, (Set.Finite.{u2} ι I) -> (forall (i : ι), (Membership.mem.{u2, u2} ι (Set.{u2} ι) (Set.instMembershipSet.{u2} ι) i I) -> (Membership.mem.{u1, u1} (Set.{u1} (α i)) (Filter.{u1} (α i)) (instMembershipSetFilter.{u1} (α i)) (s i) (f i))) -> (Membership.mem.{max u2 u1, max u1 u2} (Set.{max u2 u1} (forall (i : ι), α i)) (Filter.{max u2 u1} (forall (i : ι), α i)) (instMembershipSetFilter.{max u2 u1} (forall (i : ι), α i)) (Set.pi.{u2, u1} ι (fun (i : ι) => α i) I s) (Filter.pi.{u2, u1} ι (fun (i : ι) => α i) f))
Case conversion may be inaccurate. Consider using '#align filter.pi_mem_pi Filter.pi_mem_piₓ'. -/
theorem pi_mem_pi {I : Set ι} (hI : I.Finite) (h : ∀ i ∈ I, s i ∈ f i) : I.pi s ∈ pi f :=
  by
  rw [pi_def, bInter_eq_Inter]
  refine' mem_infi_of_Inter hI (fun i => _) subset.rfl
  exact preimage_mem_comap (h i i.2)
#align filter.pi_mem_pi Filter.pi_mem_pi

/- warning: filter.mem_pi -> Filter.mem_pi is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : Set.{max u1 u2} (forall (i : ι), α i)}, Iff (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (forall (i : ι), α i)) (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.hasMem.{max u1 u2} (forall (i : ι), α i)) s (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f)) (Exists.{succ u1} (Set.{u1} ι) (fun (I : Set.{u1} ι) => And (Set.Finite.{u1} ι I) (Exists.{max (succ u1) (succ u2)} (forall (i : ι), Set.{u2} (α i)) (fun (t : forall (i : ι), Set.{u2} (α i)) => And (forall (i : ι), Membership.Mem.{u2, u2} (Set.{u2} (α i)) (Filter.{u2} (α i)) (Filter.hasMem.{u2} (α i)) (t i) (f i)) (HasSubset.Subset.{max u1 u2} (Set.{max u1 u2} (forall (i : ι), α i)) (Set.hasSubset.{max u1 u2} (forall (i : ι), α i)) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) I t) s)))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} {f : forall (i : ι), Filter.{u1} (α i)} {s : Set.{max u2 u1} (forall (i : ι), α i)}, Iff (Membership.mem.{max u2 u1, max u1 u2} (Set.{max u2 u1} (forall (i : ι), α i)) (Filter.{max u2 u1} (forall (i : ι), α i)) (instMembershipSetFilter.{max u2 u1} (forall (i : ι), α i)) s (Filter.pi.{u2, u1} ι (fun (i : ι) => α i) f)) (Exists.{succ u2} (Set.{u2} ι) (fun (I : Set.{u2} ι) => And (Set.Finite.{u2} ι I) (Exists.{max (succ u2) (succ u1)} (forall (i : ι), Set.{u1} (α i)) (fun (t : forall (i : ι), Set.{u1} (α i)) => And (forall (i : ι), Membership.mem.{u1, u1} (Set.{u1} (α i)) (Filter.{u1} (α i)) (instMembershipSetFilter.{u1} (α i)) (t i) (f i)) (HasSubset.Subset.{max u2 u1} (Set.{max u2 u1} (forall (i : ι), α i)) (Set.instHasSubsetSet.{max u2 u1} (forall (i : ι), α i)) (Set.pi.{u2, u1} ι (fun (i : ι) => α i) I t) s)))))
Case conversion may be inaccurate. Consider using '#align filter.mem_pi Filter.mem_piₓ'. -/
theorem mem_pi {s : Set (∀ i, α i)} :
    s ∈ pi f ↔ ∃ I : Set ι, I.Finite ∧ ∃ t : ∀ i, Set (α i), (∀ i, t i ∈ f i) ∧ I.pi t ⊆ s :=
  by
  constructor
  · simp only [pi, mem_infi', mem_comap, pi_def]
    rintro ⟨I, If, V, hVf, hVI, rfl, -⟩
    choose t htf htV using hVf
    exact ⟨I, If, t, htf, Inter₂_mono fun i _ => htV i⟩
  · rintro ⟨I, If, t, htf, hts⟩
    exact mem_of_superset (pi_mem_pi If fun i _ => htf i) hts
#align filter.mem_pi Filter.mem_pi

/- warning: filter.mem_pi' -> Filter.mem_pi' is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : Set.{max u1 u2} (forall (i : ι), α i)}, Iff (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (forall (i : ι), α i)) (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.hasMem.{max u1 u2} (forall (i : ι), α i)) s (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f)) (Exists.{succ u1} (Finset.{u1} ι) (fun (I : Finset.{u1} ι) => Exists.{max (succ u1) (succ u2)} (forall (i : ι), Set.{u2} (α i)) (fun (t : forall (i : ι), Set.{u2} (α i)) => And (forall (i : ι), Membership.Mem.{u2, u2} (Set.{u2} (α i)) (Filter.{u2} (α i)) (Filter.hasMem.{u2} (α i)) (t i) (f i)) (HasSubset.Subset.{max u1 u2} (Set.{max u1 u2} (forall (i : ι), α i)) (Set.hasSubset.{max u1 u2} (forall (i : ι), α i)) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Finset.{u1} ι) (Set.{u1} ι) (HasLiftT.mk.{succ u1, succ u1} (Finset.{u1} ι) (Set.{u1} ι) (CoeTCₓ.coe.{succ u1, succ u1} (Finset.{u1} ι) (Set.{u1} ι) (Finset.Set.hasCoeT.{u1} ι))) I) t) s))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} {f : forall (i : ι), Filter.{u1} (α i)} {s : Set.{max u2 u1} (forall (i : ι), α i)}, Iff (Membership.mem.{max u2 u1, max u1 u2} (Set.{max u2 u1} (forall (i : ι), α i)) (Filter.{max u2 u1} (forall (i : ι), α i)) (instMembershipSetFilter.{max u2 u1} (forall (i : ι), α i)) s (Filter.pi.{u2, u1} ι (fun (i : ι) => α i) f)) (Exists.{succ u2} (Finset.{u2} ι) (fun (I : Finset.{u2} ι) => Exists.{max (succ u2) (succ u1)} (forall (i : ι), Set.{u1} (α i)) (fun (t : forall (i : ι), Set.{u1} (α i)) => And (forall (i : ι), Membership.mem.{u1, u1} (Set.{u1} (α i)) (Filter.{u1} (α i)) (instMembershipSetFilter.{u1} (α i)) (t i) (f i)) (HasSubset.Subset.{max u1 u2} (Set.{max u2 u1} (forall (i : ι), α i)) (Set.instHasSubsetSet.{max u1 u2} (forall (i : ι), α i)) (Set.pi.{u2, u1} ι (fun (i : ι) => α i) (Finset.toSet.{u2} ι I) t) s))))
Case conversion may be inaccurate. Consider using '#align filter.mem_pi' Filter.mem_pi'ₓ'. -/
theorem mem_pi' {s : Set (∀ i, α i)} :
    s ∈ pi f ↔ ∃ I : Finset ι, ∃ t : ∀ i, Set (α i), (∀ i, t i ∈ f i) ∧ Set.pi (↑I) t ⊆ s :=
  mem_pi.trans exists_finite_iff_finset
#align filter.mem_pi' Filter.mem_pi'

#print Filter.mem_of_pi_mem_pi /-
theorem mem_of_pi_mem_pi [∀ i, NeBot (f i)] {I : Set ι} (h : I.pi s ∈ pi f) {i : ι} (hi : i ∈ I) :
    s i ∈ f i := by
  rcases mem_pi.1 h with ⟨I', I'f, t, htf, hts⟩
  refine' mem_of_superset (htf i) fun x hx => _
  have : ∀ i, (t i).Nonempty := fun i => nonempty_of_mem (htf i)
  choose g hg
  have : update g i x ∈ I'.pi t := by
    intro j hj
    rcases eq_or_ne j i with (rfl | hne) <;> simp [*]
  simpa using hts this i hi
#align filter.mem_of_pi_mem_pi Filter.mem_of_pi_mem_pi
-/

#print Filter.pi_mem_pi_iff /-
@[simp]
theorem pi_mem_pi_iff [∀ i, NeBot (f i)] {I : Set ι} (hI : I.Finite) :
    I.pi s ∈ pi f ↔ ∀ i ∈ I, s i ∈ f i :=
  ⟨fun h i hi => mem_of_pi_mem_pi h hi, pi_mem_pi hI⟩
#align filter.pi_mem_pi_iff Filter.pi_mem_pi_iff
-/

#print Filter.hasBasis_pi /-
theorem hasBasis_pi {ι' : ι → Type} {s : ∀ i, ι' i → Set (α i)} {p : ∀ i, ι' i → Prop}
    (h : ∀ i, (f i).HasBasis (p i) (s i)) :
    (pi f).HasBasis (fun If : Set ι × ∀ i, ι' i => If.1.Finite ∧ ∀ i ∈ If.1, p i (If.2 i))
      fun If : Set ι × ∀ i, ι' i => If.1.pi fun i => s i <| If.2 i :=
  by
  have : (pi f).HasBasis _ _ := has_basis_infi' fun i => (h i).comap (eval i : (∀ j, α j) → α i)
  convert this
  ext
  simp
#align filter.has_basis_pi Filter.hasBasis_pi
-/

/- warning: filter.pi_inf_principal_univ_pi_eq_bot -> Filter.pi_inf_principal_univ_pi_eq_bot is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : forall (i : ι), Set.{u2} (α i)}, Iff (Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (forall (i : ι), α i)) (Inf.inf.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.hasInf.{max u1 u2} (forall (i : ι), α i)) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f) (Filter.principal.{max u1 u2} (forall (i : ι), α i) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) (Set.univ.{u1} ι) s))) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (CompleteLattice.toHasBot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.completeLattice.{max u1 u2} (forall (i : ι), α i))))) (Exists.{succ u1} ι (fun (i : ι) => Eq.{succ u2} (Filter.{u2} (α i)) (Inf.inf.{u2} (Filter.{u2} (α i)) (Filter.hasInf.{u2} (α i)) (f i) (Filter.principal.{u2} (α i) (s i))) (Bot.bot.{u2} (Filter.{u2} (α i)) (CompleteLattice.toHasBot.{u2} (Filter.{u2} (α i)) (Filter.completeLattice.{u2} (α i))))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} {f : forall (i : ι), Filter.{u1} (α i)} {s : forall (i : ι), Set.{u1} (α i)}, Iff (Eq.{max (succ u2) (succ u1)} (Filter.{max u2 u1} (forall (i : ι), α i)) (Inf.inf.{max u1 u2} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.instInfFilter.{max u2 u1} (forall (i : ι), α i)) (Filter.pi.{u2, u1} ι (fun (i : ι) => α i) f) (Filter.principal.{max u2 u1} (forall (i : ι), α i) (Set.pi.{u2, u1} ι (fun (i : ι) => α i) (Set.univ.{u2} ι) s))) (Bot.bot.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (CompleteLattice.toBot.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.instCompleteLatticeFilter.{max u2 u1} (forall (i : ι), α i))))) (Exists.{succ u2} ι (fun (i : ι) => Eq.{succ u1} (Filter.{u1} (α i)) (Inf.inf.{u1} (Filter.{u1} (α i)) (Filter.instInfFilter.{u1} (α i)) (f i) (Filter.principal.{u1} (α i) (s i))) (Bot.bot.{u1} (Filter.{u1} (α i)) (CompleteLattice.toBot.{u1} (Filter.{u1} (α i)) (Filter.instCompleteLatticeFilter.{u1} (α i))))))
Case conversion may be inaccurate. Consider using '#align filter.pi_inf_principal_univ_pi_eq_bot Filter.pi_inf_principal_univ_pi_eq_botₓ'. -/
@[simp]
theorem pi_inf_principal_univ_pi_eq_bot : pi f ⊓ 𝓟 (Set.pi univ s) = ⊥ ↔ ∃ i, f i ⊓ 𝓟 (s i) = ⊥ :=
  by
  constructor
  · simp only [inf_principal_eq_bot, mem_pi]
    contrapose!
    rintro (hsf : ∀ i, ∃ᶠ x in f i, x ∈ s i) I If t htf hts
    have : ∀ i, (s i ∩ t i).Nonempty := fun i => ((hsf i).and_eventually (htf i)).exists
    choose x hxs hxt
    exact hts (fun i hi => hxt i) (mem_univ_pi.2 hxs)
  · simp only [inf_principal_eq_bot]
    rintro ⟨i, hi⟩
    filter_upwards [mem_pi_of_mem i hi]with x using mt fun h => h i trivial
#align filter.pi_inf_principal_univ_pi_eq_bot Filter.pi_inf_principal_univ_pi_eq_bot

/- warning: filter.pi_inf_principal_pi_eq_bot -> Filter.pi_inf_principal_pi_eq_bot is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : forall (i : ι), Set.{u2} (α i)} [_inst_1 : forall (i : ι), Filter.NeBot.{u2} (α i) (f i)] {I : Set.{u1} ι}, Iff (Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (forall (i : ι), α i)) (Inf.inf.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.hasInf.{max u1 u2} (forall (i : ι), α i)) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f) (Filter.principal.{max u1 u2} (forall (i : ι), α i) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) I s))) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (CompleteLattice.toHasBot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.completeLattice.{max u1 u2} (forall (i : ι), α i))))) (Exists.{succ u1} ι (fun (i : ι) => Exists.{0} (Membership.Mem.{u1, u1} ι (Set.{u1} ι) (Set.hasMem.{u1} ι) i I) (fun (H : Membership.Mem.{u1, u1} ι (Set.{u1} ι) (Set.hasMem.{u1} ι) i I) => Eq.{succ u2} (Filter.{u2} (α i)) (Inf.inf.{u2} (Filter.{u2} (α i)) (Filter.hasInf.{u2} (α i)) (f i) (Filter.principal.{u2} (α i) (s i))) (Bot.bot.{u2} (Filter.{u2} (α i)) (CompleteLattice.toHasBot.{u2} (Filter.{u2} (α i)) (Filter.completeLattice.{u2} (α i)))))))
but is expected to have type
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : forall (i : ι), Set.{u2} (α i)} [_inst_1 : forall (i : ι), Filter.NeBot.{u2} (α i) (f i)] {I : Set.{u1} ι}, Iff (Eq.{max (succ u1) (succ u2)} (Filter.{max u1 u2} (forall (i : ι), α i)) (Inf.inf.{max u2 u1} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.instInfFilter.{max u1 u2} (forall (i : ι), α i)) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f) (Filter.principal.{max u1 u2} (forall (i : ι), α i) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) I s))) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (CompleteLattice.toBot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.instCompleteLatticeFilter.{max u1 u2} (forall (i : ι), α i))))) (Exists.{succ u1} ι (fun (i : ι) => And (Membership.mem.{u1, u1} ι (Set.{u1} ι) (Set.instMembershipSet.{u1} ι) i I) (Eq.{succ u2} (Filter.{u2} (α i)) (Inf.inf.{u2} (Filter.{u2} (α i)) (Filter.instInfFilter.{u2} (α i)) (f i) (Filter.principal.{u2} (α i) (s i))) (Bot.bot.{u2} (Filter.{u2} (α i)) (CompleteLattice.toBot.{u2} (Filter.{u2} (α i)) (Filter.instCompleteLatticeFilter.{u2} (α i)))))))
Case conversion may be inaccurate. Consider using '#align filter.pi_inf_principal_pi_eq_bot Filter.pi_inf_principal_pi_eq_botₓ'. -/
@[simp]
theorem pi_inf_principal_pi_eq_bot [∀ i, NeBot (f i)] {I : Set ι} :
    pi f ⊓ 𝓟 (Set.pi I s) = ⊥ ↔ ∃ i ∈ I, f i ⊓ 𝓟 (s i) = ⊥ :=
  by
  rw [← univ_pi_piecewise I, pi_inf_principal_univ_pi_eq_bot]
  refine' exists_congr fun i => _
  by_cases hi : i ∈ I <;> simp [hi, (‹∀ i, ne_bot (f i)› i).Ne]
#align filter.pi_inf_principal_pi_eq_bot Filter.pi_inf_principal_pi_eq_bot

/- warning: filter.pi_inf_principal_univ_pi_ne_bot -> Filter.pi_inf_principal_univ_pi_neBot is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : forall (i : ι), Set.{u2} (α i)}, Iff (Filter.NeBot.{max u1 u2} (forall (i : ι), α i) (Inf.inf.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.hasInf.{max u1 u2} (forall (i : ι), α i)) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f) (Filter.principal.{max u1 u2} (forall (i : ι), α i) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) (Set.univ.{u1} ι) s)))) (forall (i : ι), Filter.NeBot.{u2} (α i) (Inf.inf.{u2} (Filter.{u2} (α i)) (Filter.hasInf.{u2} (α i)) (f i) (Filter.principal.{u2} (α i) (s i))))
but is expected to have type
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : forall (i : ι), Set.{u2} (α i)}, Iff (Filter.NeBot.{max u2 u1} (forall (i : ι), α i) (Inf.inf.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.instInfFilter.{max u1 u2} (forall (i : ι), α i)) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f) (Filter.principal.{max u1 u2} (forall (i : ι), α i) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) (Set.univ.{u1} ι) s)))) (forall (i : ι), Filter.NeBot.{u2} (α i) (Inf.inf.{u2} (Filter.{u2} (α i)) (Filter.instInfFilter.{u2} (α i)) (f i) (Filter.principal.{u2} (α i) (s i))))
Case conversion may be inaccurate. Consider using '#align filter.pi_inf_principal_univ_pi_ne_bot Filter.pi_inf_principal_univ_pi_neBotₓ'. -/
@[simp]
theorem pi_inf_principal_univ_pi_neBot :
    NeBot (pi f ⊓ 𝓟 (Set.pi univ s)) ↔ ∀ i, NeBot (f i ⊓ 𝓟 (s i)) := by simp [ne_bot_iff]
#align filter.pi_inf_principal_univ_pi_ne_bot Filter.pi_inf_principal_univ_pi_neBot

/- warning: filter.pi_inf_principal_pi_ne_bot -> Filter.pi_inf_principal_pi_neBot is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : forall (i : ι), Set.{u2} (α i)} [_inst_1 : forall (i : ι), Filter.NeBot.{u2} (α i) (f i)] {I : Set.{u1} ι}, Iff (Filter.NeBot.{max u1 u2} (forall (i : ι), α i) (Inf.inf.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.hasInf.{max u1 u2} (forall (i : ι), α i)) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f) (Filter.principal.{max u1 u2} (forall (i : ι), α i) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) I s)))) (forall (i : ι), (Membership.Mem.{u1, u1} ι (Set.{u1} ι) (Set.hasMem.{u1} ι) i I) -> (Filter.NeBot.{u2} (α i) (Inf.inf.{u2} (Filter.{u2} (α i)) (Filter.hasInf.{u2} (α i)) (f i) (Filter.principal.{u2} (α i) (s i)))))
but is expected to have type
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : forall (i : ι), Set.{u2} (α i)} [_inst_1 : forall (i : ι), Filter.NeBot.{u2} (α i) (f i)] {I : Set.{u1} ι}, Iff (Filter.NeBot.{max u2 u1} (forall (i : ι), α i) (Inf.inf.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.instInfFilter.{max u1 u2} (forall (i : ι), α i)) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f) (Filter.principal.{max u1 u2} (forall (i : ι), α i) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) I s)))) (forall (i : ι), (Membership.mem.{u1, u1} ι (Set.{u1} ι) (Set.instMembershipSet.{u1} ι) i I) -> (Filter.NeBot.{u2} (α i) (Inf.inf.{u2} (Filter.{u2} (α i)) (Filter.instInfFilter.{u2} (α i)) (f i) (Filter.principal.{u2} (α i) (s i)))))
Case conversion may be inaccurate. Consider using '#align filter.pi_inf_principal_pi_ne_bot Filter.pi_inf_principal_pi_neBotₓ'. -/
@[simp]
theorem pi_inf_principal_pi_neBot [∀ i, NeBot (f i)] {I : Set ι} :
    NeBot (pi f ⊓ 𝓟 (I.pi s)) ↔ ∀ i ∈ I, NeBot (f i ⊓ 𝓟 (s i)) := by simp [ne_bot_iff]
#align filter.pi_inf_principal_pi_ne_bot Filter.pi_inf_principal_pi_neBot

/- warning: filter.pi_inf_principal_pi.ne_bot -> Filter.PiInfPrincipalPi.neBot is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : forall (i : ι), Set.{u2} (α i)} [h : forall (i : ι), Filter.NeBot.{u2} (α i) (Inf.inf.{u2} (Filter.{u2} (α i)) (Filter.hasInf.{u2} (α i)) (f i) (Filter.principal.{u2} (α i) (s i)))] {I : Set.{u1} ι}, Filter.NeBot.{max u1 u2} (forall (i : ι), α i) (Inf.inf.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.hasInf.{max u1 u2} (forall (i : ι), α i)) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f) (Filter.principal.{max u1 u2} (forall (i : ι), α i) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) I s)))
but is expected to have type
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : forall (i : ι), Set.{u2} (α i)} [h : forall (i : ι), Filter.NeBot.{u2} (α i) (Inf.inf.{u2} (Filter.{u2} (α i)) (Filter.instInfFilter.{u2} (α i)) (f i) (Filter.principal.{u2} (α i) (s i)))] {I : Set.{u1} ι}, Filter.NeBot.{max u2 u1} (forall (i : ι), α i) (Inf.inf.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.instInfFilter.{max u1 u2} (forall (i : ι), α i)) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f) (Filter.principal.{max u1 u2} (forall (i : ι), α i) (Set.pi.{u1, u2} ι (fun (i : ι) => α i) I s)))
Case conversion may be inaccurate. Consider using '#align filter.pi_inf_principal_pi.ne_bot Filter.PiInfPrincipalPi.neBotₓ'. -/
instance PiInfPrincipalPi.neBot [h : ∀ i, NeBot (f i ⊓ 𝓟 (s i))] {I : Set ι} :
    NeBot (pi f ⊓ 𝓟 (I.pi s)) :=
  (pi_inf_principal_univ_pi_neBot.2 ‹_›).mono <|
    inf_le_inf_left _ <| principal_mono.2 fun x hx i hi => hx i trivial
#align filter.pi_inf_principal_pi.ne_bot Filter.PiInfPrincipalPi.neBot

/- warning: filter.pi_eq_bot -> Filter.pi_eq_bot is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)}, Iff (Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (CompleteLattice.toHasBot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.completeLattice.{max u1 u2} (forall (i : ι), α i))))) (Exists.{succ u1} ι (fun (i : ι) => Eq.{succ u2} (Filter.{u2} (α i)) (f i) (Bot.bot.{u2} (Filter.{u2} (α i)) (CompleteLattice.toHasBot.{u2} (Filter.{u2} (α i)) (Filter.completeLattice.{u2} (α i))))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} {f : forall (i : ι), Filter.{u1} (α i)}, Iff (Eq.{max (succ u2) (succ u1)} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.pi.{u2, u1} ι (fun (i : ι) => α i) f) (Bot.bot.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (CompleteLattice.toBot.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.instCompleteLatticeFilter.{max u2 u1} (forall (i : ι), α i))))) (Exists.{succ u2} ι (fun (i : ι) => Eq.{succ u1} (Filter.{u1} (α i)) (f i) (Bot.bot.{u1} (Filter.{u1} (α i)) (CompleteLattice.toBot.{u1} (Filter.{u1} (α i)) (Filter.instCompleteLatticeFilter.{u1} (α i))))))
Case conversion may be inaccurate. Consider using '#align filter.pi_eq_bot Filter.pi_eq_botₓ'. -/
@[simp]
theorem pi_eq_bot : pi f = ⊥ ↔ ∃ i, f i = ⊥ := by
  simpa using @pi_inf_principal_univ_pi_eq_bot ι α f fun _ => univ
#align filter.pi_eq_bot Filter.pi_eq_bot

#print Filter.pi_neBot /-
@[simp]
theorem pi_neBot : NeBot (pi f) ↔ ∀ i, NeBot (f i) := by simp [ne_bot_iff]
#align filter.pi_ne_bot Filter.pi_neBot
-/

instance [∀ i, NeBot (f i)] : NeBot (pi f) :=
  pi_neBot.2 ‹_›

#print Filter.map_eval_pi /-
@[simp]
theorem map_eval_pi (f : ∀ i, Filter (α i)) [∀ i, NeBot (f i)] (i : ι) :
    map (eval i) (pi f) = f i :=
  by
  refine' le_antisymm (tendsto_eval_pi f i) fun s hs => _
  rcases mem_pi.1 (mem_map.1 hs) with ⟨I, hIf, t, htf, hI⟩
  rw [← image_subset_iff] at hI
  refine' mem_of_superset (htf i) ((subset_eval_image_pi _ _).trans hI)
  exact nonempty_of_mem (pi_mem_pi hIf fun i hi => htf i)
#align filter.map_eval_pi Filter.map_eval_pi
-/

/- warning: filter.pi_le_pi -> Filter.pi_le_pi is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f₁ : forall (i : ι), Filter.{u2} (α i)} {f₂ : forall (i : ι), Filter.{u2} (α i)} [_inst_1 : forall (i : ι), Filter.NeBot.{u2} (α i) (f₁ i)], Iff (LE.le.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.partialOrder.{max u1 u2} (forall (i : ι), α i)))) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f₁) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f₂)) (forall (i : ι), LE.le.{u2} (Filter.{u2} (α i)) (Preorder.toLE.{u2} (Filter.{u2} (α i)) (PartialOrder.toPreorder.{u2} (Filter.{u2} (α i)) (Filter.partialOrder.{u2} (α i)))) (f₁ i) (f₂ i))
but is expected to have type
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f₁ : forall (i : ι), Filter.{u2} (α i)} {f₂ : forall (i : ι), Filter.{u2} (α i)} [_inst_1 : forall (i : ι), Filter.NeBot.{u2} (α i) (f₁ i)], Iff (LE.le.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.instPartialOrderFilter.{max u1 u2} (forall (i : ι), α i)))) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f₁) (Filter.pi.{u1, u2} ι (fun (i : ι) => α i) f₂)) (forall (i : ι), LE.le.{u2} (Filter.{u2} (α i)) (Preorder.toLE.{u2} (Filter.{u2} (α i)) (PartialOrder.toPreorder.{u2} (Filter.{u2} (α i)) (Filter.instPartialOrderFilter.{u2} (α i)))) (f₁ i) (f₂ i))
Case conversion may be inaccurate. Consider using '#align filter.pi_le_pi Filter.pi_le_piₓ'. -/
@[simp]
theorem pi_le_pi [∀ i, NeBot (f₁ i)] : pi f₁ ≤ pi f₂ ↔ ∀ i, f₁ i ≤ f₂ i :=
  ⟨fun h i => map_eval_pi f₁ i ▸ (tendsto_eval_pi _ _).mono_left h, pi_mono⟩
#align filter.pi_le_pi Filter.pi_le_pi

#print Filter.pi_inj /-
@[simp]
theorem pi_inj [∀ i, NeBot (f₁ i)] : pi f₁ = pi f₂ ↔ f₁ = f₂ :=
  by
  refine' ⟨fun h => _, congr_arg pi⟩
  have hle : f₁ ≤ f₂ := pi_le_pi.1 h.le
  haveI : ∀ i, ne_bot (f₂ i) := fun i => ne_bot_of_le (hle i)
  exact hle.antisymm (pi_le_pi.1 h.ge)
#align filter.pi_inj Filter.pi_inj
-/

end Pi

/-! ### `n`-ary coproducts of filters -/

section Coprod

#print Filter.coprodᵢ /-
/-- Coproduct of filters. -/
protected def coprodᵢ (f : ∀ i, Filter (α i)) : Filter (∀ i, α i) :=
  ⨆ i : ι, comap (eval i) (f i)
#align filter.Coprod Filter.coprodᵢ
-/

/- warning: filter.mem_Coprod_iff -> Filter.mem_coprodᵢ_iff is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : Set.{max u1 u2} (forall (i : ι), α i)}, Iff (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (forall (i : ι), α i)) (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.hasMem.{max u1 u2} (forall (i : ι), α i)) s (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => α i) f)) (forall (i : ι), Exists.{succ u2} (Set.{u2} (α i)) (fun (t₁ : Set.{u2} (α i)) => Exists.{0} (Membership.Mem.{u2, u2} (Set.{u2} (α i)) (Filter.{u2} (α i)) (Filter.hasMem.{u2} (α i)) t₁ (f i)) (fun (H : Membership.Mem.{u2, u2} (Set.{u2} (α i)) (Filter.{u2} (α i)) (Filter.hasMem.{u2} (α i)) t₁ (f i)) => HasSubset.Subset.{max u1 u2} (Set.{max u1 u2} (forall (x : ι), α x)) (Set.hasSubset.{max u1 u2} (forall (x : ι), α x)) (Set.preimage.{max u1 u2, u2} (forall (x : ι), α x) (α i) (Function.eval.{succ u1, succ u2} ι (fun (i : ι) => α i) i) t₁) s)))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} {f : forall (i : ι), Filter.{u1} (α i)} {s : Set.{max u2 u1} (forall (i : ι), α i)}, Iff (Membership.mem.{max u2 u1, max u1 u2} (Set.{max u2 u1} (forall (i : ι), α i)) (Filter.{max u2 u1} (forall (i : ι), α i)) (instMembershipSetFilter.{max u2 u1} (forall (i : ι), α i)) s (Filter.coprodᵢ.{u2, u1} ι (fun (i : ι) => α i) f)) (forall (i : ι), Exists.{succ u1} (Set.{u1} (α i)) (fun (t₁ : Set.{u1} (α i)) => And (Membership.mem.{u1, u1} (Set.{u1} (α i)) (Filter.{u1} (α i)) (instMembershipSetFilter.{u1} (α i)) t₁ (f i)) (HasSubset.Subset.{max u2 u1} (Set.{max u2 u1} (forall (x : ι), α x)) (Set.instHasSubsetSet.{max u2 u1} (forall (x : ι), α x)) (Set.preimage.{max u2 u1, u1} (forall (x : ι), α x) (α i) (Function.eval.{succ u2, succ u1} ι α i) t₁) s)))
Case conversion may be inaccurate. Consider using '#align filter.mem_Coprod_iff Filter.mem_coprodᵢ_iffₓ'. -/
theorem mem_coprodᵢ_iff {s : Set (∀ i, α i)} :
    s ∈ Filter.coprodᵢ f ↔ ∀ i : ι, ∃ t₁ ∈ f i, eval i ⁻¹' t₁ ⊆ s := by simp [Filter.coprodᵢ]
#align filter.mem_Coprod_iff Filter.mem_coprodᵢ_iff

/- warning: filter.compl_mem_Coprod -> Filter.compl_mem_coprodᵢ is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {s : Set.{max u1 u2} (forall (i : ι), α i)}, Iff (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (forall (i : ι), α i)) (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.hasMem.{max u1 u2} (forall (i : ι), α i)) (HasCompl.compl.{max u1 u2} (Set.{max u1 u2} (forall (i : ι), α i)) (BooleanAlgebra.toHasCompl.{max u1 u2} (Set.{max u1 u2} (forall (i : ι), α i)) (Set.booleanAlgebra.{max u1 u2} (forall (i : ι), α i))) s) (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => α i) f)) (forall (i : ι), Membership.Mem.{u2, u2} (Set.{u2} (α i)) (Filter.{u2} (α i)) (Filter.hasMem.{u2} (α i)) (HasCompl.compl.{u2} (Set.{u2} (α i)) (BooleanAlgebra.toHasCompl.{u2} (Set.{u2} (α i)) (Set.booleanAlgebra.{u2} (α i))) (Set.image.{max u1 u2, u2} (forall (x : ι), α x) (α i) (Function.eval.{succ u1, succ u2} ι (fun (i : ι) => α i) i) s)) (f i))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} {f : forall (i : ι), Filter.{u1} (α i)} {s : Set.{max u2 u1} (forall (i : ι), α i)}, Iff (Membership.mem.{max u2 u1, max u1 u2} (Set.{max u2 u1} (forall (i : ι), α i)) (Filter.{max u2 u1} (forall (i : ι), α i)) (instMembershipSetFilter.{max u2 u1} (forall (i : ι), α i)) (HasCompl.compl.{max u2 u1} (Set.{max u2 u1} (forall (i : ι), α i)) (BooleanAlgebra.toHasCompl.{max u2 u1} (Set.{max u2 u1} (forall (i : ι), α i)) (Set.instBooleanAlgebraSet.{max u2 u1} (forall (i : ι), α i))) s) (Filter.coprodᵢ.{u2, u1} ι (fun (i : ι) => α i) f)) (forall (i : ι), Membership.mem.{u1, u1} (Set.{u1} (α i)) (Filter.{u1} (α i)) (instMembershipSetFilter.{u1} (α i)) (HasCompl.compl.{u1} (Set.{u1} (α i)) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} (α i)) (Set.instBooleanAlgebraSet.{u1} (α i))) (Set.image.{max u2 u1, u1} (forall (x : ι), α x) (α i) (Function.eval.{succ u2, succ u1} ι (fun (i : ι) => α i) i) s)) (f i))
Case conversion may be inaccurate. Consider using '#align filter.compl_mem_Coprod Filter.compl_mem_coprodᵢₓ'. -/
theorem compl_mem_coprodᵢ {s : Set (∀ i, α i)} :
    sᶜ ∈ Filter.coprodᵢ f ↔ ∀ i, (eval i '' s)ᶜ ∈ f i := by
  simp only [Filter.coprodᵢ, mem_supr, compl_mem_comap]
#align filter.compl_mem_Coprod Filter.compl_mem_coprodᵢ

#print Filter.coprodᵢ_neBot_iff' /-
theorem coprodᵢ_neBot_iff' : NeBot (Filter.coprodᵢ f) ↔ (∀ i, Nonempty (α i)) ∧ ∃ d, NeBot (f d) :=
  by simp only [Filter.coprodᵢ, supr_ne_bot, ← exists_and_left, ← comap_eval_ne_bot_iff']
#align filter.Coprod_ne_bot_iff' Filter.coprodᵢ_neBot_iff'
-/

#print Filter.coprodᵢ_neBot_iff /-
@[simp]
theorem coprodᵢ_neBot_iff [∀ i, Nonempty (α i)] : NeBot (Filter.coprodᵢ f) ↔ ∃ d, NeBot (f d) := by
  simp [Coprod_ne_bot_iff', *]
#align filter.Coprod_ne_bot_iff Filter.coprodᵢ_neBot_iff
-/

/- warning: filter.Coprod_eq_bot_iff' -> Filter.coprodᵢ_eq_bot_iff' is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)}, Iff (Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => α i) f) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (CompleteLattice.toHasBot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.completeLattice.{max u1 u2} (forall (i : ι), α i))))) (Or (Exists.{succ u1} ι (fun (i : ι) => IsEmpty.{succ u2} (α i))) (Eq.{max (succ u1) (succ u2)} (forall (i : ι), Filter.{u2} (α i)) f (Bot.bot.{max u1 u2} (forall (i : ι), Filter.{u2} (α i)) (Pi.hasBot.{u1, u2} ι (fun (i : ι) => Filter.{u2} (α i)) (fun (i : ι) => CompleteLattice.toHasBot.{u2} (Filter.{u2} (α i)) (Filter.completeLattice.{u2} (α i)))))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}} {f : forall (i : ι), Filter.{u1} (α i)}, Iff (Eq.{max (succ u2) (succ u1)} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.coprodᵢ.{u2, u1} ι (fun (i : ι) => α i) f) (Bot.bot.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (CompleteLattice.toBot.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.instCompleteLatticeFilter.{max u2 u1} (forall (i : ι), α i))))) (Or (Exists.{succ u2} ι (fun (i : ι) => IsEmpty.{succ u1} (α i))) (Eq.{max (succ u2) (succ u1)} (forall (i : ι), Filter.{u1} (α i)) f (Bot.bot.{max u2 u1} (forall (i : ι), Filter.{u1} (α i)) (Pi.instBotForAll.{u2, u1} ι (fun (i : ι) => Filter.{u1} (α i)) (fun (i : ι) => CompleteLattice.toBot.{u1} (Filter.{u1} (α i)) (Filter.instCompleteLatticeFilter.{u1} (α i)))))))
Case conversion may be inaccurate. Consider using '#align filter.Coprod_eq_bot_iff' Filter.coprodᵢ_eq_bot_iff'ₓ'. -/
theorem coprodᵢ_eq_bot_iff' : Filter.coprodᵢ f = ⊥ ↔ (∃ i, IsEmpty (α i)) ∨ f = ⊥ := by
  simpa [not_and_or, funext_iff] using not_congr Coprod_ne_bot_iff'
#align filter.Coprod_eq_bot_iff' Filter.coprodᵢ_eq_bot_iff'

/- warning: filter.Coprod_eq_bot_iff -> Filter.coprodᵢ_eq_bot_iff is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} [_inst_1 : forall (i : ι), Nonempty.{succ u2} (α i)], Iff (Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => α i) f) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (CompleteLattice.toHasBot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.completeLattice.{max u1 u2} (forall (i : ι), α i))))) (Eq.{max (succ u1) (succ u2)} (forall (i : ι), Filter.{u2} (α i)) f (Bot.bot.{max u1 u2} (forall (i : ι), Filter.{u2} (α i)) (Pi.hasBot.{u1, u2} ι (fun (i : ι) => Filter.{u2} (α i)) (fun (i : ι) => CompleteLattice.toHasBot.{u2} (Filter.{u2} (α i)) (Filter.completeLattice.{u2} (α i))))))
but is expected to have type
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} [_inst_1 : forall (i : ι), Nonempty.{succ u2} (α i)], Iff (Eq.{max (succ u1) (succ u2)} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => α i) f) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (CompleteLattice.toBot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.instCompleteLatticeFilter.{max u1 u2} (forall (i : ι), α i))))) (Eq.{max (succ u1) (succ u2)} (forall (i : ι), Filter.{u2} (α i)) f (Bot.bot.{max u1 u2} (forall (i : ι), Filter.{u2} (α i)) (Pi.instBotForAll.{u1, u2} ι (fun (i : ι) => Filter.{u2} (α i)) (fun (i : ι) => CompleteLattice.toBot.{u2} (Filter.{u2} (α i)) (Filter.instCompleteLatticeFilter.{u2} (α i))))))
Case conversion may be inaccurate. Consider using '#align filter.Coprod_eq_bot_iff Filter.coprodᵢ_eq_bot_iffₓ'. -/
@[simp]
theorem coprodᵢ_eq_bot_iff [∀ i, Nonempty (α i)] : Filter.coprodᵢ f = ⊥ ↔ f = ⊥ := by
  simpa [funext_iff] using not_congr Coprod_ne_bot_iff
#align filter.Coprod_eq_bot_iff Filter.coprodᵢ_eq_bot_iff

/- warning: filter.Coprod_bot' -> Filter.coprodᵢ_bot' is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => α i) (Bot.bot.{max u1 u2} (forall (i : ι), Filter.{u2} (α i)) (Pi.hasBot.{u1, u2} ι (fun (i : ι) => Filter.{u2} (α i)) (fun (i : ι) => CompleteLattice.toHasBot.{u2} (Filter.{u2} (α i)) (Filter.completeLattice.{u2} (α i)))))) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (CompleteLattice.toHasBot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.completeLattice.{max u1 u2} (forall (i : ι), α i))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}}, Eq.{max (succ u2) (succ u1)} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.coprodᵢ.{u2, u1} ι (fun (i : ι) => α i) (Bot.bot.{max u2 u1} (forall (i : ι), Filter.{u1} (α i)) (Pi.instBotForAll.{u2, u1} ι (fun (i : ι) => Filter.{u1} (α i)) (fun (i : ι) => CompleteLattice.toBot.{u1} (Filter.{u1} (α i)) (Filter.instCompleteLatticeFilter.{u1} (α i)))))) (Bot.bot.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (CompleteLattice.toBot.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.instCompleteLatticeFilter.{max u2 u1} (forall (i : ι), α i))))
Case conversion may be inaccurate. Consider using '#align filter.Coprod_bot' Filter.coprodᵢ_bot'ₓ'. -/
@[simp]
theorem coprodᵢ_bot' : Filter.coprodᵢ (⊥ : ∀ i, Filter (α i)) = ⊥ :=
  coprodᵢ_eq_bot_iff'.2 (Or.inr rfl)
#align filter.Coprod_bot' Filter.coprodᵢ_bot'

/- warning: filter.Coprod_bot -> Filter.coprodᵢ_bot is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.coprodᵢ.{u1, u2} ι (fun (_x : ι) => α _x) (fun (_x : ι) => Bot.bot.{u2} (Filter.{u2} (α _x)) (CompleteLattice.toHasBot.{u2} (Filter.{u2} (α _x)) (Filter.completeLattice.{u2} (α _x))))) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (CompleteLattice.toHasBot.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.completeLattice.{max u1 u2} (forall (i : ι), α i))))
but is expected to have type
  forall {ι : Type.{u2}} {α : ι -> Type.{u1}}, Eq.{max (succ u2) (succ u1)} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.coprodᵢ.{u2, u1} ι (fun (_x : ι) => α _x) (fun (_x : ι) => Bot.bot.{u1} (Filter.{u1} (α _x)) (CompleteLattice.toBot.{u1} (Filter.{u1} (α _x)) (Filter.instCompleteLatticeFilter.{u1} (α _x))))) (Bot.bot.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (CompleteLattice.toBot.{max u2 u1} (Filter.{max u2 u1} (forall (i : ι), α i)) (Filter.instCompleteLatticeFilter.{max u2 u1} (forall (i : ι), α i))))
Case conversion may be inaccurate. Consider using '#align filter.Coprod_bot Filter.coprodᵢ_botₓ'. -/
@[simp]
theorem coprodᵢ_bot : Filter.coprodᵢ (fun _ => ⊥ : ∀ i, Filter (α i)) = ⊥ :=
  coprodᵢ_bot'
#align filter.Coprod_bot Filter.coprodᵢ_bot

#print Filter.NeBot.coprodᵢ /-
theorem NeBot.coprodᵢ [∀ i, Nonempty (α i)] {i : ι} (h : NeBot (f i)) : NeBot (Filter.coprodᵢ f) :=
  coprodᵢ_neBot_iff.2 ⟨i, h⟩
#align filter.ne_bot.Coprod Filter.NeBot.coprodᵢ
-/

#print Filter.coprodᵢ_neBot /-
@[instance]
theorem coprodᵢ_neBot [∀ i, Nonempty (α i)] [Nonempty ι] (f : ∀ i, Filter (α i))
    [H : ∀ i, NeBot (f i)] : NeBot (Filter.coprodᵢ f) :=
  (H (Classical.arbitrary ι)).coprodᵢ
#align filter.Coprod_ne_bot Filter.coprodᵢ_neBot
-/

/- warning: filter.Coprod_mono -> Filter.coprodᵢ_mono is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f₁ : forall (i : ι), Filter.{u2} (α i)} {f₂ : forall (i : ι), Filter.{u2} (α i)}, (forall (i : ι), LE.le.{u2} (Filter.{u2} (α i)) (Preorder.toLE.{u2} (Filter.{u2} (α i)) (PartialOrder.toPreorder.{u2} (Filter.{u2} (α i)) (Filter.partialOrder.{u2} (α i)))) (f₁ i) (f₂ i)) -> (LE.le.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.partialOrder.{max u1 u2} (forall (i : ι), α i)))) (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => α i) f₁) (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => α i) f₂))
but is expected to have type
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f₁ : forall (i : ι), Filter.{u2} (α i)} {f₂ : forall (i : ι), Filter.{u2} (α i)}, (forall (i : ι), LE.le.{u2} (Filter.{u2} (α i)) (Preorder.toLE.{u2} (Filter.{u2} (α i)) (PartialOrder.toPreorder.{u2} (Filter.{u2} (α i)) (Filter.instPartialOrderFilter.{u2} (α i)))) (f₁ i) (f₂ i)) -> (LE.le.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (forall (i : ι), α i)) (Filter.instPartialOrderFilter.{max u1 u2} (forall (i : ι), α i)))) (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => α i) f₁) (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => α i) f₂))
Case conversion may be inaccurate. Consider using '#align filter.Coprod_mono Filter.coprodᵢ_monoₓ'. -/
@[mono]
theorem coprodᵢ_mono (hf : ∀ i, f₁ i ≤ f₂ i) : Filter.coprodᵢ f₁ ≤ Filter.coprodᵢ f₂ :=
  supᵢ_mono fun i => comap_mono (hf i)
#align filter.Coprod_mono Filter.coprodᵢ_mono

variable {β : ι → Type _} {m : ∀ i, α i → β i}

/- warning: filter.map_pi_map_Coprod_le -> Filter.map_pi_map_coprodᵢ_le is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {α : ι -> Type.{u2}} {f : forall (i : ι), Filter.{u2} (α i)} {β : ι -> Type.{u3}} {m : forall (i : ι), (α i) -> (β i)}, LE.le.{max u1 u3} (Filter.{max u1 u3} (forall (i : ι), β i)) (Preorder.toLE.{max u1 u3} (Filter.{max u1 u3} (forall (i : ι), β i)) (PartialOrder.toPreorder.{max u1 u3} (Filter.{max u1 u3} (forall (i : ι), β i)) (Filter.partialOrder.{max u1 u3} (forall (i : ι), β i)))) (Filter.map.{max u1 u2, max u1 u3} (forall (i : ι), α i) (forall (i : ι), β i) (fun (k : forall (i : ι), α i) (i : ι) => m i (k i)) (Filter.coprodᵢ.{u1, u2} ι (fun (i : ι) => α i) f)) (Filter.coprodᵢ.{u1, u3} ι (fun (i : ι) => β i) (fun (i : ι) => Filter.map.{u2, u3} (α i) (β i) (m i) (f i)))
but is expected to have type
  forall {ι : Type.{u3}} {α : ι -> Type.{u1}} {f : forall (i : ι), Filter.{u1} (α i)} {β : ι -> Type.{u2}} {m : forall (i : ι), (α i) -> (β i)}, LE.le.{max u3 u2} (Filter.{max u3 u2} (forall (i : ι), β i)) (Preorder.toLE.{max u3 u2} (Filter.{max u3 u2} (forall (i : ι), β i)) (PartialOrder.toPreorder.{max u3 u2} (Filter.{max u3 u2} (forall (i : ι), β i)) (Filter.instPartialOrderFilter.{max u3 u2} (forall (i : ι), β i)))) (Filter.map.{max u3 u1, max u3 u2} (forall (i : ι), α i) (forall (i : ι), β i) (fun (k : forall (i : ι), α i) (i : ι) => m i (k i)) (Filter.coprodᵢ.{u3, u1} ι (fun (i : ι) => α i) f)) (Filter.coprodᵢ.{u3, u2} ι (fun (i : ι) => β i) (fun (i : ι) => Filter.map.{u1, u2} (α i) (β i) (m i) (f i)))
Case conversion may be inaccurate. Consider using '#align filter.map_pi_map_Coprod_le Filter.map_pi_map_coprodᵢ_leₓ'. -/
theorem map_pi_map_coprodᵢ_le :
    map (fun k : ∀ i, α i => fun i => m i (k i)) (Filter.coprodᵢ f) ≤
      Filter.coprodᵢ fun i => map (m i) (f i) :=
  by
  simp only [le_def, mem_map, mem_Coprod_iff]
  intro s h i
  obtain ⟨t, H, hH⟩ := h i
  exact ⟨{ x : α i | m i x ∈ t }, H, fun x hx => hH hx⟩
#align filter.map_pi_map_Coprod_le Filter.map_pi_map_coprodᵢ_le

#print Filter.Tendsto.pi_map_coprodᵢ /-
theorem Tendsto.pi_map_coprodᵢ {g : ∀ i, Filter (β i)} (h : ∀ i, Tendsto (m i) (f i) (g i)) :
    Tendsto (fun k : ∀ i, α i => fun i => m i (k i)) (Filter.coprodᵢ f) (Filter.coprodᵢ g) :=
  map_pi_map_coprodᵢ_le.trans (coprodᵢ_mono h)
#align filter.tendsto.pi_map_Coprod Filter.Tendsto.pi_map_coprodᵢ
-/

end Coprod

end Filter