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

! This file was ported from Lean 3 source module logic.embedding.basic
! leanprover-community/mathlib commit 70d50ecfd4900dd6d328da39ab7ebd516abe4025
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Option.Basic
import Mathlib.Data.Prod.PProd
import Mathlib.Logic.Equiv.Basic

/-!
# Injective functions
-/

universe u v w x

namespace Function

-- port note: in Lean 3 this was tagged @[nolint has_nonempty_instance]
/-- `α ↪ β` is a bundled injective function. -/
structure Embedding (α : Sort _) (β : Sort _) where
  /-- An embedding as a function. Use coercion instead. -/
  toFun : α → β
  /-- An embedding is an injective function. Use `Function.Embedding.injective` instead. -/
  inj' : Injective toFun
#align function.embedding Function.Embedding

/-- An embedding, a.k.a. a bundled injective function. -/
infixr:25 " ↪ " => Embedding

instance {α : Sort u} {β : Sort v} : EmbeddingLike (α ↪ β) α β where
  coe := Embedding.toFun
  injective' := Embedding.inj'
  coe_injective' f g h := by { cases f; cases g; congr }

initialize_simps_projections Embedding (toFun → apply)

-- porting note: this needs `tactic.lift`.
--instance {α β : Sort _} : CanLift (α → β) (α ↪ β) coeFn Injective where prf f hf := ⟨⟨f, hf⟩, rfl⟩

end Function

section Equiv

variable {α : Sort u} {β : Sort v} (f : α ≃ β)

/-- Convert an `α ≃ β` to `α ↪ β`.

This is also available as a coercion `Equiv.coeEmbedding`.
The explicit `Equiv.toEmbedding` version is preferred though, since the coercion can have issues
inferring the type of the resulting embedding. For example:

```lean
-- Works:
example (s : Finset (Fin 3)) (f : Equiv.Perm (Fin 3)) : s.map f.toEmbedding = s.map f := by simp
-- Error, `f` has type `Fin 3 ≃ Fin 3` but is expected to have type `Fin 3 ↪ ?m_1 : Type ?`
example (s : Finset (Fin 3)) (f : Equiv.Perm (Fin 3)) : s.map f = s.map f.toEmbedding := by simp
```
-/
protected def Equiv.toEmbedding : α ↪ β :=
  ⟨f, f.injective⟩
#align equiv.to_embedding Equiv.toEmbedding

@[simp]
theorem Equiv.coe_toEmbedding : (f.toEmbedding : α → β) = f :=
  rfl
#align equiv.coe_to_embedding Equiv.coe_toEmbedding

theorem Equiv.toEmbedding_apply (a : α) : f.toEmbedding a = f a :=
  rfl
#align equiv.to_embedding_apply Equiv.toEmbedding_apply

instance Equiv.coeEmbedding : Coe (α ≃ β) (α ↪ β) :=
  ⟨Equiv.toEmbedding⟩
#align equiv.coe_embedding Equiv.coeEmbedding

@[reducible]
instance Equiv.Perm.coeEmbedding : Coe (Equiv.Perm α) (α ↪ α) :=
  Equiv.coeEmbedding
#align equiv.perm.coe_embedding Equiv.Perm.coeEmbedding

-- port note : `theorem Equiv.coe_eq_to_embedding : ↑f = f.toEmbedding` is a
-- syntactic tautology in Lean 4

end Equiv

namespace Function

namespace Embedding

theorem coe_injective {α β} : @Injective (α ↪ β) (α → β) (λ f => ↑f) :=
  FunLike.coe_injective
#align function.embedding.coe_injective Function.Embedding.coe_injective

@[ext]
theorem ext {α β} {f g : Embedding α β} (h : ∀ x, f x = g x) : f = g :=
  FunLike.ext f g h
#align function.embedding.ext Function.Embedding.ext

-- port note : in Lean 3 `FunLike.ext_iff.symm` works
theorem ext_iff {α β} {f g : Embedding α β} : (∀ x, f x = g x) ↔ f = g :=
  Iff.symm (FunLike.ext_iff)
#align function.embedding.ext_iff Function.Embedding.ext_iff

@[simp]
theorem toFun_eq_coe {α β} (f : α ↪ β) : toFun f = f :=
  rfl
#align function.embedding.to_fun_eq_coe Function.Embedding.toFun_eq_coe

@[simp]
theorem coeFn_mk {α β} (f : α → β) (i) : (@mk _ _ f i : α → β) = f :=
  rfl
#align function.embedding.coe_fn_mk Function.Embedding.coeFn_mk

@[simp]
theorem mk_coe {α β : Type _} (f : α ↪ β) (inj) : (⟨f, inj⟩ : α ↪ β) = f :=
  rfl
#align function.embedding.mk_coe Function.Embedding.mk_coe

protected theorem injective {α β} (f : α ↪ β) : Injective f :=
  EmbeddingLike.injective f
#align function.embedding.injective Function.Embedding.injective

theorem apply_eq_iff_eq {α β} (f : α ↪ β) (x y : α) : f x = f y ↔ x = y :=
  EmbeddingLike.apply_eq_iff_eq f
#align function.embedding.apply_eq_iff_eq Function.Embedding.apply_eq_iff_eq

/-- The identity map as a `Function.Embedding`. -/
@[refl, simps (config := { simpRhs := true })]
protected def refl (α : Sort _) : α ↪ α :=
  ⟨id, injective_id⟩
#align function.embedding.refl Function.Embedding.refl
#align function.embedding.refl_apply Function.Embedding.refl_apply

/-- Composition of `f : α ↪ β` and `g : β ↪ γ`. -/
@[trans, simps (config := { simpRhs := true })]
protected def trans {α β γ} (f : α ↪ β) (g : β ↪ γ) : α ↪ γ :=
  ⟨g ∘ f, g.injective.comp f.injective⟩
#align function.embedding.trans Function.Embedding.trans
#align function.embedding.trans_apply Function.Embedding.trans_apply

instance : Trans Embedding Embedding Embedding := ⟨Embedding.trans⟩

@[simp]
theorem equiv_toEmbedding_trans_symm_toEmbedding {α β : Sort _} (e : α ≃ β) :
    e.toEmbedding.trans e.symm.toEmbedding = Embedding.refl _ := by
  ext
  simp
#align function.embedding.equiv_to_embedding_trans_symm_to_embedding Function.Embedding.equiv_toEmbedding_trans_symm_toEmbedding

@[simp]
theorem equiv_symm_toEmbedding_trans_toEmbedding {α β : Sort _} (e : α ≃ β) :
    e.symm.toEmbedding.trans e.toEmbedding = Embedding.refl _ := by
  ext
  simp
#align function.embedding.equiv_symm_to_embedding_trans_to_embedding Function.Embedding.equiv_symm_toEmbedding_trans_toEmbedding

/-- Transfer an embedding along a pair of equivalences. -/
@[simps! (config := { fullyApplied := false, simpRhs := true })]
protected def congr {α : Sort u} {β : Sort v} {γ : Sort w} {δ : Sort x} (e₁ : α ≃ β) (e₂ : γ ≃ δ)
    (f : α ↪ γ) : β ↪ δ :=
  (Equiv.toEmbedding e₁.symm).trans (f.trans e₂.toEmbedding)
#align function.embedding.congr Function.Embedding.congr
#align function.embedding.congr_apply Function.Embedding.congr_apply

/-- A right inverse `surjInv` of a surjective function as an `Embedding`. -/
protected noncomputable def ofSurjective {α β} (f : β → α) (hf : Surjective f) : α ↪ β :=
  ⟨surjInv hf, injective_surjInv _⟩
#align function.embedding.of_surjective Function.Embedding.ofSurjective

/-- Convert a surjective `Embedding` to an `Equiv` -/
protected noncomputable def equivOfSurjective {α β} (f : α ↪ β) (hf : Surjective f) : α ≃ β :=
  Equiv.ofBijective f ⟨f.injective, hf⟩
#align function.embedding.equiv_of_surjective Function.Embedding.equivOfSurjective

/-- There is always an embedding from an empty type. -/
protected def ofIsEmpty {α β} [IsEmpty α] : α ↪ β :=
  ⟨isEmptyElim, isEmptyElim⟩
#align function.embedding.of_is_empty Function.Embedding.ofIsEmpty

/-- Change the value of an embedding `f` at one point. If the prescribed image
is already occupied by some `f a'`, then swap the values at these two points. -/
def setValue {α β} (f : α ↪ β) (a : α) (b : β) [∀ a', Decidable (a' = a)]
    [∀ a', Decidable (f a' = b)] : α ↪ β :=
  ⟨fun a' => if a' = a then b else if f a' = b then f a else f a', by
    intro x y (h : ite _ _ _ = ite _ _ _)
    -- TODO: once we have `cc` we can avoid all the manual cases below by doing
    -- split_ifs at h <;> (try subst b) <;> (try simp only [f.injective.eq_iff] at *) <;> cc
    split_ifs at h with h₁ h₂ _ _ h₅ h₆ <;>
        (try subst b) <;>
        (try simp only [f.injective.eq_iff] at *)
    · rw[h₁,h₂]
    · rw[h₁,h]
    · rw[h₅,←h]
    · exact h₆.symm
    · exfalso; exact h₅ h.symm
    · exfalso; exact h₁ h
    · exact h ⟩
#align function.embedding.set_value Function.Embedding.setValue

theorem setValue_eq {α β} (f : α ↪ β) (a : α) (b : β) [∀ a', Decidable (a' = a)]
    [∀ a', Decidable (f a' = b)] : setValue f a b a = b := by
  simp [setValue]
#align function.embedding.set_value_eq Function.Embedding.setValue_eq

/-- Embedding into `Option α` using `some`. -/
@[simps (config := { fullyApplied := false })]
protected def some {α} : α ↪ Option α :=
  ⟨some, Option.some_injective α⟩
#align function.embedding.some Function.Embedding.some
#align function.embedding.some_apply Function.Embedding.some_apply

-- porting note: Lean 4 unfolds coercion `α → Option α` to `some`, so there is no separate
-- `Function.Embedding.coeOption`.
#align function.embedding.coe_option Function.Embedding.some

/-- A version of `Option.map` for `Function.Embedding`s. -/
@[simps (config := { fullyApplied := false })]
def optionMap {α β} (f : α ↪ β) : Option α ↪ Option β :=
  ⟨Option.map f, Option.map_injective f.injective⟩
#align function.embedding.option_map Function.Embedding.optionMap
#align function.embedding.option_map_apply Function.Embedding.optionMap_apply

/-- Embedding of a `Subtype`. -/
def subtype {α} (p : α → Prop) : Subtype p ↪ α :=
  ⟨Subtype.val, fun _ _ => Subtype.ext⟩
#align function.embedding.subtype Function.Embedding.subtype

@[simp]
theorem coe_subtype {α} (p : α → Prop) : ↑(subtype p) = Subtype.val :=
  rfl
#align function.embedding.coe_subtype Function.Embedding.coe_subtype

/-- `Quotient.out` as an embedding. -/
noncomputable def quotientOut (α) [s : Setoid α] : Quotient s ↪ α :=
  ⟨_, Quotient.out_injective⟩
#align function.embedding.quotient_out Function.Embedding.quotientOut

@[simp]
theorem coe_quotientOut (α) [Setoid α] : ↑(quotientOut α) = Quotient.out :=
  rfl
