/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura

! This file was ported from Lean 3 source module init.ite_simp
! leanprover-community/mathlib commit 4a03bdeb31b3688c31d02d7ff8e0ff2e5d6174db
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
prelude
import Leanbin.Init.Data.Bool.Default

/-!
# Simplification lemmas for ite.

We don't prove them at logic.lean because it is easier to prove them using
the tactic framework.
-/

@[simp]
theorem if_true_right_eq_or (p : Prop) [h : Decidable p] (q : Prop) :
    (if p then q else True) = (¬p ∨ q) := by by_cases p <;> simp [h]
#align if_true_right_eq_or if_true_right_eq_or

@[simp]
theorem if_true_left_eq_or (p : Prop) [h : Decidable p] (q : Prop) :
    (if p then True else q) = (p ∨ q) := by by_cases p <;> simp [h]
#align if_true_left_eq_or if_true_left_eq_or

@[simp]
theorem if_false_right_eq_and (p : Prop) [h : Decidable p] (q : Prop) :
    (if p then q else False) = (p ∧ q) := by by_cases p <;> simp [h]
#align if_false_right_eq_and if_false_right_eq_and

@[simp]
theorem if_false_left_eq_and (p : Prop) [h : Decidable p] (q : Prop) :
    (if p then False else q) = (¬p ∧ q) := by by_cases p <;> simp [h]
#align if_false_left_eq_and if_false_left_eq_and


-- import data.set
import tactic
import data.set
import data.real.basic

-- dEAduction imports
import structures2
import utils
import compute

-- General principles :
-- Type should be defined as parameters, in order to be implicit everywhere
-- other parameters are implicit in definitions, i.e. defined using '{}' (e.g. {A : set X} )
-- but explicit everywhere else, i.e. defined using '()' (e.g. (A : set X) )
-- each definition must be an iff statement (since it will be called with 'rw' or 'symp_rw')

-- no Magic button ("compute")
/- dEAduction
Title
    Tautologies (tutoriel)
Author
    Frédéric Le Roux
Institution
    Université du Monde
Description
    Ce fichier contient exercices très simples qui peuvent servir à tester ou illustrer
    les effets des différents boutons logiques.
DefaultAvailableProof
    NONE
DefaultAvailableMagic
    Assumption
-/

-- logic names ['and', 'or', 'negate', 'implies', 'iff', 'forall', 'exists']
-- proofs names ['proof_methods', 'new_object', 'apply']
-- magic names ['compute', 'assumption']

local attribute [instance] classical.prop_decidable
---------------------------------------------
-- global parameters = implicit variables --
---------------------------------------------
section course
variables (P Q R: Prop) -- NOT global
notation [parsing_only] P ` \and ` Q := P ∧ Q
notation [parsing_only]  P ` \or ` Q := P ∨ Q
notation [parsing_only]  ` \not ` P := ¬ P
notation [parsing_only]  P ` \implies ` Q := P → Q
notation [parsing_only]  P ` \iff ` Q := P ↔ Q

------------------
-- COURSE TITLE --
------------------
-- namespace Tautologies

lemma exercise.implication (H: P → Q):
P → Q
:=
/- dEAduction
PrettyName
    Implication
Description
    Utilisation et démonstration d'une implication.
AvailableLogic
    implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.et (H: P ∧ Q):
P ∧ Q
:=
/- dEAduction
PrettyName
    Conjonction
Description
    Utilisation et démonstration d'une conjonction.
AvailableLogic
    and
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.ou_1 (H: P ∨ Q) (HP: P → R) (HQ: Q → R):
R
:=
/- dEAduction
PrettyName
    Disjonction I
Description
    Utilisation d'une disjonction.
AvailableLogic
    or implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.ou_2 (H: P):
P ∨ Q
:=
/- dEAduction
PrettyName
    Disjonction II
Description
    Démonstration d'une disjonction.
AvailableLogic
    or
AvailableMagic
    assumption
-/
begin
    sorry
end

variable S: Prop

lemma exercise.ou_3 (H: P ∨ Q) (HP: P → R) (HQ: Q → S):
R ∨ S
:=
/- dEAduction
PrettyName
    Disjonction III
Description
    Utilisation et démonstration d'une disjonction.
AvailableLogic
    or implies
AvailableMagic
    assumption
-/
begin
    sorry
end

variables (X: Type)

