/-
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, Patrick Massot
-/
import algebra.order.field
import data.finset.preimage
import data.set.intervals.disjoint
import order.filter.bases

/-!
# `at_top` and `at_bot` filters on preorded sets, monoids and groups.

In this file we define the filters

* `at_top`: corresponds to `n → +∞`;
* `at_bot`: corresponds to `n → -∞`.

Then we prove many lemmas like “if `f → +∞`, then `f ± c → +∞`”.
-/

variables {ι ι' α β γ : Type*}

open set
open_locale classical filter big_operators

namespace filter
/-- `at_top` is the filter representing the limit `→ ∞` on an ordered set.
  It is generated by the collection of up-sets `{b | a ≤ b}`.
  (The preorder need not have a top element for this to be well defined,
  and indeed is trivial when a top element exists.) -/
def at_top [preorder α] : filter α := ⨅ a, 𝓟 (Ici a)

/-- `at_bot` is the filter representing the limit `→ -∞` on an ordered set.
  It is generated by the collection of down-sets `{b | b ≤ a}`.
  (The preorder need not have a bottom element for this to be well defined,
  and indeed is trivial when a bottom element exists.) -/
def at_bot [preorder α] : filter α := ⨅ a, 𝓟 (Iic a)

lemma mem_at_top [preorder α] (a : α) : {b : α | a ≤ b} ∈ @at_top α _ :=
mem_infi_of_mem a $ subset.refl _

lemma Ici_mem_at_top [preorder α] (a : α) : Ici a ∈ (at_top : filter α) := mem_at_top a

lemma Ioi_mem_at_top [preorder α] [no_max_order α] (x : α) : Ioi x ∈ (at_top : filter α) :=
let ⟨z, hz⟩ := exists_gt x in mem_of_superset (mem_at_top z) $ λ y h,  lt_of_lt_of_le hz h

lemma mem_at_bot [preorder α] (a : α) : {b : α | b ≤ a} ∈ @at_bot α _ :=
mem_infi_of_mem a $ subset.refl _

lemma Iic_mem_at_bot [preorder α] (a : α) : Iic a ∈ (at_bot : filter α) := mem_at_bot a

lemma Iio_mem_at_bot [preorder α] [no_min_order α] (x : α) : Iio x ∈ (at_bot : filter α) :=
let ⟨z, hz⟩ := exists_lt x in mem_of_superset (mem_at_bot z) $ λ y h, lt_of_le_of_lt h hz

lemma disjoint_at_bot_principal_Ioi [preorder α] (x : α) : disjoint at_bot (𝓟 (Ioi x)) :=
disjoint_of_disjoint_of_mem (Iic_disjoint_Ioi le_rfl) (Iic_mem_at_bot x) (mem_principal_self _)

lemma disjoint_at_top_principal_Iio [preorder α] (x : α) : disjoint at_top (𝓟 (Iio x)) :=
@disjoint_at_bot_principal_Ioi αᵒᵈ _ _

lemma disjoint_at_top_principal_Iic [preorder α] [no_max_order α] (x : α) :
  disjoint at_top (𝓟 (Iic x)) :=
disjoint_of_disjoint_of_mem (Iic_disjoint_Ioi le_rfl).symm (Ioi_mem_at_top x) (mem_principal_self _)

lemma disjoint_at_bot_principal_Ici [preorder α] [no_min_order α] (x : α) :
  disjoint at_bot (𝓟 (Ici x)) :=
@disjoint_at_top_principal_Iic αᵒᵈ _ _ _

lemma disjoint_at_bot_at_top [partial_order α] [nontrivial α] :
  disjoint (at_bot : filter α) at_top :=
begin
  rcases exists_pair_ne α with ⟨x, y, hne⟩,
  by_cases hle : x ≤ y,
  { refine disjoint_of_disjoint_of_mem _ (Iic_mem_at_bot x) (Ici_mem_at_top y),
    exact Iic_disjoint_Ici.2 (hle.lt_of_ne hne).not_le },
  { refine disjoint_of_disjoint_of_mem _ (Iic_mem_at_bot y) (Ici_mem_at_top x),
    exact Iic_disjoint_Ici.2 hle }
end

lemma disjoint_at_top_at_bot [partial_order α] [nontrivial α] :
  disjoint (at_top : filter α) at_bot :=
disjoint_at_bot_at_top.symm

lemma at_top_basis [nonempty α] [semilattice_sup α] :
  (@at_top α _).has_basis (λ _, true) Ici :=
has_basis_infi_principal (directed_of_sup $ λ a b, Ici_subset_Ici.2)

lemma at_top_basis' [semilattice_sup α] (a : α) :
  (@at_top α _).has_basis (λ x, a ≤ x) Ici :=
⟨λ t, (@at_top_basis α ⟨a⟩ _).mem_iff.trans
  ⟨λ ⟨x, _, hx⟩, ⟨x ⊔ a, le_sup_right, λ y hy, hx (le_trans le_sup_left hy)⟩,
    λ ⟨x, _, hx⟩, ⟨x, trivial, hx⟩⟩⟩

lemma at_bot_basis [nonempty α] [semilattice_inf α] : (@at_bot α _).has_basis (λ _, true) Iic :=
@at_top_basis αᵒᵈ _ _

lemma at_bot_basis' [semilattice_inf α] (a : α) : (@at_bot α _).has_basis (λ x, x ≤ a) Iic :=
@at_top_basis' αᵒᵈ _ _

@[instance]
lemma at_top_ne_bot [nonempty α] [semilattice_sup α] : ne_bot (at_top : filter α) :=
at_top_basis.ne_bot_iff.2 $ λ a _, nonempty_Ici

@[instance]
lemma at_bot_ne_bot [nonempty α] [semilattice_inf α] : ne_bot (at_bot : filter α) :=
@at_top_ne_bot αᵒᵈ _ _

@[simp]
lemma mem_at_top_sets [nonempty α] [semilattice_sup α] {s : set α} :
  s ∈ (at_top : filter α) ↔ ∃a:α, ∀b≥a, b ∈ s :=
at_top_basis.mem_iff.trans $ exists_congr $ λ _, exists_const _

@[simp]
lemma mem_at_bot_sets [nonempty α] [semilattice_inf α] {s : set α} :
  s ∈ (at_bot : filter α) ↔ ∃a:α, ∀b≤a, b ∈ s :=
@mem_at_top_sets αᵒᵈ _ _ _

@[simp]
lemma eventually_at_top [semilattice_sup α] [nonempty α] {p : α → Prop} :
  (∀ᶠ x in at_top, p x) ↔ (∃ a, ∀ b ≥ a, p b) :=
mem_at_top_sets

@[simp]
lemma eventually_at_bot [semilattice_inf α] [nonempty α] {p : α → Prop} :
  (∀ᶠ x in at_bot, p x) ↔ (∃ a, ∀ b ≤ a, p b) :=
mem_at_bot_sets

lemma eventually_ge_at_top [preorder α] (a : α) : ∀ᶠ x in at_top, a ≤ x := mem_at_top a

lemma eventually_le_at_bot [preorder α] (a : α) : ∀ᶠ x in at_bot, x ≤ a := mem_at_bot a

lemma eventually_gt_at_top [preorder α] [no_max_order α] (a : α) :
  ∀ᶠ x in at_top, a < x :=
Ioi_mem_at_top a

lemma eventually_ne_at_top [preorder α] [no_max_order α] (a : α) :
  ∀ᶠ x in at_top, x ≠ a :=
(eventually_gt_at_top a).mono $ λ x, ne_of_gt

lemma tendsto.eventually_gt_at_top [preorder β] [no_max_order β] {f : α → β} {l : filter α}
  (hf : tendsto f l at_top) (c : β) : ∀ᶠ x in l, c < f x :=
hf.eventually (eventually_gt_at_top c)

lemma tendsto.eventually_ge_at_top [preorder β] {f : α → β} {l : filter α}
  (hf : tendsto f l at_top) (c : β) : ∀ᶠ x in l, c ≤ f x :=
hf.eventually (eventually_ge_at_top c)

lemma tendsto.eventually_ne_at_top [preorder β] [no_max_order β] {f : α → β} {l : filter α}
  (hf : tendsto f l at_top) (c : β) : ∀ᶠ x in l, f x ≠ c :=
hf.eventually (eventually_ne_at_top c)

lemma eventually_lt_at_bot [preorder α] [no_min_order α] (a : α) :
  ∀ᶠ x in at_bot, x < a :=
Iio_mem_at_bot a

lemma eventually_ne_at_bot [preorder α] [no_min_order α] (a : α) :
  ∀ᶠ x in at_bot, x ≠ a :=
(eventually_lt_at_bot a).mono $ λ x, ne_of_lt

lemma tendsto.eventually_lt_at_bot [preorder β] [no_min_order β] {f : α → β} {l : filter α}
  (hf : tendsto f l at_bot) (c : β) : ∀ᶠ x in l, f x < c :=
hf.eventually (eventually_lt_at_bot c)

lemma tendsto.eventually_le_at_bot [preorder β] {f : α → β} {l : filter α}
  (hf : tendsto f l at_bot) (c : β) : ∀ᶠ x in l, f x ≤ c :=
hf.eventually (eventually_le_at_bot c)

lemma tendsto.eventually_ne_at_bot [preorder β] [no_min_order β] {f : α → β} {l : filter α}
  (hf : tendsto f l at_bot) (c : β) :  ∀ᶠ x in l, f x ≠ c :=
hf.eventually (eventually_ne_at_bot c)

lemma at_top_basis_Ioi [nonempty α] [semilattice_sup α] [no_max_order α] :
  (@at_top α _).has_basis (λ _, true) Ioi :=
at_top_basis.to_has_basis (λ a ha, ⟨a, ha, Ioi_subset_Ici_self⟩) $
  λ a ha, (exists_gt a).imp $ λ b hb, ⟨ha, Ici_subset_Ioi.2 hb⟩

lemma at_top_countable_basis [nonempty α] [semilattice_sup α] [encodable α] :
  has_countable_basis (at_top : filter α) (λ _, true) Ici :=
{ countable := countable_encodable _,
  .. at_top_basis }

lemma at_bot_countable_basis [nonempty α] [semilattice_inf α] [encodable α] :
  has_countable_basis (at_bot : filter α) (λ _, true) Iic :=
{ countable := countable_encodable _,
  .. at_bot_basis }

@[priority 200]
instance at_top.is_countably_generated [preorder α] [encodable α] :
  (at_top : filter $ α).is_countably_generated :=
is_countably_generated_seq _

@[priority 200]
instance at_bot.is_countably_generated [preorder α] [encodable α] :
  (at_bot : filter $ α).is_countably_generated :=
is_countably_generated_seq _

lemma order_top.at_top_eq (α) [partial_order α] [order_top α] : (at_top : filter α) = pure ⊤ :=
le_antisymm (le_pure_iff.2 $ (eventually_ge_at_top ⊤).mono $ λ b, top_unique)
  (le_infi $ λ b, le_principal_iff.2 le_top)

@[nontriviality]
lemma subsingleton.at_top_eq (α) [subsingleton α] [preorder α] : (at_top : filter α) = ⊤ :=
begin
  refine top_unique (λ s hs x, _),
  letI : unique α := ⟨⟨x⟩, λ y, subsingleton.elim y x⟩,
  rw [at_top, infi_unique, unique.default_eq x, mem_principal] at hs,
  exact hs left_mem_Ici
end

@[nontriviality]
lemma subsingleton.at_bot_eq (α) [subsingleton α] [preorder α] : (at_bot : filter α) = ⊤ :=
@subsingleton.at_top_eq αᵒᵈ _ _

lemma tendsto_at_top_pure [partial_order α] [order_top α] (f : α → β) :
  tendsto f at_top (pure $ f ⊤) :=
(order_top.at_top_eq α).symm ▸ tendsto_pure_pure _ _

lemma tendsto_at_bot_pure [partial_order α] [order_bot α] (f : α → β) :
  tendsto f at_bot (pure $ f ⊥) :=
@tendsto_at_top_pure αᵒᵈ _ _ _ _

lemma eventually.exists_forall_of_at_top [semilattice_sup α] [nonempty α] {p : α → Prop}
  (h : ∀ᶠ x in at_top, p x) : ∃ a, ∀ b ≥ a, p b :=
eventually_at_top.mp h

lemma eventually.exists_forall_of_at_bot [semilattice_inf α] [nonempty α] {p : α → Prop}
  (h : ∀ᶠ x in at_bot, p x) : ∃ a, ∀ b ≤ a, p b :=
eventually_at_bot.mp h

lemma frequently_at_top [semilattice_sup α] [nonempty α] {p : α → Prop} :
  (∃ᶠ x in at_top, p x) ↔ (∀ a, ∃ b ≥ a, p b) :=
by simp [at_top_basis.frequently_iff]

lemma frequently_at_bot [semilattice_inf α] [nonempty α] {p : α → Prop} :
  (∃ᶠ x in at_bot, p x) ↔ (∀ a, ∃ b ≤ a, p b) :=
@frequently_at_top αᵒᵈ _ _ _

lemma frequently_at_top' [semilattice_sup α] [nonempty α] [no_max_order α] {p : α → Prop} :
  (∃ᶠ x in at_top, p x) ↔ (∀ a, ∃ b > a, p b) :=
by simp [at_top_basis_Ioi.frequently_iff]

lemma frequently_at_bot' [semilattice_inf α] [nonempty α] [no_min_order α] {p : α → Prop} :
  (∃ᶠ x in at_bot, p x) ↔ (∀ a, ∃ b < a, p b) :=
@frequently_at_top' αᵒᵈ _ _ _ _

lemma frequently.forall_exists_of_at_top [semilattice_sup α] [nonempty α] {p : α → Prop}
  (h : ∃ᶠ x in at_top, p x) : ∀ a, ∃ b ≥ a, p b :=
frequently_at_top.mp h

lemma frequently.forall_exists_of_at_bot [semilattice_inf α] [nonempty α] {p : α → Prop}
  (h : ∃ᶠ x in at_bot, p x) : ∀ a, ∃ b ≤ a, p b :=
frequently_at_bot.mp h

lemma map_at_top_eq [nonempty α] [semilattice_sup α] {f : α → β} :
  at_top.map f = (⨅a, 𝓟 $ f '' {a' | a ≤ a'}) :=
(at_top_basis.map _).eq_infi

lemma map_at_bot_eq [nonempty α] [semilattice_inf α] {f : α → β} :
  at_bot.map f = (⨅a, 𝓟 $ f '' {a' | a' ≤ a}) :=
@map_at_top_eq αᵒᵈ _ _ _ _

lemma tendsto_at_top [preorder β] {m : α → β} {f : filter α} :
  tendsto m f at_top ↔ (∀b, ∀ᶠ a in f, b ≤ m a) :=
by simp only [at_top, tendsto_infi, tendsto_principal, mem_Ici]

lemma tendsto_at_bot [preorder β] {m : α → β} {f : filter α} :
  tendsto m f at_bot ↔ (∀b, ∀ᶠ a in f, m a ≤ b) :=
@tendsto_at_top α βᵒᵈ _ m f

lemma tendsto_at_top_mono' [preorder β] (l : filter α) ⦃f₁ f₂ : α → β⦄ (h : f₁ ≤ᶠ[l] f₂) :
  tendsto f₁ l at_top → tendsto f₂ l at_top :=
assume h₁, tendsto_at_top.2 $ λ b, mp_mem (tendsto_at_top.1 h₁ b)
  (monotone_mem (λ a ha ha₁, le_trans ha₁ ha) h)

lemma tendsto_at_bot_mono' [preorder β] (l : filter α) ⦃f₁ f₂ : α → β⦄ (h : f₁ ≤ᶠ[l] f₂) :
  tendsto f₂ l at_bot → tendsto f₁ l at_bot :=
@tendsto_at_top_mono' _ βᵒᵈ _ _ _ _ h

lemma tendsto_at_top_mono [preorder β] {l : filter α} {f g : α → β} (h : ∀ n, f n ≤ g n) :
  tendsto f l at_top → tendsto g l at_top :=
tendsto_at_top_mono' l $ eventually_of_forall h

lemma tendsto_at_bot_mono [preorder β] {l : filter α} {f g : α → β} (h : ∀ n, f n ≤ g n) :
  tendsto g l at_bot → tendsto f l at_bot :=
@tendsto_at_top_mono _ βᵒᵈ _ _ _ _ h

/-!
### Sequences
-/

lemma inf_map_at_top_ne_bot_iff [semilattice_sup α] [nonempty α] {F : filter β} {u : α → β} :
  ne_bot (F ⊓ (map u at_top)) ↔ ∀ U ∈ F, ∀ N, ∃ n ≥ N, u n ∈ U :=
by simp_rw [inf_ne_bot_iff_frequently_left, frequently_map, frequently_at_top]; refl

lemma inf_map_at_bot_ne_bot_iff [semilattice_inf α] [nonempty α] {F : filter β} {u : α → β} :
  ne_bot (F ⊓ (map u at_bot)) ↔ ∀ U ∈ F, ∀ N, ∃ n ≤ N, u n ∈ U :=
@inf_map_at_top_ne_bot_iff αᵒᵈ _ _ _ _ _

lemma extraction_of_frequently_at_top' {P : ℕ → Prop} (h : ∀ N, ∃ n > N, P n) :
  ∃ φ : ℕ → ℕ, strict_mono φ ∧ ∀ n, P (φ n) :=
begin
  choose u hu using h,
  cases forall_and_distrib.mp hu with hu hu',
  exact ⟨u ∘ (nat.rec 0 (λ n v, u v)), strict_mono_nat_of_lt_succ (λ n, hu _), λ n, hu' _⟩,
end

lemma extraction_of_frequently_at_top {P : ℕ → Prop} (h : ∃ᶠ n in at_top, P n) :
  ∃ φ : ℕ → ℕ, strict_mono φ ∧ ∀ n, P (φ n) :=
begin
  rw frequently_at_top' at h,
  exact extraction_of_frequently_at_top' h,
end

lemma extraction_of_eventually_at_top {P : ℕ → Prop} (h : ∀ᶠ n in at_top, P n) :
  ∃ φ : ℕ → ℕ, strict_mono φ ∧ ∀ n, P (φ n) :=
extraction_of_frequently_at_top h.frequently

lemma extraction_forall_of_frequently {P : ℕ → ℕ → Prop} (h : ∀ n, ∃ᶠ k in at_top, P n k) :
  ∃ φ : ℕ → ℕ, strict_mono φ ∧ ∀ n, P n (φ n) :=
begin
  simp only [frequently_at_top'] at h,
  choose u hu hu' using h,
  use (λ n, nat.rec_on n (u 0 0) (λ n v, u (n+1) v) : ℕ → ℕ),
  split,
  { apply strict_mono_nat_of_lt_succ,
    intro n,
    apply hu },
  { intros n,
    cases n ; simp [hu'] },
end

lemma extraction_forall_of_eventually  {P : ℕ → ℕ → Prop} (h : ∀ n, ∀ᶠ k in at_top, P n k) :
  ∃ φ : ℕ → ℕ, strict_mono φ ∧ ∀ n, P n (φ n) :=
extraction_forall_of_frequently (λ n, (h n).frequently)

lemma extraction_forall_of_eventually' {P : ℕ → ℕ → Prop} (h : ∀ n, ∃ N, ∀ k ≥ N, P n k) :
  ∃ φ : ℕ → ℕ, strict_mono φ ∧ ∀ n, P n (φ n) :=
extraction_forall_of_eventually (by simp [eventually_at_top, h])

lemma exists_le_of_tendsto_at_top [semilattice_sup α] [preorder β] {u : α → β}
  (h : tendsto u at_top at_top) (a : α) (b : β) : ∃ a' ≥ a, b ≤ u a' :=
begin
  have : ∀ᶠ x in at_top, a ≤ x ∧ b ≤ u x :=
    (eventually_ge_at_top a).and (h.eventually $ eventually_ge_at_top b),
  haveI : nonempty α := ⟨a⟩,
  rcases this.exists with ⟨a', ha, hb⟩,
  exact ⟨a', ha, hb⟩
end

@[nolint ge_or_gt] -- see Note [nolint_ge]
lemma exists_le_of_tendsto_at_bot [semilattice_sup α] [preorder β] {u : α → β}
  (h : tendsto u at_top at_bot) : ∀ a b, ∃ a' ≥ a, u a' ≤ b :=
@exists_le_of_tendsto_at_top _ βᵒᵈ _ _ _ h

lemma exists_lt_of_tendsto_at_top [semilattice_sup α] [preorder β] [no_max_order β]
  {u : α → β} (h : tendsto u at_top at_top) (a : α) (b : β) : ∃ a' ≥ a, b < u a' :=
begin
  cases exists_gt b with b' hb',
  rcases exists_le_of_tendsto_at_top h a b' with ⟨a', ha', ha''⟩,
  exact ⟨a', ha', lt_of_lt_of_le hb' ha''⟩
end

@[nolint ge_or_gt] -- see Note [nolint_ge]
lemma exists_lt_of_tendsto_at_bot [semilattice_sup α] [preorder β] [no_min_order β]
  {u : α → β} (h : tendsto u at_top at_bot) : ∀ a b, ∃ a' ≥ a, u a' < b :=
@exists_lt_of_tendsto_at_top _ βᵒᵈ _ _ _ _ h

/--
If `u` is a sequence which is unbounded above,
then after any point, it reaches a value strictly greater than all previous values.
-/
lemma high_scores [linear_order β] [no_max_order β] {u : ℕ → β}
  (hu : tendsto u at_top at_top) : ∀ N, ∃ n ≥ N, ∀ k < n, u k < u n :=
begin
  intros N,
  obtain ⟨k : ℕ, hkn : k ≤ N, hku : ∀ l ≤ N, u l ≤ u k⟩ : ∃ k ≤ N, ∀ l ≤ N, u l ≤ u k,
    from exists_max_image _ u (finite_le_nat N) ⟨N, le_refl N⟩,
  have ex : ∃ n ≥ N, u k < u n,
    from exists_lt_of_tendsto_at_top hu _ _,
  obtain ⟨n : ℕ, hnN : n ≥ N, hnk : u k < u n, hn_min : ∀ m, m < n → N ≤ m → u m ≤ u k⟩ :
    ∃ n ≥ N, u k < u n ∧ ∀ m, m < n → N ≤ m → u m ≤ u k,
  { rcases nat.find_x ex with ⟨n, ⟨hnN, hnk⟩, hn_min⟩,
    push_neg at hn_min,
    exact ⟨n, hnN, hnk, hn_min⟩ },
  use [n, hnN],
  rintros (l : ℕ) (hl : l < n),
  have hlk : u l ≤ u k,
  { cases (le_total l N : l ≤ N ∨ N ≤ l) with H H,
    { exact hku l H },
    { exact hn_min l hl H } },
  calc u l ≤ u k : hlk
       ... < u n : hnk
end

/--
If `u` is a sequence which is unbounded below,
then after any point, it reaches a value strictly smaller than all previous values.
-/
@[nolint ge_or_gt] -- see Note [nolint_ge]
lemma low_scores [linear_order β] [no_min_order β] {u : ℕ → β}
  (hu : tendsto u at_top at_bot) : ∀ N, ∃ n ≥ N, ∀ k < n, u n < u k :=
@high_scores βᵒᵈ _ _ _ hu

/--
If `u` is a sequence which is unbounded above,
then it `frequently` reaches a value strictly greater than all previous values.
-/
lemma frequently_high_scores [linear_order β] [no_max_order β] {u : ℕ → β}
  (hu : tendsto u at_top at_top) : ∃ᶠ n in at_top, ∀ k < n, u k < u n :=
by simpa [frequently_at_top] using high_scores hu

/--
If `u` is a sequence which is unbounded below,
then it `frequently` reaches a value strictly smaller than all previous values.
-/
lemma frequently_low_scores [linear_order β] [no_min_order β] {u : ℕ → β}
  (hu : tendsto u at_top at_bot) : ∃ᶠ n in at_top, ∀ k < n, u n < u k :=
@frequently_high_scores βᵒᵈ _ _ _ hu

lemma strict_mono_subseq_of_tendsto_at_top
  {β : Type*} [linear_order β] [no_max_order β]
  {u : ℕ → β} (hu : tendsto u at_top at_top) :
  ∃ φ : ℕ → ℕ, strict_mono φ ∧ strict_mono (u ∘ φ) :=
let ⟨φ, h, h'⟩ := extraction_of_frequently_at_top (frequently_high_scores hu) in
⟨φ, h, λ n m hnm, h' m _ (h hnm)⟩

lemma strict_mono_subseq_of_id_le {u : ℕ → ℕ} (hu : ∀ n, n ≤ u n) :
  ∃ φ : ℕ → ℕ, strict_mono φ ∧ strict_mono (u ∘ φ) :=
strict_mono_subseq_of_tendsto_at_top (tendsto_at_top_mono hu tendsto_id)

lemma _root_.strict_mono.tendsto_at_top {φ : ℕ → ℕ} (h : strict_mono φ) :
  tendsto φ at_top at_top :=
tendsto_at_top_mono h.id_le tendsto_id

section ordered_add_comm_monoid

variables [ordered_add_comm_monoid β] {l : filter α} {f g : α → β}

lemma tendsto_at_top_add_nonneg_left' (hf : ∀ᶠ x in l, 0 ≤ f x) (hg : tendsto g l at_top) :
  tendsto (λ x, f x + g x) l at_top :=
tendsto_at_top_mono' l (hf.mono (λ x, le_add_of_nonneg_left)) hg

lemma tendsto_at_bot_add_nonpos_left' (hf : ∀ᶠ x in l, f x ≤ 0) (hg : tendsto g l at_bot) :
  tendsto (λ x, f x + g x) l at_bot :=
@tendsto_at_top_add_nonneg_left' _ βᵒᵈ _ _ _ _ hf hg

lemma tendsto_at_top_add_nonneg_left (hf : ∀ x, 0 ≤ f x) (hg : tendsto g l at_top) :
  tendsto (λ x, f x + g x) l at_top :=
tendsto_at_top_add_nonneg_left' (eventually_of_forall hf) hg

lemma tendsto_at_bot_add_nonpos_left (hf : ∀ x, f x ≤ 0) (hg : tendsto g l at_bot) :
  tendsto (λ x, f x + g x) l at_bot :=
@tendsto_at_top_add_nonneg_left _ βᵒᵈ _ _ _ _ hf hg

lemma tendsto_at_top_add_nonneg_right' (hf : tendsto f l at_top) (hg : ∀ᶠ x in l, 0 ≤ g x) :
  tendsto (λ x, f x + g x) l at_top :=
tendsto_at_top_mono' l (monotone_mem (λ x, le_add_of_nonneg_right) hg) hf

lemma tendsto_at_bot_add_nonpos_right' (hf : tendsto f l at_bot) (hg : ∀ᶠ x in l, g x ≤ 0) :
  tendsto (λ x, f x + g x) l at_bot :=
@tendsto_at_top_add_nonneg_right' _ βᵒᵈ _ _ _ _ hf hg

lemma tendsto_at_top_add_nonneg_right (hf : tendsto f l at_top) (hg : ∀ x, 0 ≤ g x) :
  tendsto (λ x, f x + g x) l at_top :=
tendsto_at_top_add_nonneg_right' hf (eventually_of_forall hg)

lemma tendsto_at_bot_add_nonpos_right (hf : tendsto f l at_bot) (hg : ∀ x, g x ≤ 0) :
  tendsto (λ x, f x + g x) l at_bot :=
@tendsto_at_top_add_nonneg_right _ βᵒᵈ _ _ _ _ hf hg

lemma tendsto_at_top_add (hf : tendsto f l at_top) (hg : tendsto g l at_top) :
  tendsto (λ x, f x + g x) l at_top :=
tendsto_at_top_add_nonneg_left' (tendsto_at_top.mp hf 0) hg

lemma tendsto_at_bot_add (hf : tendsto f l at_bot) (hg : tendsto g l at_bot) :
  tendsto (λ x, f x + g x) l at_bot :=
@tendsto_at_top_add _ βᵒᵈ _ _ _ _ hf hg

lemma tendsto.nsmul_at_top (hf : tendsto f l at_top) {n : ℕ} (hn : 0 < n) :
  tendsto (λ x, n • f x) l at_top :=
tendsto_at_top.2 $ λ y, (tendsto_at_top.1 hf y).mp $ (tendsto_at_top.1 hf 0).mono $ λ x h₀ hy,
calc y ≤ f x : hy
... = 1 • f x : (one_nsmul _).symm
... ≤  n • f x : nsmul_le_nsmul h₀ hn

lemma tendsto.nsmul_at_bot (hf : tendsto f l at_bot) {n : ℕ} (hn : 0 < n) :
  tendsto (λ x, n • f x) l at_bot :=
@tendsto.nsmul_at_top α βᵒᵈ _ l f hf n hn

lemma tendsto_bit0_at_top : tendsto bit0 (at_top : filter β) at_top :=
tendsto_at_top_add tendsto_id tendsto_id

lemma tendsto_bit0_at_bot : tendsto bit0 (at_bot : filter β) at_bot :=
tendsto_at_bot_add tendsto_id tendsto_id

end ordered_add_comm_monoid

section ordered_cancel_add_comm_monoid

variables [ordered_cancel_add_comm_monoid β] {l : filter α} {f g : α → β}

lemma tendsto_at_top_of_add_const_left (C : β) (hf : tendsto (λ x, C + f x) l at_top) :
  tendsto f l at_top :=
tendsto_at_top.2 $ assume b, (tendsto_at_top.1 hf (C + b)).mono (λ x, le_of_add_le_add_left)

lemma tendsto_at_bot_of_add_const_left (C : β) (hf : tendsto (λ x, C + f x) l at_bot) :
  tendsto f l at_bot :=
@tendsto_at_top_of_add_const_left _ βᵒᵈ _ _ _ C hf

lemma tendsto_at_top_of_add_const_right (C : β) (hf : tendsto (λ x, f x + C) l at_top) :
  tendsto f l at_top :=
tendsto_at_top.2 $ assume b, (tendsto_at_top.1 hf (b + C)).mono (λ x, le_of_add_le_add_right)

lemma tendsto_at_bot_of_add_const_right (C : β) (hf : tendsto (λ x, f x + C) l at_bot) :
  tendsto f l at_bot :=
@tendsto_at_top_of_add_const_right _ βᵒᵈ _ _ _ C hf

lemma tendsto_at_top_of_add_bdd_above_left' (C) (hC : ∀ᶠ x in l, f x ≤ C)
  (h : tendsto (λ x, f x + g x) l at_top) :
  tendsto g l at_top :=
tendsto_at_top_of_add_const_left C
  (tendsto_at_top_mono' l (hC.mono (λ x hx, add_le_add_right hx (g x))) h)

lemma tendsto_at_bot_of_add_bdd_below_left' (C) (hC : ∀ᶠ x in l, C ≤ f x)
  (h : tendsto (λ x, f x + g x) l at_bot) :
  tendsto g l at_bot :=
@tendsto_at_top_of_add_bdd_above_left' _ βᵒᵈ _ _ _ _ C hC h

lemma tendsto_at_top_of_add_bdd_above_left (C) (hC : ∀ x, f x ≤ C) :
  tendsto (λ x, f x + g x) l at_top → tendsto g l at_top :=
tendsto_at_top_of_add_bdd_above_left' C (univ_mem' hC)

lemma tendsto_at_bot_of_add_bdd_below_left (C) (hC : ∀ x, C ≤ f x) :
  tendsto (λ x, f x + g x) l at_bot → tendsto g l at_bot :=
@tendsto_at_top_of_add_bdd_above_left _ βᵒᵈ _ _ _ _ C hC

lemma tendsto_at_top_of_add_bdd_above_right' (C) (hC : ∀ᶠ x in l, g x ≤ C)
  (h : tendsto (λ x, f x + g x) l at_top) :
  tendsto f l at_top :=
tendsto_at_top_of_add_const_right C
  (tendsto_at_top_mono' l (hC.mono (λ x hx, add_le_add_left hx (f x))) h)

lemma tendsto_at_bot_of_add_bdd_below_right' (C) (hC : ∀ᶠ x in l, C ≤ g x)
  (h : tendsto (λ x, f x + g x) l at_bot) :
  tendsto f l at_bot :=
@tendsto_at_top_of_add_bdd_above_right' _ βᵒᵈ _ _ _ _ C hC h

lemma tendsto_at_top_of_add_bdd_above_right (C) (hC : ∀ x, g x ≤ C) :
  tendsto (λ x, f x + g x) l at_top → tendsto f l at_top :=
tendsto_at_top_of_add_bdd_above_right' C (univ_mem' hC)

lemma tendsto_at_bot_of_add_bdd_below_right (C) (hC : ∀ x, C ≤ g x) :
  tendsto (λ x, f x + g x) l at_bot → tendsto f l at_bot :=
@tendsto_at_top_of_add_bdd_above_right _ βᵒᵈ _ _ _ _ C hC

end ordered_cancel_add_comm_monoid

section ordered_group

variables [ordered_add_comm_group β] (l : filter α) {f g : α → β}

lemma tendsto_at_top_add_left_of_le' (C : β) (hf : ∀ᶠ x in l, C ≤ f x) (hg : tendsto g l at_top) :
  tendsto (λ x, f x + g x) l at_top :=
@tendsto_at_top_of_add_bdd_above_left' _ _ _ l (λ x, -(f x)) (λ x, f x + g x) (-C)
  (by simpa) (by simpa)

lemma tendsto_at_bot_add_left_of_ge' (C : β) (hf : ∀ᶠ x in l, f x ≤ C) (hg : tendsto g l at_bot) :
  tendsto (λ x, f x + g x) l at_bot :=
@tendsto_at_top_add_left_of_le' _ βᵒᵈ _ _ _ _ C hf hg

lemma tendsto_at_top_add_left_of_le (C : β) (hf : ∀ x, C ≤ f x) (hg : tendsto g l at_top) :
  tendsto (λ x, f x + g x) l at_top :=
tendsto_at_top_add_left_of_le' l C (univ_mem' hf) hg

lemma tendsto_at_bot_add_left_of_ge (C : β) (hf : ∀ x, f x ≤ C) (hg : tendsto g l at_bot) :
  tendsto (λ x, f x + g x) l at_bot :=
@tendsto_at_top_add_left_of_le _ βᵒᵈ _ _ _ _ C hf hg

lemma tendsto_at_top_add_right_of_le' (C : β) (hf : tendsto f l at_top) (hg : ∀ᶠ x in l, C ≤ g x) :
  tendsto (λ x, f x + g x) l at_top :=
@tendsto_at_top_of_add_bdd_above_right' _ _ _ l (λ x, f x + g x) (λ x, -(g x)) (-C)
  (by simp [hg]) (by simp [hf])

lemma tendsto_at_bot_add_right_of_ge' (C : β) (hf : tendsto f l at_bot) (hg : ∀ᶠ x in l, g x ≤ C) :
  tendsto (λ x, f x + g x) l at_bot :=
@tendsto_at_top_add_right_of_le' _ βᵒᵈ _ _ _ _ C hf hg

lemma tendsto_at_top_add_right_of_le (C : β) (hf : tendsto f l at_top) (hg : ∀ x, C ≤ g x) :
  tendsto (λ x, f x + g x) l at_top :=
tendsto_at_top_add_right_of_le' l C hf (univ_mem' hg)

lemma tendsto_at_bot_add_right_of_ge (C : β) (hf : tendsto f l at_bot) (hg : ∀ x, g x ≤ C) :
  tendsto (λ x, f x + g x) l at_bot :=
@tendsto_at_top_add_right_of_le _ βᵒᵈ _ _ _ _ C hf hg

lemma tendsto_at_top_add_const_left (C : β) (hf : tendsto f l at_top) :
  tendsto (λ x, C + f x) l at_top :=
tendsto_at_top_add_left_of_le' l C (univ_mem' $ λ _, le_refl C) hf

lemma tendsto_at_bot_add_const_left (C : β) (hf : tendsto f l at_bot) :
  tendsto (λ x, C + f x) l at_bot :=
@tendsto_at_top_add_const_left _ βᵒᵈ _ _ _ C hf

lemma tendsto_at_top_add_const_right (C : β) (hf : tendsto f l at_top) :
  tendsto (λ x, f x + C) l at_top :=
tendsto_at_top_add_right_of_le' l C hf (univ_mem' $ λ _, le_refl C)

