/-
Copyright (c) 2022 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johanes Hölzl, Patrick Massot, Yury Kudryashov, Kevin Wilson, Heather Macbeth

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

/-!
# Product and coproduct filters

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

In this file we define `filter.prod f g` (notation: `f ×ᶠ g`) and `filter.coprod f g`. The product
of two filters is the largest filter `l` such that `filter.tendsto prod.fst l f` and
`filter.tendsto prod.snd l g`.

## Implementation details

The product filter cannot be defined using the monad structure on filters. For example:

```lean
F := do {x ← seq, y ← top, return (x, y)}
G := do {y ← top, x ← seq, return (x, y)}
```
hence:
```lean
s ∈ F  ↔  ∃ n, [n..∞] × univ ⊆ s
s ∈ G  ↔  ∀ i:ℕ, ∃ n, [n..∞] × {i} ⊆ s
```
Now `⋃ i, [i..∞] × {i}` is in `G` but not in `F`.
As product filter we want to have `F` as result.

## Notations

* `f ×ᶠ g` : `filter.prod f g`, localized in `filter`.

-/

open Set

open Filter

namespace Filter

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

section Prod

variable {s : Set α} {t : Set β} {f : Filter α} {g : Filter β}

#print Filter.prod /-
/-- Product of filters. This is the filter generated by cartesian products
of elements of the component filters. -/
protected def prod (f : Filter α) (g : Filter β) : Filter (α × β) :=
  f.comap Prod.fst ⊓ g.comap Prod.snd
#align filter.prod Filter.prod
-/

-- mathport name: filter.prod
scoped infixl:60 " ×ᶠ " => Filter.prod

/- warning: filter.prod_mem_prod -> Filter.prod_mem_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {s : Set.{u1} α} {t : Set.{u2} β} {f : Filter.{u1} α} {g : Filter.{u2} β}, (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s f) -> (Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) t g) -> (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.hasMem.{max u1 u2} (Prod.{u1, u2} α β)) (Set.prod.{u1, u2} α β s t) (Filter.prod.{u1, u2} α β f g))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {s : Set.{u2} α} {t : Set.{u1} β} {f : Filter.{u2} α} {g : Filter.{u1} β}, (Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) s f) -> (Membership.mem.{u1, u1} (Set.{u1} β) (Filter.{u1} β) (instMembershipSetFilter.{u1} β) t g) -> (Membership.mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (instMembershipSetFilter.{max u2 u1} (Prod.{u2, u1} α β)) (Set.prod.{u2, u1} α β s t) (Filter.prod.{u2, u1} α β f g))
Case conversion may be inaccurate. Consider using '#align filter.prod_mem_prod Filter.prod_mem_prodₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem prod_mem_prod {s : Set α} {t : Set β} {f : Filter α} {g : Filter β} (hs : s ∈ f)
    (ht : t ∈ g) : s ×ˢ t ∈ f ×ᶠ g :=
  inter_mem_inf (preimage_mem_comap hs) (preimage_mem_comap ht)
#align filter.prod_mem_prod Filter.prod_mem_prod

/- warning: filter.mem_prod_iff -> Filter.mem_prod_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {s : Set.{max u1 u2} (Prod.{u1, u2} α β)} {f : Filter.{u1} α} {g : Filter.{u2} β}, Iff (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.hasMem.{max u1 u2} (Prod.{u1, u2} α β)) s (Filter.prod.{u1, u2} α β f g)) (Exists.{succ u1} (Set.{u1} α) (fun (t₁ : Set.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t₁ f) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t₁ f) => Exists.{succ u2} (Set.{u2} β) (fun (t₂ : Set.{u2} β) => Exists.{0} (Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) t₂ g) (fun (H : Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) t₂ g) => HasSubset.Subset.{max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Set.hasSubset.{max u1 u2} (Prod.{u1, u2} α β)) (Set.prod.{u1, u2} α β t₁ t₂) s)))))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} {s : Set.{max u2 u1} (Prod.{u1, u2} α β)} {f : Filter.{u1} α} {g : Filter.{u2} β}, Iff (Membership.mem.{max u1 u2, max u2 u1} (Set.{max u2 u1} (Prod.{u1, u2} α β)) (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (instMembershipSetFilter.{max u1 u2} (Prod.{u1, u2} α β)) s (Filter.prod.{u1, u2} α β f g)) (Exists.{succ u1} (Set.{u1} α) (fun (t₁ : Set.{u1} α) => And (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) t₁ f) (Exists.{succ u2} (Set.{u2} β) (fun (t₂ : Set.{u2} β) => And (Membership.mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (instMembershipSetFilter.{u2} β) t₂ g) (HasSubset.Subset.{max u2 u1} (Set.{max u2 u1} (Prod.{u1, u2} α β)) (Set.instHasSubsetSet.{max u1 u2} (Prod.{u1, u2} α β)) (Set.prod.{u1, u2} α β t₁ t₂) s)))))
Case conversion may be inaccurate. Consider using '#align filter.mem_prod_iff Filter.mem_prod_iffₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem mem_prod_iff {s : Set (α × β)} {f : Filter α} {g : Filter β} :
    s ∈ f ×ᶠ g ↔ ∃ t₁ ∈ f, ∃ t₂ ∈ g, t₁ ×ˢ t₂ ⊆ s :=
  by
  simp only [Filter.prod]
  constructor
  · rintro ⟨t₁, ⟨s₁, hs₁, hts₁⟩, t₂, ⟨s₂, hs₂, hts₂⟩, rfl⟩
    exact ⟨s₁, hs₁, s₂, hs₂, fun p ⟨h, h'⟩ => ⟨hts₁ h, hts₂ h'⟩⟩
  · rintro ⟨t₁, ht₁, t₂, ht₂, h⟩
    exact mem_inf_of_inter (preimage_mem_comap ht₁) (preimage_mem_comap ht₂) h
#align filter.mem_prod_iff Filter.mem_prod_iff

/- warning: filter.prod_mem_prod_iff -> Filter.prod_mem_prod_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {s : Set.{u1} α} {t : Set.{u2} β} {f : Filter.{u1} α} {g : Filter.{u2} β} [_inst_1 : Filter.NeBot.{u1} α f] [_inst_2 : Filter.NeBot.{u2} β g], Iff (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.hasMem.{max u1 u2} (Prod.{u1, u2} α β)) (Set.prod.{u1, u2} α β s t) (Filter.prod.{u1, u2} α β f g)) (And (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s f) (Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) t g))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {s : Set.{u2} α} {t : Set.{u1} β} {f : Filter.{u2} α} {g : Filter.{u1} β} [_inst_1 : Filter.NeBot.{u2} α f] [_inst_2 : Filter.NeBot.{u1} β g], Iff (Membership.mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (instMembershipSetFilter.{max u2 u1} (Prod.{u2, u1} α β)) (Set.prod.{u2, u1} α β s t) (Filter.prod.{u2, u1} α β f g)) (And (Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) s f) (Membership.mem.{u1, u1} (Set.{u1} β) (Filter.{u1} β) (instMembershipSetFilter.{u1} β) t g))
Case conversion may be inaccurate. Consider using '#align filter.prod_mem_prod_iff Filter.prod_mem_prod_iffₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem prod_mem_prod_iff {s : Set α} {t : Set β} {f : Filter α} {g : Filter β} [f.ne_bot]
    [g.ne_bot] : s ×ˢ t ∈ f ×ᶠ g ↔ s ∈ f ∧ t ∈ g :=
  ⟨fun h =>
    let ⟨s', hs', t', ht', H⟩ := mem_prod_iff.1 h
    (prod_subset_prod_iff.1 H).elim
      (fun ⟨hs's, ht't⟩ => ⟨mem_of_superset hs' hs's, mem_of_superset ht' ht't⟩) fun h =>
      h.elim (fun hs'e => absurd hs'e (nonempty_of_mem hs').ne_empty) fun ht'e =>
        absurd ht'e (nonempty_of_mem ht').ne_empty,
    fun h => prod_mem_prod h.1 h.2⟩
#align filter.prod_mem_prod_iff Filter.prod_mem_prod_iff

/- warning: filter.mem_prod_principal -> Filter.mem_prod_principal is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {s : Set.{max u1 u2} (Prod.{u1, u2} α β)} {t : Set.{u2} β}, Iff (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.hasMem.{max u1 u2} (Prod.{u1, u2} α β)) s (Filter.prod.{u1, u2} α β f (Filter.principal.{u2} β t))) (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) (setOf.{u1} α (fun (a : α) => forall (b : β), (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) b t) -> (Membership.Mem.{max u1 u2, max u1 u2} (Prod.{u1, u2} α β) (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Set.hasMem.{max u1 u2} (Prod.{u1, u2} α β)) (Prod.mk.{u1, u2} α β a b) s))) f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {s : Set.{max u1 u2} (Prod.{u2, u1} α β)} {t : Set.{u1} β}, Iff (Membership.mem.{max u2 u1, max u1 u2} (Set.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (instMembershipSetFilter.{max u2 u1} (Prod.{u2, u1} α β)) s (Filter.prod.{u2, u1} α β f (Filter.principal.{u1} β t))) (Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) (setOf.{u2} α (fun (a : α) => forall (b : β), (Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) b t) -> (Membership.mem.{max u1 u2, max u2 u1} (Prod.{u2, u1} α β) (Set.{max u1 u2} (Prod.{u2, u1} α β)) (Set.instMembershipSet.{max u1 u2} (Prod.{u2, u1} α β)) (Prod.mk.{u2, u1} α β a b) s))) f)
Case conversion may be inaccurate. Consider using '#align filter.mem_prod_principal Filter.mem_prod_principalₓ'. -/
theorem mem_prod_principal {f : Filter α} {s : Set (α × β)} {t : Set β} :
    s ∈ f ×ᶠ 𝓟 t ↔ { a | ∀ b ∈ t, (a, b) ∈ s } ∈ f :=
  by
  rw [← @exists_mem_subset_iff _ f, mem_prod_iff]
  refine' exists₂_congr fun u u_in => ⟨_, fun h => ⟨t, mem_principal_self t, _⟩⟩
  · rintro ⟨v, v_in, hv⟩ a a_in b b_in
    exact hv (mk_mem_prod a_in <| v_in b_in)
  · rintro ⟨x, y⟩ ⟨hx, hy⟩
    exact h hx y hy
#align filter.mem_prod_principal Filter.mem_prod_principal

/- warning: filter.mem_prod_top -> Filter.mem_prod_top is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {s : Set.{max u1 u2} (Prod.{u1, u2} α β)}, Iff (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.hasMem.{max u1 u2} (Prod.{u1, u2} α β)) s (Filter.prod.{u1, u2} α β f (Top.top.{u2} (Filter.{u2} β) (Filter.hasTop.{u2} β)))) (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) (setOf.{u1} α (fun (a : α) => forall (b : β), Membership.Mem.{max u1 u2, max u1 u2} (Prod.{u1, u2} α β) (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Set.hasMem.{max u1 u2} (Prod.{u1, u2} α β)) (Prod.mk.{u1, u2} α β a b) s)) f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {s : Set.{max u1 u2} (Prod.{u2, u1} α β)}, Iff (Membership.mem.{max u2 u1, max u1 u2} (Set.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (instMembershipSetFilter.{max u2 u1} (Prod.{u2, u1} α β)) s (Filter.prod.{u2, u1} α β f (Top.top.{u1} (Filter.{u1} β) (Filter.instTopFilter.{u1} β)))) (Membership.mem.{u2, u2} (Set.{u2} α) (Filter.{u2} α) (instMembershipSetFilter.{u2} α) (setOf.{u2} α (fun (a : α) => forall (b : β), Membership.mem.{max u1 u2, max u2 u1} (Prod.{u2, u1} α β) (Set.{max u1 u2} (Prod.{u2, u1} α β)) (Set.instMembershipSet.{max u2 u1} (Prod.{u2, u1} α β)) (Prod.mk.{u2, u1} α β a b) s)) f)
Case conversion may be inaccurate. Consider using '#align filter.mem_prod_top Filter.mem_prod_topₓ'. -/
theorem mem_prod_top {f : Filter α} {s : Set (α × β)} :
    s ∈ f ×ᶠ (⊤ : Filter β) ↔ { a | ∀ b, (a, b) ∈ s } ∈ f :=
  by
  rw [← principal_univ, mem_prod_principal]
  simp only [mem_univ, forall_true_left]
#align filter.mem_prod_top Filter.mem_prod_top

/- warning: filter.eventually_prod_principal_iff -> Filter.eventually_prod_principal_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {p : (Prod.{u1, u2} α β) -> Prop} {s : Set.{u2} β}, Iff (Filter.Eventually.{max u1 u2} (Prod.{u1, u2} α β) (fun (x : Prod.{u1, u2} α β) => p x) (Filter.prod.{u1, u2} α β f (Filter.principal.{u2} β s))) (Filter.Eventually.{u1} α (fun (x : α) => forall (y : β), (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) y s) -> (p (Prod.mk.{u1, u2} α β x y))) f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {p : (Prod.{u2, u1} α β) -> Prop} {s : Set.{u1} β}, Iff (Filter.Eventually.{max u2 u1} (Prod.{u2, u1} α β) (fun (x : Prod.{u2, u1} α β) => p x) (Filter.prod.{u2, u1} α β f (Filter.principal.{u1} β s))) (Filter.Eventually.{u2} α (fun (x : α) => forall (y : β), (Membership.mem.{u1, u1} β (Set.{u1} β) (Set.instMembershipSet.{u1} β) y s) -> (p (Prod.mk.{u2, u1} α β x y))) f)
Case conversion may be inaccurate. Consider using '#align filter.eventually_prod_principal_iff Filter.eventually_prod_principal_iffₓ'. -/
theorem eventually_prod_principal_iff {p : α × β → Prop} {s : Set β} :
    (∀ᶠ x : α × β in f ×ᶠ 𝓟 s, p x) ↔ ∀ᶠ x : α in f, ∀ y : β, y ∈ s → p (x, y) :=
  by
  rw [eventually_iff, eventually_iff, mem_prod_principal]
  simp only [mem_set_of_eq]
#align filter.eventually_prod_principal_iff Filter.eventually_prod_principal_iff

/- warning: filter.comap_prod -> Filter.comap_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : α -> (Prod.{u2, u3} β γ)) (b : Filter.{u2} β) (c : Filter.{u3} γ), Eq.{succ u1} (Filter.{u1} α) (Filter.comap.{u1, max u2 u3} α (Prod.{u2, u3} β γ) f (Filter.prod.{u2, u3} β γ b c)) (Inf.inf.{u1} (Filter.{u1} α) (Filter.hasInf.{u1} α) (Filter.comap.{u1, u2} α β (Function.comp.{succ u1, max (succ u2) (succ u3), succ u2} α (Prod.{u2, u3} β γ) β (Prod.fst.{u2, u3} β γ) f) b) (Filter.comap.{u1, u3} α γ (Function.comp.{succ u1, max (succ u2) (succ u3), succ u3} α (Prod.{u2, u3} β γ) γ (Prod.snd.{u2, u3} β γ) f) c))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} (f : α -> (Prod.{u3, u2} β γ)) (b : Filter.{u3} β) (c : Filter.{u2} γ), Eq.{succ u1} (Filter.{u1} α) (Filter.comap.{u1, max u3 u2} α (Prod.{u3, u2} β γ) f (Filter.prod.{u3, u2} β γ b c)) (Inf.inf.{u1} (Filter.{u1} α) (Filter.instInfFilter.{u1} α) (Filter.comap.{u1, u3} α β (Function.comp.{succ u1, max (succ u2) (succ u3), succ u3} α (Prod.{u3, u2} β γ) β (Prod.fst.{u3, u2} β γ) f) b) (Filter.comap.{u1, u2} α γ (Function.comp.{succ u1, max (succ u2) (succ u3), succ u2} α (Prod.{u3, u2} β γ) γ (Prod.snd.{u3, u2} β γ) f) c))
Case conversion may be inaccurate. Consider using '#align filter.comap_prod Filter.comap_prodₓ'. -/
theorem comap_prod (f : α → β × γ) (b : Filter β) (c : Filter γ) :
    comap f (b ×ᶠ c) = comap (Prod.fst ∘ f) b ⊓ comap (Prod.snd ∘ f) c := by
  erw [comap_inf, Filter.comap_comap, Filter.comap_comap]
#align filter.comap_prod Filter.comap_prod