lemma exercise.quel_que_soit (P Q: X → Prop) (H: ∀ x, P x) (H': ∀ x, (P x → Q x)):
∀ x, Q x
:=
/- dEAduction
PrettyName
    Quel que soit
Description
    Utilisation et démonstration d'une propriété universelle.
AvailableLogic
    forall implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.Existe_0 : (∃ x: ℝ, x >0) :=
/- dEAduction
PrettyName
    Existence (a)
Description
    Démonstration d'une propriété existentielle.
AvailableLogic
    exists
AvailableMagic
    assumption
-/
begin
    todo
end

lemma exercise.Existe_b : (∃ a b: ℝ, a < b) :=
/- dEAduction
PrettyName
    Existence (b)
Description
    Démonstration d'une propriété existentielle.
AvailableLogic
    exists
AvailableMagic
    assumption
-/
begin
    todo
end

lemma exercise.Existe_1 (P Q: X → Prop) (x: X) (H: P x) (H': (P x → Q x)):
∃ x, Q x
:=
/- dEAduction
PrettyName
    Existence I
Description
    Démonstration d'une propriété existentielle.
AvailableLogic
    forall exists implies
AvailableMagic
    assumption
-/
begin
    sorry
end

lemma exercise.Existe_2 (P Q: X → Prop) (H: ∃ x, P x) (H': ∀ x, (P x → Q x)):
∃ x, Q x
:=
/- dEAduction
PrettyName
    Existence II
Description
    Utilisation et démonstration d'une propriété existentielle.
AvailableLogic
    forall exists implies
AvailableMagic
    assumption
-/
begin
    sorry
end

-- end Tautologies

end course


/-
Copyright (c) 2021 Eric Wieser. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Wieser
-/
import algebra.star.basic
import algebra.ring.pi
import algebra.module.pi

/-!
# `star` on pi types

We put a `has_star` structure on pi types that operates elementwise, such that it describes the
complex conjugation of vectors.
-/

universes u v w
variable {I : Type u}     -- The indexing type
variable {f : I → Type v} -- The family of types already equipped with instances

namespace pi

instance [Π i, has_star (f i)] : has_star (Π i, f i) :=
{ star := λ x i, star (x i) }

@[simp] lemma star_apply [Π i, has_star (f i)] (x : Π i, f i) (i : I) : star x i = star (x i) := rfl

lemma star_def [Π i, has_star (f i)] (x : Π i, f i) : star x = λ i, star (x i) := rfl

instance [Π i, has_involutive_star (f i)] : has_involutive_star (Π i, f i) :=
{ star_involutive := λ _, funext $ λ _, star_star _ }

instance [Π i, semigroup (f i)] [Π i, star_semigroup (f i)] : star_semigroup (Π i, f i) :=
{ star_mul := λ _ _, funext $ λ _, star_mul _ _ }

instance [Π i, add_monoid (f i)] [Π i, star_add_monoid (f i)] : star_add_monoid (Π i, f i) :=
{ star_add := λ _ _, funext $ λ _, star_add _ _ }

instance [Π i, non_unital_semiring (f i)] [Π i, star_ring (f i)] : star_ring (Π i, f i) :=
{ ..pi.star_add_monoid, ..(pi.star_semigroup : star_semigroup (Π i, f i)) }

instance {R : Type w}
  [Π i, has_scalar R (f i)] [has_star R] [Π i, has_star (f i)] [Π i, star_module R (f i)] :
  star_module R (Π i, f i) :=
{ star_smul := λ r x, funext $ λ i, star_smul r (x i) }

end pi


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

Additional theorems about the `vector` type.
-/
import data.vector data.list.basic data.sigma data.equiv.basic
       category.traversable

universes u
variables {n : ℕ}

namespace vector
variables {α : Type*}

attribute [simp] head_cons tail_cons

instance [inhabited α] : inhabited (vector α n) :=
⟨of_fn (λ _, default α)⟩

theorem to_list_injective : function.injective (@to_list α n) :=
subtype.val_injective

@[simp] theorem to_list_of_fn : ∀ {n} (f : fin n → α), to_list (of_fn f) = list.of_fn f
| 0     f := rfl
| (n+1) f := by rw [of_fn, list.of_fn_succ, to_list_cons, to_list_of_fn]

@[simp] theorem mk_to_list :
  ∀ (v : vector α n) h, (⟨to_list v, h⟩ : vector α n) = v
| ⟨l, h₁⟩ h₂ := rfl

theorem nth_eq_nth_le : ∀ (v : vector α n) (i),
  nth v i = v.to_list.nth_le i.1 (by rw to_list_length; exact i.2)
| ⟨l, h⟩ i := rfl

@[simp] theorem nth_of_fn {n} (f : fin n → α) (i) : nth (of_fn f) i = f i :=
by rw [nth_eq_nth_le, ← list.nth_le_of_fn f];
   congr; apply to_list_of_fn

@[simp] theorem of_fn_nth (v : vector α n) : of_fn (nth v) = v :=
begin
  rcases v with ⟨l, rfl⟩,
  apply to_list_injective,
  change nth ⟨l, eq.refl _⟩ with λ i, nth ⟨l, rfl⟩ i,
  simp [nth, list.of_fn_nth_le]
end

@[simp] theorem nth_tail : ∀ (v : vector α n.succ) (i : fin n),
  nth (tail v) i = nth v i.succ
| ⟨a::l, e⟩ ⟨i, h⟩ := by simp [nth_eq_nth_le]; refl

@[simp] theorem tail_of_fn {n : ℕ} (f : fin n.succ → α) :
  tail (of_fn f) = of_fn (λ i, f i.succ) :=
(of_fn_nth _).symm.trans $ by congr; funext i; simp

theorem head'_to_list : ∀ (v : vector α n.succ),
  (to_list v).head' = some (head v)
| ⟨a::l, e⟩ := rfl

def reverse (v : vector α n) : vector α n :=
⟨v.to_list.reverse, by simp⟩

@[simp] theorem nth_zero : ∀ (v : vector α n.succ), nth v 0 = head v
| ⟨a::l, e⟩ := rfl

@[simp] theorem head_of_fn
  {n : ℕ} (f : fin n.succ → α) : head (of_fn f) = f 0 :=
by rw [← nth_zero, nth_of_fn]

@[simp] theorem nth_cons_zero
  (a : α) (v : vector α n) : nth (a :: v) 0 = a :=
by simp [nth_zero]

@[simp] theorem nth_cons_succ
  (a : α) (v : vector α n) (i : fin n) : nth (a :: v) i.succ = nth v i :=
by rw [← nth_tail, tail_cons]

def m_of_fn {m} [monad m] {α : Type u} : ∀ {n}, (fin n → m α) → m (vector α n)
| 0     f := pure nil
| (n+1) f := do a ← f 0, v ← m_of_fn (λi, f i.succ), pure (a :: v)

theorem m_of_fn_pure {m} [monad m] [is_lawful_monad m] {α} :
  ∀ {n} (f : fin n → α), @m_of_fn m _ _ _ (λ i, pure (f i)) = pure (of_fn f)
| 0     f := rfl
| (n+1) f := by simp [m_of_fn, @m_of_fn_pure n, of_fn]

def mmap {m} [monad m] {α} {β : Type u} (f : α → m β) :
  ∀ {n}, vector α n → m (vector β n)
| _ ⟨[], rfl⟩   := pure nil
| _ ⟨a::l, rfl⟩ := do h' ← f a, t' ← mmap ⟨l, rfl⟩, pure (h' :: t')

@[simp] theorem mmap_nil {m} [monad m] {α β} (f : α → m β) :
  mmap f nil = pure nil := rfl

@[simp] theorem mmap_cons {m} [monad m] {α β} (f : α → m β) (a) :
  ∀ {n} (v : vector α n), mmap f (a::v) =
  do h' ← f a, t' ← mmap f v, pure (h' :: t')
| _ ⟨l, rfl⟩ := rfl

@[extensionality] theorem ext : ∀ {v w : vector α n}
  (h : ∀ m : fin n, vector.nth v m = vector.nth w m), v = w
| ⟨v, hv⟩ ⟨w, hw⟩ h := subtype.eq (list.ext_le (by rw [hv, hw])
  (λ m hm hn, h ⟨m, hv ▸ hm⟩))

instance zero_subsingleton : subsingleton (vector α 0) :=
⟨λ _ _, vector.ext (λ m, fin.elim0 m)⟩

def to_array : vector α n → array n α
| ⟨xs, h⟩ := cast (by rw h) xs.to_array

section insert_nth
variable {a : α}

def insert_nth (a : α) (i : fin (n+1)) (v : vector α n) : vector α (n+1) :=
⟨v.1.insert_nth i.1 a,
  begin
    rw [list.length_insert_nth, v.2],
    rw [v.2, ← nat.succ_le_succ_iff],
    exact i.2
  end⟩

lemma insert_nth_val {i : fin (n+1)} {v : vector α n} :
  (v.insert_nth a i).val = v.val.insert_nth i.1 a :=
rfl

@[simp] lemma remove_nth_val {i : fin n} :
  ∀{v : vector α n}, (remove_nth i v).val = v.val.remove_nth i.1
| ⟨l, hl⟩ := rfl

lemma remove_nth_insert_nth {v : vector α n} {i : fin (n+1)} : remove_nth i (insert_nth a i v) = v :=
subtype.eq $ list.remove_nth_insert_nth i.1 v.1

lemma remove_nth_insert_nth_ne {v : vector α (n+1)} :
  ∀{i j : fin (n+2)} (h : i ≠ j),
    remove_nth i (insert_nth a j v) = insert_nth a (i.pred_above j h.symm) (remove_nth (j.pred_above i h) v)
| ⟨i, hi⟩ ⟨j, hj⟩ ne :=
  begin
    have : i ≠ j := fin.vne_of_ne ne,
    refine subtype.eq _,
    dsimp [insert_nth, remove_nth, fin.pred_above, fin.cast_lt],
    rcases lt_trichotomy i j with h | h | h,
    { have h_nji : ¬ j < i := lt_asymm h,
      have j_pos : 0 < j := lt_of_le_of_lt (zero_le i) h,
      simp [h, h_nji, fin.lt_iff_val_lt_val],
      rw [show j.pred = j - 1, from rfl, list.insert_nth_remove_nth_of_ge, nat.sub_add_cancel j_pos],
      { rw [v.2], exact lt_of_lt_of_le h (nat.le_of_succ_le_succ hj) },
      { exact nat.le_sub_right_of_add_le h } },
    { exact (this h).elim },
    { have h_nij : ¬ i < j := lt_asymm h,
      have i_pos : 0 < i := lt_of_le_of_lt (zero_le j) h,
      simp [h, h_nij, fin.lt_iff_val_lt_val],
      rw [show i.pred = i - 1, from rfl, list.insert_nth_remove_nth_of_le, nat.sub_add_cancel i_pos],
      { show i - 1 + 1 ≤ v.val.length,
        rw [v.2, nat.sub_add_cancel i_pos],
        exact nat.le_of_lt_succ hi },
      { exact nat.le_sub_right_of_add_le h } }
  end

lemma insert_nth_comm (a b : α) (i j : fin (n+1)) (h : i ≤ j) :
  ∀(v : vector α n), (v.insert_nth a i).insert_nth b j.succ = (v.insert_nth b j).insert_nth a i.cast_succ
| ⟨l, hl⟩ :=
  begin
    refine subtype.eq _,
    simp [insert_nth_val, fin.succ_val, fin.cast_succ],
    apply list.insert_nth_comm,
    { assumption },
    { rw hl, exact nat.le_of_succ_le_succ j.2 }
  end

end insert_nth

end vector

namespace vector

section traverse

variables {F G : Type u → Type u}
variables [applicative F] [applicative G]

open applicative functor
open list (cons) nat

private def traverse_aux {α β : Type u} (f : α → F β) :
  Π (x : list α), F (vector β x.length)
| []      := pure vector.nil
| (x::xs) := vector.cons <$> f x <*> traverse_aux xs

protected def traverse {α β : Type u} (f : α → F β) : vector α n → F (vector β n)
| ⟨v, Hv⟩ := cast (by rw Hv) $ traverse_aux f v

variables [is_lawful_applicative F] [is_lawful_applicative G]
variables {α β γ : Type u}

@[simp] protected lemma traverse_def
  (f : α → F β) (x : α) : ∀ (xs : vector α n),
  (x :: xs).traverse f = cons <$> f x <*> xs.traverse f :=
by rintro ⟨xs, rfl⟩; refl

protected lemma id_traverse : ∀ (x : vector α n), x.traverse id.mk = x :=
begin
  rintro ⟨x, rfl⟩, dsimp [vector.traverse, cast],
  induction x with x xs IH, {refl},
  simp! [IH], refl
end

open function

protected lemma comp_traverse (f : β → F γ) (g : α → G β) : ∀ (x : vector α n),
  vector.traverse (comp.mk ∘ functor.map f ∘ g) x =
  comp.mk (vector.traverse f <$> vector.traverse g x) :=
by rintro ⟨x, rfl⟩; dsimp [vector.traverse, cast];
   induction x with x xs; simp! [cast, *] with functor_norm;
   [refl, simp [(∘)]]

protected lemma traverse_eq_map_id {α β} (f : α → β) : ∀ (x : vector α n),
  x.traverse (id.mk ∘ f) = id.mk (map f x) :=
by rintro ⟨x, rfl⟩; simp!;
   induction x; simp! * with functor_norm; refl

variable (η : applicative_transformation F G)

protected lemma naturality {α β : Type*}
  (f : α → F β) : ∀ (x : vector α n),
  η (x.traverse f) = x.traverse (@η _ ∘ f) :=
by rintro ⟨x, rfl⟩; simp! [cast];
   induction x with x xs IH; simp! * with functor_norm

end traverse

instance : traversable.{u} (flip vector n) :=
{ traverse := @vector.traverse n,
  map := λ α β, @vector.map.{u u} α β n }

instance : is_lawful_traversable.{u} (flip vector n) :=
{ id_traverse := @vector.id_traverse n,
  comp_traverse := @vector.comp_traverse n,
  traverse_eq_map_id := @vector.traverse_eq_map_id n,
  naturality := @vector.naturality n,
  id_map := by intros; cases x; simp! [(<$>)],
  comp_map := by intros; cases x; simp! [(<$>)] }

end vector


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

import data.set.pointwise.basic
import data.list.of_fn

/-!
# Pointwise operations with lists of sets

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

This file proves some lemmas about pointwise algebraic operations with lists of sets.
-/

namespace set

variables {F α β γ : Type*}
variables [monoid α] {s t : set α} {a : α} {m n : ℕ}

open_locale pointwise

@[to_additive] lemma mem_prod_list_of_fn {a : α} {s : fin n → set α} :
  a ∈ (list.of_fn s).prod ↔ ∃ f : (Π i : fin n, s i), (list.of_fn (λ i, (f i : α))).prod = a :=
begin
  induction n with n ih generalizing a,
  { simp_rw [list.of_fn_zero, list.prod_nil, fin.exists_fin_zero_pi, eq_comm, set.mem_one] },
  { simp_rw [list.of_fn_succ, list.prod_cons, fin.exists_fin_succ_pi, fin.cons_zero, fin.cons_succ,
      mem_mul, @ih, exists_and_distrib_left, exists_exists_eq_and, set_coe.exists, subtype.coe_mk,
      exists_prop] }
end

@[to_additive] lemma mem_list_prod {l : list (set α)} {a : α} :
  a ∈ l.prod ↔ ∃ l' : list (Σ s : set α, ↥s),
    list.prod (l'.map (λ x, (sigma.snd x : α))) = a ∧ l'.map sigma.fst = l :=
begin
  induction l using list.of_fn_rec with n f,
  simp_rw [list.exists_iff_exists_tuple, list.map_of_fn, list.of_fn_inj', and.left_comm,
    exists_and_distrib_left, exists_eq_left, heq_iff_eq, function.comp, mem_prod_list_of_fn],
  split,
  { rintros ⟨fi, rfl⟩,  exact ⟨λ i, ⟨_, fi i⟩, rfl, rfl⟩, },
  { rintros ⟨fi, rfl, rfl⟩, exact ⟨λ i, _, rfl⟩, },
end

@[to_additive] lemma mem_pow {a : α} {n : ℕ} :
  a ∈ s ^ n ↔ ∃ f : fin n → s, (list.of_fn (λ i, (f i : α))).prod = a :=
by rw [←mem_prod_list_of_fn, list.of_fn_const, list.prod_replicate]

end set


import morphisms.finite
import algebraic_geometry.finite_kScheme

open category_theory opposite

namespace algebraic_geometry

open_locale topological_space

universe u

variables {X Y : Scheme.{u}} (f : X ⟶ Y) (x : X.carrier)

-- def Scheme.hom.fiber_set (y : Y.carrier) : set X.carrier :=
-- { x | f.1.base x = y }

local notation `f⁻¹f` x:100 := f.fiber (f.1.base x)

@[derive is_iso]
noncomputable
def Scheme.hom.fiber_congr {x y : Y.carrier} (e : x = y) : f.fiber x ⟶ f.fiber y :=
limits.pullback.map _ _ _ _ (𝟙 _) (Scheme.Spec.map (Y.residue_field_of_eq e).op) (𝟙 _)
  (by rw [category.comp_id, category.id_comp])
  (by rw [category.comp_id, Scheme.residue_field_of_eq_from_Spec])

@[simp, reassoc]
lemma Scheme.hom.fiber_congr_ι {x y : Y.carrier} (e : x = y) :
  f.fiber_congr e ≫ f.fiber_ι y = f.fiber_ι x :=
(limits.pullback.lift_fst _ _ _).trans (category.comp_id _)

noncomputable
def Scheme.hom.as_fiber (x : X.carrier) : (f.fiber (f.1.base x)).carrier :=
(f.fiber_carrier (f.1.base x)).symm ⟨x, rfl⟩

lemma Scheme.hom.fiber_carrier_as_fiber (x : X.carrier) :
  ↑(f.fiber_carrier (f.1.base x) (f.as_fiber x)) = x :=
subtype.ext_iff.mp ((f.fiber_carrier (f.1.base x)).apply_symm_apply ⟨x, rfl⟩)

lemma Scheme.hom.as_fiber_fiber_carrier {y : Y.carrier} (x : (f.fiber y).carrier) :
  f.as_fiber (f.fiber_carrier y x) = (f.fiber_congr (f.fiber_carrier y x).prop.symm).1.base x :=
begin
  simp only [Scheme.hom.as_fiber, homeomorph.symm_apply_eq, Scheme.hom.fiber_carrier_apply,
    subtype.coe_mk, subtype.ext_iff, ← Scheme.comp_val_base_apply, Scheme.hom.fiber_congr_ι],
end

def Scheme.hom.quasi_finite_at (x : X.carrier) : Prop :=
𝓝[f.1.base ⁻¹' {f.1.base x} \ {x}] x = ⊥

lemma inducing.comap_nhds_within {α β : Type*} [topological_space α] [topological_space β]
  {f : α → β} (hf : inducing f) (x : α) (s : set β) :
    filter.comap f (𝓝[s] (f x)) = 𝓝[f ⁻¹' s] x :=
by rw [nhds_within, nhds_within, filter.comap_inf, hf.nhds_eq_comap, filter.comap_principal]

lemma embedding.comap_nhds_within_ne {α β : Type*} [topological_space α] [topological_space β]
  {f : α → β} (hf : embedding f) (x : α) : filter.comap f (𝓝[≠] (f x)) = 𝓝[≠] x :=
begin
  rw [hf.to_inducing.comap_nhds_within, set.preimage_compl],
  congr, ext, exact hf.inj.eq_iff
end

.
lemma Scheme.hom.quasi_finite_at_iff_is_open {x : X.carrier} :
  f.quasi_finite_at x ↔ is_open ({f.as_fiber x} : set (f.fiber $ f.1.base x).carrier) :=
begin
  change 𝓝[{ y | f.1.base y = f.1.base x } ∩ {x}ᶜ] x = ⊥ ↔ _,
  transitivity is_open (f.fiber_carrier (f.1.base x) '' {f.as_fiber x}),
  { rw [set.image_singleton, ← is_isolated_point_iff, set.inter_comm,
    nhds_within_inter', ← filter.subtype_coe_map_comap, filter.map_eq_bot_iff,
    ← embedding_subtype_coe.comap_nhds_within_ne, f.fiber_carrier_as_fiber] },
  { exact (open_embedding.open_iff_image_open $ homeomorph.open_embedding _).symm }
end

lemma Scheme.hom.quasi_finite_at_tfae [locally_of_finite_type f] (x : X.carrier) :
  tfae [f.quasi_finite_at x,
    is_open ({f.as_fiber x} : set (f.fiber $ f.1.base x).carrier),
    is_clopen ({f.as_fiber x} : set (f.fiber $ f.1.base x).carrier),
    (PresheafedSpace.stalk_map (f.fiber_to_residue_field (f.1.base x)).1 $ f.as_fiber x).finite,
    is_closed ({f.as_fiber x} : set (f.fiber $ f.1.base x).carrier) ∧
      stable_under_generalization ({f.as_fiber x} : set (f.fiber $ f.1.base x).carrier)] :=
begin
  rw list.tfae_cons_cons,
  split, { exact f.quasi_finite_at_iff_is_open },
  exact @is_open_singleton_tfae (f.fiber (f.1.base x))
    (local_ring.residue_field (Y.presheaf.stalk $ f.1.base x)) _
    (f.fiber_to_residue_field (f.1.base x))
    (category_theory.limits.pullback.snd.locally_of_finite_type _ _)
    (f.as_fiber x)
end

@[mk_iff]
class locally_quasi_finite extends locally_of_finite_type f : Prop :=
(quasi_finite_at : ∀ x, f.quasi_finite_at x)

lemma locally_quasi_finite_iff_fiber {f : X ⟶ Y} [locally_of_finite_type f] :
  locally_quasi_finite f ↔ ∀ y, discrete_topology (f.fiber y).carrier :=
begin
  simp_rw [discrete_topology_iff_forall_is_open_singleton, locally_quasi_finite_iff,
    and_iff_right ‹locally_of_finite_type f›, f.quasi_finite_at_iff_is_open],
  split,
  { intros H x y,
    have := H (f.fiber_carrier _ y),
    rwa [f.as_fiber_fiber_carrier, ← set.image_singleton,
      ← open_embedding.open_iff_image_open] at this,
    exact (Top.homeo_of_iso $
      Scheme.forget_to_Top.map_iso $ as_iso $ f.fiber_congr _).open_embedding },
  { intros H x, apply H }
end

instance locally_quasi_finite.discrete_fiber [locally_quasi_finite f] (y : Y.carrier) :
  discrete_topology (f.fiber y).carrier :=
locally_quasi_finite_iff_fiber.mp ‹locally_quasi_finite f› y

noncomputable
def _root_.category_theory.limits.pullback.map_iso {C} [category C] {X Y Z X' Y' Z' : C}
  (f : X ⟶ Z) (g : Y ⟶ Z) (f' : X' ⟶ Z') (g' : Y' ⟶ Z') (i₁ : X ≅ X')
  (i₂ : Y ≅ Y') (i₃ : Z ≅ Z') (h₁ : f ≫ i₃.hom = i₁.hom ≫ f')
  (h₂ : g ≫ i₃.hom = i₂.hom ≫ g') [limits.has_pullback f g] [limits.has_pullback f' g'] :
  limits.pullback f g ≅ limits.pullback f' g' :=
{ hom := limits.pullback.map f g f' g' i₁.hom i₂.hom i₃.hom h₁ h₂,
  inv := limits.pullback.map f' g' f g i₁.inv i₂.inv i₃.inv
    (by rw [iso.comp_inv_eq, category.assoc, h₁, iso.inv_hom_id_assoc])
    (by rw [iso.comp_inv_eq, category.assoc, h₂, iso.inv_hom_id_assoc]),
  hom_inv_id' := by { ext; simp },
  inv_hom_id' := by { ext; simp } }
.
open _root_.category_theory.limits

noncomputable
def fiber_fst_iso_pullback_fiber {X Y Z : Scheme} (f : X ⟶ Z) (g : Y ⟶ Z) (x : X.carrier) :
  (limits.pullback.fst : limits.pullback f g ⟶ _).fiber x ≅
    limits.pullback (g.fiber_to_residue_field (f.1.base x))
      (Scheme.Spec.map $ (f.map_residue_field x).op) :=
begin
  delta Scheme.hom.fiber Scheme.hom.fiber_ι,
  refine pullback_symmetry _ _ ≪≫ pullback_right_pullback_fst_iso _ _ _ ≪≫
    pullback.congr_hom (f.map_residue_field_from_Spec_residue_field x).symm rfl ≪≫
    (pullback_right_pullback_fst_iso _ _ _).symm ≪≫ pullback_symmetry _ _ ≪≫
    pullback.map_iso _ _ _ _ (pullback_symmetry _ _) (iso.refl _) (iso.refl _) _ _,
  { simp only [iso.refl_hom, category.comp_id, Scheme.hom.fiber_to_residue_field,
      pullback_symmetry_hom_comp_snd] },
  { simp only [iso.refl_hom, category.comp_id, category.id_comp] },
end

lemma Scheme.hom.quasi_finite_at.comp {X Y Z : Scheme} {f : X ⟶ Y} {g : Y ⟶ Z}
  {x : X.carrier} (hf : f.quasi_finite_at x) (hg : g.quasi_finite_at (f.1.base x)) :
  (f ≫ g).quasi_finite_at x :=
begin
  delta Scheme.hom.quasi_finite_at at hf hg ⊢,
  rw [eq_bot_iff, ← hf],
  refine le_inf inf_le_left (le_of_le_of_eq _ sup_bot_eq),
  rw [← @filter.comap_bot _ _ f.1.base, ← hg, nhds_within, nhds_within, filter.comap_inf,
    filter.comap_principal, sup_inf_left, filter.sup_principal],
  refine le_inf (inf_le_left.trans (le_trans _ le_sup_right)) (inf_le_right.trans _),
  { rw ← filter.tendsto_iff_comap, exact f.1.base.2.continuous_at },
  { rw filter.principal_mono, intros y hy,
    by_cases f.1.base y = f.1.base x, exacts [or.inl ⟨h, hy.2⟩, or.inr ⟨hy.1, h⟩] }
end

lemma Scheme.hom.quasi_finite_at.of_comp {X Y Z : Scheme} {f : X ⟶ Y} {g : Y ⟶ Z}
  {x : X.carrier} (h : (f ≫ g).quasi_finite_at x) :
  f.quasi_finite_at x :=
begin
  delta Scheme.hom.quasi_finite_at at h ⊢,
  simp_rw [eq_bot_iff, ← h, Scheme.comp_val_base, coe_comp],
  exact nhds_within_mono _ (λ x h, ⟨(congr_arg g.1.base h.1 : _), h.2⟩),
end

lemma Scheme.hom.quasi_finite_at.iff_comp {X Y Z : Scheme} {f : X ⟶ Y} {g : Y ⟶ Z}
  {x : X.carrier} (hg : g.quasi_finite_at (f.1.base x)) :
  (f ≫ g).quasi_finite_at x ↔ f.quasi_finite_at x :=
⟨Scheme.hom.quasi_finite_at.of_comp, λ h, Scheme.hom.quasi_finite_at.comp h hg⟩

lemma locally_quasi_finite_stable_under_composition :
  morphism_property.stable_under_composition @locally_quasi_finite :=
λ X Y Z f g hf hg, by exactI ⟨λ x, Scheme.hom.quasi_finite_at.comp (hf.2 _) (hg.2 _)⟩

lemma locally_quasi_finite_of_comp {X Y Z : Scheme} (f : X ⟶ Y) (g : Y ⟶ Z)
  (hfg : locally_quasi_finite (f ≫ g)) : locally_quasi_finite f :=
@@locally_quasi_finite.mk (locally_of_finite_type_of_comp f g)
  (λ x, Scheme.hom.quasi_finite_at.of_comp (hfg.2 x))

lemma Scheme.hom.quasi_finite_at.of_injective
  (hf : function.injective f.1.base) (x) : f.quasi_finite_at x :=
begin
  rw [Scheme.hom.quasi_finite_at, eq_bot_iff, nhds_within],
  refine inf_le_right.trans_eq (filter.principal_eq_bot_iff.mpr _),
  rw set.eq_empty_iff_forall_not_mem,
  rintro y ⟨h₁, h₂⟩, exact h₂ (hf h₁)
end

lemma locally_quasi_finite_of_mono [mono f] [locally_of_finite_type f] :
  locally_quasi_finite f :=
⟨f^.quasi_finite_at.of_injective (injective_of_mono f)⟩

lemma locally_quasi_finite_respects_iso :
  morphism_property.respects_iso @locally_quasi_finite :=
locally_quasi_finite_stable_under_composition.respects_iso
  (λ X Y e, locally_quasi_finite_of_mono e.hom)

lemma locally_quasi_finite_stable_under_base_change :
  morphism_property.stable_under_base_change @locally_quasi_finite :=
morphism_property.stable_under_base_change.mk locally_quasi_finite_respects_iso
begin
  introsI X Y S f g H,
  rw locally_quasi_finite_iff_fiber,
  intro y,
  refine @@open_embedding.discrete_topology _ _ _ (Top.homeo_of_iso (Scheme.forget_to_Top.map_iso $
    fiber_fst_iso_pullback_fiber f g y)).open_embedding (show _, from _),
  apply_with discrete_topology_pullback_carrier_of_finite_type { instances := ff },
  { delta Scheme.hom.fiber_to_residue_field, apply_instance },
  { apply_instance }
end

instance locally_quasi_finite_comp {X Y Z : Scheme} (f : X ⟶ Y) (g : Y ⟶ Z)
  [locally_quasi_finite f] [locally_quasi_finite g] : locally_quasi_finite (f ≫ g) :=
locally_quasi_finite_stable_under_composition _ _ infer_instance infer_instance

instance {X Y S : Scheme} (f : X ⟶ S) (g : Y ⟶ S) [locally_quasi_finite g] :
  locally_quasi_finite (pullback.fst : pullback f g ⟶ X) :=
locally_quasi_finite_stable_under_base_change (is_pullback.of_has_pullback f g).flip infer_instance

instance {X Y S : Scheme} (f : X ⟶ S) (g : Y ⟶ S) [locally_quasi_finite f] :
  locally_quasi_finite (pullback.snd : pullback f g ⟶ Y) :=
locally_quasi_finite_stable_under_base_change (is_pullback.of_has_pullback f g) infer_instance

local notation `𝖲𝗉𝖾𝖼` K := Scheme.Spec.obj (op $ CommRing.of $ K)

lemma finite_of_quasi_finite {K : Type*} [field K] (f : X ⟶ 𝖲𝗉𝖾𝖼 K)
  [locally_quasi_finite f] [quasi_compact f] : finite f :=
begin
  rw (finite_tfae_of_finite_type f).out 3 1,
  refine ((f.fiber_carrier (show prime_spectrum K, from ⊥)).trans ((homeomorph.set_congr _).trans
    (homeomorph.set.univ X.carrier))).symm.open_embedding.discrete_topology,
  exact set.eq_univ_iff_forall.mpr (λ x, @subsingleton.elim (prime_spectrum K) _ _ _)
end

lemma quasi_finite_tfae (f : X ⟶ Y) [locally_of_finite_type f] [quasi_compact f] :
  tfae [locally_quasi_finite f,
    ∀ x, _root_.finite (f.fiber x).carrier,
    ∀ x, finite (f.fiber_to_residue_field x),
    ∀ (K : Type u) [field K], by exactI ∀ (g : Scheme.Spec.obj (op $ CommRing.of K) ⟶ Y),
      finite (limits.pullback.snd : limits.pullback f g ⟶ _)] :=
begin
  have := λ x, @finite_tfae_of_finite_type (f.fiber x) _ _
    (f.fiber_to_residue_field x)
    (category_theory.limits.pullback.snd.locally_of_finite_type _ _)
    (category_theory.limits.pullback.snd.quasi_compact _ _),
  tfae_have : 1 → 4, { introsI, exact finite_of_quasi_finite _ },
  tfae_have : 4 → 3, { intros H x, apply H },
  tfae_have : 3 ↔ 2, { exact forall_congr (λ x, (this x).out 3 0) },
  tfae_have : 2 ↔ 1,
  { exact (forall_congr (λ x, (this x).out 0 1)).trans locally_quasi_finite_iff_fiber.symm },
  tfae_finish,
end

instance locally_quasi_finite.finite_fiber [locally_quasi_finite f] [quasi_compact f]
  (y : Y.carrier) :
  _root_.finite (f.fiber y).carrier :=
let H := (quasi_finite_tfae f).out 0 1 in H.mp infer_instance y

lemma locally_quasi_finite.set_finite_fiber [locally_quasi_finite f] [quasi_compact f]
  (y : Y.carrier) : { x | f.1.base x = y }.finite :=
begin
  apply_with set.to_finite { instances := ff },
  exact (f.fiber_carrier y).finite_iff.mp (locally_quasi_finite.finite_fiber f y),
end

instance finite.to_locally_quasi_finite [finite f] : locally_quasi_finite f :=
by { rw (quasi_finite_tfae f).out 0 3, intros, apply_instance }

instance is_immersion.to_locally_quasi_finite [is_immersion f] : locally_quasi_finite f :=
locally_quasi_finite_of_mono f

lemma Scheme.hom.quasi_finite_at.of_comp_is_open_immersion {X Y Z : Scheme} {f : X ⟶ Y}
  {g : Y ⟶ Z} [is_open_immersion f] {x : X.carrier} (h : (f ≫ g).quasi_finite_at x) :
  g.quasi_finite_at (f.1.base x) :=
begin
  delta Scheme.hom.quasi_finite_at at h ⊢,
  simp_rw [Scheme.comp_val_base, coe_comp] at h,
  rw [eq_bot_iff, ← @filter.map_bot _ _ f.1.base, ← h,
    (is_immersion.base_embedding f).map_nhds_within_eq, ← nhds_within_inter_of_mem],
  show set.range f.1.base ∈ nhds_within _ _,
  { apply nhds_within_le_nhds,
    exact is_open.mem_nhds (is_open_immersion.base_open f).2 (set.mem_range_self x) },
  apply nhds_within_mono,
  rintros _ ⟨⟨y, rfl⟩, h₁, h₂⟩,
  exact ⟨_, ⟨h₁, λ e, h₂ (congr_arg f.1.base e)⟩, rfl⟩,
end

lemma locally_quasi_finite_is_local_at_source :
  property_is_local_at_source @locally_quasi_finite :=
begin
  refine ⟨locally_quasi_finite_respects_iso, _, _⟩,
  { introsI, apply_instance },
  { introsI X Y f 𝒰 H,
    refine @@locally_quasi_finite.mk
      (locally_of_finite_type_is_local_at_source.3 f 𝒰 infer_instance) _,
    intro x,
    obtain ⟨y, hy⟩ := 𝒰.covers x,
    exact hy ▸ ((H $ 𝒰.f x).2 y).of_comp_is_open_immersion }
end

lemma locally_quasi_finite_is_local_at_target :
  property_is_local_at_target @locally_quasi_finite :=
begin
  refine ⟨locally_quasi_finite_respects_iso, _, _⟩,
  { introsI, delta morphism_restrict, apply_instance },
  { introsI X Y f 𝒰 H,
    apply locally_quasi_finite_is_local_at_source.3 f (𝒰.pullback_cover f),
    intro i,
    rw [Scheme.open_cover.pullback_cover_map, pullback.condition],
    apply_instance }
end

end algebraic_geometry


/-
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, Jeremy Avigad, Yury Kudryashov

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

/-!
# The cofinite filter

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

In this file we define

`cofinite`: the filter of sets with finite complement

and prove its basic properties. In particular, we prove that for `ℕ` it is equal to `at_top`.

## TODO

Define filters for other cardinalities of the complement.
-/

open Set Function

open Classical

variable {ι α β : Type _} {l : Filter α}

namespace Filter

#print Filter.cofinite /-
/-- The cofinite filter is the filter of subsets whose complements are finite. -/
def cofinite : Filter α where
  sets := { s | sᶜ.Finite }
  univ_sets := by simp only [compl_univ, finite_empty, mem_set_of_eq]
  sets_of_superset s t (hs : sᶜ.Finite) (st : s ⊆ t) := hs.Subset <| compl_subset_compl.2 st
  inter_sets s t (hs : sᶜ.Finite) (ht : tᶜ.Finite) := by
    simp only [compl_inter, finite.union, ht, hs, mem_set_of_eq]
#align filter.cofinite Filter.cofinite
-/

/- warning: filter.mem_cofinite -> Filter.mem_cofinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} α}, Iff (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s (Filter.cofinite.{u1} α)) (Set.Finite.{u1} α (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)) s))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} α}, Iff (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) s (Filter.cofinite.{u1} α)) (Set.Finite.{u1} α (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)) s))
Case conversion may be inaccurate. Consider using '#align filter.mem_cofinite Filter.mem_cofiniteₓ'. -/
@[simp]
theorem mem_cofinite {s : Set α} : s ∈ @cofinite α ↔ sᶜ.Finite :=
  Iff.rfl
#align filter.mem_cofinite Filter.mem_cofinite

#print Filter.eventually_cofinite /-
@[simp]
theorem eventually_cofinite {p : α → Prop} : (∀ᶠ x in cofinite, p x) ↔ { x | ¬p x }.Finite :=
  Iff.rfl
#align filter.eventually_cofinite Filter.eventually_cofinite
-/

/- warning: filter.has_basis_cofinite -> Filter.hasBasis_cofinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}}, Filter.HasBasis.{u1, succ u1} α (Set.{u1} α) (Filter.cofinite.{u1} α) (fun (s : Set.{u1} α) => Set.Finite.{u1} α s) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)))
but is expected to have type
  forall {α : Type.{u1}}, Filter.HasBasis.{u1, succ u1} α (Set.{u1} α) (Filter.cofinite.{u1} α) (fun (s : Set.{u1} α) => Set.Finite.{u1} α s) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)))