lemma tendsto_at_bot_add_const_right (C : β) (hf : tendsto f l at_bot) :
  tendsto (λ x, f x + C) l at_bot :=
@tendsto_at_top_add_const_right _ βᵒᵈ _ _ _ C hf

lemma tendsto_neg_at_top_at_bot : tendsto (has_neg.neg : β → β) at_top at_bot :=
begin
  simp only [tendsto_at_bot, neg_le],
  exact λ b, eventually_ge_at_top _
end

lemma tendsto_neg_at_bot_at_top : tendsto (has_neg.neg : β → β) at_bot at_top :=
@tendsto_neg_at_top_at_bot βᵒᵈ _

lemma tendsto_at_top_iff_tends_to_neg_at_bot : tendsto f l at_top ↔ tendsto (-f) l at_bot :=
have hf : f = has_neg.neg ∘ -f, { ext, simp, },
⟨tendsto_neg_at_top_at_bot.comp, λ h, hf.symm ▸ tendsto_neg_at_bot_at_top.comp h⟩

lemma tendsto_at_bot_iff_tends_to_neg_at_top : tendsto f l at_bot ↔ tendsto (-f) l at_top :=
@tendsto_at_top_iff_tends_to_neg_at_bot α βᵒᵈ _ l f

end ordered_group

