import data.stream.defs
import data.stream.init

import algebra.big_operators.basic

import tactic.basic
import tactic.ring

/-

Puzzle referenced from this tweet: https://twitter.com/sigfpe/status/1474173467016589323

From the book _Out of their Minds: The Lives and Discoveries of 15 Great Computer Scientists_
by Dennis Shasha and Cathy Lazere.

Problem: Suppose each (finite) word is either "decent" or "indecent". Given an infinite
sequence of characters, can you always break it into finite words so that all of them
except perhaps the first one belong to the same class?

Answer: yes.

-/

open_locale big_operators

variable {α : Type}

def break_into_words :
   (stream ℕ) → -- word lengths
   (stream α) → -- original sequence
   (stream (list α)) -- sequence of words
 := function.curry
     (stream.corec
       (λ ⟨lengths, a'⟩, stream.take (lengths.head) a')
       (λ ⟨lengths, a'⟩, ⟨lengths.tail, stream.drop (lengths.head) a'⟩))

--#eval (stream.take 10 (break_into_words id id))

/--
  Dropping the first word is equivalent to dropping `first_length` symbols of the original stream.
-/
lemma break_into_words_cons
  (lengths : stream ℕ)
  (first_length : ℕ)
  (a : stream α) :
  (break_into_words (first_length::lengths) a).tail = break_into_words lengths (a.drop first_length) :=
begin
  simp[break_into_words, stream.corec,stream.tail_map, stream.tail_iterate],
  refl
end

lemma break_into_words_closed_form
    (lengths : stream ℕ)
    (a : stream α)
   : break_into_words lengths a =
      (λ i, stream.take (lengths i) (stream.drop (∑(j : ℕ) in finset.range i, lengths j) a)) :=
begin
  funext n,
  convert_to ((stream.corec (λ x, stream.take (x.fst.head) x.snd)
                 (λ x, ⟨x.fst.tail, stream.drop (x.fst.head) x.snd⟩)) :
                  stream ℕ × stream α → stream (list α)) ⟨lengths, a⟩ n =
             stream.take (lengths n) (stream.drop (∑(j : ℕ) in finset.range n, lengths j) a),
  { rw[break_into_words, function.curry], congr; ext ⟨a,b⟩; refl },
  rw [stream.corec_def,stream.map],
  simp only,
  congr,
  { revert a lengths,
    induction n with pn hpn,
    { intros a b, refl},
    { intros a b,
      { have hnth: b pn.succ = b.nth pn.succ := rfl,
        rw[hnth, stream.nth_succ, stream.nth_succ, stream.iterate_eq, stream.tail_cons, hpn],
        refl } } },
  { revert a lengths,
    induction n with pn hpn,
    { intros a b, refl},
    { intros a b,
      rw [stream.nth_succ, stream.iterate_eq, stream.tail_cons, hpn, stream.drop_drop, finset.sum_range_succ'],
      congr } }
end

def all_same_class
  (is_decent : list α → Prop)
  (b : stream (list α))
  : Prop :=
 stream.all is_decent b ∨ stream.all (λ w ,¬is_decent w) b

def all_prefixes
  (p : list α → Prop)
  (a : stream α) : Prop :=
stream.all p (stream.inits a)

lemma take_prefix
   (is_decent : list α → Prop)
   (a : stream α)
   (ha : all_prefixes is_decent a)
   (n : ℕ)
   (hn : 0 < n) : is_decent (a.take n) :=
begin
  cases n,
  { exfalso, exact nat.lt_asymm hn hn},
  have ht := ha n,
  rwa [stream.nth_inits] at ht,
end

structure decent_word (a : stream α) (is_decent: list α → Prop) : Type :=
  (start : ℕ)
  (length : ℕ)
  (nonempty : 0 < length)
  (h : is_decent ((a.drop start).take length))

structure decent_accumulator (a: stream α) (is_decent : list α → Prop) : Type :=
  (start : ℕ)
  (prefix_decent: all_prefixes is_decent (stream.drop start a))

noncomputable def choose_decent_words
    (is_decent : list α → Prop)
    (a : stream α)
    (hinit: all_prefixes is_decent a)
    (hnot: ∀ (n : ℕ), ∃ (k : ℕ), 0 < k ∧
            all_prefixes is_decent (stream.drop (n + k) a))
     : stream (decent_word a is_decent) :=
stream.corec (λ (acc: decent_accumulator a is_decent),
                  let new_word_length := classical.some (hnot acc.start) in
                  let new_word_nonempty := (classical.some_spec (hnot acc.start)).1 in
                  ⟨acc.start, new_word_length, new_word_nonempty,
                   take_prefix
                    is_decent _ acc.prefix_decent new_word_length new_word_nonempty⟩)
             (λ acc, ⟨acc.start + classical.some (hnot acc.start),
                      (classical.some_spec (hnot acc.start)).2⟩)
             ⟨0, hinit⟩

lemma chosen_decent_closed_form
    (is_decent : list α → Prop)
    (a : stream α)
    (hinit: all_prefixes is_decent a)
    (hnot: ∀ (n : ℕ), ∃ (k : ℕ), 0 < k ∧
            all_prefixes is_decent (stream.drop (n + k) a))
  : ∀ n : ℕ, (((choose_decent_words is_decent a hinit hnot).nth n).start =
              ∑(j : ℕ) in finset.range n, ((choose_decent_words is_decent a hinit hnot).nth j).length)
            :=
begin
  intro n,
  induction n with n pn,
  { refl },
  { rw[finset.sum_range_succ, ← pn], refl },
end

lemma check_decent_words
    (is_decent : list α → Prop)
    (a : stream α)
    (hinit: all_prefixes is_decent a)
    (hnot: ∀ (n : ℕ), ∃ (k : ℕ), 0 < k ∧
            all_prefixes is_decent (stream.drop (n + k) a))
  : stream.all
      is_decent
      (break_into_words
          (λ i, ((choose_decent_words is_decent a hinit hnot).nth i).length)
          a) :=
begin
  rw [break_into_words_closed_form],
  simp_rw [←chosen_decent_closed_form],
  intro j,
  exact ((choose_decent_words is_decent a hinit hnot).nth j).h
end

structure indecent_word (a : stream α) (is_decent: list α → Prop) : Type :=
  (start : ℕ)
  (length : ℕ)
  (nonempty : 0 < length)
  (h : ¬is_decent ((a.drop start).take length))

lemma not_all_prefixes
    (is_decent : list α → Prop)
    (a : stream α)
    (h : ¬ all_prefixes is_decent a) :
    ∃ n, ¬ is_decent (a.take (nat.succ n)) :=
begin
  simp[all_prefixes, stream.all_def] at h,
  obtain ⟨x, hx⟩ := h,
  rw [stream.nth_inits] at hx,
  use x
end

/-
 accumulator is: n, the number of symbols consumed so far
-/
noncomputable def choose_indecent_words
    (is_decent : list α → Prop)
    (a : stream α)
    (h: ∀ (k : ℕ), ¬all_prefixes is_decent (stream.drop k a))
     : stream (indecent_word a is_decent) :=
stream.corec (λ n, let hd := not_all_prefixes is_decent (stream.drop n a) (h n) in
                   let new_word_length := nat.succ (classical.some hd) in
                   let hh := (classical.some_spec hd) in
                   ⟨n, new_word_length, nat.succ_pos _, hh⟩
             )
             (λ n, let hd := not_all_prefixes is_decent (stream.drop n a) (h n) in
                   let new_word_length := nat.succ (classical.some hd) in
                   n + new_word_length)
             0

lemma chosen_indecent_closed_form
    (is_decent : list α → Prop)
    (a : stream α)
    (h: ∀ (k : ℕ), ¬all_prefixes is_decent (stream.drop k a))
  : ∀ n : ℕ, (((choose_indecent_words is_decent a h).nth n).start =
              ∑(j : ℕ) in finset.range n, ((choose_indecent_words is_decent a h).nth j).length)
            :=
begin
  intro n,
  induction n with n pn,
  { refl },
  { rw[finset.sum_range_succ, ← pn],
    refl },
end

lemma check_indecent_words
    (is_decent : list α → Prop)
    (a : stream α)
    (h: ∀ (k : ℕ), ¬all_prefixes is_decent (stream.drop k a))
  : stream.all
      (λ x, ¬ is_decent x)
      (break_into_words
          (λ i, ((choose_indecent_words is_decent a h).nth i).length)
          a) :=
begin
  rw [break_into_words_closed_form],
  simp_rw [←chosen_indecent_closed_form],
  intro j,
  exact ((choose_indecent_words is_decent a h).nth j).h
end

theorem kolmogorov_puzzle
  (is_decent : list α → Prop)
  (a : stream α)
  : (∃ (lengths : stream ℕ),
     (stream.all (λ length, 0 < length) lengths ∧
      all_same_class is_decent (break_into_words lengths a).tail)) :=
begin
  let p : Prop :=
     (∃ (n : ℕ), ∀ (k : ℕ), 0 < k →
         ¬ all_prefixes is_decent (stream.drop (n + k) a)),

  cases classical.em p with h hnot,
  { obtain ⟨n, hn⟩ := h,
    let a' := stream.drop (n + 1) a,
    have hn' : ∀ (k : ℕ), ¬all_prefixes is_decent (stream.drop k a'),
    { intro k,
      have hnk := hn (k + 1) (nat.succ_pos _),
      rw [stream.drop_drop],
      have ha : k + (n + 1) = n + (k + 1) := by ring,
      rwa[ha] },
    let d := choose_indecent_words is_decent a' hn',
    use n.succ::(λ i, (d.nth i).length),
    split,
    { intro i,
      cases i,
      {exact nat.succ_pos n},
      { exact (d.nth i).nonempty }},
    { right,
      rw [break_into_words_cons],
      exact check_indecent_words is_decent a' hn' }},
  { push_neg at hnot,
    obtain ⟨k, hkp, hinit⟩ := hnot 0,
    have hdka : stream.drop (0 + k) a = stream.drop k a := by { rw [←stream.drop_drop], refl },
    rw [hdka] at hinit,
    let a' := stream.drop k a,
    have hnot' : ∀ (n : ℕ), ∃ (k : ℕ), 0 < k ∧ all_prefixes is_decent (stream.drop (n + k) a'),
    { intros n',
      obtain ⟨k', hk0', hk'⟩ := hnot (k + n'),
      use k',
      split,
      { exact hk0' },
      have hd: (stream.drop (k + n' + k') a) = (stream.drop (n' + k') a'),
      { rw [stream.drop_drop],
        ring_nf },
      rwa [←hd] },
    let d := choose_decent_words is_decent a' hinit hnot',
    use k::(λ i, (d.nth i).length),
    split,
    { intro i,
      cases i,
      { exact hkp },
      {exact (d.nth i).nonempty }},
    { left,
      rw [break_into_words_cons],
      exact check_decent_words is_decent a' hinit hnot' }},
end


import Mathlib
import HOLFloat.Common
import HOLFloat.Fixed
-- Note: Using Aesop for proof searches
--set_option pp.all  true

@[simp]
theorem fformat_valid_imp_radix_lt_one {fmt : format} : is_valid_fformat fmt → 1 < fmt.r := by
  intro h; exact h.left

@[simp]
theorem fformat_valid_imp_radix_even {fmt : format} : is_valid_fformat fmt → fmt.r % 2 = 0 := by
  intro h; exact h.right.left

@[simp]
theorem fformat_valid_imp_prec_lt_0 {fmt : format} : is_valid_fformat fmt → 0 < fmt.p := by
  intro h; exact h.right.right

@[simp]
theorem fformat_radix_lt_1: ∀ (fmt : fformat), 1 < fmt.val.r := by
  intro h
  exact h.prop.left

@[simp]
theorem fformat_radix_lt_0 : ∀ (fmt : fformat), 0 < fmt.val.r := by
  intro h
  have h₁ := h.prop.left
  have h₂ : 0 < (1 : Int) := by trivial
  apply Int.lt_trans h₂ h₁

@[simp]
theorem  gt_zero_ne_zero (x : Int) : 1 < x → x ≠ 0 := by
  intros h h₁; simp [h₁] at h;

@[simp]
theorem fformat_radix_ne_0 : ∀(fmt : fformat), fmt.val.r ≠ 0 := by
  intro h;
  have h₁ := h.prop.left
  apply gt_zero_ne_zero; trivial

@[simp]
theorem fformat_radix_even (fmt :fformat) : fmt.val.r % 2 = 0 := by
  simp [fmt.prop.right.left]

@[simp]
theorem fformat_prec_lt_0 (fmt : fformat) : 0 < fmt.val.p := by
  have h := fmt.prop.right.right
  exact h

-- NOTE: Don't think we need this
/-
let FFORMAT_PREC_IPOW_EQ_EXP =
  prove(`!(fmt:fformat). &(fr fmt) ipow (&(fp fmt) - &1) =
      &((fr fmt) EXP ((fp fmt) - 1))`,
	REPEAT GEN_TAC THEN MATCH_MP_TAC IPOW_EQ_EXP_P THEN
	REWRITE_TAC[FFORMAT_PREC_LT_0]);;

-/

@[simp]
theorem fformat_radix_ipow_le_0 (fmt : fformat)(e : ℤ) : fmt.val.r ^ e ≠ 0 := by
  simp [HPow.hPow, Pow.pow, pow_int]
  cases e with
  | ofNat n =>
    simp [pow_ne_zero]
  | negSucc n =>
  -- HACK: using aesop
    simp_all only [zpow_negSucc, inv_eq_zero, add_pos_iff, or_true, pow_eq_zero_iff, Int.cast_eq_zero, fformat_radix_lt_1,
    gt_zero_ne_zero, not_false_iff]

-- NOTE: proving some theorems from the paper

--NOTE: Major theorems goes here


/-
Copyright (c) 2021 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/

import group_theory.subgroup.basic

/-!
# Saturated subgroups

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

## Tags
subgroup, subgroups

-/

namespace subgroup

variables {G : Type*} [group G]