Case conversion may be inaccurate. Consider using '#align filter.has_basis_cofinite Filter.hasBasis_cofiniteₓ'. -/
theorem hasBasis_cofinite : HasBasis cofinite (fun s : Set α => s.Finite) compl :=
  ⟨fun s =>
    ⟨fun h => ⟨sᶜ, h, (compl_compl s).Subset⟩, fun ⟨t, htf, hts⟩ =>
      htf.Subset <| compl_subset_comm.2 hts⟩⟩
#align filter.has_basis_cofinite Filter.hasBasis_cofinite

#print Filter.cofinite_neBot /-
instance cofinite_neBot [Infinite α] : NeBot (@cofinite α) :=
  hasBasis_cofinite.neBot_iff.2 fun s hs => hs.infinite_compl.Nonempty
#align filter.cofinite_ne_bot Filter.cofinite_neBot
-/

#print Filter.frequently_cofinite_iff_infinite /-
theorem frequently_cofinite_iff_infinite {p : α → Prop} :
    (∃ᶠ x in cofinite, p x) ↔ Set.Infinite { x | p x } := by
  simp only [Filter.Frequently, Filter.Eventually, mem_cofinite, compl_set_of, Classical.not_not,
    Set.Infinite]
#align filter.frequently_cofinite_iff_infinite Filter.frequently_cofinite_iff_infinite
-/