section ordered_semiring

variables [ordered_semiring α] {l : filter β} {f g : β → α}

lemma tendsto_bit1_at_top : tendsto bit1 (at_top : filter α) at_top :=
tendsto_at_top_add_nonneg_right tendsto_bit0_at_top (λ _, zero_le_one)

lemma tendsto.at_top_mul_at_top (hf : tendsto f l at_top) (hg : tendsto g l at_top) :
  tendsto (λ x, f x * g x) l at_top :=
begin
  refine tendsto_at_top_mono' _ _ hg,
  filter_upwards [hg.eventually (eventually_ge_at_top 0), hf.eventually (eventually_ge_at_top 1)]
    with _ using le_mul_of_one_le_left,
end

lemma tendsto_mul_self_at_top : tendsto (λ x : α, x * x) at_top at_top :=
tendsto_id.at_top_mul_at_top tendsto_id

/-- The monomial function `x^n` tends to `+∞` at `+∞` for any positive natural `n`.
A version for positive real powers exists as `tendsto_rpow_at_top`. -/
lemma tendsto_pow_at_top {n : ℕ} (hn : 1 ≤ n) : tendsto (λ x : α, x ^ n) at_top at_top :=
begin
  refine tendsto_at_top_mono' _ ((eventually_ge_at_top 1).mono $ λ x hx, _) tendsto_id,
  simpa only [pow_one] using pow_le_pow hx hn
end

end ordered_semiring

lemma zero_pow_eventually_eq [monoid_with_zero α] :
  (λ n : ℕ, (0 : α) ^ n) =ᶠ[at_top] (λ n, 0) :=
eventually_at_top.2 ⟨1, λ n hn, zero_pow (zero_lt_one.trans_le hn)⟩

section ordered_ring

variables [ordered_ring α] {l : filter β} {f g : β → α}

lemma tendsto.at_top_mul_at_bot (hf : tendsto f l at_top) (hg : tendsto g l at_bot) :
  tendsto (λ x, f x * g x) l at_bot :=
have _ := (hf.at_top_mul_at_top $ tendsto_neg_at_bot_at_top.comp hg),
by simpa only [(∘), neg_mul_eq_mul_neg, neg_neg] using tendsto_neg_at_top_at_bot.comp this

lemma tendsto.at_bot_mul_at_top (hf : tendsto f l at_bot) (hg : tendsto g l at_top) :
  tendsto (λ x, f x * g x) l at_bot :=
have tendsto (λ x, (-f x) * g x) l at_top :=
  ( (tendsto_neg_at_bot_at_top.comp hf).at_top_mul_at_top hg),
by simpa only [(∘), neg_mul_eq_neg_mul, neg_neg] using tendsto_neg_at_top_at_bot.comp this

lemma tendsto.at_bot_mul_at_bot (hf : tendsto f l at_bot) (hg : tendsto g l at_bot) :
  tendsto (λ x, f x * g x) l at_top :=
have tendsto (λ x, (-f x) * (-g x)) l at_top :=
  (tendsto_neg_at_bot_at_top.comp hf).at_top_mul_at_top (tendsto_neg_at_bot_at_top.comp hg),
by simpa only [neg_mul_neg] using this

end ordered_ring

section linear_ordered_add_comm_group

variables [linear_ordered_add_comm_group α]

/-- $\lim_{x\to+\infty}|x|=+\infty$ -/
lemma tendsto_abs_at_top_at_top : tendsto (abs : α → α) at_top at_top :=
tendsto_at_top_mono le_abs_self tendsto_id

/-- $\lim_{x\to-\infty}|x|=+\infty$ -/
lemma tendsto_abs_at_bot_at_top : tendsto (abs : α → α) at_bot at_top :=
tendsto_at_top_mono neg_le_abs_self tendsto_neg_at_bot_at_top