/-- A subgroup `H` of `G` is *saturated* if for all `n : ℕ` and `g : G` with `g^n ∈ H`
we have `n = 0` or `g ∈ H`. -/
@[to_additive "An additive subgroup `H` of `G` is *saturated* if
for all `n : ℕ` and `g : G` with `n•g ∈ H` we have `n = 0` or `g ∈ H`."]
def saturated (H : subgroup G) : Prop := ∀ ⦃n g⦄, g ^ n ∈ H → n = 0 ∨ g ∈ H

@[to_additive] lemma saturated_iff_npow {H : subgroup G} :
  saturated H ↔ (∀ (n : ℕ) (g : G), g ^ n ∈ H → n = 0 ∨ g ∈ H) := iff.rfl

@[to_additive] lemma saturated_iff_zpow {H : subgroup G} :
  saturated H ↔ (∀ (n : ℤ) (g : G), g ^ n ∈ H → n = 0 ∨ g ∈ H) :=
begin
  split,
  { rintros hH ⟨n⟩ g hgn,
    { simp only [int.coe_nat_eq_zero, int.of_nat_eq_coe, zpow_coe_nat] at hgn ⊢,
      exact hH hgn },
    { suffices : g ^ (n+1) ∈ H,
      { refine (hH this).imp _ id, simp only [is_empty.forall_iff, nat.succ_ne_zero], },
      simpa only [inv_mem_iff, zpow_neg_succ_of_nat] using hgn, } },
  { intros h n g hgn,
    specialize h n g,
    simp only [int.coe_nat_eq_zero, zpow_coe_nat] at h,
    apply h hgn }
end

end subgroup

namespace add_subgroup

lemma ker_saturated {A₁ A₂ : Type*} [add_comm_group A₁] [add_comm_group A₂]
  [no_zero_smul_divisors ℕ A₂] (f : A₁ →+ A₂) :
  (f.ker).saturated :=
begin
  intros n g hg,
  simpa only [f.mem_ker, nsmul_eq_smul, f.map_nsmul, smul_eq_zero] using hg
end

end add_subgroup


inductive iszero : nat → Prop
| iszero_0 : iszero 0
| iszero_S : ∀ n: nat, iszero (nat.succ n) → iszero n

example : ∀ n: nat, iszero n ↔ n = 0 :=
begin
    intros,
    split,
        intros,
        induction a,
            trivial,
        have : nat.succ a_n > 0,
            apply nat.zero_lt_succ,
        contradiction,
    intros,
    rw a,
    apply iszero.iszero_0,
end


/-
Copyright (c) 2021 Manuel Candales. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Manuel Candales, Benjamin Davidson
-/
import geometry.euclidean.angle.unoriented.affine
import geometry.euclidean.sphere.basic

/-!
# Power of a point (intersecting chords and secants)

This file proves basic geometrical results about power of a point (intersecting chords and
secants) in spheres in real inner product spaces and Euclidean affine spaces.

## Main theorems

* `mul_dist_eq_mul_dist_of_cospherical_of_angle_eq_pi`: Intersecting Chords Theorem (Freek No. 55).
* `mul_dist_eq_mul_dist_of_cospherical_of_angle_eq_zero`: Intersecting Secants Theorem.
-/

open real
open_locale euclidean_geometry real_inner_product_space real

variables {V : Type*} [normed_add_comm_group V] [inner_product_space ℝ V]

namespace inner_product_geometry

/-!
### Geometrical results on spheres in real inner product spaces

This section develops some results on spheres in real inner product spaces,
which are used to deduce corresponding results for Euclidean affine spaces.
-/

lemma mul_norm_eq_abs_sub_sq_norm {x y z : V}
  (h₁ : ∃ k : ℝ, k ≠ 1 ∧ x + y = k • (x - y)) (h₂ : ‖z - y‖ = ‖z + y‖) :
  ‖x - y‖ * ‖x + y‖ = |‖z + y‖ ^ 2 - ‖z - x‖ ^ 2| :=
begin
  obtain ⟨k, hk_ne_one, hk⟩ := h₁,
  let r := (k - 1)⁻¹ * (k + 1),

  have hxy : x = r • y,
  { rw [← smul_smul, eq_inv_smul_iff₀ (sub_ne_zero.mpr hk_ne_one), ← sub_eq_zero],
    calc  (k - 1) • x - (k + 1) • y
        = (k • x - x) - (k • y + y) : by simp_rw [sub_smul, add_smul, one_smul]
    ... = (k • x - k • y) - (x + y) : by simp_rw [← sub_sub, sub_right_comm]
    ... = k • (x - y) - (x + y)     : by rw ← smul_sub k x y
    ... = 0                         : sub_eq_zero.mpr hk.symm },

  have hzy : ⟪z, y⟫ = 0,
    by rwa [inner_eq_zero_iff_angle_eq_pi_div_two, ← norm_add_eq_norm_sub_iff_angle_eq_pi_div_two,
      eq_comm],

  have hzx : ⟪z, x⟫ = 0 := by rw [hxy, inner_smul_right, hzy, mul_zero],

  calc  ‖x - y‖ * ‖x + y‖
      = ‖(r - 1) • y‖ * ‖(r + 1) • y‖      : by simp [sub_smul, add_smul, hxy]
  ... = ‖r - 1‖ * ‖y‖ * (‖r + 1‖ * ‖y‖)      : by simp_rw [norm_smul]
  ... = ‖r - 1‖ * ‖r + 1‖ * ‖y‖ ^ 2         : by ring
  ... = |(r - 1) * (r + 1) * ‖y‖ ^ 2| : by simp [abs_mul]
  ... = |r ^ 2 * ‖y‖ ^ 2 - ‖y‖ ^ 2|    : by ring_nf
  ... = |‖x‖ ^ 2 - ‖y‖ ^ 2|            : by simp [hxy, norm_smul, mul_pow, sq_abs]
  ... = |‖z + y‖ ^ 2 - ‖z - x‖ ^ 2|    : by simp [norm_add_sq_real, norm_sub_sq_real,
                                                    hzy, hzx, abs_sub_comm],
end

end inner_product_geometry

namespace euclidean_geometry

/-!
### Geometrical results on spheres in Euclidean affine spaces

This section develops some results on spheres in Euclidean affine spaces.
-/

open inner_product_geometry

variables {P : Type*} [metric_space P] [normed_add_torsor V P]
include V

/-- If `P` is a point on the line `AB` and `Q` is equidistant from `A` and `B`, then
`AP * BP = abs (BQ ^ 2 - PQ ^ 2)`. -/
lemma mul_dist_eq_abs_sub_sq_dist {a b p q : P}
  (hp : ∃ k : ℝ, k ≠ 1 ∧ b -ᵥ p = k • (a -ᵥ p)) (hq : dist a q = dist b q) :
  dist a p * dist b p = |dist b q ^ 2 - dist p q ^ 2| :=
begin
  let m : P := midpoint ℝ a b,
  obtain ⟨v, h1, h2, h3⟩ := ⟨vsub_sub_vsub_cancel_left, v a p m, v p q m, v a q m⟩,
  have h : ∀ r, b -ᵥ r = (m -ᵥ r) + (m -ᵥ a) :=
    λ r, by rw [midpoint_vsub_left, ← right_vsub_midpoint, add_comm, vsub_add_vsub_cancel],
  iterate 4 { rw dist_eq_norm_vsub V },
  rw [← h1, ← h2, h, h],
  rw [← h1, h] at hp,
  rw [dist_eq_norm_vsub V a q, dist_eq_norm_vsub V b q, ← h3, h] at hq,
  exact mul_norm_eq_abs_sub_sq_norm hp hq,
end

/-- If `A`, `B`, `C`, `D` are cospherical and `P` is on both lines `AB` and `CD`, then
`AP * BP = CP * DP`. -/
lemma mul_dist_eq_mul_dist_of_cospherical {a b c d p : P}
  (h : cospherical ({a, b, c, d} : set P))
  (hapb : ∃ k₁ : ℝ, k₁ ≠ 1 ∧ b -ᵥ p = k₁ • (a -ᵥ p))
  (hcpd : ∃ k₂ : ℝ, k₂ ≠ 1 ∧ d -ᵥ p = k₂ • (c -ᵥ p)) :
  dist a p * dist b p = dist c p * dist d p :=
begin
  obtain ⟨q, r, h'⟩ := (cospherical_def {a, b, c, d}).mp h,
  obtain ⟨ha, hb, hc, hd⟩ := ⟨h' a _, h' b _, h' c _, h' d _⟩,
  { rw ← hd at hc,
    rw ← hb at ha,
    rw [mul_dist_eq_abs_sub_sq_dist hapb ha, hb, mul_dist_eq_abs_sub_sq_dist hcpd hc, hd] },
  all_goals { simp },
end

/-- **Intersecting Chords Theorem**. -/
theorem mul_dist_eq_mul_dist_of_cospherical_of_angle_eq_pi {a b c d p : P}
  (h : cospherical ({a, b, c, d} : set P))
  (hapb : ∠ a p b = π) (hcpd : ∠ c p d = π) :
  dist a p * dist b p = dist c p * dist d p :=
begin
  obtain ⟨-, k₁, _, hab⟩ := angle_eq_pi_iff.mp hapb,
  obtain ⟨-, k₂, _, hcd⟩ := angle_eq_pi_iff.mp hcpd,
  exact mul_dist_eq_mul_dist_of_cospherical h ⟨k₁, (by linarith), hab⟩ ⟨k₂, (by linarith), hcd⟩,
end

/-- **Intersecting Secants Theorem**. -/
theorem mul_dist_eq_mul_dist_of_cospherical_of_angle_eq_zero {a b c d p : P}
  (h : cospherical ({a, b, c, d} : set P))
  (hab : a ≠ b) (hcd : c ≠ d) (hapb : ∠ a p b = 0) (hcpd : ∠ c p d = 0) :
  dist a p * dist b p = dist c p * dist d p :=
begin
  obtain ⟨-, k₁, -, hab₁⟩ := angle_eq_zero_iff.mp hapb,
  obtain ⟨-, k₂, -, hcd₁⟩ := angle_eq_zero_iff.mp hcpd,
  refine mul_dist_eq_mul_dist_of_cospherical h ⟨k₁, _, hab₁⟩ ⟨k₂, _, hcd₁⟩;
  by_contra hnot;
  simp only [not_not, *, one_smul] at *,
  exacts [hab (vsub_left_cancel hab₁).symm, hcd (vsub_left_cancel hcd₁).symm],
end

end euclidean_geometry


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

! This file was ported from Lean 3 source module field_theory.laurent
! leanprover-community/mathlib commit 70fd9563a21e7b963887c9360bd29b2393e6225a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Polynomial.Taylor
import Mathbin.FieldTheory.Ratfunc

/-!
# Laurent expansions of rational functions

## Main declarations

* `ratfunc.laurent`: the Laurent expansion of the rational function `f` at `r`, as an `alg_hom`.
* `ratfunc.laurent_injective`: the Laurent expansion at `r` is unique

## Implementation details

Implemented as the quotient of two Taylor expansions, over domains.
An auxiliary definition is provided first to make the construction of the `alg_hom` easier,
  which works on `comm_ring` which are not necessarily domains.
-/

universe u

namespace Ratfunc

noncomputable section

open Polynomial

open Classical nonZeroDivisors Polynomial

variable {R : Type u} [CommRing R] [hdomain : IsDomain R] (r s : R) (p q : R[X]) (f : Ratfunc R)

theorem taylor_mem_nonZeroDivisors (hp : p ∈ R[X]⁰) : taylor r p ∈ R[X]⁰ :=
  by
  rw [mem_nonZeroDivisors_iff]
  intro x hx
  have : x = taylor (r - r) x := by simp
  rwa [this, sub_eq_add_neg, ← taylor_taylor, ← taylor_mul,
    LinearMap.map_eq_zero_iff _ (taylor_injective _), mul_right_mem_nonZeroDivisors_eq_zero_iff hp,
    LinearMap.map_eq_zero_iff _ (taylor_injective _)] at hx
#align ratfunc.taylor_mem_non_zero_divisors Ratfunc.taylor_mem_nonZeroDivisors

/-- The Laurent expansion of rational functions about a value.
Auxiliary definition, usage when over integral domains should prefer `ratfunc.laurent`. -/
def laurentAux : Ratfunc R →+* Ratfunc R :=
  Ratfunc.mapRingHom
    (RingHom.mk (taylor r) (taylor_one _) (taylor_mul _) (LinearMap.map_zero _)
      (LinearMap.map_add _))
    (taylor_mem_nonZeroDivisors _)
#align ratfunc.laurent_aux Ratfunc.laurentAux

theorem laurentAux_of_fraction_ring_mk (q : R[X]⁰) :
    laurentAux r (of_fraction_ring (Localization.mk p q)) =
      of_fraction_ring
        (Localization.mk (taylor r p) ⟨taylor r q, taylor_mem_nonZeroDivisors r q q.Prop⟩) :=
  map_apply_of_fraction_ring_mk _ _ _ _
#align ratfunc.laurent_aux_of_fraction_ring_mk Ratfunc.laurentAux_of_fraction_ring_mk

include hdomain

theorem laurentAux_div :
    laurentAux r (algebraMap _ _ p / algebraMap _ _ q) =
      algebraMap _ _ (taylor r p) / algebraMap _ _ (taylor r q) :=
  map_apply_div _ _ _ _
#align ratfunc.laurent_aux_div Ratfunc.laurentAux_div

@[simp]
theorem laurentAux_algebraMap : laurentAux r (algebraMap _ _ p) = algebraMap _ _ (taylor r p) := by
  rw [← mk_one, ← mk_one, mk_eq_div, laurent_aux_div, mk_eq_div, taylor_one, _root_.map_one]
#align ratfunc.laurent_aux_algebra_map Ratfunc.laurentAux_algebraMap

/-- The Laurent expansion of rational functions about a value. -/
def laurent : Ratfunc R →ₐ[R] Ratfunc R :=
  Ratfunc.mapAlgHom
    (AlgHom.mk (taylor r) (taylor_one _) (taylor_mul _) (LinearMap.map_zero _) (LinearMap.map_add _)
      (by simp [Polynomial.algebraMap_apply]))
    (taylor_mem_nonZeroDivisors _)
#align ratfunc.laurent Ratfunc.laurent

theorem laurent_div :
    laurent r (algebraMap _ _ p / algebraMap _ _ q) =
      algebraMap _ _ (taylor r p) / algebraMap _ _ (taylor r q) :=
  laurentAux_div r p q
#align ratfunc.laurent_div Ratfunc.laurent_div

@[simp]
theorem laurent_algebraMap : laurent r (algebraMap _ _ p) = algebraMap _ _ (taylor r p) :=
  laurentAux_algebraMap _ _
#align ratfunc.laurent_algebra_map Ratfunc.laurent_algebraMap

@[simp]
theorem laurent_x : laurent r x = x + c r := by
  rw [← algebra_map_X, laurent_algebra_map, taylor_X, _root_.map_add, algebra_map_C]
#align ratfunc.laurent_X Ratfunc.laurent_x

@[simp]
theorem laurent_c (x : R) : laurent r (c x) = c x := by
  rw [← algebra_map_C, laurent_algebra_map, taylor_C]
#align ratfunc.laurent_C Ratfunc.laurent_c

@[simp]
theorem laurent_at_zero : laurent 0 f = f :=
  by
  induction f using Ratfunc.induction_on
  simp
#align ratfunc.laurent_at_zero Ratfunc.laurent_at_zero

theorem laurent_laurent : laurent r (laurent s f) = laurent (r + s) f :=
  by
  induction f using Ratfunc.induction_on
  simp_rw [laurent_div, taylor_taylor]
#align ratfunc.laurent_laurent Ratfunc.laurent_laurent

theorem laurent_injective : Function.Injective (laurent r) := fun _ _ h => by
  simpa [laurent_laurent] using congr_arg (laurent (-r)) h
#align ratfunc.laurent_injective Ratfunc.laurent_injective

end Ratfunc


/-
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, Johan Commelin, Mario Carneiro

! This file was ported from Lean 3 source module data.mv_polynomial.comm_ring
! leanprover-community/mathlib commit 972aa4234fa56ce119d19506045158a9d76881fd
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.MvPolynomial.Variables

/-!
# Multivariate polynomials over a ring

Many results about polynomials hold when the coefficient ring is a commutative semiring.
Some stronger results can be derived when we assume this semiring is a ring.

This file does not define any new operations, but proves some of these stronger results.

## Notation

As in other polynomial files, we typically use the notation:

+ `σ : Type*` (indexing the variables)

+ `R : Type*` `[CommRing R]` (the coefficients)

+ `s : σ →₀ ℕ`, a function from `σ` to `ℕ` which is zero away from a finite set.
This will give rise to a monomial in `MvPolynomial σ R` which mathematicians might call `X^s`

+ `a : R`

+ `i : σ`, with corresponding monomial `X i`, often denoted `X_i` by mathematicians

+ `p : MvPolynomial σ R`

-/

noncomputable section

open Classical BigOperators

open Set Function Finsupp AddMonoidAlgebra

open BigOperators

universe u v

variable {R : Type u} {S : Type v}

namespace MvPolynomial

variable {σ : Type _} {a a' a₁ a₂ : R} {e : ℕ} {n m : σ} {s : σ →₀ ℕ}

section CommRing

variable [CommRing R]

variable {p q : MvPolynomial σ R}

instance : CommRing (MvPolynomial σ R) :=
  AddMonoidAlgebra.commRing

variable (σ a a')

-- @[simp] -- Porting note: simp can prove this
theorem C_sub : (C (a - a') : MvPolynomial σ R) = C a - C a' :=
  RingHom.map_sub _ _ _
set_option linter.uppercaseLean3 false in
#align mv_polynomial.C_sub MvPolynomial.C_sub

-- @[simp] -- Porting note: simp can prove this
theorem C_neg : (C (-a) : MvPolynomial σ R) = -C a :=
  RingHom.map_neg _ _
set_option linter.uppercaseLean3 false in
#align mv_polynomial.C_neg MvPolynomial.C_neg

@[simp]
theorem coeff_neg (m : σ →₀ ℕ) (p : MvPolynomial σ R) : coeff m (-p) = -coeff m p :=
  Finsupp.neg_apply _ _
#align mv_polynomial.coeff_neg MvPolynomial.coeff_neg

@[simp]
theorem coeff_sub (m : σ →₀ ℕ) (p q : MvPolynomial σ R) : coeff m (p - q) = coeff m p - coeff m q :=
  Finsupp.sub_apply _ _ _
#align mv_polynomial.coeff_sub MvPolynomial.coeff_sub

@[simp]
theorem support_neg : (-p).support = p.support :=
  Finsupp.support_neg p
#align mv_polynomial.support_neg MvPolynomial.support_neg

theorem support_sub (p q : MvPolynomial σ R) : (p - q).support ⊆ p.support ∪ q.support :=
  Finsupp.support_sub
#align mv_polynomial.support_sub MvPolynomial.support_sub

variable {σ} (p)

section Degrees

theorem degrees_neg (p : MvPolynomial σ R) : (-p).degrees = p.degrees := by
  rw [degrees, support_neg]; rfl
#align mv_polynomial.degrees_neg MvPolynomial.degrees_neg

theorem degrees_sub (p q : MvPolynomial σ R) : (p - q).degrees ≤ p.degrees ⊔ q.degrees := by
  simpa only [sub_eq_add_neg] using le_trans (degrees_add p (-q)) (by rw [degrees_neg])
#align mv_polynomial.degrees_sub MvPolynomial.degrees_sub

end Degrees

section Vars

@[simp]
theorem vars_neg : (-p).vars = p.vars := by simp [vars, degrees_neg]
#align mv_polynomial.vars_neg MvPolynomial.vars_neg

theorem vars_sub_subset : (p - q).vars ⊆ p.vars ∪ q.vars := by
  convert vars_add_subset p (-q) using 2 <;> simp [sub_eq_add_neg]
#align mv_polynomial.vars_sub_subset MvPolynomial.vars_sub_subset

@[simp]
theorem vars_sub_of_disjoint (hpq : Disjoint p.vars q.vars) : (p - q).vars = p.vars ∪ q.vars := by
  rw [← vars_neg q] at hpq
  convert vars_add_of_disjoint hpq using 2 <;> simp [sub_eq_add_neg]
#align mv_polynomial.vars_sub_of_disjoint MvPolynomial.vars_sub_of_disjoint

end Vars

section Eval₂

variable [CommRing S]

variable (f : R →+* S) (g : σ → S)

@[simp]
theorem eval₂_sub : (p - q).eval₂ f g = p.eval₂ f g - q.eval₂ f g :=
  (eval₂Hom f g).map_sub _ _
#align mv_polynomial.eval₂_sub MvPolynomial.eval₂_sub

@[simp]
theorem eval₂_neg : (-p).eval₂ f g = -p.eval₂ f g :=
  (eval₂Hom f g).map_neg _
#align mv_polynomial.eval₂_neg MvPolynomial.eval₂_neg

theorem hom_C (f : MvPolynomial σ ℤ →+* S) (n : ℤ) : f (C n) = (n : S) :=
  eq_intCast (f.comp C) n
set_option linter.uppercaseLean3 false in
#align mv_polynomial.hom_C MvPolynomial.hom_C

/-- A ring homomorphism f : Z[X_1, X_2, ...] → R
is determined by the evaluations f(X_1), f(X_2), ... -/
@[simp]
theorem eval₂Hom_X {R : Type u} (c : ℤ →+* S) (f : MvPolynomial R ℤ →+* S) (x : MvPolynomial R ℤ) :
    eval₂ c (f ∘ X) x = f x := by
  apply MvPolynomial.induction_on x
    (fun n => by
      rw [hom_C f, eval₂_C]
      exact eq_intCast c n)
    (fun p q hp hq => by
      rw [eval₂_add, hp, hq]
      exact (f.map_add _ _).symm)
    fun p n hp => by
    rw [eval₂_mul, eval₂_X, hp]
    exact (f.map_mul _ _).symm
set_option linter.uppercaseLean3 false in
#align mv_polynomial.eval₂_hom_X MvPolynomial.eval₂Hom_X

/-- Ring homomorphisms out of integer polynomials on a type `σ` are the same as
functions out of the type `σ`, -/
def homEquiv : (MvPolynomial σ ℤ →+* S) ≃ (σ → S) where
  toFun f := f ∘ X
  invFun f := eval₂Hom (Int.castRingHom S) f
  left_inv f := RingHom.ext <| eval₂Hom_X _ _
  right_inv f := funext fun x => by simp only [coe_eval₂Hom, Function.comp_apply, eval₂_X]
#align mv_polynomial.hom_equiv MvPolynomial.homEquiv

end Eval₂

section DegreeOf

theorem degreeOf_sub_lt {x : σ} {f g : MvPolynomial σ R} {k : ℕ} (h : 0 < k)
    (hf : ∀ m : σ →₀ ℕ, m ∈ f.support → k ≤ m x → coeff m f = coeff m g)
    (hg : ∀ m : σ →₀ ℕ, m ∈ g.support → k ≤ m x → coeff m f = coeff m g) :
    degreeOf x (f - g) < k := by
  rw [degreeOf_lt_iff h]
  intro m hm
  by_contra hc
  simp only [not_lt] at hc
  have h := support_sub σ f g hm
  simp only [mem_support_iff, Ne.def, coeff_sub, sub_eq_zero] at hm
  cases' Finset.mem_union.1 h with cf cg
  · exact hm (hf m cf hc)
  · exact hm (hg m cg hc)
#align mv_polynomial.degree_of_sub_lt MvPolynomial.degreeOf_sub_lt

end DegreeOf

section TotalDegree

@[simp]
theorem totalDegree_neg (a : MvPolynomial σ R) : (-a).totalDegree = a.totalDegree := by
  simp only [totalDegree, support_neg]
#align mv_polynomial.total_degree_neg MvPolynomial.totalDegree_neg

theorem totalDegree_sub (a b : MvPolynomial σ R) :
    (a - b).totalDegree ≤ max a.totalDegree b.totalDegree :=
  calc
    (a - b).totalDegree = (a + -b).totalDegree := by rw [sub_eq_add_neg]
    _ ≤ max a.totalDegree (-b).totalDegree := (totalDegree_add a (-b))
    _ = max a.totalDegree b.totalDegree := by rw [totalDegree_neg]

#align mv_polynomial.total_degree_sub MvPolynomial.totalDegree_sub

end TotalDegree

end CommRing

end MvPolynomial


/-
Copyright (c) 2022 Jakob von Raumer. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jakob von Raumer, Kevin Klinge
-/
import algebra.ring.regular
import group_theory.submonoid.basic

/-!

# (Right) Ore sets

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

This defines right Ore sets on arbitrary monoids.

## References

* https://ncatlab.org/nlab/show/Ore+set

-/

namespace ore_localization

section monoid

/-- A submonoid `S` of a monoid `R` is (right) Ore if common factors on the left can be turned
into common factors on the right, and if each pair of `r : R` and `s : S` admits an Ore numerator
`v : R` and an Ore denominator `u : S` such that `r * u = s * v`. -/
class ore_set {R : Type*} [monoid R] (S : submonoid R) :=
(ore_left_cancel : ∀ (r₁ r₂ : R) (s : S), ↑s * r₁ = s * r₂ → ∃ s' : S, r₁ * s' = r₂ * s')
(ore_num : R → S → R)
(ore_denom : R → S → S)
(ore_eq : ∀ (r : R) (s : S), r * ore_denom r s = s * ore_num r s)

variables {R : Type*} [monoid R] {S : submonoid R} [ore_set S]

/-- Common factors on the left can be turned into common factors on the right, a weak form of
cancellability. -/
lemma ore_left_cancel (r₁ r₂ : R) (s : S) (h : ↑s * r₁ = s * r₂) : ∃ s' : S, r₁ * s' = r₂ * s' :=
ore_set.ore_left_cancel r₁ r₂ s h

/-- The Ore numerator of a fraction. -/
def ore_num (r : R) (s : S) : R := ore_set.ore_num r s

/-- The Ore denominator of a fraction. -/
def ore_denom (r : R) (s : S) : S := ore_set.ore_denom r s

/-- The Ore condition of a fraction, expressed in terms of `ore_num` and `ore_denom`. -/
lemma ore_eq (r : R) (s : S) : r * (ore_denom r s) = s * (ore_num r s) := ore_set.ore_eq r s

/-- The Ore condition bundled in a sigma type. This is useful in situations where we want to obtain
both witnesses and the condition for a given fraction. -/
def ore_condition (r : R) (s : S) : Σ' r' : R, Σ' s' : S, r * s' = s * r' :=
⟨ore_num r s, ore_denom r s, ore_eq r s⟩

/-- The trivial submonoid is an Ore set. -/
instance ore_set_bot : ore_set (⊥ : submonoid R) :=
{ ore_left_cancel := λ _ _ s h,
    ⟨s, begin
          rcases s with ⟨s, hs⟩,
          rw submonoid.mem_bot at hs,
          subst hs,
          rw [set_like.coe_mk, one_mul, one_mul] at h,
          subst h
        end⟩,
  ore_num := λ r _, r,
  ore_denom := λ _ s, s,
  ore_eq := λ _ s, by { rcases s with ⟨s, hs⟩, rw submonoid.mem_bot at hs, simp [hs] } }

/-- Every submonoid of a commutative monoid is an Ore set. -/
@[priority 100]
instance ore_set_comm {R} [comm_monoid R] (S : submonoid R) : ore_set S :=
{ ore_left_cancel := λ m n s h, ⟨s, by rw [mul_comm n s, mul_comm m s, h]⟩,
  ore_num := λ r _, r,
  ore_denom := λ _ s, s,
  ore_eq := λ r s, by rw mul_comm }

end monoid

/-- Cancellability in monoids with zeros can act as a replacement for the `ore_left_cancel`
condition of an ore set. -/
def ore_set_of_cancel_monoid_with_zero
  {R : Type*} [cancel_monoid_with_zero R] {S : submonoid R}
  (ore_num : R → S → R) (ore_denom : R → S → S)
  (ore_eq : ∀ (r : R) (s : S), r * (ore_denom r s) = s * (ore_num r s)) :
  ore_set S :=
{ ore_left_cancel := λ r₁ r₂ s h, ⟨s, mul_eq_mul_right_iff.mpr (mul_eq_mul_left_iff.mp h)⟩,
  ore_num := ore_num,
  ore_denom := ore_denom,
  ore_eq := ore_eq }

/-- In rings without zero divisors, the first (cancellability) condition is always fulfilled,
it suffices to give a proof for the Ore condition itself. -/
def ore_set_of_no_zero_divisors
  {R : Type*} [ring R] [no_zero_divisors R] {S : submonoid R}
  (ore_num : R → S → R) (ore_denom : R → S → S)
  (ore_eq : ∀ (r : R) (s : S), r * (ore_denom r s) = s * (ore_num r s)) :
  ore_set S :=
begin
  letI : cancel_monoid_with_zero R := no_zero_divisors.to_cancel_monoid_with_zero,
  exact ore_set_of_cancel_monoid_with_zero ore_num ore_denom ore_eq
end

end ore_localization


/-
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 Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.order.filter.bases
import Mathlib.data.finset.preimage
import Mathlib.PostPort

universes u_3 u_1 u_4 u_5 u_2 u_6

namespace Mathlib

/-!
# `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 → +∞`”.
-/

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 {α : Type u_3} [preorder α] : filter α :=
  infi fun (a : α) => principal (set.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 {α : Type u_3} [preorder α] : filter α :=
  infi fun (a : α) => principal (set.Iic a)

theorem mem_at_top {α : Type u_3} [preorder α] (a : α) : (set_of fun (b : α) => a ≤ b) ∈ at_top :=
  mem_infi_sets a (set.subset.refl (set.Ici a))

theorem Ioi_mem_at_top {α : Type u_3} [preorder α] [no_top_order α] (x : α) : set.Ioi x ∈ at_top := sorry

theorem mem_at_bot {α : Type u_3} [preorder α] (a : α) : (set_of fun (b : α) => b ≤ a) ∈ at_bot :=
  mem_infi_sets a (set.subset.refl (set.Iic a))

theorem Iio_mem_at_bot {α : Type u_3} [preorder α] [no_bot_order α] (x : α) : set.Iio x ∈ at_bot := sorry

theorem at_top_basis {α : Type u_3} [Nonempty α] [semilattice_sup α] : has_basis at_top (fun (_x : α) => True) set.Ici :=
  has_basis_infi_principal (directed_of_sup fun (a b : α) => iff.mpr set.Ici_subset_Ici)

theorem at_top_basis' {α : Type u_3} [semilattice_sup α] (a : α) : has_basis at_top (fun (x : α) => a ≤ x) set.Ici := sorry

theorem at_bot_basis {α : Type u_3} [Nonempty α] [semilattice_inf α] : has_basis at_bot (fun (_x : α) => True) set.Iic :=
  at_top_basis

theorem at_bot_basis' {α : Type u_3} [semilattice_inf α] (a : α) : has_basis at_bot (fun (x : α) => x ≤ a) set.Iic :=
  at_top_basis' a

instance at_top_ne_bot {α : Type u_3} [Nonempty α] [semilattice_sup α] : ne_bot at_top :=
  iff.mpr (has_basis.ne_bot_iff at_top_basis) fun (a : α) (_x : True) => set.nonempty_Ici

instance at_bot_ne_bot {α : Type u_3} [Nonempty α] [semilattice_inf α] : ne_bot at_bot :=
  at_top_ne_bot

@[simp] theorem mem_at_top_sets {α : Type u_3} [Nonempty α] [semilattice_sup α] {s : set α} : s ∈ at_top ↔ ∃ (a : α), ∀ (b : α), b ≥ a → b ∈ s :=
  iff.trans (has_basis.mem_iff at_top_basis) (exists_congr fun (_x : α) => exists_const True)

@[simp] theorem mem_at_bot_sets {α : Type u_3} [Nonempty α] [semilattice_inf α] {s : set α} : s ∈ at_bot ↔ ∃ (a : α), ∀ (b : α), b ≤ a → b ∈ s :=
  mem_at_top_sets

@[simp] theorem eventually_at_top {α : Type u_3} [semilattice_sup α] [Nonempty α] {p : α → Prop} : filter.eventually (fun (x : α) => p x) at_top ↔ ∃ (a : α), ∀ (b : α), b ≥ a → p b :=
  mem_at_top_sets

@[simp] theorem eventually_at_bot {α : Type u_3} [semilattice_inf α] [Nonempty α] {p : α → Prop} : filter.eventually (fun (x : α) => p x) at_bot ↔ ∃ (a : α), ∀ (b : α), b ≤ a → p b :=
  mem_at_bot_sets

theorem eventually_ge_at_top {α : Type u_3} [preorder α] (a : α) : filter.eventually (fun (x : α) => a ≤ x) at_top :=
  mem_at_top a

theorem eventually_le_at_bot {α : Type u_3} [preorder α] (a : α) : filter.eventually (fun (x : α) => x ≤ a) at_bot :=
  mem_at_bot a

theorem eventually_gt_at_top {α : Type u_3} [preorder α] [no_top_order α] (a : α) : filter.eventually (fun (x : α) => a < x) at_top :=
  Ioi_mem_at_top a

theorem eventually_lt_at_bot {α : Type u_3} [preorder α] [no_bot_order α] (a : α) : filter.eventually (fun (x : α) => x < a) at_bot :=
  Iio_mem_at_bot a

theorem at_top_basis_Ioi {α : Type u_3} [Nonempty α] [semilattice_sup α] [no_top_order α] : has_basis at_top (fun (_x : α) => True) set.Ioi := sorry

theorem at_top_countable_basis {α : Type u_3} [Nonempty α] [semilattice_sup α] [encodable α] : has_countable_basis at_top (fun (_x : α) => True) set.Ici :=
  has_countable_basis.mk (has_basis.mk (has_basis.mem_iff' at_top_basis))
    (set.countable_encodable (set_of fun (_x : α) => True))

theorem at_bot_countable_basis {α : Type u_3} [Nonempty α] [semilattice_inf α] [encodable α] : has_countable_basis at_bot (fun (_x : α) => True) set.Iic :=
  has_countable_basis.mk (has_basis.mk (has_basis.mem_iff' at_bot_basis))
    (set.countable_encodable (set_of fun (_x : α) => True))

theorem is_countably_generated_at_top {α : Type u_3} [Nonempty α] [semilattice_sup α] [encodable α] : is_countably_generated at_top :=
  has_countable_basis.is_countably_generated at_top_countable_basis

theorem is_countably_generated_at_bot {α : Type u_3} [Nonempty α] [semilattice_inf α] [encodable α] : is_countably_generated at_bot :=
  has_countable_basis.is_countably_generated at_bot_countable_basis

theorem order_top.at_top_eq (α : Type u_1) [order_top α] : at_top = pure ⊤ :=
  le_antisymm (iff.mpr le_pure_iff (eventually.mono (eventually_ge_at_top ⊤) fun (b : α) => top_unique))
    (le_infi fun (b : α) => iff.mpr le_principal_iff le_top)

theorem order_bot.at_bot_eq (α : Type u_1) [order_bot α] : at_bot = pure ⊥ :=
  order_top.at_top_eq (order_dual α)

theorem subsingleton.at_top_eq (α : Type u_1) [subsingleton α] [preorder α] : at_top = ⊤ := sorry

theorem subsingleton.at_bot_eq (α : Type u_1) [subsingleton α] [preorder α] : at_bot = ⊤ :=
  subsingleton.at_top_eq (order_dual α)

theorem tendsto_at_top_pure {α : Type u_3} {β : Type u_4} [order_top α] (f : α → β) : tendsto f at_top (pure (f ⊤)) :=
  Eq.symm (order_top.at_top_eq α) ▸ tendsto_pure_pure f ⊤

theorem tendsto_at_bot_pure {α : Type u_3} {β : Type u_4} [order_bot α] (f : α → β) : tendsto f at_bot (pure (f ⊥)) :=
  tendsto_at_top_pure f

theorem eventually.exists_forall_of_at_top {α : Type u_3} [semilattice_sup α] [Nonempty α] {p : α → Prop} (h : filter.eventually (fun (x : α) => p x) at_top) : ∃ (a : α), ∀ (b : α), b ≥ a → p b :=
  iff.mp eventually_at_top h

theorem eventually.exists_forall_of_at_bot {α : Type u_3} [semilattice_inf α] [Nonempty α] {p : α → Prop} (h : filter.eventually (fun (x : α) => p x) at_bot) : ∃ (a : α), ∀ (b : α), b ≤ a → p b :=
  iff.mp eventually_at_bot h

theorem frequently_at_top {α : Type u_3} [semilattice_sup α] [Nonempty α] {p : α → Prop} : filter.frequently (fun (x : α) => p x) at_top ↔ ∀ (a : α), ∃ (b : α), ∃ (H : b ≥ a), p b := sorry

theorem frequently_at_bot {α : Type u_3} [semilattice_inf α] [Nonempty α] {p : α → Prop} : filter.frequently (fun (x : α) => p x) at_bot ↔ ∀ (a : α), ∃ (b : α), ∃ (H : b ≤ a), p b :=
  frequently_at_top

theorem frequently_at_top' {α : Type u_3} [semilattice_sup α] [Nonempty α] [no_top_order α] {p : α → Prop} : filter.frequently (fun (x : α) => p x) at_top ↔ ∀ (a : α), ∃ (b : α), ∃ (H : b > a), p b := sorry

theorem frequently_at_bot' {α : Type u_3} [semilattice_inf α] [Nonempty α] [no_bot_order α] {p : α → Prop} : filter.frequently (fun (x : α) => p x) at_bot ↔ ∀ (a : α), ∃ (b : α), ∃ (H : b < a), p b :=
  frequently_at_top'

theorem frequently.forall_exists_of_at_top {α : Type u_3} [semilattice_sup α] [Nonempty α] {p : α → Prop} (h : filter.frequently (fun (x : α) => p x) at_top) (a : α) : ∃ (b : α), ∃ (H : b ≥ a), p b :=
  iff.mp frequently_at_top h

theorem frequently.forall_exists_of_at_bot {α : Type u_3} [semilattice_inf α] [Nonempty α] {p : α → Prop} (h : filter.frequently (fun (x : α) => p x) at_bot) (a : α) : ∃ (b : α), ∃ (H : b ≤ a), p b :=
  iff.mp frequently_at_bot h

theorem map_at_top_eq {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_sup α] {f : α → β} : map f at_top = infi fun (a : α) => principal (f '' set_of fun (a' : α) => a ≤ a') :=
  has_basis.eq_infi (has_basis.map f at_top_basis)

theorem map_at_bot_eq {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_inf α] {f : α → β} : map f at_bot = infi fun (a : α) => principal (f '' set_of fun (a' : α) => a' ≤ a) :=
  map_at_top_eq

theorem tendsto_at_top {α : Type u_3} {β : Type u_4} [preorder β] {m : α → β} {f : filter α} : tendsto m f at_top ↔ ∀ (b : β), filter.eventually (fun (a : α) => b ≤ m a) f := sorry

theorem tendsto_at_bot {α : Type u_3} {β : Type u_4} [preorder β] {m : α → β} {f : filter α} : tendsto m f at_bot ↔ ∀ (b : β), filter.eventually (fun (a : α) => m a ≤ b) f :=
  tendsto_at_top

theorem tendsto_at_top_mono' {α : Type u_3} {β : Type u_4} [preorder β] (l : filter α) {f₁ : α → β} {f₂ : α → β} (h : eventually_le l f₁ f₂) : tendsto f₁ l at_top → tendsto f₂ l at_top := sorry

theorem tendsto_at_bot_mono' {α : Type u_3} {β : Type u_4} [preorder β] (l : filter α) {f₁ : α → β} {f₂ : α → β} (h : eventually_le l f₁ f₂) : tendsto f₂ l at_bot → tendsto f₁ l at_bot :=
  tendsto_at_top_mono' l h

theorem tendsto_at_top_mono {α : Type u_3} {β : Type u_4} [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)

theorem tendsto_at_bot_mono {α : Type u_3} {β : Type u_4} [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
-/

theorem inf_map_at_top_ne_bot_iff {α : Type u_3} {β : Type u_4} [semilattice_sup α] [Nonempty α] {F : filter β} {u : α → β} : ne_bot (F ⊓ map u at_top) ↔ ∀ (U : set β) (H : U ∈ F) (N : α), ∃ (n : α), ∃ (H : n ≥ N), u n ∈ U := sorry

theorem inf_map_at_bot_ne_bot_iff {α : Type u_3} {β : Type u_4} [semilattice_inf α] [Nonempty α] {F : filter β} {u : α → β} : ne_bot (F ⊓ map u at_bot) ↔ ∀ (U : set β) (H : U ∈ F) (N : α), ∃ (n : α), ∃ (H : n ≤ N), u n ∈ U :=
  inf_map_at_top_ne_bot_iff

theorem extraction_of_frequently_at_top' {P : ℕ → Prop} (h : ∀ (N : ℕ), ∃ (n : ℕ), ∃ (H : n > N), P n) : ∃ (φ : ℕ → ℕ), strict_mono φ ∧ ∀ (n : ℕ), P (φ n) := sorry

theorem extraction_of_frequently_at_top {P : ℕ → Prop} (h : filter.frequently (fun (n : ℕ) => P n) at_top) : ∃ (φ : ℕ → ℕ), strict_mono φ ∧ ∀ (n : ℕ), P (φ n) :=
  extraction_of_frequently_at_top'
    (eq.mp (Eq._oldrec (Eq.refl (filter.frequently (fun (n : ℕ) => P n) at_top)) (propext frequently_at_top')) h)

theorem extraction_of_eventually_at_top {P : ℕ → Prop} (h : filter.eventually (fun (n : ℕ) => P n) at_top) : ∃ (φ : ℕ → ℕ), strict_mono φ ∧ ∀ (n : ℕ), P (φ n) :=
  extraction_of_frequently_at_top (eventually.frequently h)

theorem exists_le_of_tendsto_at_top {α : Type u_3} {β : Type u_4} [semilattice_sup α] [preorder β] {u : α → β} (h : tendsto u at_top at_top) (a : α) (b : β) : ∃ (a' : α), ∃ (H : a' ≥ a), b ≤ u a' := sorry

theorem exists_le_of_tendsto_at_bot {α : Type u_3} {β : Type u_4} [semilattice_sup α] [preorder β] {u : α → β} (h : tendsto u at_top at_bot) (a : α) (b : β) : ∃ (a' : α), ∃ (H : a' ≥ a), u a' ≤ b :=
  exists_le_of_tendsto_at_top h

theorem exists_lt_of_tendsto_at_top {α : Type u_3} {β : Type u_4} [semilattice_sup α] [preorder β] [no_top_order β] {u : α → β} (h : tendsto u at_top at_top) (a : α) (b : β) : ∃ (a' : α), ∃ (H : a' ≥ a), b < u a' := sorry

theorem exists_lt_of_tendsto_at_bot {α : Type u_3} {β : Type u_4} [semilattice_sup α] [preorder β] [no_bot_order β] {u : α → β} (h : tendsto u at_top at_bot) (a : α) (b : β) : ∃ (a' : α), ∃ (H : 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.
-/
theorem high_scores {β : Type u_4} [linear_order β] [no_top_order β] {u : ℕ → β} (hu : tendsto u at_top at_top) (N : ℕ) : ∃ (n : ℕ), ∃ (H : n ≥ N), ∀ (k : ℕ), k < n → u k < u n := sorry

/--
If `u` is a sequence which is unbounded below,
then after any point, it reaches a value strictly smaller than all previous values.
-/
theorem low_scores {β : Type u_4} [linear_order β] [no_bot_order β] {u : ℕ → β} (hu : tendsto u at_top at_bot) (N : ℕ) : ∃ (n : ℕ), ∃ (H : n ≥ N), ∀ (k : ℕ), 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.
-/
theorem frequently_high_scores {β : Type u_4} [linear_order β] [no_top_order β] {u : ℕ → β} (hu : tendsto u at_top at_top) : filter.frequently (fun (n : ℕ) => ∀ (k : ℕ), k < n → u k < u n) at_top := sorry

/--
If `u` is a sequence which is unbounded below,
then it `frequently` reaches a value strictly smaller than all previous values.
-/
theorem frequently_low_scores {β : Type u_4} [linear_order β] [no_bot_order β] {u : ℕ → β} (hu : tendsto u at_top at_bot) : filter.frequently (fun (n : ℕ) => ∀ (k : ℕ), k < n → u n < u k) at_top :=
  frequently_high_scores hu

theorem strict_mono_subseq_of_tendsto_at_top {β : Type u_1} [linear_order β] [no_top_order β] {u : ℕ → β} (hu : tendsto u at_top at_top) : ∃ (φ : ℕ → ℕ), strict_mono φ ∧ strict_mono (u ∘ φ) := sorry

theorem 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)

theorem strict_mono_tendsto_at_top {φ : ℕ → ℕ} (h : strict_mono φ) : tendsto φ at_top at_top :=
  tendsto_at_top_mono (strict_mono.id_le h) tendsto_id

theorem tendsto_at_top_add_nonneg_left' {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (hf : filter.eventually (fun (x : α) => 0 ≤ f x) l) (hg : tendsto g l at_top) : tendsto (fun (x : α) => f x + g x) l at_top :=
  tendsto_at_top_mono' l (eventually.mono hf fun (x : α) => le_add_of_nonneg_left) hg

theorem tendsto_at_bot_add_nonpos_left' {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (hf : filter.eventually (fun (x : α) => f x ≤ 0) l) (hg : tendsto g l at_bot) : tendsto (fun (x : α) => f x + g x) l at_bot :=
  tendsto_at_top_add_nonneg_left' hf hg

theorem tendsto_at_top_add_nonneg_left {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (hf : ∀ (x : α), 0 ≤ f x) (hg : tendsto g l at_top) : tendsto (fun (x : α) => f x + g x) l at_top :=
  tendsto_at_top_add_nonneg_left' (eventually_of_forall hf) hg

theorem tendsto_at_bot_add_nonpos_left {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (hf : ∀ (x : α), f x ≤ 0) (hg : tendsto g l at_bot) : tendsto (fun (x : α) => f x + g x) l at_bot :=
  tendsto_at_top_add_nonneg_left hf hg

theorem tendsto_at_top_add_nonneg_right' {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (hf : tendsto f l at_top) (hg : filter.eventually (fun (x : α) => 0 ≤ g x) l) : tendsto (fun (x : α) => f x + g x) l at_top :=
  tendsto_at_top_mono' l (monotone_mem_sets (fun (x : α) => le_add_of_nonneg_right) hg) hf

theorem tendsto_at_bot_add_nonpos_right' {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (hf : tendsto f l at_bot) (hg : filter.eventually (fun (x : α) => g x ≤ 0) l) : tendsto (fun (x : α) => f x + g x) l at_bot :=
  tendsto_at_top_add_nonneg_right' hf hg

theorem tendsto_at_top_add_nonneg_right {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (hf : tendsto f l at_top) (hg : ∀ (x : α), 0 ≤ g x) : tendsto (fun (x : α) => f x + g x) l at_top :=
  tendsto_at_top_add_nonneg_right' hf (eventually_of_forall hg)

theorem tendsto_at_bot_add_nonpos_right {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (hf : tendsto f l at_bot) (hg : ∀ (x : α), g x ≤ 0) : tendsto (fun (x : α) => f x + g x) l at_bot :=
  tendsto_at_top_add_nonneg_right hf hg

theorem tendsto_at_top_add {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (hf : tendsto f l at_top) (hg : tendsto g l at_top) : tendsto (fun (x : α) => f x + g x) l at_top :=
  tendsto_at_top_add_nonneg_left' (iff.mp tendsto_at_top hf 0) hg

theorem tendsto_at_bot_add {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (hf : tendsto f l at_bot) (hg : tendsto g l at_bot) : tendsto (fun (x : α) => f x + g x) l at_bot :=
  tendsto_at_top_add hf hg

theorem tendsto.nsmul_at_top {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} (hf : tendsto f l at_top) {n : ℕ} (hn : 0 < n) : tendsto (fun (x : α) => n •ℕ f x) l at_top := sorry

theorem tendsto.nsmul_at_bot {α : Type u_3} {β : Type u_4} [ordered_add_comm_monoid β] {l : filter α} {f : α → β} (hf : tendsto f l at_bot) {n : ℕ} (hn : 0 < n) : tendsto (fun (x : α) => n •ℕ f x) l at_bot :=
  tendsto.nsmul_at_top hf hn

theorem tendsto_bit0_at_top {β : Type u_4} [ordered_add_comm_monoid β] : tendsto bit0 at_top at_top :=
  tendsto_at_top_add tendsto_id tendsto_id

theorem tendsto_bit0_at_bot {β : Type u_4} [ordered_add_comm_monoid β] : tendsto bit0 at_bot at_bot :=
  tendsto_at_bot_add tendsto_id tendsto_id

theorem tendsto_at_top_of_add_const_left {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} (C : β) (hf : tendsto (fun (x : α) => C + f x) l at_top) : tendsto f l at_top :=
  iff.mpr tendsto_at_top
    fun (b : β) => eventually.mono (iff.mp tendsto_at_top hf (C + b)) fun (x : α) => le_of_add_le_add_left

theorem tendsto_at_bot_of_add_const_left {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} (C : β) (hf : tendsto (fun (x : α) => C + f x) l at_bot) : tendsto f l at_bot :=
  tendsto_at_top_of_add_const_left C hf

theorem tendsto_at_top_of_add_const_right {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} (C : β) (hf : tendsto (fun (x : α) => f x + C) l at_top) : tendsto f l at_top :=
  iff.mpr tendsto_at_top
    fun (b : β) => eventually.mono (iff.mp tendsto_at_top hf (b + C)) fun (x : α) => le_of_add_le_add_right

theorem tendsto_at_bot_of_add_const_right {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} (C : β) (hf : tendsto (fun (x : α) => f x + C) l at_bot) : tendsto f l at_bot :=
  tendsto_at_top_of_add_const_right C hf

theorem tendsto_at_top_of_add_bdd_above_left' {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (C : β) (hC : filter.eventually (fun (x : α) => f x ≤ C) l) (h : tendsto (fun (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 (eventually.mono hC fun (x : α) (hx : f x ≤ C) => add_le_add_right hx (g x)) h)

theorem tendsto_at_bot_of_add_bdd_below_left' {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (C : β) (hC : filter.eventually (fun (x : α) => C ≤ f x) l) (h : tendsto (fun (x : α) => f x + g x) l at_bot) : tendsto g l at_bot :=
  tendsto_at_top_of_add_bdd_above_left' C hC h

theorem tendsto_at_top_of_add_bdd_above_left {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (C : β) (hC : ∀ (x : α), f x ≤ C) : tendsto (fun (x : α) => f x + g x) l at_top → tendsto g l at_top :=
  tendsto_at_top_of_add_bdd_above_left' C (univ_mem_sets' hC)

theorem tendsto_at_bot_of_add_bdd_below_left {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (C : β) (hC : ∀ (x : α), C ≤ f x) : tendsto (fun (x : α) => f x + g x) l at_bot → tendsto g l at_bot :=
  tendsto_at_top_of_add_bdd_above_left C hC

theorem tendsto_at_top_of_add_bdd_above_right' {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (C : β) (hC : filter.eventually (fun (x : α) => g x ≤ C) l) (h : tendsto (fun (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 (eventually.mono hC fun (x : α) (hx : g x ≤ C) => add_le_add_left hx (f x)) h)

theorem tendsto_at_bot_of_add_bdd_below_right' {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (C : β) (hC : filter.eventually (fun (x : α) => C ≤ g x) l) (h : tendsto (fun (x : α) => f x + g x) l at_bot) : tendsto f l at_bot :=
  tendsto_at_top_of_add_bdd_above_right' C hC h

theorem tendsto_at_top_of_add_bdd_above_right {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (C : β) (hC : ∀ (x : α), g x ≤ C) : tendsto (fun (x : α) => f x + g x) l at_top → tendsto f l at_top :=
  tendsto_at_top_of_add_bdd_above_right' C (univ_mem_sets' hC)

theorem tendsto_at_bot_of_add_bdd_below_right {α : Type u_3} {β : Type u_4} [ordered_cancel_add_comm_monoid β] {l : filter α} {f : α → β} {g : α → β} (C : β) (hC : ∀ (x : α), C ≤ g x) : tendsto (fun (x : α) => f x + g x) l at_bot → tendsto f l at_bot :=
  tendsto_at_top_of_add_bdd_above_right C hC

theorem tendsto_at_top_add_left_of_le' {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} {g : α → β} (C : β) (hf : filter.eventually (fun (x : α) => C ≤ f x) l) (hg : tendsto g l at_top) : tendsto (fun (x : α) => f x + g x) l at_top := sorry

theorem tendsto_at_bot_add_left_of_ge' {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} {g : α → β} (C : β) (hf : filter.eventually (fun (x : α) => f x ≤ C) l) (hg : tendsto g l at_bot) : tendsto (fun (x : α) => f x + g x) l at_bot :=
  tendsto_at_top_add_left_of_le' l C hf hg

theorem tendsto_at_top_add_left_of_le {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} {g : α → β} (C : β) (hf : ∀ (x : α), C ≤ f x) (hg : tendsto g l at_top) : tendsto (fun (x : α) => f x + g x) l at_top :=
  tendsto_at_top_add_left_of_le' l C (univ_mem_sets' hf) hg

theorem tendsto_at_bot_add_left_of_ge {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} {g : α → β} (C : β) (hf : ∀ (x : α), f x ≤ C) (hg : tendsto g l at_bot) : tendsto (fun (x : α) => f x + g x) l at_bot :=
  tendsto_at_top_add_left_of_le l C hf hg

theorem tendsto_at_top_add_right_of_le' {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} {g : α → β} (C : β) (hf : tendsto f l at_top) (hg : filter.eventually (fun (x : α) => C ≤ g x) l) : tendsto (fun (x : α) => f x + g x) l at_top := sorry

theorem tendsto_at_bot_add_right_of_ge' {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} {g : α → β} (C : β) (hf : tendsto f l at_bot) (hg : filter.eventually (fun (x : α) => g x ≤ C) l) : tendsto (fun (x : α) => f x + g x) l at_bot :=
  tendsto_at_top_add_right_of_le' l C hf hg

theorem tendsto_at_top_add_right_of_le {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} {g : α → β} (C : β) (hf : tendsto f l at_top) (hg : ∀ (x : α), C ≤ g x) : tendsto (fun (x : α) => f x + g x) l at_top :=
  tendsto_at_top_add_right_of_le' l C hf (univ_mem_sets' hg)

theorem tendsto_at_bot_add_right_of_ge {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} {g : α → β} (C : β) (hf : tendsto f l at_bot) (hg : ∀ (x : α), g x ≤ C) : tendsto (fun (x : α) => f x + g x) l at_bot :=
  tendsto_at_top_add_right_of_le l C hf hg

theorem tendsto_at_top_add_const_left {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} (C : β) (hf : tendsto f l at_top) : tendsto (fun (x : α) => C + f x) l at_top :=
  tendsto_at_top_add_left_of_le' l C (univ_mem_sets' fun (_x : α) => le_refl C) hf

theorem tendsto_at_bot_add_const_left {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} (C : β) (hf : tendsto f l at_bot) : tendsto (fun (x : α) => C + f x) l at_bot :=
  tendsto_at_top_add_const_left l C hf

theorem tendsto_at_top_add_const_right {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} (C : β) (hf : tendsto f l at_top) : tendsto (fun (x : α) => f x + C) l at_top :=
  tendsto_at_top_add_right_of_le' l C hf (univ_mem_sets' fun (_x : α) => le_refl C)

theorem tendsto_at_bot_add_const_right {α : Type u_3} {β : Type u_4} [ordered_add_comm_group β] (l : filter α) {f : α → β} (C : β) (hf : tendsto f l at_bot) : tendsto (fun (x : α) => f x + C) l at_bot :=
  tendsto_at_top_add_const_right l C hf

theorem tendsto_neg_at_top_at_bot {β : Type u_4} [ordered_add_comm_group β] : tendsto Neg.neg at_top at_bot := sorry

theorem tendsto_neg_at_bot_at_top {β : Type u_4} [ordered_add_comm_group β] : tendsto Neg.neg at_bot at_top :=
  tendsto_neg_at_top_at_bot

theorem tendsto_bit1_at_top {α : Type u_3} [ordered_semiring α] : tendsto bit1 at_top at_top :=
  tendsto_at_top_add_nonneg_right tendsto_bit0_at_top fun (_x : α) => zero_le_one

theorem tendsto.at_top_mul_at_top {α : Type u_3} {β : Type u_4} [ordered_semiring α] {l : filter β} {f : β → α} {g : β → α} (hf : tendsto f l at_top) (hg : tendsto g l at_top) : tendsto (fun (x : β) => f x * g x) l at_top := sorry

theorem tendsto_mul_self_at_top {α : Type u_3} [ordered_semiring α] : tendsto (fun (x : α) => x * x) at_top at_top :=
  tendsto.at_top_mul_at_top tendsto_id 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`. -/
theorem tendsto_pow_at_top {α : Type u_3} [ordered_semiring α] {n : ℕ} (hn : 1 ≤ n) : tendsto (fun (x : α) => x ^ n) at_top at_top := sorry

theorem zero_pow_eventually_eq {α : Type u_3} [monoid_with_zero α] : eventually_eq at_top (fun (n : ℕ) => 0 ^ n) fun (n : ℕ) => 0 :=
  iff.mpr eventually_at_top (Exists.intro 1 fun (n : ℕ) (hn : n ≥ 1) => zero_pow (has_lt.lt.trans_le zero_lt_one hn))

theorem tendsto.at_top_mul_at_bot {α : Type u_3} {β : Type u_4} [ordered_ring α] {l : filter β} {f : β → α} {g : β → α} (hf : tendsto f l at_top) (hg : tendsto g l at_bot) : tendsto (fun (x : β) => f x * g x) l at_bot := sorry

theorem tendsto.at_bot_mul_at_top {α : Type u_3} {β : Type u_4} [ordered_ring α] {l : filter β} {f : β → α} {g : β → α} (hf : tendsto f l at_bot) (hg : tendsto g l at_top) : tendsto (fun (x : β) => f x * g x) l at_bot := sorry

theorem tendsto.at_bot_mul_at_bot {α : Type u_3} {β : Type u_4} [ordered_ring α] {l : filter β} {f : β → α} {g : β → α} (hf : tendsto f l at_bot) (hg : tendsto g l at_bot) : tendsto (fun (x : β) => f x * g x) l at_top := sorry

/-- $\lim_{x\to+\infty}|x|=+\infty$ -/
theorem tendsto_abs_at_top_at_top {α : Type u_3} [linear_ordered_add_comm_group α] : tendsto abs at_top at_top :=
  tendsto_at_top_mono le_abs_self tendsto_id

/-- $\lim_{x\to-\infty}|x|=+\infty$ -/
theorem tendsto_abs_at_bot_at_top {α : Type u_3} [linear_ordered_add_comm_group α] : tendsto abs at_bot at_top :=
  tendsto_at_top_mono neg_le_abs_self tendsto_neg_at_bot_at_top

theorem tendsto.at_top_of_const_mul {α : Type u_3} {β : Type u_4} [linear_ordered_semiring α] {l : filter β} {f : β → α} {c : α} (hc : 0 < c) (hf : tendsto (fun (x : β) => c * f x) l at_top) : tendsto f l at_top :=
  iff.mpr tendsto_at_top
    fun (b : α) =>
      eventually.mono (iff.mp tendsto_at_top hf (c * b)) fun (x : β) (hx : c * b ≤ c * f x) => le_of_mul_le_mul_left hx hc

theorem tendsto.at_top_of_mul_const {α : Type u_3} {β : Type u_4} [linear_ordered_semiring α] {l : filter β} {f : β → α} {c : α} (hc : 0 < c) (hf : tendsto (fun (x : β) => f x * c) l at_top) : tendsto f l at_top := sorry

theorem nonneg_of_eventually_pow_nonneg {α : Type u_3} [linear_ordered_ring α] {a : α} (h : filter.eventually (fun (n : ℕ) => 0 ≤ a ^ n) at_top) : 0 ≤ a :=
  (fun (_a : ∃ (x : ℕ), (fun (x : ℕ) => 0 ≤ a ^ bit1 x) x) =>
      Exists.dcases_on _a fun (w : ℕ) (h_1 : 0 ≤ a ^ bit1 w) => idRhs (0 ≤ a) (iff.mp pow_bit1_nonneg_iff h_1))
    (eventually.exists (tendsto.eventually tendsto_bit1_at_top h))

/-- 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. -/
theorem tendsto.const_mul_at_top {α : Type u_3} {β : Type u_4} [linear_ordered_field α] {l : filter β} {f : β → α} {r : α} (hr : 0 < r) (hf : tendsto f l at_top) : tendsto (fun (x : β) => r * f x) l at_top := sorry

/-- 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. -/
theorem tendsto.at_top_mul_const {α : Type u_3} {β : Type u_4} [linear_ordered_field α] {l : filter β} {f : β → α} {r : α} (hr : 0 < r) (hf : tendsto f l at_top) : tendsto (fun (x : β) => f x * r) l at_top := sorry

/-- If a function tends to infinity along a filter, then this function divided by a positive
constant also tends to infinity. -/
theorem tendsto.at_top_div_const {α : Type u_3} {β : Type u_4} [linear_ordered_field α] {l : filter β} {f : β → α} {r : α} (hr : 0 < r) (hf : tendsto f l at_top) : tendsto (fun (x : β) => f x / r) l at_top :=
  tendsto.at_top_mul_const (iff.mpr inv_pos hr) hf

/-- If a function tends to infinity along a filter, then this function multiplied by a negative
constant (on the left) tends to negative infinity. -/
theorem tendsto.neg_const_mul_at_top {α : Type u_3} {β : Type u_4} [linear_ordered_field α] {l : filter β} {f : β → α} {r : α} (hr : r < 0) (hf : tendsto f l at_top) : tendsto (fun (x : β) => r * f x) l at_bot := sorry

/-- If a function tends to infinity along a filter, then this function multiplied by a negative
constant (on the right) tends to negative infinity. -/
theorem tendsto.at_top_mul_neg_const {α : Type u_3} {β : Type u_4} [linear_ordered_field α] {l : filter β} {f : β → α} {r : α} (hr : r < 0) (hf : tendsto f l at_top) : tendsto (fun (x : β) => f x * r) l at_bot := sorry

/-- 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. -/
theorem tendsto.const_mul_at_bot {α : Type u_3} {β : Type u_4} [linear_ordered_field α] {l : filter β} {f : β → α} {r : α} (hr : 0 < r) (hf : tendsto f l at_bot) : tendsto (fun (x : β) => r * f x) l at_bot := sorry

/-- 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. -/
theorem tendsto.at_bot_mul_const {α : Type u_3} {β : Type u_4} [linear_ordered_field α] {l : filter β} {f : β → α} {r : α} (hr : 0 < r) (hf : tendsto f l at_bot) : tendsto (fun (x : β) => f x * r) l at_bot := sorry

/-- If a function tends to negative infinity along a filter, then this function divided by
a positive constant also tends to negative infinity. -/
theorem tendsto.at_bot_div_const {α : Type u_3} {β : Type u_4} [linear_ordered_field α] {l : filter β} {f : β → α} {r : α} (hr : 0 < r) (hf : tendsto f l at_bot) : tendsto (fun (x : β) => f x / r) l at_bot :=
  tendsto.at_bot_mul_const (iff.mpr inv_pos hr) hf

/-- 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. -/
theorem tendsto.neg_const_mul_at_bot {α : Type u_3} {β : Type u_4} [linear_ordered_field α] {l : filter β} {f : β → α} {r : α} (hr : r < 0) (hf : tendsto f l at_bot) : tendsto (fun (x : β) => r * f x) l at_top := sorry

/-- 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. -/
theorem tendsto.at_bot_mul_neg_const {α : Type u_3} {β : Type u_4} [linear_ordered_field α] {l : filter β} {f : β → α} {r : α} (hr : r < 0) (hf : tendsto f l at_bot) : tendsto (fun (x : β) => f x * r) l at_top := sorry

theorem tendsto_at_top' {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_sup α] {f : α → β} {l : filter β} : tendsto f at_top l ↔ ∀ (s : set β), s ∈ l → ∃ (a : α), ∀ (b : α), b ≥ a → f b ∈ s := sorry

theorem tendsto_at_bot' {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_inf α] {f : α → β} {l : filter β} : tendsto f at_bot l ↔ ∀ (s : set β), s ∈ l → ∃ (a : α), ∀ (b : α), b ≤ a → f b ∈ s :=
  tendsto_at_top'

theorem tendsto_at_top_principal {α : Type u_3} {β : Type u_4} [Nonempty β] [semilattice_sup β] {f : β → α} {s : set α} : tendsto f at_top (principal s) ↔ ∃ (N : β), ∀ (n : β), n ≥ N → f n ∈ s := sorry

theorem tendsto_at_bot_principal {α : Type u_3} {β : Type u_4} [Nonempty β] [semilattice_inf β] {f : β → α} {s : set α} : tendsto f at_bot (principal s) ↔ ∃ (N : β), ∀ (n : β), n ≤ N → f n ∈ s :=
  tendsto_at_top_principal

/-- A function `f` grows to `+∞` independent of an order-preserving embedding `e`. -/
theorem tendsto_at_top_at_top {α : Type u_3} {β : Type u_4} [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 fun (b : β) => tendsto_at_top_principal)

theorem tendsto_at_top_at_bot {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_sup α] [preorder β] {f : α → β} : tendsto f at_top at_bot ↔ ∀ (b : β), ∃ (i : α), ∀ (a : α), i ≤ a → f a ≤ b :=
  tendsto_at_top_at_top

theorem tendsto_at_bot_at_top {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_inf α] [preorder β] {f : α → β} : tendsto f at_bot at_top ↔ ∀ (b : β), ∃ (i : α), ∀ (a : α), a ≤ i → b ≤ f a :=
  tendsto_at_top_at_top

theorem tendsto_at_bot_at_bot {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_inf α] [preorder β] {f : α → β} : tendsto f at_bot at_bot ↔ ∀ (b : β), ∃ (i : α), ∀ (a : α), a ≤ i → f a ≤ b :=
  tendsto_at_top_at_top

theorem tendsto_at_top_at_top_of_monotone {α : Type u_3} {β : Type u_4} [preorder α] [preorder β] {f : α → β} (hf : monotone f) (h : ∀ (b : β), ∃ (a : α), b ≤ f a) : tendsto f at_top at_top := sorry

theorem tendsto_at_bot_at_bot_of_monotone {α : Type u_3} {β : Type u_4} [preorder α] [preorder β] {f : α → β} (hf : monotone f) (h : ∀ (b : β), ∃ (a : α), f a ≤ b) : tendsto f at_bot at_bot := sorry

theorem tendsto_at_top_at_top_iff_of_monotone {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_sup α] [preorder β] {f : α → β} (hf : monotone f) : tendsto f at_top at_top ↔ ∀ (b : β), ∃ (a : α), b ≤ f a := sorry

theorem tendsto_at_bot_at_bot_iff_of_monotone {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_inf α] [preorder β] {f : α → β} (hf : monotone f) : tendsto f at_bot at_bot ↔ ∀ (b : β), ∃ (a : α), f a ≤ b := sorry

theorem Mathlib.monotone.tendsto_at_top_at_top {α : Type u_3} {β : Type u_4} [preorder α] [preorder β] {f : α → β} (hf : monotone f) (h : ∀ (b : β), ∃ (a : α), b ≤ f a) : tendsto f at_top at_top :=
  tendsto_at_top_at_top_of_monotone

theorem Mathlib.monotone.tendsto_at_bot_at_bot {α : Type u_3} {β : Type u_4} [preorder α] [preorder β] {f : α → β} (hf : monotone f) (h : ∀ (b : β), ∃ (a : α), f a ≤ b) : tendsto f at_bot at_bot :=
  tendsto_at_bot_at_bot_of_monotone

theorem Mathlib.monotone.tendsto_at_top_at_top_iff {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_sup α] [preorder β] {f : α → β} (hf : monotone f) : tendsto f at_top at_top ↔ ∀ (b : β), ∃ (a : α), b ≤ f a :=
  tendsto_at_top_at_top_iff_of_monotone

theorem Mathlib.monotone.tendsto_at_bot_at_bot_iff {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_inf α] [preorder β] {f : α → β} (hf : monotone f) : tendsto f at_bot at_bot ↔ ∀ (b : β), ∃ (a : α), f a ≤ b :=
  tendsto_at_bot_at_bot_iff_of_monotone

theorem tendsto_at_top_embedding {α : Type u_3} {β : Type u_4} {γ : Type u_5} [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 := sorry

/-- A function `f` goes to `-∞` independent of an order-preserving embedding `e`. -/
theorem tendsto_at_bot_embedding {α : Type u_3} {β : Type u_4} {γ : Type u_5} [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 (function.swap hm) hu

theorem tendsto_finset_range : tendsto finset.range at_top at_top :=
  monotone.tendsto_at_top_at_top finset.range_mono finset.exists_nat_subset_range

theorem at_top_finset_eq_infi {α : Type u_3} : at_top = infi fun (x : α) => principal (set.Ici (singleton x)) := sorry

/-- 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`. -/
theorem tendsto_at_top_finset_of_monotone {α : Type u_3} {β : Type u_4} [preorder β] {f : β → finset α} (h : monotone f) (h' : ∀ (x : α), ∃ (n : β), x ∈ f n) : tendsto f at_top at_top := sorry

theorem Mathlib.monotone.tendsto_at_top_finset {α : Type u_3} {β : Type u_4} [preorder β] {f : β → finset α} (h : monotone f) (h' : ∀ (x : α), ∃ (n : β), x ∈ f n) : tendsto f at_top at_top :=
  tendsto_at_top_finset_of_monotone

theorem tendsto_finset_image_at_top_at_top {β : Type u_4} {γ : Type u_5} {i : β → γ} {j : γ → β} (h : function.left_inverse j i) : tendsto (finset.image j) at_top at_top := sorry

theorem tendsto_finset_preimage_at_top_at_top {α : Type u_3} {β : Type u_4} {f : α → β} (hf : function.injective f) : tendsto (fun (s : finset β) => finset.preimage s f (function.injective.inj_on hf (f ⁻¹' ↑s))) at_top at_top :=
  monotone.tendsto_at_top_finset (finset.monotone_preimage hf)
    fun (x : α) => Exists.intro (singleton (f x)) (iff.mpr finset.mem_preimage (finset.mem_singleton_self (f x)))

theorem prod_at_top_at_top_eq {β₁ : Type u_1} {β₂ : Type u_2} [semilattice_sup β₁] [semilattice_sup β₂] : filter.prod at_top at_top = at_top := sorry

theorem prod_at_bot_at_bot_eq {β₁ : Type u_1} {β₂ : Type u_2} [semilattice_inf β₁] [semilattice_inf β₂] : filter.prod at_bot at_bot = at_bot :=
  prod_at_top_at_top_eq

theorem prod_map_at_top_eq {α₁ : Type u_1} {α₂ : Type u_2} {β₁ : Type u_3} {β₂ : Type u_4} [semilattice_sup β₁] [semilattice_sup β₂] (u₁ : β₁ → α₁) (u₂ : β₂ → α₂) : filter.prod (map u₁ at_top) (map u₂ at_top) = map (prod.map u₁ u₂) at_top := sorry

theorem prod_map_at_bot_eq {α₁ : Type u_1} {α₂ : Type u_2} {β₁ : Type u_3} {β₂ : Type u_4} [semilattice_inf β₁] [semilattice_inf β₂] (u₁ : β₁ → α₁) (u₂ : β₂ → α₂) : filter.prod (map u₁ at_bot) (map u₂ at_bot) = map (prod.map u₁ u₂) at_bot :=
  prod_map_at_top_eq u₁ u₂

/-- 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'`. -/
theorem map_at_top_eq_of_gc {α : Type u_3} {β : Type u_4} [semilattice_sup α] [semilattice_sup β] {f : α → β} (g : β → α) (b' : β) (hf : monotone f) (gc : ∀ (a : α) (b : β), b ≥ b' → (f a ≤ b ↔ a ≤ g b)) (hgi : ∀ (b : β), b ≥ b' → b ≤ f (g b)) : map f at_top = at_top := sorry

theorem map_at_bot_eq_of_gc {α : Type u_3} {β : Type u_4} [semilattice_inf α] [semilattice_inf β] {f : α → β} (g : β → α) (b' : β) (hf : monotone f) (gc : ∀ (a : α) (b : β), b ≤ b' → (b ≤ f a ↔ g b ≤ a)) (hgi : ∀ (b : β), b ≤ b' → f (g b) ≤ b) : map f at_bot = at_bot :=
  map_at_top_eq_of_gc (fun (b : β) => g b) b' (monotone.order_dual hf) gc hgi

theorem map_coe_at_top_of_Ici_subset {α : Type u_3} [semilattice_sup α] {a : α} {s : set α} (h : set.Ici a ⊆ s) : map coe at_top = at_top := sorry

/-- The image of the filter `at_top` on `Ici a` under the coercion equals `at_top`. -/
@[simp] theorem map_coe_Ici_at_top {α : Type u_3} [semilattice_sup α] (a : α) : map coe at_top = at_top :=
  map_coe_at_top_of_Ici_subset (set.subset.refl (set.Ici a))

/-- The image of the filter `at_top` on `Ioi a` under the coercion equals `at_top`. -/
@[simp] theorem map_coe_Ioi_at_top {α : Type u_3} [semilattice_sup α] [no_top_order α] (a : α) : map coe at_top = at_top :=
  Exists.dcases_on (no_top a) fun (b : α) (hb : a < b) => map_coe_at_top_of_Ici_subset (iff.mpr set.Ici_subset_Ioi hb)

/-- The `at_top` filter for an open interval `Ioi a` comes from the `at_top` filter in the ambient
order. -/
theorem at_top_Ioi_eq {α : Type u_3} [semilattice_sup α] (a : α) : at_top = comap coe at_top := sorry

/-- The `at_top` filter for an open interval `Ici a` comes from the `at_top` filter in the ambient
order. -/
theorem at_top_Ici_eq {α : Type u_3} [semilattice_sup α] (a : α) : at_top = comap coe at_top :=
  eq.mpr (id (Eq._oldrec (Eq.refl (at_top = comap coe at_top)) (Eq.symm (map_coe_Ici_at_top a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (at_top = comap coe (map coe at_top))) (comap_map subtype.coe_injective)))
      (Eq.refl at_top))

/-- The `at_bot` filter for an open interval `Iio a` comes from the `at_bot` filter in the ambient
order. -/
@[simp] theorem map_coe_Iio_at_bot {α : Type u_3} [semilattice_inf α] [no_bot_order α] (a : α) : map coe at_bot = at_bot :=
  map_coe_Ioi_at_top a

/-- The `at_bot` filter for an open interval `Iio a` comes from the `at_bot` filter in the ambient
order. -/
theorem at_bot_Iio_eq {α : Type u_3} [semilattice_inf α] (a : α) : at_bot = comap coe at_bot :=
  at_top_Ioi_eq a

/-- The `at_bot` filter for an open interval `Iic a` comes from the `at_bot` filter in the ambient
order. -/
@[simp] theorem map_coe_Iic_at_bot {α : Type u_3} [semilattice_inf α] (a : α) : map coe at_bot = at_bot :=
  map_coe_Ici_at_top a

/-- The `at_bot` filter for an open interval `Iic a` comes from the `at_bot` filter in the ambient
order. -/
theorem at_bot_Iic_eq {α : Type u_3} [semilattice_inf α] (a : α) : at_bot = comap coe at_bot :=
  at_top_Ici_eq a

theorem tendsto_Ioi_at_top {α : Type u_3} {β : Type u_4} [semilattice_sup α] {a : α} {f : β → ↥(set.Ioi a)} {l : filter β} : tendsto f l at_top ↔ tendsto (fun (x : β) => ↑(f x)) l at_top := sorry

theorem tendsto_Iio_at_bot {α : Type u_3} {β : Type u_4} [semilattice_inf α] {a : α} {f : β → ↥(set.Iio a)} {l : filter β} : tendsto f l at_bot ↔ tendsto (fun (x : β) => ↑(f x)) l at_bot := sorry

theorem tendsto_Ici_at_top {α : Type u_3} {β : Type u_4} [semilattice_sup α] {a : α} {f : β → ↥(set.Ici a)} {l : filter β} : tendsto f l at_top ↔ tendsto (fun (x : β) => ↑(f x)) l at_top := sorry

theorem tendsto_Iic_at_bot {α : Type u_3} {β : Type u_4} [semilattice_inf α] {a : α} {f : β → ↥(set.Iic a)} {l : filter β} : tendsto f l at_bot ↔ tendsto (fun (x : β) => ↑(f x)) l at_bot := sorry

@[simp] theorem tendsto_comp_coe_Ioi_at_top {α : Type u_3} {β : Type u_4} [semilattice_sup α] [no_top_order α] {a : α} {f : α → β} {l : filter β} : tendsto (fun (x : ↥(set.Ioi a)) => f ↑x) at_top l ↔ tendsto f at_top l := sorry

@[simp] theorem tendsto_comp_coe_Ici_at_top {α : Type u_3} {β : Type u_4} [semilattice_sup α] {a : α} {f : α → β} {l : filter β} : tendsto (fun (x : ↥(set.Ici a)) => f ↑x) at_top l ↔ tendsto f at_top l := sorry

@[simp] theorem tendsto_comp_coe_Iio_at_bot {α : Type u_3} {β : Type u_4} [semilattice_inf α] [no_bot_order α] {a : α} {f : α → β} {l : filter β} : tendsto (fun (x : ↥(set.Iio a)) => f ↑x) at_bot l ↔ tendsto f at_bot l := sorry

@[simp] theorem tendsto_comp_coe_Iic_at_bot {α : Type u_3} {β : Type u_4} [semilattice_inf α] {a : α} {f : α → β} {l : filter β} : tendsto (fun (x : ↥(set.Iic a)) => f ↑x) at_bot l ↔ tendsto f at_bot l := sorry

theorem map_add_at_top_eq_nat (k : ℕ) : map (fun (a : ℕ) => a + k) at_top = at_top :=
  map_at_top_eq_of_gc (fun (a : ℕ) => a - k) k (fun (a b : ℕ) (h : a ≤ b) => add_le_add_right h k)
    (fun (a b : ℕ) (h : b ≥ k) => iff.symm (nat.le_sub_right_iff_add_le h))
    fun (a : ℕ) (h : a ≥ k) => eq.mpr (id (Eq._oldrec (Eq.refl (a ≤ a - k + k)) (nat.sub_add_cancel h))) (le_refl a)

theorem map_sub_at_top_eq_nat (k : ℕ) : map (fun (a : ℕ) => a - k) at_top = at_top :=
  map_at_top_eq_of_gc (fun (a : ℕ) => a + k) 0 (fun (a b : ℕ) (h : a ≤ b) => nat.sub_le_sub_right h k)
    (fun (a b : ℕ) (_x : b ≥ 0) => nat.sub_le_right_iff_le_add)
    fun (b : ℕ) (_x : b ≥ 0) => eq.mpr (id (Eq._oldrec (Eq.refl (b ≤ b + k - k)) (nat.add_sub_cancel b k))) (le_refl b)

theorem tendsto_add_at_top_nat (k : ℕ) : tendsto (fun (a : ℕ) => a + k) at_top at_top :=
  le_of_eq (map_add_at_top_eq_nat k)

theorem tendsto_sub_at_top_nat (k : ℕ) : tendsto (fun (a : ℕ) => a - k) at_top at_top :=
  le_of_eq (map_sub_at_top_eq_nat k)

theorem tendsto_add_at_top_iff_nat {α : Type u_3} {f : ℕ → α} {l : filter α} (k : ℕ) : tendsto (fun (n : ℕ) => f (n + k)) at_top l ↔ tendsto f at_top l := sorry

theorem map_div_at_top_eq_nat (k : ℕ) (hk : 0 < k) : map (fun (a : ℕ) => a / k) at_top = at_top := sorry

/-- 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`. -/
theorem tendsto_at_top_at_top_of_monotone' {ι : Type u_1} {α : Type u_3} [preorder ι] [linear_order α] {u : ι → α} (h : monotone u) (H : ¬bdd_above (set.range u)) : tendsto u at_top at_top := sorry

/-- 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`. -/
theorem tendsto_at_bot_at_bot_of_monotone' {ι : Type u_1} {α : Type u_3} [preorder ι] [linear_order α] {u : ι → α} (h : monotone u) (H : ¬bdd_below (set.range u)) : tendsto u at_bot at_bot :=
  tendsto_at_top_at_top_of_monotone' (monotone.order_dual h) H

theorem unbounded_of_tendsto_at_top {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_sup α] [preorder β] [no_top_order β] {f : α → β} (h : tendsto f at_top at_top) : ¬bdd_above (set.range f) := sorry

theorem unbounded_of_tendsto_at_bot {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_sup α] [preorder β] [no_bot_order β] {f : α → β} (h : tendsto f at_top at_bot) : ¬bdd_below (set.range f) :=
  unbounded_of_tendsto_at_top h

theorem unbounded_of_tendsto_at_top' {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_inf α] [preorder β] [no_top_order β] {f : α → β} (h : tendsto f at_bot at_top) : ¬bdd_above (set.range f) :=
  unbounded_of_tendsto_at_top h

theorem unbounded_of_tendsto_at_bot' {α : Type u_3} {β : Type u_4} [Nonempty α] [semilattice_inf α] [preorder β] [no_bot_order β] {f : α → β} (h : tendsto f at_bot at_bot) : ¬bdd_below (set.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`. -/
theorem tendsto_at_top_of_monotone_of_filter {ι : Type u_1} {α : Type u_3} [preorder ι] [preorder α] {l : filter ι} {u : ι → α} (h : monotone u) [ne_bot l] (hu : tendsto u l at_top) : tendsto u at_top at_top :=
  monotone.tendsto_at_top_at_top h fun (b : α) => eventually.exists (tendsto.eventually hu (mem_at_top b))

/-- If a monotone function `u : ι → α` tends to `at_bot` along *some* non-trivial filter `l`, then
it tends to `at_bot` along `at_bot`. -/
theorem tendsto_at_bot_of_monotone_of_filter {ι : Type u_1} {α : Type u_3} [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 (monotone.order_dual h) hu

theorem tendsto_at_top_of_monotone_of_subseq {ι : Type u_1} {ι' : Type u_2} {α : Type u_3} [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)

theorem tendsto_at_bot_of_monotone_of_subseq {ι : Type u_1} {ι' : Type u_2} {α : Type u_3} [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`. -/
theorem map_at_top_finset_prod_le_of_prod_eq {α : Type u_3} {β : Type u_4} {γ : Type u_5} [comm_monoid α] {f : β → α} {g : γ → α} (h_eq : ∀ (u : finset γ),
  ∃ (v : finset β),
    ∀ (v' : finset β),
      v ⊆ v' → ∃ (u' : finset γ), u ⊆ u' ∧ (finset.prod u' fun (x : γ) => g x) = finset.prod v' fun (b : β) => f b) : map (fun (s : finset β) => finset.prod s fun (b : β) => f b) at_top ≤
  map (fun (s : finset γ) => finset.prod s fun (x : γ) => g x) at_top := sorry

theorem has_antimono_basis.tendsto {ι : Type u_1} {α : Type u_3} [semilattice_sup ι] [Nonempty ι] {l : filter α} {p : ι → Prop} {s : ι → set α} (hl : has_antimono_basis l p s) {φ : ι → α} (h : ∀ (i : ι), φ i ∈ s i) : tendsto φ at_top l := sorry

namespace is_countably_generated

/-- 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`. -/
theorem tendsto_iff_seq_tendsto {α : Type u_3} {β : Type u_4} {f : α → β} {k : filter α} {l : filter β} (hcb : is_countably_generated k) : tendsto f k l ↔ ∀ (x : ℕ → α), tendsto x at_top k → tendsto (f ∘ x) at_top l := sorry

theorem tendsto_of_seq_tendsto {α : Type u_3} {β : Type u_4} {f : α → β} {k : filter α} {l : filter β} (hcb : is_countably_generated k) : (∀ (x : ℕ → α), tendsto x at_top k → tendsto (f ∘ x) at_top l) → tendsto f k l :=
  iff.mpr (tendsto_iff_seq_tendsto hcb)

theorem subseq_tendsto {α : Type u_3} {f : filter α} (hf : is_countably_generated f) {u : ℕ → α} (hx : ne_bot (f ⊓ map u at_top)) : ∃ (θ : ℕ → ℕ), strict_mono θ ∧ tendsto (u ∘ θ) at_top f := sorry

end is_countably_generated

end filter

theorem exists_lt_mul_self {R : Type u_6} [linear_ordered_semiring R] (a : R) : ∃ (x : R), ∃ (H : x ≥ 0), a < x * x := sorry

theorem exists_le_mul_self {R : Type u_6} [linear_ordered_semiring R] (a : R) : ∃ (x : R), ∃ (H : x ≥ 0), a ≤ x * x := sorry

namespace order_iso

@[simp] theorem comap_at_top {α : Type u_3} {β : Type u_4} [preorder α] [preorder β] (e : α ≃o β) : filter.comap (⇑e) filter.at_top = filter.at_top := sorry

@[simp] theorem comap_at_bot {α : Type u_3} {β : Type u_4} [preorder α] [preorder β] (e : α ≃o β) : filter.comap (⇑e) filter.at_bot = filter.at_bot :=
  comap_at_top (order_iso.dual e)

@[simp] theorem map_at_top {α : Type u_3} {β : Type u_4} [preorder α] [preorder β] (e : α ≃o β) : filter.map (⇑e) filter.at_top = filter.at_top := sorry

@[simp] theorem map_at_bot {α : Type u_3} {β : Type u_4} [preorder α] [preorder β] (e : α ≃o β) : filter.map (⇑e) filter.at_bot = filter.at_bot :=
  map_at_top (order_iso.dual e)

theorem tendsto_at_top {α : Type u_3} {β : Type u_4} [preorder α] [preorder β] (e : α ≃o β) : filter.tendsto (⇑e) filter.at_top filter.at_top :=
  eq.le (map_at_top e)

theorem tendsto_at_bot {α : Type u_3} {β : Type u_4} [preorder α] [preorder β] (e : α ≃o β) : filter.tendsto (⇑e) filter.at_bot filter.at_bot :=
  eq.le (map_at_bot e)

@[simp] theorem tendsto_at_top_iff {α : Type u_3} {β : Type u_4} {γ : Type u_5} [preorder α] [preorder β] {l : filter γ} {f : γ → α} (e : α ≃o β) : filter.tendsto (fun (x : γ) => coe_fn e (f x)) l filter.at_top ↔ filter.tendsto f l filter.at_top := sorry

@[simp] theorem tendsto_at_bot_iff {α : Type u_3} {β : Type u_4} {γ : Type u_5} [preorder α] [preorder β] {l : filter γ} {f : γ → α} (e : α ≃o β) : filter.tendsto (fun (x : γ) => coe_fn e (f x)) l filter.at_bot ↔ filter.tendsto f l filter.at_bot :=
  tendsto_at_top_iff (order_iso.dual e)

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.-/
theorem function.injective.map_at_top_finset_sum_eq {α : Type u_3} {β : Type u_4} {γ : Type u_5} [add_comm_monoid α] {g : γ → β} (hg : function.injective g) {f : β → α} (hf : ∀ (x : β), ¬x ∈ set.range g → f x = 0) : filter.map (fun (s : finset γ) => finset.sum s fun (i : γ) => f (g i)) filter.at_top =
  filter.map (fun (s : finset β) => finset.sum s fun (i : β) => f i) filter.at_top := sorry


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

import field_theory.splitting_field

/-!
# Cubics and discriminants

This file defines cubic polynomials over a semiring and their discriminants over a splitting field.

## Main definitions

* `cubic`: the structure representing a cubic polynomial.
* `disc`: the discriminant of a cubic polynomial.

## Main statements

* `disc_ne_zero_iff_roots_nodup`: the cubic discriminant is not equal to zero if and only if
  the cubic has no duplicate roots.

## References

* https://en.wikipedia.org/wiki/Cubic_equation
* https://en.wikipedia.org/wiki/Discriminant

## Tags

cubic, discriminant, polynomial, root
-/

noncomputable theory

/-- The structure representing a cubic polynomial. -/
@[ext] structure cubic (R : Type*) := (a b c d : R)

namespace cubic

open cubic polynomial
open_locale polynomial

variables {R S F K : Type*}

instance [inhabited R] : inhabited (cubic R) := ⟨⟨default, default, default, default⟩⟩

instance [has_zero R] : has_zero (cubic R) := ⟨⟨0, 0, 0, 0⟩⟩

section basic

variables {P : cubic R} [semiring R]

/-- Convert a cubic polynomial to a polynomial. -/
def to_poly (P : cubic R) : R[X] := C P.a * X ^ 3 + C P.b * X ^ 2 + C P.c * X + C P.d

/-! ### Coefficients -/

section coeff

private lemma coeffs :
  (∀ n > 3, P.to_poly.coeff n = 0) ∧ P.to_poly.coeff 3 = P.a ∧ P.to_poly.coeff 2 = P.b
    ∧ P.to_poly.coeff 1 = P.c ∧ P.to_poly.coeff 0 = P.d :=
begin
  simp only [to_poly, coeff_add, coeff_C, coeff_C_mul_X, coeff_C_mul_X_pow],
  norm_num,
  intros n hn,
  repeat { rw [if_neg] },
  any_goals { linarith only [hn] },
  repeat { rw [zero_add] }
end

@[simp] lemma coeff_gt_three (n : ℕ) (hn : 3 < n) : P.to_poly.coeff n = 0 := coeffs.1 n hn

@[simp] lemma coeff_three : P.to_poly.coeff 3 = P.a := coeffs.2.1

@[simp] lemma coeff_two : P.to_poly.coeff 2 = P.b := coeffs.2.2.1

@[simp] lemma coeff_one : P.to_poly.coeff 1 = P.c := coeffs.2.2.2.1

@[simp] lemma coeff_zero : P.to_poly.coeff 0 = P.d := coeffs.2.2.2.2

lemma a_of_eq {Q : cubic R} (h : P.to_poly = Q.to_poly) : P.a = Q.a :=
by rw [← coeff_three, h, coeff_three]

lemma b_of_eq {Q : cubic R} (h : P.to_poly = Q.to_poly) : P.b = Q.b :=
by rw [← coeff_two, h, coeff_two]

lemma c_of_eq {Q : cubic R} (h : P.to_poly = Q.to_poly) : P.c = Q.c :=
by rw [← coeff_one, h, coeff_one]

lemma d_of_eq {Q : cubic R} (h : P.to_poly = Q.to_poly) : P.d = Q.d :=
by rw [← coeff_zero, h, coeff_zero]

@[simp] lemma to_poly_injective (P Q : cubic R) : P.to_poly = Q.to_poly ↔ P = Q :=
⟨λ h, cubic.ext _ _ (a_of_eq h) (b_of_eq h) (c_of_eq h) (d_of_eq h), congr_arg _⟩

@[simp] lemma of_a_eq_zero (ha : P.a = 0) : P.to_poly = C P.b * X ^ 2 + C P.c * X + C P.d :=
by rw [to_poly, C_eq_zero.mpr ha, zero_mul, zero_add]

@[simp] lemma of_a_b_eq_zero (ha : P.a = 0) (hb : P.b = 0) : P.to_poly = C P.c * X + C P.d :=
by rw [of_a_eq_zero ha, C_eq_zero.mpr hb, zero_mul, zero_add]

@[simp] lemma of_a_b_c_eq_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c = 0) : P.to_poly = C P.d :=
by rw [of_a_b_eq_zero ha hb, C_eq_zero.mpr hc, zero_mul, zero_add]

@[simp] lemma of_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c = 0) (hd : P.d = 0) : P.to_poly = 0 :=
by rw [of_a_b_c_eq_zero ha hb hc, C_eq_zero.mpr hd]

@[simp] lemma zero : (0 : cubic R).to_poly = 0 := of_zero rfl rfl rfl rfl

@[simp] lemma eq_zero_iff : P.to_poly = 0 ↔ P = 0 := by rw [← zero, to_poly_injective]

lemma ne_zero (h0 : ¬P.a = 0 ∨ ¬P.b = 0 ∨ ¬P.c = 0 ∨ ¬P.d = 0) : P.to_poly ≠ 0 :=
by { contrapose! h0, rw [eq_zero_iff.mp h0], exact ⟨rfl, rfl, rfl, rfl⟩ }

lemma ne_zero_of_a_ne_zero (ha : P.a ≠ 0) : P.to_poly ≠ 0 := (or_imp_distrib.mp ne_zero).1 ha

lemma ne_zero_of_b_ne_zero (hb : P.b ≠ 0) : P.to_poly ≠ 0 :=
(or_imp_distrib.mp (or_imp_distrib.mp ne_zero).2).1 hb

lemma ne_zero_of_c_ne_zero (hc : P.c ≠ 0) : P.to_poly ≠ 0 :=
(or_imp_distrib.mp (or_imp_distrib.mp (or_imp_distrib.mp ne_zero).2).2).1 hc

lemma ne_zero_of_d_ne_zero (hd : P.d ≠ 0) : P.to_poly ≠ 0 :=
(or_imp_distrib.mp (or_imp_distrib.mp (or_imp_distrib.mp ne_zero).2).2).2 hd

end coeff

/-! ### Degrees -/

section degree

/-- The equivalence between cubic polynomials and polynomials of degree at most three. -/
@[simps] def equiv : cubic R ≃ {p : R[X] // p.degree ≤ 3} :=
{ to_fun    := λ P, ⟨P.to_poly, degree_cubic_le⟩,
  inv_fun   := λ f, ⟨coeff f 3, coeff f 2, coeff f 1, coeff f 0⟩,
  left_inv  := λ P, by ext; simp only [subtype.coe_mk, coeffs],
  right_inv := λ f,
  begin
    ext (_ | _ | _ | _ | n); simp only [subtype.coe_mk, coeffs],
    have h3 : 3 < n + 4 := by linarith only,
    rw [coeff_gt_three _ h3,
        (degree_le_iff_coeff_zero (f : R[X]) 3).mp f.2 _ $ with_bot.coe_lt_coe.mpr h3]
  end }

lemma degree (ha : P.a ≠ 0) : P.to_poly.degree = 3 := degree_cubic ha

lemma degree_of_a_eq_zero (ha : P.a = 0) (hb : P.b ≠ 0) : P.to_poly.degree = 2 :=
by rw [of_a_eq_zero ha, degree_quadratic hb]

lemma degree_of_a_b_eq_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c ≠ 0) : P.to_poly.degree = 1 :=
by rw [of_a_b_eq_zero ha hb, degree_linear hc]

lemma degree_of_a_b_c_eq_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c = 0) (hd : P.d ≠ 0) :
  P.to_poly.degree = 0 :=
by rw [of_a_b_c_eq_zero ha hb hc, degree_C hd]

lemma degree_of_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c = 0) (hd : P.d = 0) :
  P.to_poly.degree = ⊥ :=
by rw [of_zero ha hb hc hd, degree_zero]

lemma leading_coeff (ha : P.a ≠ 0) : P.to_poly.leading_coeff = P.a := leading_coeff_cubic ha

lemma leading_coeff_of_a_eq_zero (ha : P.a = 0) (hb : P.b ≠ 0) : P.to_poly.leading_coeff = P.b :=
by rw [of_a_eq_zero ha, leading_coeff_quadratic hb]

lemma leading_coeff_of_a_b_eq_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c ≠ 0) :
  P.to_poly.leading_coeff = P.c :=
by rw [of_a_b_eq_zero ha hb, leading_coeff_linear hc]

lemma leading_coeff_of_a_b_c_eq_zero (ha : P.a = 0) (hb : P.b = 0) (hc : P.c = 0) :
  P.to_poly.leading_coeff = P.d :=
by rw [of_a_b_c_eq_zero ha hb hc, leading_coeff_C]

end degree

/-! ### Map across a homomorphism -/

section map

variables [semiring S] {φ : R →+* S}

/-- Map a cubic polynomial across a semiring homomorphism. -/
def map (φ : R →+* S) (P : cubic R) : cubic S := ⟨φ P.a, φ P.b, φ P.c, φ P.d⟩

lemma map_to_poly : (map φ P).to_poly = polynomial.map φ P.to_poly :=
by simp only [map, to_poly, map_C, map_X, polynomial.map_add, polynomial.map_mul,
              polynomial.map_pow]

end map

end basic

section roots

open multiset

/-! ### Roots over an extension -/

section extension

variables {P : cubic R} [comm_ring R] [comm_ring S] {φ : R →+* S}

/-- The roots of a cubic polynomial. -/
def roots [is_domain R] (P : cubic R) : multiset R := P.to_poly.roots

lemma map_roots [is_domain S] : (map φ P).roots = (polynomial.map φ P.to_poly).roots :=
by rw [roots, map_to_poly]

theorem mem_roots_iff [is_domain R] (h0 : P.to_poly ≠ 0) (x : R) :
  x ∈ P.roots ↔ P.a * x ^ 3 + P.b * x ^ 2 + P.c * x + P.d = 0 :=
begin
  rw [roots, mem_roots h0, is_root, to_poly],
  simp only [eval_C, eval_X, eval_add, eval_mul, eval_pow]
end

theorem card_roots_le [is_domain R] [decidable_eq R] : P.roots.to_finset.card ≤ 3 :=
begin
  apply (to_finset_card_le P.to_poly.roots).trans,
  by_cases hP : P.to_poly = 0,
  { exact (card_roots' P.to_poly).trans (by { rw [hP, nat_degree_zero], exact zero_le 3 }) },
  { simpa only [← @with_bot.coe_le_coe _ _ _ 3] using (card_roots hP).trans degree_cubic_le }
end

end extension

variables {P : cubic F} [field F] [field K] {φ : F →+* K} {x y z : K}

/-! ### Roots over a splitting field -/

section split

theorem splits_iff_card_roots (ha : P.a ≠ 0) : splits φ P.to_poly ↔ (map φ P).roots.card = 3 :=
begin
  replace ha : (map φ P).a ≠ 0 := (ring_hom.map_ne_zero φ).mpr ha,
  nth_rewrite_lhs 0 [← ring_hom.id_comp φ],
  rw [roots, ← splits_map_iff, ← map_to_poly, splits_iff_card_roots,
      ← ((degree_eq_iff_nat_degree_eq $ ne_zero_of_a_ne_zero ha).mp $ degree ha : _ = 3)]
end

theorem splits_iff_roots_eq_three (ha : P.a ≠ 0) :
  splits φ P.to_poly ↔ ∃ x y z : K, (map φ P).roots = {x, y, z} :=
by rw [splits_iff_card_roots ha, card_eq_three]

theorem eq_prod_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
  (map φ P).to_poly = C (φ P.a) * (X - C x) * (X - C y) * (X - C z) :=
begin
  rw [map_to_poly, eq_prod_roots_of_splits $ (splits_iff_roots_eq_three ha).mpr $ exists.intro x $
        exists.intro y $ exists.intro z h3, leading_coeff ha, ← map_roots, h3],
  change C (φ P.a) * ((X - C x) ::ₘ (X - C y) ::ₘ {X - C z}).prod = _,
  rw [prod_cons, prod_cons, prod_singleton, mul_assoc, mul_assoc]
end

theorem eq_sum_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
  map φ P = ⟨φ P.a, φ P.a * -(x + y + z), φ P.a * (x * y + x * z + y * z), φ P.a * -(x * y * z)⟩ :=
begin
  apply_fun to_poly,
  any_goals { exact λ P Q, (to_poly_injective P Q).mp },
  rw [eq_prod_three_roots ha h3, to_poly],
  simp only [C_neg, C_add, C_mul],
  ring1
end

theorem b_eq_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
  φ P.b = φ P.a * -(x + y + z) :=
by injection eq_sum_three_roots ha h3

theorem c_eq_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
  φ P.c = φ P.a * (x * y + x * z + y * z) :=
by injection eq_sum_three_roots ha h3

theorem d_eq_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
  φ P.d = φ P.a * -(x * y * z) :=
by injection eq_sum_three_roots ha h3

end split

/-! ### Discriminant over a splitting field -/

section discriminant

/-- The discriminant of a cubic polynomial. -/
def disc {R : Type*} [ring R] (P : cubic R) : R :=
P.b ^ 2 * P.c ^ 2 - 4 * P.a * P.c ^ 3 - 4 * P.b ^ 3 * P.d - 27 * P.a ^ 2 * P.d ^ 2
  + 18 * P.a * P.b * P.c * P.d

theorem disc_eq_prod_three_roots (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
  φ P.disc = (φ P.a * φ P.a * (x - y) * (x - z) * (y - z)) ^ 2 :=
begin
  simp only [disc, ring_hom.map_add, ring_hom.map_sub, ring_hom.map_mul, map_pow],
  simp only [ring_hom.map_one, map_bit0, map_bit1],
  rw [b_eq_three_roots ha h3, c_eq_three_roots ha h3, d_eq_three_roots ha h3],
  ring1
end

theorem disc_ne_zero_iff_roots_ne (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
  P.disc ≠ 0 ↔ x ≠ y ∧ x ≠ z ∧ y ≠ z :=
begin
  rw [← ring_hom.map_ne_zero φ, disc_eq_prod_three_roots ha h3, pow_two],
  simp only [mul_ne_zero_iff, sub_ne_zero],
  rw [ring_hom.map_ne_zero],
  tautology
end

theorem disc_ne_zero_iff_roots_nodup (ha : P.a ≠ 0) (h3 : (map φ P).roots = {x, y, z}) :
  P.disc ≠ 0 ↔ (map φ P).roots.nodup :=
begin
  rw [disc_ne_zero_iff_roots_ne ha h3, h3],
  change _ ↔ (x ::ₘ y ::ₘ {z}).nodup,
  rw [nodup_cons, nodup_cons, mem_cons, mem_singleton, mem_singleton],
  simp only [nodup_singleton],
  tautology
end

theorem card_roots_of_disc_ne_zero [decidable_eq K] (ha : P.a ≠ 0)
  (h3 : (map φ P).roots = {x, y, z}) (hd : P.disc ≠ 0) : (map φ P).roots.to_finset.card = 3 :=
begin
  rw [to_finset_card_of_nodup $ (disc_ne_zero_iff_roots_nodup ha h3).mp hd,
      ← splits_iff_card_roots ha, splits_iff_roots_eq_three ha],
  exact ⟨x, ⟨y, ⟨z, h3⟩⟩⟩
end

end discriminant

end roots

end cubic