/- warning: set.finite.compl_mem_cofinite -> Set.Finite.compl_mem_cofinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {s : Set.{u1} α}, (Set.Finite.{u1} α s) -> (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)) s) (Filter.cofinite.{u1} α))
but is expected to have type
  forall {α : Type.{u1}} {s : Set.{u1} α}, (Set.Finite.{u1} α s) -> (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)) s) (Filter.cofinite.{u1} α))
Case conversion may be inaccurate. Consider using '#align set.finite.compl_mem_cofinite Set.Finite.compl_mem_cofiniteₓ'. -/
theorem Set.Finite.compl_mem_cofinite {s : Set α} (hs : s.Finite) : sᶜ ∈ @cofinite α :=
  mem_cofinite.2 <| (compl_compl s).symm ▸ hs
#align set.finite.compl_mem_cofinite Set.Finite.compl_mem_cofinite

#print Set.Finite.eventually_cofinite_nmem /-
theorem Set.Finite.eventually_cofinite_nmem {s : Set α} (hs : s.Finite) : ∀ᶠ x in cofinite, x ∉ s :=
  hs.compl_mem_cofinite
#align set.finite.eventually_cofinite_nmem Set.Finite.eventually_cofinite_nmem
-/

#print Finset.eventually_cofinite_nmem /-
theorem Finset.eventually_cofinite_nmem (s : Finset α) : ∀ᶠ x in cofinite, x ∉ s :=
  s.finite_toSet.eventually_cofinite_nmem
#align finset.eventually_cofinite_nmem Finset.eventually_cofinite_nmem
-/

#print Set.infinite_iff_frequently_cofinite /-
theorem Set.infinite_iff_frequently_cofinite {s : Set α} :
    Set.Infinite s ↔ ∃ᶠ x in cofinite, x ∈ s :=
  frequently_cofinite_iff_infinite.symm
#align set.infinite_iff_frequently_cofinite Set.infinite_iff_frequently_cofinite
-/

#print Filter.eventually_cofinite_ne /-
theorem eventually_cofinite_ne (x : α) : ∀ᶠ a in cofinite, a ≠ x :=
  (Set.finite_singleton x).eventually_cofinite_nmem
#align filter.eventually_cofinite_ne Filter.eventually_cofinite_ne
-/

/- warning: filter.le_cofinite_iff_compl_singleton_mem -> Filter.le_cofinite_iff_compl_singleton_mem is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) l (Filter.cofinite.{u1} α)) (forall (x : α), Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.booleanAlgebra.{u1} α)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.hasSingleton.{u1} α) x)) l)
but is expected to have type
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) l (Filter.cofinite.{u1} α)) (forall (x : α), Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) (HasCompl.compl.{u1} (Set.{u1} α) (BooleanAlgebra.toHasCompl.{u1} (Set.{u1} α) (Set.instBooleanAlgebraSet.{u1} α)) (Singleton.singleton.{u1, u1} α (Set.{u1} α) (Set.instSingletonSet.{u1} α) x)) l)
Case conversion may be inaccurate. Consider using '#align filter.le_cofinite_iff_compl_singleton_mem Filter.le_cofinite_iff_compl_singleton_memₓ'. -/
theorem le_cofinite_iff_compl_singleton_mem : l ≤ cofinite ↔ ∀ x, {x}ᶜ ∈ l :=
  by
  refine' ⟨fun h x => h (finite_singleton x).compl_mem_cofinite, fun h s (hs : sᶜ.Finite) => _⟩
  rw [← compl_compl s, ← bUnion_of_singleton (sᶜ), compl_Union₂, Filter.binterᵢ_mem hs]
  exact fun x _ => h x
#align filter.le_cofinite_iff_compl_singleton_mem Filter.le_cofinite_iff_compl_singleton_mem

/- warning: filter.le_cofinite_iff_eventually_ne -> Filter.le_cofinite_iff_eventually_ne is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) l (Filter.cofinite.{u1} α)) (forall (x : α), Filter.Eventually.{u1} α (fun (y : α) => Ne.{succ u1} α y x) l)
but is expected to have type
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) l (Filter.cofinite.{u1} α)) (forall (x : α), Filter.Eventually.{u1} α (fun (y : α) => Ne.{succ u1} α y x) l)
Case conversion may be inaccurate. Consider using '#align filter.le_cofinite_iff_eventually_ne Filter.le_cofinite_iff_eventually_neₓ'. -/
theorem le_cofinite_iff_eventually_ne : l ≤ cofinite ↔ ∀ x, ∀ᶠ y in l, y ≠ x :=
  le_cofinite_iff_compl_singleton_mem
#align filter.le_cofinite_iff_eventually_ne Filter.le_cofinite_iff_eventually_ne

/- warning: filter.at_top_le_cofinite -> Filter.atTop_le_cofinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : NoMaxOrder.{u1} α (Preorder.toLT.{u1} α _inst_1)], LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (Filter.atTop.{u1} α _inst_1) (Filter.cofinite.{u1} α)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Preorder.{u1} α] [_inst_2 : NoMaxOrder.{u1} α (Preorder.toLT.{u1} α _inst_1)], LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) (Filter.atTop.{u1} α _inst_1) (Filter.cofinite.{u1} α)
Case conversion may be inaccurate. Consider using '#align filter.at_top_le_cofinite Filter.atTop_le_cofiniteₓ'. -/
/-- If `α` is a preorder with no maximal element, then `at_top ≤ cofinite`. -/
theorem atTop_le_cofinite [Preorder α] [NoMaxOrder α] : (atTop : Filter α) ≤ cofinite :=
  le_cofinite_iff_eventually_ne.mpr eventually_ne_atTop
#align filter.at_top_le_cofinite Filter.atTop_le_cofinite

/- warning: filter.comap_cofinite_le -> Filter.comap_cofinite_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} (f : α -> β), LE.le.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (Filter.comap.{u1, u2} α β f (Filter.cofinite.{u2} β)) (Filter.cofinite.{u1} α)
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} (f : α -> β), LE.le.{u2} (Filter.{u2} α) (Preorder.toLE.{u2} (Filter.{u2} α) (PartialOrder.toPreorder.{u2} (Filter.{u2} α) (Filter.instPartialOrderFilter.{u2} α))) (Filter.comap.{u2, u1} α β f (Filter.cofinite.{u1} β)) (Filter.cofinite.{u2} α)
Case conversion may be inaccurate. Consider using '#align filter.comap_cofinite_le Filter.comap_cofinite_leₓ'. -/
theorem comap_cofinite_le (f : α → β) : comap f cofinite ≤ cofinite :=
  le_cofinite_iff_eventually_ne.mpr fun x =>
    mem_comap.2 ⟨{f x}ᶜ, (finite_singleton _).compl_mem_cofinite, fun y => ne_of_apply_ne f⟩
#align filter.comap_cofinite_le Filter.comap_cofinite_le

/- warning: filter.coprod_cofinite -> Filter.coprod_cofinite 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} α β (Filter.cofinite.{u1} α) (Filter.cofinite.{u2} β)) (Filter.cofinite.{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} α β (Filter.cofinite.{u2} α) (Filter.cofinite.{u1} β)) (Filter.cofinite.{max u2 u1} (Prod.{u2, u1} α β))
Case conversion may be inaccurate. Consider using '#align filter.coprod_cofinite Filter.coprod_cofiniteₓ'. -/
/-- The coproduct of the cofinite filters on two types is the cofinite filter on their product. -/
theorem coprod_cofinite : (cofinite : Filter α).coprod (cofinite : Filter β) = cofinite :=
  Filter.coext fun s => by
    simp only [compl_mem_coprod, mem_cofinite, compl_compl, finite_image_fst_and_snd_iff]
#align filter.coprod_cofinite Filter.coprod_cofinite

#print Filter.coprodᵢ_cofinite /-
/-- Finite product of finite sets is finite -/
theorem coprodᵢ_cofinite {α : ι → Type _} [Finite ι] :
    (Filter.coprodᵢ fun i => (cofinite : Filter (α i))) = cofinite :=
  Filter.coext fun s => by
    simp only [compl_mem_Coprod, mem_cofinite, compl_compl, forall_finite_image_eval_iff]
#align filter.Coprod_cofinite Filter.coprodᵢ_cofinite
-/

/- warning: filter.disjoint_cofinite_left -> Filter.disjoint_cofinite_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (Disjoint.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α) (BoundedOrder.toOrderBot.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (CompleteLattice.toBoundedOrder.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))) (Filter.cofinite.{u1} α) l) (Exists.{succ u1} (Set.{u1} α) (fun (s : Set.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s l) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s l) => Set.Finite.{u1} α s)))
but is expected to have type
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (Disjoint.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α) (BoundedOrder.toOrderBot.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) (CompleteLattice.toBoundedOrder.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))) (Filter.cofinite.{u1} α) l) (Exists.{succ u1} (Set.{u1} α) (fun (s : Set.{u1} α) => And (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) s l) (Set.Finite.{u1} α s)))
Case conversion may be inaccurate. Consider using '#align filter.disjoint_cofinite_left Filter.disjoint_cofinite_leftₓ'. -/
@[simp]
theorem disjoint_cofinite_left : Disjoint cofinite l ↔ ∃ s ∈ l, Set.Finite s :=
  by
  simp only [has_basis_cofinite.disjoint_iff l.basis_sets, id, disjoint_compl_left_iff_subset]
  exact
    ⟨fun ⟨s, hs, t, ht, hts⟩ => ⟨t, ht, hs.Subset hts⟩, fun ⟨s, hs, hsf⟩ =>
      ⟨s, hsf, s, hs, subset.rfl⟩⟩
#align filter.disjoint_cofinite_left Filter.disjoint_cofinite_left

/- warning: filter.disjoint_cofinite_right -> Filter.disjoint_cofinite_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (Disjoint.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α) (BoundedOrder.toOrderBot.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.partialOrder.{u1} α))) (CompleteLattice.toBoundedOrder.{u1} (Filter.{u1} α) (Filter.completeLattice.{u1} α))) l (Filter.cofinite.{u1} α)) (Exists.{succ u1} (Set.{u1} α) (fun (s : Set.{u1} α) => Exists.{0} (Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s l) (fun (H : Membership.Mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (Filter.hasMem.{u1} α) s l) => Set.Finite.{u1} α s)))
but is expected to have type
  forall {α : Type.{u1}} {l : Filter.{u1} α}, Iff (Disjoint.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α) (BoundedOrder.toOrderBot.{u1} (Filter.{u1} α) (Preorder.toLE.{u1} (Filter.{u1} α) (PartialOrder.toPreorder.{u1} (Filter.{u1} α) (Filter.instPartialOrderFilter.{u1} α))) (CompleteLattice.toBoundedOrder.{u1} (Filter.{u1} α) (Filter.instCompleteLatticeFilter.{u1} α))) l (Filter.cofinite.{u1} α)) (Exists.{succ u1} (Set.{u1} α) (fun (s : Set.{u1} α) => And (Membership.mem.{u1, u1} (Set.{u1} α) (Filter.{u1} α) (instMembershipSetFilter.{u1} α) s l) (Set.Finite.{u1} α s)))
Case conversion may be inaccurate. Consider using '#align filter.disjoint_cofinite_right Filter.disjoint_cofinite_rightₓ'. -/
@[simp]
theorem disjoint_cofinite_right : Disjoint l cofinite ↔ ∃ s ∈ l, Set.Finite s :=
  disjoint_comm.trans disjoint_cofinite_left
#align filter.disjoint_cofinite_right Filter.disjoint_cofinite_right

end Filter

open Filter

#print Nat.cofinite_eq_atTop /-
/-- For natural numbers the filters `cofinite` and `at_top` coincide. -/
theorem Nat.cofinite_eq_atTop : @cofinite ℕ = atTop :=
  by
  refine' le_antisymm _ at_top_le_cofinite
  refine' at_top_basis.ge_iff.2 fun N hN => _
  simpa only [mem_cofinite, compl_Ici] using finite_lt_nat N
#align nat.cofinite_eq_at_top Nat.cofinite_eq_atTop
-/