#align function.embedding.coe_quotient_out Function.Embedding.coe_quotientOut

/-- Choosing an element `b : β` gives an embedding of `punit` into `β`. -/
def punit {β : Sort _} (b : β) : PUnit ↪ β :=
  ⟨fun _ => b, by
    rintro ⟨⟩ ⟨⟩ _
    rfl⟩
#align function.embedding.punit Function.Embedding.punit

/-- Fixing an element `b : β` gives an embedding `α ↪ α × β`. -/
@[simps]
def sectl (α : Sort _) {β : Sort _} (b : β) : α ↪ α × β :=
  ⟨fun a => (a, b), fun _ _ h => congr_arg Prod.fst h⟩
#align function.embedding.sectl Function.Embedding.sectl
#align function.embedding.sectl_apply Function.Embedding.sectl_apply

/-- Fixing an element `a : α` gives an embedding `β ↪ α × β`. -/
@[simps]
def sectr {α : Sort _} (a : α) (β : Sort _) : β ↪ α × β :=
  ⟨fun b => (a, b), fun _ _ h => congr_arg Prod.snd h⟩
#align function.embedding.sectr Function.Embedding.sectr
#align function.embedding.sectr_apply Function.Embedding.sectr_apply

/-- If `e₁` and `e₂` are embeddings, then so is `prod.map e₁ e₂ : (a, b) ↦ (e₁ a, e₂ b)`. -/
def prodMap {α β γ δ : Type _} (e₁ : α ↪ β) (e₂ : γ ↪ δ) : α × γ ↪ β × δ :=
  ⟨Prod.map e₁ e₂, e₁.injective.Prod_map e₂.injective⟩
#align function.embedding.prod_map Function.Embedding.prodMap

@[simp]
theorem coe_prodMap {α β γ δ : Type _} (e₁ : α ↪ β) (e₂ : γ ↪ δ) :
    e₁.prodMap e₂ = Prod.map e₁ e₂ :=
  rfl
#align function.embedding.coe_prod_map Function.Embedding.coe_prodMap

/-- If `e₁` and `e₂` are embeddings, then so is `λ ⟨a, b⟩, ⟨e₁ a, e₂ b⟩ : pprod α γ → pprod β δ`. -/
def pprodMap {α β γ δ : Sort _} (e₁ : α ↪ β) (e₂ : γ ↪ δ) : PProd α γ ↪ PProd β δ :=
  ⟨fun x => ⟨e₁ x.1, e₂ x.2⟩, e₁.injective.pprod_map e₂.injective⟩
#align function.embedding.pprod_map Function.Embedding.pprodMap

section Sum

open Sum

/-- If `e₁` and `e₂` are embeddings, then so is `sum.map e₁ e₂`. -/
def sumMap {α β γ δ : Type _} (e₁ : α ↪ β) (e₂ : γ ↪ δ) : Sum α γ ↪ Sum β δ :=
  ⟨Sum.map e₁ e₂, e₁.injective.sum_map e₂.injective⟩
#align function.embedding.sum_map Function.Embedding.sumMap

@[simp]
theorem coe_sumMap {α β γ δ} (e₁ : α ↪ β) (e₂ : γ ↪ δ) : sumMap e₁ e₂ = Sum.map e₁ e₂ :=
  rfl
#align function.embedding.coe_sum_map Function.Embedding.coe_sumMap

/-- The embedding of `α` into the sum `α ⊕ β`. -/
@[simps]
def inl {α β : Type _} : α ↪ Sum α β :=
  ⟨Sum.inl, fun _ _ => Sum.inl.inj⟩
#align function.embedding.inl Function.Embedding.inl
#align function.embedding.inl_apply Function.Embedding.inl_apply

/-- The embedding of `β` into the sum `α ⊕ β`. -/
@[simps]
def inr {α β : Type _} : β ↪ Sum α β :=
  ⟨Sum.inr, fun _ _ => Sum.inr.inj⟩
#align function.embedding.inr Function.Embedding.inr
#align function.embedding.inr_apply Function.Embedding.inr_apply

end Sum

section Sigma