/- warning: filter.prod_top -> Filter.prod_top is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β f (Top.top.{u2} (Filter.{u2} β) (Filter.hasTop.{u2} β))) (Filter.comap.{max u1 u2, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β f (Top.top.{u1} (Filter.{u1} β) (Filter.instTopFilter.{u1} β))) (Filter.comap.{max u1 u2, u2} (Prod.{u2, u1} α β) α (Prod.fst.{u2, u1} α β) f)
Case conversion may be inaccurate. Consider using '#align filter.prod_top Filter.prod_topₓ'. -/
theorem prod_top {f : Filter α} : f ×ᶠ (⊤ : Filter β) = f.comap Prod.fst := by
  rw [Filter.prod, comap_top, inf_top_eq]
#align filter.prod_top Filter.prod_top

/- warning: filter.sup_prod -> Filter.sup_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f₁ : Filter.{u1} α) (f₂ : Filter.{u1} α) (g : Filter.{u2} β), Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β (Sup.sup.{u1} (Filter.{u1} α) (SemilatticeSup.toHasSup.{u1} (Filter.{u1} α) (Lattice.toSemilatticeSup.{u1} (Filter.{u1} α) (ConditionallyCompleteLattice.toLattice.{u1} (Filter.{u1} α) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))))) f₁ f₂) g) (Sup.sup.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (SemilatticeSup.toHasSup.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Lattice.toSemilatticeSup.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (ConditionallyCompleteLattice.toLattice.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (CompleteLattice.toConditionallyCompleteLattice.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.completeLattice.{max u1 u2} (Prod.{u1, u2} α β)))))) (Filter.prod.{u1, u2} α β f₁ g) (Filter.prod.{u1, u2} α β f₂ g))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (f₁ : Filter.{u2} α) (f₂ : Filter.{u2} α) (g : Filter.{u1} β), Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β (Sup.sup.{u2} (Filter.{u2} α) (SemilatticeSup.toSup.{u2} (Filter.{u2} α) (Lattice.toSemilatticeSup.{u2} (Filter.{u2} α) (CompleteLattice.toLattice.{u2} (Filter.{u2} α) (Filter.instCompleteLatticeFilter.{u2} α)))) f₁ f₂) g) (Sup.sup.{max u1 u2} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (SemilatticeSup.toSup.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Lattice.toSemilatticeSup.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (CompleteLattice.toLattice.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.instCompleteLatticeFilter.{max u2 u1} (Prod.{u2, u1} α β))))) (Filter.prod.{u2, u1} α β f₁ g) (Filter.prod.{u2, u1} α β f₂ g))
Case conversion may be inaccurate. Consider using '#align filter.sup_prod Filter.sup_prodₓ'. -/
theorem sup_prod (f₁ f₂ : Filter α) (g : Filter β) : f₁ ⊔ f₂ ×ᶠ g = (f₁ ×ᶠ g) ⊔ (f₂ ×ᶠ g) := by
  rw [Filter.prod, comap_sup, inf_sup_right, ← Filter.prod, ← Filter.prod]
#align filter.sup_prod Filter.sup_prod

/- warning: filter.prod_sup -> Filter.prod_sup is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f : Filter.{u1} α) (g₁ : Filter.{u2} β) (g₂ : Filter.{u2} β), Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β f (Sup.sup.{u2} (Filter.{u2} β) (SemilatticeSup.toHasSup.{u2} (Filter.{u2} β) (Lattice.toSemilatticeSup.{u2} (Filter.{u2} β) (ConditionallyCompleteLattice.toLattice.{u2} (Filter.{u2} β) (CompleteLattice.toConditionallyCompleteLattice.{u2} (Filter.{u2} β) (Filter.completeLattice.{u2} β))))) g₁ g₂)) (Sup.sup.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (SemilatticeSup.toHasSup.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Lattice.toSemilatticeSup.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (ConditionallyCompleteLattice.toLattice.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (CompleteLattice.toConditionallyCompleteLattice.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.completeLattice.{max u1 u2} (Prod.{u1, u2} α β)))))) (Filter.prod.{u1, u2} α β f g₁) (Filter.prod.{u1, u2} α β f g₂))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (f : Filter.{u2} α) (g₁ : Filter.{u1} β) (g₂ : Filter.{u1} β), Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β f (Sup.sup.{u1} (Filter.{u1} β) (SemilatticeSup.toSup.{u1} (Filter.{u1} β) (Lattice.toSemilatticeSup.{u1} (Filter.{u1} β) (CompleteLattice.toLattice.{u1} (Filter.{u1} β) (Filter.instCompleteLatticeFilter.{u1} β)))) g₁ g₂)) (Sup.sup.{max u1 u2} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (SemilatticeSup.toSup.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Lattice.toSemilatticeSup.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (CompleteLattice.toLattice.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.instCompleteLatticeFilter.{max u2 u1} (Prod.{u2, u1} α β))))) (Filter.prod.{u2, u1} α β f g₁) (Filter.prod.{u2, u1} α β f g₂))
Case conversion may be inaccurate. Consider using '#align filter.prod_sup Filter.prod_supₓ'. -/
theorem prod_sup (f : Filter α) (g₁ g₂ : Filter β) : f ×ᶠ g₁ ⊔ g₂ = (f ×ᶠ g₁) ⊔ (f ×ᶠ g₂) := by
  rw [Filter.prod, comap_sup, inf_sup_left, ← Filter.prod, ← Filter.prod]
#align filter.prod_sup Filter.prod_sup

/- warning: filter.eventually_prod_iff -> Filter.eventually_prod_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {p : (Prod.{u1, u2} α β) -> Prop} {f : Filter.{u1} α} {g : Filter.{u2} β}, Iff (Filter.Eventually.{max u1 u2} (Prod.{u1, u2} α β) (fun (x : Prod.{u1, u2} α β) => p x) (Filter.prod.{u1, u2} α β f g)) (Exists.{succ u1} (α -> Prop) (fun (pa : α -> Prop) => Exists.{0} (Filter.Eventually.{u1} α (fun (x : α) => pa x) f) (fun (ha : Filter.Eventually.{u1} α (fun (x : α) => pa x) f) => Exists.{succ u2} (β -> Prop) (fun (pb : β -> Prop) => Exists.{0} (Filter.Eventually.{u2} β (fun (y : β) => pb y) g) (fun (hb : Filter.Eventually.{u2} β (fun (y : β) => pb y) g) => forall {x : α}, (pa x) -> (forall {y : β}, (pb y) -> (p (Prod.mk.{u1, u2} α β x y))))))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {p : (Prod.{u2, u1} α β) -> Prop} {f : Filter.{u2} α} {g : Filter.{u1} β}, Iff (Filter.Eventually.{max u2 u1} (Prod.{u2, u1} α β) (fun (x : Prod.{u2, u1} α β) => p x) (Filter.prod.{u2, u1} α β f g)) (Exists.{succ u2} (α -> Prop) (fun (pa : α -> Prop) => And (Filter.Eventually.{u2} α (fun (x : α) => pa x) f) (Exists.{succ u1} (β -> Prop) (fun (pb : β -> Prop) => And (Filter.Eventually.{u1} β (fun (y : β) => pb y) g) (forall {x : α}, (pa x) -> (forall {y : β}, (pb y) -> (p (Prod.mk.{u2, u1} α β x y))))))))
Case conversion may be inaccurate. Consider using '#align filter.eventually_prod_iff Filter.eventually_prod_iffₓ'. -/
theorem eventually_prod_iff {p : α × β → Prop} {f : Filter α} {g : Filter β} :
    (∀ᶠ x in f ×ᶠ g, p x) ↔
      ∃ (pa : α → Prop)(ha : ∀ᶠ x in f, pa x)(pb : β → Prop)(hb : ∀ᶠ y in g, pb y),
        ∀ {x}, pa x → ∀ {y}, pb y → p (x, y) :=
  by simpa only [Set.prod_subset_iff] using @mem_prod_iff α β p f g
#align filter.eventually_prod_iff Filter.eventually_prod_iff

/- warning: filter.tendsto_fst -> Filter.tendsto_fst is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} β}, Filter.Tendsto.{max u1 u2, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) (Filter.prod.{u1, u2} α β f g) f
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} β}, Filter.Tendsto.{max u1 u2, u2} (Prod.{u2, u1} α β) α (Prod.fst.{u2, u1} α β) (Filter.prod.{u2, u1} α β f g) f
Case conversion may be inaccurate. Consider using '#align filter.tendsto_fst Filter.tendsto_fstₓ'. -/
theorem tendsto_fst {f : Filter α} {g : Filter β} : Tendsto Prod.fst (f ×ᶠ g) f :=
  tendsto_inf_left tendsto_comap
#align filter.tendsto_fst Filter.tendsto_fst

/- warning: filter.tendsto_snd -> Filter.tendsto_snd is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} β}, Filter.Tendsto.{max u1 u2, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) (Filter.prod.{u1, u2} α β f g) g
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} β}, Filter.Tendsto.{max u1 u2, u1} (Prod.{u2, u1} α β) β (Prod.snd.{u2, u1} α β) (Filter.prod.{u2, u1} α β f g) g
Case conversion may be inaccurate. Consider using '#align filter.tendsto_snd Filter.tendsto_sndₓ'. -/
theorem tendsto_snd {f : Filter α} {g : Filter β} : Tendsto Prod.snd (f ×ᶠ g) g :=
  tendsto_inf_right tendsto_comap
#align filter.tendsto_snd Filter.tendsto_snd

/- warning: filter.tendsto.prod_mk -> Filter.Tendsto.prod_mk is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {f : Filter.{u1} α} {g : Filter.{u2} β} {h : Filter.{u3} γ} {m₁ : α -> β} {m₂ : α -> γ}, (Filter.Tendsto.{u1, u2} α β m₁ f g) -> (Filter.Tendsto.{u1, u3} α γ m₂ f h) -> (Filter.Tendsto.{u1, max u2 u3} α (Prod.{u2, u3} β γ) (fun (x : α) => Prod.mk.{u2, u3} β γ (m₁ x) (m₂ x)) f (Filter.prod.{u2, u3} β γ g h))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} {f : Filter.{u3} α} {g : Filter.{u2} β} {h : Filter.{u1} γ} {m₁ : α -> β} {m₂ : α -> γ}, (Filter.Tendsto.{u3, u2} α β m₁ f g) -> (Filter.Tendsto.{u3, u1} α γ m₂ f h) -> (Filter.Tendsto.{u3, max u1 u2} α (Prod.{u2, u1} β γ) (fun (x : α) => Prod.mk.{u2, u1} β γ (m₁ x) (m₂ x)) f (Filter.prod.{u2, u1} β γ g h))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.prod_mk Filter.Tendsto.prod_mkₓ'. -/
theorem Tendsto.prod_mk {f : Filter α} {g : Filter β} {h : Filter γ} {m₁ : α → β} {m₂ : α → γ}
    (h₁ : Tendsto m₁ f g) (h₂ : Tendsto m₂ f h) : Tendsto (fun x => (m₁ x, m₂ x)) f (g ×ᶠ h) :=
  tendsto_inf.2 ⟨tendsto_comap_iff.2 h₁, tendsto_comap_iff.2 h₂⟩
#align filter.tendsto.prod_mk Filter.Tendsto.prod_mk

/- warning: filter.tendsto_prod_swap -> Filter.tendsto_prod_swap is a dubious translation:
lean 3 declaration is
  forall {α1 : Type.{u1}} {α2 : Type.{u2}} {a1 : Filter.{u1} α1} {a2 : Filter.{u2} α2}, Filter.Tendsto.{max u1 u2, max u2 u1} (Prod.{u1, u2} α1 α2) (Prod.{u2, u1} α2 α1) (Prod.swap.{u1, u2} α1 α2) (Filter.prod.{u1, u2} α1 α2 a1 a2) (Filter.prod.{u2, u1} α2 α1 a2 a1)
but is expected to have type
  forall {α1 : Type.{u2}} {α2 : Type.{u1}} {a1 : Filter.{u2} α1} {a2 : Filter.{u1} α2}, Filter.Tendsto.{max u2 u1, max u2 u1} (Prod.{u2, u1} α1 α2) (Prod.{u1, u2} α2 α1) (Prod.swap.{u2, u1} α1 α2) (Filter.prod.{u2, u1} α1 α2 a1 a2) (Filter.prod.{u1, u2} α2 α1 a2 a1)
Case conversion may be inaccurate. Consider using '#align filter.tendsto_prod_swap Filter.tendsto_prod_swapₓ'. -/
theorem tendsto_prod_swap {α1 α2 : Type _} {a1 : Filter α1} {a2 : Filter α2} :
    Tendsto (Prod.swap : α1 × α2 → α2 × α1) (a1 ×ᶠ a2) (a2 ×ᶠ a1) :=
  tendsto_snd.prod_mk tendsto_fst
#align filter.tendsto_prod_swap Filter.tendsto_prod_swap

/- warning: filter.eventually.prod_inl -> Filter.Eventually.prod_inl is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {la : Filter.{u1} α} {p : α -> Prop}, (Filter.Eventually.{u1} α (fun (x : α) => p x) la) -> (forall (lb : Filter.{u2} β), Filter.Eventually.{max u1 u2} (Prod.{u1, u2} α β) (fun (x : Prod.{u1, u2} α β) => p (Prod.fst.{u1, u2} α β x)) (Filter.prod.{u1, u2} α β la lb))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {la : Filter.{u2} α} {p : α -> Prop}, (Filter.Eventually.{u2} α (fun (x : α) => p x) la) -> (forall (lb : Filter.{u1} β), Filter.Eventually.{max u2 u1} (Prod.{u2, u1} α β) (fun (x : Prod.{u2, u1} α β) => p (Prod.fst.{u2, u1} α β x)) (Filter.prod.{u2, u1} α β la lb))
Case conversion may be inaccurate. Consider using '#align filter.eventually.prod_inl Filter.Eventually.prod_inlₓ'. -/
theorem Eventually.prod_inl {la : Filter α} {p : α → Prop} (h : ∀ᶠ x in la, p x) (lb : Filter β) :
    ∀ᶠ x in la ×ᶠ lb, p (x : α × β).1 :=
  tendsto_fst.Eventually h
#align filter.eventually.prod_inl Filter.Eventually.prod_inl

#print Filter.Eventually.prod_inr /-
theorem Eventually.prod_inr {lb : Filter β} {p : β → Prop} (h : ∀ᶠ x in lb, p x) (la : Filter α) :
    ∀ᶠ x in la ×ᶠ lb, p (x : α × β).2 :=
  tendsto_snd.Eventually h
#align filter.eventually.prod_inr Filter.Eventually.prod_inr
-/

/- warning: filter.eventually.prod_mk -> Filter.Eventually.prod_mk is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {la : Filter.{u1} α} {pa : α -> Prop}, (Filter.Eventually.{u1} α (fun (x : α) => pa x) la) -> (forall {lb : Filter.{u2} β} {pb : β -> Prop}, (Filter.Eventually.{u2} β (fun (y : β) => pb y) lb) -> (Filter.Eventually.{max u1 u2} (Prod.{u1, u2} α β) (fun (p : Prod.{u1, u2} α β) => And (pa (Prod.fst.{u1, u2} α β p)) (pb (Prod.snd.{u1, u2} α β p))) (Filter.prod.{u1, u2} α β la lb)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {la : Filter.{u2} α} {pa : α -> Prop}, (Filter.Eventually.{u2} α (fun (x : α) => pa x) la) -> (forall {lb : Filter.{u1} β} {pb : β -> Prop}, (Filter.Eventually.{u1} β (fun (y : β) => pb y) lb) -> (Filter.Eventually.{max u2 u1} (Prod.{u2, u1} α β) (fun (p : Prod.{u2, u1} α β) => And (pa (Prod.fst.{u2, u1} α β p)) (pb (Prod.snd.{u2, u1} α β p))) (Filter.prod.{u2, u1} α β la lb)))
Case conversion may be inaccurate. Consider using '#align filter.eventually.prod_mk Filter.Eventually.prod_mkₓ'. -/
theorem Eventually.prod_mk {la : Filter α} {pa : α → Prop} (ha : ∀ᶠ x in la, pa x) {lb : Filter β}
    {pb : β → Prop} (hb : ∀ᶠ y in lb, pb y) : ∀ᶠ p in la ×ᶠ lb, pa (p : α × β).1 ∧ pb p.2 :=
  (ha.prod_inl lb).And (hb.prod_inr la)
#align filter.eventually.prod_mk Filter.Eventually.prod_mk

/- warning: filter.eventually_eq.prod_map -> Filter.EventuallyEq.prod_map is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} {la : Filter.{u1} α} {fa : α -> γ} {ga : α -> γ}, (Filter.EventuallyEq.{u1, u3} α γ la fa ga) -> (forall {lb : Filter.{u2} β} {fb : β -> δ} {gb : β -> δ}, (Filter.EventuallyEq.{u2, u4} β δ lb fb gb) -> (Filter.EventuallyEq.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) (Filter.prod.{u1, u2} α β la lb) (Prod.map.{u1, u3, u2, u4} α γ β δ fa fb) (Prod.map.{u1, u3, u2, u4} α γ β δ ga gb)))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u1}} {γ : Type.{u2}} {δ : Type.{u4}} {la : Filter.{u3} α} {fa : α -> γ} {ga : α -> γ}, (Filter.EventuallyEq.{u3, u2} α γ la fa ga) -> (forall {lb : Filter.{u1} β} {fb : β -> δ} {gb : β -> δ}, (Filter.EventuallyEq.{u1, u4} β δ lb fb gb) -> (Filter.EventuallyEq.{max u1 u3, max u4 u2} (Prod.{u3, u1} α β) (Prod.{u2, u4} γ δ) (Filter.prod.{u3, u1} α β la lb) (Prod.map.{u3, u2, u1, u4} α γ β δ fa fb) (Prod.map.{u3, u2, u1, u4} α γ β δ ga gb)))
Case conversion may be inaccurate. Consider using '#align filter.eventually_eq.prod_map Filter.EventuallyEq.prod_mapₓ'. -/
theorem EventuallyEq.prod_map {δ} {la : Filter α} {fa ga : α → γ} (ha : fa =ᶠ[la] ga)
    {lb : Filter β} {fb gb : β → δ} (hb : fb =ᶠ[lb] gb) :
    Prod.map fa fb =ᶠ[la ×ᶠ lb] Prod.map ga gb :=
  (Eventually.prod_mk ha hb).mono fun x h => Prod.ext h.1 h.2
#align filter.eventually_eq.prod_map Filter.EventuallyEq.prod_map