#print Nat.frequently_atTop_iff_infinite /-
theorem Nat.frequently_atTop_iff_infinite {p : ℕ → Prop} :
    (∃ᶠ n in atTop, p n) ↔ Set.Infinite { n | p n } := by
  rw [← Nat.cofinite_eq_atTop, frequently_cofinite_iff_infinite]
#align nat.frequently_at_top_iff_infinite Nat.frequently_atTop_iff_infinite
-/

/- warning: filter.tendsto.exists_within_forall_le -> Filter.Tendsto.exists_within_forall_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {s : Set.{u1} α}, (Set.Nonempty.{u1} α s) -> (forall {f : α -> β}, (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.atTop.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))))) -> (Exists.{succ u1} α (fun (a₀ : α) => Exists.{0} (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a₀ s) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a₀ s) => forall (a : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f a₀) (f a))))))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : LinearOrder.{u1} β] {s : Set.{u2} α}, (Set.Nonempty.{u2} α s) -> (forall {f : α -> β}, (Filter.Tendsto.{u2, u1} α β f (Filter.cofinite.{u2} α) (Filter.atTop.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1))))))) -> (Exists.{succ u2} α (fun (a₀ : α) => And (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a₀ s) (forall (a : α), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) a s) -> (LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_1)))))) (f a₀) (f a))))))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.exists_within_forall_le Filter.Tendsto.exists_within_forall_leₓ'. -/
theorem Filter.Tendsto.exists_within_forall_le {α β : Type _} [LinearOrder β] {s : Set α}
    (hs : s.Nonempty) {f : α → β} (hf : Filter.Tendsto f Filter.cofinite Filter.atTop) :
    ∃ a₀ ∈ s, ∀ a ∈ s, f a₀ ≤ f a :=
  by
  rcases em (∃ y ∈ s, ∃ x, f y < x) with (⟨y, hys, x, hx⟩ | not_all_top)
  · -- the set of points `{y | f y < x}` is nonempty and finite, so we take `min` over this set
    have : { y | ¬x ≤ f y }.Finite := filter.eventually_cofinite.mp (tendsto_at_top.1 hf x)
    simp only [not_le] at this
    obtain ⟨a₀, ⟨ha₀ : f a₀ < x, ha₀s⟩, others_bigger⟩ :=
      exists_min_image _ f (this.inter_of_left s) ⟨y, hx, hys⟩
    refine' ⟨a₀, ha₀s, fun a has => (lt_or_le (f a) x).elim _ (le_trans ha₀.le)⟩
    exact fun h => others_bigger a ⟨h, has⟩
  · -- in this case, f is constant because all values are at top
    push_neg  at not_all_top
    obtain ⟨a₀, ha₀s⟩ := hs
    exact ⟨a₀, ha₀s, fun a ha => not_all_top a ha (f a₀)⟩
#align filter.tendsto.exists_within_forall_le Filter.Tendsto.exists_within_forall_le

/- warning: filter.tendsto.exists_forall_le -> Filter.Tendsto.exists_forall_le is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Nonempty.{succ u1} α] [_inst_2 : LinearOrder.{u2} β] {f : α -> β}, (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.atTop.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2)))))) -> (Exists.{succ u1} α (fun (a₀ : α) => forall (a : α), LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2))))) (f a₀) (f a)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Nonempty.{succ u2} α] [_inst_2 : LinearOrder.{u1} β] {f : α -> β}, (Filter.Tendsto.{u2, u1} α β f (Filter.cofinite.{u2} α) (Filter.atTop.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2))))))) -> (Exists.{succ u2} α (fun (a₀ : α) => forall (a : α), LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2)))))) (f a₀) (f a)))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.exists_forall_le Filter.Tendsto.exists_forall_leₓ'. -/
theorem Filter.Tendsto.exists_forall_le [Nonempty α] [LinearOrder β] {f : α → β}
    (hf : Tendsto f cofinite atTop) : ∃ a₀, ∀ a, f a₀ ≤ f a :=
  let ⟨a₀, _, ha₀⟩ := hf.exists_within_forall_le univ_nonempty
  ⟨a₀, fun a => ha₀ a (mem_univ _)⟩
#align filter.tendsto.exists_forall_le Filter.Tendsto.exists_forall_le

/- warning: filter.tendsto.exists_within_forall_ge -> Filter.Tendsto.exists_within_forall_ge is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {s : Set.{u1} α}, (Set.Nonempty.{u1} α s) -> (forall {f : α -> β}, (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.atBot.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1)))))) -> (Exists.{succ u1} α (fun (a₀ : α) => Exists.{0} (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a₀ s) (fun (H : Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a₀ s) => forall (a : α), (Membership.Mem.{u1, u1} α (Set.{u1} α) (Set.hasMem.{u1} α) a s) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_1))))) (f a) (f a₀))))))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : LinearOrder.{u2} β] {s : Set.{u1} α}, (Set.Nonempty.{u1} α s) -> (forall {f : α -> β}, (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.atBot.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (DistribLattice.toLattice.{u2} β (instDistribLattice.{u2} β _inst_1))))))) -> (Exists.{succ u1} α (fun (a₀ : α) => And (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a₀ s) (forall (a : α), (Membership.mem.{u1, u1} α (Set.{u1} α) (Set.instMembershipSet.{u1} α) a s) -> (LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (DistribLattice.toLattice.{u2} β (instDistribLattice.{u2} β _inst_1)))))) (f a) (f a₀))))))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.exists_within_forall_ge Filter.Tendsto.exists_within_forall_geₓ'. -/
theorem Filter.Tendsto.exists_within_forall_ge [LinearOrder β] {s : Set α} (hs : s.Nonempty)
    {f : α → β} (hf : Filter.Tendsto f Filter.cofinite Filter.atBot) :
    ∃ a₀ ∈ s, ∀ a ∈ s, f a ≤ f a₀ :=
  @Filter.Tendsto.exists_within_forall_le _ βᵒᵈ _ _ hs _ hf
#align filter.tendsto.exists_within_forall_ge Filter.Tendsto.exists_within_forall_ge

/- warning: filter.tendsto.exists_forall_ge -> Filter.Tendsto.exists_forall_ge is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Nonempty.{succ u1} α] [_inst_2 : LinearOrder.{u2} β] {f : α -> β}, (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.atBot.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2)))))) -> (Exists.{succ u1} α (fun (a₀ : α) => forall (a : α), LE.le.{u2} β (Preorder.toLE.{u2} β (PartialOrder.toPreorder.{u2} β (SemilatticeInf.toPartialOrder.{u2} β (Lattice.toSemilatticeInf.{u2} β (LinearOrder.toLattice.{u2} β _inst_2))))) (f a) (f a₀)))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} [_inst_1 : Nonempty.{succ u2} α] [_inst_2 : LinearOrder.{u1} β] {f : α -> β}, (Filter.Tendsto.{u2, u1} α β f (Filter.cofinite.{u2} α) (Filter.atBot.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2))))))) -> (Exists.{succ u2} α (fun (a₀ : α) => forall (a : α), LE.le.{u1} β (Preorder.toLE.{u1} β (PartialOrder.toPreorder.{u1} β (SemilatticeInf.toPartialOrder.{u1} β (Lattice.toSemilatticeInf.{u1} β (DistribLattice.toLattice.{u1} β (instDistribLattice.{u1} β _inst_2)))))) (f a) (f a₀)))
Case conversion may be inaccurate. Consider using '#align filter.tendsto.exists_forall_ge Filter.Tendsto.exists_forall_geₓ'. -/
theorem Filter.Tendsto.exists_forall_ge [Nonempty α] [LinearOrder β] {f : α → β}
    (hf : Tendsto f cofinite atBot) : ∃ a₀, ∀ a, f a ≤ f a₀ :=
  @Filter.Tendsto.exists_forall_le _ βᵒᵈ _ _ _ hf
#align filter.tendsto.exists_forall_ge Filter.Tendsto.exists_forall_ge

/- warning: function.injective.tendsto_cofinite -> Function.Injective.tendsto_cofinite is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : α -> β}, (Function.Injective.{succ u1, succ u2} α β f) -> (Filter.Tendsto.{u1, u2} α β f (Filter.cofinite.{u1} α) (Filter.cofinite.{u2} β))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : α -> β}, (Function.Injective.{succ u2, succ u1} α β f) -> (Filter.Tendsto.{u2, u1} α β f (Filter.cofinite.{u2} α) (Filter.cofinite.{u1} β))
Case conversion may be inaccurate. Consider using '#align function.injective.tendsto_cofinite Function.Injective.tendsto_cofiniteₓ'. -/
/-- For an injective function `f`, inverse images of finite sets are finite. See also
`filter.comap_cofinite_le` and `function.injective.comap_cofinite_eq`. -/
theorem Function.Injective.tendsto_cofinite {f : α → β} (hf : Injective f) :
    Tendsto f cofinite cofinite := fun s h => h.Preimage (hf.InjOn _)
#align function.injective.tendsto_cofinite Function.Injective.tendsto_cofinite

/- warning: function.injective.comap_cofinite_eq -> Function.Injective.comap_cofinite_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} {f : α -> β}, (Function.Injective.{succ u1, succ u2} α β f) -> (Eq.{succ u1} (Filter.{u1} α) (Filter.comap.{u1, u2} α β f (Filter.cofinite.{u2} β)) (Filter.cofinite.{u1} α))
but is expected to have type
  forall {α : Type.{u2}} {β : Type.{u1}} {f : α -> β}, (Function.Injective.{succ u2, succ u1} α β f) -> (Eq.{succ u2} (Filter.{u2} α) (Filter.comap.{u2, u1} α β f (Filter.cofinite.{u1} β)) (Filter.cofinite.{u2} α))
Case conversion may be inaccurate. Consider using '#align function.injective.comap_cofinite_eq Function.Injective.comap_cofinite_eqₓ'. -/
/-- The pullback of the `filter.cofinite` under an injective function is equal to `filter.cofinite`.
See also `filter.comap_cofinite_le` and `function.injective.tendsto_cofinite`. -/
theorem Function.Injective.comap_cofinite_eq {f : α → β} (hf : Injective f) :
    comap f cofinite = cofinite :=
  (comap_cofinite_le f).antisymm hf.tendsto_cofinite.le_comap
#align function.injective.comap_cofinite_eq Function.Injective.comap_cofinite_eq

#print Function.Injective.nat_tendsto_atTop /-
/-- An injective sequence `f : ℕ → ℕ` tends to infinity at infinity. -/
theorem Function.Injective.nat_tendsto_atTop {f : ℕ → ℕ} (hf : Injective f) :
    Tendsto f atTop atTop :=
  Nat.cofinite_eq_atTop ▸ hf.tendsto_cofinite
#align function.injective.nat_tendsto_at_top Function.Injective.nat_tendsto_atTop
-/


/-
Copyright (c) 2022 Anatole Dedecker. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel, Anatole Dedecker
-/
import analysis.locally_convex.balanced_core_hull
import topology.algebra.module.simple
import topology.algebra.module.determinant

/-!
# Finite dimensional topological vector spaces over complete fields

Let `𝕜` be a complete nontrivially normed field, and `E` a topological vector space (TVS) over
`𝕜` (i.e we have `[add_comm_group E] [module 𝕜 E] [topological_space E] [topological_add_group E]`
and `[has_continuous_smul 𝕜 E]`).

If `E` is finite dimensional and Hausdorff, then all linear maps from `E` to any other TVS are
continuous.

When `E` is a normed space, this gets us the equivalence of norms in finite dimension.

## Main results :

* `linear_map.continuous_iff_is_closed_ker` : a linear form is continuous if and only if its kernel
  is closed.
* `linear_map.continuous_of_finite_dimensional` : a linear map on a finite-dimensional Hausdorff
  space over a complete field is continuous.

## TODO

Generalize more of `analysis/normed_space/finite_dimension` to general TVSs.

## Implementation detail

The main result from which everything follows is the fact that, if `ξ : ι → E` is a finite basis,
then `ξ.equiv_fun : E →ₗ (ι → 𝕜)` is continuous. However, for technical reasons, it is easier to
prove this when `ι` and `E` live ine the same universe. So we start by doing that as a private
lemma, then we deduce `linear_map.continuous_of_finite_dimensional` from it, and then the general
result follows as `continuous_equiv_fun_basis`.

-/

universes u v w x

noncomputable theory

open set finite_dimensional topological_space filter
open_locale big_operators

section field

variables {𝕜 E F : Type*} [field 𝕜] [topological_space 𝕜] [add_comm_group E] [module 𝕜 E]
  [topological_space E] [add_comm_group F] [module 𝕜 F] [topological_space F]
  [topological_add_group F] [has_continuous_smul 𝕜 F]

/-- The space of continuous linear maps between finite-dimensional spaces is finite-dimensional. -/
instance [finite_dimensional 𝕜 E] [finite_dimensional 𝕜 F] :
  finite_dimensional 𝕜 (E →L[𝕜] F) :=
finite_dimensional.of_injective
  (continuous_linear_map.coe_lm 𝕜 : (E →L[𝕜] F) →ₗ[𝕜] (E →ₗ[𝕜] F))
  continuous_linear_map.coe_injective

end field

section normed_field

variables {𝕜 : Type u} [hnorm : nontrivially_normed_field 𝕜]
  {E : Type v} [add_comm_group E] [module 𝕜 E] [topological_space E]
  [topological_add_group E] [has_continuous_smul 𝕜 E]
  {F : Type w} [add_comm_group F] [module 𝕜 F] [topological_space F]
  [topological_add_group F] [has_continuous_smul 𝕜 F]
  {F' : Type x} [add_comm_group F'] [module 𝕜 F'] [topological_space F']
  [topological_add_group F'] [has_continuous_smul 𝕜 F']

include hnorm