variable {α α' : Type _} {β : α → Type _} {β' : α' → Type _}

/-- `Sigma.mk` as an `Function.Embedding`. -/
@[simps apply]
def sigmaMk (a : α) : β a ↪ Σx, β x :=
  ⟨Sigma.mk a, sigma_mk_injective⟩
#align function.embedding.sigma_mk Function.Embedding.sigmaMk
#align function.embedding.sigma_mk_apply Function.Embedding.sigmaMk_apply

/-- If `f : α ↪ α'` is an embedding and `g : Π a, β α ↪ β' (f α)` is a family
of embeddings, then `Sigma.map f g` is an embedding. -/
@[simps apply]
def sigmaMap (f : α ↪ α') (g : ∀ a, β a ↪ β' (f a)) : (Σa, β a) ↪ Σa', β' a' :=
  ⟨Sigma.map f fun a => g a, f.injective.sigma_map fun a => (g a).injective⟩
#align function.embedding.sigma_map Function.Embedding.sigmaMap
#align function.embedding.sigma_map_apply Function.Embedding.sigmaMap_apply

end Sigma

/-- Define an embedding `(Π a : α, β a) ↪ (Π a : α, γ a)` from a family of embeddings
`e : Π a, (β a ↪ γ a)`. This embedding sends `f` to `λ a, e a (f a)`. -/
@[simps]
def piCongrRight {α : Sort _} {β γ : α → Sort _} (e : ∀ a, β a ↪ γ a) : (∀ a, β a) ↪ ∀ a, γ a :=
  ⟨fun f a => e a (f a), fun _ _ h => funext fun a => (e a).injective (congr_fun h a)⟩
#align function.embedding.Pi_congr_right Function.Embedding.piCongrRight
#align function.embedding.Pi_congr_right_apply Function.Embedding.piCongrRight_apply

/-- An embedding `e : α ↪ β` defines an embedding `(γ → α) ↪ (γ → β)` that sends each `f`
to `e ∘ f`. -/
def arrowCongrRight {α : Sort u} {β : Sort v} {γ : Sort w} (e : α ↪ β) : (γ → α) ↪ γ → β :=
  piCongrRight fun _ => e
#align function.embedding.arrow_congr_right Function.Embedding.arrowCongrRight

@[simp]
theorem arrowCongrRight_apply {α : Sort u} {β : Sort v} {γ : Sort w} (e : α ↪ β) (f : γ ↪ α) :
    arrowCongrRight e f = e ∘ f :=
  rfl
#align function.embedding.arrow_congr_right_apply Function.Embedding.arrowCongrRight_apply

/-- An embedding `e : α ↪ β` defines an embedding `(α → γ) ↪ (β → γ)` for any inhabited type `γ`.
This embedding sends each `f : α → γ` to a function `g : β → γ` such that `g ∘ e = f` and
`g y = default` whenever `y ∉ range e`. -/
noncomputable def arrowCongrLeft {α : Sort u} {β : Sort v} {γ : Sort w} [Inhabited γ] (e : α ↪ β) :
    (α → γ) ↪ β → γ :=
  ⟨fun f => extend e f default, fun f₁ f₂ h =>
    funext fun x => by simpa only [e.injective.extend_apply] using congr_fun h (e x)⟩
#align function.embedding.arrow_congr_left Function.Embedding.arrowCongrLeft

/-- Restrict both domain and codomain of an embedding. -/
protected def subtypeMap {α β} {p : α → Prop} {q : β → Prop} (f : α ↪ β)
    (h : ∀ ⦃x⦄, p x → q (f x)) :
    { x : α // p x } ↪ { y : β // q y } :=
  ⟨Subtype.map f h, Subtype.map_injective h f.2⟩
#align function.embedding.subtype_map Function.Embedding.subtypeMap

open Set

theorem swap_apply {α β : Type _} [DecidableEq α] [DecidableEq β] (f : α ↪ β) (x y z : α) :
    Equiv.swap (f x) (f y) (f z) = f (Equiv.swap x y z) :=
  f.injective.swap_apply x y z
#align function.embedding.swap_apply Function.Embedding.swap_apply

theorem swap_comp {α β : Type _} [DecidableEq α] [DecidableEq β] (f : α ↪ β) (x y : α) :
    Equiv.swap (f x) (f y) ∘ f = f ∘ Equiv.swap x y :=
  f.injective.swap_comp x y
#align function.embedding.swap_comp Function.Embedding.swap_comp

end Embedding

end Function

namespace Equiv

open Function Embedding

/-- Given an equivalence to a subtype, produce an embedding to the elements of the corresponding
set. -/
@[simps!]
def asEmbedding {p : β → Prop} (e : α ≃ Subtype p) : α ↪ β :=
  e.toEmbedding.trans (subtype p)
#align equiv.as_embedding Equiv.asEmbedding
#align equiv.as_embedding_apply Equiv.asEmbedding_apply

/-- The type of embeddings `α ↪ β` is equivalent to
    the subtype of all injective functions `α → β`. -/
def subtypeInjectiveEquivEmbedding (α β : Sort _) :
    { f : α → β // Injective f } ≃ (α ↪ β) where
  toFun f := ⟨f.val, f.property⟩
  invFun f := ⟨f, f.injective⟩
  left_inv _ := rfl
  right_inv _ := rfl
#align equiv.subtype_injective_equiv_embedding Equiv.subtypeInjectiveEquivEmbedding

-- porting note: in Lean 3 this had `@[congr]`
/-- If `α₁ ≃ α₂` and `β₁ ≃ β₂`, then the type of embeddings `α₁ ↪ β₁`
is equivalent to the type of embeddings `α₂ ↪ β₂`. -/
@[simps apply]
def embeddingCongr {α β γ δ : Sort _} (h : α ≃ β) (h' : γ ≃ δ) : (α ↪ γ) ≃ (β ↪ δ) where
  toFun f := f.congr h h'
  invFun f := f.congr h.symm h'.symm
  left_inv x := by
    ext
    simp
  right_inv x := by
    ext
    simp
#align equiv.embedding_congr Equiv.embeddingCongr
#align equiv.embedding_congr_apply Equiv.embeddingCongr_apply

@[simp]
theorem embeddingCongr_refl {α β : Sort _} :
    embeddingCongr (Equiv.refl α) (Equiv.refl β) = Equiv.refl (α ↪ β) :=
  rfl
#align equiv.embedding_congr_refl Equiv.embeddingCongr_refl

@[simp]
theorem embeddingCongr_trans {α₁ β₁ α₂ β₂ α₃ β₃ : Sort _} (e₁ : α₁ ≃ α₂) (e₁' : β₁ ≃ β₂)
    (e₂ : α₂ ≃ α₃) (e₂' : β₂ ≃ β₃) :
    embeddingCongr (e₁.trans e₂) (e₁'.trans e₂') =
      (embeddingCongr e₁ e₁').trans (embeddingCongr e₂ e₂') :=
  rfl
#align equiv.embedding_congr_trans Equiv.embeddingCongr_trans

@[simp]
theorem embeddingCongr_symm {α₁ β₁ α₂ β₂ : Sort _} (e₁ : α₁ ≃ α₂) (e₂ : β₁ ≃ β₂) :
    (embeddingCongr e₁ e₂).symm = embeddingCongr e₁.symm e₂.symm :=
  rfl
#align equiv.embedding_congr_symm Equiv.embeddingCongr_symm

theorem embeddingCongr_apply_trans {α₁ β₁ γ₁ α₂ β₂ γ₂ : Sort _} (ea : α₁ ≃ α₂) (eb : β₁ ≃ β₂)
    (ec : γ₁ ≃ γ₂) (f : α₁ ↪ β₁) (g : β₁ ↪ γ₁) :
    Equiv.embeddingCongr ea ec (f.trans g) =
      (Equiv.embeddingCongr ea eb f).trans (Equiv.embeddingCongr eb ec g) := by
  ext
  simp
#align equiv.embedding_congr_apply_trans Equiv.embeddingCongr_apply_trans

@[simp]
theorem refl_toEmbedding {α : Type _} : (Equiv.refl α).toEmbedding = Embedding.refl α :=
  rfl
#align equiv.refl_to_embedding Equiv.refl_toEmbedding

@[simp]
theorem trans_toEmbedding {α β γ : Type _} (e : α ≃ β) (f : β ≃ γ) :
    (e.trans f).toEmbedding = e.toEmbedding.trans f.toEmbedding :=
  rfl
#align equiv.trans_to_embedding Equiv.trans_toEmbedding

end Equiv

section Subtype

variable {α : Type _}

/-- A subtype `{x // p x ∨ q x}` over a disjunction of `p q : α → Prop` can be injectively split
into a sum of subtypes `{x // p x} ⊕ {x // q x}` such that `¬ p x` is sent to the right. -/
def subtypeOrLeftEmbedding (p q : α → Prop) [DecidablePred p] :
    { x // p x ∨ q x } ↪ Sum { x // p x } { x // q x } :=
  ⟨fun x => if h : p x then Sum.inl ⟨x, h⟩ else Sum.inr ⟨x, x.prop.resolve_left h⟩, by
    intro x y
    dsimp only
    split_ifs <;> simp [Subtype.ext_iff]⟩
#align subtype_or_left_embedding subtypeOrLeftEmbedding

theorem subtypeOrLeftEmbedding_apply_left {p q : α → Prop} [DecidablePred p]
    (x : { x // p x ∨ q x }) (hx : p x) :
    subtypeOrLeftEmbedding p q x = Sum.inl ⟨x, hx⟩ :=
  dif_pos hx
#align subtype_or_left_embedding_apply_left subtypeOrLeftEmbedding_apply_left

theorem subtypeOrLeftEmbedding_apply_right {p q : α → Prop} [DecidablePred p]
    (x : { x // p x ∨ q x }) (hx : ¬p x) :
    subtypeOrLeftEmbedding p q x = Sum.inr ⟨x, x.prop.resolve_left hx⟩ :=
  dif_neg hx
#align subtype_or_left_embedding_apply_right subtypeOrLeftEmbedding_apply_right

/-- A subtype `{x // p x}` can be injectively sent to into a subtype `{x // q x}`,
if `p x → q x` for all `x : α`. -/
@[simps]
def Subtype.impEmbedding (p q : α → Prop) (h : ∀ x, p x → q x) : { x // p x } ↪ { x // q x } :=
  ⟨fun x => ⟨x, h x x.prop⟩, fun x y => by simp [Subtype.ext_iff]⟩
#align subtype.imp_embedding Subtype.impEmbedding
#align subtype.imp_embedding_apply_coe Subtype.impEmbedding_apply_coe

end Subtype


/-
Copyright (c) 2017 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.big_operators.intervals
import Mathlib.topology.instances.real
import Mathlib.topology.algebra.module
import Mathlib.data.indicator_function
import Mathlib.data.equiv.encodable.lattice
import Mathlib.order.filter.at_top_bot
import Mathlib.PostPort

universes u_1 u_2 u_3 u_4 u_5 u_6

namespace Mathlib

/-!
# Infinite sum over a topological monoid

This sum is known as unconditionally convergent, as it sums to the same value under all possible
permutations. For Euclidean spaces (finite dimensional Banach spaces) this is equivalent to absolute
convergence.

Note: There are summable sequences which are not unconditionally convergent! The other way holds
generally, see `has_sum.tendsto_sum_nat`.

## References

* Bourbaki: General Topology (1995), Chapter 3 §5 (Infinite sums in commutative groups)

-/

/-- Infinite sum on a topological monoid

The `at_top` filter on `finset β` is the limit of all finite sets towards the entire type. So we sum
up bigger and bigger sets. This sum operation is invariant under reordering. In particular,
the function `ℕ → ℝ` sending `n` to `(-1)^n / (n+1)` does not have a
sum for this definition, but a series which is absolutely convergent will have the correct sum.

This is based on Mario Carneiro's
[infinite sum `df-tsms` in Metamath](http://us.metamath.org/mpeuni/df-tsms.html).

For the definition or many statements, `α` does not need to be a topological monoid. We only add
this assumption later, for the lemmas where it is relevant.
-/
def has_sum {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] (f : β → α) (a : α) :=
  filter.tendsto (fun (s : finset β) => finset.sum s fun (b : β) => f b) filter.at_top (nhds a)

/-- `summable f` means that `f` has some (infinite) sum. Use `tsum` to get the value. -/
def summable {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] (f : β → α) :=
  ∃ (a : α), has_sum f a

/-- `∑' i, f i` is the sum of `f` it exists, or 0 otherwise -/
def tsum {α : Type u_1} [add_comm_monoid α] [topological_space α] {β : Type u_2} (f : β → α) : α :=
  dite (summable f) (fun (h : summable f) => classical.some h) fun (h : ¬summable f) => 0

-- see Note [operator precedence of big operators]

theorem summable.has_sum {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} (ha : summable f) : has_sum f (tsum fun (b : β) => f b) := sorry

theorem has_sum.summable {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} (h : has_sum f a) : summable f :=
  Exists.intro a h

/-- Constant zero function has sum `0` -/
theorem has_sum_zero {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] : has_sum (fun (b : β) => 0) 0 := sorry

theorem summable_zero {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] : summable fun (b : β) => 0 :=
  has_sum.summable has_sum_zero

theorem tsum_eq_zero_of_not_summable {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} (h : ¬summable f) : (tsum fun (b : β) => f b) = 0 := sorry

theorem has_sum.has_sum_of_sum_eq {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {g : γ → α} (h_eq : ∀ (u : finset γ),
  ∃ (v : finset β),
    ∀ (v' : finset β),
      v ⊆ v' → ∃ (u' : finset γ), u ⊆ u' ∧ (finset.sum u' fun (x : γ) => g x) = finset.sum v' fun (b : β) => f b) (hf : has_sum g a) : has_sum f a :=
  le_trans (filter.map_at_top_finset_sum_le_of_sum_eq h_eq) hf

theorem has_sum_iff_has_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {g : γ → α} (h₁ : ∀ (u : finset γ),
  ∃ (v : finset β),
    ∀ (v' : finset β),
      v ⊆ v' → ∃ (u' : finset γ), u ⊆ u' ∧ (finset.sum u' fun (x : γ) => g x) = finset.sum v' fun (b : β) => f b) (h₂ : ∀ (v : finset β),
  ∃ (u : finset γ),
    ∀ (u' : finset γ),
      u ⊆ u' → ∃ (v' : finset β), v ⊆ v' ∧ (finset.sum v' fun (b : β) => f b) = finset.sum u' fun (x : γ) => g x) : has_sum f a ↔ has_sum g a :=
  { mp := has_sum.has_sum_of_sum_eq h₂, mpr := has_sum.has_sum_of_sum_eq h₁ }

theorem function.injective.has_sum_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {g : γ → β} (hg : function.injective g) (hf : ∀ (x : β), ¬x ∈ set.range g → f x = 0) : has_sum (f ∘ g) a ↔ has_sum f a := sorry

theorem function.injective.summable_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β → α} {g : γ → β} (hg : function.injective g) (hf : ∀ (x : β), ¬x ∈ set.range g → f x = 0) : summable (f ∘ g) ↔ summable f :=
  exists_congr fun (_x : α) => function.injective.has_sum_iff hg hf

theorem has_sum_subtype_iff_of_support_subset {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {s : set β} (hf : function.support f ⊆ s) : has_sum (f ∘ coe) a ↔ has_sum f a := sorry

theorem has_sum_subtype_iff_indicator {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {s : set β} : has_sum (f ∘ coe) a ↔ has_sum (set.indicator s f) a := sorry

@[simp] theorem has_sum_subtype_support {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} : has_sum (f ∘ coe) a ↔ has_sum f a :=
  has_sum_subtype_iff_of_support_subset (set.subset.refl (function.support f))

theorem has_sum_fintype {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [fintype β] (f : β → α) : has_sum f (finset.sum finset.univ fun (b : β) => f b) :=
  order_top.tendsto_at_top_nhds fun (s : finset β) => finset.sum s fun (b : β) => f b

protected theorem finset.has_sum {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] (s : finset β) (f : β → α) : has_sum (f ∘ coe) (finset.sum s fun (b : β) => f b) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (has_sum (f ∘ coe) (finset.sum s fun (b : β) => f b))) (Eq.symm finset.sum_attach)))
    (has_sum_fintype (f ∘ coe))

protected theorem finset.summable {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] (s : finset β) (f : β → α) : summable (f ∘ coe) :=
  has_sum.summable (finset.has_sum s f)

protected theorem set.finite.summable {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {s : set β} (hs : set.finite s) (f : β → α) : summable (f ∘ coe) := sorry

/-- If a function `f` vanishes outside of a finite set `s`, then it `has_sum` `∑ b in s, f b`. -/
theorem has_sum_sum_of_ne_finset_zero {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} {s : finset β} (hf : ∀ (b : β), ¬b ∈ s → f b = 0) : has_sum f (finset.sum s fun (b : β) => f b) :=
  iff.mp (has_sum_subtype_iff_of_support_subset (iff.mpr function.support_subset_iff' hf)) (finset.has_sum s f)

theorem summable_of_ne_finset_zero {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} {s : finset β} (hf : ∀ (b : β), ¬b ∈ s → f b = 0) : summable f :=
  has_sum.summable (has_sum_sum_of_ne_finset_zero hf)

theorem has_sum_single {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} (b : β) (hf : ∀ (b' : β), b' ≠ b → f b' = 0) : has_sum f (f b) := sorry

theorem has_sum_ite_eq {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] (b : β) (a : α) : has_sum (fun (b' : β) => ite (b' = b) a 0) a := sorry

theorem equiv.has_sum_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} (e : γ ≃ β) : has_sum (f ∘ ⇑e) a ↔ has_sum f a := sorry

theorem equiv.summable_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β → α} (e : γ ≃ β) : summable (f ∘ ⇑e) ↔ summable f :=
  exists_congr fun (a : α) => equiv.has_sum_iff e

theorem summable.prod_symm {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β × γ → α} (hf : summable f) : summable fun (p : γ × β) => f (prod.swap p) :=
  iff.mpr (equiv.summable_iff (equiv.prod_comm γ β)) hf

theorem equiv.has_sum_iff_of_support {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {g : γ → α} (e : ↥(function.support f) ≃ ↥(function.support g)) (he : ∀ (x : ↥(function.support f)), g ↑(coe_fn e x) = f ↑x) : has_sum f a ↔ has_sum g a := sorry

theorem has_sum_iff_has_sum_of_ne_zero_bij {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {g : γ → α} (i : ↥(function.support g) → β) (hi : ∀ {x y : ↥(function.support g)}, i x = i y → ↑x = ↑y) (hf : function.support f ⊆ set.range i) (hfg : ∀ (x : ↥(function.support g)), f (i x) = g ↑x) : has_sum f a ↔ has_sum g a := sorry

theorem equiv.summable_iff_of_support {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β → α} {g : γ → α} (e : ↥(function.support f) ≃ ↥(function.support g)) (he : ∀ (x : ↥(function.support f)), g ↑(coe_fn e x) = f ↑x) : summable f ↔ summable g :=
  exists_congr fun (_x : α) => equiv.has_sum_iff_of_support e he

protected theorem has_sum.map {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} [add_comm_monoid γ] [topological_space γ] (hf : has_sum f a) (g : α →+ γ) (hg : continuous ⇑g) : has_sum (⇑g ∘ f) (coe_fn g a) := sorry

protected theorem summable.map {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {f : β → α} [add_comm_monoid γ] [topological_space γ] (hf : summable f) (g : α →+ γ) (hg : continuous ⇑g) : summable (⇑g ∘ f) :=
  has_sum.summable (has_sum.map (summable.has_sum hf) g hg)

/-- If `f : ℕ → α` has sum `a`, then the partial sums `∑_{i=0}^{n-1} f i` converge to `a`. -/
theorem has_sum.tendsto_sum_nat {α : Type u_1} [add_comm_monoid α] [topological_space α] {a : α} {f : ℕ → α} (h : has_sum f a) : filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i) filter.at_top (nhds a) :=
  filter.tendsto.comp h filter.tendsto_finset_range

theorem has_sum.unique {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} {a₁ : α} {a₂ : α} [t2_space α] : has_sum f a₁ → has_sum f a₂ → a₁ = a₂ :=
  tendsto_nhds_unique

theorem summable.has_sum_iff_tendsto_nat {α : Type u_1} [add_comm_monoid α] [topological_space α] [t2_space α] {f : ℕ → α} {a : α} (hf : summable f) : has_sum f a ↔ filter.tendsto (fun (n : ℕ) => finset.sum (finset.range n) fun (i : ℕ) => f i) filter.at_top (nhds a) := sorry

theorem equiv.summable_iff_of_has_sum_iff {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] {α' : Type u_4} [add_comm_monoid α'] [topological_space α'] (e : α' ≃ α) {f : β → α} {g : γ → α'} (he : ∀ {a : α'}, has_sum f (coe_fn e a) ↔ has_sum g a) : summable f ↔ summable g := sorry

theorem has_sum.add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} {g : β → α} {a : α} {b : α} [has_continuous_add α] (hf : has_sum f a) (hg : has_sum g b) : has_sum (fun (b : β) => f b + g b) (a + b) := sorry

theorem summable.add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} {g : β → α} [has_continuous_add α] (hf : summable f) (hg : summable g) : summable fun (b : β) => f b + g b :=
  has_sum.summable (has_sum.add (summable.has_sum hf) (summable.has_sum hg))

theorem has_sum_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [has_continuous_add α] {f : γ → β → α} {a : γ → α} {s : finset γ} : (∀ (i : γ), i ∈ s → has_sum (f i) (a i)) →
  has_sum (fun (b : β) => finset.sum s fun (i : γ) => f i b) (finset.sum s fun (i : γ) => a i) := sorry

theorem summable_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [has_continuous_add α] {f : γ → β → α} {s : finset γ} (hf : ∀ (i : γ), i ∈ s → summable (f i)) : summable fun (b : β) => finset.sum s fun (i : γ) => f i b :=
  has_sum.summable (has_sum_sum fun (i : γ) (hi : i ∈ s) => summable.has_sum (hf i hi))

theorem has_sum.add_compl {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {b : α} [has_continuous_add α] {s : set β} (ha : has_sum (f ∘ coe) a) (hb : has_sum (f ∘ coe) b) : has_sum f (a + b) := sorry

theorem summable.add_compl {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} [has_continuous_add α] {s : set β} (hs : summable (f ∘ coe)) (hsc : summable (f ∘ coe)) : summable f :=
  has_sum.summable (has_sum.add_compl (summable.has_sum hs) (summable.has_sum hsc))

theorem has_sum.compl_add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} {a : α} {b : α} [has_continuous_add α] {s : set β} (ha : has_sum (f ∘ coe) a) (hb : has_sum (f ∘ coe) b) : has_sum f (a + b) := sorry

theorem summable.compl_add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] {f : β → α} [has_continuous_add α] {s : set β} (hs : summable (f ∘ coe)) (hsc : summable (f ∘ coe)) : summable f :=
  has_sum.summable (has_sum.compl_add (summable.has_sum hs) (summable.has_sum hsc))

theorem has_sum.sigma {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [has_continuous_add α] [regular_space α] {γ : β → Type u_3} {f : (sigma fun (b : β) => γ b) → α} {g : β → α} {a : α} (ha : has_sum f a) (hf : ∀ (b : β), has_sum (fun (c : γ b) => f (sigma.mk b c)) (g b)) : has_sum g a := sorry

/-- If a series `f` on `β × γ` has sum `a` and for each `b` the restriction of `f` to `{b} × γ`
has sum `g b`, then the series `g` has sum `a`. -/
theorem has_sum.prod_fiberwise {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [has_continuous_add α] [regular_space α] {f : β × γ → α} {g : β → α} {a : α} (ha : has_sum f a) (hf : ∀ (b : β), has_sum (fun (c : γ) => f (b, c)) (g b)) : has_sum g a :=
  has_sum.sigma (iff.mpr (equiv.has_sum_iff (equiv.sigma_equiv_prod β γ)) ha) hf

theorem summable.sigma' {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [has_continuous_add α] [regular_space α] {γ : β → Type u_3} {f : (sigma fun (b : β) => γ b) → α} (ha : summable f) (hf : ∀ (b : β), summable fun (c : γ b) => f (sigma.mk b c)) : summable fun (b : β) => tsum fun (c : γ b) => f (sigma.mk b c) :=
  has_sum.summable (has_sum.sigma (summable.has_sum ha) fun (b : β) => summable.has_sum (hf b))

theorem has_sum.sigma_of_has_sum {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [has_continuous_add α] [regular_space α] {γ : β → Type u_3} {f : (sigma fun (b : β) => γ b) → α} {g : β → α} {a : α} (ha : has_sum g a) (hf : ∀ (b : β), has_sum (fun (c : γ b) => f (sigma.mk b c)) (g b)) (hf' : summable f) : has_sum f a := sorry

theorem has_sum.tsum_eq {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] {f : β → α} {a : α} (ha : has_sum f a) : (tsum fun (b : β) => f b) = a :=
  has_sum.unique (summable.has_sum (Exists.intro a ha)) ha

theorem summable.has_sum_iff {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] {f : β → α} {a : α} (h : summable f) : has_sum f a ↔ (tsum fun (b : β) => f b) = a :=
  { mp := has_sum.tsum_eq, mpr := fun (eq : (tsum fun (b : β) => f b) = a) => eq ▸ summable.has_sum h }

@[simp] theorem tsum_zero {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] : (tsum fun (b : β) => 0) = 0 :=
  has_sum.tsum_eq has_sum_zero

theorem tsum_eq_sum {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] {f : β → α} {s : finset β} (hf : ∀ (b : β), ¬b ∈ s → f b = 0) : (tsum fun (b : β) => f b) = finset.sum s fun (b : β) => f b :=
  has_sum.tsum_eq (has_sum_sum_of_ne_finset_zero hf)

theorem tsum_fintype {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] [fintype β] (f : β → α) : (tsum fun (b : β) => f b) = finset.sum finset.univ fun (b : β) => f b :=
  has_sum.tsum_eq (has_sum_fintype f)

@[simp] theorem finset.tsum_subtype {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] (s : finset β) (f : β → α) : (tsum fun (x : Subtype fun (x : β) => x ∈ s) => f ↑x) = finset.sum s fun (x : β) => f x :=
  has_sum.tsum_eq (finset.has_sum s f)

@[simp] theorem finset.tsum_subtype' {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] (s : finset β) (f : β → α) : (tsum fun (x : ↥↑s) => f ↑x) = finset.sum s fun (x : β) => f x :=
  finset.tsum_subtype s f

theorem tsum_eq_single {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] {f : β → α} (b : β) (hf : ∀ (b' : β), b' ≠ b → f b' = 0) : (tsum fun (b : β) => f b) = f b :=
  has_sum.tsum_eq (has_sum_single b hf)

@[simp] theorem tsum_ite_eq {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] (b : β) (a : α) : (tsum fun (b' : β) => ite (b' = b) a 0) = a :=
  has_sum.tsum_eq (has_sum_ite_eq b a)

theorem equiv.tsum_eq_tsum_of_has_sum_iff_has_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [t2_space α] {α' : Type u_4} [add_comm_monoid α'] [topological_space α'] (e : α' ≃ α) (h0 : coe_fn e 0 = 0) {f : β → α} {g : γ → α'} (h : ∀ {a : α'}, has_sum f (coe_fn e a) ↔ has_sum g a) : (tsum fun (b : β) => f b) = coe_fn e (tsum fun (c : γ) => g c) := sorry

theorem tsum_eq_tsum_of_has_sum_iff_has_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [t2_space α] {f : β → α} {g : γ → α} (h : ∀ {a : α}, has_sum f a ↔ has_sum g a) : (tsum fun (b : β) => f b) = tsum fun (c : γ) => g c :=
  equiv.tsum_eq_tsum_of_has_sum_iff_has_sum (equiv.refl α) rfl h

theorem equiv.tsum_eq {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [t2_space α] (j : γ ≃ β) (f : β → α) : (tsum fun (c : γ) => f (coe_fn j c)) = tsum fun (b : β) => f b :=
  tsum_eq_tsum_of_has_sum_iff_has_sum fun (a : α) => equiv.has_sum_iff j

theorem equiv.tsum_eq_tsum_of_support {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [t2_space α] {f : β → α} {g : γ → α} (e : ↥(function.support f) ≃ ↥(function.support g)) (he : ∀ (x : ↥(function.support f)), g ↑(coe_fn e x) = f ↑x) : (tsum fun (x : β) => f x) = tsum fun (y : γ) => g y :=
  tsum_eq_tsum_of_has_sum_iff_has_sum fun (_x : α) => equiv.has_sum_iff_of_support e he

theorem tsum_eq_tsum_of_ne_zero_bij {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [t2_space α] {f : β → α} {g : γ → α} (i : ↥(function.support g) → β) (hi : ∀ {x y : ↥(function.support g)}, i x = i y → ↑x = ↑y) (hf : function.support f ⊆ set.range i) (hfg : ∀ (x : ↥(function.support g)), f (i x) = g ↑x) : (tsum fun (x : β) => f x) = tsum fun (y : γ) => g y :=
  tsum_eq_tsum_of_has_sum_iff_has_sum fun (_x : α) => has_sum_iff_has_sum_of_ne_zero_bij i hi hf hfg

theorem tsum_subtype {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] (s : set β) (f : β → α) : (tsum fun (x : ↥s) => f ↑x) = tsum fun (x : β) => set.indicator s f x :=
  tsum_eq_tsum_of_has_sum_iff_has_sum fun (_x : α) => has_sum_subtype_iff_indicator

theorem tsum_add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] {f : β → α} {g : β → α} [has_continuous_add α] (hf : summable f) (hg : summable g) : (tsum fun (b : β) => f b + g b) = (tsum fun (b : β) => f b) + tsum fun (b : β) => g b :=
  has_sum.tsum_eq (has_sum.add (summable.has_sum hf) (summable.has_sum hg))

theorem tsum_sum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [t2_space α] [has_continuous_add α] {f : γ → β → α} {s : finset γ} (hf : ∀ (i : γ), i ∈ s → summable (f i)) : (tsum fun (b : β) => finset.sum s fun (i : γ) => f i b) = finset.sum s fun (i : γ) => tsum fun (b : β) => f i b :=
  has_sum.tsum_eq (has_sum_sum fun (i : γ) (hi : i ∈ s) => summable.has_sum (hf i hi))

theorem tsum_sigma' {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] [has_continuous_add α] [regular_space α] {γ : β → Type u_3} {f : (sigma fun (b : β) => γ b) → α} (h₁ : ∀ (b : β), summable fun (c : γ b) => f (sigma.mk b c)) (h₂ : summable f) : (tsum fun (p : sigma fun (b : β) => γ b) => f p) = tsum fun (b : β) => tsum fun (c : γ b) => f (sigma.mk b c) :=
  Eq.symm (has_sum.tsum_eq (has_sum.sigma (summable.has_sum h₂) fun (b : β) => summable.has_sum (h₁ b)))

theorem tsum_prod' {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [t2_space α] [has_continuous_add α] [regular_space α] {f : β × γ → α} (h : summable f) (h₁ : ∀ (b : β), summable fun (c : γ) => f (b, c)) : (tsum fun (p : β × γ) => f p) = tsum fun (b : β) => tsum fun (c : γ) => f (b, c) :=
  Eq.symm (has_sum.tsum_eq (has_sum.prod_fiberwise (summable.has_sum h) fun (b : β) => summable.has_sum (h₁ b)))

theorem tsum_comm' {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [t2_space α] [has_continuous_add α] [regular_space α] {f : β → γ → α} (h : summable (function.uncurry f)) (h₁ : ∀ (b : β), summable (f b)) (h₂ : ∀ (c : γ), summable fun (b : β) => f b c) : (tsum fun (c : γ) => tsum fun (b : β) => f b c) = tsum fun (b : β) => tsum fun (c : γ) => f b c := sorry

/-- You can compute a sum over an encodably type by summing over the natural numbers and
  taking a supremum. This is useful for outer measures. -/
theorem tsum_supr_decode2 {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [t2_space α] [encodable γ] [complete_lattice β] (m : β → α) (m0 : m ⊥ = 0) (s : γ → β) : (tsum fun (i : ℕ) => m (supr fun (b : γ) => supr fun (H : b ∈ encodable.decode2 γ i) => s b)) =
  tsum fun (b : γ) => m (s b) := sorry

/-- `tsum_supr_decode2` specialized to the complete lattice of sets. -/
theorem tsum_Union_decode2 {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [t2_space α] [encodable γ] (m : set β → α) (m0 : m ∅ = 0) (s : γ → set β) : (tsum fun (i : ℕ) => m (set.Union fun (b : γ) => set.Union fun (H : b ∈ encodable.decode2 γ i) => s b)) =
  tsum fun (b : γ) => m (s b) :=
  tsum_supr_decode2 m m0 s

/-! Some properties about measure-like functions.
  These could also be functions defined on complete sublattices of sets, with the property
  that they are countably sub-additive.
  `R` will probably be instantiated with `(≤)` in all applications.
-/

/-- If a function is countably sub-additive then it is sub-additive on encodable types -/
theorem rel_supr_tsum {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_monoid α] [topological_space α] [t2_space α] [encodable γ] [complete_lattice β] (m : β → α) (m0 : m ⊥ = 0) (R : α → α → Prop) (m_supr : ∀ (s : ℕ → β), R (m (supr fun (i : ℕ) => s i)) (tsum fun (i : ℕ) => m (s i))) (s : γ → β) : R (m (supr fun (b : γ) => s b)) (tsum fun (b : γ) => m (s b)) := sorry

/-- If a function is countably sub-additive then it is sub-additive on finite sets -/
theorem rel_supr_sum {α : Type u_1} {β : Type u_2} {δ : Type u_4} [add_comm_monoid α] [topological_space α] [t2_space α] [complete_lattice β] (m : β → α) (m0 : m ⊥ = 0) (R : α → α → Prop) (m_supr : ∀ (s : ℕ → β), R (m (supr fun (i : ℕ) => s i)) (tsum fun (i : ℕ) => m (s i))) (s : δ → β) (t : finset δ) : R (m (supr fun (d : δ) => supr fun (H : d ∈ t) => s d)) (finset.sum t fun (d : δ) => m (s d)) := sorry

/-- If a function is countably sub-additive then it is binary sub-additive -/
theorem rel_sup_add {α : Type u_1} {β : Type u_2} [add_comm_monoid α] [topological_space α] [t2_space α] [complete_lattice β] (m : β → α) (m0 : m ⊥ = 0) (R : α → α → Prop) (m_supr : ∀ (s : ℕ → β), R (m (supr fun (i : ℕ) => s i)) (tsum fun (i : ℕ) => m (s i))) (s₁ : β) (s₂ : β) : R (m (s₁ ⊔ s₂)) (m s₁ + m s₂) := sorry

theorem pi.has_sum {α : Type u_1} {ι : Type u_5} {π : α → Type u_6} [(x : α) → add_comm_monoid (π x)] [(x : α) → topological_space (π x)] {f : ι → (x : α) → π x} {g : (x : α) → π x} : has_sum f g ↔ ∀ (x : α), has_sum (fun (i : ι) => f i x) (g x) := sorry

theorem pi.summable {α : Type u_1} {ι : Type u_5} {π : α → Type u_6} [(x : α) → add_comm_monoid (π x)] [(x : α) → topological_space (π x)] {f : ι → (x : α) → π x} : summable f ↔ ∀ (x : α), summable fun (i : ι) => f i x := sorry

theorem tsum_apply {α : Type u_1} {ι : Type u_5} {π : α → Type u_6} [(x : α) → add_comm_monoid (π x)] [(x : α) → topological_space (π x)] [∀ (x : α), t2_space (π x)] {f : ι → (x : α) → π x} {x : α} (hf : summable f) : tsum (fun (i : ι) => f i) x = tsum fun (i : ι) => f i x :=
  Eq.symm (has_sum.tsum_eq (iff.mp pi.has_sum (summable.has_sum hf) x))

-- `by simpa using` speeds up elaboration. Why?

theorem has_sum.neg {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} {a : α} (h : has_sum f a) : has_sum (fun (b : β) => -f b) (-a) :=
  eq.mpr (id (Eq.refl (has_sum (fun (b : β) => -f b) (-a))))
    (eq.mp (Eq.refl (has_sum (⇑(-add_monoid_hom.id α) ∘ f) (coe_fn (-add_monoid_hom.id α) a)))
      (has_sum.map h (-add_monoid_hom.id α) continuous_neg))

theorem summable.neg {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} (hf : summable f) : summable fun (b : β) => -f b :=
  has_sum.summable (has_sum.neg (summable.has_sum hf))

theorem summable.of_neg {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} (hf : summable fun (b : β) => -f b) : summable f := sorry

theorem summable_neg_iff {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} : (summable fun (b : β) => -f b) ↔ summable f :=
  { mp := summable.of_neg, mpr := summable.neg }

theorem has_sum.sub {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} {g : β → α} {a₁ : α} {a₂ : α} (hf : has_sum f a₁) (hg : has_sum g a₂) : has_sum (fun (b : β) => f b - g b) (a₁ - a₂) := sorry

theorem summable.sub {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} {g : β → α} (hf : summable f) (hg : summable g) : summable fun (b : β) => f b - g b :=
  has_sum.summable (has_sum.sub (summable.has_sum hf) (summable.has_sum hg))

theorem has_sum.has_sum_compl_iff {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} {a₁ : α} {a₂ : α} {s : set β} (hf : has_sum (f ∘ coe) a₁) : has_sum (f ∘ coe) a₂ ↔ has_sum f (a₁ + a₂) := sorry

theorem has_sum.has_sum_iff_compl {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} {a₁ : α} {a₂ : α} {s : set β} (hf : has_sum (f ∘ coe) a₁) : has_sum f a₂ ↔ has_sum (f ∘ coe) (a₂ - a₁) := sorry

theorem summable.summable_compl_iff {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} {s : set β} (hf : summable (f ∘ coe)) : summable (f ∘ coe) ↔ summable f := sorry

protected theorem finset.has_sum_compl_iff {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} {a : α} (s : finset β) : has_sum (fun (x : Subtype fun (x : β) => ¬x ∈ s) => f ↑x) a ↔ has_sum f (a + finset.sum s fun (i : β) => f i) := sorry

protected theorem finset.has_sum_iff_compl {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} {a : α} (s : finset β) : has_sum f a ↔ has_sum (fun (x : Subtype fun (x : β) => ¬x ∈ s) => f ↑x) (a - finset.sum s fun (i : β) => f i) :=
  has_sum.has_sum_iff_compl (finset.has_sum s f)

protected theorem finset.summable_compl_iff {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} (s : finset β) : (summable fun (x : Subtype fun (x : β) => ¬x ∈ s) => f ↑x) ↔ summable f :=
  summable.summable_compl_iff (finset.summable s f)

theorem set.finite.summable_compl_iff {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} {s : set β} (hs : set.finite s) : summable (f ∘ coe) ↔ summable f :=
  summable.summable_compl_iff (set.finite.summable hs f)

theorem tsum_neg {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} [t2_space α] (hf : summable f) : (tsum fun (b : β) => -f b) = -tsum fun (b : β) => f b :=
  has_sum.tsum_eq (has_sum.neg (summable.has_sum hf))

theorem tsum_sub {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} {g : β → α} [t2_space α] (hf : summable f) (hg : summable g) : (tsum fun (b : β) => f b - g b) = (tsum fun (b : β) => f b) - tsum fun (b : β) => g b :=
  has_sum.tsum_eq (has_sum.sub (summable.has_sum hf) (summable.has_sum hg))

theorem tsum_add_tsum_compl {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} [t2_space α] {s : set β} (hs : summable (f ∘ coe)) (hsc : summable (f ∘ coe)) : ((tsum fun (x : ↥s) => f ↑x) + tsum fun (x : ↥(sᶜ)) => f ↑x) = tsum fun (x : β) => f x :=
  Eq.symm (has_sum.tsum_eq (has_sum.add_compl (summable.has_sum hs) (summable.has_sum hsc)))

theorem sum_add_tsum_compl {α : Type u_1} {β : Type u_2} [add_comm_group α] [topological_space α] [topological_add_group α] {f : β → α} [t2_space α] {s : finset β} (hf : summable f) : ((finset.sum s fun (x : β) => f x) + tsum fun (x : ↥(↑sᶜ)) => f ↑x) = tsum fun (x : β) => f x :=
  Eq.symm
    (has_sum.tsum_eq
      (has_sum.add_compl (finset.has_sum s f) (summable.has_sum (iff.mpr (finset.summable_compl_iff s) hf))))

/-!
### Sums on subtypes

If `s` is a finset of `α`, we show that the summability of `f` in the whole space and on the subtype
`univ - s` are equivalent, and relate their sums. For a function defined on `ℕ`, we deduce the
formula `(∑ i in range k, f i) + (∑' i, f (i + k)) = (∑' i, f i)`, in `sum_add_tsum_nat_add`.
-/

theorem has_sum_nat_add_iff {α : Type u_1} [add_comm_group α] [topological_space α] [topological_add_group α] {f : ℕ → α} (k : ℕ) {a : α} : has_sum (fun (n : ℕ) => f (n + k)) a ↔ has_sum f (a + finset.sum (finset.range k) fun (i : ℕ) => f i) := sorry

theorem summable_nat_add_iff {α : Type u_1} [add_comm_group α] [topological_space α] [topological_add_group α] {f : ℕ → α} (k : ℕ) : (summable fun (n : ℕ) => f (n + k)) ↔ summable f :=
  iff.symm
    (equiv.summable_iff_of_has_sum_iff (equiv.add_right (finset.sum (finset.range k) fun (i : ℕ) => f i))
      fun (a : α) => iff.symm (has_sum_nat_add_iff k))

theorem has_sum_nat_add_iff' {α : Type u_1} [add_comm_group α] [topological_space α] [topological_add_group α] {f : ℕ → α} (k : ℕ) {a : α} : has_sum (fun (n : ℕ) => f (n + k)) (a - finset.sum (finset.range k) fun (i : ℕ) => f i) ↔ has_sum f a := sorry

theorem sum_add_tsum_nat_add {α : Type u_1} [add_comm_group α] [topological_space α] [topological_add_group α] [t2_space α] {f : ℕ → α} (k : ℕ) (h : summable f) : ((finset.sum (finset.range k) fun (i : ℕ) => f i) + tsum fun (i : ℕ) => f (i + k)) = tsum fun (i : ℕ) => f i := sorry

theorem tsum_eq_zero_add {α : Type u_1} [add_comm_group α] [topological_space α] [topological_add_group α] [t2_space α] {f : ℕ → α} (hf : summable f) : (tsum fun (b : ℕ) => f b) = f 0 + tsum fun (b : ℕ) => f (b + 1) := sorry

/-- For `f : ℕ → α`, then `∑' k, f (k + i)` tends to zero. This does not require a summability
assumption on `f`, as otherwise all sums are zero. -/
theorem tendsto_sum_nat_add {α : Type u_1} [add_comm_group α] [topological_space α] [topological_add_group α] [t2_space α] (f : ℕ → α) : filter.tendsto (fun (i : ℕ) => tsum fun (k : ℕ) => f (k + i)) filter.at_top (nhds 0) := sorry

theorem has_sum.mul_left {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α] [topological_semiring α] {f : β → α} {a₁ : α} (a₂ : α) (h : has_sum f a₁) : has_sum (fun (b : β) => a₂ * f b) (a₂ * a₁) :=
  eq.mpr (id (Eq.refl (has_sum (fun (b : β) => a₂ * f b) (a₂ * a₁))))
    (eq.mp (Eq.refl (has_sum (⇑(add_monoid_hom.mul_left a₂) ∘ f) (coe_fn (add_monoid_hom.mul_left a₂) a₁)))
      (has_sum.map h (add_monoid_hom.mul_left a₂) (continuous.mul continuous_const continuous_id)))

theorem has_sum.mul_right {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α] [topological_semiring α] {f : β → α} {a₁ : α} (a₂ : α) (hf : has_sum f a₁) : has_sum (fun (b : β) => f b * a₂) (a₁ * a₂) :=
  eq.mpr (id (Eq.refl (has_sum (fun (b : β) => f b * a₂) (a₁ * a₂))))
    (eq.mp (Eq.refl (has_sum (⇑(add_monoid_hom.mul_right a₂) ∘ f) (coe_fn (add_monoid_hom.mul_right a₂) a₁)))
      (has_sum.map hf (add_monoid_hom.mul_right a₂) (continuous.mul continuous_id continuous_const)))

theorem summable.mul_left {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α] [topological_semiring α] {f : β → α} (a : α) (hf : summable f) : summable fun (b : β) => a * f b :=
  has_sum.summable (has_sum.mul_left a (summable.has_sum hf))

theorem summable.mul_right {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α] [topological_semiring α] {f : β → α} (a : α) (hf : summable f) : summable fun (b : β) => f b * a :=
  has_sum.summable (has_sum.mul_right a (summable.has_sum hf))

theorem summable.tsum_mul_left {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α] [topological_semiring α] {f : β → α} [t2_space α] (a : α) (hf : summable f) : (tsum fun (b : β) => a * f b) = a * tsum fun (b : β) => f b :=
  has_sum.tsum_eq (has_sum.mul_left a (summable.has_sum hf))

theorem summable.tsum_mul_right {α : Type u_1} {β : Type u_2} [semiring α] [topological_space α] [topological_semiring α] {f : β → α} [t2_space α] (a : α) (hf : summable f) : (tsum fun (b : β) => f b * a) = (tsum fun (b : β) => f b) * a :=
  has_sum.tsum_eq (has_sum.mul_right a (summable.has_sum hf))

theorem has_sum.smul {α : Type u_1} {β : Type u_2} {R : Type u_5} [semiring R] [topological_space R] [topological_space α] [add_comm_monoid α] [semimodule R α] [topological_semimodule R α] {f : β → α} {a : α} {r : R} (hf : has_sum f a) : has_sum (fun (z : β) => r • f z) (r • a) :=
  has_sum.map hf (const_smul_hom α r) (continuous.smul continuous_const continuous_id)

theorem summable.smul {α : Type u_1} {β : Type u_2} {R : Type u_5} [semiring R] [topological_space R] [topological_space α] [add_comm_monoid α] [semimodule R α] [topological_semimodule R α] {f : β → α} {r : R} (hf : summable f) : summable fun (z : β) => r • f z :=
  has_sum.summable (has_sum.smul (summable.has_sum hf))

theorem tsum_smul {α : Type u_1} {β : Type u_2} {R : Type u_5} [semiring R] [topological_space R] [topological_space α] [add_comm_monoid α] [semimodule R α] [topological_semimodule R α] {f : β → α} [t2_space α] {r : R} (hf : summable f) : (tsum fun (z : β) => r • f z) = r • tsum fun (z : β) => f z :=
  has_sum.tsum_eq (has_sum.smul (summable.has_sum hf))

theorem has_sum.div_const {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α] [topological_semiring α] {f : β → α} {a : α} (h : has_sum f a) (b : α) : has_sum (fun (x : β) => f x / b) (a / b) := sorry

theorem has_sum_mul_left_iff {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α] [topological_semiring α] {f : β → α} {a₁ : α} {a₂ : α} (h : a₂ ≠ 0) : has_sum f a₁ ↔ has_sum (fun (b : β) => a₂ * f b) (a₂ * a₁) := sorry

theorem has_sum_mul_right_iff {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α] [topological_semiring α] {f : β → α} {a₁ : α} {a₂ : α} (h : a₂ ≠ 0) : has_sum f a₁ ↔ has_sum (fun (b : β) => f b * a₂) (a₁ * a₂) := sorry

theorem summable_mul_left_iff {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α] [topological_semiring α] {f : β → α} {a : α} (h : a ≠ 0) : summable f ↔ summable fun (b : β) => a * f b := sorry

theorem summable_mul_right_iff {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α] [topological_semiring α] {f : β → α} {a : α} (h : a ≠ 0) : summable f ↔ summable fun (b : β) => f b * a := sorry

theorem tsum_mul_left {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α] [topological_semiring α] {f : β → α} {a : α} [t2_space α] : (tsum fun (x : β) => a * f x) = a * tsum fun (x : β) => f x := sorry

theorem tsum_mul_right {α : Type u_1} {β : Type u_2} [division_ring α] [topological_space α] [topological_semiring α] {f : β → α} {a : α} [t2_space α] : (tsum fun (x : β) => f x * a) = (tsum fun (x : β) => f x) * a := sorry

theorem has_sum_le {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} {g : β → α} {a₁ : α} {a₂ : α} (h : ∀ (b : β), f b ≤ g b) (hf : has_sum f a₁) (hg : has_sum g a₂) : a₁ ≤ a₂ :=
  le_of_tendsto_of_tendsto' hf hg fun (s : finset β) => finset.sum_le_sum fun (b : β) (_x : b ∈ s) => h b

theorem has_sum_le_inj {α : Type u_1} {β : Type u_2} {γ : Type u_3} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} {a₁ : α} {a₂ : α} {g : γ → α} (i : β → γ) (hi : function.injective i) (hs : ∀ (c : γ), ¬c ∈ set.range i → 0 ≤ g c) (h : ∀ (b : β), f b ≤ g (i b)) (hf : has_sum f a₁) (hg : has_sum g a₂) : a₁ ≤ a₂ := sorry

theorem tsum_le_tsum_of_inj {α : Type u_1} {β : Type u_2} {γ : Type u_3} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} {g : γ → α} (i : β → γ) (hi : function.injective i) (hs : ∀ (c : γ), ¬c ∈ set.range i → 0 ≤ g c) (h : ∀ (b : β), f b ≤ g (i b)) (hf : summable f) (hg : summable g) : tsum f ≤ tsum g :=
  has_sum_le_inj i hi hs h (summable.has_sum hf) (summable.has_sum hg)

theorem sum_le_has_sum {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {a : α} {f : β → α} (s : finset β) (hs : ∀ (b : β), ¬b ∈ s → 0 ≤ f b) (hf : has_sum f a) : (finset.sum s fun (b : β) => f b) ≤ a := sorry

theorem le_has_sum {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} {a : α} (hf : has_sum f a) (b : β) (hb : ∀ (b' : β), b' ≠ b → 0 ≤ f b') : f b ≤ a := sorry

theorem sum_le_tsum {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} (s : finset β) (hs : ∀ (b : β), ¬b ∈ s → 0 ≤ f b) (hf : summable f) : (finset.sum s fun (b : β) => f b) ≤ tsum f :=
  sum_le_has_sum s hs (summable.has_sum hf)

theorem le_tsum {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} (hf : summable f) (b : β) (hb : ∀ (b' : β), b' ≠ b → 0 ≤ f b') : f b ≤ tsum fun (b : β) => f b :=
  le_has_sum (summable.has_sum hf) b hb

theorem tsum_le_tsum {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} {g : β → α} (h : ∀ (b : β), f b ≤ g b) (hf : summable f) (hg : summable g) : (tsum fun (b : β) => f b) ≤ tsum fun (b : β) => g b :=
  has_sum_le h (summable.has_sum hf) (summable.has_sum hg)

theorem has_sum.nonneg {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {g : β → α} {a : α} (h : ∀ (b : β), 0 ≤ g b) (ha : has_sum g a) : 0 ≤ a :=
  has_sum_le h has_sum_zero ha

theorem has_sum.nonpos {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {g : β → α} {a : α} (h : ∀ (b : β), g b ≤ 0) (ha : has_sum g a) : a ≤ 0 :=
  has_sum_le h ha has_sum_zero

theorem tsum_nonneg {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {g : β → α} (h : ∀ (b : β), 0 ≤ g b) : 0 ≤ tsum fun (b : β) => g b := sorry

theorem tsum_nonpos {α : Type u_1} {β : Type u_2} [ordered_add_comm_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} (h : ∀ (b : β), f b ≤ 0) : (tsum fun (b : β) => f b) ≤ 0 := sorry

theorem le_has_sum' {α : Type u_1} {β : Type u_2} [canonically_ordered_add_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} {a : α} (hf : has_sum f a) (b : β) : f b ≤ a :=
  le_has_sum hf b fun (_x : β) (_x_1 : _x ≠ b) => zero_le (f _x)

theorem le_tsum' {α : Type u_1} {β : Type u_2} [canonically_ordered_add_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} (hf : summable f) (b : β) : f b ≤ tsum fun (b : β) => f b :=
  le_tsum hf b fun (_x : β) (_x_1 : _x ≠ b) => zero_le (f _x)

theorem has_sum_zero_iff {α : Type u_1} {β : Type u_2} [canonically_ordered_add_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} : has_sum f 0 ↔ ∀ (x : β), f x = 0 := sorry

theorem tsum_eq_zero_iff {α : Type u_1} {β : Type u_2} [canonically_ordered_add_monoid α] [topological_space α] [order_closed_topology α] {f : β → α} (hf : summable f) : (tsum fun (i : β) => f i) = 0 ↔ ∀ (x : β), f x = 0 := sorry

theorem summable_iff_cauchy_seq_finset {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α] [complete_space α] {f : β → α} : summable f ↔ cauchy_seq fun (s : finset β) => finset.sum s fun (b : β) => f b :=
  iff.symm cauchy_map_iff_exists_tendsto

theorem cauchy_seq_finset_iff_vanishing {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α] [uniform_add_group α] {f : β → α} : (cauchy_seq fun (s : finset β) => finset.sum s fun (b : β) => f b) ↔
  ∀ (e : set α), e ∈ nhds 0 → ∃ (s : finset β), ∀ (t : finset β), disjoint t s → (finset.sum t fun (b : β) => f b) ∈ e := sorry

theorem summable_iff_vanishing {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α] [uniform_add_group α] {f : β → α} [complete_space α] : summable f ↔
  ∀ (e : set α), e ∈ nhds 0 → ∃ (s : finset β), ∀ (t : finset β), disjoint t s → (finset.sum t fun (b : β) => f b) ∈ e := sorry

/- TODO: generalize to monoid with a uniform continuous subtraction operator: `(a + b) - b = a` -/

theorem summable.summable_of_eq_zero_or_self {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α] [uniform_add_group α] {f : β → α} {g : β → α} [complete_space α] (hf : summable f) (h : ∀ (b : β), g b = 0 ∨ g b = f b) : summable g := sorry

protected theorem summable.indicator {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α] [uniform_add_group α] {f : β → α} [complete_space α] (hf : summable f) (s : set β) : summable (set.indicator s f) :=
  summable.summable_of_eq_zero_or_self hf (set.indicator_eq_zero_or_self s f)

theorem summable.comp_injective {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_group α] [uniform_space α] [uniform_add_group α] {f : β → α} [complete_space α] {i : γ → β} (hf : summable f) (hi : function.injective i) : summable (f ∘ i) := sorry

theorem summable.subtype {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α] [uniform_add_group α] {f : β → α} [complete_space α] (hf : summable f) (s : set β) : summable (f ∘ coe) :=
  summable.comp_injective hf subtype.coe_injective

theorem summable_subtype_and_compl {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α] [uniform_add_group α] {f : β → α} [complete_space α] {s : set β} : ((summable fun (x : ↥s) => f ↑x) ∧ summable fun (x : ↥(sᶜ)) => f ↑x) ↔ summable f :=
  { mp := iff.mpr and_imp summable.add_compl,
    mpr := fun (h : summable f) => { left := summable.subtype h s, right := summable.subtype h (sᶜ) } }

theorem summable.sigma_factor {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α] [uniform_add_group α] [complete_space α] {γ : β → Type u_3} {f : (sigma fun (b : β) => γ b) → α} (ha : summable f) (b : β) : summable fun (c : γ b) => f (sigma.mk b c) :=
  summable.comp_injective ha sigma_mk_injective

theorem summable.sigma {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α] [uniform_add_group α] [complete_space α] [regular_space α] {γ : β → Type u_3} {f : (sigma fun (b : β) => γ b) → α} (ha : summable f) : summable fun (b : β) => tsum fun (c : γ b) => f (sigma.mk b c) :=
  summable.sigma' ha fun (b : β) => summable.sigma_factor ha b

theorem summable.prod_factor {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_group α] [uniform_space α] [uniform_add_group α] [complete_space α] {f : β × γ → α} (h : summable f) (b : β) : summable fun (c : γ) => f (b, c) :=
  summable.comp_injective h
    fun (c₁ c₂ : γ) (h : (fun (c : γ) => (b, c)) c₁ = (fun (c : γ) => (b, c)) c₂) => and.right (iff.mp prod.ext_iff h)

theorem tsum_sigma {α : Type u_1} {β : Type u_2} [add_comm_group α] [uniform_space α] [uniform_add_group α] [complete_space α] [regular_space α] {γ : β → Type u_3} {f : (sigma fun (b : β) => γ b) → α} (ha : summable f) : (tsum fun (p : sigma fun (b : β) => γ b) => f p) = tsum fun (b : β) => tsum fun (c : γ b) => f (sigma.mk b c) :=
  tsum_sigma' (fun (b : β) => summable.sigma_factor ha b) ha

theorem tsum_prod {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_group α] [uniform_space α] [uniform_add_group α] [complete_space α] [regular_space α] {f : β × γ → α} (h : summable f) : (tsum fun (p : β × γ) => f p) = tsum fun (b : β) => tsum fun (c : γ) => f (b, c) :=
  tsum_prod' h (summable.prod_factor h)

theorem tsum_comm {α : Type u_1} {β : Type u_2} {γ : Type u_3} [add_comm_group α] [uniform_space α] [uniform_add_group α] [complete_space α] [regular_space α] {f : β → γ → α} (h : summable (function.uncurry f)) : (tsum fun (c : γ) => tsum fun (b : β) => f b c) = tsum fun (b : β) => tsum fun (c : γ) => f b c :=
  tsum_comm' h (summable.prod_factor h) (summable.prod_factor (summable.prod_symm h))

theorem summable.vanishing {α : Type u_1} {G : Type u_5} [topological_space G] [add_comm_group G] [topological_add_group G] {f : α → G} (hf : summable f) {e : set G} (he : e ∈ nhds 0) : ∃ (s : finset α), ∀ (t : finset α), disjoint t s → (finset.sum t fun (k : α) => f k) ∈ e := sorry

/-- Series divergence test: if `f` is a convergent series, then `f x` tends to zero along
`cofinite`. -/
theorem summable.tendsto_cofinite_zero {α : Type u_1} {G : Type u_5} [topological_space G] [add_comm_group G] [topological_add_group G] {f : α → G} (hf : summable f) : filter.tendsto f filter.cofinite (nhds 0) := sorry

theorem summable_abs_iff {α : Type u_1} {β : Type u_2} [linear_ordered_add_comm_group β] [uniform_space β] [uniform_add_group β] [complete_space β] {f : α → β} : (summable fun (x : α) => abs (f x)) ↔ summable f := sorry

theorem summable.of_abs {α : Type u_1} {β : Type u_2} [linear_ordered_add_comm_group β] [uniform_space β] [uniform_add_group β] [complete_space β] {f : α → β} : (summable fun (x : α) => abs (f x)) → summable f :=
  iff.mp summable_abs_iff

/-- If the extended distance between consequent points of a sequence is estimated
by a summable series of `nnreal`s, then the original sequence is a Cauchy sequence. -/
theorem cauchy_seq_of_edist_le_of_summable {α : Type u_1} [emetric_space α] {f : ℕ → α} (d : ℕ → nnreal) (hf : ∀ (n : ℕ), edist (f n) (f (Nat.succ n)) ≤ ↑(d n)) (hd : summable d) : cauchy_seq f := sorry

/-- If the distance between consequent points of a sequence is estimated by a summable series,
then the original sequence is a Cauchy sequence. -/
theorem cauchy_seq_of_dist_le_of_summable {α : Type u_1} [metric_space α] {f : ℕ → α} (d : ℕ → ℝ) (hf : ∀ (n : ℕ), dist (f n) (f (Nat.succ n)) ≤ d n) (hd : summable d) : cauchy_seq f := sorry

theorem cauchy_seq_of_summable_dist {α : Type u_1} [metric_space α] {f : ℕ → α} (h : summable fun (n : ℕ) => dist (f n) (f (Nat.succ n))) : cauchy_seq f :=
  cauchy_seq_of_dist_le_of_summable (fun (n : ℕ) => dist (f n) (f (Nat.succ n)))
    (fun (_x : ℕ) => le_refl (dist (f _x) (f (Nat.succ _x)))) h

theorem dist_le_tsum_of_dist_le_of_tendsto {α : Type u_1} [metric_space α] {f : ℕ → α} (d : ℕ → ℝ) (hf : ∀ (n : ℕ), dist (f n) (f (Nat.succ n)) ≤ d n) (hd : summable d) {a : α} (ha : filter.tendsto f filter.at_top (nhds a)) (n : ℕ) : dist (f n) a ≤ tsum fun (m : ℕ) => d (n + m) := sorry

theorem dist_le_tsum_of_dist_le_of_tendsto₀ {α : Type u_1} [metric_space α] {f : ℕ → α} (d : ℕ → ℝ) (hf : ∀ (n : ℕ), dist (f n) (f (Nat.succ n)) ≤ d n) (hd : summable d) {a : α} (ha : filter.tendsto f filter.at_top (nhds a)) : dist (f 0) a ≤ tsum d := sorry

theorem dist_le_tsum_dist_of_tendsto {α : Type u_1} [metric_space α] {f : ℕ → α} (h : summable fun (n : ℕ) => dist (f n) (f (Nat.succ n))) {a : α} (ha : filter.tendsto f filter.at_top (nhds a)) (n : ℕ) : dist (f n) a ≤ tsum fun (m : ℕ) => dist (f (n + m)) (f (Nat.succ (n + m))) :=
  (fun (this : dist (f n) a ≤ tsum fun (m : ℕ) => (fun (n : ℕ) => dist (f n) (f (Nat.succ n))) (n + m)) => this)
    (dist_le_tsum_of_dist_le_of_tendsto (fun (n : ℕ) => dist (f n) (f (Nat.succ n)))
      (fun (_x : ℕ) => le_refl (dist (f _x) (f (Nat.succ _x)))) h ha n)

theorem dist_le_tsum_dist_of_tendsto₀ {α : Type u_1} [metric_space α] {f : ℕ → α} (h : summable fun (n : ℕ) => dist (f n) (f (Nat.succ n))) {a : α} (ha : filter.tendsto f filter.at_top (nhds a)) : dist (f 0) a ≤ tsum fun (n : ℕ) => dist (f n) (f (Nat.succ n)) := sorry


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

Computational realization of filters (experimental).
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.order.filter.cofinite
import Mathlib.PostPort

universes u_1 u_2 l u_3 u_4 u_5

namespace Mathlib

/-- A `cfilter α σ` is a realization of a filter (base) on `α`,
  represented by a type `σ` together with operations for the top element and
  the binary inf operation. -/
structure cfilter (α : Type u_1) (σ : Type u_2) [partial_order α]
where
  f : σ → α
  pt : σ
  inf : σ → σ → σ
  inf_le_left : ∀ (a b : σ), f (inf a b) ≤ f a
  inf_le_right : ∀ (a b : σ), f (inf a b) ≤ f b

namespace cfilter

protected instance has_coe_to_fun {α : Type u_1} {σ : Type u_3} [partial_order α] : has_coe_to_fun (cfilter α σ) :=
  has_coe_to_fun.mk (fun (x : cfilter α σ) => σ → α) f

@[simp] theorem coe_mk {α : Type u_1} {σ : Type u_3} [partial_order α] (f : σ → α) (pt : σ) (inf : σ → σ → σ) (h₁ : ∀ (a b : σ), f (inf a b) ≤ f a) (h₂ : ∀ (a b : σ), f (inf a b) ≤ f b) (a : σ) : coe_fn (mk f pt inf h₁ h₂) a = f a :=
  rfl

/-- Map a cfilter to an equivalent representation type. -/
def of_equiv {α : Type u_1} {σ : Type u_3} {τ : Type u_4} [partial_order α] (E : σ ≃ τ) : cfilter α σ → cfilter α τ :=
  sorry

@[simp] theorem of_equiv_val {α : Type u_1} {σ : Type u_3} {τ : Type u_4} [partial_order α] (E : σ ≃ τ) (F : cfilter α σ) (a : τ) : coe_fn (of_equiv E F) a = coe_fn F (coe_fn (equiv.symm E) a) := sorry

/-- The filter represented by a `cfilter` is the collection of supersets of
  elements of the filter base. -/
def to_filter {α : Type u_1} {σ : Type u_3} (F : cfilter (set α) σ) : filter α :=
  filter.mk (set_of fun (a : set α) => ∃ (b : σ), coe_fn F b ⊆ a) sorry sorry sorry

@[simp] theorem mem_to_filter_sets {α : Type u_1} {σ : Type u_3} (F : cfilter (set α) σ) {a : set α} : a ∈ to_filter F ↔ ∃ (b : σ), coe_fn F b ⊆ a :=
  iff.rfl

end cfilter

/-- A realizer for filter `f` is a cfilter which generates `f`. -/
structure filter.realizer {α : Type u_1} (f : filter α)
where
  σ : Type u_5
  F : cfilter (set α) σ
  eq : cfilter.to_filter F = f

protected def cfilter.to_realizer {α : Type u_1} {σ : Type u_3} (F : cfilter (set α) σ) : filter.realizer (cfilter.to_filter F) :=
  filter.realizer.mk σ F sorry

namespace filter.realizer

theorem mem_sets {α : Type u_1} {f : filter α} (F : realizer f) {a : set α} : a ∈ f ↔ ∃ (b : σ F), coe_fn (F F) b ⊆ a := sorry

-- Used because it has better definitional equalities than the eq.rec proof

def of_eq {α : Type u_1} {f : filter α} {g : filter α} (e : f = g) (F : realizer f) : realizer g :=
  mk (σ F) (F F) sorry

/-- A filter realizes itself. -/
def of_filter {α : Type u_1} (f : filter α) : realizer f :=
  mk (↥(sets f))
    (cfilter.mk subtype.val { val := set.univ, property := univ_mem_sets } (fun (_x : ↥(sets f)) => sorry) sorry sorry)
    sorry

/-- Transfer a filter realizer to another realizer on a different base type. -/
def of_equiv {α : Type u_1} {τ : Type u_4} {f : filter α} (F : realizer f) (E : σ F ≃ τ) : realizer f :=
  mk τ (cfilter.of_equiv E (F F)) sorry

@[simp] theorem of_equiv_σ {α : Type u_1} {τ : Type u_4} {f : filter α} (F : realizer f) (E : σ F ≃ τ) : σ (of_equiv F E) = τ :=
  rfl

@[simp] theorem of_equiv_F {α : Type u_1} {τ : Type u_4} {f : filter α} (F : realizer f) (E : σ F ≃ τ) (s : τ) : coe_fn (F (of_equiv F E)) s = coe_fn (F F) (coe_fn (equiv.symm E) s) := sorry

/-- `unit` is a realizer for the principal filter -/
protected def principal {α : Type u_1} (s : set α) : realizer (principal s) :=
  mk Unit (cfilter.mk (fun (_x : Unit) => s) Unit.unit (fun (_x _x : Unit) => Unit.unit) sorry sorry) sorry

@[simp] theorem principal_σ {α : Type u_1} (s : set α) : σ (realizer.principal s) = Unit :=
  rfl

@[simp] theorem principal_F {α : Type u_1} (s : set α) (u : Unit) : coe_fn (F (realizer.principal s)) u = s :=
  rfl

/-- `unit` is a realizer for the top filter -/
protected def top {α : Type u_1} : realizer ⊤ :=
  of_eq principal_univ (realizer.principal set.univ)

@[simp] theorem top_σ {α : Type u_1} : σ realizer.top = Unit :=
  rfl

@[simp] theorem top_F {α : Type u_1} (u : Unit) : coe_fn (F realizer.top) u = set.univ :=
  rfl

/-- `unit` is a realizer for the bottom filter -/
protected def bot {α : Type u_1} : realizer ⊥ :=
  of_eq principal_empty (realizer.principal ∅)

@[simp] theorem bot_σ {α : Type u_1} : σ realizer.bot = Unit :=
  rfl

@[simp] theorem bot_F {α : Type u_1} (u : Unit) : coe_fn (F realizer.bot) u = ∅ :=
  rfl

/-- Construct a realizer for `map m f` given a realizer for `f` -/
protected def map {α : Type u_1} {β : Type u_2} (m : α → β) {f : filter α} (F : realizer f) : realizer (map m f) :=
  mk (σ F) (cfilter.mk (fun (s : σ F) => m '' coe_fn (F F) s) (cfilter.pt (F F)) (cfilter.inf (F F)) sorry sorry) sorry

@[simp] theorem map_σ {α : Type u_1} {β : Type u_2} (m : α → β) {f : filter α} (F : realizer f) : σ (realizer.map m F) = σ F :=
  rfl

@[simp] theorem map_F {α : Type u_1} {β : Type u_2} (m : α → β) {f : filter α} (F : realizer f) (s : σ (realizer.map m F)) : coe_fn (F (realizer.map m F)) s = m '' coe_fn (F F) s :=
  rfl

/-- Construct a realizer for `comap m f` given a realizer for `f` -/
protected def comap {α : Type u_1} {β : Type u_2} (m : α → β) {f : filter β} (F : realizer f) : realizer (comap m f) :=
  mk (σ F) (cfilter.mk (fun (s : σ F) => m ⁻¹' coe_fn (F F) s) (cfilter.pt (F F)) (cfilter.inf (F F)) sorry sorry) sorry

/-- Construct a realizer for the sup of two filters -/
protected def sup {α : Type u_1} {f : filter α} {g : filter α} (F : realizer f) (G : realizer g) : realizer (f ⊔ g) :=
  mk (σ F × σ G)
    (cfilter.mk (fun (_x : σ F × σ G) => sorry) (cfilter.pt (F F), cfilter.pt (F G)) (fun (_x : σ F × σ G) => sorry) sorry
      sorry)
    sorry

/-- Construct a realizer for the inf of two filters -/
protected def inf {α : Type u_1} {f : filter α} {g : filter α} (F : realizer f) (G : realizer g) : realizer (f ⊓ g) :=
  mk (σ F × σ G)
    (cfilter.mk (fun (_x : σ F × σ G) => sorry) (cfilter.pt (F F), cfilter.pt (F G)) (fun (_x : σ F × σ G) => sorry) sorry
      sorry)
    sorry

/-- Construct a realizer for the cofinite filter -/
protected def cofinite {α : Type u_1} [DecidableEq α] : realizer cofinite :=
  mk (finset α) (cfilter.mk (fun (s : finset α) => set_of fun (a : α) => ¬a ∈ s) ∅ has_union.union sorry sorry) sorry

/-- Construct a realizer for filter bind -/
protected def bind {α : Type u_1} {β : Type u_2} {f : filter α} {m : α → filter β} (F : realizer f) (G : (i : α) → realizer (m i)) : realizer (bind f m) :=
  mk (sigma fun (s : σ F) => (i : α) → i ∈ coe_fn (F F) s → σ (G i))
    (cfilter.mk (fun (_x : sigma fun (s : σ F) => (i : α) → i ∈ coe_fn (F F) s → σ (G i)) => sorry)
      (sigma.mk (cfilter.pt (F F)) fun (i : α) (H : i ∈ coe_fn (F F) (cfilter.pt (F F))) => cfilter.pt (F (G i)))
      (fun (_x : sigma fun (s : σ F) => (i : α) → i ∈ coe_fn (F F) s → σ (G i)) => sorry) sorry sorry)
    sorry

/-- Construct a realizer for indexed supremum -/
protected def Sup {α : Type u_1} {β : Type u_2} {f : α → filter β} (F : (i : α) → realizer (f i)) : realizer (supr fun (i : α) => f i) :=
  let F' : realizer (supr fun (i : α) => f i) := of_eq sorry (realizer.bind realizer.top F);
  of_equiv F'
    ((fun (this : (sigma fun (u : Unit) => (i : α) → True → σ (F i)) ≃ ((i : α) → σ (F i))) => this)
      (equiv.mk (fun (_x : sigma fun (u : Unit) => (i : α) → True → σ (F i)) => sorry)
        (fun (f_1 : (i : α) → σ (F i)) => sigma.mk Unit.unit fun (i : α) (_x : True) => f_1 i) sorry sorry))

/-- Construct a realizer for the product of filters -/
protected def prod {α : Type u_1} {f : filter α} {g : filter α} (F : realizer f) (G : realizer g) : realizer (filter.prod f g) :=
  realizer.inf (realizer.comap prod.fst F) (realizer.comap prod.snd G)

theorem le_iff {α : Type u_1} {f : filter α} {g : filter α} (F : realizer f) (G : realizer g) : f ≤ g ↔ ∀ (b : σ G), ∃ (a : σ F), coe_fn (F F) a ≤ coe_fn (F G) b := sorry

theorem tendsto_iff {α : Type u_1} {β : Type u_2} (f : α → β) {l₁ : filter α} {l₂ : filter β} (L₁ : realizer l₁) (L₂ : realizer l₂) : tendsto f l₁ l₂ ↔ ∀ (b : σ L₂), ∃ (a : σ L₁), ∀ (x : α), x ∈ coe_fn (F L₁) a → f x ∈ coe_fn (F L₂) b :=
  iff.trans (le_iff (realizer.map f L₁) L₂)
    (forall_congr fun (b : σ L₂) => exists_congr fun (a : σ (realizer.map f L₁)) => set.image_subset_iff)

theorem ne_bot_iff {α : Type u_1} {f : filter α} (F : realizer f) : f ≠ ⊥ ↔ ∀ (a : σ F), set.nonempty (coe_fn (F F) a) := sorry