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

import set_theory.ordinal_arithmetic
import tactic.linarith
import logic.small

/-!
# Cardinals and ordinals

Relationships between cardinals and ordinals, properties of cardinals that are proved
using ordinals.

## Main definitions

* The function `cardinal.aleph'` gives the cardinals listed by their ordinal
  index, and is the inverse of `cardinal.aleph_idx`.
  `aleph' n = n`, `aleph' ω = cardinal.omega = ℵ₀`, `aleph' (ω + 1) = ℵ₁`, etc.
  It is an order isomorphism between ordinals and cardinals.
* The function `cardinal.aleph` gives the infinite cardinals listed by their
  ordinal index. `aleph 0 = cardinal.omega = ℵ₀`, `aleph 1 = ℵ₁` is the first
  uncountable cardinal, and so on.

## Main Statements

* `cardinal.mul_eq_max` and `cardinal.add_eq_max` state that the product (resp. sum) of two infinite
  cardinals is just their maximum. Several variations around this fact are also given.
* `cardinal.mk_list_eq_mk` : when `α` is infinite, `α` and `list α` have the same cardinality.
* simp lemmas for inequalities between `bit0 a` and `bit1 b` are registered, making `simp`
  able to prove inequalities about numeral cardinals.

## Tags

cardinal arithmetic (for infinite cardinals)
-/

noncomputable theory

open function cardinal set equiv
open_locale classical cardinal

universes u v w

namespace cardinal
section using_ordinals
open ordinal

theorem ord_is_limit {c} (co : ω ≤ c) : (ord c).is_limit :=
begin
  refine ⟨λ h, omega_ne_zero _, λ a, lt_imp_lt_of_le_imp_le _⟩,
  { rw [← ordinal.le_zero, ord_le] at h,
    simpa only [card_zero, nonpos_iff_eq_zero] using le_trans co h },
  { intro h, rw [ord_le] at h ⊢,
    rwa [← @add_one_of_omega_le (card a), ← card_succ],
    rw [← ord_le, ← le_succ_of_is_limit, ord_le],
    { exact le_trans co h },
    { rw ord_omega, exact omega_is_limit } }
end

/-- The `aleph'` index function, which gives the ordinal index of a cardinal.
  (The `aleph'` part is because unlike `aleph` this counts also the
  finite stages. So `aleph_idx n = n`, `aleph_idx ω = ω`,
  `aleph_idx ℵ₁ = ω + 1` and so on.)
  In this definition, we register additionally that this function is an initial segment,
  i.e., it is order preserving and its range is an initial segment of the ordinals.
  For the basic function version, see `aleph_idx`.
  For an upgraded version stating that the range is everything, see `aleph_idx.rel_iso`. -/
def aleph_idx.initial_seg : @initial_seg cardinal ordinal (<) (<) :=
@rel_embedding.collapse cardinal ordinal (<) (<) _ cardinal.ord.order_embedding.lt_embedding

/-- The `aleph'` index function, which gives the ordinal index of a cardinal.
  (The `aleph'` part is because unlike `aleph` this counts also the
  finite stages. So `aleph_idx n = n`, `aleph_idx ω = ω`,
  `aleph_idx ℵ₁ = ω + 1` and so on.)
  For an upgraded version stating that the range is everything, see `aleph_idx.rel_iso`. -/
def aleph_idx : cardinal → ordinal := aleph_idx.initial_seg

@[simp] theorem aleph_idx.initial_seg_coe :
  (aleph_idx.initial_seg : cardinal → ordinal) = aleph_idx := rfl

@[simp] theorem aleph_idx_lt {a b} : aleph_idx a < aleph_idx b ↔ a < b :=
aleph_idx.initial_seg.to_rel_embedding.map_rel_iff

@[simp] theorem aleph_idx_le {a b} : aleph_idx a ≤ aleph_idx b ↔ a ≤ b :=
by rw [← not_lt, ← not_lt, aleph_idx_lt]

theorem aleph_idx.init {a b} : b < aleph_idx a → ∃ c, aleph_idx c = b :=
aleph_idx.initial_seg.init _ _

/-- The `aleph'` index function, which gives the ordinal index of a cardinal.
  (The `aleph'` part is because unlike `aleph` this counts also the
  finite stages. So `aleph_idx n = n`, `aleph_idx ω = ω`,
  `aleph_idx ℵ₁ = ω + 1` and so on.)
  In this version, we register additionally that this function is an order isomorphism
  between cardinals and ordinals.
  For the basic function version, see `aleph_idx`. -/
def aleph_idx.rel_iso : @rel_iso cardinal.{u} ordinal.{u} (<) (<) :=
@rel_iso.of_surjective cardinal.{u} ordinal.{u} (<) (<) aleph_idx.initial_seg.{u} $
(initial_seg.eq_or_principal aleph_idx.initial_seg.{u}).resolve_right $
λ ⟨o, e⟩, begin
  have : ∀ c, aleph_idx c < o := λ c, (e _).2 ⟨_, rfl⟩,
  refine ordinal.induction_on o _ this, introsI α r _ h,
  let s := sup.{u u} (λ a:α, inv_fun aleph_idx (ordinal.typein r a)),
  apply not_le_of_gt (lt_succ_self s),
  have I : injective aleph_idx := aleph_idx.initial_seg.to_embedding.injective,
  simpa only [typein_enum, left_inverse_inv_fun I (succ s)] using
    le_sup.{u u} (λ a, inv_fun aleph_idx (ordinal.typein r a))
      (ordinal.enum r _ (h (succ s))),
end

@[simp] theorem aleph_idx.rel_iso_coe :
  (aleph_idx.rel_iso : cardinal → ordinal) = aleph_idx := rfl

@[simp] theorem type_cardinal : @ordinal.type cardinal (<) _ = ordinal.univ.{u (u+1)} :=
by rw ordinal.univ_id; exact quotient.sound ⟨aleph_idx.rel_iso⟩

@[simp] theorem mk_cardinal : #cardinal = univ.{u (u+1)} :=
by simpa only [card_type, card_univ] using congr_arg card type_cardinal

/-- The `aleph'` function gives the cardinals listed by their ordinal
  index, and is the inverse of `aleph_idx`.
  `aleph' n = n`, `aleph' ω = ω`, `aleph' (ω + 1) = ℵ₁`, etc.
  In this version, we register additionally that this function is an order isomorphism
  between ordinals and cardinals.
  For the basic function version, see `aleph'`. -/
def aleph'.rel_iso := cardinal.aleph_idx.rel_iso.symm

/-- The `aleph'` function gives the cardinals listed by their ordinal
  index, and is the inverse of `aleph_idx`.
  `aleph' n = n`, `aleph' ω = ω`, `aleph' (ω + 1) = ℵ₁`, etc. -/
def aleph' : ordinal → cardinal := aleph'.rel_iso

@[simp] theorem aleph'.rel_iso_coe :
  (aleph'.rel_iso : ordinal → cardinal) = aleph' := rfl

@[simp] theorem aleph'_lt {o₁ o₂ : ordinal.{u}} : aleph' o₁ < aleph' o₂ ↔ o₁ < o₂ :=
aleph'.rel_iso.map_rel_iff

@[simp] theorem aleph'_le {o₁ o₂ : ordinal.{u}} : aleph' o₁ ≤ aleph' o₂ ↔ o₁ ≤ o₂ :=
le_iff_le_iff_lt_iff_lt.2 aleph'_lt

@[simp] theorem aleph'_aleph_idx (c : cardinal.{u}) : aleph' c.aleph_idx = c :=
cardinal.aleph_idx.rel_iso.to_equiv.symm_apply_apply c

@[simp] theorem aleph_idx_aleph' (o : ordinal.{u}) : (aleph' o).aleph_idx = o :=
cardinal.aleph_idx.rel_iso.to_equiv.apply_symm_apply o

@[simp] theorem aleph'_zero : aleph' 0 = 0 :=
by rw [← nonpos_iff_eq_zero, ← aleph'_aleph_idx 0, aleph'_le];
   apply ordinal.zero_le