/- warning: filter.eventually_le.prod_map -> Filter.EventuallyLE.prod_map is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} [_inst_1 : LE.{u3} γ] [_inst_2 : LE.{u4} δ] {la : Filter.{u1} α} {fa : α -> γ} {ga : α -> γ}, (Filter.EventuallyLE.{u1, u3} α γ _inst_1 la fa ga) -> (forall {lb : Filter.{u2} β} {fb : β -> δ} {gb : β -> δ}, (Filter.EventuallyLE.{u2, u4} β δ _inst_2 lb fb gb) -> (Filter.EventuallyLE.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) (Prod.hasLe.{u3, u4} γ δ _inst_1 _inst_2) (Filter.prod.{u1, u2} α β la lb) (Prod.map.{u1, u3, u2, u4} α γ β δ fa fb) (Prod.map.{u1, u3, u2, u4} α γ β δ ga gb)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {γ : Type.{u3}} {δ : Type.{u4}} [_inst_1 : LE.{u3} γ] [_inst_2 : LE.{u4} δ] {la : Filter.{u2} α} {fa : α -> γ} {ga : α -> γ}, (Filter.EventuallyLE.{u2, u3} α γ _inst_1 la fa ga) -> (forall {lb : Filter.{u1} β} {fb : β -> δ} {gb : β -> δ}, (Filter.EventuallyLE.{u1, u4} β δ _inst_2 lb fb gb) -> (Filter.EventuallyLE.{max u1 u2, max u4 u3} (Prod.{u2, u1} α β) (Prod.{u3, u4} γ δ) (Prod.instLEProd.{u3, u4} γ δ _inst_1 _inst_2) (Filter.prod.{u2, u1} α β la lb) (Prod.map.{u2, u3, u1, u4} α γ β δ fa fb) (Prod.map.{u2, u3, u1, u4} α γ β δ ga gb)))
Case conversion may be inaccurate. Consider using '#align filter.eventually_le.prod_map Filter.EventuallyLE.prod_mapₓ'. -/
theorem EventuallyLE.prod_map {δ} [LE γ] [LE δ] {la : Filter α} {fa ga : α → γ} (ha : fa ≤ᶠ[la] ga)
    {lb : Filter β} {fb gb : β → δ} (hb : fb ≤ᶠ[lb] gb) :
    Prod.map fa fb ≤ᶠ[la ×ᶠ lb] Prod.map ga gb :=
  Eventually.prod_mk ha hb
#align filter.eventually_le.prod_map Filter.EventuallyLE.prod_map

/- warning: filter.eventually.curry -> Filter.Eventually.curry is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {la : Filter.{u1} α} {lb : Filter.{u2} β} {p : (Prod.{u1, u2} α β) -> Prop}, (Filter.Eventually.{max u1 u2} (Prod.{u1, u2} α β) (fun (x : Prod.{u1, u2} α β) => p x) (Filter.prod.{u1, u2} α β la lb)) -> (Filter.Eventually.{u1} α (fun (x : α) => Filter.Eventually.{u2} β (fun (y : β) => p (Prod.mk.{u1, u2} α β x y)) lb) la)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {la : Filter.{u2} α} {lb : Filter.{u1} β} {p : (Prod.{u2, u1} α β) -> Prop}, (Filter.Eventually.{max u2 u1} (Prod.{u2, u1} α β) (fun (x : Prod.{u2, u1} α β) => p x) (Filter.prod.{u2, u1} α β la lb)) -> (Filter.Eventually.{u2} α (fun (x : α) => Filter.Eventually.{u1} β (fun (y : β) => p (Prod.mk.{u2, u1} α β x y)) lb) la)
Case conversion may be inaccurate. Consider using '#align filter.eventually.curry Filter.Eventually.curryₓ'. -/
theorem Eventually.curry {la : Filter α} {lb : Filter β} {p : α × β → Prop}
    (h : ∀ᶠ x in la ×ᶠ lb, p x) : ∀ᶠ x in la, ∀ᶠ y in lb, p (x, y) :=
  by
  rcases eventually_prod_iff.1 h with ⟨pa, ha, pb, hb, h⟩
  exact ha.mono fun a ha => hb.mono fun b hb => h ha hb
#align filter.eventually.curry Filter.Eventually.curry

#print Filter.Eventually.diag_of_prod /-
/-- A fact that is eventually true about all pairs `l ×ᶠ l` is eventually true about
all diagonal pairs `(i, i)` -/
theorem Eventually.diag_of_prod {f : Filter α} {p : α × α → Prop} (h : ∀ᶠ i in f ×ᶠ f, p i) :
    ∀ᶠ i in f, p (i, i) :=
  by
  obtain ⟨t, ht, s, hs, hst⟩ := eventually_prod_iff.1 h
  apply (ht.and hs).mono fun x hx => hst hx.1 hx.2
#align filter.eventually.diag_of_prod Filter.Eventually.diag_of_prod
-/

/- warning: filter.eventually.diag_of_prod_left -> Filter.Eventually.diag_of_prod_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {γ : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} γ} {p : (Prod.{u1, u2} (Prod.{u1, u1} α α) γ) -> Prop}, (Filter.Eventually.{max u1 u2} (Prod.{u1, u2} (Prod.{u1, u1} α α) γ) (fun (x : Prod.{u1, u2} (Prod.{u1, u1} α α) γ) => p x) (Filter.prod.{u1, u2} (Prod.{u1, u1} α α) γ (Filter.prod.{u1, u1} α α f f) g)) -> (Filter.Eventually.{max u1 u2} (Prod.{u1, u2} α γ) (fun (x : Prod.{u1, u2} α γ) => p (Prod.mk.{u1, u2} (Prod.{u1, u1} α α) γ (Prod.mk.{u1, u1} α α (Prod.fst.{u1, u2} α γ x) (Prod.fst.{u1, u2} α γ x)) (Prod.snd.{u1, u2} α γ x))) (Filter.prod.{u1, u2} α γ f g))
but is expected to have type
  forall {α : Type.{u2}} {γ : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} γ} {p : (Prod.{u2, u1} (Prod.{u2, u2} α α) γ) -> Prop}, (Filter.Eventually.{max u2 u1} (Prod.{u2, u1} (Prod.{u2, u2} α α) γ) (fun (x : Prod.{u2, u1} (Prod.{u2, u2} α α) γ) => p x) (Filter.prod.{u2, u1} (Prod.{u2, u2} α α) γ (Filter.prod.{u2, u2} α α f f) g)) -> (Filter.Eventually.{max u2 u1} (Prod.{u2, u1} α γ) (fun (x : Prod.{u2, u1} α γ) => p (Prod.mk.{u2, u1} (Prod.{u2, u2} α α) γ (Prod.mk.{u2, u2} α α (Prod.fst.{u2, u1} α γ x) (Prod.fst.{u2, u1} α γ x)) (Prod.snd.{u2, u1} α γ x))) (Filter.prod.{u2, u1} α γ f g))
Case conversion may be inaccurate. Consider using '#align filter.eventually.diag_of_prod_left Filter.Eventually.diag_of_prod_leftₓ'. -/
theorem Eventually.diag_of_prod_left {f : Filter α} {g : Filter γ} {p : (α × α) × γ → Prop} :
    (∀ᶠ x in f ×ᶠ f ×ᶠ g, p x) → ∀ᶠ x : α × γ in f ×ᶠ g, p ((x.1, x.1), x.2) :=
  by
  intro h
  obtain ⟨t, ht, s, hs, hst⟩ := eventually_prod_iff.1 h
  refine' (ht.diag_of_prod.prod_mk hs).mono fun x hx => by simp only [hst hx.1 hx.2, Prod.mk.eta]
#align filter.eventually.diag_of_prod_left Filter.Eventually.diag_of_prod_left

/- warning: filter.eventually.diag_of_prod_right -> Filter.Eventually.diag_of_prod_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {γ : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} γ} {p : (Prod.{u1, u2} α (Prod.{u2, u2} γ γ)) -> Prop}, (Filter.Eventually.{max u1 u2} (Prod.{u1, u2} α (Prod.{u2, u2} γ γ)) (fun (x : Prod.{u1, u2} α (Prod.{u2, u2} γ γ)) => p x) (Filter.prod.{u1, u2} α (Prod.{u2, u2} γ γ) f (Filter.prod.{u2, u2} γ γ g g))) -> (Filter.Eventually.{max u1 u2} (Prod.{u1, u2} α γ) (fun (x : Prod.{u1, u2} α γ) => p (Prod.mk.{u1, u2} α (Prod.{u2, u2} γ γ) (Prod.fst.{u1, u2} α γ x) (Prod.mk.{u2, u2} γ γ (Prod.snd.{u1, u2} α γ x) (Prod.snd.{u1, u2} α γ x)))) (Filter.prod.{u1, u2} α γ f g))
but is expected to have type
  forall {α : Type.{u2}} {γ : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} γ} {p : (Prod.{u2, u1} α (Prod.{u1, u1} γ γ)) -> Prop}, (Filter.Eventually.{max u2 u1} (Prod.{u2, u1} α (Prod.{u1, u1} γ γ)) (fun (x : Prod.{u2, u1} α (Prod.{u1, u1} γ γ)) => p x) (Filter.prod.{u2, u1} α (Prod.{u1, u1} γ γ) f (Filter.prod.{u1, u1} γ γ g g))) -> (Filter.Eventually.{max u2 u1} (Prod.{u2, u1} α γ) (fun (x : Prod.{u2, u1} α γ) => p (Prod.mk.{u2, u1} α (Prod.{u1, u1} γ γ) (Prod.fst.{u2, u1} α γ x) (Prod.mk.{u1, u1} γ γ (Prod.snd.{u2, u1} α γ x) (Prod.snd.{u2, u1} α γ x)))) (Filter.prod.{u2, u1} α γ f g))
Case conversion may be inaccurate. Consider using '#align filter.eventually.diag_of_prod_right Filter.Eventually.diag_of_prod_rightₓ'. -/
theorem Eventually.diag_of_prod_right {f : Filter α} {g : Filter γ} {p : α × γ × γ → Prop} :
    (∀ᶠ x in f ×ᶠ (g ×ᶠ g), p x) → ∀ᶠ x : α × γ in f ×ᶠ g, p (x.1, x.2, x.2) :=
  by
  intro h
  obtain ⟨t, ht, s, hs, hst⟩ := eventually_prod_iff.1 h
  refine' (ht.prod_mk hs.diag_of_prod).mono fun x hx => by simp only [hst hx.1 hx.2, Prod.mk.eta]
#align filter.eventually.diag_of_prod_right Filter.Eventually.diag_of_prod_right

#print Filter.tendsto_diag /-
theorem tendsto_diag : Tendsto (fun i => (i, i)) f (f ×ᶠ f) :=
  tendsto_iff_eventually.mpr fun _ hpr => hpr.diag_of_prod
#align filter.tendsto_diag Filter.tendsto_diag
-/

/- warning: filter.prod_infi_left -> Filter.prod_infᵢ_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {ι : Sort.{u3}} [_inst_1 : Nonempty.{u3} ι] {f : ι -> (Filter.{u1} α)} {g : Filter.{u2} β}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β (infᵢ.{u1, u3} (Filter.{u1} α) (ConditionallyCompleteLattice.toHasInf.{u1} (Filter.{u1} α) (CompleteLattice.toConditionallyCompleteLattice.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))) ι (fun (i : ι) => f i)) g) (infᵢ.{max u1 u2, u3} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (ConditionallyCompleteLattice.toHasInf.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (CompleteLattice.toConditionallyCompleteLattice.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.completeLattice.{max u1 u2} (Prod.{u1, u2} α β)))) ι (fun (i : ι) => Filter.prod.{u1, u2} α β (f i) g))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {ι : Sort.{u3}} [_inst_1 : Nonempty.{u3} ι] {f : ι -> (Filter.{u2} α)} {g : Filter.{u1} β}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β (infᵢ.{u2, u3} (Filter.{u2} α) (CompleteLattice.toInfSet.{u2} (Filter.{u2} α) (Filter.instCompleteLatticeFilter.{u2} α)) ι (fun (i : ι) => f i)) g) (infᵢ.{max u1 u2, u3} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (CompleteLattice.toInfSet.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.instCompleteLatticeFilter.{max u2 u1} (Prod.{u2, u1} α β))) ι (fun (i : ι) => Filter.prod.{u2, u1} α β (f i) g))
Case conversion may be inaccurate. Consider using '#align filter.prod_infi_left Filter.prod_infᵢ_leftₓ'. -/
theorem prod_infᵢ_left [Nonempty ι] {f : ι → Filter α} {g : Filter β} :
    (⨅ i, f i) ×ᶠ g = ⨅ i, f i ×ᶠ g :=
  by
  rw [Filter.prod, comap_infi, infᵢ_inf]
  simp only [Filter.prod, eq_self_iff_true]
#align filter.prod_infi_left Filter.prod_infᵢ_left

/- warning: filter.prod_infi_right -> Filter.prod_infᵢ_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {ι : Sort.{u3}} [_inst_1 : Nonempty.{u3} ι] {f : Filter.{u1} α} {g : ι -> (Filter.{u2} β)}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β f (infᵢ.{u2, u3} (Filter.{u2} β) (ConditionallyCompleteLattice.toHasInf.{u2} (Filter.{u2} β) (CompleteLattice.toConditionallyCompleteLattice.{u2} (Filter.{u2} β) (Filter.completeLattice.{u2} β))) ι (fun (i : ι) => g i))) (infᵢ.{max u1 u2, u3} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (ConditionallyCompleteLattice.toHasInf.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (CompleteLattice.toConditionallyCompleteLattice.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.completeLattice.{max u1 u2} (Prod.{u1, u2} α β)))) ι (fun (i : ι) => Filter.prod.{u1, u2} α β f (g i)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {ι : Sort.{u3}} [_inst_1 : Nonempty.{u3} ι] {f : Filter.{u2} α} {g : ι -> (Filter.{u1} β)}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β f (infᵢ.{u1, u3} (Filter.{u1} β) (CompleteLattice.toInfSet.{u1} (Filter.{u1} β) (Filter.instCompleteLatticeFilter.{u1} β)) ι (fun (i : ι) => g i))) (infᵢ.{max u1 u2, u3} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (CompleteLattice.toInfSet.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.instCompleteLatticeFilter.{max u2 u1} (Prod.{u2, u1} α β))) ι (fun (i : ι) => Filter.prod.{u2, u1} α β f (g i)))
Case conversion may be inaccurate. Consider using '#align filter.prod_infi_right Filter.prod_infᵢ_rightₓ'. -/
theorem prod_infᵢ_right [Nonempty ι] {f : Filter α} {g : ι → Filter β} :
    (f ×ᶠ ⨅ i, g i) = ⨅ i, f ×ᶠ g i :=
  by
  rw [Filter.prod, comap_infi, inf_infᵢ]
  simp only [Filter.prod, eq_self_iff_true]
#align filter.prod_infi_right Filter.prod_infᵢ_right

/- warning: filter.prod_mono -> Filter.prod_mono is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f₁ : Filter.{u1} α} {f₂ : Filter.{u1} α} {g₁ : Filter.{u2} β} {g₂ : Filter.{u2} β}, (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) f₁ f₂) -> (LE.le.{u2} (Filter.{u2} β) (Preorder.toLE.{u2} (Filter.{u2} β) (PartialOrder.toPreorder.{u2} (Filter.{u2} β) (Filter.partialOrder.{u2} β))) g₁ g₂) -> (LE.le.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.partialOrder.{max u1 u2} (Prod.{u1, u2} α β)))) (Filter.prod.{u1, u2} α β f₁ g₁) (Filter.prod.{u1, u2} α β f₂ g₂))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f₁ : Filter.{u2} α} {f₂ : Filter.{u2} α} {g₁ : Filter.{u1} β} {g₂ : Filter.{u1} β}, (LE.le.{u2} (Filter.{u2} α) (Preorder.toLE.{u2} (Filter.{u2} α) (PartialOrder.toPreorder.{u2} (Filter.{u2} α) (Filter.instPartialOrderFilter.{u2} α))) f₁ f₂) -> (LE.le.{u1} (Filter.{u1} β) (Preorder.toLE.{u1} (Filter.{u1} β) (PartialOrder.toPreorder.{u1} (Filter.{u1} β) (Filter.instPartialOrderFilter.{u1} β))) g₁ g₂) -> (LE.le.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Preorder.toLE.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (PartialOrder.toPreorder.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.instPartialOrderFilter.{max u2 u1} (Prod.{u2, u1} α β)))) (Filter.prod.{u2, u1} α β f₁ g₁) (Filter.prod.{u2, u1} α β f₂ g₂))
Case conversion may be inaccurate. Consider using '#align filter.prod_mono Filter.prod_monoₓ'. -/
@[mono]
theorem prod_mono {f₁ f₂ : Filter α} {g₁ g₂ : Filter β} (hf : f₁ ≤ f₂) (hg : g₁ ≤ g₂) :
    f₁ ×ᶠ g₁ ≤ f₂ ×ᶠ g₂ :=
  inf_le_inf (comap_mono hf) (comap_mono hg)
#align filter.prod_mono Filter.prod_mono