@[simp] lemma comap_abs_at_top : comap (abs : α → α) at_top = at_bot ⊔ at_top :=
begin
  refine le_antisymm (((at_top_basis.comap _).le_basis_iff (at_bot_basis.sup at_top_basis)).2 _)
    (sup_le tendsto_abs_at_bot_at_top.le_comap tendsto_abs_at_top_at_top.le_comap),
  rintro ⟨a, b⟩ -,
  refine ⟨max (-a) b, trivial, λ x hx, _⟩,
  rw [mem_preimage, mem_Ici, le_abs', max_le_iff, ← min_neg_neg, le_min_iff, neg_neg] at hx,
  exact hx.imp and.left and.right
end

end linear_ordered_add_comm_group

section linear_ordered_semiring

variables [linear_ordered_semiring α] {l : filter β} {f : β → α}

lemma tendsto.at_top_of_const_mul {c : α} (hc : 0 < c) (hf : tendsto (λ x, c * f x) l at_top) :
  tendsto f l at_top :=
tendsto_at_top.2 $ λ b, (tendsto_at_top.1 hf (c * b)).mono $ λ x hx, le_of_mul_le_mul_left hx hc

lemma tendsto.at_top_of_mul_const {c : α} (hc : 0 < c) (hf : tendsto (λ x, f x * c) l at_top) :
  tendsto f l at_top :=
tendsto_at_top.2 $ λ b, (tendsto_at_top.1 hf (b * c)).mono $ λ x hx, le_of_mul_le_mul_right hx hc

end linear_ordered_semiring

lemma nonneg_of_eventually_pow_nonneg [linear_ordered_ring α] {a : α}
  (h : ∀ᶠ n in at_top, 0 ≤ a ^ (n : ℕ)) : 0 ≤ a :=
let ⟨n, hn⟩ := (tendsto_bit1_at_top.eventually h).exists in pow_bit1_nonneg_iff.1 hn

section linear_ordered_field

variables [linear_ordered_field α] {l : filter β} {f : β → α} {r : α}

/-- If a function tends to infinity along a filter, then this function multiplied by a positive
constant (on the left) also tends to infinity. For a version working in `ℕ` or `ℤ`, use
`filter.tendsto.const_mul_at_top'` instead. -/
lemma tendsto.const_mul_at_top (hr : 0 < r) (hf : tendsto f l at_top) :
  tendsto (λx, r * f x) l at_top :=
tendsto.at_top_of_const_mul (inv_pos.2 hr) $ by simpa only [inv_mul_cancel_left₀ hr.ne']

/-- If a function tends to infinity along a filter, then this function multiplied by a positive
constant (on the right) also tends to infinity. For a version working in `ℕ` or `ℤ`, use
`filter.tendsto.at_top_mul_const'` instead. -/
lemma tendsto.at_top_mul_const (hr : 0 < r) (hf : tendsto f l at_top) :
  tendsto (λx, f x * r) l at_top :=
by simpa only [mul_comm] using hf.const_mul_at_top hr

/-- If a function tends to infinity along a filter, then this function divided by a positive
constant also tends to infinity. -/
lemma tendsto.at_top_div_const (hr : 0 < r) (hf : tendsto f l at_top) :
  tendsto (λx, f x / r) l at_top :=
by simpa only [div_eq_mul_inv] using hf.at_top_mul_const (inv_pos.2 hr)

/-- If a function tends to infinity along a filter, then this function multiplied by a negative
constant (on the left) tends to negative infinity. -/
lemma tendsto.neg_const_mul_at_top (hr : r < 0) (hf : tendsto f l at_top) :
  tendsto (λ x, r * f x) l at_bot :=
by simpa only [(∘), neg_mul_eq_neg_mul, neg_neg]
  using tendsto_neg_at_top_at_bot.comp (hf.const_mul_at_top (neg_pos.2 hr))

/-- If a function tends to infinity along a filter, then this function multiplied by a negative
constant (on the right) tends to negative infinity. -/
lemma tendsto.at_top_mul_neg_const (hr : r < 0) (hf : tendsto f l at_top) :
  tendsto (λ x, f x * r) l at_bot :=
by simpa only [mul_comm] using hf.neg_const_mul_at_top hr

/-- If a function tends to negative infinity along a filter, then this function multiplied by
a positive constant (on the left) also tends to negative infinity. -/
lemma tendsto.const_mul_at_bot (hr : 0 < r) (hf : tendsto f l at_bot) :
  tendsto (λx, r * f x) l at_bot :=
by simpa only [(∘), neg_mul_eq_mul_neg, neg_neg]
  using tendsto_neg_at_top_at_bot.comp ((tendsto_neg_at_bot_at_top.comp hf).const_mul_at_top hr)

/-- If a function tends to negative infinity along a filter, then this function multiplied by
a positive constant (on the right) also tends to negative infinity. -/
lemma tendsto.at_bot_mul_const (hr : 0 < r) (hf : tendsto f l at_bot) :
  tendsto (λx, f x * r) l at_bot :=
by simpa only [mul_comm] using hf.const_mul_at_bot hr

/-- If a function tends to negative infinity along a filter, then this function divided by
a positive constant also tends to negative infinity. -/
lemma tendsto.at_bot_div_const (hr : 0 < r) (hf : tendsto f l at_bot) :
  tendsto (λx, f x / r) l at_bot :=
by simpa only [div_eq_mul_inv] using hf.at_bot_mul_const (inv_pos.2 hr)

/-- If a function tends to negative infinity along a filter, then this function multiplied by
a negative constant (on the left) tends to positive infinity. -/
lemma tendsto.neg_const_mul_at_bot (hr : r < 0) (hf : tendsto f l at_bot) :
  tendsto (λ x, r * f x) l at_top :=
by simpa only [(∘), neg_mul_eq_neg_mul, neg_neg]
  using tendsto_neg_at_bot_at_top.comp (hf.const_mul_at_bot (neg_pos.2 hr))

/-- If a function tends to negative infinity along a filter, then this function multiplied by
a negative constant (on the right) tends to positive infinity. -/
lemma tendsto.at_bot_mul_neg_const (hr : r < 0) (hf : tendsto f l at_bot) :
  tendsto (λ x, f x * r) l at_top :=
by simpa only [mul_comm] using hf.neg_const_mul_at_bot hr

lemma tendsto_const_mul_pow_at_top {c : α} {n : ℕ}
  (hn : 1 ≤ n) (hc : 0 < c) : tendsto (λ x, c * x^n) at_top at_top :=
tendsto.const_mul_at_top hc (tendsto_pow_at_top hn)

lemma tendsto_const_mul_pow_at_top_iff (c : α) (n : ℕ) :
  tendsto (λ x, c * x^n) at_top at_top ↔ 1 ≤ n ∧ 0 < c :=
begin
  refine ⟨λ h, _, λ h, tendsto_const_mul_pow_at_top h.1 h.2⟩,
  simp only [tendsto_at_top, eventually_at_top] at h,
  have : 0 < c := let ⟨x, hx⟩ := h 1 in
    pos_of_mul_pos_right (lt_of_lt_of_le zero_lt_one (hx (max x 1) (le_max_left x 1)))
    (pow_nonneg (le_trans zero_le_one (le_max_right x 1)) n),
  refine ⟨nat.succ_le_iff.mp (lt_of_le_of_ne (zero_le n) (ne.symm (λ hn, _))), this⟩,
  obtain ⟨x, hx⟩ := h (c + 1),
  specialize hx x le_rfl,
  rw [hn, pow_zero, mul_one, add_le_iff_nonpos_right] at hx,
  exact absurd hx (not_le.mpr zero_lt_one),
end

lemma tendsto_neg_const_mul_pow_at_top {c : α} {n : ℕ}
  (hn : 1 ≤ n) (hc : c < 0) : tendsto (λ x, c * x^n) at_top at_bot :=
tendsto.neg_const_mul_at_top hc (tendsto_pow_at_top hn)

lemma tendsto_neg_const_mul_pow_at_top_iff (c : α) (n : ℕ) :
  tendsto (λ x, c * x^n) at_top at_bot ↔ 1 ≤ n ∧ c < 0 :=
begin
  refine ⟨λ h, _, λ h, tendsto_neg_const_mul_pow_at_top h.1 h.2⟩,
  simp only [tendsto_at_bot, eventually_at_top] at h,
  have : c < 0 := let ⟨x, hx⟩ := h (-1) in
    neg_of_mul_neg_right (lt_of_le_of_lt (hx (max x 1) (le_max_left x 1)) (by simp [zero_lt_one]))
    (pow_nonneg (le_trans zero_le_one (le_max_right x 1)) n),
  refine ⟨nat.succ_le_iff.mp (lt_of_le_of_ne (zero_le n) (ne.symm (λ hn, _))), this⟩,
  obtain ⟨x, hx⟩ := h (c - 1),
  specialize hx x le_rfl,
  rw [hn, pow_zero, mul_one, le_sub, sub_self] at hx,
  exact absurd hx (not_le.mpr zero_lt_one),
end

end linear_ordered_field

open_locale filter

lemma tendsto_at_top' [nonempty α] [semilattice_sup α] {f : α → β} {l : filter β} :
  tendsto f at_top l ↔ (∀s ∈ l, ∃a, ∀b≥a, f b ∈ s) :=
by simp only [tendsto_def, mem_at_top_sets]; refl

lemma tendsto_at_bot' [nonempty α] [semilattice_inf α] {f : α → β} {l : filter β} :
  tendsto f at_bot l ↔ (∀s ∈ l, ∃a, ∀b≤a, f b ∈ s) :=
@tendsto_at_top' αᵒᵈ _ _ _ _ _

theorem tendsto_at_top_principal [nonempty β] [semilattice_sup β] {f : β → α} {s : set α} :
  tendsto f at_top (𝓟 s) ↔ ∃N, ∀n≥N, f n ∈ s :=
by rw [tendsto_iff_comap, comap_principal, le_principal_iff, mem_at_top_sets]; refl

theorem tendsto_at_bot_principal [nonempty β] [semilattice_inf β] {f : β → α} {s : set α} :
  tendsto f at_bot (𝓟 s) ↔ ∃N, ∀n≤N, f n ∈ s :=
@tendsto_at_top_principal _ βᵒᵈ _ _ _ _

/-- A function `f` grows to `+∞` independent of an order-preserving embedding `e`. -/
lemma tendsto_at_top_at_top [nonempty α] [semilattice_sup α] [preorder β] {f : α → β} :
  tendsto f at_top at_top ↔ ∀ b : β, ∃ i : α, ∀ a : α, i ≤ a → b ≤ f a :=
iff.trans tendsto_infi $ forall_congr $ assume b, tendsto_at_top_principal

lemma tendsto_at_top_at_bot [nonempty α] [semilattice_sup α] [preorder β] {f : α → β} :
  tendsto f at_top at_bot ↔ ∀ (b : β), ∃ (i : α), ∀ (a : α), i ≤ a → f a ≤ b :=
@tendsto_at_top_at_top α βᵒᵈ _ _ _ f

lemma tendsto_at_bot_at_top [nonempty α] [semilattice_inf α] [preorder β] {f : α → β} :
  tendsto f at_bot at_top ↔ ∀ (b : β), ∃ (i : α), ∀ (a : α), a ≤ i → b ≤ f a :=
@tendsto_at_top_at_top αᵒᵈ β _ _ _ f

lemma tendsto_at_bot_at_bot [nonempty α] [semilattice_inf α] [preorder β] {f : α → β} :
  tendsto f at_bot at_bot ↔ ∀ (b : β), ∃ (i : α), ∀ (a : α), a ≤ i → f a ≤ b :=
@tendsto_at_top_at_top αᵒᵈ βᵒᵈ _ _ _ f

lemma tendsto_at_top_at_top_of_monotone [preorder α] [preorder β] {f : α → β} (hf : monotone f)
  (h : ∀ b, ∃ a, b ≤ f a) :
  tendsto f at_top at_top :=
tendsto_infi.2 $ λ b, tendsto_principal.2 $ let ⟨a, ha⟩ := h b in
mem_of_superset (mem_at_top a) $ λ a' ha', le_trans ha (hf ha')

lemma tendsto_at_bot_at_bot_of_monotone [preorder α] [preorder β] {f : α → β} (hf : monotone f)
  (h : ∀ b, ∃ a, f a ≤ b) :
  tendsto f at_bot at_bot :=
tendsto_infi.2 $ λ b, tendsto_principal.2 $ let ⟨a, ha⟩ := h b in
mem_of_superset (mem_at_bot a) $ λ a' ha', le_trans (hf ha') ha

lemma tendsto_at_top_at_top_iff_of_monotone [nonempty α] [semilattice_sup α] [preorder β]
  {f : α → β} (hf : monotone f) :
  tendsto f at_top at_top ↔ ∀ b : β, ∃ a : α, b ≤ f a :=
tendsto_at_top_at_top.trans $ forall_congr $ λ b, exists_congr $ λ a,
  ⟨λ h, h a (le_refl a), λ h a' ha', le_trans h $ hf ha'⟩

lemma tendsto_at_bot_at_bot_iff_of_monotone [nonempty α] [semilattice_inf α] [preorder β]
  {f : α → β} (hf : monotone f) :
  tendsto f at_bot at_bot ↔ ∀ b : β, ∃ a : α, f a ≤ b :=
tendsto_at_bot_at_bot.trans $ forall_congr $ λ b, exists_congr $ λ a,
  ⟨λ h, h a (le_refl a), λ h a' ha', le_trans (hf ha') h⟩

alias tendsto_at_top_at_top_of_monotone ← monotone.tendsto_at_top_at_top
alias tendsto_at_bot_at_bot_of_monotone ← monotone.tendsto_at_bot_at_bot
alias tendsto_at_top_at_top_iff_of_monotone ← monotone.tendsto_at_top_at_top_iff
alias tendsto_at_bot_at_bot_iff_of_monotone ← monotone.tendsto_at_bot_at_bot_iff

lemma comap_embedding_at_top [preorder β] [preorder γ] {e : β → γ}
  (hm : ∀b₁ b₂, e b₁ ≤ e b₂ ↔ b₁ ≤ b₂) (hu : ∀c, ∃b, c ≤ e b) :
  comap e at_top = at_top :=
le_antisymm
  (le_infi $ λ b, le_principal_iff.2 $ mem_comap.2 ⟨Ici (e b), mem_at_top _, λ x, (hm _ _).1⟩)
  (tendsto_at_top_at_top_of_monotone (λ _ _, (hm _ _).2) hu).le_comap

lemma comap_embedding_at_bot [preorder β] [preorder γ] {e : β → γ}
  (hm : ∀ b₁ b₂, e b₁ ≤ e b₂ ↔ b₁ ≤ b₂) (hu : ∀c, ∃b, e b ≤ c) :
  comap e at_bot = at_bot :=
@comap_embedding_at_top βᵒᵈ γᵒᵈ _ _ e (function.swap hm) hu

lemma tendsto_at_top_embedding [preorder β] [preorder γ]
  {f : α → β} {e : β → γ} {l : filter α}
  (hm : ∀b₁ b₂, e b₁ ≤ e b₂ ↔ b₁ ≤ b₂) (hu : ∀c, ∃b, c ≤ e b) :
  tendsto (e ∘ f) l at_top ↔ tendsto f l at_top :=
by rw [← comap_embedding_at_top hm hu, tendsto_comap_iff]

/-- A function `f` goes to `-∞` independent of an order-preserving embedding `e`. -/
lemma tendsto_at_bot_embedding [preorder β] [preorder γ]
  {f : α → β} {e : β → γ} {l : filter α}
  (hm : ∀b₁ b₂, e b₁ ≤ e b₂ ↔ b₁ ≤ b₂) (hu : ∀c, ∃b, e b ≤ c) :
  tendsto (e ∘ f) l at_bot ↔ tendsto f l at_bot :=
@tendsto_at_top_embedding α βᵒᵈ γᵒᵈ _ _ f e l (function.swap hm) hu

lemma tendsto_finset_range : tendsto finset.range at_top at_top :=
finset.range_mono.tendsto_at_top_at_top finset.exists_nat_subset_range

lemma at_top_finset_eq_infi : (at_top : filter $ finset α) = ⨅ x : α, 𝓟 (Ici {x}) :=
begin
  refine le_antisymm (le_infi (λ i, le_principal_iff.2 $ mem_at_top {i})) _,
  refine le_infi (λ s, le_principal_iff.2 $ mem_infi_of_Inter s.finite_to_set
                  (λ i, mem_principal_self _) _),
  simp only [subset_def, mem_Inter, set_coe.forall, mem_Ici, finset.le_iff_subset,
    finset.mem_singleton, finset.subset_iff, forall_eq], dsimp,
  exact λ t, id
end

/-- If `f` is a monotone sequence of `finset`s and each `x` belongs to one of `f n`, then
`tendsto f at_top at_top`. -/
lemma tendsto_at_top_finset_of_monotone [preorder β]
  {f : β → finset α} (h : monotone f) (h' : ∀ x : α, ∃ n, x ∈ f n) :
  tendsto f at_top at_top :=
begin
  simp only [at_top_finset_eq_infi, tendsto_infi, tendsto_principal],
  intro a,
  rcases h' a with ⟨b, hb⟩,
  exact eventually.mono (mem_at_top b)
    (λ b' hb', le_trans (finset.singleton_subset_iff.2 hb) (h hb')),
end

alias tendsto_at_top_finset_of_monotone ← monotone.tendsto_at_top_finset

lemma tendsto_finset_image_at_top_at_top {i : β → γ} {j : γ → β} (h : function.left_inverse j i) :
  tendsto (finset.image j) at_top at_top :=
(finset.image_mono j).tendsto_at_top_finset $ assume a,
  ⟨{i a}, by simp only [finset.image_singleton, h a, finset.mem_singleton]⟩

lemma tendsto_finset_preimage_at_top_at_top {f : α → β} (hf : function.injective f) :
  tendsto (λ s : finset β, s.preimage f (hf.inj_on _)) at_top at_top :=
(finset.monotone_preimage hf).tendsto_at_top_finset $
  λ x, ⟨{f x}, finset.mem_preimage.2 $ finset.mem_singleton_self _⟩

lemma prod_at_top_at_top_eq {β₁ β₂ : Type*} [semilattice_sup β₁] [semilattice_sup β₂] :
  (at_top : filter β₁) ×ᶠ (at_top : filter β₂) = (at_top : filter (β₁ × β₂)) :=
begin
  casesI (is_empty_or_nonempty β₁).symm,
  casesI (is_empty_or_nonempty β₂).symm,
  { simp [at_top, prod_infi_left, prod_infi_right, infi_prod],
    exact infi_comm, },
  { simp only [at_top.filter_eq_bot_of_is_empty, prod_bot] },
  { simp only [at_top.filter_eq_bot_of_is_empty, bot_prod] },
end

lemma prod_at_bot_at_bot_eq {β₁ β₂ : Type*} [semilattice_inf β₁] [semilattice_inf β₂] :
  (at_bot : filter β₁) ×ᶠ (at_bot : filter β₂) = (at_bot : filter (β₁ × β₂)) :=
@prod_at_top_at_top_eq β₁ᵒᵈ β₂ᵒᵈ _ _

lemma prod_map_at_top_eq {α₁ α₂ β₁ β₂ : Type*} [semilattice_sup β₁] [semilattice_sup β₂]
  (u₁ : β₁ → α₁) (u₂ : β₂ → α₂) :
  (map u₁ at_top) ×ᶠ (map u₂ at_top) = map (prod.map u₁ u₂) at_top :=
by rw [prod_map_map_eq, prod_at_top_at_top_eq, prod.map_def]

lemma prod_map_at_bot_eq {α₁ α₂ β₁ β₂ : Type*} [semilattice_inf β₁] [semilattice_inf β₂]
  (u₁ : β₁ → α₁) (u₂ : β₂ → α₂) :
  (map u₁ at_bot) ×ᶠ (map u₂ at_bot) = map (prod.map u₁ u₂) at_bot :=
@prod_map_at_top_eq _ _ β₁ᵒᵈ β₂ᵒᵈ _ _ _ _

lemma tendsto.subseq_mem {F : filter α} {V : ℕ → set α} (h : ∀ n, V n ∈ F) {u : ℕ → α}
  (hu : tendsto u at_top F) : ∃ φ : ℕ → ℕ, strict_mono φ ∧ ∀ n, u (φ n) ∈ V n :=
extraction_forall_of_eventually' (λ n, tendsto_at_top'.mp hu _ (h n) : ∀ n, ∃ N, ∀ k ≥ N, u k ∈ V n)

lemma tendsto_at_bot_diagonal [semilattice_inf α] : tendsto (λ a : α, (a, a)) at_bot at_bot :=
by { rw ← prod_at_bot_at_bot_eq, exact tendsto_id.prod_mk tendsto_id }

lemma tendsto_at_top_diagonal [semilattice_sup α] : tendsto (λ a : α, (a, a)) at_top at_top :=
by { rw ← prod_at_top_at_top_eq, exact tendsto_id.prod_mk tendsto_id }

lemma tendsto.prod_map_prod_at_bot [semilattice_inf γ] {F : filter α} {G : filter β}
  {f : α → γ} {g : β → γ} (hf : tendsto f F at_bot) (hg : tendsto g G at_bot) :
  tendsto (prod.map f g) (F ×ᶠ G) at_bot :=
by { rw ← prod_at_bot_at_bot_eq, exact hf.prod_map hg, }

lemma tendsto.prod_map_prod_at_top [semilattice_sup γ] {F : filter α} {G : filter β}
  {f : α → γ} {g : β → γ} (hf : tendsto f F at_top) (hg : tendsto g G at_top) :
  tendsto (prod.map f g) (F ×ᶠ G) at_top :=
by { rw ← prod_at_top_at_top_eq, exact hf.prod_map hg, }

lemma tendsto.prod_at_bot [semilattice_inf α] [semilattice_inf γ]
  {f g : α → γ} (hf : tendsto f at_bot at_bot) (hg : tendsto g at_bot at_bot) :
  tendsto (prod.map f g) at_bot at_bot :=
by { rw ← prod_at_bot_at_bot_eq, exact hf.prod_map_prod_at_bot hg, }

lemma tendsto.prod_at_top [semilattice_sup α] [semilattice_sup γ]
  {f g : α → γ} (hf : tendsto f at_top at_top) (hg : tendsto g at_top at_top) :
  tendsto (prod.map f g) at_top at_top :=
by { rw ← prod_at_top_at_top_eq, exact hf.prod_map_prod_at_top hg, }

lemma eventually_at_bot_prod_self [semilattice_inf α] [nonempty α] {p : α × α → Prop} :
  (∀ᶠ x in at_bot, p x) ↔ (∃ a, ∀ k l, k ≤ a → l ≤ a → p (k, l)) :=
by simp [← prod_at_bot_at_bot_eq, at_bot_basis.prod_self.eventually_iff]

lemma eventually_at_top_prod_self [semilattice_sup α] [nonempty α] {p : α × α → Prop} :
  (∀ᶠ x in at_top, p x) ↔ (∃ a, ∀ k l, a ≤ k → a ≤ l → p (k, l)) :=
by simp [← prod_at_top_at_top_eq, at_top_basis.prod_self.eventually_iff]

lemma eventually_at_bot_prod_self' [semilattice_inf α] [nonempty α] {p : α × α → Prop} :
  (∀ᶠ x in at_bot, p x) ↔ (∃ a, ∀ k ≤ a, ∀ l ≤ a, p (k, l)) :=
begin
  rw filter.eventually_at_bot_prod_self,
  apply exists_congr,
  tauto,
end

lemma eventually_at_top_prod_self' [semilattice_sup α] [nonempty α] {p : α × α → Prop} :
  (∀ᶠ x in at_top, p x) ↔ (∃ a, ∀ k ≥ a, ∀ l ≥ a, p (k, l)) :=
begin
  rw filter.eventually_at_top_prod_self,
  apply exists_congr,
  tauto,
end

lemma eventually_at_top_curry [semilattice_sup α] [semilattice_sup β] {p : α × β → Prop}
  (hp : ∀ᶠ (x : α × β) in filter.at_top, p x) :
  ∀ᶠ k in at_top, ∀ᶠ l in at_top, p (k, l) :=
begin
  rw ← prod_at_top_at_top_eq at hp,
  exact hp.curry,
end

lemma eventually_at_bot_curry [semilattice_inf α] [semilattice_inf β] {p : α × β → Prop}
  (hp : ∀ᶠ (x : α × β) in filter.at_bot, p x) :
  ∀ᶠ k in at_bot, ∀ᶠ l in at_bot, p (k, l) :=
@eventually_at_top_curry αᵒᵈ βᵒᵈ _ _ _ hp

/-- A function `f` maps upwards closed sets (at_top sets) to upwards closed sets when it is a
Galois insertion. The Galois "insertion" and "connection" is weakened to only require it to be an
insertion and a connetion above `b'`. -/
lemma map_at_top_eq_of_gc [semilattice_sup α] [semilattice_sup β] {f : α → β} (g : β → α) (b' : β)
  (hf : monotone f) (gc : ∀a, ∀b≥b', f a ≤ b ↔ a ≤ g b) (hgi : ∀b≥b', b ≤ f (g b)) :
  map f at_top = at_top :=
begin
  refine le_antisymm
    (hf.tendsto_at_top_at_top $ λ b, ⟨g (b ⊔ b'), le_sup_left.trans $ hgi _ le_sup_right⟩) _,
  rw [@map_at_top_eq _ _ ⟨g b'⟩],
  refine le_infi (λ a, infi_le_of_le (f a ⊔ b') $ principal_mono.2 $ λ b hb, _),
  rw [mem_Ici, sup_le_iff] at hb,
  exact ⟨g b, (gc _ _ hb.2).1 hb.1, le_antisymm ((gc _ _ hb.2).2 le_rfl) (hgi _ hb.2)⟩
end

lemma map_at_bot_eq_of_gc [semilattice_inf α] [semilattice_inf β] {f : α → β} (g : β → α) (b' : β)
  (hf : monotone f) (gc : ∀a, ∀b≤b', b ≤ f a ↔ g b ≤ a) (hgi : ∀b≤b', f (g b) ≤ b) :
  map f at_bot = at_bot :=
@map_at_top_eq_of_gc αᵒᵈ βᵒᵈ _ _ _ _ _ hf.dual gc hgi

lemma map_coe_at_top_of_Ici_subset [semilattice_sup α] {a : α} {s : set α} (h : Ici a ⊆ s) :
  map (coe : s → α) at_top = at_top :=
begin
  have : directed (≥) (λ x : s, 𝓟 (Ici x)),
  { intros x y,
    use ⟨x ⊔ y ⊔ a, h le_sup_right⟩,
    simp only [ge_iff_le, principal_mono, Ici_subset_Ici, ← subtype.coe_le_coe, subtype.coe_mk],
    exact ⟨le_sup_left.trans le_sup_left, le_sup_right.trans le_sup_left⟩ },
  haveI : nonempty s := ⟨⟨a, h le_rfl⟩⟩,
  simp only [le_antisymm_iff, at_top, le_infi_iff, le_principal_iff, mem_map, mem_set_of_eq,
    map_infi_eq this, map_principal],
  split,
  { intro x,
    refine mem_of_superset (mem_infi_of_mem ⟨x ⊔ a, h le_sup_right⟩ (mem_principal_self _)) _,
    rintro _ ⟨y, hy, rfl⟩,
    exact le_trans le_sup_left (subtype.coe_le_coe.2 hy) },
  { intro x,
    filter_upwards [mem_at_top (↑x ⊔ a)] with b hb,
    exact ⟨⟨b, h $ le_sup_right.trans hb⟩, subtype.coe_le_coe.1 (le_sup_left.trans hb), rfl⟩, },
end

/-- The image of the filter `at_top` on `Ici a` under the coercion equals `at_top`. -/
@[simp] lemma map_coe_Ici_at_top [semilattice_sup α] (a : α) :
  map (coe : Ici a → α) at_top = at_top :=
map_coe_at_top_of_Ici_subset (subset.refl _)

/-- The image of the filter `at_top` on `Ioi a` under the coercion equals `at_top`. -/
@[simp] lemma map_coe_Ioi_at_top [semilattice_sup α] [no_max_order α] (a : α) :
  map (coe : Ioi a → α) at_top = at_top :=
let ⟨b, hb⟩ := exists_gt a in map_coe_at_top_of_Ici_subset $ Ici_subset_Ioi.2 hb

/-- The `at_top` filter for an open interval `Ioi a` comes from the `at_top` filter in the ambient
order. -/
lemma at_top_Ioi_eq [semilattice_sup α] (a : α) :
  at_top = comap (coe : Ioi a → α) at_top :=
begin
  nontriviality,
  rcases nontrivial_iff_nonempty.1 ‹_› with ⟨b, hb⟩,
  rw [← map_coe_at_top_of_Ici_subset (Ici_subset_Ioi.2 hb), comap_map subtype.coe_injective]
end

/-- The `at_top` filter for an open interval `Ici a` comes from the `at_top` filter in the ambient
order. -/
lemma at_top_Ici_eq [semilattice_sup α] (a : α) :
  at_top = comap (coe : Ici a → α) at_top :=
by rw [← map_coe_Ici_at_top a, comap_map subtype.coe_injective]

/-- The `at_bot` filter for an open interval `Iio a` comes from the `at_bot` filter in the ambient
order. -/
@[simp] lemma map_coe_Iio_at_bot [semilattice_inf α] [no_min_order α] (a : α) :
  map (coe : Iio a → α) at_bot = at_bot :=
@map_coe_Ioi_at_top αᵒᵈ _ _ _

/-- The `at_bot` filter for an open interval `Iio a` comes from the `at_bot` filter in the ambient
order. -/
lemma at_bot_Iio_eq [semilattice_inf α] (a : α) : at_bot = comap (coe : Iio a → α) at_bot :=
@at_top_Ioi_eq αᵒᵈ _ _

/-- The `at_bot` filter for an open interval `Iic a` comes from the `at_bot` filter in the ambient
order. -/
@[simp] lemma map_coe_Iic_at_bot [semilattice_inf α] (a : α) :
  map (coe : Iic a → α) at_bot = at_bot :=
@map_coe_Ici_at_top αᵒᵈ _ _

/-- The `at_bot` filter for an open interval `Iic a` comes from the `at_bot` filter in the ambient
order. -/
lemma at_bot_Iic_eq [semilattice_inf α] (a : α) :
  at_bot = comap (coe : Iic a → α) at_bot :=
@at_top_Ici_eq αᵒᵈ _ _

lemma tendsto_Ioi_at_top [semilattice_sup α] {a : α} {f : β → Ioi a}
  {l : filter β} :
  tendsto f l at_top ↔ tendsto (λ x, (f x : α)) l at_top :=
by rw [at_top_Ioi_eq, tendsto_comap_iff]

lemma tendsto_Iio_at_bot [semilattice_inf α] {a : α} {f : β → Iio a}
  {l : filter β} :
  tendsto f l at_bot ↔ tendsto (λ x, (f x : α)) l at_bot :=
by rw [at_bot_Iio_eq, tendsto_comap_iff]

lemma tendsto_Ici_at_top [semilattice_sup α] {a : α} {f : β → Ici a} {l : filter β} :
  tendsto f l at_top ↔ tendsto (λ x, (f x : α)) l at_top :=
by rw [at_top_Ici_eq, tendsto_comap_iff]

lemma tendsto_Iic_at_bot [semilattice_inf α] {a : α} {f : β → Iic a} {l : filter β} :
  tendsto f l at_bot ↔ tendsto (λ x, (f x : α)) l at_bot :=
by rw [at_bot_Iic_eq, tendsto_comap_iff]

@[simp] lemma tendsto_comp_coe_Ioi_at_top [semilattice_sup α] [no_max_order α] {a : α}
  {f : α → β} {l : filter β} :
  tendsto (λ x : Ioi a, f x) at_top l ↔ tendsto f at_top l :=
by rw [← map_coe_Ioi_at_top a, tendsto_map'_iff]

@[simp] lemma tendsto_comp_coe_Ici_at_top [semilattice_sup α] {a : α}
  {f : α → β} {l : filter β} :
  tendsto (λ x : Ici a, f x) at_top l ↔ tendsto f at_top l :=
by rw [← map_coe_Ici_at_top a, tendsto_map'_iff]

@[simp] lemma tendsto_comp_coe_Iio_at_bot [semilattice_inf α] [no_min_order α] {a : α}
  {f : α → β} {l : filter β} :
  tendsto (λ x : Iio a, f x) at_bot l ↔ tendsto f at_bot l :=
by rw [← map_coe_Iio_at_bot a, tendsto_map'_iff]

@[simp] lemma tendsto_comp_coe_Iic_at_bot [semilattice_inf α] {a : α}
  {f : α → β} {l : filter β} :
  tendsto (λ x : Iic a, f x) at_bot l ↔ tendsto f at_bot l :=
by rw [← map_coe_Iic_at_bot a, tendsto_map'_iff]

lemma map_add_at_top_eq_nat (k : ℕ) : map (λa, a + k) at_top = at_top :=
map_at_top_eq_of_gc (λa, a - k) k
  (assume a b h, add_le_add_right h k)
  (assume a b h, (le_tsub_iff_right h).symm)
  (assume a h, by rw [tsub_add_cancel_of_le h])

lemma map_sub_at_top_eq_nat (k : ℕ) : map (λa, a - k) at_top = at_top :=
map_at_top_eq_of_gc (λa, a + k) 0
  (assume a b h, tsub_le_tsub_right h _)
  (assume a b _, tsub_le_iff_right)
  (assume b _, by rw [add_tsub_cancel_right])

lemma tendsto_add_at_top_nat (k : ℕ) : tendsto (λa, a + k) at_top at_top :=
le_of_eq (map_add_at_top_eq_nat k)

lemma tendsto_sub_at_top_nat (k : ℕ) : tendsto (λa, a - k) at_top at_top :=
le_of_eq (map_sub_at_top_eq_nat k)

lemma tendsto_add_at_top_iff_nat {f : ℕ → α} {l : filter α} (k : ℕ) :
  tendsto (λn, f (n + k)) at_top l ↔ tendsto f at_top l :=
show tendsto (f ∘ (λn, n + k)) at_top l ↔ tendsto f at_top l,
  by rw [← tendsto_map'_iff, map_add_at_top_eq_nat]

lemma map_div_at_top_eq_nat (k : ℕ) (hk : 0 < k) : map (λa, a / k) at_top = at_top :=
map_at_top_eq_of_gc (λb, b * k + (k - 1)) 1
  (assume a b h, nat.div_le_div_right h)
  (assume a b _,
    calc a / k ≤ b ↔ a / k < b + 1 : by rw [← nat.succ_eq_add_one, nat.lt_succ_iff]
      ... ↔ a < (b + 1) * k : nat.div_lt_iff_lt_mul hk
      ... ↔ _ :
      begin
        cases k,
        exact (lt_irrefl _ hk).elim,
        rw [add_mul, one_mul, nat.succ_sub_succ_eq_sub,
          tsub_zero, nat.add_succ, nat.lt_succ_iff],
      end)
  (assume b _,
    calc b = (b * k) / k : by rw [nat.mul_div_cancel b hk]
      ... ≤ (b * k + (k - 1)) / k : nat.div_le_div_right $ nat.le_add_right _ _)

/-- If `u` is a monotone function with linear ordered codomain and the range of `u` is not bounded
above, then `tendsto u at_top at_top`. -/
lemma tendsto_at_top_at_top_of_monotone' [preorder ι] [linear_order α]
  {u : ι → α} (h : monotone u) (H : ¬bdd_above (range u)) :
  tendsto u at_top at_top :=
begin
  apply h.tendsto_at_top_at_top,
  intro b,
  rcases not_bdd_above_iff.1 H b with ⟨_, ⟨N, rfl⟩, hN⟩,
  exact ⟨N, le_of_lt hN⟩,
end

/-- If `u` is a monotone function with linear ordered codomain and the range of `u` is not bounded
below, then `tendsto u at_bot at_bot`. -/
lemma tendsto_at_bot_at_bot_of_monotone' [preorder ι] [linear_order α]
  {u : ι → α} (h : monotone u) (H : ¬bdd_below (range u)) :
  tendsto u at_bot at_bot :=
@tendsto_at_top_at_top_of_monotone' ιᵒᵈ αᵒᵈ _ _ _ h.dual H

lemma unbounded_of_tendsto_at_top [nonempty α] [semilattice_sup α] [preorder β] [no_max_order β]
  {f : α → β} (h : tendsto f at_top at_top) :
  ¬ bdd_above (range f) :=
begin
  rintros ⟨M, hM⟩,
  cases mem_at_top_sets.mp (h $ Ioi_mem_at_top M) with a ha,
  apply lt_irrefl M,
  calc
  M < f a : ha a le_rfl
  ... ≤ M : hM (set.mem_range_self a)
end

lemma unbounded_of_tendsto_at_bot [nonempty α] [semilattice_sup α] [preorder β] [no_min_order β]
  {f : α → β} (h : tendsto f at_top at_bot) :
  ¬ bdd_below (range f) :=
@unbounded_of_tendsto_at_top _ βᵒᵈ _ _ _ _ _ h

lemma unbounded_of_tendsto_at_top' [nonempty α] [semilattice_inf α] [preorder β] [no_max_order β]
  {f : α → β} (h : tendsto f at_bot at_top) :
  ¬ bdd_above (range f) :=
@unbounded_of_tendsto_at_top αᵒᵈ _ _ _ _ _ _ h

lemma unbounded_of_tendsto_at_bot' [nonempty α] [semilattice_inf α] [preorder β] [no_min_order β]
  {f : α → β} (h : tendsto f at_bot at_bot) :
  ¬ bdd_below (range f) :=
@unbounded_of_tendsto_at_top αᵒᵈ βᵒᵈ _ _ _ _ _ h

/-- If a monotone function `u : ι → α` tends to `at_top` along *some* non-trivial filter `l`, then
it tends to `at_top` along `at_top`. -/
lemma tendsto_at_top_of_monotone_of_filter [preorder ι] [preorder α] {l : filter ι}
  {u : ι → α} (h : monotone u) [ne_bot l] (hu : tendsto u l at_top) :
  tendsto u at_top at_top :=
h.tendsto_at_top_at_top $ λ b, (hu.eventually (mem_at_top b)).exists

/-- If a monotone function `u : ι → α` tends to `at_bot` along *some* non-trivial filter `l`, then
it tends to `at_bot` along `at_bot`. -/
lemma tendsto_at_bot_of_monotone_of_filter [preorder ι] [preorder α] {l : filter ι}
  {u : ι → α} (h : monotone u) [ne_bot l] (hu : tendsto u l at_bot) :
  tendsto u at_bot at_bot :=
@tendsto_at_top_of_monotone_of_filter ιᵒᵈ αᵒᵈ _ _ _ _ h.dual _ hu

lemma tendsto_at_top_of_monotone_of_subseq [preorder ι] [preorder α] {u : ι → α}
  {φ : ι' → ι} (h : monotone u) {l : filter ι'} [ne_bot l]
  (H : tendsto (u ∘ φ) l at_top) :
  tendsto u at_top at_top :=
tendsto_at_top_of_monotone_of_filter h (tendsto_map' H)

lemma tendsto_at_bot_of_monotone_of_subseq [preorder ι] [preorder α] {u : ι → α}
  {φ : ι' → ι} (h : monotone u) {l : filter ι'} [ne_bot l]
  (H : tendsto (u ∘ φ) l at_bot) :
  tendsto u at_bot at_bot :=
tendsto_at_bot_of_monotone_of_filter h (tendsto_map' H)

/-- Let `f` and `g` be two maps to the same commutative monoid. This lemma gives a sufficient
condition for comparison of the filter `at_top.map (λ s, ∏ b in s, f b)` with
`at_top.map (λ s, ∏ b in s, g b)`. This is useful to compare the set of limit points of
`Π b in s, f b` as `s → at_top` with the similar set for `g`. -/
@[to_additive "Let `f` and `g` be two maps to the same commutative additive monoid. This lemma gives
a sufficient condition for comparison of the filter `at_top.map (λ s, ∑ b in s, f b)` with
`at_top.map (λ s, ∑ b in s, g b)`. This is useful to compare the set of limit points of
`∑ b in s, f b` as `s → at_top` with the similar set for `g`."]
lemma map_at_top_finset_prod_le_of_prod_eq [comm_monoid α] {f : β → α} {g : γ → α}
  (h_eq : ∀u:finset γ, ∃v:finset β, ∀v', v ⊆ v' → ∃u', u ⊆ u' ∧ ∏ x in u', g x = ∏ b in v', f b) :
  at_top.map (λs:finset β, ∏ b in s, f b) ≤ at_top.map (λs:finset γ, ∏ x in s, g x) :=
by rw [map_at_top_eq, map_at_top_eq];
from (le_infi $ assume b, let ⟨v, hv⟩ := h_eq b in infi_le_of_le v $
  by simp [set.image_subset_iff]; exact hv)

lemma has_antitone_basis.eventually_subset [preorder ι] {l : filter α}
  {s : ι → set α} (hl : l.has_antitone_basis s) {t : set α} (ht : t ∈ l) :
  ∀ᶠ i in at_top, s i ⊆ t :=
let ⟨i, _, hi⟩ := hl.to_has_basis.mem_iff.1 ht
in (eventually_ge_at_top i).mono $ λ j hj, (hl.antitone hj).trans hi

protected lemma has_antitone_basis.tendsto [preorder ι] {l : filter α}
  {s : ι → set α} (hl : l.has_antitone_basis s) {φ : ι → α}
  (h : ∀ i : ι, φ i ∈ s i) : tendsto φ at_top l  :=
λ t ht, mem_map.2 $ (hl.eventually_subset ht).mono $ λ i hi, hi (h i)

lemma has_antitone_basis.comp_mono [semilattice_sup ι] [nonempty ι] [preorder ι'] {l : filter α}
  {s : ι' → set α} (hs : l.has_antitone_basis s)
  {φ : ι → ι'} (φ_mono : monotone φ) (hφ : tendsto φ at_top at_top) :
  l.has_antitone_basis (s ∘ φ) :=
⟨hs.to_has_basis.to_has_basis
  (λ n hn, (hφ.eventually (eventually_ge_at_top n)).exists.imp $ λ m hm, ⟨trivial, hs.antitone hm⟩)
  (λ n hn, ⟨φ n, trivial, subset.rfl⟩), hs.antitone.comp_monotone φ_mono⟩

lemma has_antitone_basis.comp_strict_mono {l : filter α} {s : ℕ → set α}
  (hs : l.has_antitone_basis s) {φ : ℕ → ℕ} (hφ : strict_mono φ) :
  l.has_antitone_basis (s ∘ φ) :=
hs.comp_mono hφ.monotone hφ.tendsto_at_top

/-- Given an antitone basis `s : ℕ → set α` of a filter, extract an antitone subbasis `s ∘ φ`,
`φ : ℕ → ℕ`, such that `m < n` implies `r (φ m) (φ n)`. This lemma can be used to extract an
antitone basis with basis sets decreasing "sufficiently fast". -/
lemma has_antitone_basis.subbasis_with_rel {f : filter α} {s : ℕ → set α}
  (hs : f.has_antitone_basis s) {r : ℕ → ℕ → Prop} (hr : ∀ m, ∀ᶠ n in at_top, r m n) :
  ∃ φ : ℕ → ℕ, strict_mono φ ∧ (∀ ⦃m n⦄, m < n → r (φ m) (φ n)) ∧ f.has_antitone_basis (s ∘ φ) :=
begin
  suffices : ∃ φ : ℕ → ℕ, strict_mono φ ∧ ∀ m n, m < n → r (φ m) (φ n),
  { rcases this with ⟨φ, hφ, hrφ⟩,
    exact ⟨φ, hφ, hrφ, hs.comp_strict_mono hφ⟩ },
  have : ∀ t : set ℕ, t.finite → ∀ᶠ n in at_top, ∀ m ∈ t, m < n ∧ r m n,
    from λ t ht, (eventually_all_finite ht).2 (λ m hm, (eventually_gt_at_top m).and (hr _)),
  rcases seq_of_forall_finite_exists (λ t ht, (this t ht).exists) with ⟨φ, hφ⟩,
  simp only [ball_image_iff, forall_and_distrib, mem_Iio] at hφ,
  exact ⟨φ, forall_swap.2 hφ.1, forall_swap.2 hφ.2⟩
end

/-- If `f` is a nontrivial countably generated filter, then there exists a sequence that converges
to `f`. -/
lemma exists_seq_tendsto (f : filter α) [is_countably_generated f] [ne_bot f] :
  ∃ x : ℕ → α, tendsto x at_top f :=
begin
  obtain ⟨B, h⟩ := f.exists_antitone_basis,
  choose x hx using λ n, filter.nonempty_of_mem (h.mem n),
  exact ⟨x, h.tendsto hx⟩
end

/-- An abstract version of continuity of sequentially continuous functions on metric spaces:
if a filter `k` is countably generated then `tendsto f k l` iff for every sequence `u`
converging to `k`, `f ∘ u` tends to `l`. -/
lemma tendsto_iff_seq_tendsto {f : α → β} {k : filter α} {l : filter β} [k.is_countably_generated] :
  tendsto f k l ↔ (∀ x : ℕ → α, tendsto x at_top k → tendsto (f ∘ x) at_top l) :=
begin
  refine ⟨λ h x hx, h.comp hx, λ H s hs, _⟩,
  contrapose! H,
  haveI : ne_bot (k ⊓ 𝓟 (f ⁻¹' sᶜ)), by simpa [ne_bot_iff,  inf_principal_eq_bot],
  rcases (k ⊓ 𝓟 (f ⁻¹' sᶜ)).exists_seq_tendsto with ⟨x, hx⟩,
  rw [tendsto_inf, tendsto_principal] at hx,
  refine ⟨x, hx.1, λ h, _⟩,
  rcases (hx.2.and (h hs)).exists with ⟨N, hnmem, hmem⟩,
  exact hnmem hmem
end

lemma tendsto_of_seq_tendsto {f : α → β} {k : filter α} {l : filter β} [k.is_countably_generated] :
  (∀ x : ℕ → α, tendsto x at_top k → tendsto (f ∘ x) at_top l) → tendsto f k l :=
tendsto_iff_seq_tendsto.2

lemma tendsto_iff_forall_eventually_mem {α ι : Type*} {x : ι → α} {f : filter α} {l : filter ι} :
  tendsto x l f ↔ ∀ s ∈ f, ∀ᶠ n in l, x n ∈ s :=
by { rw tendsto_def, refine forall_congr (λ s, imp_congr_right (λ hsf, _)), refl, }

lemma not_tendsto_iff_exists_frequently_nmem {α ι : Type*} {x : ι → α} {f : filter α}
  {l : filter ι} :
  ¬ tendsto x l f ↔ ∃ s ∈ f, ∃ᶠ n in l, x n ∉ s :=
begin
  rw tendsto_iff_forall_eventually_mem,
  push_neg,
  refine exists_congr (λ s, _),
  rw [not_eventually, exists_prop],
end

lemma frequently_iff_seq_frequently {ι : Type*} {l : filter ι} {p : ι → Prop}
  [hl : l.is_countably_generated] :
  (∃ᶠ n in l, p n) ↔ ∃ (x : ℕ → ι), tendsto x at_top l ∧ ∃ᶠ (n : ℕ) in at_top, p (x n) :=
begin
  refine ⟨λ h_freq, _, λ h_exists_freq, _⟩,
  { haveI : ne_bot (l ⊓ 𝓟 {x : ι | p x}), by simpa [ne_bot_iff, inf_principal_eq_bot],
    obtain ⟨x, hx⟩ := exists_seq_tendsto (l ⊓ (𝓟 {x : ι | p x})),
    rw tendsto_inf at hx,
    cases hx with hx_l hx_p,
    refine ⟨x, hx_l, _⟩,
    rw tendsto_principal at hx_p,
    exact hx_p.frequently, },
  { obtain ⟨x, hx_tendsto, hx_freq⟩ := h_exists_freq,
    simp_rw [filter.frequently, filter.eventually] at hx_freq ⊢,
    have : {n : ℕ | ¬p (x n)} = {n | x n ∈ {y | ¬ p y}} := rfl,
    rw [this, ← mem_map'] at hx_freq,
    contrapose! hx_freq,
    exact hx_tendsto hx_freq, },
end

lemma eventually_iff_seq_eventually {ι : Type*} {l : filter ι} {p : ι → Prop}
  [hl : l.is_countably_generated] :
  (∀ᶠ n in l, p n) ↔ ∀ (x : ℕ → ι), tendsto x at_top l → ∀ᶠ (n : ℕ) in at_top, p (x n) :=
begin
  have : (∀ᶠ n in l, p n) ↔ ¬ ∃ᶠ n in l, ¬(p n),
  { rw not_frequently, simp_rw not_not, },
  rw [this, frequently_iff_seq_frequently],
  push_neg,
  simp_rw [not_frequently, not_not],
end

lemma subseq_forall_of_frequently {ι : Type*} {x : ℕ → ι} {p : ι → Prop} {l : filter ι}
  (h_tendsto : tendsto x at_top l) (h : ∃ᶠ n in at_top, p (x n)) :
  ∃ ns : ℕ → ℕ, tendsto (λ n, x (ns n)) at_top l ∧ ∀ n, p (x (ns n)) :=
begin
  rw tendsto_iff_seq_tendsto at h_tendsto,
  choose ns hge hns using frequently_at_top.1 h,
  exact ⟨ns, h_tendsto ns (tendsto_at_top_mono hge tendsto_id), hns⟩,
end

lemma exists_seq_forall_of_frequently {ι : Type*} {l : filter ι} {p : ι → Prop}
  [hl : l.is_countably_generated] (h : ∃ᶠ n in l, p n) :
  ∃ ns : ℕ → ι, tendsto ns at_top l ∧ ∀ n, p (ns n) :=
begin
  rw frequently_iff_seq_frequently at h,
  obtain ⟨x, hx_tendsto, hx_freq⟩ := h,
  obtain ⟨n_to_n, h_tendsto, h_freq⟩ := subseq_forall_of_frequently hx_tendsto hx_freq,
  exact ⟨x ∘ n_to_n, h_tendsto, h_freq⟩,
end

/-- A sequence converges if every subsequence has a convergent subsequence. -/
lemma tendsto_of_subseq_tendsto {α ι : Type*}
  {x : ι → α} {f : filter α} {l : filter ι} [l.is_countably_generated]
  (hxy : ∀ ns : ℕ → ι, tendsto ns at_top l →
    ∃ ms : ℕ → ℕ, tendsto (λ n, x (ns $ ms n)) at_top f) :
  tendsto x l f :=
begin
  by_contra h,
  obtain ⟨s, hs, hfreq⟩ : ∃ s ∈ f, ∃ᶠ n in l, x n ∉ s,
    by rwa not_tendsto_iff_exists_frequently_nmem at h,
  obtain ⟨y, hy_tendsto, hy_freq⟩ := exists_seq_forall_of_frequently hfreq,
  specialize hxy y hy_tendsto,
  obtain ⟨ms, hms_tendsto⟩ := hxy,
  specialize hms_tendsto hs,
  rw mem_map at hms_tendsto,
  have hms_freq : ∀ (n : ℕ), x (y (ms n)) ∉ s, from λ n, hy_freq (ms n),
  have h_empty : (λ (n : ℕ), x (y (ms n))) ⁻¹' s = ∅,
  { ext1 n,
    simp only [set.mem_preimage, set.mem_empty_eq, iff_false],
    exact hms_freq n, },
  rw h_empty at hms_tendsto,
  exact empty_not_mem at_top hms_tendsto,
end

lemma subseq_tendsto_of_ne_bot {f : filter α} [is_countably_generated f] {u : ℕ → α}
  (hx : ne_bot (f ⊓ map u at_top)) :
  ∃ (θ : ℕ → ℕ), (strict_mono θ) ∧ (tendsto (u ∘ θ) at_top f) :=
begin
  obtain ⟨B, h⟩ := f.exists_antitone_basis,
  have : ∀ N, ∃ n ≥ N, u n ∈ B N,
    from λ N, filter.inf_map_at_top_ne_bot_iff.mp hx _ (h.to_has_basis.mem_of_mem trivial) N,
  choose φ hφ using this,
  cases forall_and_distrib.mp hφ with φ_ge φ_in,
  have lim_uφ : tendsto (u ∘ φ) at_top f,
    from h.tendsto φ_in,
  have lim_φ : tendsto φ at_top at_top,
    from (tendsto_at_top_mono φ_ge tendsto_id),
  obtain ⟨ψ, hψ, hψφ⟩ : ∃ ψ : ℕ → ℕ, strict_mono ψ ∧ strict_mono (φ ∘ ψ),
    from strict_mono_subseq_of_tendsto_at_top lim_φ,
  exact ⟨φ ∘ ψ, hψφ, lim_uφ.comp hψ.tendsto_at_top⟩,
end

end filter

open filter finset

section

variables {R : Type*} [linear_ordered_semiring R]

lemma exists_lt_mul_self (a : R) : ∃ x ≥ 0, a < x * x :=
let ⟨x, hxa, hx0⟩ :=((tendsto_mul_self_at_top.eventually (eventually_gt_at_top a)).and
  (eventually_ge_at_top 0)).exists
in ⟨x, hx0, hxa⟩

lemma exists_le_mul_self (a : R) : ∃ x ≥ 0, a ≤ x * x :=
let ⟨x, hx0, hxa⟩ := exists_lt_mul_self a in ⟨x, hx0, hxa.le⟩

end

namespace order_iso

variables [preorder α] [preorder β]

@[simp] lemma comap_at_top (e : α ≃o β) : comap e at_top = at_top :=
by simp [at_top, ← e.surjective.infi_comp]

@[simp] lemma comap_at_bot (e : α ≃o β) : comap e at_bot = at_bot :=
e.dual.comap_at_top

@[simp] lemma map_at_top (e : α ≃o β) : map (e : α → β) at_top = at_top :=
by rw [← e.comap_at_top, map_comap_of_surjective e.surjective]

@[simp] lemma map_at_bot (e : α ≃o β) : map (e : α → β) at_bot = at_bot :=
e.dual.map_at_top

lemma tendsto_at_top (e : α ≃o β) : tendsto e at_top at_top :=
e.map_at_top.le

lemma tendsto_at_bot (e : α ≃o β) : tendsto e at_bot at_bot :=
e.map_at_bot.le

@[simp] lemma tendsto_at_top_iff {l : filter γ} {f : γ → α} (e : α ≃o β) :
  tendsto (λ x, e (f x)) l at_top ↔ tendsto f l at_top :=
by rw [← e.comap_at_top, tendsto_comap_iff]

@[simp] lemma tendsto_at_bot_iff {l : filter γ} {f : γ → α} (e : α ≃o β) :
  tendsto (λ x, e (f x)) l at_bot ↔ tendsto f l at_bot :=
e.dual.tendsto_at_top_iff

end order_iso

/-- Let `g : γ → β` be an injective function and `f : β → α` be a function from the codomain of `g`
to a commutative monoid. Suppose that `f x = 1` outside of the range of `g`. Then the filters
`at_top.map (λ s, ∏ i in s, f (g i))` and `at_top.map (λ s, ∏ i in s, f i)` coincide.

The additive version of this lemma is used to prove the equality `∑' x, f (g x) = ∑' y, f y` under
the same assumptions.-/
@[to_additive]
lemma function.injective.map_at_top_finset_prod_eq [comm_monoid α] {g : γ → β}
  (hg : function.injective g) {f : β → α} (hf : ∀ x ∉ set.range g, f x = 1) :
  map (λ s, ∏ i in s, f (g i)) at_top = map (λ s, ∏ i in s, f i) at_top :=
begin
  apply le_antisymm; refine map_at_top_finset_prod_le_of_prod_eq (λ s, _),
  { refine ⟨s.preimage g (hg.inj_on _), λ t ht, _⟩,
    refine ⟨t.image g ∪ s, finset.subset_union_right _ _, _⟩,
    rw [← finset.prod_image (hg.inj_on _)],
    refine (prod_subset (subset_union_left _ _) _).symm,
    simp only [finset.mem_union, finset.mem_image],
    refine λ y hy hyt, hf y (mt _ hyt),
    rintros ⟨x, rfl⟩,
    exact ⟨x, ht (finset.mem_preimage.2 $ hy.resolve_left hyt), rfl⟩ },
  { refine ⟨s.image g, λ t ht, _⟩,
    simp only [← prod_preimage _ _ (hg.inj_on _) _ (λ x _, hf x)],
    exact ⟨_, (image_subset_iff_subset_preimage _).1 ht, rfl⟩ }
end

/-- Let `g : γ → β` be an injective function and `f : β → α` be a function from the codomain of `g`
to an additive commutative monoid. Suppose that `f x = 0` outside of the range of `g`. Then the
filters `at_top.map (λ s, ∑ i in s, f (g i))` and `at_top.map (λ s, ∑ i in s, f i)` coincide.

This lemma is used to prove the equality `∑' x, f (g x) = ∑' y, f y` under
the same assumptions.-/
add_decl_doc function.injective.map_at_top_finset_sum_eq


import data.finset
import data.fintype.basic
import data.set.finite
import tactic.derive_fintype

section intro

variable {α : Type*}
-- Конечность типа или множества представлена в mathlib тремя основными типами: `A : finset α`, `fintype A` и `finite A`

-- 1. finset - конструктивно конечное множество
-- `finset` = `multiset` + `nodup`
-- `multiset` = классы эквивалентности `list` по отношению "a - перестановка b"
-- Для того, чтобы применять большинство операций (добавление/объединение/...), нужен инстанс `decidable_eq α`: нужно уметь разрешимо сравнивать элементы (чтобы, например, знать `card`)

variable (A : finset α)

#check A.card
#reduce ({0,1,2,3} : finset ℕ).card
#check @finset.card_insert_of_mem

-- 2. fintype α - класс, говорящий о том, что α - конечный тип
-- fintype α = (elems [] : finset α) (complete : ∀ x : α, x ∈ elems)
-- Можно использовать @[derive fintype], для новосозданных `inductive`
-- https://github.com/leanprover-community/mathlib/pull/3772

variable [fintype α]

example : fintype bool := by show_term {apply_instance}
example : fintype (fin 10) := by show_term {apply_instance}

#check @finset.univ

@[derive fintype]
inductive test (α : Type) [fintype α] : Type
| c1 : bool → test
| c2 : (fin 2) → (fin 3) → test
| c3 : α × α → test

-- 3. finite B - доказательство конечности множества B
-- def finite (s : set α) : Prop := nonempty (fintype s)
-- `nonempty (fintype s)` - Prop-версия того, что существует элемент типа `fintype s`
-- `fintype s` - то же самое, что `fintype {x : α // x ∈ s}` или `fintype (subtype s)` - подтип `α` состоящий из пар `⟨x, h : x ∈ s⟩`
-- Удобно использовать тогда, когда в API `finset` нет того, что хочется делать с множествами, а в `set` есть

open set

variables (β : Type) (B : set β) (hB : finite B)

#check @finite.of_fintype
end intro

-- Докажем несколько лемм про `finset`
namespace finset

variables {α : Type} {A B : finset α} [decidable_eq α]

lemma card_erase_of_mem' {x} (h : x ∈ A) : (A.erase x).card + 1 = A.card :=
begin
  rw [card_erase_of_mem h, nat.add_one, nat.succ_pred_eq_of_pos],
  refine card_pos.2 ⟨_, h⟩,
end

end finset


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import data.fintype.big_operators
import data.fintype.perm
import data.fintype.prod
import dynamics.fixed_points.basic

/-!
# Formalization of IMO 1987, Q1

Let $p_{n, k}$ be the number of permutations of a set of cardinality `n ≥ 1` that fix exactly `k`
elements. Prove that $∑_{k=0}^n k p_{n,k}=n!$.

To prove this identity, we show that both sides are equal to the cardinality of the set
`{(x : α, σ : perm α) | σ x = x}`, regrouping by `card (fixed_points σ)` for the left hand side and
by `x` for the right hand side.

The original problem assumes `n ≥ 1`. It turns out that a version with `n * (n - 1)!` in the RHS
holds true for `n = 0` as well, so we first prove it, then deduce the original version in the case
`n ≥ 1`. -/

variables (α : Type*) [fintype α] [decidable_eq α]

open_locale big_operators nat
open equiv fintype function finset (range sum_const) set (Iic)

namespace imo_1987_q1

/-- The set of pairs `(x : α, σ : perm α)` such that `σ x = x` is equivalent to the set of pairs
`(x : α, σ : perm {x}ᶜ)`. -/
def fixed_points_equiv :
  {σx : α × perm α // σx.2 σx.1 = σx.1} ≃ Σ x : α, perm ({x}ᶜ : set α) :=
calc {σx : α × perm α // σx.2 σx.1 = σx.1} ≃ Σ x : α, {σ : perm α // σ x = x} :
  set_prod_equiv_sigma _
... ≃ Σ x : α, {σ : perm α // ∀ y : ({x} : set α), σ y = equiv.refl ↥({x} : set α) y} :
  sigma_congr_right (λ x, equiv.set.of_eq $ by { simp only [set_coe.forall], dsimp, simp })
... ≃ Σ x : α, perm ({x}ᶜ : set α) :
  sigma_congr_right (λ x, by apply equiv.set.compl)

theorem card_fixed_points :
  card {σx : α × perm α // σx.2 σx.1 = σx.1} = card α * (card α - 1)! :=
by simp [card_congr (fixed_points_equiv α), card_perm, finset.filter_not, finset.card_sdiff,
  finset.filter_eq', finset.card_univ]

/-- Given `α : Type*` and `k : ℕ`, `fiber α k` is the set of permutations of `α` with exactly `k`
fixed points. -/
@[derive fintype]
def fiber (k : ℕ) : set (perm α) := {σ : perm α | card (fixed_points σ) = k}

@[simp] lemma mem_fiber {σ : perm α} {k : ℕ} : σ ∈ fiber α k ↔ card (fixed_points σ) = k := iff.rfl

/-- `p α k` is the number of permutations of `α` with exactly `k` fixed points. -/
def p (k : ℕ) := card (fiber α k)

/-- The set of triples `(k ≤ card α, σ ∈ fiber α k, x ∈ fixed_points σ)` is equivalent
to the set of pairs `(x : α, σ : perm α)` such that `σ x = x`. The equivalence sends
`(k, σ, x)` to `(x, σ)` and `(x, σ)` to `(card (fixed_points σ), σ, x)`.

It is easy to see that the cardinality of the LHS is given by
`∑ k : fin (card α + 1), k * p α k`. -/
def fixed_points_equiv' :
  (Σ (k : fin (card α + 1)) (σ : fiber α k), fixed_points σ.1) ≃
    {σx : α × perm α // σx.2 σx.1 = σx.1} :=
{ to_fun := λ p, ⟨⟨p.2.2, p.2.1⟩, p.2.2.2⟩,
  inv_fun := λ p,
    ⟨⟨card (fixed_points p.1.2), (card_subtype_le _).trans_lt (nat.lt_succ_self _)⟩,
     ⟨p.1.2, rfl⟩, ⟨p.1.1, p.2⟩⟩,
  left_inv := λ ⟨⟨k, hk⟩, ⟨σ, hσ⟩, ⟨x, hx⟩⟩, by { simp only [mem_fiber, fin.coe_mk] at hσ,
    subst k, refl },
  right_inv := λ ⟨⟨x, σ⟩, h⟩, rfl }

/-- Main statement for any `(α : Type*) [fintype α]`. -/
theorem main_fintype :
  ∑ k in range (card α + 1), k * p α k = card α * (card α - 1)! :=
have A : ∀ k (σ : fiber α k), card (fixed_points ⇑(↑σ : perm α)) = k := λ k σ, σ.2,
by simpa [A, ← fin.sum_univ_eq_sum_range, -card_of_finset, finset.card_univ,
  card_fixed_points, mul_comm] using card_congr (fixed_points_equiv' α)

/-- Main statement for permutations of `fin n`, a version that works for `n = 0`. -/
theorem main₀ (n : ℕ) :
  ∑ k in range (n + 1), k * p (fin n) k = n * (n - 1)! :=
by simpa using main_fintype (fin n)

/-- Main statement for permutations of `fin n`. -/
theorem main {n : ℕ} (hn : 1 ≤ n) :
  ∑ k in range (n + 1), k * p (fin n) k = n! :=
by rw [main₀, nat.mul_factorial_pred (zero_lt_one.trans_le hn)]

end imo_1987_q1


import .lovelib

/- # LoVe Demo 13: Rational and Real Numbers

We review the construction of `ℚ` and `ℝ` as quotient types.

A procedure to construct types with specific properties:

1. Create a new type that can represent all elements, but not necessarily in a
   unique manner.

2. Quotient this representation, equating elements that should be equal.

3. Define operators on the quotient type by lifting functions from the base
   type and prove that they are compatible with the quotient relation.

We used this approach in lecture 11 to construct `ℤ`. It can be used for
`ℚ` and `ℝ` as well. -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/- ## Rational Numbers

**Step 1:** A rational number is a number that can be expressed as a fraction
`n / d` of integers `n` and `d ≠ 0`: -/

structure fraction :=
(num           : ℤ)
(denom         : ℤ)
(denom_ne_zero : denom ≠ 0)

/- The number `n` is called the numerator, and the number `d` is called the
denominator.

The representation of a rational number as a fraction is not unique—e.g.,
`1 / 2 = 2 / 4 = -1 / -2`.

**Step 2:** Two fractions `n₁ / d₁` and `n₂ / d₂` represent the same rational
number if the ratio between numerator and denominator are the same—i.e.,
`n₁ * d₂ = n₂ * d₁`. This will be our equivalence relation `≈` on fractions. -/

namespace fraction

@[instance] def setoid : setoid fraction :=
{ r     := λa b : fraction, num a * denom b = num b * denom a,
  iseqv :=
    begin
      repeat { apply and.intro },
      { intros a; refl },
      { intros a b h; cc },
      { intros a b c eq_ab eq_bc,
        apply int.eq_of_mul_eq_mul_right (denom_ne_zero b),
        cc }
    end }

lemma setoid_iff (a b : fraction) :
  a ≈ b ↔ num a * denom b = num b * denom a :=
by refl

/- **Step 3:** Define `0 := 0 / 1`, `1 := 1 / 1`, addition, multiplication, etc.

    `n₁ / d₁ + n₂ / d₂`     := `(n₁ * d₂ + n₂ * d₁) / (d₁ * d₂)`
    `(n₁ / d₁) * (n₂ / d₂)` := `(n₁ * n₂) / (d₁ * d₂)`

Then show that they are compatible with `≈`. -/

def of_int (i : ℤ) : fraction :=
{ num           := i,
  denom         := 1,
  denom_ne_zero := by simp }

@[instance] def has_zero : has_zero fraction :=
{ zero := of_int 0 }

@[instance] def has_one : has_one fraction :=
{ one := of_int 1 }

@[instance] def has_add : has_add fraction :=
{ add := λa b : fraction,
    { num           := num a * denom b + num b * denom a,
      denom         := denom a * denom b,
      denom_ne_zero :=
        by apply mul_ne_zero; exact denom_ne_zero _ } }

@[simp] lemma add_num (a b : fraction) :
  num (a + b) = num a * denom b + num b * denom a :=
by refl

@[simp] lemma add_denom (a b : fraction) :
  denom (a + b) = denom a * denom b :=
by refl

lemma add_equiv_add {a a' b b' : fraction} (ha : a ≈ a')
    (hb : b ≈ b') :
  a + b ≈ a' + b' :=
begin
  simp [setoid_iff, add_denom, add_num] at *,
  calc  (num a * denom b + num b * denom a)
          * (denom a' * denom b')
      = num a * denom a' * denom b * denom b'
          + num b * denom b' * denom a * denom a' :
    by simp [add_mul, mul_add]; ac_refl
  ... = num a' * denom a * denom b * denom b'
          + num b' * denom b * denom a * denom a' :
    by simp [*]
  ... = (num a' * denom b' + num b' * denom a')
          * (denom a * denom b) :
    by simp [add_mul, mul_add]; ac_refl
end

@[instance] def has_neg : has_neg fraction :=
{ neg := λa : fraction,
    { num := - num a,
      ..a } }

@[simp] lemma neg_num (a : fraction) :
  num (- a) = - num a :=
by refl

@[simp] lemma neg_denom (a : fraction) :
  denom (- a) = denom a :=
by refl

lemma setoid_neg {a a' : fraction} (hab : a ≈ a') :
  - a ≈ - a' :=
by simp [setoid_iff] at hab ⊢; exact hab

@[instance] def has_mul : has_mul fraction :=
{ mul := λa b : fraction,
    { num           := num a * num b,
      denom         := denom a * denom b,
      denom_ne_zero :=
        mul_ne_zero (denom_ne_zero a) (denom_ne_zero b) } }

@[simp] lemma mul_num (a b : fraction) :
  num (a * b) = num a * num b :=
by refl

@[simp] lemma mul_denom (a b : fraction) :
  denom (a * b) = denom a * denom b :=
by refl

lemma setoid_mul {a a' b b' : fraction} (ha : a ≈ a')
    (hb : b ≈ b') :
  a * b ≈ a' * b' :=
by simp [setoid_iff] at ha hb ⊢; cc

@[instance] def has_inv : has_inv fraction :=
{ inv := λa : fraction,
    if ha : num a = 0 then
      0
    else
      { num           := denom a,
        denom         := num a,
        denom_ne_zero := ha } }

lemma inv_def (a : fraction) (ha : num a ≠ 0) :
  a⁻¹ =
  { num           := denom a,
    denom         := num a,
    denom_ne_zero := ha } :=
dif_neg ha

lemma inv_zero (a : fraction) (ha : num a = 0) :
  a⁻¹ = 0 :=
dif_pos ha

@[simp] lemma inv_num (a : fraction) (ha : num a ≠ 0) :
  num (a⁻¹) = denom a :=
by rw inv_def a ha

@[simp] lemma inv_denom (a : fraction) (ha : num a ≠ 0) :
  denom (a⁻¹) = num a :=
by rw inv_def a ha

lemma setoid_inv {a a' : fraction} (ha : a ≈ a') :
  a⁻¹ ≈ a'⁻¹ :=
begin
  cases' classical.em (num a = 0),
  case inl : ha0 {
    cases' classical.em (num a' = 0),
    case inl : ha'0 {
      simp [ha0, ha'0, inv_zero] },
    case inr : ha'0 {
      simp [ha0, ha'0, setoid_iff, denom_ne_zero] at ha,
      cc } },
  case inr : ha0 {
    cases' classical.em (num a' = 0),
    case inl : ha'0 {
      simp [setoid_iff, ha'0, denom_ne_zero] at ha,
      cc },
    case inr : ha'0 {
      simp [setoid_iff, ha0, ha'0] at ha ⊢,
      cc } }
end

end fraction

def rat : Type :=
quotient fraction.setoid

namespace rat

@[instance] def has_zero : has_zero rat :=
{ zero := ⟦0⟧ }

@[instance] def has_one : has_one rat :=
{ one := ⟦1⟧ }

@[instance] def has_add : has_add rat :=
{ add := quotient.lift₂ (λa b : fraction, ⟦a + b⟧)
    begin
      intros a b a' b' ha hb,
      apply quotient.sound,
      exact fraction.add_equiv_add ha hb
    end }

@[instance] def has_neg : has_neg rat :=
{ neg := quotient.lift (λa : fraction, ⟦- a⟧)
    begin
      intros a a' ha,
      apply quotient.sound,
      exact fraction.setoid_neg ha
    end }

@[instance] def has_mul : has_mul rat :=
{ mul := quotient.lift₂ (λa b : fraction, ⟦a * b⟧)
    begin
      intros a b a' b' ha hb,
      apply quotient.sound,
      exact fraction.setoid_mul ha hb
    end }

@[instance] def has_inv : has_inv rat :=
{ inv := quotient.lift (λa : fraction, ⟦a⁻¹⟧)
    begin
      intros a a' ha,
      apply quotient.sound,
      exact fraction.setoid_inv ha
    end }

end rat

/- ### Alternative Definitions of `ℚ`

**Alternative 1:** Define `ℚ` as a subtype of `fraction`, with the requirement
that the numerator and the denominator have no common divisors except `1` and
`-1`: -/

namespace alternative_1

def rat.is_canonical (a : fraction) : Prop :=
nat.coprime (int.nat_abs (fraction.num a))
  (int.nat_abs (fraction.denom a))

def rat := {a : fraction // rat.is_canonical a}

end alternative_1

/- This is more or less the `mathlib` definition.

Advantages:

* no quotient required;
* more efficient computation;
* more properties are syntactic equalities up to computation.

Disadvantage:

* more complicated function definitions.

**Alternative 2**: Define all elements syntactically, including the desired
operations: -/

namespace alternative_2

inductive pre_rat : Type
| zero : pre_rat
| one  : pre_rat
| add  : pre_rat → pre_rat → pre_rat
| sub  : pre_rat → pre_rat → pre_rat
| mul  : pre_rat → pre_rat → pre_rat
| div  : pre_rat → pre_rat → pre_rat

/- Then quotient `pre_rat` to enforce congruence rules and the field axioms: -/

inductive rat.rel : pre_rat → pre_rat → Prop
| add_congr {a b c d : pre_rat} :
  rat.rel a b → rat.rel c d →
  rat.rel (pre_rat.add a c) (pre_rat.add b d)
| add_assoc {a b c : pre_rat} :
  rat.rel (pre_rat.add a (pre_rat.add b c))
    (pre_rat.add (pre_rat.add a b) c)
| zero_add {a : pre_rat} :
  rat.rel (pre_rat.add pre_rat.zero a) a
-- etc.

def rat : Type :=
quot rat.rel

end alternative_2

/- Advantages:

* no dependency on `ℤ`;
* easy proofs of the field axioms;
* general recipe reusable for other algebraic constructions (e.g., free monoids,
  free groups).

Disadvantage:

* the definition of orders and lemmas about them are more complicated.

### Real Numbers

Some sequences of rational numbers seem to converge because the numbers in the
sequence get closer and closer to each other, and yet do not converge to a
rational number.

Example:

    `a₀ = 1`
    `a₁ = 1.4`
    `a₂ = 1.41`
    `a₃ = 1.414`
    `a₄ = 1.4142`
    `a₅ = 1.41421`
    `a₆ = 1.414213`
    `a₇ = 1.4142135`
       ⋮

This sequence seems to converge because each `a_n` is at most `10^-n` away from
any of the following numbers. But the limit is `√2`, which is not a rational
number.

The rational numbers are incomplete, and the reals are their  __completion__.

To construct the reals, we need to fill in the gaps that are revealed by these
sequences that seem to converge, but do not.

Mathematically, a sequence `a₀, a₁, …` of rational numbers is __Cauchy__ if for
any `ε > 0`, there exists an `N ∈ ℕ` such that for all `m ≥ N`, we have
`|a_N - a_m| < ε`.

In other words, no matter how small we choose `ε`, we can always find a point in
the sequence from which all following numbers deviate less than by `ε`. -/

def is_cau_seq (f : ℕ → ℚ) : Prop :=
∀ε > 0, ∃N, ∀m ≥ N, abs (f N - f m) < ε

/- Not every sequence is a Cauchy sequence: -/

lemma id_not_cau_seq :
  ¬ is_cau_seq (λn : ℕ, (n : ℚ)) :=
begin
  rw is_cau_seq,
  intro h,
  cases' h 1 zero_lt_one with i hi,
  have hi_succi :=
    hi (i + 1) (by simp),
  simp [←sub_sub] at hi_succi,
  linarith
end

/- We define a type of Cauchy sequences as a subtype: -/

def cau_seq : Type :=
{f : ℕ → ℚ // is_cau_seq f}

def seq_of (f : cau_seq) : ℕ → ℚ :=
subtype.val f

/- Cauchy sequences represent real numbers:

* `a_n = 1 / n` represents the real number `0`;
* `1, 1.4, 1.41, …` represents the real number `√2`;
* `a_n = 0` also represents the real number `0`.

Since different Cauchy sequences can represent the same real number, we need to
take the quotient. Formally, two sequences represent the same real number when
their difference converges to zero: -/

namespace cau_seq

@[instance] def setoid : setoid cau_seq :=
{ r     := λf g : cau_seq,
    ∀ε > 0, ∃N, ∀m ≥ N, abs (seq_of f m - seq_of g m) < ε,
  iseqv :=
    begin
      apply and.intro,
      { intros f ε hε,
        apply exists.intro 0,
        finish },
      apply and.intro,
      { intros f g hfg ε hε,
        cases' hfg ε hε with N hN,
        apply exists.intro N,
        intros m hm,
        rw abs_sub,
        apply hN m hm },
      { intros f g h hfg hgh ε hε,
        cases' hfg (ε / 2) (half_pos hε) with N₁ hN₁,
        cases' hgh (ε / 2) (half_pos hε) with N₂ hN₂,
        apply exists.intro (max N₁ N₂),
        intros m hm,
        calc  abs (seq_of f m - seq_of h m)
            ≤ abs (seq_of f m - seq_of g m)
              + abs (seq_of g m - seq_of h m) :
          by apply abs_sub_le
        ... < ε / 2 + ε / 2 :
          add_lt_add (hN₁ m (le_of_max_le_left hm))
            (hN₂ m (le_of_max_le_right hm))
        ... = ε :
          by simp }
    end }

lemma setoid_iff (f g : cau_seq) :
  f ≈ g ↔
  ∀ε > 0, ∃N, ∀m ≥ N, abs (seq_of f m - seq_of g m) < ε :=
by refl

/- We can define constants such as `0` and `1` as a constant sequence. Any
constant sequence is a Cauchy sequence: -/

def const (q : ℚ) : cau_seq :=
subtype.mk (λ_ : ℕ, q) (by rw is_cau_seq; intros ε hε; finish)

/- Defining addition of real numbers requires a little more effort. We define
addition on Cauchy sequences as pairwise addition: -/

@[instance] def has_add : has_add cau_seq :=
{ add := λf g : cau_seq,
    subtype.mk (λn : ℕ, seq_of f n + seq_of g n) sorry }

/- Above, we omit the proof that the addition of two Cauchy sequences is again
a Cauchy sequence.

Next, we need to show that this addition is compatible with `≈`: -/

lemma add_equiv_add {f f' g g' : cau_seq} (hf : f ≈ f')
    (hg : g ≈ g') :
  f + g ≈ f' + g' :=
begin
  intros ε₀ hε₀,
  simp [setoid_iff],
  cases' hf (ε₀ / 2) (half_pos hε₀) with Nf hNf,
  cases' hg (ε₀ / 2) (half_pos hε₀) with Ng hNg,
  apply exists.intro (max Nf Ng),
  intros m hm,
  calc  abs (seq_of (f + g) m - seq_of (f' + g') m)
      = abs ((seq_of f m + seq_of g m)
           - (seq_of f' m + seq_of g' m)) :
    by refl
  ... = abs ((seq_of f m - seq_of f' m)
           + (seq_of g m - seq_of g' m)) :
    begin
      have arg_eq :
        seq_of f m + seq_of g m - (seq_of f' m + seq_of g' m) =
        seq_of f m - seq_of f' m + (seq_of g m - seq_of g' m),
        by linarith,
      rw arg_eq
    end
  ... ≤ abs (seq_of f m - seq_of f' m)
      + abs (seq_of g m - seq_of g' m) :
    by apply abs_add
  ... < ε₀ / 2 + ε₀ / 2 :
    add_lt_add (hNf m (le_of_max_le_left hm))
      (hNg m (le_of_max_le_right hm))
  ... = ε₀ :
    by simp
end

end cau_seq

/- The real numbers are the quotient: -/

def real : Type :=
quotient cau_seq.setoid

namespace real

@[instance] def has_zero : has_zero real :=
{ zero := ⟦cau_seq.const 0⟧ }

@[instance] def has_one : has_one real :=
{ one := ⟦cau_seq.const 1⟧ }

@[instance] def has_add : has_add real :=
{ add := quotient.lift₂ (λa b : cau_seq, ⟦a + b⟧)
    begin
      intros a b a' b' ha hb,
      apply quotient.sound,
      exact cau_seq.add_equiv_add ha hb,
    end }

end real

/- ### Alternative Definitions of `ℝ`

* Dedekind cuts: `r : ℝ` is represented essentially as `{x : ℚ | x < r}`.

* Binary sequences `ℕ → bool` can represent the interval `[0, 1]`. This can be
  used to build `ℝ`. -/

end LoVe


import tactic.interactive

-- "set X" means the power set of X
-- a : X means a ∈ X

theorem useful (X : Type) (f : X → set X) : ∃ S : set X, ∀ x : X, ¬ (f x = S) :=
begin
  let S := {a : X | a ∉ f a},
  use S,
  intro x,
  intro H,
  have H2 : x ∈ f x ↔ x ∈ S,
    simp [H],
  change x ∈ f x ↔ ¬ (x ∈ f x) at H2,
  cc,
end

open function

theorem cantor (X : Type) : ¬ (∃ f : X → set X, bijective f) :=
begin
  intro H,
  cases H with f Hf,
  cases Hf with Hi Hs,
  cases (useful X f) with S HS,
  have H2 := Hs S,
  cases H2 with a Ha,
  apply HS a,
  assumption,
end

theorem uncountable : ¬ (∃ f : ℕ → set ℕ, bijective f) :=
cantor ℕ


variables (real : Type) [ordered_ring real]
variables (log exp : real → real)
variable log_exp_eq : ∀ x, log (exp x) = x
variable exp_log_eq : ∀ {x}, x > 0 → exp (log x) = x
variable exp_pos : ∀ x, exp x > 0
variable exp_add : ∀ x y, exp (x + y) = exp x * exp y

-- this ensures the assumptions are available in tactic proofs
include log_exp_eq exp_log_eq exp_pos exp_add

example (x y z : real) : exp (x + y + z) = exp x * exp y * exp z :=
  by rw [exp_add, exp_add]

example (y : real) (h : y > 0)  : exp (log y) = y :=
  exp_log_eq h

theorem log_mul {x y : real} (hx : x > 0) (hy : y > 0) : log (x * y) = log x + log y :=
  by rw [←exp_log_eq hx, ←exp_log_eq hy, ←exp_add, log_exp_eq, log_exp_eq, log_exp_eq]


import algebra.order.ring

#check sub_nonneg
#check sub_mul

variables {R : Type*} [ordered_ring R]
variables a b c : R

example (h : a ≤ b) (h' : 0 ≤ c) : a * c ≤ b * c :=
begin
  rw ← sub_nonneg,
  rw ← sub_mul,
  have h'' : 0 ≤ b - a := sub_nonneg.2 h,
  exact mul_nonneg h'' h',
end


/-
Copyright (c) 2020 Anatole Dedecker. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Anatole Dedecker, Alexey Soloyev, Junyan Xu
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.real.irrational
import Mathlib.data.nat.fib
import Mathlib.data.matrix.notation
import Mathlib.tactic.ring_exp
import Mathlib.algebra.linear_recurrence
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# The golden ratio and its conjugate

This file defines the golden ratio `φ := (1 + √5)/2` and its conjugate
`ψ := (1 - √5)/2`, which are the two real roots of `X² - X - 1`.

Along with various computational facts about them, we prove their
irrationality, and we link them to the Fibonacci sequence by proving
Binet's formula.
-/

/-- The golden ratio `φ := (1 + √5)/2`. -/
def golden_ratio : ℝ := (1 + real.sqrt (bit1 (bit0 1))) / bit0 1

/-- The conjugate of the golden ratio `ψ := (1 - √5)/2`. -/
def golden_conj : ℝ := (1 - real.sqrt (bit1 (bit0 1))) / bit0 1

/-- The inverse of the golden ratio is the opposite of its conjugate. -/
theorem inv_gold : golden_ratio⁻¹ = -golden_conj := sorry

/-- The opposite of the golden ratio is the inverse of its conjugate. -/
theorem inv_gold_conj : golden_conj⁻¹ = -golden_ratio := sorry

@[simp] theorem gold_mul_gold_conj : golden_ratio * golden_conj = -1 := sorry

@[simp] theorem gold_conj_mul_gold : golden_conj * golden_ratio = -1 :=
  eq.mpr
    (id
      (Eq._oldrec (Eq.refl (golden_conj * golden_ratio = -1)) (mul_comm golden_conj golden_ratio)))
    gold_mul_gold_conj

@[simp] theorem gold_add_gold_conj : golden_ratio + golden_conj = 1 := sorry

theorem one_sub_gold_conj : 1 - golden_ratio = golden_conj := sorry

theorem one_sub_gold : 1 - golden_conj = golden_ratio := sorry

@[simp] theorem gold_sub_gold_conj : golden_ratio - golden_conj = real.sqrt (bit1 (bit0 1)) := sorry

@[simp] theorem gold_sq : golden_ratio ^ bit0 1 = golden_ratio + 1 := sorry

@[simp] theorem gold_conj_sq : golden_conj ^ bit0 1 = golden_conj + 1 := sorry

theorem gold_pos : 0 < golden_ratio := sorry

theorem gold_ne_zero : golden_ratio ≠ 0 := ne_of_gt gold_pos

theorem one_lt_gold : 1 < golden_ratio := sorry

theorem gold_conj_neg : golden_conj < 0 := sorry

theorem gold_conj_ne_zero : golden_conj ≠ 0 := ne_of_lt gold_conj_neg

theorem neg_one_lt_gold_conj : -1 < golden_conj :=
  eq.mpr (id (Eq._oldrec (Eq.refl (-1 < golden_conj)) (propext neg_lt)))
    (eq.mpr (id (Eq._oldrec (Eq.refl (-golden_conj < 1)) (Eq.symm inv_gold)))
      (inv_lt_one one_lt_gold))

/-!
## Irrationality
-/

/-- The golden ratio is irrational. -/
theorem gold_irrational : irrational golden_ratio := sorry

/-- The conjugate of the golden ratio is irrational. -/
theorem gold_conj_irrational : irrational golden_conj := sorry

/-!
## Links with Fibonacci sequence
-/

/-- The recurrence relation satisfied by the Fibonacci sequence. -/
def fib_rec {α : Type u_1} [comm_semiring α] : linear_recurrence α :=
  linear_recurrence.mk (bit0 1) (matrix.vec_cons 1 (matrix.vec_cons 1 matrix.vec_empty))

/-- The characteristic polynomial of `fib_rec` is `X² - (X + 1)`. -/
theorem fib_rec_char_poly_eq {β : Type u_1} [comm_ring β] :
    linear_recurrence.char_poly fib_rec = polynomial.X ^ bit0 1 - (polynomial.X + 1) :=
  sorry

/-- As expected, the Fibonacci sequence is a solution of `fib_rec`. -/
theorem fib_is_sol_fib_rec {α : Type u_1} [comm_semiring α] :
    linear_recurrence.is_solution fib_rec fun (x : ℕ) => ↑(nat.fib x) :=
  sorry

/-- The geometric sequence `λ n, φ^n` is a solution of `fib_rec`. -/
theorem geom_gold_is_sol_fib_rec : linear_recurrence.is_solution fib_rec (pow golden_ratio) := sorry

/-- The geometric sequence `λ n, ψ^n` is a solution of `fib_rec`. -/
theorem geom_gold_conj_is_sol_fib_rec : linear_recurrence.is_solution fib_rec (pow golden_conj) :=
  sorry

/-- Binet's formula as a function equality. -/
theorem real.coe_fib_eq' :
    (fun (n : ℕ) => ↑(nat.fib n)) =
        fun (n : ℕ) => (golden_ratio ^ n - golden_conj ^ n) / real.sqrt (bit1 (bit0 1)) :=
  sorry

/-- Binet's formula as a dependent equality. -/
theorem real.coe_fib_eq (n : ℕ) :
    ↑(nat.fib n) = (golden_ratio ^ n - golden_conj ^ n) / real.sqrt (bit1 (bit0 1)) :=
  sorry

end Mathlib


import data.nat.basic
import data.fin

import tactic.linarith

open fin

---------------------------------------------------------------------------------------------
-- nat lemmas

lemma nat_add_mul_left_div_left {a b : ℕ} {c : ℕ} (c_pos : 0 < c) : (c * a + b) / c = a + b/c
:= begin
    rw add_comm,
    rw nat.add_mul_div_left _ _ c_pos,
    ring,
end

lemma nat_add_mul_left_mod_left {a b : ℕ} {c : ℕ} : (c * a + b) % c = b%c
:= begin
    rw add_comm,
    rw nat.add_mul_mod_self_left,
end

lemma add_mul_congr_factor_lt_contra {a b c d e : ℕ}
    : c < a → b < d → ¬ a * b + c = a * d + e
:= begin
    intros cp ep h,
    have h3: a * b + c < a * (b + 1), {
        apply add_lt_add_left; linarith,
    },
    have h4: a * (b + 1) ≤ a * d, {
        apply mul_le_mul; linarith,
    },
    linarith,
end

lemma add_mul_congr_factor {a b c d e : ℕ}
    : c < a → e < a → a * b + c = a * d + e → b = d
:= begin
    intros cp ep h,
    by_contradiction h1,
    cases (ne.lt_or_lt h1) with h2 h2, {
        apply add_mul_congr_factor_lt_contra cp h2 h,
    }, {
        apply add_mul_congr_factor_lt_contra ep h2 (eq.symm h),
    }
end

lemma add_mul_congr {a b c d e : ℕ}
    : a * b + c = a * d + e → c < a → e < a → b = d ∧ c = e
:= begin
    intros h cp ep,
    cases (add_mul_congr_factor cp ep h),
    split, {
        refl,
    }, {
        rw add_right_inj at h,
        exact h,
    }
end

------------------------------------------------------------------------------
-- nat.mod lemmas

section nat_mod

variables (n : ℕ) {m : ℕ}

lemma nat_decomposable_mul_add : 0 < m → ∃ r s, s < m ∧ n = m * r + s
:= begin
    intros mh,
    induction n, {
        use 0,
        use 0,
        split,
        assumption,
        simp,
    }, {
        rcases n_ih with ⟨r', ⟨s', ⟨h1, h2⟩⟩⟩,

        by_cases c: s' + 1 < m, {
            use r',
            use (s' + 1),
            split, {
                assumption,
            }, {
                rw h2,
                rw <- nat.add_one,
                ring,
            }
        }, {
            have c': m = s' + 1, {
                linarith,
            },
            use (r' + 1),
            use (s' + 1 - m),
            rw c',
            split, {
                linarith,
            }, {
                simp,
                rw h2,
                rw <- nat.add_one,
                rw c',
                ring,
            }
        }
    }
end

lemma nat_decompose_mul_add : 0 < m → n = m * (n / m) + (n % m)
:= begin
    intros mh,
    rcases (nat_decomposable_mul_add n mh) with ⟨r, ⟨s, ⟨h1,h2⟩⟩⟩,
    apply eq.trans h2,
    congr, {
        rw h2,
        rw add_comm,
        rw nat.add_mul_div_left; try {assumption},
        rw nat.div_eq_of_lt; assumption <|> simp,
    }, {
        rw h2,
        rw nat.add_mod, simp,
        rw nat.mod_eq_of_lt h1,
    }
end

end nat_mod

------------------------------------------------------------------------------
-- fin coe simplication lemmas

@[simp]
lemma fin_coe_coe_nat_eq_self (n : ℕ) : ((n : fin n.succ) : ℕ) = n
:= begin
    rw coe_eq_val,
    rw coe_val_of_lt, simp,
end

------------------------------------------------------------------------------
-- fin lemmas

lemma fin_has_zero {n} (i : fin n) : 0 < n
:= begin
    cases i,
    linarith,
end

lemma fin_mul_left_has_zero {m p} (i : fin (m*p)) : 0 < m
:= begin
    have f1: 0 < m * p, {
        apply fin_has_zero i,
    },
    apply pos_of_mul_pos_right f1; simp,
end

lemma fin_mul_right_has_zero {m p} (i : fin (m*p)) : 0 < p
:= begin
    have f1: 0 < m * p, {
        apply fin_has_zero i,
    },
    apply pos_of_mul_pos_left f1; simp,
end

lemma fin_add_mul_lt {m p : ℕ} (r : fin m) (v : fin p) : p * (r : ℕ) + (v : ℕ) < m * p
:= begin
    cases r with r hr,
    cases v with v hv,
    simp,
    have f1: p * (r + 1) ≤ p * m, {
        apply mul_le_mul; linarith,
    },
    calc p * r + v < p * r + p : by linarith
               ... = p * (r + 1) : by ring
               ... ≤ p * m : f1
               ... = m * p : by ring,
end

------------------------------------------------------------------------------
-- `cast` reduction: `fin n` cast to `fin n'` coe'd to `ℕ`.

lemma coe_cast_fin_h {n n'} (h : n = n') (i : fin n)
    : (((cast (congr_arg fin h) (i : fin n)) : fin n') : ℕ) = (i : fin n)
:= begin
    congr; assumption <|> apply h.symm <|> skip,
    exact cast_heq (congr_arg fin h) i,
end

lemma coe_cast_fin {n n'} (h : n = n') (i : fin n)
    : (((cast (congr_arg fin h) (i : fin n)) : fin n') : ℕ) = i
:= begin
    rw coe_cast_fin_h; simp,
end

------------------------------------------------------------------------------------------------
-- `cast` helpers

section cast

variables {A B : Type}
variables {a : A}
variables {b : B}

lemma cast_roundtrip (H1 : A = B) : cast H1.symm (cast H1 a) = a
:= begin
    cases H1,
    refl,
end

lemma cast_eq_of_heq (H1: A = B) : a == b → cast H1 a = b
:= begin
    intros h,
    cases h,
    refl,
end

end cast

section fun_cast

variables {A1 A2 R : Type}
variables (f : A1 → R)

lemma congr_heq_arg {x' : A2} {x : A1} (h : x' == x) : (A1 → R) = (A2 → R)
:= begin
    cases h, cc,
end

lemma cast_fun_apply_eq_of_heq_arg (x' : A2) (x : A1) (h : x' == x) : cast (congr_heq_arg h) f x' = f x
:= begin
    cases h; refl,
end

theorem cast_apply (h : A1 = A2) (x' : A2) : cast (congr_arg _ h) f x' = f (cast h.symm x')
:= begin
    rw cast_fun_apply_eq_of_heq_arg,
    apply heq_of_eq_mp; refl,
end

variables {B1 B2 : Type}
variables (f2 : A1 → B1 → R)

lemma congr_heq_arg2 {x' : A2} {y' : B2} {x : A1} {y : B1} (h1 : x' == x) (h2 : y' == y)
        : (A1 → B1 → R) = (A2 → B2 → R)
:= begin
    cases h1, cases h2, cc,
end

lemma cast_fun_apply_eq_of_heq_arg2 (x1 : A2) (x2 : B2) (y1 : A1) (y2 : B1) (h1 : x1 == y1) (h2 : x2 == y2)
        : cast (congr_heq_arg2 h1 h2) f2 x1 x2 = f2 y1 y2
:= begin
    cases h1, cases h2, refl,
end

theorem cast_apply2 (ha : A1 = A2) (hb : B1 = B2) (x : A2) (y : B2)
            : cast (congr_arg2 _ ha hb) f2 x y = f2 (cast ha.symm x) (cast hb.symm y)
:= begin
    rw cast_fun_apply_eq_of_heq_arg2; apply heq_of_eq_mp; refl,
end

end fun_cast