/-- If `𝕜` is a nontrivially normed field, any T2 topology on `𝕜` which makes it a topological
vector space over itself (with the norm topology) is *equal* to the norm topology. -/
lemma unique_topology_of_t2 {t : topological_space 𝕜}
  (h₁ : @topological_add_group 𝕜 t _)
  (h₂ : @has_continuous_smul 𝕜 𝕜 _ hnorm.to_uniform_space.to_topological_space t)
  (h₃ : @t2_space 𝕜 t) :
  t = hnorm.to_uniform_space.to_topological_space :=
begin
  -- Let `𝓣₀` denote the topology on `𝕜` induced by the norm, and `𝓣` be any T2 vector
  -- topology on `𝕜`. To show that `𝓣₀ = 𝓣`, it suffices to show that they have the same
  -- neighborhoods of 0.
  refine topological_add_group.ext h₁ infer_instance (le_antisymm _ _),
  { -- To show `𝓣 ≤ 𝓣₀`, we have to show that closed balls are `𝓣`-neighborhoods of 0.
    rw metric.nhds_basis_closed_ball.ge_iff,
    -- Let `ε > 0`. Since `𝕜` is nontrivially normed, we have `0 < ‖ξ₀‖ < ε` for some `ξ₀ : 𝕜`.
    intros ε hε,
    rcases normed_field.exists_norm_lt 𝕜 hε with ⟨ξ₀, hξ₀, hξ₀ε⟩,
    -- Since `ξ₀ ≠ 0` and `𝓣` is T2, we know that `{ξ₀}ᶜ` is a `𝓣`-neighborhood of 0.
    have : {ξ₀}ᶜ ∈ @nhds 𝕜 t 0 :=
      is_open.mem_nhds is_open_compl_singleton (ne.symm $ norm_ne_zero_iff.mp hξ₀.ne.symm),
    -- Thus, its balanced core `𝓑` is too. Let's show that the closed ball of radius `ε` contains
    -- `𝓑`, which will imply that the closed ball is indeed a `𝓣`-neighborhood of 0.
    have : balanced_core 𝕜 {ξ₀}ᶜ ∈ @nhds 𝕜 t 0 := balanced_core_mem_nhds_zero this,
    refine mem_of_superset this (λ ξ hξ, _),
    -- Let `ξ ∈ 𝓑`. We want to show `‖ξ‖ < ε`. If `ξ = 0`, this is trivial.
    by_cases hξ0 : ξ = 0,
    { rw hξ0,
      exact metric.mem_closed_ball_self hε.le },
    { rw [mem_closed_ball_zero_iff],
      -- Now suppose `ξ ≠ 0`. By contradiction, let's assume `ε < ‖ξ‖`, and show that
      -- `ξ₀ ∈ 𝓑 ⊆ {ξ₀}ᶜ`, which is a contradiction.
      by_contra' h,
      suffices : (ξ₀ * ξ⁻¹) • ξ ∈ balanced_core 𝕜 {ξ₀}ᶜ,
      { rw [smul_eq_mul 𝕜, mul_assoc, inv_mul_cancel hξ0, mul_one] at this,
        exact not_mem_compl_iff.mpr (mem_singleton ξ₀) ((balanced_core_subset _) this) },
      -- For that, we use that `𝓑` is balanced : since `‖ξ₀‖ < ε < ‖ξ‖`, we have `‖ξ₀ / ξ‖ ≤ 1`,
      -- hence `ξ₀ = (ξ₀ / ξ) • ξ ∈ 𝓑` because `ξ ∈ 𝓑`.
      refine (balanced_core_balanced _).smul_mem _ hξ,
      rw [norm_mul, norm_inv, mul_inv_le_iff (norm_pos_iff.mpr hξ0), mul_one],
      exact (hξ₀ε.trans h).le } },
  { -- Finally, to show `𝓣₀ ≤ 𝓣`, we simply argue that `id = (λ x, x • 1)` is continuous from
    -- `(𝕜, 𝓣₀)` to `(𝕜, 𝓣)` because `(•) : (𝕜, 𝓣₀) × (𝕜, 𝓣) → (𝕜, 𝓣)` is continuous.
    calc (@nhds 𝕜 hnorm.to_uniform_space.to_topological_space 0)
        = map id (@nhds 𝕜 hnorm.to_uniform_space.to_topological_space 0) : map_id.symm
    ... = map (λ x, id x • 1) (@nhds 𝕜 hnorm.to_uniform_space.to_topological_space 0) :
        by conv_rhs {congr, funext, rw [smul_eq_mul, mul_one]}; refl
    ... ≤ (@nhds 𝕜 t ((0 : 𝕜) • 1)) :
        @tendsto.smul_const _ _ _ hnorm.to_uniform_space.to_topological_space t _ _ _ _ _
          tendsto_id (1 : 𝕜)
    ... = (@nhds 𝕜 t 0) : by rw zero_smul }
end

/-- Any linear form on a topological vector space over a nontrivially normed field is continuous if
    its kernel is closed. -/
lemma linear_map.continuous_of_is_closed_ker (l : E →ₗ[𝕜] 𝕜) (hl : is_closed (l.ker : set E)) :
  continuous l :=
begin
  -- `l` is either constant or surjective. If it is constant, the result is trivial.
  by_cases H : finrank 𝕜 l.range = 0,
  { rw [finrank_eq_zero, linear_map.range_eq_bot] at H,
    rw H,
    exact continuous_zero },
  { -- In the case where `l` is surjective, we factor it as `φ : (E ⧸ l.ker) ≃ₗ[𝕜] 𝕜`. Note that
    -- `E ⧸ l.ker` is T2 since `l.ker` is closed.
    have : finrank 𝕜 l.range = 1,
      from le_antisymm (finrank_self 𝕜 ▸ l.range.finrank_le) (zero_lt_iff.mpr H),
    have hi : function.injective (l.ker.liftq l (le_refl _)),
    { rw [← linear_map.ker_eq_bot],
      exact submodule.ker_liftq_eq_bot _ _ _ (le_refl _) },
    have hs : function.surjective (l.ker.liftq l (le_refl _)),
    { rw [← linear_map.range_eq_top, submodule.range_liftq],
      exact eq_top_of_finrank_eq ((finrank_self 𝕜).symm ▸ this) },
    let φ : (E ⧸ l.ker) ≃ₗ[𝕜] 𝕜 := linear_equiv.of_bijective (l.ker.liftq l (le_refl _)) ⟨hi, hs⟩,
    have hlφ : (l : E → 𝕜) = φ ∘ l.ker.mkq,
      by ext; refl,
    -- Since the quotient map `E →ₗ[𝕜] (E ⧸ l.ker)` is continuous, the continuity of `l` will follow
    -- form the continuity of `φ`.
    suffices : continuous φ.to_equiv,
    { rw hlφ,
      exact this.comp continuous_quot_mk },
    -- The pullback by `φ.symm` of the quotient topology is a T2 topology on `𝕜`, because `φ.symm`
    -- is injective. Since `φ.symm` is linear, it is also a vector space topology.
    -- Hence, we know that it is equal to the topology induced by the norm.
    have : induced φ.to_equiv.symm infer_instance = hnorm.to_uniform_space.to_topological_space,
    { refine unique_topology_of_t2 (topological_add_group_induced φ.symm.to_linear_map)
        (has_continuous_smul_induced φ.symm.to_linear_map) _,
      rw t2_space_iff,
      exact λ x y hxy, @separated_by_continuous _ _ (induced _ _) _ _ _
        continuous_induced_dom _ _ (φ.to_equiv.symm.injective.ne hxy) },
    -- Finally, the pullback by `φ.symm` is exactly the pushforward by `φ`, so we have to prove
    -- that `φ` is continuous when `𝕜` is endowed with the pushforward by `φ` of the quotient
    -- topology, which is trivial by definition of the pushforward.
    rw [this.symm, equiv.induced_symm],
    exact continuous_coinduced_rng }
end

/-- Any linear form on a topological vector space over a nontrivially normed field is continuous if
    and only if its kernel is closed. -/
lemma linear_map.continuous_iff_is_closed_ker (l : E →ₗ[𝕜] 𝕜) :
  continuous l ↔ is_closed (l.ker : set E) :=
⟨λ h, is_closed_singleton.preimage h, l.continuous_of_is_closed_ker⟩

/-- Over a nontrivially normed field, any linear form which is nonzero on a nonempty open set is
    automatically continuous. -/
lemma linear_map.continuous_of_nonzero_on_open (l : E →ₗ[𝕜] 𝕜) (s : set E) (hs₁ : is_open s)
  (hs₂ : s.nonempty) (hs₃ : ∀ x ∈ s, l x ≠ 0) : continuous l :=
begin
  refine l.continuous_of_is_closed_ker (l.is_closed_or_dense_ker.resolve_right $ λ hl, _),
  rcases hs₂ with ⟨x, hx⟩,
  have : x ∈ interior (l.ker : set E)ᶜ,
  { rw mem_interior_iff_mem_nhds,
    exact mem_of_superset (hs₁.mem_nhds hx) hs₃ },
  rwa hl.interior_compl at this
end

variables [complete_space 𝕜]

/-- This version imposes `ι` and `E` to live in the same universe, so you should instead use
`continuous_equiv_fun_basis` which gives the same result without universe restrictions. -/
private lemma continuous_equiv_fun_basis_aux [ht2 : t2_space E] {ι : Type v} [fintype ι]
  (ξ : basis ι 𝕜 E) : continuous ξ.equiv_fun :=
begin
  letI : uniform_space E := topological_add_group.to_uniform_space E,
  letI : uniform_add_group E := topological_add_comm_group_is_uniform,
  letI : separated_space E := separated_iff_t2.mpr ht2,
  unfreezingI { induction hn : fintype.card ι with n IH generalizing ι E },
  { rw fintype.card_eq_zero_iff at hn,
    exact continuous_of_const (λ x y, funext hn.elim) },
  { haveI : finite_dimensional 𝕜 E := of_fintype_basis ξ,
    -- first step: thanks to the induction hypothesis, any n-dimensional subspace is equivalent
    -- to a standard space of dimension n, hence it is complete and therefore closed.
    have H₁ : ∀s : submodule 𝕜 E, finrank 𝕜 s = n → is_closed (s : set E),
    { assume s s_dim,
      letI : uniform_add_group s := s.to_add_subgroup.uniform_add_group,
      let b := basis.of_vector_space 𝕜 s,
      have U : uniform_embedding b.equiv_fun.symm.to_equiv,
      { have : fintype.card (basis.of_vector_space_index 𝕜 s) = n,
          by { rw ← s_dim, exact (finrank_eq_card_basis b).symm },
        have : continuous b.equiv_fun := IH b this,
        exact b.equiv_fun.symm.uniform_embedding b.equiv_fun.symm.to_linear_map.continuous_on_pi
          this },
      have : is_complete (s : set E),
        from complete_space_coe_iff_is_complete.1 ((complete_space_congr U).1 (by apply_instance)),
      exact this.is_closed },
    -- second step: any linear form is continuous, as its kernel is closed by the first step
    have H₂ : ∀f : E →ₗ[𝕜] 𝕜, continuous f,
    { assume f,
      by_cases H : finrank 𝕜 f.range = 0,
      { rw [finrank_eq_zero, linear_map.range_eq_bot] at H,
        rw H,
        exact continuous_zero },
      { have : finrank 𝕜 f.ker = n,
        { have Z := f.finrank_range_add_finrank_ker,
          rw [finrank_eq_card_basis ξ, hn] at Z,
          have : finrank 𝕜 f.range = 1,
            from le_antisymm (finrank_self 𝕜 ▸ f.range.finrank_le) (zero_lt_iff.mpr H),
          rw [this, add_comm, nat.add_one] at Z,
          exact nat.succ.inj Z },
        have : is_closed (f.ker : set E),
          from H₁ _ this,
        exact linear_map.continuous_of_is_closed_ker f this } },
    rw continuous_pi_iff,
    intros i,
    change continuous (ξ.coord i),
    exact H₂ (ξ.coord i) },
end