/- warning: filter.prod_mono_left -> Filter.prod_mono_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (g : Filter.{u2} β) {f₁ : Filter.{u1} α} {f₂ : Filter.{u1} α}, (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) f₁ f₂) -> (LE.le.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.partialOrder.{max u1 u2} (Prod.{u1, u2} α β)))) (Filter.prod.{u1, u2} α β f₁ g) (Filter.prod.{u1, u2} α β f₂ g))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} (g : Filter.{u2} β) {f₁ : Filter.{u1} α} {f₂ : Filter.{u1} α}, (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) f₁ f₂) -> (LE.le.{max u1 u2} (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (Preorder.toLE.{max u1 u2} (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (Filter.instPartialOrderFilter.{max u1 u2} (Prod.{u1, u2} α β)))) (Filter.prod.{u1, u2} α β f₁ g) (Filter.prod.{u1, u2} α β f₂ g))
Case conversion may be inaccurate. Consider using '#align filter.prod_mono_left Filter.prod_mono_leftₓ'. -/
theorem prod_mono_left (g : Filter β) {f₁ f₂ : Filter α} (hf : f₁ ≤ f₂) : f₁ ×ᶠ g ≤ f₂ ×ᶠ g :=
  Filter.prod_mono hf rfl.le
#align filter.prod_mono_left Filter.prod_mono_left

/- warning: filter.prod_mono_right -> Filter.prod_mono_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f : Filter.{u1} α) {g₁ : Filter.{u2} β} {g₂ : Filter.{u2} β}, (LE.le.{u2} (Filter.{u2} β) (Preorder.toLE.{u2} (Filter.{u2} β) (PartialOrder.toPreorder.{u2} (Filter.{u2} β) (Filter.partialOrder.{u2} β))) g₁ g₂) -> (LE.le.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.partialOrder.{max u1 u2} (Prod.{u1, u2} α β)))) (Filter.prod.{u1, u2} α β f g₁) (Filter.prod.{u1, u2} α β f g₂))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (f : Filter.{u2} α) {g₁ : Filter.{u1} β} {g₂ : Filter.{u1} β}, (LE.le.{u1} (Filter.{u1} β) (Preorder.toLE.{u1} (Filter.{u1} β) (PartialOrder.toPreorder.{u1} (Filter.{u1} β) (Filter.instPartialOrderFilter.{u1} β))) g₁ g₂) -> (LE.le.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Preorder.toLE.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (PartialOrder.toPreorder.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.instPartialOrderFilter.{max u2 u1} (Prod.{u2, u1} α β)))) (Filter.prod.{u2, u1} α β f g₁) (Filter.prod.{u2, u1} α β f g₂))
Case conversion may be inaccurate. Consider using '#align filter.prod_mono_right Filter.prod_mono_rightₓ'. -/
theorem prod_mono_right (f : Filter α) {g₁ g₂ : Filter β} (hf : g₁ ≤ g₂) : f ×ᶠ g₁ ≤ f ×ᶠ g₂ :=
  Filter.prod_mono rfl.le hf
#align filter.prod_mono_right Filter.prod_mono_right

#print Filter.prod_comap_comap_eq /-
theorem prod_comap_comap_eq.{u, v, w, x} {α₁ : Type u} {α₂ : Type v} {β₁ : Type w} {β₂ : Type x}
    {f₁ : Filter α₁} {f₂ : Filter α₂} {m₁ : β₁ → α₁} {m₂ : β₂ → α₂} :
    comap m₁ f₁ ×ᶠ comap m₂ f₂ = comap (fun p : β₁ × β₂ => (m₁ p.1, m₂ p.2)) (f₁ ×ᶠ f₂) := by
  simp only [Filter.prod, comap_comap, eq_self_iff_true, comap_inf]
#align filter.prod_comap_comap_eq Filter.prod_comap_comap_eq
-/

/- warning: filter.prod_comm' -> Filter.prod_comm' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} β}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β f g) (Filter.comap.{max u1 u2, max u2 u1} (Prod.{u1, u2} α β) (Prod.{u2, u1} β α) (Prod.swap.{u1, u2} α β) (Filter.prod.{u2, u1} β α g f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} β}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β f g) (Filter.comap.{max u1 u2, max u1 u2} (Prod.{u2, u1} α β) (Prod.{u1, u2} β α) (Prod.swap.{u2, u1} α β) (Filter.prod.{u1, u2} β α g f))
Case conversion may be inaccurate. Consider using '#align filter.prod_comm' Filter.prod_comm'ₓ'. -/
theorem prod_comm' : f ×ᶠ g = comap Prod.swap (g ×ᶠ f) := by
  simp only [Filter.prod, comap_comap, (· ∘ ·), inf_comm, Prod.fst_swap, eq_self_iff_true,
    Prod.snd_swap, comap_inf]
#align filter.prod_comm' Filter.prod_comm'

/- warning: filter.prod_comm -> Filter.prod_comm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} β}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β f g) (Filter.map.{max u2 u1, max u1 u2} (Prod.{u2, u1} β α) (Prod.{u1, u2} α β) (fun (p : Prod.{u2, u1} β α) => Prod.mk.{u1, u2} α β (Prod.snd.{u2, u1} β α p) (Prod.fst.{u2, u1} β α p)) (Filter.prod.{u2, u1} β α g f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} β}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β f g) (Filter.map.{max u2 u1, max u1 u2} (Prod.{u1, u2} β α) (Prod.{u2, u1} α β) (fun (p : Prod.{u1, u2} β α) => Prod.mk.{u2, u1} α β (Prod.snd.{u1, u2} β α p) (Prod.fst.{u1, u2} β α p)) (Filter.prod.{u1, u2} β α g f))
Case conversion may be inaccurate. Consider using '#align filter.prod_comm Filter.prod_commₓ'. -/
theorem prod_comm : f ×ᶠ g = map (fun p : β × α => (p.2, p.1)) (g ×ᶠ f) :=
  by
  rw [prod_comm', ← map_swap_eq_comap_swap]
  rfl
#align filter.prod_comm Filter.prod_comm

/- warning: filter.map_fst_prod -> Filter.map_fst_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f : Filter.{u1} α) (g : Filter.{u2} β) [_inst_1 : Filter.NeBot.{u2} β g], Eq.{succ u1} (Filter.{u1} α) (Filter.map.{max u1 u2, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) (Filter.prod.{u1, u2} α β f g)) f
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (f : Filter.{u2} α) (g : Filter.{u1} β) [_inst_1 : Filter.NeBot.{u1} β g], Eq.{succ u2} (Filter.{u2} α) (Filter.map.{max u1 u2, u2} (Prod.{u2, u1} α β) α (Prod.fst.{u2, u1} α β) (Filter.prod.{u2, u1} α β f g)) f
Case conversion may be inaccurate. Consider using '#align filter.map_fst_prod Filter.map_fst_prodₓ'. -/
@[simp]
theorem map_fst_prod (f : Filter α) (g : Filter β) [NeBot g] : map Prod.fst (f ×ᶠ g) = f :=
  by
  refine' le_antisymm tendsto_fst fun s hs => _
  rw [mem_map, mem_prod_iff] at hs
  rcases hs with ⟨t₁, h₁, t₂, h₂, hs⟩
  rw [← image_subset_iff, fst_image_prod] at hs
  exacts[mem_of_superset h₁ hs, nonempty_of_mem h₂]
#align filter.map_fst_prod Filter.map_fst_prod

/- warning: filter.map_snd_prod -> Filter.map_snd_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f : Filter.{u1} α) (g : Filter.{u2} β) [_inst_1 : Filter.NeBot.{u1} α f], Eq.{succ u2} (Filter.{u2} β) (Filter.map.{max u1 u2, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) (Filter.prod.{u1, u2} α β f g)) g
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (f : Filter.{u2} α) (g : Filter.{u1} β) [_inst_1 : Filter.NeBot.{u2} α f], Eq.{succ u1} (Filter.{u1} β) (Filter.map.{max u1 u2, u1} (Prod.{u2, u1} α β) β (Prod.snd.{u2, u1} α β) (Filter.prod.{u2, u1} α β f g)) g
Case conversion may be inaccurate. Consider using '#align filter.map_snd_prod Filter.map_snd_prodₓ'. -/
@[simp]
theorem map_snd_prod (f : Filter α) (g : Filter β) [NeBot f] : map Prod.snd (f ×ᶠ g) = g := by
  rw [prod_comm, map_map, (· ∘ ·), map_fst_prod]
#align filter.map_snd_prod Filter.map_snd_prod

/- warning: filter.prod_le_prod -> Filter.prod_le_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f₁ : Filter.{u1} α} {f₂ : Filter.{u1} α} {g₁ : Filter.{u2} β} {g₂ : Filter.{u2} β} [_inst_1 : Filter.NeBot.{u1} α f₁] [_inst_2 : Filter.NeBot.{u2} β g₁], Iff (LE.le.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.partialOrder.{max u1 u2} (Prod.{u1, u2} α β)))) (Filter.prod.{u1, u2} α β f₁ g₁) (Filter.prod.{u1, u2} α β f₂ g₂)) (And (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) f₁ f₂) (LE.le.{u2} (Filter.{u2} β) (Preorder.toLE.{u2} (Filter.{u2} β) (PartialOrder.toPreorder.{u2} (Filter.{u2} β) (Filter.partialOrder.{u2} β))) g₁ g₂))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f₁ : Filter.{u2} α} {f₂ : Filter.{u2} α} {g₁ : Filter.{u1} β} {g₂ : Filter.{u1} β} [_inst_1 : Filter.NeBot.{u2} α f₁] [_inst_2 : Filter.NeBot.{u1} β g₁], Iff (LE.le.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Preorder.toLE.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (PartialOrder.toPreorder.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.instPartialOrderFilter.{max u2 u1} (Prod.{u2, u1} α β)))) (Filter.prod.{u2, u1} α β f₁ g₁) (Filter.prod.{u2, u1} α β f₂ g₂)) (And (LE.le.{u2} (Filter.{u2} α) (Preorder.toLE.{u2} (Filter.{u2} α) (PartialOrder.toPreorder.{u2} (Filter.{u2} α) (Filter.instPartialOrderFilter.{u2} α))) f₁ f₂) (LE.le.{u1} (Filter.{u1} β) (Preorder.toLE.{u1} (Filter.{u1} β) (PartialOrder.toPreorder.{u1} (Filter.{u1} β) (Filter.instPartialOrderFilter.{u1} β))) g₁ g₂))
Case conversion may be inaccurate. Consider using '#align filter.prod_le_prod Filter.prod_le_prodₓ'. -/
@[simp]
theorem prod_le_prod {f₁ f₂ : Filter α} {g₁ g₂ : Filter β} [NeBot f₁] [NeBot g₁] :
    f₁ ×ᶠ g₁ ≤ f₂ ×ᶠ g₂ ↔ f₁ ≤ f₂ ∧ g₁ ≤ g₂ :=
  ⟨fun h =>
    ⟨map_fst_prod f₁ g₁ ▸ tendsto_fst.mono_left h, map_snd_prod f₁ g₁ ▸ tendsto_snd.mono_left h⟩,
    fun h => prod_mono h.1 h.2⟩
#align filter.prod_le_prod Filter.prod_le_prod

/- warning: filter.prod_inj -> Filter.prod_inj is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f₁ : Filter.{u1} α} {f₂ : Filter.{u1} α} {g₁ : Filter.{u2} β} {g₂ : Filter.{u2} β} [_inst_1 : Filter.NeBot.{u1} α f₁] [_inst_2 : Filter.NeBot.{u2} β g₁], Iff (Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β f₁ g₁) (Filter.prod.{u1, u2} α β f₂ g₂)) (And (Eq.{succ u1} (Filter.{u1} α) f₁ f₂) (Eq.{succ u2} (Filter.{u2} β) g₁ g₂))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f₁ : Filter.{u2} α} {f₂ : Filter.{u2} α} {g₁ : Filter.{u1} β} {g₂ : Filter.{u1} β} [_inst_1 : Filter.NeBot.{u2} α f₁] [_inst_2 : Filter.NeBot.{u1} β g₁], Iff (Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β f₁ g₁) (Filter.prod.{u2, u1} α β f₂ g₂)) (And (Eq.{succ u2} (Filter.{u2} α) f₁ f₂) (Eq.{succ u1} (Filter.{u1} β) g₁ g₂))
Case conversion may be inaccurate. Consider using '#align filter.prod_inj Filter.prod_injₓ'. -/
@[simp]
theorem prod_inj {f₁ f₂ : Filter α} {g₁ g₂ : Filter β} [NeBot f₁] [NeBot g₁] :
    f₁ ×ᶠ g₁ = f₂ ×ᶠ g₂ ↔ f₁ = f₂ ∧ g₁ = g₂ :=
  by
  refine' ⟨fun h => _, fun h => h.1 ▸ h.2 ▸ rfl⟩
  have hle : f₁ ≤ f₂ ∧ g₁ ≤ g₂ := prod_le_prod.1 h.le
  haveI := ne_bot_of_le hle.1; haveI := ne_bot_of_le hle.2
  exact ⟨hle.1.antisymm <| (prod_le_prod.1 h.ge).1, hle.2.antisymm <| (prod_le_prod.1 h.ge).2⟩
#align filter.prod_inj Filter.prod_inj

/- warning: filter.eventually_swap_iff -> Filter.eventually_swap_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} β} {p : (Prod.{u1, u2} α β) -> Prop}, Iff (Filter.Eventually.{max u1 u2} (Prod.{u1, u2} α β) (fun (x : Prod.{u1, u2} α β) => p x) (Filter.prod.{u1, u2} α β f g)) (Filter.Eventually.{max u2 u1} (Prod.{u2, u1} β α) (fun (y : Prod.{u2, u1} β α) => p (Prod.swap.{u2, u1} β α y)) (Filter.prod.{u2, u1} β α g f))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} β} {p : (Prod.{u2, u1} α β) -> Prop}, Iff (Filter.Eventually.{max u2 u1} (Prod.{u2, u1} α β) (fun (x : Prod.{u2, u1} α β) => p x) (Filter.prod.{u2, u1} α β f g)) (Filter.Eventually.{max u2 u1} (Prod.{u1, u2} β α) (fun (y : Prod.{u1, u2} β α) => p (Prod.swap.{u1, u2} β α y)) (Filter.prod.{u1, u2} β α g f))
Case conversion may be inaccurate. Consider using '#align filter.eventually_swap_iff Filter.eventually_swap_iffₓ'. -/
theorem eventually_swap_iff {p : α × β → Prop} :
    (∀ᶠ x : α × β in f ×ᶠ g, p x) ↔ ∀ᶠ y : β × α in g ×ᶠ f, p y.symm :=
  by
  rw [prod_comm, eventually_map]
  simpa
#align filter.eventually_swap_iff Filter.eventually_swap_iff

/- warning: filter.prod_assoc -> Filter.prod_assoc is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : Filter.{u1} α) (g : Filter.{u2} β) (h : Filter.{u3} γ), Eq.{succ (max u1 u2 u3)} (Filter.{max u1 u2 u3} (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ))) (Filter.map.{max (max u1 u2) u3, max u1 u2 u3} (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (coeFn.{max 1 (max (max (succ (max u1 u2)) (succ u3)) (succ u1) (succ (max u2 u3))) (max (succ u1) (succ (max u2 u3))) (succ (max u1 u2)) (succ u3), max (max (succ (max u1 u2)) (succ u3)) (succ u1) (succ (max u2 u3))} (Equiv.{max (succ (max u1 u2)) (succ u3), max (succ u1) (succ (max u2 u3))} (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ))) (fun (_x : Equiv.{max (succ (max u1 u2)) (succ u3), max (succ u1) (succ (max u2 u3))} (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ))) => (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) -> (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ))) (Equiv.hasCoeToFun.{max (succ (max u1 u2)) (succ u3), max (succ u1) (succ (max u2 u3))} (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ))) (Equiv.prodAssoc.{u1, u2, u3} α β γ)) (Filter.prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ (Filter.prod.{u1, u2} α β f g) h)) (Filter.prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ) f (Filter.prod.{u2, u3} β γ g h))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} (f : Filter.{u3} α) (g : Filter.{u2} β) (h : Filter.{u1} γ), Eq.{max (max (succ u3) (succ u2)) (succ u1)} (Filter.{max (max u3 u2) u1} (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ))) (Filter.map.{max (max u3 u2) u1, max (max u3 u2) u1} (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (FunLike.coe.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Equiv.{max (succ u1) (succ (max u2 u3)), max (succ (max u1 u2)) (succ u3)} (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ))) (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (fun (_x : Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) => Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) _x) (Equiv.instFunLikeEquiv.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ))) (Equiv.prodAssoc.{u3, u2, u1} α β γ)) (Filter.prod.{max u3 u2, u1} (Prod.{u3, u2} α β) γ (Filter.prod.{u3, u2} α β f g) h)) (Filter.prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ) f (Filter.prod.{u2, u1} β γ g h))
Case conversion may be inaccurate. Consider using '#align filter.prod_assoc Filter.prod_assocₓ'. -/
theorem prod_assoc (f : Filter α) (g : Filter β) (h : Filter γ) :
    map (Equiv.prodAssoc α β γ) (f ×ᶠ g ×ᶠ h) = f ×ᶠ (g ×ᶠ h) := by
  simp_rw [← comap_equiv_symm, Filter.prod, comap_inf, comap_comap, inf_assoc, Function.comp,
    Equiv.prodAssoc_symm_apply]
#align filter.prod_assoc Filter.prod_assoc