@[simp] theorem aleph'_succ {o : ordinal.{u}} : aleph' o.succ = (aleph' o).succ :=
le_antisymm
 (cardinal.aleph_idx_le.1 $
  by rw [aleph_idx_aleph', ordinal.succ_le, ← aleph'_lt, aleph'_aleph_idx];
     apply cardinal.lt_succ_self)
 (cardinal.succ_le.2 $ aleph'_lt.2 $ ordinal.lt_succ_self _)

@[simp] theorem aleph'_nat : ∀ n : ℕ, aleph' n = n
| 0     := aleph'_zero
| (n+1) := show aleph' (ordinal.succ n) = n.succ,
           by rw [aleph'_succ, aleph'_nat, nat_succ]

theorem aleph'_le_of_limit {o : ordinal.{u}} (l : o.is_limit) {c} :
  aleph' o ≤ c ↔ ∀ o' < o, aleph' o' ≤ c :=
⟨λ h o' h', le_trans (aleph'_le.2 $ le_of_lt h') h,
 λ h, begin
  rw [← aleph'_aleph_idx c, aleph'_le, ordinal.limit_le l],
  intros x h',
  rw [← aleph'_le, aleph'_aleph_idx],
  exact h _ h'
end⟩

@[simp] theorem aleph'_omega : aleph' ordinal.omega = ω :=
eq_of_forall_ge_iff $ λ c, begin
  simp only [aleph'_le_of_limit omega_is_limit, ordinal.lt_omega, exists_imp_distrib, omega_le],
  exact forall_swap.trans (forall_congr $ λ n, by simp only [forall_eq, aleph'_nat]),
end

/-- `aleph'` and `aleph_idx` form an equivalence between `ordinal` and `cardinal` -/
@[simp] def aleph'_equiv : ordinal ≃ cardinal :=
⟨aleph', aleph_idx, aleph_idx_aleph', aleph'_aleph_idx⟩

/-- The `aleph` function gives the infinite cardinals listed by their
  ordinal index. `aleph 0 = ω`, `aleph 1 = succ ω` is the first
  uncountable cardinal, and so on. -/
def aleph (o : ordinal) : cardinal := aleph' (ordinal.omega + o)

@[simp] theorem aleph_lt {o₁ o₂ : ordinal.{u}} : aleph o₁ < aleph o₂ ↔ o₁ < o₂ :=
aleph'_lt.trans (ordinal.add_lt_add_iff_left _)

@[simp] theorem aleph_le {o₁ o₂ : ordinal.{u}} : aleph o₁ ≤ aleph o₂ ↔ o₁ ≤ o₂ :=
le_iff_le_iff_lt_iff_lt.2 aleph_lt

@[simp] theorem aleph_succ {o : ordinal.{u}} : aleph o.succ = (aleph o).succ :=
by rw [aleph, ordinal.add_succ, aleph'_succ]; refl

@[simp] theorem aleph_zero : aleph 0 = ω :=
by simp only [aleph, add_zero, aleph'_omega]

theorem omega_le_aleph' {o : ordinal} : ω ≤ aleph' o ↔ ordinal.omega ≤ o :=
by rw [← aleph'_omega, aleph'_le]

theorem omega_le_aleph (o : ordinal) : ω ≤ aleph o :=
by rw [aleph, omega_le_aleph']; apply ordinal.le_add_right

theorem ord_aleph_is_limit (o : ordinal) : is_limit (aleph o).ord :=
ord_is_limit $ omega_le_aleph _

theorem exists_aleph {c : cardinal} : ω ≤ c ↔ ∃ o, c = aleph o :=
⟨λ h, ⟨aleph_idx c - ordinal.omega,
  by rw [aleph, ordinal.add_sub_cancel_of_le, aleph'_aleph_idx];
     rwa [← omega_le_aleph', aleph'_aleph_idx]⟩,
 λ ⟨o, e⟩, e.symm ▸ omega_le_aleph _⟩

theorem aleph'_is_normal : is_normal (ord ∘ aleph') :=
⟨λ o, ord_lt_ord.2 $ aleph'_lt.2 $ ordinal.lt_succ_self _,
 λ o l a, by simp only [ord_le, aleph'_le_of_limit l]⟩

theorem aleph_is_normal : is_normal (ord ∘ aleph) :=
aleph'_is_normal.trans $ add_is_normal ordinal.omega

theorem succ_omega : succ ω = aleph 1 :=
by rw [← aleph_zero, ← aleph_succ, ordinal.succ_zero]

lemma countable_iff_lt_aleph_one {α : Type*} (s : set α) : countable s ↔ #s < aleph 1 :=
by rw [← succ_omega, lt_succ, mk_set_le_omega]

/-! ### Properties of `mul` -/

/-- If `α` is an infinite type, then `α × α` and `α` have the same cardinality. -/
theorem mul_eq_self {c : cardinal} (h : ω ≤ c) : c * c = c :=
begin
  refine le_antisymm _
    (by simpa only [mul_one] using
      mul_le_mul_left' (one_lt_omega.le.trans h) c),
  -- the only nontrivial part is `c * c ≤ c`. We prove it inductively.
  refine acc.rec_on (cardinal.wf.apply c) (λ c _,
    quotient.induction_on c $ λ α IH ol, _) h,
  -- consider the minimal well-order `r` on `α` (a type with cardinality `c`).
  rcases ord_eq α with ⟨r, wo, e⟩, resetI,
  letI := linear_order_of_STO' r,
  haveI : is_well_order α (<) := wo,
  -- Define an order `s` on `α × α` by writing `(a, b) < (c, d)` if `max a b < max c d`, or
  -- the max are equal and `a < c`, or the max are equal and `a = c` and `b < d`.
  let g : α × α → α := λ p, max p.1 p.2,
  let f : α × α ↪ ordinal × (α × α) :=
    ⟨λ p:α×α, (typein (<) (g p), p), λ p q, congr_arg prod.snd⟩,
  let s := f ⁻¹'o (prod.lex (<) (prod.lex (<) (<))),
  -- this is a well order on `α × α`.
  haveI : is_well_order _ s := (rel_embedding.preimage _ _).is_well_order,
  /- it suffices to show that this well order is smaller than `r`
     if it were larger, then `r` would be a strict prefix of `s`. It would be contained in
    `β × β` for some `β` of cardinality `< c`. By the inductive assumption, this set has the
    same cardinality as `β` (or it is finite if `β` is finite), so it is `< c`, which is a
    contradiction. -/
  suffices : type s ≤ type r, {exact card_le_card this},
  refine le_of_forall_lt (λ o h, _),
  rcases typein_surj s h with ⟨p, rfl⟩,
  rw [← e, lt_ord],
  refine lt_of_le_of_lt (_ : _ ≤ card (typein (<) (g p)).succ * card (typein (<) (g p)).succ) _,
  { have : {q|s q p} ⊆ (insert (g p) {x | x < (g p)}).prod (insert (g p) {x | x < (g p)}),
    { intros q h,
      simp only [s, embedding.coe_fn_mk, order.preimage, typein_lt_typein, prod.lex_def, typein_inj]
        at h,
      exact max_le_iff.1 (le_iff_lt_or_eq.2 $ h.imp_right and.left) },
    suffices H : (insert (g p) {x | r x (g p)} : set α) ≃ ({x | r x (g p)} ⊕ punit),
    { exact ⟨(set.embedding_of_subset _ _ this).trans
        ((equiv.set.prod _ _).trans (H.prod_congr H)).to_embedding⟩ },
    refine (equiv.set.insert _).trans
      ((equiv.refl _).sum_congr punit_equiv_punit),
    apply @irrefl _ r },
  cases lt_or_le (card (typein (<) (g p)).succ) ω with qo qo,
  { exact lt_of_lt_of_le (mul_lt_omega qo qo) ol },
  { suffices, {exact lt_of_le_of_lt (IH _ this qo) this},
    rw ← lt_ord, apply (ord_is_limit ol).2,
    rw [mk_def, e], apply typein_lt_type }
end

end using_ordinals

/-- If `α` and `β` are infinite types, then the cardinality of `α × β` is the maximum
of the cardinalities of `α` and `β`. -/
theorem mul_eq_max {a b : cardinal} (ha : ω ≤ a) (hb : ω ≤ b) : a * b = max a b :=
le_antisymm
  (mul_eq_self (le_trans ha (le_max_left a b)) ▸
    mul_le_mul' (le_max_left _ _) (le_max_right _ _)) $
max_le
  (by simpa only [mul_one] using
    mul_le_mul_left' (one_lt_omega.le.trans hb) a)
  (by simpa only [one_mul] using
    mul_le_mul_right' (one_lt_omega.le.trans ha) b)

@[simp] theorem omega_mul_eq {a : cardinal} (ha : ω ≤ a) : ω * a = a :=
(mul_eq_max le_rfl ha).trans (max_eq_right ha)

@[simp] theorem mul_omega_eq {a : cardinal} (ha : ω ≤ a) : a * ω = a :=
(mul_eq_max ha le_rfl).trans (max_eq_left ha)

theorem mul_lt_of_lt {a b c : cardinal} (hc : ω ≤ c)
  (h1 : a < c) (h2 : b < c) : a * b < c :=
lt_of_le_of_lt (mul_le_mul' (le_max_left a b) (le_max_right a b)) $
(lt_or_le (max a b) ω).elim
  (λ h, lt_of_lt_of_le (mul_lt_omega h h) hc)
  (λ h, by rw mul_eq_self h; exact max_lt h1 h2)

lemma mul_le_max_of_omega_le_left {a b : cardinal} (h : ω ≤ a) : a * b ≤ max a b :=
begin
  convert mul_le_mul' (le_max_left a b) (le_max_right a b),
  rw [mul_eq_self],
  refine le_trans h (le_max_left a b)
end

lemma mul_eq_max_of_omega_le_left {a b : cardinal} (h : ω ≤ a) (h' : b ≠ 0) : a * b = max a b :=
begin
  cases le_or_lt ω b with hb hb, { exact mul_eq_max h hb },
  refine (mul_le_max_of_omega_le_left h).antisymm _,
  have : b ≤ a, from hb.le.trans h,
  rw [max_eq_left this],
  convert mul_le_mul_left' (one_le_iff_ne_zero.mpr h') _, rw [mul_one],
end

theorem mul_le_max (a b : cardinal) : a * b ≤ max (max a b) ω :=
begin
  by_cases ha0 : a = 0,
  { simp [ha0] },
  by_cases hb0 : b = 0,
  { simp [hb0] },
  by_cases ha : ω ≤ a,
  { rw [mul_eq_max_of_omega_le_left ha hb0],
    exact le_max_left _ _ },
  { by_cases hb : ω ≤ b,
    { rw [mul_comm, mul_eq_max_of_omega_le_left hb ha0, max_comm],
      exact le_max_left _ _ },
    { exact le_max_of_le_right (le_of_lt (mul_lt_omega (lt_of_not_ge ha) (lt_of_not_ge hb))) } }
end

lemma mul_eq_left {a b : cardinal} (ha : ω ≤ a) (hb : b ≤ a) (hb' : b ≠ 0) : a * b = a :=
by { rw [mul_eq_max_of_omega_le_left ha hb', max_eq_left hb] }

lemma mul_eq_right {a b : cardinal} (hb : ω ≤ b) (ha : a ≤ b) (ha' : a ≠ 0) : a * b = b :=
by { rw [mul_comm, mul_eq_left hb ha ha'] }

lemma le_mul_left {a b : cardinal} (h : b ≠ 0) : a ≤ b * a :=
by { convert mul_le_mul_right' (one_le_iff_ne_zero.mpr h) _,
  rw [one_mul] }

lemma le_mul_right {a b : cardinal} (h : b ≠ 0) : a ≤ a * b :=
by { rw [mul_comm], exact le_mul_left h }

lemma mul_eq_left_iff {a b : cardinal} : a * b = a ↔ ((max ω b ≤ a ∧ b ≠ 0) ∨ b = 1 ∨ a = 0) :=
begin
  rw [max_le_iff], split,
  { intro h,
    cases (le_or_lt ω a) with ha ha,
    { have : a ≠ 0, { rintro rfl, exact not_lt_of_le ha omega_pos },
      left, use ha,
      { rw [← not_lt], intro hb, apply ne_of_gt _ h, refine lt_of_lt_of_le hb (le_mul_left this) },
      { rintro rfl, apply this, rw [_root_.mul_zero] at h, subst h }},
    right, by_cases h2a : a = 0, { right, exact h2a },
    have hb : b ≠ 0, { rintro rfl, apply h2a, rw [mul_zero] at h, subst h },
    left, rw [← h, mul_lt_omega_iff, lt_omega, lt_omega] at ha,
    rcases ha with rfl|rfl|⟨⟨n, rfl⟩, ⟨m, rfl⟩⟩, contradiction, contradiction,
    rw [← ne] at h2a, rw [← one_le_iff_ne_zero] at h2a hb, norm_cast at h2a hb h ⊢,
    apply le_antisymm _ hb, rw [← not_lt], intro h2b,
    apply ne_of_gt _ h, conv_lhs { rw [← mul_one n] },
    rwa [mul_lt_mul_left], apply nat.lt_of_succ_le h2a },
  { rintro (⟨⟨ha, hab⟩, hb⟩|rfl|rfl),
    { rw [mul_eq_max_of_omega_le_left ha hb, max_eq_left hab] },
    all_goals {simp}}
end

/-! ### Properties of `add` -/

/-- If `α` is an infinite type, then `α ⊕ α` and `α` have the same cardinality. -/
theorem add_eq_self {c : cardinal} (h : ω ≤ c) : c + c = c :=
le_antisymm
  (by simpa only [nat.cast_bit0, nat.cast_one, mul_eq_self h, two_mul] using
     mul_le_mul_right' ((nat_lt_omega 2).le.trans h) c)
  (self_le_add_left c c)

/-- If `α` is an infinite type, then the cardinality of `α ⊕ β` is the maximum
of the cardinalities of `α` and `β`. -/
theorem add_eq_max {a b : cardinal} (ha : ω ≤ a) : a + b = max a b :=
le_antisymm
  (add_eq_self (le_trans ha (le_max_left a b)) ▸
    add_le_add (le_max_left _ _) (le_max_right _ _)) $
max_le (self_le_add_right _ _) (self_le_add_left _ _)

theorem add_le_max (a b : cardinal) : a + b ≤ max (max a b) ω :=
begin
  by_cases ha : ω ≤ a,
  { rw [add_eq_max ha],
    exact le_max_left _ _ },
  { by_cases hb : ω ≤ b,
    { rw [add_comm, add_eq_max hb, max_comm],
      exact le_max_left _ _ },
    { exact le_max_of_le_right (le_of_lt (add_lt_omega (lt_of_not_ge ha) (lt_of_not_ge hb))) } }
end

theorem add_lt_of_lt {a b c : cardinal} (hc : ω ≤ c)
  (h1 : a < c) (h2 : b < c) : a + b < c :=
lt_of_le_of_lt (add_le_add (le_max_left a b) (le_max_right a b)) $
(lt_or_le (max a b) ω).elim
  (λ h, lt_of_lt_of_le (add_lt_omega h h) hc)
  (λ h, by rw add_eq_self h; exact max_lt h1 h2)

lemma eq_of_add_eq_of_omega_le {a b c : cardinal} (h : a + b = c) (ha : a < c) (hc : ω ≤ c) :
  b = c :=
begin
  apply le_antisymm,
  { rw [← h], apply self_le_add_left },
  rw[← not_lt], intro hb,
  have : a + b < c := add_lt_of_lt hc ha hb,
  simpa [h, lt_irrefl] using this
end

lemma add_eq_left {a b : cardinal} (ha : ω ≤ a) (hb : b ≤ a) : a + b = a :=
by { rw [add_eq_max ha, max_eq_left hb] }

lemma add_eq_right {a b : cardinal} (hb : ω ≤ b) (ha : a ≤ b) : a + b = b :=
by { rw [add_comm, add_eq_left hb ha] }

lemma add_eq_left_iff {a b : cardinal} : a + b = a ↔ (max ω b ≤ a ∨ b = 0) :=
begin
  rw [max_le_iff], split,
  { intro h, cases (le_or_lt ω a) with ha ha,
    { left, use ha, rw [← not_lt], intro hb, apply ne_of_gt _ h,
      exact lt_of_lt_of_le hb (self_le_add_left b a) },
    right, rw [← h, add_lt_omega_iff, lt_omega, lt_omega] at ha,
    rcases ha with ⟨⟨n, rfl⟩, ⟨m, rfl⟩⟩, norm_cast at h ⊢,
    rw [← add_right_inj, h, add_zero] },
  { rintro (⟨h1, h2⟩|h3), rw [add_eq_max h1, max_eq_left h2], rw [h3, add_zero] }
end

lemma add_eq_right_iff {a b : cardinal} : a + b = b ↔ (max ω a ≤ b ∨ a = 0) :=
by { rw [add_comm, add_eq_left_iff] }

lemma add_one_eq {a : cardinal} (ha : ω ≤ a) : a + 1 = a :=
have 1 ≤ a, from le_trans (le_of_lt one_lt_omega) ha,
add_eq_left ha this

protected lemma eq_of_add_eq_add_left {a b c : cardinal} (h : a + b = a + c) (ha : a < ω) :
  b = c :=
begin
  cases le_or_lt ω b with hb hb,
  { have : a < b := lt_of_lt_of_le ha hb,
    rw [add_eq_right hb (le_of_lt this), eq_comm] at h,
    rw [eq_of_add_eq_of_omega_le h this hb] },
  { have hc : c < ω,
    { rw [← not_le], intro hc,
      apply lt_irrefl ω, apply lt_of_le_of_lt (le_trans hc (self_le_add_left _ a)),
      rw [← h], apply add_lt_omega ha hb },
    rw [lt_omega] at *,
    rcases ha with ⟨n, rfl⟩, rcases hb with ⟨m, rfl⟩, rcases hc with ⟨k, rfl⟩,
    norm_cast at h ⊢, apply add_left_cancel h }
end

protected lemma eq_of_add_eq_add_right {a b c : cardinal} (h : a + b = c + b) (hb : b < ω) :
  a = c :=
by { rw [add_comm a b, add_comm c b] at h, exact cardinal.eq_of_add_eq_add_left h hb }

/-! ### Properties about power -/

theorem pow_le {κ μ : cardinal.{u}} (H1 : ω ≤ κ) (H2 : μ < ω) : κ ^ μ ≤ κ :=
let ⟨n, H3⟩ := lt_omega.1 H2 in
H3.symm ▸ (quotient.induction_on κ (λ α H1, nat.rec_on n
  (le_of_lt $ lt_of_lt_of_le (by rw [nat.cast_zero, power_zero];
    from one_lt_omega) H1)
  (λ n ih, trans_rel_left _
    (by { rw [nat.cast_succ, power_add, power_one];
      exact mul_le_mul_right' ih _ })
    (mul_eq_self H1))) H1)

lemma power_self_eq {c : cardinal} (h : ω ≤ c) : c ^ c = 2 ^ c :=
begin
  apply le_antisymm,
  { apply le_trans (power_le_power_right $ le_of_lt $ cantor c), rw [← power_mul, mul_eq_self h] },
  { convert power_le_power_right (le_trans (le_of_lt $ nat_lt_omega 2) h), apply nat.cast_two.symm }
end

lemma prod_eq_two_power {ι : Type u} [infinite ι] {c : ι → cardinal.{v}} (h₁ : ∀ i, 2 ≤ c i)
  (h₂ : ∀ i, lift.{u} (c i) ≤ lift.{v} (#ι)) :
  prod c = 2 ^ lift.{v} (#ι) :=
begin
  rw [← lift_id' (prod c), lift_prod, ← lift_two_power],
  apply le_antisymm,
  { refine (prod_le_prod _ _ h₂).trans_eq _,
    rw [prod_const, lift_lift, ← lift_power, power_self_eq (omega_le_mk ι), lift_umax.{u v}] },
  { rw [← prod_const', lift_prod],
    refine prod_le_prod _ _ (λ i, _),
    rw [lift_two, ← lift_two.{u v}, lift_le],
    exact h₁ i }
end

lemma power_eq_two_power {c₁ c₂ : cardinal} (h₁ : ω ≤ c₁) (h₂ : 2 ≤ c₂) (h₂' : c₂ ≤ c₁) :
  c₂ ^ c₁ = 2 ^ c₁ :=
le_antisymm (power_self_eq h₁ ▸ power_le_power_right h₂') (power_le_power_right h₂)

lemma nat_power_eq {c : cardinal.{u}} (h : ω ≤ c) {n : ℕ} (hn : 2 ≤ n) :
  (n : cardinal.{u}) ^ c = 2 ^ c :=
power_eq_two_power h (by assumption_mod_cast) ((nat_lt_omega n).le.trans h)

lemma power_nat_le {c : cardinal.{u}} {n : ℕ} (h  : ω ≤ c) : c ^ (n : cardinal.{u}) ≤ c :=
pow_le h (nat_lt_omega n)

lemma power_nat_le_max {c : cardinal.{u}} {n : ℕ} : c ^ (n : cardinal.{u}) ≤ max c ω :=
begin
  by_cases hc : ω ≤ c,
  { exact le_max_of_le_left (power_nat_le hc) },
  { exact le_max_of_le_right (le_of_lt (power_lt_omega (lt_of_not_ge hc) (nat_lt_omega _))) }
end

@[simp] lemma powerlt_omega {c : cardinal} (h : ω ≤ c) : c ^< ω = c :=
begin
  apply le_antisymm,
  { rw [powerlt_le], intro c', rw [lt_omega], rintro ⟨n, rfl⟩, apply power_nat_le h },
  convert le_powerlt one_lt_omega, rw [power_one]
end

lemma powerlt_omega_le (c : cardinal) : c ^< ω ≤ max c ω :=
begin
  cases le_or_lt ω c,
  { rw [powerlt_omega h], apply le_max_left },
  rw [powerlt_le], intros c' hc',
  refine le_trans (le_of_lt $ power_lt_omega h hc') (le_max_right _ _)
end

/-! ### Computing cardinality of various types -/

theorem mk_list_eq_mk (α : Type u) [infinite α] : #(list α) = #α :=
have H1 : ω ≤ #α := omega_le_mk α,
eq.symm $ le_antisymm ⟨⟨λ x, [x], λ x y H, (list.cons.inj H).1⟩⟩ $
calc  #(list α)
    = sum (λ n : ℕ, #α ^ (n : cardinal.{u})) : mk_list_eq_sum_pow α
... ≤ sum (λ n : ℕ, #α) : sum_le_sum _ _ $ λ n, pow_le H1 $ nat_lt_omega n
... = #α : by simp [H1]

theorem mk_finset_eq_mk (α : Type u) [infinite α] : #(finset α) = #α :=
eq.symm $ le_antisymm (mk_le_of_injective (λ x y, finset.singleton_inj.1)) $
calc #(finset α) ≤ #(list α) : mk_le_of_surjective list.to_finset_surjective
... = #α : mk_list_eq_mk α

lemma mk_bounded_set_le_of_infinite (α : Type u) [infinite α] (c : cardinal) :
  #{t : set α // mk t ≤ c} ≤ #α ^ c :=
begin
  refine le_trans _ (by rw [←add_one_eq (omega_le_mk α)]),
  induction c using cardinal.induction_on with β,
  fapply mk_le_of_surjective,
  { intro f, use sum.inl ⁻¹' range f,
    refine le_trans (mk_preimage_of_injective _ _ (λ x y, sum.inl.inj)) _,
    apply mk_range_le },
  rintro ⟨s, ⟨g⟩⟩,
  use λ y, if h : ∃(x : s), g x = y then sum.inl (classical.some h).val else sum.inr ⟨⟩,
  apply subtype.eq, ext,
  split,
  { rintro ⟨y, h⟩, dsimp only at h, by_cases h' : ∃ (z : s), g z = y,
    { rw [dif_pos h'] at h, cases sum.inl.inj h, exact (classical.some h').2 },
    { rw [dif_neg h'] at h, cases h }},
  { intro h, have : ∃(z : s), g z = g ⟨x, h⟩, exact ⟨⟨x, h⟩, rfl⟩,
    use g ⟨x, h⟩, dsimp only, rw [dif_pos this], congr',
    suffices : classical.some this = ⟨x, h⟩, exact congr_arg subtype.val this,
    apply g.2, exact classical.some_spec this }
end

lemma mk_bounded_set_le (α : Type u) (c : cardinal) :
  #{t : set α // #t ≤ c} ≤ max (#α) ω ^ c :=
begin
  transitivity #{t : set (ulift.{u} ℕ ⊕ α) // #t ≤ c},
  { refine ⟨embedding.subtype_map _ _⟩, apply embedding.image,
    use sum.inr, apply sum.inr.inj, intros s hs, exact le_trans mk_image_le hs },
  refine le_trans
    (mk_bounded_set_le_of_infinite (ulift.{u} ℕ ⊕ α) c) _,
  rw [max_comm, ←add_eq_max]; refl
end

lemma mk_bounded_subset_le {α : Type u} (s : set α) (c : cardinal.{u}) :
  #{t : set α // t ⊆ s ∧ #t ≤ c} ≤ max (#s) ω ^ c :=
begin
  refine le_trans _ (mk_bounded_set_le s c),
  refine ⟨embedding.cod_restrict _ _ _⟩,
  use λ t, coe ⁻¹' t.1,
  { rintros ⟨t, ht1, ht2⟩ ⟨t', h1t', h2t'⟩ h, apply subtype.eq, dsimp only at h ⊢,
    refine (preimage_eq_preimage' _ _).1 h; rw [subtype.range_coe]; assumption },
  rintro ⟨t, h1t, h2t⟩, exact le_trans (mk_preimage_of_injective _ _ subtype.val_injective) h2t
end

/-! ### Properties of `compl` -/

lemma mk_compl_of_infinite {α : Type*} [infinite α] (s : set α) (h2 : #s < #α) :
  #(sᶜ : set α) = #α :=
by { refine eq_of_add_eq_of_omega_le _ h2 (omega_le_mk α), exact mk_sum_compl s }

lemma mk_compl_finset_of_infinite {α : Type*} [infinite α] (s : finset α) :
  #((↑s)ᶜ : set α) = #α :=
by { apply mk_compl_of_infinite, exact (finset_card_lt_omega s).trans_le (omega_le_mk α) }

lemma mk_compl_eq_mk_compl_infinite {α : Type*} [infinite α] {s t : set α} (hs : #s < #α)
  (ht : #t < #α) : #(sᶜ : set α) = #(tᶜ : set α) :=
by { rw [mk_compl_of_infinite s hs, mk_compl_of_infinite t ht] }

lemma mk_compl_eq_mk_compl_finite_lift {α : Type u} {β : Type v} [fintype α]
  {s : set α} {t : set β} (h1 : lift.{(max v w)} (#α) = lift.{(max u w)} (#β))
  (h2 : lift.{(max v w)} (#s) = lift.{(max u w)} (#t)) :
  lift.{(max v w)} (#(sᶜ : set α)) = lift.{(max u w)} (#(tᶜ : set β)) :=
begin
  rcases lift_mk_eq.1 h1 with ⟨e⟩, letI : fintype β := fintype.of_equiv α e,
  replace h1 : fintype.card α = fintype.card β := (fintype.of_equiv_card _).symm,
  classical,
  lift s to finset α using finite.of_fintype s,
  lift t to finset β using finite.of_fintype t,
  simp only [finset.coe_sort_coe, mk_finset, lift_nat_cast, nat.cast_inj] at h2,
  simp only [← finset.coe_compl, finset.coe_sort_coe, mk_finset, finset.card_compl,
    lift_nat_cast, nat.cast_inj, h1, h2]
end

lemma mk_compl_eq_mk_compl_finite {α β : Type u} [fintype α] {s : set α} {t : set β}
  (h1 : #α = #β) (h : #s = #t) : #(sᶜ : set α) = #(tᶜ : set β) :=
by { rw ← lift_inj, apply mk_compl_eq_mk_compl_finite_lift; rwa [lift_inj] }

lemma mk_compl_eq_mk_compl_finite_same {α : Type*} [fintype α] {s t : set α}
  (h : #s = #t) : #(sᶜ : set α) = #(tᶜ : set α) :=
mk_compl_eq_mk_compl_finite rfl h

/-! ### Extending an injection to an equiv -/

theorem extend_function {α β : Type*} {s : set α} (f : s ↪ β)
  (h : nonempty ((sᶜ : set α) ≃ ((range f)ᶜ : set β))) :
  ∃ (g : α ≃ β), ∀ x : s, g x = f x :=
begin
  intros, have := h, cases this with g,
  let h : α ≃ β := (set.sum_compl (s : set α)).symm.trans
    ((sum_congr (equiv.of_injective f f.2) g).trans
    (set.sum_compl (range f))),
  refine ⟨h, _⟩, rintro ⟨x, hx⟩, simp [set.sum_compl_symm_apply_of_mem, hx]
end

theorem extend_function_finite {α β : Type*} [fintype α] {s : set α} (f : s ↪ β)
  (h : nonempty (α ≃ β)) : ∃ (g : α ≃ β), ∀ x : s, g x = f x :=
begin
  apply extend_function f,
  cases id h with g,
  rw [← lift_mk_eq] at h,
  rw [←lift_mk_eq, mk_compl_eq_mk_compl_finite_lift h],
  rw [mk_range_eq_lift], exact f.2
end

theorem extend_function_of_lt {α β : Type*} {s : set α} (f : s ↪ β) (hs : #s < #α)
  (h : nonempty (α ≃ β)) : ∃ (g : α ≃ β), ∀ x : s, g x = f x :=
begin
  casesI fintype_or_infinite α,
  { exact extend_function_finite f h },
  { apply extend_function f, cases id h with g, haveI := infinite.of_injective _ g.injective,
    rw [← lift_mk_eq'] at h ⊢,
    rwa [mk_compl_of_infinite s hs, mk_compl_of_infinite],
    rwa [← lift_lt, mk_range_eq_of_injective f.injective, ← h, lift_lt] },
end

section bit
/-!
This section proves inequalities for `bit0` and `bit1`, enabling `simp` to solve inequalities
for numeral cardinals. The complexity of the resulting algorithm is not good, as in some cases
`simp` reduces an inequality to a disjunction of two situations, depending on whether a cardinal
is finite or infinite. Since the evaluation of the branches is not lazy, this is bad. It is good
enough for practical situations, though.

For specific numbers, these inequalities could also be deduced from the corresponding
inequalities of natural numbers using `norm_cast`:
```
example : (37 : cardinal) < 42 :=
by { norm_cast, norm_num }
```
-/

lemma bit0_ne_zero (a : cardinal) : ¬bit0 a = 0 ↔ ¬a = 0 :=
by simp [bit0]

@[simp] lemma bit1_ne_zero (a : cardinal) : ¬bit1 a = 0 :=
by simp [bit1]

@[simp] lemma zero_lt_bit0 (a : cardinal) : 0 < bit0 a ↔ 0 < a :=
by { rw ←not_iff_not, simp [bit0], }

@[simp] lemma zero_lt_bit1 (a : cardinal) : 0 < bit1 a :=
lt_of_lt_of_le zero_lt_one (self_le_add_left _ _)

@[simp] lemma one_le_bit0 (a : cardinal) : 1 ≤ bit0 a ↔ 0 < a :=
⟨λ h, (zero_lt_bit0 a).mp (lt_of_lt_of_le zero_lt_one h),
 λ h, le_trans (one_le_iff_pos.mpr h) (self_le_add_left a a)⟩

@[simp] lemma one_le_bit1 (a : cardinal) : 1 ≤ bit1 a :=
self_le_add_left _ _

theorem bit0_eq_self {c : cardinal} (h : ω ≤ c) : bit0 c = c :=
add_eq_self h

@[simp] theorem bit0_lt_omega {c : cardinal} : bit0 c < ω ↔ c < ω :=
by simp [bit0, add_lt_omega_iff]

@[simp] theorem omega_le_bit0 {c : cardinal} : ω ≤ bit0 c ↔ ω ≤ c :=
by { rw ← not_iff_not, simp }

@[simp] theorem bit1_eq_self_iff {c : cardinal} : bit1 c = c ↔ ω ≤ c :=
begin
  by_cases h : ω ≤ c,
  { simp only [bit1, bit0_eq_self h, h, eq_self_iff_true, add_one_of_omega_le] },
  { refine iff_of_false (ne_of_gt _) h,
    rcases lt_omega.1 (not_le.1 h) with ⟨n, rfl⟩,
    norm_cast,
    dsimp [bit1, bit0],
    linarith }
end

@[simp] theorem bit1_lt_omega {c : cardinal} : bit1 c < ω ↔ c < ω :=
by simp [bit1, bit0, add_lt_omega_iff, one_lt_omega]

@[simp] theorem omega_le_bit1 {c : cardinal} : ω ≤ bit1 c ↔ ω ≤ c :=
by { rw ← not_iff_not, simp }

@[simp] lemma bit0_le_bit0 {a b : cardinal} : bit0 a ≤ bit0 b ↔ a ≤ b :=
begin
  cases le_or_lt ω a with ha ha; cases le_or_lt ω b with hb hb,
  { rw [bit0_eq_self ha, bit0_eq_self hb] },
  { rw bit0_eq_self ha,
    refine iff_of_false (λ h, _) (not_le_of_lt (hb.trans_le ha)),
    have A : bit0 b < ω, by simpa using hb,
    exact lt_irrefl _ (lt_of_lt_of_le (lt_of_lt_of_le A ha) h) },
  { rw [bit0_eq_self hb],
    exact iff_of_true ((bit0_lt_omega.2 ha).le.trans hb) (ha.le.trans hb) },
  { rcases lt_omega.1 ha with ⟨m, rfl⟩,
    rcases lt_omega.1 hb with ⟨n, rfl⟩,
    norm_cast,
    exact bit0_le_bit0 }
end

@[simp] lemma bit0_le_bit1 {a b : cardinal} : bit0 a ≤ bit1 b ↔ a ≤ b :=
begin
  cases le_or_lt ω a with ha ha; cases le_or_lt ω b with hb hb,
  { rw [bit0_eq_self ha, bit1_eq_self_iff.2 hb], },
  { rw bit0_eq_self ha,
    refine iff_of_false (λ h, _) (not_le_of_lt (hb.trans_le ha)),
    have A : bit1 b < ω, by simpa using hb,
    exact lt_irrefl _ (lt_of_lt_of_le (lt_of_lt_of_le A ha) h) },
  { rw [bit1_eq_self_iff.2 hb],
    exact iff_of_true ((bit0_lt_omega.2 ha).le.trans hb) (ha.le.trans hb) },
  { rcases lt_omega.1 ha with ⟨m, rfl⟩,
    rcases lt_omega.1 hb with ⟨n, rfl⟩,
    norm_cast,
    exact nat.bit0_le_bit1_iff }
end

@[simp] lemma bit1_le_bit1 {a b : cardinal} : bit1 a ≤ bit1 b ↔ a ≤ b :=
begin
  split,
  { assume h,
    apply bit0_le_bit1.1 (le_trans (self_le_add_right (bit0 a) 1) h) },
  { assume h,
    calc a + a + 1 ≤ a + b + 1 : add_le_add_right (add_le_add_left h a) 1
           ... ≤ b + b + 1 : add_le_add_right (add_le_add_right h b) 1 }
end

@[simp] lemma bit1_le_bit0 {a b : cardinal} : bit1 a ≤ bit0 b ↔ (a < b ∨ (a ≤ b ∧ ω ≤ a)) :=
begin
  cases le_or_lt ω a with ha ha; cases le_or_lt ω b with hb hb,
  { simp only [bit1_eq_self_iff.mpr ha, bit0_eq_self hb, ha, and_true],
    refine ⟨λ h, or.inr h, λ h, _⟩,
    cases h,
    { exact le_of_lt h },
    { exact h } },
  { rw bit1_eq_self_iff.2 ha,
    refine iff_of_false (λ h, _) (λ h, _),
    { have A : bit0 b < ω, by simpa using hb,
      exact lt_irrefl _ (lt_of_lt_of_le (lt_of_lt_of_le A ha) h) },
    { exact not_le_of_lt (hb.trans_le ha) (h.elim le_of_lt and.left) } },
  { rw [bit0_eq_self hb],
    exact iff_of_true ((bit1_lt_omega.2 ha).le.trans hb) (or.inl $ ha.trans_le hb) },
  { rcases lt_omega.1 ha with ⟨m, rfl⟩,
    rcases lt_omega.1 hb with ⟨n, rfl⟩,
    norm_cast,
    simp [not_le.mpr ha], }
end

@[simp] lemma bit0_lt_bit0 {a b : cardinal} : bit0 a < bit0 b ↔ a < b :=
begin
  cases le_or_lt ω a with ha ha; cases le_or_lt ω b with hb hb,
  { rw [bit0_eq_self ha, bit0_eq_self hb] },
  { rw bit0_eq_self ha,
    refine iff_of_false (λ h, _) (not_lt_of_le (hb.le.trans ha)),
    have A : bit0 b < ω, by simpa using hb,
    exact lt_irrefl _ (lt_trans (lt_of_lt_of_le A ha) h) },
  { rw [bit0_eq_self hb],
    exact iff_of_true ((bit0_lt_omega.2 ha).trans_le hb) (ha.trans_le hb) },
  { rcases lt_omega.1 ha with ⟨m, rfl⟩,
    rcases lt_omega.1 hb with ⟨n, rfl⟩,
    norm_cast,
    exact bit0_lt_bit0 }
end

@[simp] lemma bit1_lt_bit0 {a b : cardinal} : bit1 a < bit0 b ↔ a < b :=
begin
  cases le_or_lt ω a with ha ha; cases le_or_lt ω b with hb hb,
  { rw [bit1_eq_self_iff.2 ha, bit0_eq_self hb], },
  { rw bit1_eq_self_iff.2 ha,
    refine iff_of_false (λ h, _) (not_lt_of_le (hb.le.trans ha)),
    have A : bit0 b < ω, by simpa using hb,
    exact lt_irrefl _ (lt_trans (lt_of_lt_of_le A ha) h) },
  { rw [bit0_eq_self hb],
    exact iff_of_true ((bit1_lt_omega.2 ha).trans_le hb) (ha.trans_le hb) },
  { rcases lt_omega.1 ha with ⟨m, rfl⟩,
    rcases lt_omega.1 hb with ⟨n, rfl⟩,
    norm_cast,
    exact nat.bit1_lt_bit0_iff }
end

@[simp] lemma bit1_lt_bit1 {a b : cardinal} : bit1 a < bit1 b ↔ a < b :=
begin
  cases le_or_lt ω a with ha ha; cases le_or_lt ω b with hb hb,
  { rw [bit1_eq_self_iff.2 ha, bit1_eq_self_iff.2 hb], },
  { rw bit1_eq_self_iff.2 ha,
    refine iff_of_false (λ h, _) (not_lt_of_le (hb.le.trans ha)),
    have A : bit1 b < ω, by simpa using hb,
    exact lt_irrefl _ (lt_trans (lt_of_lt_of_le A ha) h) },
  { rw [bit1_eq_self_iff.2 hb],
    exact iff_of_true ((bit1_lt_omega.2 ha).trans_le hb) (ha.trans_le hb) },
  { rcases lt_omega.1 ha with ⟨m, rfl⟩,
    rcases lt_omega.1 hb with ⟨n, rfl⟩,
    norm_cast,
    exact bit1_lt_bit1 }
end

@[simp] lemma bit0_lt_bit1 {a b : cardinal} : bit0 a < bit1 b ↔ (a < b ∨ (a ≤ b ∧ a < ω)) :=
begin
  cases le_or_lt ω a with ha ha; cases le_or_lt ω b with hb hb,
  { simp [bit0_eq_self ha, bit1_eq_self_iff.2 hb, not_lt.mpr ha] },
  { rw bit0_eq_self ha,
    refine iff_of_false (λ h, _) (λ h, _),
    { have A : bit1 b < ω, by simpa using hb,
      exact lt_irrefl _ (lt_trans (lt_of_lt_of_le A ha) h) },
    { exact not_le_of_lt (hb.trans_le ha) (h.elim le_of_lt and.left) } },
  { rw [bit1_eq_self_iff.2 hb],
    exact iff_of_true ((bit0_lt_omega.2 ha).trans_le hb) (or.inl $ ha.trans_le hb) },
  { rcases lt_omega.1 ha with ⟨m, rfl⟩,
    rcases lt_omega.1 hb with ⟨n, rfl⟩,
    norm_cast,
    simp only [ha, and_true, nat.bit0_lt_bit1_iff, or_iff_right_of_imp le_of_lt] }
end

lemma one_lt_two : (1 : cardinal) < 2 :=
-- This strategy works generally to prove inequalities between numerals in `cardinality`.
by { norm_cast, norm_num }

@[simp] lemma one_lt_bit0 {a : cardinal} : 1 < bit0 a ↔ 0 < a :=
by simp [← bit1_zero]

@[simp] lemma one_lt_bit1 (a : cardinal) : 1 < bit1 a ↔ 0 < a :=
by simp [← bit1_zero]

@[simp] lemma one_le_one : (1 : cardinal) ≤ 1 :=
le_refl _

end bit

end cardinal

lemma not_injective_of_ordinal {α : Type u} (f : ordinal.{u} → α) :
  ¬ function.injective f :=
begin
  let g : ordinal.{u} → ulift.{u+1} α := λ o, ulift.up (f o),
  suffices : ¬ function.injective g,
  { intro hf, exact this (equiv.ulift.symm.injective.comp hf) },
  intro hg,
  replace hg := cardinal.mk_le_of_injective hg,
  rw cardinal.mk_ulift at hg,
  have := hg.trans_lt (cardinal.lift_lt_univ _),
  rw cardinal.univ_id at this,
  exact lt_irrefl _ this
end

lemma not_injective_of_ordinal_of_small {α : Type v} [small.{u} α] (f : ordinal.{u} → α) :
  ¬ function.injective f :=
begin
  intro hf,
  apply not_injective_of_ordinal (equiv_shrink α ∘ f),
  exact (equiv_shrink _).injective.comp hf,
end


/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import topology.continuous_function.weierstrass

/-!
# The Stone-Weierstrass theorem

If a subalgebra `A` of `C(X, ℝ)`, where `X` is a compact topological space,
separates points, then it is dense.

We argue as follows.

* In any subalgebra `A` of `C(X, ℝ)`, if `f ∈ A`, then `abs f ∈ A.topological_closure`.
  This follows from the Weierstrass approximation theorem on `[-∥f∥, ∥f∥]` by
  approximating `abs` uniformly thereon by polynomials.
* This ensures that `A.topological_closure` is actually a sublattice:
  if it contains `f` and `g`, then it contains the pointwise supremum `f ⊔ g`
  and the pointwise infimum `f ⊓ g`.
* Any nonempty sublattice `L` of `C(X, ℝ)` which separates points is dense,
  by a nice argument approximating a given `f` above and below using separating functions.
  For each `x y : X`, we pick a function `g x y ∈ L` so `g x y x = f x` and `g x y y = f y`.
  By continuity these functions remain close to `f` on small patches around `x` and `y`.
  We use compactness to identify a certain finitely indexed infimum of finitely indexed supremums
  which is then close to `f` everywhere, obtaining the desired approximation.
* Finally we put these pieces together. `L = A.topological_closure` is a nonempty sublattice
  which separates points since `A` does, and so is dense (in fact equal to `⊤`).

## Future work

Prove the complex version for self-adjoint subalgebras `A`, by separately approximating
the real and imaginary parts using the real subalgebra of real-valued functions in `A`
(which still separates points, by taking the norm-square of a separating function).

Extend to cover the case of subalgebras of the continuous functions vanishing at infinity,
on non-compact spaces.

-/

noncomputable theory

namespace continuous_map

variables {X : Type*} [topological_space X] [compact_space X]

/--
Turn a function `f : C(X, ℝ)` into a continuous map into `set.Icc (-∥f∥) (∥f∥)`,
thereby explicitly attaching bounds.
-/
def attach_bound (f : C(X, ℝ)) : C(X, set.Icc (-∥f∥) (∥f∥)) :=
{ to_fun := λ x, ⟨f x, ⟨neg_norm_le_apply f x, apply_le_norm f x⟩⟩ }

@[simp] lemma attach_bound_apply_coe (f : C(X, ℝ)) (x : X) : ((attach_bound f) x : ℝ) = f x := rfl

lemma polynomial_comp_attach_bound (A : subalgebra ℝ C(X, ℝ)) (f : A) (g : polynomial ℝ) :
  (g.to_continuous_map_on (set.Icc (-∥f∥) ∥f∥)).comp (f : C(X, ℝ)).attach_bound =
    polynomial.aeval f g :=
begin
  ext,
  simp only [continuous_map.comp_coe, function.comp_app,
    continuous_map.attach_bound_apply_coe,
    polynomial.to_continuous_map_on_to_fun,
    polynomial.aeval_subalgebra_coe,
    polynomial.aeval_continuous_map_apply,
    polynomial.to_continuous_map_to_fun],
end

/--
Given a continuous function `f` in a subalgebra of `C(X, ℝ)`, postcomposing by a polynomial
gives another function in `A`.

This lemma proves something slightly more subtle than this:
we take `f`, and think of it as a function into the restricted target `set.Icc (-∥f∥) ∥f∥)`,
and then postcompose with a polynomial function on that interval.
This is in fact the same situation as above, and so also gives a function in `A`.
-/
lemma polynomial_comp_attach_bound_mem (A : subalgebra ℝ C(X, ℝ)) (f : A) (g : polynomial ℝ) :
  (g.to_continuous_map_on (set.Icc (-∥f∥) ∥f∥)).comp (f : C(X, ℝ)).attach_bound ∈ A :=
begin
  rw polynomial_comp_attach_bound,
  apply set_like.coe_mem,
end

theorem comp_attach_bound_mem_closure
  (A : subalgebra ℝ C(X, ℝ)) (f : A) (p : C(set.Icc (-∥f∥) (∥f∥), ℝ)) :
  p.comp (attach_bound f) ∈ A.topological_closure :=
begin
  -- `p` itself is in the closure of polynomials, by the Weierstrass theorem,
  have mem_closure : p ∈ (polynomial_functions (set.Icc (-∥f∥) (∥f∥))).topological_closure :=
    continuous_map_mem_polynomial_functions_closure _ _ p,
  -- and so there are polynomials arbitrarily close.
  have frequently_mem_polynomials := mem_closure_iff_frequently.mp mem_closure,
  -- To prove `p.comp (attached_bound f)` is in the closure of `A`,
  -- we show there are elements of `A` arbitrarily close.
  apply mem_closure_iff_frequently.mpr,
  -- To show that, we pull back the polynomials close to `p`,
  refine ((comp_right_continuous_map ℝ (attach_bound (f : C(X, ℝ)))).continuous_at p).tendsto
    .frequently_map _ _ frequently_mem_polynomials,
  -- but need to show that those pullbacks are actually in `A`.
  rintros _ ⟨g, ⟨-,rfl⟩⟩,
  simp only [set_like.mem_coe, alg_hom.coe_to_ring_hom, comp_right_continuous_map_apply,
    polynomial.to_continuous_map_on_alg_hom_apply],
  apply polynomial_comp_attach_bound_mem,
end

theorem abs_mem_subalgebra_closure (A : subalgebra ℝ C(X, ℝ)) (f : A) :
  (f : C(X, ℝ)).abs ∈ A.topological_closure :=
begin
  let M := ∥f∥,
  let f' := attach_bound (f : C(X, ℝ)),
  let abs : C(set.Icc (-∥f∥) (∥f∥), ℝ) :=
  { to_fun := λ x : set.Icc (-∥f∥) (∥f∥), _root_.abs (x : ℝ) },
  change (abs.comp f') ∈ A.topological_closure,
  apply comp_attach_bound_mem_closure,
end

theorem inf_mem_subalgebra_closure (A : subalgebra ℝ C(X, ℝ)) (f g : A) :
  (f : C(X, ℝ)) ⊓ (g : C(X, ℝ)) ∈ A.topological_closure :=
begin
  rw inf_eq,
  refine A.topological_closure.smul_mem
    (A.topological_closure.sub_mem
      (A.topological_closure.add_mem (A.subalgebra_topological_closure f.property)
          (A.subalgebra_topological_closure g.property)) _) _,
  exact_mod_cast abs_mem_subalgebra_closure A _,
end

theorem inf_mem_closed_subalgebra (A : subalgebra ℝ C(X, ℝ)) (h : is_closed (A : set C(X, ℝ)))
  (f g : A) : (f : C(X, ℝ)) ⊓ (g : C(X, ℝ)) ∈ A :=
begin
  convert inf_mem_subalgebra_closure A f g,
  apply set_like.ext',
  symmetry,
  erw closure_eq_iff_is_closed,
  exact h,
end

theorem sup_mem_subalgebra_closure (A : subalgebra ℝ C(X, ℝ)) (f g : A) :
  (f : C(X, ℝ)) ⊔ (g : C(X, ℝ)) ∈ A.topological_closure :=
begin
  rw sup_eq,
  refine A.topological_closure.smul_mem
    (A.topological_closure.add_mem
      (A.topological_closure.add_mem (A.subalgebra_topological_closure f.property)
          (A.subalgebra_topological_closure g.property)) _) _,
  exact_mod_cast abs_mem_subalgebra_closure A _,
end

theorem sup_mem_closed_subalgebra (A : subalgebra ℝ C(X, ℝ)) (h : is_closed (A : set C(X, ℝ)))
  (f g : A) : (f : C(X, ℝ)) ⊔ (g : C(X, ℝ)) ∈ A :=
begin
  convert sup_mem_subalgebra_closure A f g,
  apply set_like.ext',
  symmetry,
  erw closure_eq_iff_is_closed,
  exact h,
end

open_locale topological_space

-- Here's the fun part of Stone-Weierstrass!
theorem sublattice_closure_eq_top
  (L : set C(X, ℝ)) (nA : L.nonempty)
  (inf_mem : ∀ f g ∈ L, f ⊓ g ∈ L) (sup_mem : ∀ f g ∈ L, f ⊔ g ∈ L)
  (sep : L.separates_points_strongly) :
  closure L = ⊤ :=
begin
  -- We start by boiling down to a statement about close approximation.
  apply eq_top_iff.mpr,
  rintros f -,
  refine filter.frequently.mem_closure
    ((filter.has_basis.frequently_iff metric.nhds_basis_ball).mpr (λ ε pos, _)),
  simp only [exists_prop, metric.mem_ball],

  -- It will be helpful to assume `X` is nonempty later,
  -- so we get that out of the way here.
  by_cases nX : nonempty X,
  swap,
  exact ⟨nA.some, (dist_lt_iff _ _ pos).mpr (λ x, false.elim (nX ⟨x⟩)), nA.some_spec⟩,

  /-
  The strategy now is to pick a family of continuous functions `g x y` in `A`
  with the property that `g x y x = f x` and `g x y y = f y`
  (this is immediate from `h : separates_points_strongly`)
  then use continuity to see that `g x y` is close to `f` near both `x` and `y`,
  and finally using compactness to produce the desired function `h`
  as a maximum over finitely many `x` of a minimum over finitely many `y` of the `g x y`.
  -/
  dsimp [set.separates_points_strongly] at sep,

  let g : X → X → L := λ x y, (sep f x y).some,
  have w₁ : ∀ x y, g x y x = f x := λ x y, (sep f x y).some_spec.1,
  have w₂ : ∀ x y, g x y y = f y := λ x y, (sep f x y).some_spec.2,

  -- For each `x y`, we define `U x y` to be `{z | f z - ε < g x y z}`,
  -- and observe this is a neighbourhood of `y`.
  let U : X → X → set X := λ x y, {z | f z - ε < g x y z},
  have U_nhd_y : ∀ x y, U x y ∈ 𝓝 y,
  { intros x y,
    refine mem_nhds_sets _ _,
    { apply is_open_lt; continuity, },
    { rw [set.mem_set_of_eq, w₂],
      exact sub_lt_self _ pos, }, },

  -- Fixing `x` for a moment, we have a family of functions `λ y, g x y`
  -- which on different patches (the `U x y`) are greater than `f z - ε`.
  -- Taking the supremum of these functions
  -- indexed by a finite collection of patches which cover `X`
  -- will give us an element of `A` that is globally greater than `f z - ε`
  -- and still equal to `f x` at `x`.

  -- Since `X` is compact, for every `x` there is some finset `ys t`
  -- so the union of the `U x y` for `y ∈ ys x` still covers everything.
  let ys : Π x, finset X := λ x, (compact_space.elim_nhds_subcover (U x) (U_nhd_y x)).some,
  let ys_w : ∀ x, (⋃ y ∈ ys x, U x y) = ⊤ :=
    λ x, (compact_space.elim_nhds_subcover (U x) (U_nhd_y x)).some_spec,

  have ys_nonempty : ∀ x, (ys x).nonempty :=
    λ x, set.nonempty_of_union_eq_top_of_nonempty _ _ nX (ys_w x),

  -- Thus for each `x` we have the desired `h x : A` so `f z - ε < h x z` everywhere
  -- and `h x x = f x`.
  let h : Π x, L := λ x,
    ⟨(ys x).sup' (ys_nonempty x) (λ y, (g x y : C(X, ℝ))),
      finset.sup'_mem _ sup_mem _ _ _ (λ y _, (g x y).2)⟩,
  have lt_h : ∀ x z, f z - ε < h x z,
  { intros x z,
    obtain ⟨y, ym, zm⟩ := set.exists_set_mem_of_union_eq_top _ _ (ys_w x) z,
    dsimp [h],
    simp only [finset.lt_sup'_iff, continuous_map.sup'_apply],
    exact ⟨y, ym, zm⟩, },
  have h_eq : ∀ x, h x x = f x, by { intro x, simp only [coe_fn_coe_base] at w₁, simp [w₁], },

  -- For each `x`, we define `W x` to be `{z | h x z < f z + ε}`,
  let W : Π x, set X := λ x, {z | h x z < f z + ε},
  -- This is still a neighbourhood of `x`.
  have W_nhd : ∀ x, W x ∈ 𝓝 x,
  { intros x,
    refine mem_nhds_sets _ _,
    { apply is_open_lt; continuity, },
    { dsimp only [W, set.mem_set_of_eq],
      rw h_eq,
      exact lt_add_of_pos_right _ pos}, },

  -- Since `X` is compact, there is some finset `ys t`
  -- so the union of the `W x` for `x ∈ xs` still covers everything.
  let xs : finset X := (compact_space.elim_nhds_subcover W W_nhd).some,
  let xs_w : (⋃ x ∈ xs, W x) = ⊤ :=
    (compact_space.elim_nhds_subcover W W_nhd).some_spec,
  have xs_nonempty : xs.nonempty := set.nonempty_of_union_eq_top_of_nonempty _ _ nX xs_w,

  -- Finally our candidate function is the infimum over `x ∈ xs` of the `h x`.
  -- This function is then globally less than `f z + ε`.
  let k : (L : Type*) :=
    ⟨xs.inf' xs_nonempty (λ x, (h x : C(X, ℝ))),
      finset.inf'_mem _ inf_mem _ _ _ (λ x _, (h x).2)⟩,

  refine ⟨k.1, _, k.2⟩,

  -- We just need to verify the bound, which we do pointwise.
  rw dist_lt_iff _ _ pos,
  intro z,

  -- We rewrite into this particular form,
  -- so that simp lemmas about inequalities involving `finset.inf'` can fire.
  rw [(show ∀ a b ε : ℝ, dist a b < ε ↔ a < b + ε ∧ b - ε < a,
    by { intros, simp only [← metric.mem_ball, real.ball_eq_Ioo, set.mem_Ioo, and_comm], })],

  fsplit,
  { dsimp [k],
    simp only [finset.inf'_lt_iff, continuous_map.inf'_apply],
    exact set.exists_set_mem_of_union_eq_top _ _ xs_w z, },
  { dsimp [k],
    simp only [finset.lt_inf'_iff, continuous_map.inf'_apply],
    intros x xm,
    apply lt_h, },
end

/--
The Stone-Weierstrass approximation theorem,
that a subalgebra `A` of `C(X, ℝ)`, where `X` is a compact topological space,
is dense if it separates points.
-/
theorem subalgebra_topological_closure_eq_top_of_separates_points
  (A : subalgebra ℝ C(X, ℝ)) (w : A.separates_points) :
  A.topological_closure = ⊤ :=
begin
  -- The closure of `A` is closed under taking `sup` and `inf`,
  -- and separates points strongly (since `A` does),
  -- so we can apply `sublattice_closure_eq_top`.
  apply set_like.ext',
  let L := A.topological_closure,
  have n : set.nonempty (L : set C(X, ℝ)) :=
    ⟨(1 : C(X, ℝ)), A.subalgebra_topological_closure A.one_mem⟩,
  convert sublattice_closure_eq_top
    (L : set C(X, ℝ)) n
    (λ f g fm gm, inf_mem_closed_subalgebra L A.is_closed_topological_closure ⟨f, fm⟩ ⟨g, gm⟩)
    (λ f g fm gm, sup_mem_closed_subalgebra L A.is_closed_topological_closure ⟨f, fm⟩ ⟨g, gm⟩)
    (subalgebra.separates_points.strongly
      (subalgebra.separates_points_monotone (A.subalgebra_topological_closure) w)),
  { simp, },
  { ext, simp, },
end

/--
An alternative statement of the Stone-Weierstrass theorem.

If `A` is a subalgebra of `C(X, ℝ)` which separates points (and `X` is compact),
every real-valued continuous function on `X` is a uniform limit of elements of `A`.
-/
theorem continuous_map_mem_subalgebra_closure_of_separates_points
  (A : subalgebra ℝ C(X, ℝ)) (w : A.separates_points)
  (f : C(X, ℝ)) :
  f ∈ A.topological_closure :=
begin
  rw subalgebra_topological_closure_eq_top_of_separates_points A w,
  simp,
end

/--
An alternative statement of the Stone-Weierstrass theorem,
for those who like their epsilons.

If `A` is a subalgebra of `C(X, ℝ)` which separates points (and `X` is compact),
every real-valued continuous function on `X` is within any `ε > 0` of some element of `A`.
-/
theorem exists_mem_subalgebra_near_continuous_map_of_separates_points
  (A : subalgebra ℝ C(X, ℝ)) (w : A.separates_points)
  (f : C(X, ℝ)) (ε : ℝ) (pos : 0 < ε) :
  ∃ (g : A), ∥(g : C(X, ℝ)) - f∥ < ε :=
begin
  have w := mem_closure_iff_frequently.mp
    (continuous_map_mem_subalgebra_closure_of_separates_points A w f),
  rw metric.nhds_basis_ball.frequently_iff at w,
  obtain ⟨g, H, m⟩ := w ε pos,
  rw [metric.mem_ball, dist_eq_norm] at H,
  exact ⟨⟨g, m⟩, H⟩,
end

/--
An alternative statement of the Stone-Weierstrass theorem,
for those who like their epsilons and don't like bundled continuous functions.

If `A` is a subalgebra of `C(X, ℝ)` which separates points (and `X` is compact),
every real-valued continuous function on `X` is within any `ε > 0` of some element of `A`.
-/
theorem exists_mem_subalgebra_near_continuous_of_separates_points
  (A : subalgebra ℝ C(X, ℝ)) (w : A.separates_points)
  (f : X → ℝ) (c : continuous f) (ε : ℝ) (pos : 0 < ε) :
  ∃ (g : A), ∀ x, ∥g x - f x∥ < ε :=
begin
  obtain ⟨g, b⟩ := exists_mem_subalgebra_near_continuous_map_of_separates_points A w ⟨f, c⟩ ε pos,
  use g,
  rwa norm_lt_iff _ pos at b,
end

end continuous_map


-- ---------------------------------------------------------------------
-- Ejercicio. Inportar las teoría de los números reales, filtros y
-- topología.
-- ---------------------------------------------------------------------

import data.real.basic
import order.filter.at_top_bot
import topology.instances.real

-- ---------------------------------------------------------------------
-- Ejercicio. Definir localmente |x| como el valor absoluto de x.
-- ---------------------------------------------------------------------

local notation `|` x `|` := abs x

-- ---------------------------------------------------------------------
-- Ejercicio. Definir la función
--    is_limit : (ℕ → ℝ) → ℝ → Prop
-- tal que (is_limit a l) expresa que l es el límite de la sucesión a.
-- ---------------------------------------------------------------------

definition is_limit : (ℕ → ℝ) → ℝ → Prop :=
λ a l, ∀ ε > 0, ∃ N, ∀ n ≥ N, | a n - l | < ε

-- Nota: Anteriormente vimos pruebas de propiedades de límites. A
-- continuación las volveremos a demostrar usando propiedades de filtros
-- utilizando hechos de mathlib sobre filtros.

-- ---------------------------------------------------------------------
-- Ejercicio. Abrir el espacio de nombre de filtros.
-- ---------------------------------------------------------------------

open filter

-- ---------------------------------------------------------------------
-- Ejercicio. Abrir localmente el espacio de nombre de espacios topógicos.
-- ---------------------------------------------------------------------

open_locale topological_space

-- ---------------------------------------------------------------------
-- Ejercicio. Abrir el espacio de nombre de espacios métricos.
-- ---------------------------------------------------------------------

open metric

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    is_limit a l ↔ tendsto a at_top (𝓝 l)
-- ---------------------------------------------------------------------

theorem is_limit_iff_tendsto
  (a : ℕ → ℝ)
  (l : ℝ)
  : is_limit a l ↔ tendsto a at_top (𝓝 l) :=
begin
  rw metric.tendsto_at_top,
  refl,
end

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que el límite de un producto es el producto de
-- los límites.
-- ---------------------------------------------------------------------

theorem is_limit_mul
  (a b : ℕ → ℝ)
  (l m : ℝ)
  (ha : is_limit a l)
  (hb : is_limit b m)
  : is_limit (a * b) (l * m) :=
begin
  rw is_limit_iff_tendsto at *,
  exact tendsto.mul ha hb,
end

-- ¡Esta es mucho menos trabajosoa que la que vimos en la semana 3!
-- Entonces, ¿en qué dónde está el trabajo?
--
-- Las siguientes 120 líneas de este archivo discuten la primera prueba,
-- a saber `es_limit_iff_tendsto`. Está claro que los ingredientes clave
-- son `metric.tendsto_at_top`. No hay ejercicios aquí, sólo voy a
-- explicar lo que está pasando, y hablar de las definiciones (por
-- ejemplo, `es_limit`) y su coste.
--
-- La segunda prueba utiliza `is_limit_iff_tendsto` para reducir
-- `is_limit_mul` a un teorema sobre filtros, y lo demuestra con
-- `tendsto.mul`. Nosotros probaremos nuestra propia versión de
-- `tendsto.mul` en este archivo. Así que si quieres seguir con la
-- demostración puedes pasar directamente a la sección `tendsto.mul`
-- en la línea 177 más o menos.

-- =====================================================================
-- § Definiciones en Lean                                             --
-- =====================================================================

-- Cada *definición* que haces en Lean tiene un coste. Por ejemplo mira
-- la definición de Lean de `finset`, el tipo de conjuntos finitos.  Haz
-- clic con el botón derecho del ratón en `finset` y haz clic en "ir a
-- la definición". Ves una definición, y luego más de 2000 líneas de
-- teoremas sobre esta definición. No olvides cerrar el archivo después.

-- #check finset

-- Los teoremas son necesarios porque no sirve sólo definir algún
-- concepto de conjunto finito, hay que hacerlo intuitivo para el
-- usuario final, por lo que hay que demostrar que un subconjunto de un
-- conjunto finito es finito, la unión de dos conjuntos finitos es
-- finita, la imagen de un conjunto finito bajo una aplicación mapa es
-- finita, el producto de dos conjuntos finitos es finito, un producto
-- finito de conjuntos finitos indexados por un conjunto finito es
-- finito, etc. Cada uno de esos lemas de ese archivo es completamente
-- obvio para un matemático, pero necesita ser demostrado en Lean para
-- que los matemáticos puedan usar los conjuntos de la forma en que
-- intuitivamente quieren hacerlo. Mira si puedes entender algunas de
-- las afirmaciones demostradas sobre conjuntos finitos en ese
-- archivo. Ten mucho cuidado de no editarlo. Si accidentalmente lo
-- cambias, simplemente cierra el archivo archivo sin guardarlo, o usa
-- ctrl-Z para deshacer los cambios.
--
-- Cuando desarrollamos la teoría de los límites de las secuencias en la
-- semana 3, escribimos la definición `is_limit`. Esta definición tiene
-- un coste; para que sea útil para el usuario final, tenemos que
-- demostrar una tonelada de teoremas sobre `is_limit`. Esto es lo que
-- sucede en una clase de análisis de licenciatura: ves la definición y
-- luego haces lo que los informáticos llaman la "API" o la "interfaz"
-- (un montón de lemas y teoremas sobre "is_limit"); por ejemplo,
-- "is_limit_add", que dice que `aₙ → l` y `bₙ → m` implica `a_n + b_n →
-- l + m`, y también `is_limit_neg`, `es_limit_sub`, `es_limit_mul` y
-- así sucesivamente.
--

-- Pero resulta que `is_limit` es sólo un caso muy especial de
-- `tendsto`, y como `tendsto` ya está en mathlib, ya hay una gran API
-- para `tendsto` que se ha desarrollado en los últimos años. Fue
-- iniciada por el autor original de `tendsto` y luego creció a medida
-- que otras personas usaron más `tendsto`, y añadieron a la lista de
-- lemas útiles más lemas útiles a medida que usaban `tendsto` para
-- hacer otras cosas y luego abstraían propiedades que descubrieron que
-- eran útiles. Por ejemplo, esta semana (escribo esto en febrero de
-- 2021) Heather Macbeth estaba trabajando en formas modulares en Lean y
-- descubrió que necesitaba un lema sobre `tendsto`, que, después de
-- algunas discusión en el chat de Zulip Lean, Heather y yo nos dimos
-- cuenta de que era una declaración sobre cómo `tendsto` conmuta con un
-- cierto tipo de coproducto. Lo demostramos y Heather está ahora mismo
-- en proceso de añadirlo (`tendsto.prod_map_coprod`) a `mathlib`, la
-- biblioteca de matemáticas de Lean.
--
-- Debo señalar que nunca habría trabajado en ese problema con Heather
-- si no hubiera sido por el hecho de que le había estado enseñando
-- sobre filtros y, por lo tanto, ¡tenía que aprender sobre ellos como
-- es debido!
--
-- Volvamos a ver nuestra nueva prueba de `tendsto_mul`. La prueba se
-- desprende de dos lemas de 2 líneas. Yo te explicaré el primero, y tú
-- puedes experimentar con el segundo. Veamos el primero.

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    is_limit a l ↔ tendsto a at_top (𝓝 l) :=
-- ---------------------------------------------------------------------

example
  (a : ℕ → ℝ)
  (l : ℝ)
  : is_limit a l ↔ tendsto a at_top (𝓝 l) :=
begin
  rw metric.tendsto_at_top,
  refl,
end

-- Las tripas de la primera es `metric.tendsto_at_top`, que es en
-- realidad una declaración sobre los espacios métricos. Dice que en
-- cualquier espacio métrico, la definición estándar del espacio métrico
-- épsilon-N de un límite de una secuencia es un caso especial de este
-- predicado de filtro `tendsto`. Aquí hay una prueba con más detalles
-- (`simp_rw` es sólo una versión ligeramente más versión de `rw` que
-- necesitamos por razones técnicas, porque `rw` no se ve bajo una
-- sentencia `∀`):

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    is_limit a l ↔ tendsto a at_top (𝓝 l)
-- ---------------------------------------------------------------------

-- Esta prueba más explícita utiliza la siguiente notación elegante
-- llamada "filter.eventually" :
--    `(∀ᶠ (x : α) en F, P x) ↔ {x : α | P x} ∈ F`
-- y entonces sólo se reduce a los siguientes dos hechos matemáticos
-- (aquí `ball l ε` es la bola abierta de radio `ε` centro `l` ), el
-- primero es `metric.tendsto_nhds` y el segundo `mem_at_top_sets`:
--    1) Si `a` está en un espacio métrico, entonces
--       `S ∈ 𝓝 l ↔ ∃ ε > 0, bola l ε ⊆ S`
--    2) Si `at_top` es el filtro sobre `ℕ` que vimos la última vez,
--       entonces `T ∈ at_top ↔ ∃ N : ℕ, {n : ℕ | N ≤ n} ⊆ T``
--
-- El resto es fácil, porque `tendsto a at_top (𝓝 l)` significa,
-- por definición de `tendsto`,
--    `∀ S : set ℝ, S ∈ 𝓝 l → a -¹' S ∈ at_top`
-- que se traduce en
--    `∀ S : set ℝ, (∃ ε > 0, ball l ⊆ S) → (∃ N, n ≥ N → a n ∈ S)`.
-- y si se despliega el envoltorio lógico se verá que esto no es más que
-- la definición habitual de `is_limit` (nótese que `a n ∈ ball l ε` es
-- definición es igual a `dist (a n) l < ε` que, para los reales, es
-- es igual a `|a n - l| < ε`).

-- Demostración:
--      is_limit a l
--    ↔ ∀ ε, ε > 0 → (∃ c, ∀ b , b ≥ c → b ∈ {x : ℕ | dist (a x) l < ε})
--         [por def. de límite]
--    ↔ ∀ ε, ε > 0 → {x | dist (a x) l < ε} ∈ at_top
--         [por mem_at_top_sets]
--    ↔ ∀ ε, ε > 0 → (∀ᶠ x in at_top, dist (a x) l < ε)
--         [por eventually_iff]
--    ↔ tendsto a at_top (𝓝 l)

example
  (a : ℕ → ℝ)
  (l : ℝ)
  : is_limit a l ↔ tendsto a at_top (𝓝 l) :=
begin
  simp_rw [metric.tendsto_nhds, eventually_iff, mem_at_top_sets],
  refl,
end

-- =====================================================================
-- § tendsto.mul                                                      --
-- =====================================================================

-- Veamos ahora el segundo ejemplo.

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que el límite del producto es el producto de los
-- límites.
-- ---------------------------------------------------------------------

example
  (a b : ℕ → ℝ)
  (l m : ℝ)
  (ha : is_limit a l)
  (hb : is_limit b m)
  : is_limit (a * b) (l * m) :=
begin
  rw is_limit_iff_tendsto at *,
  exact tendsto.mul ha hb,
end

-- Si pasas el ratón por encima de `tendsto.mul` en esa prueba, quizás
-- puedas distinguir que dice lo siguiente: si tenemos un espacio
-- topológico `M` con una multiplicación continua en él, y si `F` es un
-- filtro en `α` y `f` y `g` son aplicaciones `α → M`, entonces `tendsto
-- f F (𝓝 l)` y `tendsto g F (𝓝 m)` implica `tendsto a (f * g) F 𝓝 (l *
-- m)`. Aplicamos esto con `F` el filtro cofinito y ya hemos terminado,
-- usando que la multiplicación en ℝ es una función continua. ¿Cómo
-- sabía Lean esto? Pues, `[has_continuous_mul M]` estaba entre
-- corchetes así que eso significa que el sistema de inferencia de
-- clases de tipo se supone que se ocupa de ello. Vamos ver cómo se las
-- arregla con la afirmación de que la multiplicación es continua en los
-- reales.

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que la multiplicación es los reales es continua.
-- ---------------------------------------------------------------------

example : has_continuous_mul ℝ :=
begin
  apply_instance
end

-- La gente que definió `ℝ` en Lean hizo una definición, y el precio que
-- tuvieron que pagar para hacerla utilizable fue que tuvieron que hacer
-- una gran API para para demostrar que un conjunto acotado no vacío de
-- reales tiene un límite superior mínimo y que los reales eran un
-- anillo topológico (y por lo tanto la multiplicación era
-- continua). Pero este precio se pagó en 2018 por lo que los
-- matemáticos podemos ahora utilizar estos hechos de forma gratuita.
--

-- Todo lo que queda entonces, si queremos ver los detalles, es
-- demostrar `tendsto.mul`, y esto es una afirmación sobre filtros en
-- espacios topológicos, así que hagámoslo. Primero ¿qué significa
-- `continuo`?

-- =====================================================================
-- § Continuidad                                                      --
-- =====================================================================

-- ---------------------------------------------------------------------
-- Ejercicio. Declarar X e Y como espacios topológicos y f : X → Y
-- ---------------------------------------------------------------------

variables (X Y : Type) [topological_space X] [topological_space Y]
variable  (f : X → Y)

-- Si `x : X`, ¿qué significa que `f` sea continua en `x`?
-- Intuitivamente, significa que si se mueve `x` una pequeña cantidad,
-- entonces `f x` se mueve en una pequeña cantidad. En otras palabras,
-- "f" envía un pequeño entorno de `x` a un pequeño entorno de `f x`.
--
-- Si nuestro modelo mental del filtro de entorno `𝓝 x` es una especie
-- de conjunto generalizado que corresponde a un entorno infinitesimal
-- de `x`, veremos por qué Lean tiene la siguiente definición de
-- "continuo":

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que
--    continuous_at f x ↔ tendsto f (𝓝 x) (𝓝 (f x))
-- ---------------------------------------------------------------------

lemma continuous_at_def
  (x : X)
  : continuous_at f x ↔ tendsto f (𝓝 x) (𝓝 (f x)) :=
begin
  refl,
end

-- Es probable que te hayan dicho lo que significa que una función `f :
-- X → Y` entre espacios *métricos* sea continua en `x`. ¿Te han dicho
-- alguna vez lo que significa que una función entre espacios
-- "topológicos" sea continua en "x", en lugar de ser continua en todo
-- "X"? Este es lo que significa.
--
-- Ahora vamos a empezar con la prueba de `tendsto.mul`, construyendo
-- una API para la definición de `continuous_at`. No olvides cosas como
--    `tendsto_id : tendsto id x x`
-- `  tendsto.comp : tendsto g G H → tendsto f F G → tendsto (g ∘ f) F H`
-- de la orimera parte de este tema.

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que la identidad es continua en todos los
-- puntos.
-- ---------------------------------------------------------------------

example
  (x : X)
  : continuous_at id x :=
begin
  exact tendsto_id,
end

-- ---------------------------------------------------------------------
-- Ejercicio. Declarar Z como espacio topológico y g : Y → Z
-- ---------------------------------------------------------------------

variables (Z : Type) [topological_space Z]
variable  (g : Y → Z)

-- ---------------------------------------------------------------------
-- Ejercicio. Demostrar que la composición de funciones continuas es
-- continua.
-- ---------------------------------------------------------------------

example
  (x : X)
  (hf : continuous_at f x)
  (hg : continuous_at g (f x))
  : continuous_at (g ∘ f) x :=
begin
  exact tendsto.comp hg hf,
end

-- Ahora demostramos un resultado clave, llamado `tendsto.prod_mk_nhds`.
--
-- Notación para el producto de tipos: si `Y` y `Z` son tipos, entonces
-- `Y × Z` es el producto de tipos, y la notación para un término
-- general es `(y, z) : Y × Z` con `y : Y` y `z : Z`.
--
-- Un caso especial del teorema siguiente es que si las aplicaciones
-- `f : X → Y` y `g : X → Z` son continuas en `x` entonces el producto
-- `f × g : X → Y × Z` es también continua en `x`. Vamos a demostrar
-- algo más general: si `α` es un tipo cualquiera y `F : filtro α` es
-- cualquier filtro, `y : Y`, `z : Z` y `f : α → Y` y `g : α → Z`
-- satisfacen `tendsto f F (𝓝 y)` y `tendsto g F (𝓝 z)`, entonces
-- `tendsto a (f × g) F (𝓝 (y,z))`, donde "f × g" es la 1a aplicación
-- `λ x, (f x, g x)`.
-- El dato clave que necesitarás de la API de topología del producto es
--    mem_nhds_prod_iff :
--      S ∈ 𝓝 ((a, b) : X × Y) ↔`
--      ∃ (U : set X) (H : U ∈ 𝓝 a) (V :set Y) (H : V ∈ 𝓝 b), U.prod V ⊆ S
-- Esto es todo lo que deberías necesitar sobre la topología del
-- producto (no vamos a entrar en cómo está definida la topología del
-- producto, pero el hecho clave dice matemáticamente que un entorno
-- de `(a,b) : X × Y` contiene un producto de entornos de `X` y de
-- `Y`).
--
-- También necesitarás saber
--    mk_mem_prod : a ∈ U → b ∈ V → (a, b) ∈ U.prod V
-- donde para `U : set X` y `V : set Y`, `U.prod V = prod U V` es el
-- subconjunto obvio de `X × Y`.
--
-- Recordemos también de primera la parte del tema:
--     mem_map : S ∈ map φ F ↔ {x : α | φ x ∈ S} ∈ F
--     tendsto_def : tendsto f F G ↔ ∀ (S : conjunto Y), S ∈ G → f -¹' S ∈ F
-- (aunque aquí hay una trampa: la definición real de
-- `tendsto f F G` es `∀ {S : set Y}, S ∈ G ...` )

-- #check tendsto_def

-- this is called `tendsto.prod_mk_nhds` in Lean but try proving it yourself.
example {α : Type} (f : α → Y) (g : α → Z) (x : X) (F : filter α) (y : Y) (z : Z)
  (hf : tendsto f F (𝓝 y)) (hg : tendsto g F (𝓝 z)) :
  tendsto (λ x, (f x, g x)) F (𝓝 (y, z)) :=
begin
  -- say `S` is a neighbourhood of `(y, z)`.
  rintro S hS,
  -- use the given fact about neighbourhoods of point in a product of top spaces
  rw mem_nhds_prod_iff at hS,
  -- to get neighbourhoods of `y` and `z`.
  rcases hS with ⟨U, hU, V, hV, h⟩,
  -- The goal is `S ∈ map ...` so use `mem_map`.
  rw mem_map,
  -- I claim {x : α | f x ∈ U} ∈ F
  have hfxU : {x : α | f x ∈ U} ∈ F := hf hU, -- technical note: I didn't
                                  -- rewrite `tendsto_def` so I don't supply `U`
  -- I claim {x : α | g x ∈ V} ∈ F
  have hgxV : {x : α | g x ∈ V} ∈ F := hg hV,
  -- so their intersection is in F
  have hfg := inter_mem_sets hfxU hgxV,
  -- and so it suffices to show that their intersection contains `{x : α | (f x, g x) ∈ S}`
  refine mem_sets_of_superset hfg _,
  -- but we just now take everything apart
  rintro x ⟨(hxf : f x ∈ U), (hxg : g x ∈ V)⟩,
  apply h,
  -- and apply mk_mem_prod
  exact set.mk_mem_prod hxf hxg,
end

-- Armed with `tendsto.prod_mk_nhds`, let's prove the version of `tendsto.mul`
-- which we need
lemma key_lemma {α M : Type} [topological_space M] [has_mul M]
  {f g : α → M} {F : filter α} {a b : M} (hf : tendsto f F (𝓝 a))
  (hg : tendsto g F (𝓝 b))
  (hcontinuous : continuous_at (λ (mn : M × M), mn.1 * mn.2) (a,b)) :
  tendsto (f * g) F (𝓝 (a * b)) :=
begin
  set f1 : M × M → M := λ mn, mn.1 * mn.2 with hf1,
  set f2 : α → M × M := λ x, (f x, g x) with hf2,
  have h1 : f1 ∘ f2 = f * g,
  { ext x,
    refl },
  have h2 : tendsto f2 F (𝓝 (a, b)) := tendsto.prod_mk_nhds hf hg,
  rw ← h1,
  apply tendsto.comp _ h2,
  apply hcontinuous,
end

-- The final ingredient is that multiplication is continuous on ℝ, which we
-- just take from the real API:
lemma real.continuous_mul_at (a b : ℝ) :
  continuous_at (λ xy : ℝ × ℝ, xy.1 * xy.2) (a, b) :=
begin
  -- it's in the library
  exact continuous.continuous_at real.continuous_mul,
end

-- and now we have all the ingredients we need for our own proof of `is_limit_mul`!
example  (a b : ℕ → ℝ) (l m : ℝ)
  (ha : is_limit a l) (hb : is_limit b m) :
  is_limit (a * b) (l * m) :=
begin
  rw is_limit_iff_tendsto at *,
  apply key_lemma ha hb,
  apply real.continuous_mul_at,
end


/-
Copyright © 2021 Nicolò Cavalleri. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nicolò Cavalleri
-/

import tactic.basic
import algebra.module.basic

/-!
# Bundle
Basic data structure to implement fiber bundles, vector bundles (maybe fibrations?), etc. This file
should contain all possible results that do not involve any topology.
We provide a type synonym of `Σ x, E x` as `bundle.total_space E`, to be able to endow it with
a topology which is not the disjoint union topology `sigma.topological_space`. In general, the
constructions of fiber bundles we will make will be of this form.

## References
- https://en.wikipedia.org/wiki/Bundle_(mathematics)
-/

namespace bundle

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

/--
`total_space E` is the total space of the bundle `Σ x, E x`. This type synonym is used to avoid
conflicts with general sigma types.
-/
def total_space := Σ x, E x

instance [inhabited B] [inhabited (E (default B))] :
  inhabited (total_space E) := ⟨⟨default B, default (E (default B))⟩⟩

/-- `bundle.proj E` is the canonical projection `total_space E → B` on the base space. -/
@[simp] def proj : total_space E → B := sigma.fst

/-- Constructor for the total space of a `topological_fiber_bundle_core`. -/
@[simp, reducible] def total_space_mk (E : B → Type*) (b : B) (a : E b) :
  bundle.total_space E := ⟨b, a⟩

instance {x : B} : has_coe_t (E x) (total_space E) := ⟨sigma.mk x⟩

@[simp] lemma coe_fst (x : B) (v : E x) : (v : total_space E).fst = x := rfl

lemma to_total_space_coe {x : B} (v : E x) : (v : total_space E) = ⟨x, v⟩ := rfl

/-- `bundle.trivial B F` is the trivial bundle over `B` of fiber `F`. -/
def trivial (B : Type*) (F : Type*) : B → Type* := function.const B F

instance {F : Type*} [inhabited F] {b : B} : inhabited (bundle.trivial B F b) := ⟨(default F : F)⟩

/-- The trivial bundle, unlike other bundles, has a canonical projection on the fiber. -/
def trivial.proj_snd (B : Type*) (F : Type*) : (total_space (bundle.trivial B F)) → F := sigma.snd

section fiber_structures

variable [∀ x, add_comm_monoid (E x)]

@[simp] lemma coe_snd_map_apply (x : B) (v w : E x) :
  (↑(v + w) : total_space E).snd = (v : total_space E).snd + (w : total_space E).snd := rfl

variables (R : Type*) [semiring R] [∀ x, module R (E x)]

@[simp]

end fiber_structures

section trivial_instances
local attribute [reducible] bundle.trivial

variables {F : Type*} {R : Type*} [semiring R] (b : B)

instance [add_comm_monoid F] : add_comm_monoid (bundle.trivial B F b) := ‹add_comm_monoid F›
instance [add_comm_group F] : add_comm_group (bundle.trivial B F b) := ‹add_comm_group F›
instance [add_comm_monoid F] [module R F] : module R (bundle.trivial B F b) := ‹module R F›

end trivial_instances

end bundle