/-- Any linear map on a finite dimensional space over a complete field is continuous. -/
theorem linear_map.continuous_of_finite_dimensional [t2_space E] [finite_dimensional 𝕜 E]
  (f : E →ₗ[𝕜] F') :
  continuous f :=
begin
  -- for the proof, go to a model vector space `b → 𝕜` thanks to `continuous_equiv_fun_basis`, and
  -- argue that all linear maps there are continuous.
  let b := basis.of_vector_space 𝕜 E,
  have A : continuous b.equiv_fun :=
    continuous_equiv_fun_basis_aux b,
  have B : continuous (f.comp (b.equiv_fun.symm : (basis.of_vector_space_index 𝕜 E → 𝕜) →ₗ[𝕜] E)) :=
    linear_map.continuous_on_pi _,
  have : continuous ((f.comp (b.equiv_fun.symm : (basis.of_vector_space_index 𝕜 E → 𝕜) →ₗ[𝕜] E))
                      ∘ b.equiv_fun) := B.comp A,
  convert this,
  ext x,
  dsimp,
  rw [basis.equiv_fun_symm_apply, basis.sum_repr]
end

instance linear_map.continuous_linear_map_class_of_finite_dimensional
  [t2_space E] [finite_dimensional 𝕜 E] :
  continuous_linear_map_class (E →ₗ[𝕜] F') 𝕜 E F' :=
{ map_continuous := λ f, f.continuous_of_finite_dimensional,
  ..linear_map.semilinear_map_class }

/-- In finite dimensions over a non-discrete complete normed field, the canonical identification
(in terms of a basis) with `𝕜^n` (endowed with the product topology) is continuous.
This is the key fact wich makes all linear maps from a T2 finite dimensional TVS over such a field
continuous (see `linear_map.continuous_of_finite_dimensional`), which in turn implies that all
norms are equivalent in finite dimensions. -/
theorem continuous_equiv_fun_basis [t2_space E] {ι : Type*} [fintype ι] (ξ : basis ι 𝕜 E) :
  continuous ξ.equiv_fun :=
begin
  haveI : finite_dimensional 𝕜 E := of_fintype_basis ξ,
  exact ξ.equiv_fun.to_linear_map.continuous_of_finite_dimensional
end

namespace linear_map

variables [t2_space E] [finite_dimensional 𝕜 E]

/-- The continuous linear map induced by a linear map on a finite dimensional space -/
def to_continuous_linear_map : (E →ₗ[𝕜] F') ≃ₗ[𝕜] E →L[𝕜] F' :=
{ to_fun := λ f, ⟨f, f.continuous_of_finite_dimensional⟩,
  inv_fun := coe,
  map_add' := λ f g, rfl,
  map_smul' := λ c f, rfl,
  left_inv := λ f, rfl,
  right_inv := λ f, continuous_linear_map.coe_injective rfl }

@[simp] lemma coe_to_continuous_linear_map' (f : E →ₗ[𝕜] F') :
  ⇑f.to_continuous_linear_map = f := rfl

@[simp] lemma coe_to_continuous_linear_map (f : E →ₗ[𝕜] F') :
  (f.to_continuous_linear_map : E →ₗ[𝕜] F') = f := rfl

@[simp] lemma coe_to_continuous_linear_map_symm :
  ⇑(to_continuous_linear_map : (E →ₗ[𝕜] F') ≃ₗ[𝕜] E →L[𝕜] F').symm = coe := rfl

@[simp] lemma det_to_continuous_linear_map (f : E →ₗ[𝕜] E) :
  f.to_continuous_linear_map.det = f.det :=
rfl

@[simp] lemma ker_to_continuous_linear_map (f : E →ₗ[𝕜] F') :
  ker f.to_continuous_linear_map = ker f :=
rfl

@[simp] lemma range_to_continuous_linear_map (f : E →ₗ[𝕜] F') :
  range f.to_continuous_linear_map = range f :=
rfl

/-- A surjective linear map `f` with finite dimensional codomain is an open map. -/
lemma is_open_map_of_finite_dimensional (f : F →ₗ[𝕜] E) (hf : function.surjective f) :
  is_open_map f :=
begin
  rcases f.exists_right_inverse_of_surjective (linear_map.range_eq_top.2 hf) with ⟨g, hg⟩,
  refine is_open_map.of_sections (λ x, ⟨λ y, g (y - f x) + x, _, _, λ y, _⟩),
  { exact ((g.continuous_of_finite_dimensional.comp $ continuous_id.sub continuous_const).add
      continuous_const).continuous_at },
  { rw [sub_self, map_zero, zero_add] },
  { simp only [map_sub, map_add, ← comp_apply f g, hg, id_apply, sub_add_cancel] }
end

instance can_lift_continuous_linear_map : can_lift (E →ₗ[𝕜] F) (E →L[𝕜] F) coe (λ _, true) :=
⟨λ f _, ⟨f.to_continuous_linear_map, rfl⟩⟩

end linear_map

namespace linear_equiv

variables [t2_space E] [t2_space F] [finite_dimensional 𝕜 E]

/-- The continuous linear equivalence induced by a linear equivalence on a finite dimensional
space. -/
def to_continuous_linear_equiv (e : E ≃ₗ[𝕜] F) : E ≃L[𝕜] F :=
{ continuous_to_fun := e.to_linear_map.continuous_of_finite_dimensional,
  continuous_inv_fun := begin
    haveI : finite_dimensional 𝕜 F := e.finite_dimensional,
    exact e.symm.to_linear_map.continuous_of_finite_dimensional
  end,
  ..e }

@[simp] lemma coe_to_continuous_linear_equiv (e : E ≃ₗ[𝕜] F) :
  (e.to_continuous_linear_equiv : E →ₗ[𝕜] F) = e := rfl

@[simp] lemma coe_to_continuous_linear_equiv' (e : E ≃ₗ[𝕜] F) :
  (e.to_continuous_linear_equiv : E → F) = e := rfl

@[simp] lemma coe_to_continuous_linear_equiv_symm (e : E ≃ₗ[𝕜] F) :
  (e.to_continuous_linear_equiv.symm : F →ₗ[𝕜] E) = e.symm := rfl

@[simp] lemma coe_to_continuous_linear_equiv_symm' (e : E ≃ₗ[𝕜] F) :
  (e.to_continuous_linear_equiv.symm : F → E) = e.symm := rfl

@[simp] lemma to_linear_equiv_to_continuous_linear_equiv (e : E ≃ₗ[𝕜] F) :
  e.to_continuous_linear_equiv.to_linear_equiv = e :=
by { ext x, refl }

@[simp] lemma to_linear_equiv_to_continuous_linear_equiv_symm (e : E ≃ₗ[𝕜] F) :
  e.to_continuous_linear_equiv.symm.to_linear_equiv = e.symm :=
by { ext x, refl }

instance can_lift_continuous_linear_equiv :
  can_lift (E ≃ₗ[𝕜] F) (E ≃L[𝕜] F) continuous_linear_equiv.to_linear_equiv (λ _, true) :=
⟨λ f _, ⟨_, f.to_linear_equiv_to_continuous_linear_equiv⟩⟩

end linear_equiv

namespace continuous_linear_map

variables [t2_space E] [finite_dimensional 𝕜 E]

/-- Builds a continuous linear equivalence from a continuous linear map on a finite-dimensional
vector space whose determinant is nonzero. -/
def to_continuous_linear_equiv_of_det_ne_zero
  (f : E →L[𝕜] E) (hf : f.det ≠ 0) : E ≃L[𝕜] E :=
((f : E →ₗ[𝕜] E).equiv_of_det_ne_zero hf).to_continuous_linear_equiv

@[simp] lemma coe_to_continuous_linear_equiv_of_det_ne_zero (f : E →L[𝕜] E) (hf : f.det ≠ 0) :
  (f.to_continuous_linear_equiv_of_det_ne_zero hf : E →L[𝕜] E) = f :=
by { ext x, refl }

@[simp] lemma to_continuous_linear_equiv_of_det_ne_zero_apply
  (f : E →L[𝕜] E) (hf : f.det ≠ 0) (x : E) :
  f.to_continuous_linear_equiv_of_det_ne_zero hf x = f x :=
rfl

lemma _root_.matrix.to_lin_fin_two_prod_to_continuous_linear_map (a b c d : 𝕜) :
  (matrix.to_lin (basis.fin_two_prod 𝕜) (basis.fin_two_prod 𝕜)
      !![a, b; c, d]).to_continuous_linear_map =
  (a • continuous_linear_map.fst 𝕜 𝕜 𝕜 + b • continuous_linear_map.snd 𝕜 𝕜 𝕜).prod
  (c • continuous_linear_map.fst 𝕜 𝕜 𝕜 + d • continuous_linear_map.snd 𝕜 𝕜 𝕜) :=
continuous_linear_map.ext $ matrix.to_lin_fin_two_prod_apply _ _ _ _

end continuous_linear_map

end normed_field


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

Extended non-negative reals
-/
import data.real.nnreal order.bounds
noncomputable theory
open classical set lattice

local attribute [instance] prop_decidable
variables {α : Type*} {β : Type*}

/-- The extended nonnegative real numbers. This is usually denoted [0, ∞],
  and is relevant as the codomain of a measure. -/
def ennreal := with_top nnreal

local notation `∞` := (⊤ : ennreal)

namespace ennreal
variables {a b c d : ennreal} {r p q : nnreal}

instance : canonically_ordered_comm_semiring ennreal := by unfold ennreal; apply_instance
instance : decidable_linear_order ennreal := by unfold ennreal; apply_instance
instance : complete_linear_order ennreal := by unfold ennreal; apply_instance
instance : inhabited ennreal := ⟨0⟩

instance : densely_ordered ennreal := with_top.densely_ordered

instance : has_coe nnreal ennreal := ⟨ option.some ⟩

lemma none_eq_top : (none : ennreal) = (⊤ : ennreal) := rfl
lemma some_eq_coe (a : nnreal) : (some a : ennreal) = (↑a : ennreal) := rfl

/-- `to_nnreal x` returns `x` if it is real, otherwise 0. -/
protected def to_nnreal : ennreal → nnreal
| (some r) := r
| none := 0

@[simp] lemma to_nnreal_coe : (r : ennreal).to_nnreal = r := rfl

@[simp] lemma coe_to_nnreal : ∀{a:ennreal}, a ≠ ∞ → ↑(a.to_nnreal) = a
| (some r) h := rfl
| none     h := (h rfl).elim

@[simp] lemma top_to_nnreal : ∞.to_nnreal = 0 := rfl

lemma forall_ennreal {p : ennreal → Prop} : (∀a, p a) ↔ (∀r:nnreal, p r) ∧ p ∞ :=
⟨assume h, ⟨assume r, h _, h _⟩,
  assume ⟨h₁, h₂⟩ a, match a with some r := h₁ _ | none := h₂ end⟩

@[simp] lemma coe_zero : ↑(0 : nnreal) = (0 : ennreal) := rfl
@[simp] lemma coe_one : ↑(1 : nnreal) = (1 : ennreal) := rfl

@[simp] lemma coe_ne_top : (r : ennreal) ≠ ∞ := with_top.coe_ne_top
@[simp] lemma top_ne_coe : ∞ ≠ (r : ennreal) := with_top.top_ne_coe

@[simp] lemma zero_ne_top : 0 ≠ ∞ := coe_ne_top
@[simp] lemma top_ne_zero : ∞ ≠ 0 := top_ne_coe

@[simp] lemma one_ne_top : 1 ≠ ∞ := coe_ne_top
@[simp] lemma top_ne_one : ∞ ≠ 1 := top_ne_coe

@[simp] lemma coe_eq_coe : (↑r : ennreal) = ↑q ↔ r = q := with_top.coe_eq_coe
@[simp] lemma coe_le_coe : (↑r : ennreal) ≤ ↑q ↔ r ≤ q := with_top.coe_le_coe
@[simp] lemma coe_lt_coe : (↑r : ennreal) < ↑q ↔ r < q := with_top.coe_lt_coe

@[simp] lemma coe_eq_zero : (↑r : ennreal) = 0 ↔ r = 0 := coe_eq_coe
@[simp] lemma zero_eq_coe : 0 = (↑r : ennreal) ↔ 0 = r := coe_eq_coe
@[simp] lemma coe_eq_one : (↑r : ennreal) = 1 ↔ r = 1 := coe_eq_coe
@[simp] lemma one_eq_coe : 1 = (↑r : ennreal) ↔ 1 = r := coe_eq_coe

@[simp] lemma coe_add : ↑(r + p) = (r + p : ennreal) := with_top.coe_add
@[simp] lemma coe_mul : ↑(r * p) = (r * p : ennreal) := with_top.coe_mul

@[simp] lemma add_top : a + ∞ = ∞ := with_top.add_top
@[simp] lemma top_add : ∞ + a = ∞ := with_top.top_add

lemma add_eq_top : a + b = ∞ ↔ a = ∞ ∨ b = ∞ := with_top.add_eq_top _ _

lemma mul_top : a * ∞ = (if a = 0 then 0 else ∞) :=
begin split_ifs, { simp [h] }, { exact with_top.mul_top h } end

lemma top_mul : ∞ * a = (if a = 0 then 0 else ∞) :=
begin split_ifs, { simp [h] }, { exact with_top.top_mul h } end

@[simp] lemma top_mul_top : ∞ * ∞ = ∞ := with_top.top_mul_top

@[simp] lemma coe_finset_sum {s : finset α} {f : α → nnreal} : ↑(s.sum f) = (s.sum (λa, f a) : ennreal) :=
(finset.sum_hom coe coe_zero @coe_add).symm

@[simp] lemma coe_finset_prod {s : finset α} {f : α → nnreal} : ↑(s.prod f) = (s.prod (λa, f a) : ennreal) :=
(finset.prod_hom coe coe_one @coe_mul).symm

@[simp] lemma bot_eq_zero : (⊥ : ennreal) = 0 := rfl
section order

@[simp] lemma coe_lt_top : coe r < ∞ := with_top.coe_lt_top r
@[simp] lemma not_top_le_coe : ¬ (⊤:ennreal) ≤ ↑r := with_top.not_top_le_coe r
@[simp] lemma zero_lt_coe_iff : 0 < (↑p : ennreal) ↔ 0 < p := coe_lt_coe
@[simp] lemma one_le_coe_iff : (1:ennreal) ≤ ↑r ↔ 1 ≤ r := coe_le_coe
@[simp] lemma coe_le_one_iff : ↑r ≤ (1:ennreal) ↔ r ≤ 1 := coe_le_coe
@[simp] lemma coe_lt_one_iff : (↑p : ennreal) < 1 ↔ p < 1 := coe_lt_coe
@[simp] lemma one_lt_zero_iff : 1 < (↑p : ennreal) ↔ 1 < p := coe_lt_coe

lemma le_coe_iff : a ≤ ↑r ↔ (∃p:nnreal, a = p ∧ p ≤ r) := with_top.le_coe_iff r a
lemma coe_le_iff : ↑r ≤ a ↔ (∀p:nnreal, a = p → r ≤ p) := with_top.coe_le_iff r a

lemma lt_iff_exists_coe : a < b ↔ (∃p:nnreal, a = p ∧ ↑p < b) := with_top.lt_iff_exists_coe a b

-- TODO: move to canonically ordered semiring ...
protected lemma zero_lt_one : 0 < (1 : ennreal) := zero_lt_coe_iff.mpr zero_lt_one

@[simp] lemma not_lt_zero : ¬ a < 0 := by simp

lemma add_lt_add_iff_left : a < ⊤ → (a + c < a + b ↔ c < b) :=
with_top.add_lt_add_iff_left

lemma lt_add_right (ha : a < ⊤) (hb : 0 < b) : a < a + b :=
by rwa [← add_lt_add_iff_left ha, add_zero] at hb

lemma le_of_forall_epsilon_le : ∀{a b : ennreal}, (∀ε:nnreal, ε > 0 → b < ∞ → a ≤ b + ε) → a ≤ b
| a    none     h := le_top
| none (some a) h :=
  have (⊤:ennreal) ≤ ↑a + ↑(1:nnreal), from h 1 zero_lt_one coe_lt_top,
  by rw [← coe_add] at this; exact (not_top_le_coe this).elim
| (some a) (some b) h :=
    by simp only [none_eq_top, some_eq_coe, coe_add.symm, coe_le_coe, coe_lt_top, true_implies_iff] at *;
      exact nnreal.le_of_forall_epsilon_le h

protected lemma lt_iff_exists_rat_btwn :
  a < b ↔ (∃q:ℚ, 0 ≤ q ∧ a < nnreal.of_real q ∧ (nnreal.of_real q:ennreal) < b) :=
⟨λ h,
  begin
    rcases lt_iff_exists_coe.1 h with ⟨p, rfl, _⟩,
    rcases dense h with ⟨c, pc, cb⟩,
    rcases lt_iff_exists_coe.1 cb with ⟨r, rfl, _⟩,
    rcases (nnreal.lt_iff_exists_rat_btwn _ _).1 (coe_lt_coe.1 pc) with ⟨q, hq0, pq, qr⟩,
    exact ⟨q, hq0, coe_lt_coe.2 pq, lt_trans (coe_lt_coe.2 qr) cb⟩
  end,
λ ⟨q, q0, qa, qb⟩, lt_trans qa qb⟩

end order

section complete_lattice

lemma coe_Sup {s : set nnreal} : bdd_above s → (↑(Sup s) : ennreal) = (⨆a∈s, ↑a) := with_top.coe_Sup
lemma coe_Inf {s : set nnreal} : s ≠ ∅ → (↑(Inf s) : ennreal) = (⨅a∈s, ↑a) := with_top.coe_Inf

@[simp] lemma top_mem_upper_bounds {s : set ennreal} : ∞ ∈ upper_bounds s :=
assume x hx, le_top

lemma coe_mem_upper_bounds {s : set nnreal} :
  ↑r ∈ upper_bounds ((coe : nnreal → ennreal) '' s) ↔ r ∈ upper_bounds s :=
by simp [upper_bounds, ball_image_iff, -mem_image, *] {contextual := tt}

end complete_lattice

section mul

lemma mul_eq_mul_left : a ≠ 0 → a ≠ ⊤ → (a * b = a * c ↔ b = c) :=
begin
  cases a; cases b; cases c;
    simp [none_eq_top, some_eq_coe, mul_top, top_mul, -coe_mul, coe_mul.symm,
      nnreal.mul_eq_mul_left] {contextual := tt},
end

lemma mul_le_mul_left : a ≠ 0 → a ≠ ⊤ → (a * b ≤ a * c ↔ b ≤ c) :=
begin
  cases a; cases b; cases c;
    simp [none_eq_top, some_eq_coe, mul_top, top_mul, -coe_mul, coe_mul.symm] {contextual := tt},
  assume h, exact mul_le_mul_left (zero_lt_iff_ne_zero.2 h)
end

end mul

section sub
instance : has_sub ennreal := ⟨λa b, Inf {d | a ≤ d + b}⟩

lemma coe_sub : ↑(p - r) = (↑p:ennreal) - r :=
le_antisymm
  (le_Inf $ assume b (hb : ↑p ≤ b + r), coe_le_iff.2 $
    by rintros d rfl; rwa [← coe_add, coe_le_coe, ← nnreal.sub_le_iff_le_add] at hb)
  (Inf_le $ show (↑p : ennreal) ≤ ↑(p - r) + ↑r,
    by rw [← coe_add, coe_le_coe, ← nnreal.sub_le_iff_le_add])

@[simp] lemma top_sub_coe : ∞ - ↑r = ∞ :=
top_unique $ le_Inf $ by simp [add_eq_top]

@[simp] lemma sub_eq_zero_of_le (h : a ≤ b) : a - b = 0 :=
le_antisymm (Inf_le $ le_add_left h) (zero_le _)

@[simp] lemma zero_sub : 0 - a = 0 :=
le_antisymm (Inf_le $ zero_le _) (zero_le _)

@[simp] lemma sub_infty : a - ∞ = 0 :=
le_antisymm (Inf_le $ by simp) (zero_le _)

lemma sub_le_sub (h₁ : a ≤ b) (h₂ : d ≤ c) : a - c ≤ b - d :=
Inf_le_Inf $ assume e (h : b ≤ e + d),
  calc a ≤ b : h₁
    ... ≤ e + d : h
    ... ≤ e + c : add_le_add' (le_refl _) h₂

@[simp] lemma add_sub_self : ∀{a b : ennreal}, b < ∞ → (a + b) - b = a
| a        none     := by simp [none_eq_top]
| none     (some b) := by simp [none_eq_top, some_eq_coe]
| (some a) (some b) :=
  by simp [some_eq_coe]; rw [← coe_add, ← coe_sub, coe_eq_coe, nnreal.add_sub_cancel]

@[simp] lemma add_sub_self' (h : a < ∞) : (a + b) - a = b :=
by rw [add_comm, add_sub_self h]

lemma add_left_inj (h : a < ∞) : a + b = a + c ↔ b = c :=
⟨λ e, by simpa [h] using congr_arg (λ x, x - a) e, congr_arg _⟩

lemma add_right_inj (h : a < ∞) : b + a = c + a ↔ b = c :=
by rw [add_comm, add_comm c, add_left_inj h]

@[simp] lemma sub_add_cancel_of_le : ∀{a b : ennreal}, b ≤ a → (a - b) + b = a :=
begin
  simp [forall_ennreal, le_coe_iff, -add_comm] {contextual := tt},
  rintros r p x rfl h,
  rw [← coe_sub, ← coe_add, nnreal.sub_add_cancel_of_le h]
end

@[simp] lemma add_sub_cancel_of_le (h : b ≤ a) : b + (a - b) = a :=
by rwa [add_comm, sub_add_cancel_of_le]

lemma sub_add_self_eq_max : (a - b) + b = max a b :=
match le_total a b with
| or.inl h := by simp [h, max_eq_right]
| or.inr h := by simp [h, max_eq_left]
end

@[simp] protected lemma sub_le_iff_le_add : a - b ≤ c ↔ a ≤ c + b :=
iff.intro
  (assume h : a - b ≤ c,
    calc a ≤ (a - b) + b : by rw [sub_add_self_eq_max]; exact le_max_left _ _
      ... ≤ c + b : add_le_add' h (le_refl _))
  (assume h : a ≤ c + b,
    calc a - b ≤ (c + b) - b : sub_le_sub h (le_refl _)
      ... ≤ c : Inf_le (le_refl (c + b)))

lemma sub_le_self (a b : ennreal) : a - b ≤ a :=
ennreal.sub_le_iff_le_add.2 $ le_add_of_nonneg_right' $ zero_le _

@[simp] lemma sub_zero : a - 0 = a :=
eq.trans (add_zero (a - 0)).symm $ by simp

lemma sub_sub_cancel (h : a < ∞) (h2 : b ≤ a) : a - (a - b) = b :=
by rw [← add_right_inj (lt_of_le_of_lt (sub_le_self _ _) h),
  sub_add_cancel_of_le (sub_le_self _ _), add_sub_cancel_of_le h2]

end sub

section inv
instance : has_inv ennreal := ⟨λa, Inf {b | 1 ≤ a * b}⟩
instance : has_div ennreal := ⟨λa b, a * b⁻¹⟩

lemma div_def : a / b = a * b⁻¹ := rfl

@[simp] lemma inv_zero : (0 : ennreal)⁻¹ = ∞ :=
show Inf {b : ennreal | 1 ≤ 0 * b} = ∞, by simp; refl

@[simp] lemma inv_top : (∞ : ennreal)⁻¹ = 0 :=
bot_unique $ le_of_forall_le_of_dense $ λ a (h : a > 0), Inf_le $ by simp [*, ne_of_gt h, top_mul]

@[simp] lemma inv_coe (hr : r ≠ 0) : (r : ennreal)⁻¹ = ↑(r⁻¹ : nnreal) :=
le_antisymm
  (Inf_le $ by simp; rw [← coe_mul, nnreal.mul_inv_cancel hr]; exact le_refl 1)
  (le_Inf $ assume b (hb : 1 ≤ ↑r * b), coe_le_iff.2 $
    by rintros b rfl; rwa [← coe_mul, ← coe_one, coe_le_coe, ← nnreal.inv_le hr] at hb)

@[simp] lemma inv_eq_top : (a)⁻¹ = ∞ ↔ a = 0 :=
by by_cases a = 0; cases a; simp [*, none_eq_top, some_eq_coe] at *

lemma inv_ne_top : (a)⁻¹ ≠ ∞ ↔ a ≠ 0 := by simp

@[simp] lemma inv_eq_zero : (a)⁻¹ = 0 ↔ a = ∞ :=
by by_cases a = 0; cases a; simp [*, none_eq_top, some_eq_coe] at *

lemma inv_ne_zero : (a)⁻¹ ≠ 0 ↔ a ≠ ∞ := by simp

@[simp] lemma inv_inv : ∀{a:ennreal}, (a⁻¹)⁻¹ = a :=
forall_ennreal.mpr $ and.intro (assume r, by by_cases r = 0; simp [*, inv_pos]) (by simp)

lemma le_div_iff_mul_le : ∀{b}, b ≠ 0 → b ≠ ⊤ → (a ≤ c / b ↔ a * b ≤ c)
| none     h0 ht := (ht rfl).elim
| (some r) h0 ht :=
  begin
    have hr : r ≠ 0, from mt coe_eq_coe.2 h0,
    rw [← ennreal.mul_le_mul_left h0 ht],
    suffices : ↑r * a ≤ (↑r  * ↑r⁻¹) * c ↔ a * ↑r ≤ c,
    { simpa [some_eq_coe, div_def, hr, mul_left_comm, mul_comm, mul_assoc] },
    rw [← coe_mul, nnreal.mul_inv_cancel hr, coe_one, one_mul, mul_comm]
  end

lemma div_le_iff_le_mul (hb0 : b ≠ 0) (hbt : b ≠ ⊤) : (a / b ≤ c ↔ a ≤ c * b) :=
suffices a * b⁻¹ ≤ c ↔ a ≤ c / b⁻¹, by simpa [div_def],
(le_div_iff_mul_le (inv_ne_zero.2 hbt) (inv_ne_top.2 hb0)).symm

lemma inv_le_iff_le_mul : (b = ⊤ → a ≠ 0) → (a = ⊤ → b ≠ 0) → (a⁻¹ ≤ b ↔ 1 ≤ a * b) :=
begin
  cases a; cases b; simp [none_eq_top, some_eq_coe, mul_top, top_mul] {contextual := tt},
  by_cases a = 0; simp [*, -coe_mul, coe_mul.symm, nnreal.inv_le]
end

@[simp] lemma le_inv_iff_mul_le : a ≤ b⁻¹ ↔ a * b ≤ 1 :=
begin
  cases b, { by_cases a = 0; simp [*, none_eq_top, mul_top] },
  by_cases b = 0; simp [*, some_eq_coe, le_div_iff_mul_le],
  suffices : a ≤ 1 / b ↔ a * b ≤ 1, { simpa [div_def, h] },
  exact le_div_iff_mul_le (mt coe_eq_coe.1 h) coe_ne_top
end

end inv

section infi
variables {ι : Sort*} {f g : ι → ennreal}

lemma infi_add : infi f + a = ⨅i, f i + a :=
le_antisymm
  (le_infi $ assume i, add_le_add' (infi_le _ _) $ le_refl _)
  (ennreal.sub_le_iff_le_add.1 $ le_infi $ assume i, ennreal.sub_le_iff_le_add.2 $ infi_le _ _)

lemma supr_sub : (⨆i, f i) - a = (⨆i, f i - a) :=
le_antisymm
  (ennreal.sub_le_iff_le_add.2 $ supr_le $ assume i, ennreal.sub_le_iff_le_add.1 $ le_supr _ i)
  (supr_le $ assume i, ennreal.sub_le_sub (le_supr _ _) (le_refl a))

lemma sub_infi : a - (⨅i, f i) = (⨆i, a - f i) :=
begin
  refine (eq_of_forall_ge_iff $ λ c, _),
  rw [ennreal.sub_le_iff_le_add, add_comm, infi_add],
  simp [ennreal.sub_le_iff_le_add]
end

lemma Inf_add {s : set ennreal} : Inf s + a = ⨅b∈s, b + a :=
by simp [Inf_eq_infi, infi_add]

lemma add_infi {a : ennreal} : a + infi f = ⨅b, a + f b :=
by rw [add_comm, infi_add]; simp

lemma infi_add_infi (h : ∀i j, ∃k, f k + g k ≤ f i + g j) : infi f + infi g = (⨅a, f a + g a) :=
suffices (⨅a, f a + g a) ≤ infi f + infi g,
  from le_antisymm (le_infi $ assume a, add_le_add' (infi_le _ _) (infi_le _ _)) this,
calc (⨅a, f a + g a) ≤ (⨅ a a', f a + g a') :
    le_infi $ assume a, le_infi $ assume a',
      let ⟨k, h⟩ := h a a' in infi_le_of_le k h
  ... ≤ infi f + infi g :
    by simp [add_infi, infi_add, -add_comm, -le_infi_iff]; exact le_refl _

lemma infi_sum {f : ι → α → ennreal} {s : finset α} [nonempty ι]
  (h : ∀(t : finset α) (i j : ι), ∃k, ∀a∈t, f k a ≤ f i a ∧ f k a ≤ f j a) :
  (⨅i, s.sum (f i)) = s.sum (λa, ⨅i, f i a) :=
finset.induction_on s (by simp) $ assume a s ha ih,
  have ∀ (i j : ι), ∃ (k : ι), f k a + s.sum (f k) ≤ f i a + s.sum (f j),
    from assume i j,
    let ⟨k, hk⟩ := h (insert a s) i j in
    ⟨k, add_le_add' (hk a (finset.mem_insert_self _ _)).left $ finset.sum_le_sum' $
      assume a ha, (hk _ $ finset.mem_insert_of_mem ha).right⟩,
  by simp [ha, ih.symm, infi_add_infi this]

end infi

end ennreal