/- warning: filter.prod_assoc_symm -> Filter.prod_assoc_symm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : Filter.{u1} α) (g : Filter.{u2} β) (h : Filter.{u3} γ), Eq.{succ (max (max u1 u2) u3)} (Filter.{max (max u1 u2) u3} (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ)) (Filter.map.{max u1 u2 u3, max (max u1 u2) u3} (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (coeFn.{max 1 (max (max (succ u1) (succ (max u2 u3))) (succ (max u1 u2)) (succ u3)) (max (succ (max u1 u2)) (succ u3)) (succ u1) (succ (max u2 u3)), max (max (succ u1) (succ (max u2 u3))) (succ (max u1 u2)) (succ u3)} (Equiv.{max (succ u1) (succ (max u2 u3)), max (succ (max u1 u2)) (succ u3)} (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ)) (fun (_x : Equiv.{max (succ u1) (succ (max u2 u3)), max (succ (max u1 u2)) (succ u3)} (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ)) => (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) -> (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ)) (Equiv.hasCoeToFun.{max (succ u1) (succ (max u2 u3)), max (succ (max u1 u2)) (succ u3)} (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ)) (Equiv.symm.{max (succ (max u1 u2)) (succ u3), max (succ u1) (succ (max u2 u3))} (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (Equiv.prodAssoc.{u1, u2, u3} α β γ))) (Filter.prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ) f (Filter.prod.{u2, u3} β γ g h))) (Filter.prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ (Filter.prod.{u1, u2} α β f g) h)
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} (f : Filter.{u3} α) (g : Filter.{u2} β) (h : Filter.{u1} γ), Eq.{max (max (succ u3) (succ u2)) (succ u1)} (Filter.{max (max u3 u2) u1} (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ)) (Filter.map.{max (max u3 u2) u1, max (max u3 u2) u1} (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (FunLike.coe.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Equiv.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ)) (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (fun (_x : Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) => Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) _x) (Equiv.instFunLikeEquiv.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ)) (Equiv.symm.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (Equiv.prodAssoc.{u3, u2, u1} α β γ))) (Filter.prod.{u3, max u2 u1} α (Prod.{u2, u1} β γ) f (Filter.prod.{u2, u1} β γ g h))) (Filter.prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ (Filter.prod.{u3, u2} α β f g) h)
Case conversion may be inaccurate. Consider using '#align filter.prod_assoc_symm Filter.prod_assoc_symmₓ'. -/
theorem prod_assoc_symm (f : Filter α) (g : Filter β) (h : Filter γ) :
    map (Equiv.prodAssoc α β γ).symm (f ×ᶠ (g ×ᶠ h)) = f ×ᶠ g ×ᶠ h := by
  simp_rw [map_equiv_symm, Filter.prod, comap_inf, comap_comap, inf_assoc, Function.comp,
    Equiv.prodAssoc_apply]
#align filter.prod_assoc_symm Filter.prod_assoc_symm

/- warning: filter.tendsto_prod_assoc -> Filter.tendsto_prodAssoc is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {f : Filter.{u1} α} {g : Filter.{u2} β} {h : Filter.{u3} γ}, Filter.Tendsto.{max (max u1 u2) u3, max u1 u2 u3} (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (coeFn.{max 1 (max (max (succ (max u1 u2)) (succ u3)) (succ u1) (succ (max u2 u3))) (max (succ u1) (succ (max u2 u3))) (succ (max u1 u2)) (succ u3), max (max (succ (max u1 u2)) (succ u3)) (succ u1) (succ (max u2 u3))} (Equiv.{max (succ (max u1 u2)) (succ u3), max (succ u1) (succ (max u2 u3))} (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ))) (fun (_x : Equiv.{max (succ (max u1 u2)) (succ u3), max (succ u1) (succ (max u2 u3))} (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ))) => (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) -> (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ))) (Equiv.hasCoeToFun.{max (succ (max u1 u2)) (succ u3), max (succ u1) (succ (max u2 u3))} (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ))) (Equiv.prodAssoc.{u1, u2, u3} α β γ)) (Filter.prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ (Filter.prod.{u1, u2} α β f g) h) (Filter.prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ) f (Filter.prod.{u2, u3} β γ g h))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} {f : Filter.{u3} α} {g : Filter.{u2} β} {h : Filter.{u1} γ}, Filter.Tendsto.{max (max u3 u2) u1, max (max u3 u2) u1} (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (FunLike.coe.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Equiv.{max (succ u1) (succ (max u2 u3)), max (succ (max u1 u2)) (succ u3)} (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ))) (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (fun (_x : Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) => Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) _x) (Equiv.instFunLikeEquiv.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ))) (Equiv.prodAssoc.{u3, u2, u1} α β γ)) (Filter.prod.{max u3 u2, u1} (Prod.{u3, u2} α β) γ (Filter.prod.{u3, u2} α β f g) h) (Filter.prod.{u3, max u2 u1} α (Prod.{u2, u1} β γ) f (Filter.prod.{u2, u1} β γ g h))
Case conversion may be inaccurate. Consider using '#align filter.tendsto_prod_assoc Filter.tendsto_prodAssocₓ'. -/
theorem tendsto_prodAssoc {f : Filter α} {g : Filter β} {h : Filter γ} :
    Tendsto (Equiv.prodAssoc α β γ) (f ×ᶠ g ×ᶠ h) (f ×ᶠ (g ×ᶠ h)) :=
  (prod_assoc f g h).le
#align filter.tendsto_prod_assoc Filter.tendsto_prodAssoc

/- warning: filter.tendsto_prod_assoc_symm -> Filter.tendsto_prodAssoc_symm is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {f : Filter.{u1} α} {g : Filter.{u2} β} {h : Filter.{u3} γ}, Filter.Tendsto.{max u1 u2 u3, max (max u1 u2) u3} (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (coeFn.{max 1 (max (max (succ u1) (succ (max u2 u3))) (succ (max u1 u2)) (succ u3)) (max (succ (max u1 u2)) (succ u3)) (succ u1) (succ (max u2 u3)), max (max (succ u1) (succ (max u2 u3))) (succ (max u1 u2)) (succ u3)} (Equiv.{max (succ u1) (succ (max u2 u3)), max (succ (max u1 u2)) (succ u3)} (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ)) (fun (_x : Equiv.{max (succ u1) (succ (max u2 u3)), max (succ (max u1 u2)) (succ u3)} (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ)) => (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) -> (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ)) (Equiv.hasCoeToFun.{max (succ u1) (succ (max u2 u3)), max (succ (max u1 u2)) (succ u3)} (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ)) (Equiv.symm.{max (succ (max u1 u2)) (succ u3), max (succ u1) (succ (max u2 u3))} (Prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ) (Prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ)) (Equiv.prodAssoc.{u1, u2, u3} α β γ))) (Filter.prod.{u1, max u2 u3} α (Prod.{u2, u3} β γ) f (Filter.prod.{u2, u3} β γ g h)) (Filter.prod.{max u1 u2, u3} (Prod.{u1, u2} α β) γ (Filter.prod.{u1, u2} α β f g) h)
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} {f : Filter.{u3} α} {g : Filter.{u2} β} {h : Filter.{u1} γ}, Filter.Tendsto.{max (max u3 u2) u1, max (max u3 u2) u1} (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (FunLike.coe.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Equiv.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ)) (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (fun (_x : Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) => Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) _x) (Equiv.instFunLikeEquiv.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ)) (Equiv.symm.{max (max (succ u3) (succ u2)) (succ u1), max (max (succ u3) (succ u2)) (succ u1)} (Prod.{max u2 u3, u1} (Prod.{u3, u2} α β) γ) (Prod.{u3, max u1 u2} α (Prod.{u2, u1} β γ)) (Equiv.prodAssoc.{u3, u2, u1} α β γ))) (Filter.prod.{u3, max u2 u1} α (Prod.{u2, u1} β γ) f (Filter.prod.{u2, u1} β γ g h)) (Filter.prod.{max u3 u2, u1} (Prod.{u3, u2} α β) γ (Filter.prod.{u3, u2} α β f g) h)
Case conversion may be inaccurate. Consider using '#align filter.tendsto_prod_assoc_symm Filter.tendsto_prodAssoc_symmₓ'. -/
theorem tendsto_prodAssoc_symm {f : Filter α} {g : Filter β} {h : Filter γ} :
    Tendsto (Equiv.prodAssoc α β γ).symm (f ×ᶠ (g ×ᶠ h)) (f ×ᶠ g ×ᶠ h) :=
  (prod_assoc_symm f g h).le
#align filter.tendsto_prod_assoc_symm Filter.tendsto_prodAssoc_symm

/- warning: filter.map_swap4_prod -> Filter.map_swap4_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} {f : Filter.{u1} α} {g : Filter.{u2} β} {h : Filter.{u3} γ} {k : Filter.{u4} δ}, Eq.{succ (max (max u1 u3) u2 u4)} (Filter.{max (max u1 u3) u2 u4} (Prod.{max u1 u3, max u2 u4} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ))) (Filter.map.{max (max u1 u2) u3 u4, max (max u1 u3) u2 u4} (Prod.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ)) (Prod.{max u1 u3, max u2 u4} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ)) (fun (p : Prod.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ)) => Prod.mk.{max u1 u3, max u2 u4} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ) (Prod.mk.{u1, u3} α γ (Prod.fst.{u1, u2} α β (Prod.fst.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) p)) (Prod.fst.{u3, u4} γ δ (Prod.snd.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) p))) (Prod.mk.{u2, u4} β δ (Prod.snd.{u1, u2} α β (Prod.fst.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) p)) (Prod.snd.{u3, u4} γ δ (Prod.snd.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) p)))) (Filter.prod.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) (Filter.prod.{u1, u2} α β f g) (Filter.prod.{u3, u4} γ δ h k))) (Filter.prod.{max u1 u3, max u2 u4} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ) (Filter.prod.{u1, u3} α γ f h) (Filter.prod.{u2, u4} β δ g k))
but is expected to have type
  forall {α : Type.{u4}} {β : Type.{u3}} {γ : Type.{u2}} {δ : Type.{u1}} {f : Filter.{u4} α} {g : Filter.{u3} β} {h : Filter.{u2} γ} {k : Filter.{u1} δ}, Eq.{max (max (max (succ u4) (succ u3)) (succ u2)) (succ u1)} (Filter.{max (max u1 u3) u2 u4} (Prod.{max u2 u4, max u1 u3} (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ))) (Filter.map.{max (max (max u4 u3) u2) u1, max (max u1 u3) u2 u4} (Prod.{max u3 u4, max u1 u2} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ)) (Prod.{max u2 u4, max u1 u3} (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ)) (fun (p : Prod.{max u3 u4, max u1 u2} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ)) => Prod.mk.{max u2 u4, max u1 u3} (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ) (Prod.mk.{u4, u2} α γ (Prod.fst.{u4, u3} α β (Prod.fst.{max u4 u3, max u2 u1} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ) p)) (Prod.fst.{u2, u1} γ δ (Prod.snd.{max u4 u3, max u2 u1} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ) p))) (Prod.mk.{u3, u1} β δ (Prod.snd.{u4, u3} α β (Prod.fst.{max u4 u3, max u2 u1} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ) p)) (Prod.snd.{u2, u1} γ δ (Prod.snd.{max u4 u3, max u2 u1} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ) p)))) (Filter.prod.{max u4 u3, max u2 u1} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ) (Filter.prod.{u4, u3} α β f g) (Filter.prod.{u2, u1} γ δ h k))) (Filter.prod.{max u2 u4, max u1 u3} (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ) (Filter.prod.{u4, u2} α γ f h) (Filter.prod.{u3, u1} β δ g k))
Case conversion may be inaccurate. Consider using '#align filter.map_swap4_prod Filter.map_swap4_prodₓ'. -/
/-- A useful lemma when dealing with uniformities. -/
theorem map_swap4_prod {f : Filter α} {g : Filter β} {h : Filter γ} {k : Filter δ} :
    map (fun p : (α × β) × γ × δ => ((p.1.1, p.2.1), (p.1.2, p.2.2))) (f ×ᶠ g ×ᶠ (h ×ᶠ k)) =
      f ×ᶠ h ×ᶠ (g ×ᶠ k) :=
  by simp_rw [map_swap4_eq_comap, Filter.prod, comap_inf, comap_comap, inf_assoc, inf_left_comm]
#align filter.map_swap4_prod Filter.map_swap4_prod

/- warning: filter.tendsto_swap4_prod -> Filter.tendsto_swap4_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} {f : Filter.{u1} α} {g : Filter.{u2} β} {h : Filter.{u3} γ} {k : Filter.{u4} δ}, Filter.Tendsto.{max (max u1 u2) u3 u4, max (max u1 u3) u2 u4} (Prod.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ)) (Prod.{max u1 u3, max u2 u4} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ)) (fun (p : Prod.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ)) => Prod.mk.{max u1 u3, max u2 u4} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ) (Prod.mk.{u1, u3} α γ (Prod.fst.{u1, u2} α β (Prod.fst.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) p)) (Prod.fst.{u3, u4} γ δ (Prod.snd.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) p))) (Prod.mk.{u2, u4} β δ (Prod.snd.{u1, u2} α β (Prod.fst.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) p)) (Prod.snd.{u3, u4} γ δ (Prod.snd.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) p)))) (Filter.prod.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) (Filter.prod.{u1, u2} α β f g) (Filter.prod.{u3, u4} γ δ h k)) (Filter.prod.{max u1 u3, max u2 u4} (Prod.{u1, u3} α γ) (Prod.{u2, u4} β δ) (Filter.prod.{u1, u3} α γ f h) (Filter.prod.{u2, u4} β δ g k))
but is expected to have type
  forall {α : Type.{u4}} {β : Type.{u3}} {γ : Type.{u2}} {δ : Type.{u1}} {f : Filter.{u4} α} {g : Filter.{u3} β} {h : Filter.{u2} γ} {k : Filter.{u1} δ}, Filter.Tendsto.{max (max (max u4 u3) u2) u1, max (max u1 u3) u2 u4} (Prod.{max u3 u4, max u1 u2} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ)) (Prod.{max u2 u4, max u1 u3} (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ)) (fun (p : Prod.{max u3 u4, max u1 u2} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ)) => Prod.mk.{max u2 u4, max u1 u3} (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ) (Prod.mk.{u4, u2} α γ (Prod.fst.{u4, u3} α β (Prod.fst.{max u4 u3, max u2 u1} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ) p)) (Prod.fst.{u2, u1} γ δ (Prod.snd.{max u4 u3, max u2 u1} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ) p))) (Prod.mk.{u3, u1} β δ (Prod.snd.{u4, u3} α β (Prod.fst.{max u4 u3, max u2 u1} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ) p)) (Prod.snd.{u2, u1} γ δ (Prod.snd.{max u4 u3, max u2 u1} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ) p)))) (Filter.prod.{max u4 u3, max u2 u1} (Prod.{u4, u3} α β) (Prod.{u2, u1} γ δ) (Filter.prod.{u4, u3} α β f g) (Filter.prod.{u2, u1} γ δ h k)) (Filter.prod.{max u4 u2, max u3 u1} (Prod.{u4, u2} α γ) (Prod.{u3, u1} β δ) (Filter.prod.{u4, u2} α γ f h) (Filter.prod.{u3, u1} β δ g k))
Case conversion may be inaccurate. Consider using '#align filter.tendsto_swap4_prod Filter.tendsto_swap4_prodₓ'. -/
theorem tendsto_swap4_prod {f : Filter α} {g : Filter β} {h : Filter γ} {k : Filter δ} :
    Tendsto (fun p : (α × β) × γ × δ => ((p.1.1, p.2.1), (p.1.2, p.2.2))) (f ×ᶠ g ×ᶠ (h ×ᶠ k))
      (f ×ᶠ h ×ᶠ (g ×ᶠ k)) :=
  map_swap4_prod.le
#align filter.tendsto_swap4_prod Filter.tendsto_swap4_prod

/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
#print Filter.prod_map_map_eq /-
theorem prod_map_map_eq.{u, v, w, x} {α₁ : Type u} {α₂ : Type v} {β₁ : Type w} {β₂ : Type x}
    {f₁ : Filter α₁} {f₂ : Filter α₂} {m₁ : α₁ → β₁} {m₂ : α₂ → β₂} :
    map m₁ f₁ ×ᶠ map m₂ f₂ = map (fun p : α₁ × α₂ => (m₁ p.1, m₂ p.2)) (f₁ ×ᶠ f₂) :=
  le_antisymm
    (fun s hs =>
      let ⟨s₁, hs₁, s₂, hs₂, h⟩ := mem_prod_iff.mp hs
      Filter.sets_of_superset _ (prod_mem_prod (image_mem_map hs₁) (image_mem_map hs₂)) <|
        calc
          (m₁ '' s₁) ×ˢ (m₂ '' s₂) = (fun p : α₁ × α₂ => (m₁ p.1, m₂ p.2)) '' s₁ ×ˢ s₂ :=
            Set.prod_image_image_eq
          _ ⊆ _ := by rwa [image_subset_iff]
          )
    ((Tendsto.comp le_rfl tendsto_fst).prod_mk (Tendsto.comp le_rfl tendsto_snd))
#align filter.prod_map_map_eq Filter.prod_map_map_eq
-/

/- warning: filter.prod_map_map_eq' -> Filter.prod_map_map_eq' is a dubious translation:
lean 3 declaration is
  forall {α₁ : Type.{u1}} {α₂ : Type.{u2}} {β₁ : Type.{u3}} {β₂ : Type.{u4}} (f : α₁ -> α₂) (g : β₁ -> β₂) (F : Filter.{u1} α₁) (G : Filter.{u3} β₁), Eq.{succ (max u2 u4)} (Filter.{max u2 u4} (Prod.{u2, u4} α₂ β₂)) (Filter.prod.{u2, u4} α₂ β₂ (Filter.map.{u1, u2} α₁ α₂ f F) (Filter.map.{u3, u4} β₁ β₂ g G)) (Filter.map.{max u1 u3, max u2 u4} (Prod.{u1, u3} α₁ β₁) (Prod.{u2, u4} α₂ β₂) (Prod.map.{u1, u2, u3, u4} α₁ α₂ β₁ β₂ f g) (Filter.prod.{u1, u3} α₁ β₁ F G))
but is expected to have type
  forall {α₁ : Type.{u4}} {α₂ : Type.{u3}} {β₁ : Type.{u2}} {β₂ : Type.{u1}} (f : α₁ -> α₂) (g : β₁ -> β₂) (F : Filter.{u4} α₁) (G : Filter.{u2} β₁), Eq.{max (succ u3) (succ u1)} (Filter.{max u1 u3} (Prod.{u3, u1} α₂ β₂)) (Filter.prod.{u3, u1} α₂ β₂ (Filter.map.{u4, u3} α₁ α₂ f F) (Filter.map.{u2, u1} β₁ β₂ g G)) (Filter.map.{max u2 u4, max u1 u3} (Prod.{u4, u2} α₁ β₁) (Prod.{u3, u1} α₂ β₂) (Prod.map.{u4, u3, u2, u1} α₁ α₂ β₁ β₂ f g) (Filter.prod.{u4, u2} α₁ β₁ F G))
Case conversion may be inaccurate. Consider using '#align filter.prod_map_map_eq' Filter.prod_map_map_eq'ₓ'. -/
theorem prod_map_map_eq' {α₁ : Type _} {α₂ : Type _} {β₁ : Type _} {β₂ : Type _} (f : α₁ → α₂)
    (g : β₁ → β₂) (F : Filter α₁) (G : Filter β₁) :
    map f F ×ᶠ map g G = map (Prod.map f g) (F ×ᶠ G) :=
  prod_map_map_eq
#align filter.prod_map_map_eq' Filter.prod_map_map_eq'

/- warning: filter.le_prod_map_fst_snd -> Filter.le_prod_map_fst_snd is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{max u1 u2} (Prod.{u1, u2} α β)}, LE.le.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Preorder.toLE.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.partialOrder.{max u1 u2} (Prod.{u1, u2} α β)))) f (Filter.prod.{u1, u2} α β (Filter.map.{max u1 u2, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) f) (Filter.map.{max u1 u2, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) f))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{max u2 u1} (Prod.{u1, u2} α β)}, LE.le.{max u1 u2} (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (Preorder.toLE.{max u1 u2} (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (PartialOrder.toPreorder.{max u1 u2} (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (Filter.instPartialOrderFilter.{max u1 u2} (Prod.{u1, u2} α β)))) f (Filter.prod.{u1, u2} α β (Filter.map.{max u2 u1, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) f) (Filter.map.{max u2 u1, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) f))
Case conversion may be inaccurate. Consider using '#align filter.le_prod_map_fst_snd Filter.le_prod_map_fst_sndₓ'. -/
theorem le_prod_map_fst_snd {f : Filter (α × β)} : f ≤ map Prod.fst f ×ᶠ map Prod.snd f :=
  le_inf le_comap_map le_comap_map
#align filter.le_prod_map_fst_snd Filter.le_prod_map_fst_snd

/- warning: filter.tendsto.prod_map -> Filter.Tendsto.prod_map is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} {f : α -> γ} {g : β -> δ} {a : Filter.{u1} α} {b : Filter.{u2} β} {c : Filter.{u3} γ} {d : Filter.{u4} δ}, (Filter.Tendsto.{u1, u3} α γ f a c) -> (Filter.Tendsto.{u2, u4} β δ g b d) -> (Filter.Tendsto.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) (Prod.map.{u1, u3, u2, u4} α γ β δ f g) (Filter.prod.{u1, u2} α β a b) (Filter.prod.{u3, u4} γ δ c d))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} {δ : Type.{u4}} {f : α -> γ} {g : β -> δ} {a : Filter.{u3} α} {b : Filter.{u2} β} {c : Filter.{u1} γ} {d : Filter.{u4} δ}, (Filter.Tendsto.{u3, u1} α γ f a c) -> (Filter.Tendsto.{u2, u4} β δ g b d) -> (Filter.Tendsto.{max u2 u3, max u4 u1} (Prod.{u3, u2} α β) (Prod.{u1, u4} γ δ) (Prod.map.{u3, u1, u2, u4} α γ β δ f g) (Filter.prod.{u3, u2} α β a b) (Filter.prod.{u1, u4} γ δ c d))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.prod_map Filter.Tendsto.prod_mapₓ'. -/
theorem Tendsto.prod_map {δ : Type _} {f : α → γ} {g : β → δ} {a : Filter α} {b : Filter β}
    {c : Filter γ} {d : Filter δ} (hf : Tendsto f a c) (hg : Tendsto g b d) :
    Tendsto (Prod.map f g) (a ×ᶠ b) (c ×ᶠ d) :=
  by
  erw [tendsto, ← prod_map_map_eq]
  exact Filter.prod_mono hf hg
#align filter.tendsto.prod_map Filter.Tendsto.prod_map

/- warning: filter.map_prod -> Filter.map_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (m : (Prod.{u1, u2} α β) -> γ) (f : Filter.{u1} α) (g : Filter.{u2} β), Eq.{succ u3} (Filter.{u3} γ) (Filter.map.{max u1 u2, u3} (Prod.{u1, u2} α β) γ m (Filter.prod.{u1, u2} α β f g)) (Filter.seq.{u2, u3} β γ (Filter.map.{u1, max u2 u3} α (β -> γ) (fun (a : α) (b : β) => m (Prod.mk.{u1, u2} α β a b)) f) g)
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} (m : (Prod.{u3, u2} α β) -> γ) (f : Filter.{u3} α) (g : Filter.{u2} β), Eq.{succ u1} (Filter.{u1} γ) (Filter.map.{max u3 u2, u1} (Prod.{u3, u2} α β) γ m (Filter.prod.{u3, u2} α β f g)) (Filter.seq.{u2, u1} β γ (Filter.map.{u3, max u2 u1} α (β -> γ) (fun (a : α) (b : β) => m (Prod.mk.{u3, u2} α β a b)) f) g)
Case conversion may be inaccurate. Consider using '#align filter.map_prod Filter.map_prodₓ'. -/
protected theorem map_prod (m : α × β → γ) (f : Filter α) (g : Filter β) :
    map m (f ×ᶠ g) = (f.map fun a b => m (a, b)).seq g :=
  by
  simp [Filter.ext_iff, mem_prod_iff, mem_map_seq_iff]
  intro s
  constructor
  exact fun ⟨t, ht, s, hs, h⟩ => ⟨s, hs, t, ht, fun x hx y hy => @h ⟨x, y⟩ ⟨hx, hy⟩⟩
  exact fun ⟨s, hs, t, ht, h⟩ => ⟨t, ht, s, hs, fun ⟨x, y⟩ ⟨hx, hy⟩ => h x hx y hy⟩
#align filter.map_prod Filter.map_prod

/- warning: filter.prod_eq -> Filter.prod_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} β}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β f g) (Filter.seq.{u2, max u1 u2} β (Prod.{u1, u2} α β) (Filter.map.{u1, max u1 u2} α (β -> (Prod.{u1, u2} α β)) (Prod.mk.{u1, u2} α β) f) g)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} β}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β f g) (Filter.seq.{u1, max u2 u1} β (Prod.{u2, u1} α β) (Filter.map.{u2, max u1 u2} α (β -> (Prod.{u2, u1} α β)) (Prod.mk.{u2, u1} α β) f) g)
Case conversion may be inaccurate. Consider using '#align filter.prod_eq Filter.prod_eqₓ'. -/
theorem prod_eq {f : Filter α} {g : Filter β} : f ×ᶠ g = (f.map Prod.mk).seq g :=
  by
  have h := f.map_prod id g
  rwa [map_id] at h
#align filter.prod_eq Filter.prod_eq

/- warning: filter.prod_inf_prod -> Filter.prod_inf_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f₁ : Filter.{u1} α} {f₂ : Filter.{u1} α} {g₁ : Filter.{u2} β} {g₂ : Filter.{u2} β}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Inf.inf.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.hasInf.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β f₁ g₁) (Filter.prod.{u1, u2} α β f₂ g₂)) (Filter.prod.{u1, u2} α β (Inf.inf.{u1} (Filter.{u1} α) (Filter.hasInf.{u1} α) f₁ f₂) (Inf.inf.{u2} (Filter.{u2} β) (Filter.hasInf.{u2} β) g₁ g₂))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f₁ : Filter.{u2} α} {f₂ : Filter.{u2} α} {g₁ : Filter.{u1} β} {g₂ : Filter.{u1} β}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Inf.inf.{max u1 u2} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.instInfFilter.{max u2 u1} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β f₁ g₁) (Filter.prod.{u2, u1} α β f₂ g₂)) (Filter.prod.{u2, u1} α β (Inf.inf.{u2} (Filter.{u2} α) (Filter.instInfFilter.{u2} α) f₁ f₂) (Inf.inf.{u1} (Filter.{u1} β) (Filter.instInfFilter.{u1} β) g₁ g₂))
Case conversion may be inaccurate. Consider using '#align filter.prod_inf_prod Filter.prod_inf_prodₓ'. -/
theorem prod_inf_prod {f₁ f₂ : Filter α} {g₁ g₂ : Filter β} :
    (f₁ ×ᶠ g₁) ⊓ (f₂ ×ᶠ g₂) = f₁ ⊓ f₂ ×ᶠ g₁ ⊓ g₂ := by
  simp only [Filter.prod, comap_inf, inf_comm, inf_assoc, inf_left_comm]
#align filter.prod_inf_prod Filter.prod_inf_prod

/- warning: filter.prod_bot -> Filter.prod_bot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β f (Bot.bot.{u2} (Filter.{u2} β) (CompleteLattice.toHasBot.{u2} (Filter.{u2} β) (Filter.completeLattice.{u2} β)))) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (CompleteLattice.toHasBot.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.completeLattice.{max u1 u2} (Prod.{u1, u2} α β))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β f (Bot.bot.{u1} (Filter.{u1} β) (CompleteLattice.toBot.{u1} (Filter.{u1} β) (Filter.instCompleteLatticeFilter.{u1} β)))) (Bot.bot.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (CompleteLattice.toBot.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.instCompleteLatticeFilter.{max u2 u1} (Prod.{u2, u1} α β))))
Case conversion may be inaccurate. Consider using '#align filter.prod_bot Filter.prod_botₓ'. -/
@[simp]
theorem prod_bot {f : Filter α} : f ×ᶠ (⊥ : Filter β) = ⊥ := by simp [Filter.prod]
#align filter.prod_bot Filter.prod_bot

/- warning: filter.bot_prod -> Filter.bot_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {g : Filter.{u2} β}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toHasBot.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))) g) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (CompleteLattice.toHasBot.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.completeLattice.{max u1 u2} (Prod.{u1, u2} α β))))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} {g : Filter.{u2} β}, Eq.{max (succ u1) (succ u2)} (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toBot.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))) g) (Bot.bot.{max u1 u2} (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (CompleteLattice.toBot.{max u1 u2} (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (Filter.instCompleteLatticeFilter.{max u1 u2} (Prod.{u1, u2} α β))))
Case conversion may be inaccurate. Consider using '#align filter.bot_prod Filter.bot_prodₓ'. -/
@[simp]
theorem bot_prod {g : Filter β} : (⊥ : Filter α) ×ᶠ g = ⊥ := by simp [Filter.prod]
#align filter.bot_prod Filter.bot_prod

/- warning: filter.prod_principal_principal -> Filter.prod_principal_principal is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {s : Set.{u1} α} {t : Set.{u2} β}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β (Filter.principal.{u1} α s) (Filter.principal.{u2} β t)) (Filter.principal.{max u1 u2} (Prod.{u1, u2} α β) (Set.prod.{u1, u2} α β s t))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {s : Set.{u2} α} {t : Set.{u1} β}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β (Filter.principal.{u2} α s) (Filter.principal.{u1} β t)) (Filter.principal.{max u1 u2} (Prod.{u2, u1} α β) (Set.prod.{u2, u1} α β s t))
Case conversion may be inaccurate. Consider using '#align filter.prod_principal_principal Filter.prod_principal_principalₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
@[simp]
theorem prod_principal_principal {s : Set α} {t : Set β} : 𝓟 s ×ᶠ 𝓟 t = 𝓟 (s ×ˢ t) := by
  simp only [Filter.prod, comap_principal, principal_eq_iff_eq, comap_principal, inf_principal] <;>
    rfl
#align filter.prod_principal_principal Filter.prod_principal_principal

#print Filter.pure_prod /-
@[simp]
theorem pure_prod {a : α} {f : Filter β} : pure a ×ᶠ f = map (Prod.mk a) f := by
  rw [prod_eq, map_pure, pure_seq_eq_map]
#align filter.pure_prod Filter.pure_prod
-/

/- warning: filter.map_pure_prod -> Filter.map_pure_prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} (f : α -> β -> γ) (a : α) (B : Filter.{u2} β), Eq.{succ u3} (Filter.{u3} γ) (Filter.map.{max u1 u2, u3} (Prod.{u1, u2} α β) γ (Function.uncurry.{u1, u2, u3} α β γ f) (Filter.prod.{u1, u2} α β (Pure.pure.{u1, u1} Filter.{u1} Filter.hasPure.{u1} α a) B)) (Filter.map.{u2, u3} β γ (f a) B)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u3}} {γ : Type.{u2}} (f : α -> β -> γ) (a : α) (B : Filter.{u3} β), Eq.{succ u2} (Filter.{u2} γ) (Filter.map.{max u3 u1, u2} (Prod.{u1, u3} α β) γ (Function.uncurry.{u1, u3, u2} α β γ f) (Filter.prod.{u1, u3} α β (Pure.pure.{u1, u1} Filter.{u1} Filter.instPureFilter.{u1} α a) B)) (Filter.map.{u3, u2} β γ (f a) B)
Case conversion may be inaccurate. Consider using '#align filter.map_pure_prod Filter.map_pure_prodₓ'. -/
theorem map_pure_prod (f : α → β → γ) (a : α) (B : Filter β) :
    Filter.map (Function.uncurry f) (pure a ×ᶠ B) = Filter.map (f a) B :=
  by
  rw [Filter.pure_prod]
  rfl
#align filter.map_pure_prod Filter.map_pure_prod

/- warning: filter.prod_pure -> Filter.prod_pure is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {b : β}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β f (Pure.pure.{u2, u2} Filter.{u2} Filter.hasPure.{u2} β b)) (Filter.map.{u1, max u1 u2} α (Prod.{u1, u2} α β) (fun (a : α) => Prod.mk.{u1, u2} α β a b) f)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {b : β}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β f (Pure.pure.{u1, u1} Filter.{u1} Filter.instPureFilter.{u1} β b)) (Filter.map.{u2, max u1 u2} α (Prod.{u2, u1} α β) (fun (a : α) => Prod.mk.{u2, u1} α β a b) f)
Case conversion may be inaccurate. Consider using '#align filter.prod_pure Filter.prod_pureₓ'. -/
@[simp]
theorem prod_pure {f : Filter α} {b : β} : f ×ᶠ pure b = map (fun a => (a, b)) f := by
  rw [prod_eq, seq_pure, map_map]
#align filter.prod_pure Filter.prod_pure

/- warning: filter.prod_pure_pure -> Filter.prod_pure_pure is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {a : α} {b : β}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β (Pure.pure.{u1, u1} Filter.{u1} Filter.hasPure.{u1} α a) (Pure.pure.{u2, u2} Filter.{u2} Filter.hasPure.{u2} β b)) (Pure.pure.{max u1 u2, max u1 u2} Filter.{max u1 u2} Filter.hasPure.{max u1 u2} (Prod.{u1, u2} α β) (Prod.mk.{u1, u2} α β a b))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {a : α} {b : β}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β (Pure.pure.{u2, u2} Filter.{u2} Filter.instPureFilter.{u2} α a) (Pure.pure.{u1, u1} Filter.{u1} Filter.instPureFilter.{u1} β b)) (Pure.pure.{max u1 u2, max u2 u1} Filter.{max u1 u2} Filter.instPureFilter.{max u2 u1} (Prod.{u2, u1} α β) (Prod.mk.{u2, u1} α β a b))
Case conversion may be inaccurate. Consider using '#align filter.prod_pure_pure Filter.prod_pure_pureₓ'. -/
theorem prod_pure_pure {a : α} {b : β} : pure a ×ᶠ pure b = pure (a, b) := by simp
#align filter.prod_pure_pure Filter.prod_pure_pure

/- warning: filter.prod_eq_bot -> Filter.prod_eq_bot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} β}, Iff (Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.prod.{u1, u2} α β f g) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (CompleteLattice.toHasBot.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.completeLattice.{max u1 u2} (Prod.{u1, u2} α β))))) (Or (Eq.{succ u1} (Filter.{u1} α) f (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toHasBot.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α)))) (Eq.{succ u2} (Filter.{u2} β) g (Bot.bot.{u2} (Filter.{u2} β) (CompleteLattice.toHasBot.{u2} (Filter.{u2} β) (Filter.completeLattice.{u2} β)))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} β}, Iff (Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.prod.{u2, u1} α β f g) (Bot.bot.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (CompleteLattice.toBot.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.instCompleteLatticeFilter.{max u2 u1} (Prod.{u2, u1} α β))))) (Or (Eq.{succ u2} (Filter.{u2} α) f (Bot.bot.{u2} (Filter.{u2} α) (CompleteLattice.toBot.{u2} (Filter.{u2} α) (Filter.instCompleteLatticeFilter.{u2} α)))) (Eq.{succ u1} (Filter.{u1} β) g (Bot.bot.{u1} (Filter.{u1} β) (CompleteLattice.toBot.{u1} (Filter.{u1} β) (Filter.instCompleteLatticeFilter.{u1} β)))))
Case conversion may be inaccurate. Consider using '#align filter.prod_eq_bot Filter.prod_eq_botₓ'. -/
theorem prod_eq_bot {f : Filter α} {g : Filter β} : f ×ᶠ g = ⊥ ↔ f = ⊥ ∨ g = ⊥ :=
  by
  constructor
  · intro h
    rcases mem_prod_iff.1 (empty_mem_iff_bot.2 h) with ⟨s, hs, t, ht, hst⟩
    rw [subset_empty_iff, Set.prod_eq_empty_iff] at hst
    cases' hst with s_eq t_eq
    · left
      exact empty_mem_iff_bot.1 (s_eq ▸ hs)
    · right
      exact empty_mem_iff_bot.1 (t_eq ▸ ht)
  · rintro (rfl | rfl)
    exact bot_prod
    exact prod_bot
#align filter.prod_eq_bot Filter.prod_eq_bot

/- warning: filter.prod_ne_bot -> Filter.prod_neBot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} β}, Iff (Filter.NeBot.{max u1 u2} (Prod.{u1, u2} α β) (Filter.prod.{u1, u2} α β f g)) (And (Filter.NeBot.{u1} α f) (Filter.NeBot.{u2} β g))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} β}, Iff (Filter.NeBot.{max u1 u2} (Prod.{u2, u1} α β) (Filter.prod.{u2, u1} α β f g)) (And (Filter.NeBot.{u2} α f) (Filter.NeBot.{u1} β g))
Case conversion may be inaccurate. Consider using '#align filter.prod_ne_bot Filter.prod_neBotₓ'. -/
theorem prod_neBot {f : Filter α} {g : Filter β} : NeBot (f ×ᶠ g) ↔ NeBot f ∧ NeBot g := by
  simp only [ne_bot_iff, Ne, prod_eq_bot, not_or]
#align filter.prod_ne_bot Filter.prod_neBot

/- warning: filter.ne_bot.prod -> Filter.NeBot.prod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} β}, (Filter.NeBot.{u1} α f) -> (Filter.NeBot.{u2} β g) -> (Filter.NeBot.{max u1 u2} (Prod.{u1, u2} α β) (Filter.prod.{u1, u2} α β f g))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} β}, (Filter.NeBot.{u2} α f) -> (Filter.NeBot.{u1} β g) -> (Filter.NeBot.{max u1 u2} (Prod.{u2, u1} α β) (Filter.prod.{u2, u1} α β f g))
Case conversion may be inaccurate. Consider using '#align filter.ne_bot.prod Filter.NeBot.prodₓ'. -/
theorem NeBot.prod {f : Filter α} {g : Filter β} (hf : NeBot f) (hg : NeBot g) : NeBot (f ×ᶠ g) :=
  prod_neBot.2 ⟨hf, hg⟩
#align filter.ne_bot.prod Filter.NeBot.prod

#print Filter.prod_neBot' /-
instance prod_neBot' {f : Filter α} {g : Filter β} [hf : NeBot f] [hg : NeBot g] : NeBot (f ×ᶠ g) :=
  hf.Prod hg
#align filter.prod_ne_bot' Filter.prod_neBot'
-/

/- warning: filter.tendsto_prod_iff -> Filter.tendsto_prod_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {f : (Prod.{u1, u2} α β) -> γ} {x : Filter.{u1} α} {y : Filter.{u2} β} {z : Filter.{u3} γ}, Iff (Filter.Tendsto.{max u1 u2, u3} (Prod.{u1, u2} α β) γ f (Filter.prod.{u1, u2} α β x y) z) (forall (W : Set.{u3} γ), (Membership.Mem.{u3, u3} (Set.{u3} γ) (Filter.{u3} γ) (Filter.hasMem.{u3} γ) W z) -> (Exists.{succ u1} (Set.{u1} α) (fun (U : Set.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) U x) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) U x) => Exists.{succ u2} (Set.{u2} β) (fun (V : Set.{u2} β) => Exists.{0} (Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) V y) (fun (H : Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) V y) => forall (x : α) (y : β), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) x U) -> (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) y V) -> (Membership.Mem.{u3, u3} γ (Set.{u3} γ) (Set.hasMem.{u3} γ) (f (Prod.mk.{u1, u2} α β x y)) W)))))))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} {f : (Prod.{u3, u2} α β) -> γ} {x : Filter.{u3} α} {y : Filter.{u2} β} {z : Filter.{u1} γ}, Iff (Filter.Tendsto.{max u3 u2, u1} (Prod.{u3, u2} α β) γ f (Filter.prod.{u3, u2} α β x y) z) (forall (W : Set.{u1} γ), (Membership.mem.{u1, u1} (Set.{u1} γ) (Filter.{u1} γ) (instMembershipSetFilter.{u1} γ) W z) -> (Exists.{succ u3} (Set.{u3} α) (fun (U : Set.{u3} α) => And (Membership.mem.{u3, u3} (Set.{u3} α) (Filter.{u3} α) (instMembershipSetFilter.{u3} α) U x) (Exists.{succ u2} (Set.{u2} β) (fun (V : Set.{u2} β) => And (Membership.mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (instMembershipSetFilter.{u2} β) V y) (forall (x : α) (y : β), (Membership.mem.{u3, u3} α (Set.{u3} α) (Set.instMembershipSet.{u3} α) x U) -> (Membership.mem.{u2, u2} β (Set.{u2} β) (Set.instMembershipSet.{u2} β) y V) -> (Membership.mem.{u1, u1} γ (Set.{u1} γ) (Set.instMembershipSet.{u1} γ) (f (Prod.mk.{u3, u2} α β x y)) W)))))))
Case conversion may be inaccurate. Consider using '#align filter.tendsto_prod_iff Filter.tendsto_prod_iffₓ'. -/
theorem tendsto_prod_iff {f : α × β → γ} {x : Filter α} {y : Filter β} {z : Filter γ} :
    Filter.Tendsto f (x ×ᶠ y) z ↔ ∀ W ∈ z, ∃ U ∈ x, ∃ V ∈ y, ∀ x y, x ∈ U → y ∈ V → f (x, y) ∈ W :=
  by simp only [tendsto_def, mem_prod_iff, prod_sub_preimage_iff, exists_prop, iff_self_iff]
#align filter.tendsto_prod_iff Filter.tendsto_prod_iff

/- warning: filter.tendsto_prod_iff' -> Filter.tendsto_prod_iff' is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {f : Filter.{u1} α} {g : Filter.{u2} β} {g' : Filter.{u3} γ} {s : α -> (Prod.{u2, u3} β γ)}, Iff (Filter.Tendsto.{u1, max u2 u3} α (Prod.{u2, u3} β γ) s f (Filter.prod.{u2, u3} β γ g g')) (And (Filter.Tendsto.{u1, u2} α β (fun (n : α) => Prod.fst.{u2, u3} β γ (s n)) f g) (Filter.Tendsto.{u1, u3} α γ (fun (n : α) => Prod.snd.{u2, u3} β γ (s n)) f g'))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} {f : Filter.{u3} α} {g : Filter.{u2} β} {g' : Filter.{u1} γ} {s : α -> (Prod.{u2, u1} β γ)}, Iff (Filter.Tendsto.{u3, max u2 u1} α (Prod.{u2, u1} β γ) s f (Filter.prod.{u2, u1} β γ g g')) (And (Filter.Tendsto.{u3, u2} α β (fun (n : α) => Prod.fst.{u2, u1} β γ (s n)) f g) (Filter.Tendsto.{u3, u1} α γ (fun (n : α) => Prod.snd.{u2, u1} β γ (s n)) f g'))
Case conversion may be inaccurate. Consider using '#align filter.tendsto_prod_iff' Filter.tendsto_prod_iff'ₓ'. -/
theorem tendsto_prod_iff' {f : Filter α} {g : Filter β} {g' : Filter γ} {s : α → β × γ} :
    Tendsto s f (g ×ᶠ g') ↔ Tendsto (fun n => (s n).1) f g ∧ Tendsto (fun n => (s n).2) f g' :=
  by
  unfold Filter.prod
  simp only [tendsto_inf, tendsto_comap_iff, iff_self_iff]
#align filter.tendsto_prod_iff' Filter.tendsto_prod_iff'

end Prod

/-! ### Coproducts of filters -/

section Coprod

variable {f : Filter α} {g : Filter β}

#print Filter.coprod /-
/-- Coproduct of filters. -/
protected def coprod (f : Filter α) (g : Filter β) : Filter (α × β) :=
  f.comap Prod.fst ⊔ g.comap Prod.snd
#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}} {s : Set.{max u1 u2} (Prod.{u1, u2} α β)} {f : Filter.{u1} α} {g : Filter.{u2} β}, Iff (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.hasMem.{max u1 u2} (Prod.{u1, u2} α β)) s (Filter.coprod.{u1, u2} α β f g)) (And (Exists.{succ u1} (Set.{u1} α) (fun (t₁ : Set.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t₁ f) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) t₁ f) => HasSubset.Subset.{max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Set.hasSubset.{max u1 u2} (Prod.{u1, u2} α β)) (Set.preimage.{max u1 u2, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) t₁) s))) (Exists.{succ u2} (Set.{u2} β) (fun (t₂ : Set.{u2} β) => Exists.{0} (Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) t₂ g) (fun (H : Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) t₂ g) => HasSubset.Subset.{max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Set.hasSubset.{max u1 u2} (Prod.{u1, u2} α β)) (Set.preimage.{max u1 u2, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) t₂) s))))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} {s : Set.{max u2 u1} (Prod.{u1, u2} α β)} {f : Filter.{u1} α} {g : Filter.{u2} β}, Iff (Membership.mem.{max u1 u2, max u1 u2} (Set.{max u2 u1} (Prod.{u1, u2} α β)) (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (instMembershipSetFilter.{max u1 u2} (Prod.{u1, u2} α β)) s (Filter.coprod.{u1, u2} α β f g)) (And (Exists.{succ u1} (Set.{u1} α) (fun (t₁ : Set.{u1} α) => And (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) t₁ f) (HasSubset.Subset.{max u2 u1} (Set.{max u2 u1} (Prod.{u1, u2} α β)) (Set.instHasSubsetSet.{max u1 u2} (Prod.{u1, u2} α β)) (Set.preimage.{max u2 u1, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) t₁) s))) (Exists.{succ u2} (Set.{u2} β) (fun (t₂ : Set.{u2} β) => And (Membership.mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (instMembershipSetFilter.{u2} β) t₂ g) (HasSubset.Subset.{max u2 u1} (Set.{max u2 u1} (Prod.{u1, u2} α β)) (Set.instHasSubsetSet.{max u1 u2} (Prod.{u1, u2} α β)) (Set.preimage.{max u2 u1, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) t₂) s))))
Case conversion may be inaccurate. Consider using '#align filter.mem_coprod_iff Filter.mem_coprod_iffₓ'. -/
theorem mem_coprod_iff {s : Set (α × β)} {f : Filter α} {g : Filter β} :
    s ∈ f.coprod g ↔ (∃ t₁ ∈ f, Prod.fst ⁻¹' t₁ ⊆ s) ∧ ∃ t₂ ∈ g, Prod.snd ⁻¹' t₂ ⊆ s := by
  simp [Filter.coprod]
#align filter.mem_coprod_iff Filter.mem_coprod_iff

/- warning: filter.bot_coprod -> Filter.bot_coprod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (l : Filter.{u2} β), Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.coprod.{u1, u2} α β (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toHasBot.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))) l) (Filter.comap.{max u1 u2, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) l)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} (l : Filter.{u2} β), Eq.{max (succ u1) (succ u2)} (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (Filter.coprod.{u1, u2} α β (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toBot.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))) l) (Filter.comap.{max u2 u1, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) l)
Case conversion may be inaccurate. Consider using '#align filter.bot_coprod Filter.bot_coprodₓ'. -/
@[simp]
theorem bot_coprod (l : Filter β) : (⊥ : Filter α).coprod l = comap Prod.snd l := by
  simp [Filter.coprod]
#align filter.bot_coprod Filter.bot_coprod

/- warning: filter.coprod_bot -> Filter.coprod_bot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (l : Filter.{u1} α), Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.coprod.{u1, u2} α β l (Bot.bot.{u2} (Filter.{u2} β) (CompleteLattice.toHasBot.{u2} (Filter.{u2} β) (Filter.completeLattice.{u2} β)))) (Filter.comap.{max u1 u2, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) l)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (l : Filter.{u2} α), Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.coprod.{u2, u1} α β l (Bot.bot.{u1} (Filter.{u1} β) (CompleteLattice.toBot.{u1} (Filter.{u1} β) (Filter.instCompleteLatticeFilter.{u1} β)))) (Filter.comap.{max u1 u2, u2} (Prod.{u2, u1} α β) α (Prod.fst.{u2, u1} α β) l)
Case conversion may be inaccurate. Consider using '#align filter.coprod_bot Filter.coprod_botₓ'. -/
@[simp]
theorem coprod_bot (l : Filter α) : l.coprod (⊥ : Filter β) = comap Prod.fst l := by
  simp [Filter.coprod]
#align filter.coprod_bot Filter.coprod_bot

/- warning: filter.bot_coprod_bot -> Filter.bot_coprod_bot is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}}, Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.coprod.{u1, u2} α β (Bot.bot.{u1} (Filter.{u1} α) (CompleteLattice.toHasBot.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))) (Bot.bot.{u2} (Filter.{u2} β) (CompleteLattice.toHasBot.{u2} (Filter.{u2} β) (Filter.completeLattice.{u2} β)))) (Bot.bot.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (CompleteLattice.toHasBot.{max u1 u2} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.completeLattice.{max u1 u2} (Prod.{u1, u2} α β))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}}, Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.coprod.{u2, u1} α β (Bot.bot.{u2} (Filter.{u2} α) (CompleteLattice.toBot.{u2} (Filter.{u2} α) (Filter.instCompleteLatticeFilter.{u2} α))) (Bot.bot.{u1} (Filter.{u1} β) (CompleteLattice.toBot.{u1} (Filter.{u1} β) (Filter.instCompleteLatticeFilter.{u1} β)))) (Bot.bot.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (CompleteLattice.toBot.{max u2 u1} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.instCompleteLatticeFilter.{max u2 u1} (Prod.{u2, u1} α β))))
Case conversion may be inaccurate. Consider using '#align filter.bot_coprod_bot Filter.bot_coprod_botₓ'. -/
theorem bot_coprod_bot : (⊥ : Filter α).coprod (⊥ : Filter β) = ⊥ := by simp
#align filter.bot_coprod_bot Filter.bot_coprod_bot

/- warning: filter.compl_mem_coprod -> Filter.compl_mem_coprod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {s : Set.{max u1 u2} (Prod.{u1, u2} α β)} {la : Filter.{u1} α} {lb : Filter.{u2} β}, Iff (Membership.Mem.{max u1 u2, max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.hasMem.{max u1 u2} (Prod.{u1, u2} α β)) (HasCompl.compl.{max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (BooleanAlgebra.toHasCompl.{max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Set.booleanAlgebra.{max u1 u2} (Prod.{u1, u2} α β))) s) (Filter.coprod.{u1, u2} α β la lb)) (And (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)) (Set.image.{max u1 u2, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) s)) la) (Membership.Mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (Filter.hasMem.{u2} β) (HasCompl.compl.{u2} (Set.{u2} β) (BooleanAlgebra.toHasCompl.{u2} (Set.{u2} β) (Set.booleanAlgebra.{u2} β)) (Set.image.{max u1 u2, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) s)) lb))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} {s : Set.{max u2 u1} (Prod.{u1, u2} α β)} {la : Filter.{u1} α} {lb : Filter.{u2} β}, Iff (Membership.mem.{max u1 u2, max u1 u2} (Set.{max u2 u1} (Prod.{u1, u2} α β)) (Filter.{max u2 u1} (Prod.{u1, u2} α β)) (instMembershipSetFilter.{max u1 u2} (Prod.{u1, u2} α β)) (HasCompl.compl.{max u1 u2} (Set.{max u2 u1} (Prod.{u1, u2} α β)) (BooleanAlgebra.toHasCompl.{max u1 u2} (Set.{max u2 u1} (Prod.{u1, u2} α β)) (Set.instBooleanAlgebraSet.{max u1 u2} (Prod.{u1, u2} α β))) s) (Filter.coprod.{u1, u2} α β la lb)) (And (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)) (Set.image.{max u2 u1, u1} (Prod.{u1, u2} α β) α (Prod.fst.{u1, u2} α β) s)) la) (Membership.mem.{u2, u2} (Set.{u2} β) (Filter.{u2} β) (instMembershipSetFilter.{u2} β) (HasCompl.compl.{u2} (Set.{u2} β) (BooleanAlgebra.toHasCompl.{u2} (Set.{u2} β) (Set.instBooleanAlgebraSet.{u2} β)) (Set.image.{max u2 u1, u2} (Prod.{u1, u2} α β) β (Prod.snd.{u1, u2} α β) s)) lb))
Case conversion may be inaccurate. Consider using '#align filter.compl_mem_coprod Filter.compl_mem_coprodₓ'. -/
theorem compl_mem_coprod {s : Set (α × β)} {la : Filter α} {lb : Filter β} :
    sᶜ ∈ la.coprod lb ↔ (Prod.fst '' s)ᶜ ∈ la ∧ (Prod.snd '' s)ᶜ ∈ lb := by
  simp only [Filter.coprod, mem_sup, compl_mem_comap]
#align filter.compl_mem_coprod Filter.compl_mem_coprod

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

/- warning: filter.coprod_ne_bot_iff -> Filter.coprod_neBot_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} β}, Iff (Filter.NeBot.{max u1 u2} (Prod.{u1, u2} α β) (Filter.coprod.{u1, u2} α β f g)) (Or (And (Filter.NeBot.{u1} α f) (Nonempty.{succ u2} β)) (And (Nonempty.{succ u1} α) (Filter.NeBot.{u2} β g)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} β}, Iff (Filter.NeBot.{max u2 u1} (Prod.{u2, u1} α β) (Filter.coprod.{u2, u1} α β f g)) (Or (And (Filter.NeBot.{u2} α f) (Nonempty.{succ u1} β)) (And (Nonempty.{succ u2} α) (Filter.NeBot.{u1} β g)))
Case conversion may be inaccurate. Consider using '#align filter.coprod_ne_bot_iff Filter.coprod_neBot_iffₓ'. -/
theorem coprod_neBot_iff : (f.coprod g).ne_bot ↔ f.ne_bot ∧ Nonempty β ∨ Nonempty α ∧ g.ne_bot := by
  simp [Filter.coprod]
#align filter.coprod_ne_bot_iff Filter.coprod_neBot_iff

/- warning: filter.coprod_ne_bot_left -> Filter.coprod_neBot_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : Filter.{u1} α} {g : Filter.{u2} β} [_inst_1 : Filter.NeBot.{u1} α f] [_inst_2 : Nonempty.{succ u2} β], Filter.NeBot.{max u1 u2} (Prod.{u1, u2} α β) (Filter.coprod.{u1, u2} α β f g)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : Filter.{u2} α} {g : Filter.{u1} β} [_inst_1 : Filter.NeBot.{u2} α f] [_inst_2 : Nonempty.{succ u1} β], Filter.NeBot.{max u2 u1} (Prod.{u2, u1} α β) (Filter.coprod.{u2, u1} α β f g)
Case conversion may be inaccurate. Consider using '#align filter.coprod_ne_bot_left Filter.coprod_neBot_leftₓ'. -/
@[instance]
theorem coprod_neBot_left [NeBot f] [Nonempty β] : (f.coprod g).ne_bot :=
  coprod_neBot_iff.2 (Or.inl ⟨‹_›, ‹_›⟩)
#align filter.coprod_ne_bot_left Filter.coprod_neBot_left

#print Filter.coprod_neBot_right /-
@[instance]
theorem coprod_neBot_right [NeBot g] [Nonempty α] : (f.coprod g).ne_bot :=
  coprod_neBot_iff.2 (Or.inr ⟨‹_›, ‹_›⟩)
#align filter.coprod_ne_bot_right Filter.coprod_neBot_right
-/

/- warning: filter.principal_coprod_principal -> Filter.principal_coprod_principal is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (s : Set.{u1} α) (t : Set.{u2} β), Eq.{succ (max u1 u2)} (Filter.{max u1 u2} (Prod.{u1, u2} α β)) (Filter.coprod.{u1, u2} α β (Filter.principal.{u1} α s) (Filter.principal.{u2} β t)) (Filter.principal.{max u1 u2} (Prod.{u1, u2} α β) (HasCompl.compl.{max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (BooleanAlgebra.toHasCompl.{max u1 u2} (Set.{max u1 u2} (Prod.{u1, u2} α β)) (Set.booleanAlgebra.{max u1 u2} (Prod.{u1, u2} α β))) (Set.prod.{u1, u2} α β (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)) s) (HasCompl.compl.{u2} (Set.{u2} β) (BooleanAlgebra.toHasCompl.{u2} (Set.{u2} β) (Set.booleanAlgebra.{u2} β)) t))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (s : Set.{u2} α) (t : Set.{u1} β), Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} α β)) (Filter.coprod.{u2, u1} α β (Filter.principal.{u2} α s) (Filter.principal.{u1} β t)) (Filter.principal.{max u1 u2} (Prod.{u2, u1} α β) (HasCompl.compl.{max u1 u2} (Set.{max u1 u2} (Prod.{u2, u1} α β)) (BooleanAlgebra.toHasCompl.{max u2 u1} (Set.{max u1 u2} (Prod.{u2, u1} α β)) (Set.instBooleanAlgebraSet.{max u2 u1} (Prod.{u2, u1} α β))) (Set.prod.{u2, u1} α β (HasCompl.compl.{u2} (Set.{u2} α) (BooleanAlgebra.toHasCompl.{u2} (Set.{u2} α) (Set.instBooleanAlgebraSet.{u2} α)) s) (HasCompl.compl.{u1} (Set.{u1} β) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} β) (Set.instBooleanAlgebraSet.{u1} β)) t))))
Case conversion may be inaccurate. Consider using '#align filter.principal_coprod_principal Filter.principal_coprod_principalₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
theorem principal_coprod_principal (s : Set α) (t : Set β) : (𝓟 s).coprod (𝓟 t) = 𝓟 ((sᶜ ×ˢ tᶜ)ᶜ) :=
  by
  rw [Filter.coprod, comap_principal, comap_principal, sup_principal, Set.prod_eq, compl_inter,
    preimage_compl, preimage_compl, compl_compl, compl_compl]
#align filter.principal_coprod_principal Filter.principal_coprod_principal

/- warning: filter.map_prod_map_coprod_le -> Filter.map_prod_map_coprod_le is a dubious translation:
lean 3 declaration is
  forall {α₁ : Type.{u1}} {α₂ : Type.{u2}} {β₁ : Type.{u3}} {β₂ : Type.{u4}} {f₁ : Filter.{u1} α₁} {f₂ : Filter.{u2} α₂} {m₁ : α₁ -> β₁} {m₂ : α₂ -> β₂}, LE.le.{max u3 u4} (Filter.{max u3 u4} (Prod.{u3, u4} β₁ β₂)) (Preorder.toLE.{max u3 u4} (Filter.{max u3 u4} (Prod.{u3, u4} β₁ β₂)) (PartialOrder.toPreorder.{max u3 u4} (Filter.{max u3 u4} (Prod.{u3, u4} β₁ β₂)) (Filter.partialOrder.{max u3 u4} (Prod.{u3, u4} β₁ β₂)))) (Filter.map.{max u1 u2, max u3 u4} (Prod.{u1, u2} α₁ α₂) (Prod.{u3, u4} β₁ β₂) (Prod.map.{u1, u3, u2, u4} α₁ β₁ α₂ β₂ m₁ m₂) (Filter.coprod.{u1, u2} α₁ α₂ f₁ f₂)) (Filter.coprod.{u3, u4} β₁ β₂ (Filter.map.{u1, u3} α₁ β₁ m₁ f₁) (Filter.map.{u2, u4} α₂ β₂ m₂ f₂))
but is expected to have type
  forall {α₁ : Type.{u1}} {α₂ : Type.{u2}} {β₁ : Type.{u3}} {β₂ : Type.{u4}} {f₁ : Filter.{u1} α₁} {f₂ : Filter.{u2} α₂} {m₁ : α₁ -> β₁} {m₂ : α₂ -> β₂}, LE.le.{max u3 u4} (Filter.{max u4 u3} (Prod.{u3, u4} β₁ β₂)) (Preorder.toLE.{max u3 u4} (Filter.{max u4 u3} (Prod.{u3, u4} β₁ β₂)) (PartialOrder.toPreorder.{max u3 u4} (Filter.{max u4 u3} (Prod.{u3, u4} β₁ β₂)) (Filter.instPartialOrderFilter.{max u3 u4} (Prod.{u3, u4} β₁ β₂)))) (Filter.map.{max u2 u1, max u4 u3} (Prod.{u1, u2} α₁ α₂) (Prod.{u3, u4} β₁ β₂) (Prod.map.{u1, u3, u2, u4} α₁ β₁ α₂ β₂ m₁ m₂) (Filter.coprod.{u1, u2} α₁ α₂ f₁ f₂)) (Filter.coprod.{u3, u4} β₁ β₂ (Filter.map.{u1, u3} α₁ β₁ m₁ f₁) (Filter.map.{u2, u4} α₂ β₂ m₂ f₂))
Case conversion may be inaccurate. Consider using '#align filter.map_prod_map_coprod_le Filter.map_prod_map_coprod_leₓ'. -/
-- this inequality can be strict; see `map_const_principal_coprod_map_id_principal` and
-- `map_prod_map_const_id_principal_coprod_principal` below.
theorem map_prod_map_coprod_le.{u, v, w, x} {α₁ : Type u} {α₂ : Type v} {β₁ : Type w} {β₂ : Type x}
    {f₁ : Filter α₁} {f₂ : Filter α₂} {m₁ : α₁ → β₁} {m₂ : α₂ → β₂} :
    map (Prod.map m₁ m₂) (f₁.coprod f₂) ≤ (map m₁ f₁).coprod (map m₂ f₂) :=
  by
  intro s
  simp only [mem_map, mem_coprod_iff]
  rintro ⟨⟨u₁, hu₁, h₁⟩, u₂, hu₂, h₂⟩
  refine' ⟨⟨m₁ ⁻¹' u₁, hu₁, fun _ hx => h₁ _⟩, ⟨m₂ ⁻¹' u₂, hu₂, fun _ hx => h₂ _⟩⟩ <;> convert hx
#align filter.map_prod_map_coprod_le Filter.map_prod_map_coprod_le

/- warning: filter.map_const_principal_coprod_map_id_principal -> Filter.map_const_principal_coprod_map_id_principal is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {ι : Type.{u3}} (a : α) (b : β) (i : ι), Eq.{succ (max u2 u3)} (Filter.{max u2 u3} (Prod.{u2, u3} β ι)) (Filter.coprod.{u2, u3} β ι (Filter.map.{u1, u2} α β (fun (_x : α) => b) (Filter.principal.{u1} α (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) a))) (Filter.map.{u3, u3} ι ι (id.{succ u3} ι) (Filter.principal.{u3} ι (Singleton.singleton.{u3, u3} ι (Set.{u3} ι) (Set.hasSingleton.{u3} ι) i)))) (Filter.principal.{max u2 u3} (Prod.{u2, u3} β ι) (Union.union.{max u2 u3} (Set.{max u2 u3} (Prod.{u2, u3} β ι)) (Set.hasUnion.{max u2 u3} (Prod.{u2, u3} β ι)) (Set.prod.{u2, u3} β ι (Singleton.singleton.{u2, u2} β (Set.{u2} β) (Set.hasSingleton.{u2} β) b) (Set.univ.{u3} ι)) (Set.prod.{u2, u3} β ι (Set.univ.{u2} β) (Singleton.singleton.{u3, u3} ι (Set.{u3} ι) (Set.hasSingleton.{u3} ι) i))))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {ι : Type.{u1}} (a : α) (b : β) (i : ι), Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} β ι)) (Filter.coprod.{u2, u1} β ι (Filter.map.{u3, u2} α β (fun (_x : α) => b) (Filter.principal.{u3} α (Singleton.singleton.{u3, u3} α (Set.{u3} α) (Set.instSingletonSet.{u3} α) a))) (Filter.map.{u1, u1} ι ι (id.{succ u1} ι) (Filter.principal.{u1} ι (Singleton.singleton.{u1, u1} ι (Set.{u1} ι) (Set.instSingletonSet.{u1} ι) i)))) (Filter.principal.{max u1 u2} (Prod.{u2, u1} β ι) (Union.union.{max u1 u2} (Set.{max u1 u2} (Prod.{u2, u1} β ι)) (Set.instUnionSet.{max u2 u1} (Prod.{u2, u1} β ι)) (Set.prod.{u2, u1} β ι (Singleton.singleton.{u2, u2} β (Set.{u2} β) (Set.instSingletonSet.{u2} β) b) (Set.univ.{u1} ι)) (Set.prod.{u2, u1} β ι (Set.univ.{u2} β) (Singleton.singleton.{u1, u1} ι (Set.{u1} ι) (Set.instSingletonSet.{u1} ι) i))))
Case conversion may be inaccurate. Consider using '#align filter.map_const_principal_coprod_map_id_principal Filter.map_const_principal_coprod_map_id_principalₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- Characterization of the coproduct of the `filter.map`s of two principal filters `𝓟 {a}` and
`𝓟 {i}`, the first under the constant function `λ a, b` and the second under the identity function.
Together with the next lemma, `map_prod_map_const_id_principal_coprod_principal`, this provides an
example showing that the inequality in the lemma `map_prod_map_coprod_le` can be strict. -/
theorem map_const_principal_coprod_map_id_principal {α β ι : Type _} (a : α) (b : β) (i : ι) :
    (map (fun _ : α => b) (𝓟 {a})).coprod (map id (𝓟 {i})) =
      𝓟 (({b} : Set β) ×ˢ univ ∪ univ ×ˢ ({i} : Set ι)) :=
  by
  simp only [map_principal, Filter.coprod, comap_principal, sup_principal, image_singleton,
    image_id, prod_univ, univ_prod]
#align filter.map_const_principal_coprod_map_id_principal Filter.map_const_principal_coprod_map_id_principal

/- warning: filter.map_prod_map_const_id_principal_coprod_principal -> Filter.map_prod_map_const_id_principal_coprod_principal is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {ι : Type.{u3}} (a : α) (b : β) (i : ι), Eq.{succ (max u2 u3)} (Filter.{max u2 u3} (Prod.{u2, u3} β ι)) (Filter.map.{max u1 u3, max u2 u3} (Prod.{u1, u3} α ι) (Prod.{u2, u3} β ι) (Prod.map.{u1, u2, u3, u3} α β ι ι (fun (_x : α) => b) (id.{succ u3} ι)) (Filter.coprod.{u1, u3} α ι (Filter.principal.{u1} α (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) a)) (Filter.principal.{u3} ι (Singleton.singleton.{u3, u3} ι (Set.{u3} ι) (Set.hasSingleton.{u3} ι) i)))) (Filter.principal.{max u2 u3} (Prod.{u2, u3} β ι) (Set.prod.{u2, u3} β ι (Singleton.singleton.{u2, u2} β (Set.{u2} β) (Set.hasSingleton.{u2} β) b) (Set.univ.{u3} ι)))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {ι : Type.{u1}} (a : α) (b : β) (i : ι), Eq.{max (succ u2) (succ u1)} (Filter.{max u1 u2} (Prod.{u2, u1} β ι)) (Filter.map.{max u1 u3, max u1 u2} (Prod.{u3, u1} α ι) (Prod.{u2, u1} β ι) (Prod.map.{u3, u2, u1, u1} α β ι ι (fun (_x : α) => b) (id.{succ u1} ι)) (Filter.coprod.{u3, u1} α ι (Filter.principal.{u3} α (Singleton.singleton.{u3, u3} α (Set.{u3} α) (Set.instSingletonSet.{u3} α) a)) (Filter.principal.{u1} ι (Singleton.singleton.{u1, u1} ι (Set.{u1} ι) (Set.instSingletonSet.{u1} ι) i)))) (Filter.principal.{max u1 u2} (Prod.{u2, u1} β ι) (Set.prod.{u2, u1} β ι (Singleton.singleton.{u2, u2} β (Set.{u2} β) (Set.instSingletonSet.{u2} β) b) (Set.univ.{u1} ι)))
Case conversion may be inaccurate. Consider using '#align filter.map_prod_map_const_id_principal_coprod_principal Filter.map_prod_map_const_id_principal_coprod_principalₓ'. -/
/- ./././Mathport/Syntax/Translate/Expr.lean:177:8: unsupported: ambiguous notation -/
/-- Characterization of the `filter.map` of the coproduct of two principal filters `𝓟 {a}` and
`𝓟 {i}`, under the `prod.map` of two functions, respectively the constant function `λ a, b` and the
identity function.  Together with the previous lemma,
`map_const_principal_coprod_map_id_principal`, this provides an example showing that the inequality
in the lemma `map_prod_map_coprod_le` can be strict. -/
theorem map_prod_map_const_id_principal_coprod_principal {α β ι : Type _} (a : α) (b : β) (i : ι) :
    map (Prod.map (fun _ : α => b) id) ((𝓟 {a}).coprod (𝓟 {i})) =
      𝓟 (({b} : Set β) ×ˢ (univ : Set ι)) :=
  by
  rw [principal_coprod_principal, map_principal]
  congr
  ext ⟨b', i'⟩
  constructor
  · rintro ⟨⟨a'', i''⟩, h₁, h₂, h₃⟩
    simp
  · rintro ⟨h₁, h₂⟩
    use (a, i')
    simpa using h₁.symm
#align filter.map_prod_map_const_id_principal_coprod_principal Filter.map_prod_map_const_id_principal_coprod_principal

/- warning: filter.tendsto.prod_map_coprod -> Filter.Tendsto.prod_map_coprod is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {γ : Type.{u3}} {δ : Type.{u4}} {f : α -> γ} {g : β -> δ} {a : Filter.{u1} α} {b : Filter.{u2} β} {c : Filter.{u3} γ} {d : Filter.{u4} δ}, (Filter.Tendsto.{u1, u3} α γ f a c) -> (Filter.Tendsto.{u2, u4} β δ g b d) -> (Filter.Tendsto.{max u1 u2, max u3 u4} (Prod.{u1, u2} α β) (Prod.{u3, u4} γ δ) (Prod.map.{u1, u3, u2, u4} α γ β δ f g) (Filter.coprod.{u1, u2} α β a b) (Filter.coprod.{u3, u4} γ δ c d))
but is expected to have type
  forall {α : Type.{u3}} {β : Type.{u2}} {γ : Type.{u1}} {δ : Type.{u4}} {f : α -> γ} {g : β -> δ} {a : Filter.{u3} α} {b : Filter.{u2} β} {c : Filter.{u1} γ} {d : Filter.{u4} δ}, (Filter.Tendsto.{u3, u1} α γ f a c) -> (Filter.Tendsto.{u2, u4} β δ g b d) -> (Filter.Tendsto.{max u2 u3, max u4 u1} (Prod.{u3, u2} α β) (Prod.{u1, u4} γ δ) (Prod.map.{u3, u1, u2, u4} α γ β δ f g) (Filter.coprod.{u3, u2} α β a b) (Filter.coprod.{u1, u4} γ δ c d))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.prod_map_coprod Filter.Tendsto.prod_map_coprodₓ'. -/
theorem Tendsto.prod_map_coprod {δ : Type _} {f : α → γ} {g : β → δ} {a : Filter α} {b : Filter β}
    {c : Filter γ} {d : Filter δ} (hf : Tendsto f a c) (hg : Tendsto g b d) :
    Tendsto (Prod.map f g) (a.coprod b) (c.coprod d) :=
  map_prod_map_coprod_le.trans (coprod_mono hf hg)
#align filter.tendsto.prod_map_coprod Filter.Tendsto.prod_map_coprod

end Coprod

end Filter