/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alexander Bentkamp, Yury Kudriashov
-/
import analysis.convex.jensen
import analysis.normed_space.finite_dimension
import topology.path_connected
import topology.algebra.affine

/-!
# Topological and metric properties of convex sets

We prove the following facts:

* `convex.interior` : interior of a convex set is convex;
* `convex.closure` : closure of a convex set is convex;
* `set.finite.compact_convex_hull` : convex hull of a finite set is compact;
* `set.finite.is_closed_convex_hull` : convex hull of a finite set is closed;
* `convex_on_dist` : distance to a fixed point is convex on any convex set;
* `convex_hull_ediam`, `convex_hull_diam` : convex hull of a set has the same (e)metric diameter
  as the original set;
* `bounded_convex_hull` : convex hull of a set is bounded if and only if the original set
  is bounded.
* `bounded_std_simplex`, `is_closed_std_simplex`, `compact_std_simplex`: topological properties
  of the standard simplex;
-/

variables {ι : Type*} {E : Type*}

open set
open_locale pointwise

lemma real.convex_iff_is_preconnected {s : set ℝ} : convex ℝ s ↔ is_preconnected s :=
convex_iff_ord_connected.trans is_preconnected_iff_ord_connected.symm

alias real.convex_iff_is_preconnected ↔ convex.is_preconnected is_preconnected.convex

/-! ### Standard simplex -/

section std_simplex

variables [fintype ι]

/-- Every vector in `std_simplex 𝕜 ι` has `max`-norm at most `1`. -/
lemma std_simplex_subset_closed_ball :
  std_simplex ℝ ι ⊆ metric.closed_ball 0 1 :=
begin
  assume f hf,
  rw [metric.mem_closed_ball, dist_zero_right],
  refine (nnreal.coe_one ▸ nnreal.coe_le_coe.2 $ finset.sup_le $ λ x hx, _),
  change |f x| ≤ 1,
  rw [abs_of_nonneg $ hf.1 x],
  exact (mem_Icc_of_mem_std_simplex hf x).2
end

variable (ι)

/-- `std_simplex ℝ ι` is bounded. -/
lemma bounded_std_simplex : metric.bounded (std_simplex ℝ ι) :=
(metric.bounded_iff_subset_ball 0).2 ⟨1, std_simplex_subset_closed_ball⟩

/-- `std_simplex ℝ ι` is closed. -/
lemma is_closed_std_simplex : is_closed (std_simplex ℝ ι) :=
(std_simplex_eq_inter ℝ ι).symm ▸ is_closed.inter
  (is_closed_Inter $ λ i, is_closed_le continuous_const (continuous_apply i))
  (is_closed_eq (continuous_finset_sum _ $ λ x _, continuous_apply x) continuous_const)

/-- `std_simplex ℝ ι` is compact. -/
lemma compact_std_simplex : is_compact (std_simplex ℝ ι) :=
metric.compact_iff_closed_bounded.2 ⟨is_closed_std_simplex ι, bounded_std_simplex ι⟩

end std_simplex

/-! ### Topological vector space -/

section has_continuous_smul

variables [add_comm_group E] [module ℝ E] [topological_space E]
  [topological_add_group E] [has_continuous_smul ℝ E]

/-- In a topological vector space, the interior of a convex set is convex. -/
lemma convex.interior {s : set E} (hs : convex ℝ s) : convex ℝ (interior s) :=
convex_iff_pointwise_add_subset.mpr $ λ a b ha hb hab,
  have h : is_open (a • interior s + b • interior s), from
  or.elim (classical.em (a = 0))
  (λ heq,
    have hne : b ≠ 0, by { rw [heq, zero_add] at hab, rw hab, exact one_ne_zero },
    by { rw ← image_smul,
         exact (is_open_map_smul₀ hne _ is_open_interior).add_left } )
  (λ hne,
    by { rw ← image_smul,
         exact (is_open_map_smul₀ hne _ is_open_interior).add_right }),
  (subset_interior_iff_subset_of_open h).mpr $ subset.trans
    (by { simp only [← image_smul], apply add_subset_add; exact image_subset _ interior_subset })
    (convex_iff_pointwise_add_subset.mp hs ha hb hab)

/-- In a topological vector space, the closure of a convex set is convex. -/
lemma convex.closure {s : set E} (hs : convex ℝ s) : convex ℝ (closure s) :=
λ x y hx hy a b ha hb hab,
let f : E → E → E := λ x' y', a • x' + b • y' in
have hf : continuous (λ p : E × E, f p.1 p.2), from
  (continuous_const.smul continuous_fst).add (continuous_const.smul continuous_snd),
show f x y ∈ closure s, from
  mem_closure_of_continuous2 hf hx hy (λ x' hx' y' hy', subset_closure
  (hs hx' hy' ha hb hab))

/-- Convex hull of a finite set is compact. -/
lemma set.finite.compact_convex_hull {s : set E} (hs : finite s) :
  is_compact (convex_hull ℝ s) :=
begin
  rw [hs.convex_hull_eq_image],
  apply (compact_std_simplex _).image,
  haveI := hs.fintype,
  apply linear_map.continuous_on_pi
end

/-- Convex hull of a finite set is closed. -/
lemma set.finite.is_closed_convex_hull [t2_space E] {s : set E} (hs : finite s) :
  is_closed (convex_hull ℝ s) :=
hs.compact_convex_hull.is_closed

/-- If `x ∈ s` and `y ∈ interior s`, then the segment `(x, y]` is included in `interior s`. -/
lemma convex.add_smul_sub_mem_interior {s : set E} (hs : convex ℝ s)
  {x y : E} (hx : x ∈ s) (hy : y ∈ interior s) {t : ℝ} (ht : t ∈ Ioc (0 : ℝ) 1) :
  x + t • (y - x) ∈ interior s :=
begin
  let f := λ z, x + t • (z - x),
  have : is_open_map f := (is_open_map_add_left _).comp
    ((is_open_map_smul (units.mk0 _ ht.1.ne')).comp (is_open_map_sub_right _)),
  apply mem_interior.2 ⟨f '' (interior s), _, this _ is_open_interior, mem_image_of_mem _ hy⟩,
  refine image_subset_iff.2 (λ z hz, _),
  exact hs.add_smul_sub_mem hx (interior_subset hz) ⟨ht.1.le, ht.2⟩,
end

/-- If `x ∈ s` and `x + y ∈ interior s`, then `x + t y ∈ interior s` for `t ∈ (0, 1]`. -/
lemma convex.add_smul_mem_interior {s : set E} (hs : convex ℝ s)
  {x y : E} (hx : x ∈ s) (hy : x + y ∈ interior s) {t : ℝ} (ht : t ∈ Ioc (0 : ℝ) 1) :
  x + t • y ∈ interior s :=
by { convert hs.add_smul_sub_mem_interior hx hy ht, abel }

open affine_map

/-- If we dilate a convex set about a point in its interior by a scale `t > 1`, the interior of
the result contains the original set.

TODO Generalise this from convex sets to sets that are balanced / star-shaped about `x`. -/
lemma convex.subset_interior_image_homothety_of_one_lt
  {s : set E} (hs : convex ℝ s) {x : E} (hx : x ∈ interior s) (t : ℝ) (ht : 1 < t) :
  s ⊆ interior (image (homothety x t) s) :=
begin
  intros y hy,
  let I := { z | ∃ (u : ℝ), u ∈ Ioc (0 : ℝ) 1 ∧ z = y + u • (x - y) },
  have hI : I ⊆ interior s,
  { rintros z ⟨u, hu, rfl⟩, exact hs.add_smul_sub_mem_interior hy hx hu, },
  let z := homothety x t⁻¹ y,
  have hz₁ : z ∈ interior s,
  { suffices : z ∈ I, { exact hI this, },
    use 1 - t⁻¹,
    split,
    { simp only [mem_Ioc, sub_le_self_iff, inv_nonneg, sub_pos, inv_lt_one ht, true_and],
      linarith, },
    { simp only [z, homothety_apply, sub_smul, smul_sub, vsub_eq_sub, vadd_eq_add, one_smul],
      abel, }, },
  have ht' : t ≠ 0, { linarith, },
  have hz₂ : y = homothety x t z, { simp [z, ht', homothety_apply, smul_smul], },
  rw hz₂,
  rw mem_interior at hz₁ ⊢,
  obtain ⟨U, hU₁, hU₂, hU₃⟩ := hz₁,
  exact ⟨image (homothety x t) U,
         image_subset ⇑(homothety x t) hU₁,
         homothety_is_open_map x t ht' U hU₂,
         mem_image_of_mem ⇑(homothety x t) hU₃⟩,
end

lemma convex.is_path_connected {s : set E} (hconv : convex ℝ s) (hne : s.nonempty) :
  is_path_connected s :=
begin
  refine is_path_connected_iff.mpr ⟨hne, _⟩,
  intros x y x_in y_in,
  have H := hconv.segment_subset x_in y_in,
  rw segment_eq_image_line_map at H,
  exact joined_in.of_line affine_map.line_map_continuous.continuous_on (line_map_apply_zero _ _)
    (line_map_apply_one _ _) H
end

@[priority 100]
instance topological_add_group.path_connected : path_connected_space E :=
path_connected_space_iff_univ.mpr $ convex_univ.is_path_connected ⟨(0 : E), trivial⟩

end has_continuous_smul

/-! ### Normed vector space -/

section normed_space
variables [normed_group E] [normed_space ℝ E]

lemma convex_on_dist (z : E) (s : set E) (hs : convex ℝ s) :
  convex_on ℝ s (λz', dist z' z) :=
and.intro hs $
assume x y hx hy a b ha hb hab,
calc
  dist (a • x + b • y) z = ∥ (a • x + b • y) - (a + b) • z ∥ :
    by rw [hab, one_smul, normed_group.dist_eq]
  ... = ∥a • (x - z) + b • (y - z)∥ :
    by rw [add_smul, smul_sub, smul_sub, sub_eq_add_neg, sub_eq_add_neg, sub_eq_add_neg, neg_add,
           ←add_assoc, add_assoc (a • x), add_comm (b • y)]; simp only [add_assoc]
  ... ≤ ∥a • (x - z)∥ + ∥b • (y - z)∥ :
    norm_add_le (a • (x - z)) (b • (y - z))
  ... = a * dist x z + b * dist y z :
    by simp [norm_smul, normed_group.dist_eq, real.norm_eq_abs, abs_of_nonneg ha, abs_of_nonneg hb]

lemma convex_ball (a : E) (r : ℝ) : convex ℝ (metric.ball a r) :=
by simpa only [metric.ball, sep_univ] using (convex_on_dist a _ convex_univ).convex_lt r

lemma convex_closed_ball (a : E) (r : ℝ) : convex ℝ (metric.closed_ball a r) :=
by simpa only [metric.closed_ball, sep_univ] using (convex_on_dist a _ convex_univ).convex_le r

/-- Given a point `x` in the convex hull of `s` and a point `y`, there exists a point
of `s` at distance at least `dist x y` from `y`. -/
lemma convex_hull_exists_dist_ge {s : set E} {x : E} (hx : x ∈ convex_hull ℝ s) (y : E) :
  ∃ x' ∈ s, dist x y ≤ dist x' y :=
(convex_on_dist y _ (convex_convex_hull ℝ _)).exists_ge_of_mem_convex_hull hx

/-- Given a point `x` in the convex hull of `s` and a point `y` in the convex hull of `t`,
there exist points `x' ∈ s` and `y' ∈ t` at distance at least `dist x y`. -/
lemma convex_hull_exists_dist_ge2 {s t : set E} {x y : E}
  (hx : x ∈ convex_hull ℝ s) (hy : y ∈ convex_hull ℝ t) :
  ∃ (x' ∈ s) (y' ∈ t), dist x y ≤ dist x' y' :=
begin
  rcases convex_hull_exists_dist_ge hx y with ⟨x', hx', Hx'⟩,
  rcases convex_hull_exists_dist_ge hy x' with ⟨y', hy', Hy'⟩,
  use [x', hx', y', hy'],
  exact le_trans Hx' (dist_comm y x' ▸ dist_comm y' x' ▸ Hy')
end

/-- Emetric diameter of the convex hull of a set `s` equals the emetric diameter of `s. -/
@[simp] lemma convex_hull_ediam (s : set E) :
  emetric.diam (convex_hull ℝ s) = emetric.diam s :=
begin
  refine (emetric.diam_le $ λ x hx y hy, _).antisymm (emetric.diam_mono $ subset_convex_hull ℝ s),
  rcases convex_hull_exists_dist_ge2 hx hy with ⟨x', hx', y', hy', H⟩,
  rw edist_dist,
  apply le_trans (ennreal.of_real_le_of_real H),
  rw ← edist_dist,
  exact emetric.edist_le_diam_of_mem hx' hy'
end

/-- Diameter of the convex hull of a set `s` equals the emetric diameter of `s. -/
@[simp] lemma convex_hull_diam (s : set E) :
  metric.diam (convex_hull ℝ s) = metric.diam s :=
by simp only [metric.diam, convex_hull_ediam]

/-- Convex hull of `s` is bounded if and only if `s` is bounded. -/
@[simp] lemma bounded_convex_hull {s : set E} :
  metric.bounded (convex_hull ℝ s) ↔ metric.bounded s :=
by simp only [metric.bounded_iff_ediam_ne_top, convex_hull_ediam]

@[priority 100]
instance normed_space.loc_path_connected : loc_path_connected_space E :=
loc_path_connected_of_bases (λ x, metric.nhds_basis_ball)
  (λ x r r_pos, (convex_ball x r).is_path_connected $ by simp [r_pos])

end normed_space


/-
Copyright (c) 2016 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad
-/
import data.nat.basic
import order.monotone.basic

/-!
# Basic instances on the integers

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

This file contains:
* instances on `ℤ`. The stronger one is `int.comm_ring`.
  See `data/int/defs/order` for `int.linear_ordered_comm_ring`.
* basic lemmas about the integers, but which do not use the ordered algebra hierarchy.
-/

open nat

namespace int

instance : inhabited ℤ := ⟨int.zero⟩

instance : nontrivial ℤ :=
⟨⟨0, 1, int.zero_ne_one⟩⟩

instance : comm_ring ℤ :=
{ add            := int.add,
  add_assoc      := int.add_assoc,
  zero           := int.zero,
  zero_add       := int.zero_add,
  add_zero       := int.add_zero,
  neg            := int.neg,
  add_left_neg   := int.add_left_neg,
  add_comm       := int.add_comm,
  mul            := int.mul,
  mul_assoc      := int.mul_assoc,
  one            := int.one,
  one_mul        := int.one_mul,
  mul_one        := int.mul_one,
  sub            := int.sub,
  left_distrib   := int.distrib_left,
  right_distrib  := int.distrib_right,
  mul_comm       := int.mul_comm,
  nat_cast       := int.of_nat,
  nat_cast_zero  := rfl,
  nat_cast_succ  := λ n, rfl,
  int_cast       := λ n, n,
  int_cast_of_nat := λ n, rfl,
  int_cast_neg_succ_of_nat := λ n, rfl,
  zsmul          := (*),
  zsmul_zero'    := int.zero_mul,
  zsmul_succ'    := λ n x, by rw [nat.succ_eq_add_one, nat.add_comm, of_nat_add, int.distrib_right,
                      of_nat_one, int.one_mul],
  zsmul_neg'     := λ n x, int.neg_mul_eq_neg_mul_symm (n.succ : ℤ) x }

/-! ### Extra instances to short-circuit type class resolution

These also prevent non-computable instances like `int.normed_comm_ring` being used to construct
these instances non-computably.
-/
-- instance : has_sub int            := by apply_instance -- This is in core
instance : add_comm_monoid ℤ    := by apply_instance
instance : add_monoid ℤ         := by apply_instance
instance : monoid ℤ             := by apply_instance
instance : comm_monoid ℤ        := by apply_instance
instance : comm_semigroup ℤ     := by apply_instance
instance : semigroup ℤ          := by apply_instance
instance : add_comm_group ℤ     := by apply_instance
instance : add_group ℤ          := by apply_instance
instance : add_comm_semigroup ℤ := by apply_instance
instance : add_semigroup ℤ      := by apply_instance
instance : comm_semiring ℤ      := by apply_instance
instance : semiring ℤ           := by apply_instance
instance : ring ℤ               := by apply_instance
instance : distrib ℤ            := by apply_instance

end int

namespace int

@[simp] lemma add_neg_one (i : ℤ) : i + -1 = i - 1 := rfl

@[simp] theorem sign_coe_add_one (n : ℕ) : int.sign (n + 1) = 1 := rfl
@[simp] theorem sign_neg_succ_of_nat (n : ℕ) : int.sign -[1+ n] = -1 := rfl

@[simp] lemma default_eq_zero : default = (0 : ℤ) := rfl

meta instance : has_to_format ℤ := ⟨λ z, to_string z⟩

section
-- Note that here we are disabling the "safety" of reflected, to allow us to reuse `int.mk_numeral`.
-- The usual way to provide the required `reflected` instance would be via rewriting to prove that
-- the expression we use here is equivalent.
local attribute [semireducible] reflected
meta instance reflect : has_reflect ℤ :=
int.mk_numeral `(ℤ) `(by apply_instance : has_zero ℤ) `(by apply_instance : has_one ℤ)
                    `(by apply_instance : has_add ℤ) `(by apply_instance : has_neg ℤ)
end

attribute [simp] int.bodd

@[simp] theorem add_def {a b : ℤ} : int.add a b = a + b := rfl
@[simp] theorem mul_def {a b : ℤ} : int.mul a b = a * b := rfl

@[simp] lemma neg_succ_not_nonneg (n : ℕ) : 0 ≤ -[1+ n] ↔ false :=
by { simp only [not_le, iff_false], exact int.neg_succ_lt_zero n, }

@[simp] lemma neg_succ_not_pos (n : ℕ) : 0 < -[1+ n] ↔ false :=
by simp only [not_lt, iff_false]

@[simp] lemma neg_succ_sub_one (n : ℕ) : -[1+ n] - 1 = -[1+ (n+1)] := rfl
@[simp] theorem coe_nat_mul_neg_succ (m n : ℕ) : (m : ℤ) * -[1+ n] = -(m * succ n) := rfl
@[simp] theorem neg_succ_mul_coe_nat (m n : ℕ) : -[1+ m] * n = -(succ m * n) := rfl
@[simp] theorem neg_succ_mul_neg_succ (m n : ℕ) : -[1+ m] * -[1+ n] = succ m * succ n := rfl

theorem coe_nat_le {m n : ℕ} : (↑m : ℤ) ≤ ↑n ↔ m ≤ n := coe_nat_le_coe_nat_iff m n
theorem coe_nat_lt {m n : ℕ} : (↑m : ℤ) < ↑n ↔ m < n := coe_nat_lt_coe_nat_iff m n
theorem coe_nat_inj' {m n : ℕ} : (↑m : ℤ) = ↑n ↔ m = n := int.coe_nat_eq_coe_nat_iff m n

lemma coe_nat_strict_mono : strict_mono (coe : ℕ → ℤ) := λ _ _, int.coe_nat_lt.2

lemma coe_nat_nonneg (n : ℕ) : 0 ≤ (n : ℤ) := coe_nat_le.2 (nat.zero_le _)

@[simp] lemma neg_of_nat_ne_zero (n : ℕ) : -[1+ n] ≠ 0 := λ h, int.no_confusion h
@[simp] lemma zero_ne_neg_of_nat (n : ℕ) : 0 ≠ -[1+ n] := λ h, int.no_confusion h

/-! ### succ and pred -/

/-- Immediate successor of an integer: `succ n = n + 1` -/
def succ (a : ℤ) := a + 1

/-- Immediate predecessor of an integer: `pred n = n - 1` -/
def pred (a : ℤ) := a - 1

theorem nat_succ_eq_int_succ (n : ℕ) : (nat.succ n : ℤ) = int.succ n := rfl

theorem pred_succ (a : ℤ) : pred (succ a) = a := add_sub_cancel _ _

theorem succ_pred (a : ℤ) : succ (pred a) = a := sub_add_cancel _ _

theorem neg_succ (a : ℤ) : -succ a = pred (-a) := neg_add _ _

theorem succ_neg_succ (a : ℤ) : succ (-succ a) = -a :=
by rw [neg_succ, succ_pred]

theorem neg_pred (a : ℤ) : -pred a = succ (-a) :=
by rw [neg_eq_iff_eq_neg.mp (neg_succ (-a)), neg_neg]

theorem pred_neg_pred (a : ℤ) : pred (-pred a) = -a :=
by rw [neg_pred, pred_succ]

theorem pred_nat_succ (n : ℕ) : pred (nat.succ n) = n := pred_succ n

theorem neg_nat_succ (n : ℕ) : -(nat.succ n : ℤ) = pred (-n) := neg_succ n

theorem succ_neg_nat_succ (n : ℕ) : succ (-nat.succ n) = -n := succ_neg_succ n

theorem add_one_le_iff {a b : ℤ} : a + 1 ≤ b ↔ a < b := iff.rfl

@[norm_cast] lemma coe_pred_of_pos {n : ℕ} (h : 0 < n) : ((n - 1 : ℕ) : ℤ) = (n : ℤ) - 1 :=
by { cases n, cases h, simp, }

@[elab_as_eliminator] protected lemma induction_on {p : ℤ → Prop}
  (i : ℤ) (hz : p 0) (hp : ∀ i : ℕ, p i → p (i + 1)) (hn : ∀ i : ℕ, p (-i) → p (-i - 1)) : p i :=
begin
  induction i,
  { induction i,
    { exact hz },
    { exact hp _ i_ih } },
  { have : ∀ n:ℕ, p (- n),
    { intro n, induction n,
      { simp [hz] },
      { convert hn _ n_ih using 1, simp [sub_eq_neg_add] } },
    exact this (i + 1) }
end

/-! ### nat abs -/

variables {a b : ℤ} {n : ℕ}

attribute [simp] nat_abs_of_nat nat_abs_zero nat_abs_one

theorem nat_abs_add_le (a b : ℤ) : nat_abs (a + b) ≤ nat_abs a + nat_abs b :=
begin
  have : ∀ (a b : ℕ), nat_abs (sub_nat_nat a (nat.succ b)) ≤ nat.succ (a + b),
  { refine (λ a b : ℕ, sub_nat_nat_elim a b.succ
      (λ m n i, n = b.succ → nat_abs i ≤ (m + b).succ) _ (λ i n e, _) rfl),
    { rintro i n rfl,
      rw [add_comm _ i, add_assoc],
      exact nat.le_add_right i (b.succ + b).succ },
    { apply succ_le_succ,
      rw [← succ.inj e, ← add_assoc, add_comm],
      apply nat.le_add_right } },
  cases a; cases b with b b; simp [nat_abs, nat.succ_add];
  try {refl}; [skip, rw add_comm a b]; apply this
end

lemma nat_abs_sub_le (a b : ℤ) : nat_abs (a - b) ≤ nat_abs a + nat_abs b :=
by { rw [sub_eq_add_neg, ← int.nat_abs_neg b], apply nat_abs_add_le }

theorem nat_abs_neg_of_nat (n : ℕ) : nat_abs (neg_of_nat n) = n :=
by cases n; refl

theorem nat_abs_mul (a b : ℤ) : nat_abs (a * b) = (nat_abs a) * (nat_abs b) :=
by cases a; cases b;
  simp only [← int.mul_def, int.mul, nat_abs_neg_of_nat, eq_self_iff_true, int.nat_abs]

lemma nat_abs_mul_nat_abs_eq {a b : ℤ} {c : ℕ} (h : a * b = (c : ℤ)) :
  a.nat_abs * b.nat_abs = c :=
by rw [← nat_abs_mul, h, nat_abs_of_nat]

lemma nat_abs_mul_self' (a : ℤ) : (nat_abs a * nat_abs a : ℤ) = a * a :=
by rw [← int.coe_nat_mul, nat_abs_mul_self]

theorem neg_succ_of_nat_eq' (m : ℕ) : -[1+ m] = -m - 1 :=
by simp [neg_succ_of_nat_eq, sub_eq_neg_add]

lemma nat_abs_ne_zero_of_ne_zero {z : ℤ} (hz : z ≠ 0) : z.nat_abs ≠ 0 :=
λ h, hz $ int.eq_zero_of_nat_abs_eq_zero h

@[simp] lemma nat_abs_eq_zero {a : ℤ} : a.nat_abs = 0 ↔ a = 0 :=
⟨int.eq_zero_of_nat_abs_eq_zero, λ h, h.symm ▸ rfl⟩

lemma nat_abs_lt_nat_abs_of_nonneg_of_lt {a b : ℤ} (w₁ : 0 ≤ a) (w₂ : a < b) :
  a.nat_abs < b.nat_abs :=
begin
  lift b to ℕ using le_trans w₁ (le_of_lt w₂),
  lift a to ℕ using w₁,
  simpa [coe_nat_lt] using w₂,
end

lemma nat_abs_eq_nat_abs_iff {a b : ℤ} : a.nat_abs = b.nat_abs ↔ a = b ∨ a = -b :=
begin
  split; intro h,
  { cases int.nat_abs_eq a with h₁ h₁; cases int.nat_abs_eq b with h₂ h₂;
    rw [h₁, h₂]; simp [h], },
  { cases h; rw h, rw int.nat_abs_neg, },
end

lemma nat_abs_eq_iff {a : ℤ} {n : ℕ} : a.nat_abs = n ↔ a = n ∨ a = -n :=
by rw [←int.nat_abs_eq_nat_abs_iff, int.nat_abs_of_nat]

/-! ### `/`  -/

@[simp] theorem of_nat_div (m n : ℕ) : of_nat (m / n) = (of_nat m) / (of_nat n) := rfl

@[simp, norm_cast] theorem coe_nat_div (m n : ℕ) : ((m / n : ℕ) : ℤ) = m / n := rfl

theorem neg_succ_of_nat_div (m : ℕ) {b : ℤ} (H : 0 < b) :
  -[1+m] / b = -(m / b + 1) :=
match b, eq_succ_of_zero_lt H with ._, ⟨n, rfl⟩ := rfl end

-- Will be generalized to Euclidean domains.
local attribute [simp]
protected theorem zero_div : ∀ (b : ℤ), 0 / b = 0
| (n:ℕ) := show of_nat _ = _, by simp
| -[1+ n] := show -of_nat _ = _, by simp

local attribute [simp] -- Will be generalized to Euclidean domains.
protected theorem div_zero : ∀ (a : ℤ), a / 0 = 0
| (n:ℕ) := show of_nat _ = _, by simp
| -[1+ n] := rfl

@[simp] protected theorem div_neg : ∀ (a b : ℤ), a / -b = -(a / b)
| (m : ℕ) 0       := show of_nat (m / 0) = -(m / 0 : ℕ), by rw nat.div_zero; refl
| (m : ℕ) (n+1:ℕ) := rfl
| (m : ℕ) -[1+ n] := (neg_neg _).symm
| -[1+ m] 0       := rfl
| -[1+ m] (n+1:ℕ) := rfl
| -[1+ m] -[1+ n] := rfl

theorem div_of_neg_of_pos {a b : ℤ} (Ha : a < 0) (Hb : 0 < b) : a / b = -((-a - 1) / b + 1) :=
match a, b, eq_neg_succ_of_lt_zero Ha, eq_succ_of_zero_lt Hb with
| ._, ._, ⟨m, rfl⟩, ⟨n, rfl⟩ :=
  by change (- -[1+ m] : ℤ) with (m+1 : ℤ); rw add_sub_cancel; refl
end

protected theorem div_nonneg {a b : ℤ} (Ha : 0 ≤ a) (Hb : 0 ≤ b) : 0 ≤ a / b :=
match a, b, eq_coe_of_zero_le Ha, eq_coe_of_zero_le Hb with
| ._, ._, ⟨m, rfl⟩, ⟨n, rfl⟩ := coe_zero_le _
end

theorem div_neg' {a b : ℤ} (Ha : a < 0) (Hb : 0 < b) : a / b < 0 :=
match a, b, eq_neg_succ_of_lt_zero Ha, eq_succ_of_zero_lt Hb with
| ._, ._, ⟨m, rfl⟩, ⟨n, rfl⟩ := neg_succ_lt_zero _
end

@[simp] protected theorem div_one : ∀ (a : ℤ), a / 1 = a
| (n:ℕ) := congr_arg of_nat (nat.div_one _)
| -[1+ n] := congr_arg neg_succ_of_nat (nat.div_one _)

theorem div_eq_zero_of_lt {a b : ℤ} (H1 : 0 ≤ a) (H2 : a < b) : a / b = 0 :=
match a, b, eq_coe_of_zero_le H1, eq_succ_of_zero_lt (lt_of_le_of_lt H1 H2), H2  with
| ._, ._, ⟨m, rfl⟩, ⟨n, rfl⟩, H2 :=
  congr_arg of_nat $ nat.div_eq_of_lt $ lt_of_coe_nat_lt_coe_nat H2
end

/-! ### mod -/

theorem of_nat_mod (m n : nat) : (m % n : ℤ) = of_nat (m % n) := rfl

@[simp, norm_cast] theorem coe_nat_mod (m n : ℕ) : (↑(m % n) : ℤ) = ↑m % ↑n := rfl

theorem neg_succ_of_nat_mod (m : ℕ) {b : ℤ} (bpos : 0 < b) :
  -[1+m] % b = b - 1 - m % b :=
by rw [sub_sub, add_comm]; exact
match b, eq_succ_of_zero_lt bpos with ._, ⟨n, rfl⟩ := rfl end

@[simp] theorem mod_neg : ∀ (a b : ℤ), a % -b = a % b
| (m : ℕ) n := @congr_arg ℕ ℤ _ _ (λ i, ↑(m % i)) (nat_abs_neg _)
| -[1+ m] n := @congr_arg ℕ ℤ _ _ (λ i, sub_nat_nat i (nat.succ (m % i))) (nat_abs_neg _)

local attribute [simp] -- Will be generalized to Euclidean domains.
theorem zero_mod (b : ℤ) : 0 % b = 0 := rfl

local attribute [simp] -- Will be generalized to Euclidean domains.
theorem mod_zero : ∀ (a : ℤ), a % 0 = a
| (m : ℕ) := congr_arg of_nat $ nat.mod_zero _
| -[1+ m] := congr_arg neg_succ_of_nat $ nat.mod_zero _

local attribute [simp] -- Will be generalized to Euclidean domains.
theorem mod_one : ∀ (a : ℤ), a % 1 = 0
| (m : ℕ) := congr_arg of_nat $ nat.mod_one _
| -[1+ m] := show (1 - (m % 1).succ : ℤ) = 0, by rw nat.mod_one; refl

theorem mod_eq_of_lt {a b : ℤ} (H1 : 0 ≤ a) (H2 : a < b) : a % b = a :=
match a, b, eq_coe_of_zero_le H1, eq_coe_of_zero_le (le_trans H1 (le_of_lt H2)), H2 with
| ._, ._, ⟨m, rfl⟩, ⟨n, rfl⟩, H2 :=
  congr_arg of_nat $ nat.mod_eq_of_lt (lt_of_coe_nat_lt_coe_nat H2)
end

theorem mod_add_div_aux (m n : ℕ) : (n - (m % n + 1) - (n * (m / n) + n) : ℤ) = -[1+ m] :=
begin
  rw [← sub_sub, neg_succ_of_nat_coe, sub_sub (n:ℤ), eq_comm, neg_eq_iff_eq_neg,
      neg_sub, sub_sub_self, add_right_comm],
  exact @congr_arg ℕ ℤ _ _ (λi, (i + 1 : ℤ)) (nat.mod_add_div _ _).symm
end

theorem mod_add_div : ∀ (a b : ℤ), a % b + b * (a / b) = a
| (m : ℕ) (n : ℕ) := congr_arg of_nat (nat.mod_add_div _ _)
| (m : ℕ) -[1+ n] := show (_ + -(n+1) * -((m) / (n + 1) : ℕ) : ℤ) = _,
  by rw [neg_mul_neg]; exact congr_arg of_nat (nat.mod_add_div _ _)
| -[1+ m] 0       := by rw [mod_zero, int.div_zero]; refl
| -[1+ m] (n+1:ℕ) := mod_add_div_aux m n.succ
| -[1+ m] -[1+ n] := mod_add_div_aux m n.succ

theorem div_add_mod (a b : ℤ) : b * (a / b) + a % b = a :=
(add_comm _ _).trans (mod_add_div _ _)

lemma mod_add_div' (m k : ℤ) : m % k + (m / k) * k = m :=
by { rw mul_comm, exact mod_add_div _ _ }

lemma div_add_mod' (m k : ℤ) : (m / k) * k + m % k = m :=
by { rw mul_comm, exact div_add_mod _ _ }

theorem mod_def (a b : ℤ) : a % b = a - b * (a / b) :=
eq_sub_of_add_eq (mod_add_div _ _)

/-! ### properties of `/` and `%` -/

@[simp] theorem mul_div_mul_of_pos {a : ℤ} (b c : ℤ) (H : 0 < a) : a * b / (a * c) = b / c :=
suffices ∀ (m k : ℕ) (b : ℤ), (m.succ * b / (m.succ * k) : ℤ) = b / k, from
match a, eq_succ_of_zero_lt H, c, eq_coe_or_neg c with
| ._, ⟨m, rfl⟩, ._, ⟨k, or.inl rfl⟩ := this _ _ _
| ._, ⟨m, rfl⟩, ._, ⟨k, or.inr rfl⟩ :=
  by rw [mul_neg, int.div_neg, int.div_neg];
     apply congr_arg has_neg.neg; apply this
end,
λ m k b, match b, k with
| (n : ℕ), k   := congr_arg of_nat (nat.mul_div_mul _ _ m.succ_pos)
| -[1+ n], 0   := by rw [int.coe_nat_zero, mul_zero, int.div_zero, int.div_zero]
| -[1+ n], k+1 := congr_arg neg_succ_of_nat $
  show (m.succ * n + m) / (m.succ * k.succ) = n / k.succ, begin
    apply nat.div_eq_of_lt_le,
    { refine le_trans _ (nat.le_add_right _ _),
      rw [← nat.mul_div_mul _ _ m.succ_pos],
      apply nat.div_mul_le_self },
    { change m.succ * n.succ ≤ _,
      rw [mul_left_comm],
      apply nat.mul_le_mul_left,
      apply (nat.div_lt_iff_lt_mul k.succ_pos).1,
      apply nat.lt_succ_self }
  end
end

@[simp] theorem mul_div_mul_of_pos_left (a : ℤ) {b : ℤ} (H : 0 < b) (c : ℤ) :
  a * b / (c * b) = a / c :=
by rw [mul_comm, mul_comm c, mul_div_mul_of_pos _ _ H]

@[simp] theorem mul_mod_mul_of_pos {a : ℤ} (H : 0 < a) (b c : ℤ) : a * b % (a * c) = a * (b % c) :=
by rw [mod_def, mod_def, mul_div_mul_of_pos _ _ H, mul_sub_left_distrib, mul_assoc]

theorem mul_div_cancel_of_mod_eq_zero {a b : ℤ} (H : a % b = 0) : b * (a / b) = a :=
by have := mod_add_div a b; rwa [H, zero_add] at this

theorem div_mul_cancel_of_mod_eq_zero {a b : ℤ} (H : a % b = 0) : a / b * b = a :=
by rw [mul_comm, mul_div_cancel_of_mod_eq_zero H]

lemma nat_abs_sign (z : ℤ) :
  z.sign.nat_abs = if z = 0 then 0 else 1 :=
by rcases z with (_ | _) | _; refl

lemma nat_abs_sign_of_nonzero {z : ℤ} (hz : z ≠ 0) :
  z.sign.nat_abs = 1 :=
by rw [int.nat_abs_sign, if_neg hz]

lemma sign_coe_nat_of_nonzero {n : ℕ} (hn : n ≠ 0) :
  int.sign n = 1 :=
begin
  obtain ⟨n, rfl⟩ := nat.exists_eq_succ_of_ne_zero hn,
  exact int.sign_of_succ n
end

@[simp] lemma sign_neg (z : ℤ) :
  int.sign (-z) = -int.sign z :=
by rcases z with (_ | _)| _; refl

theorem div_sign : ∀ a b, a / sign b = a * sign b
| a (n+1:ℕ) := by unfold sign; simp
| a 0       := by simp [sign]
| a -[1+ n] := by simp [sign]

@[simp] theorem sign_mul : ∀ a b, sign (a * b) = sign a * sign b
| a       0       := by simp
| 0       b       := by simp
| (m+1:ℕ) (n+1:ℕ) := rfl
| (m+1:ℕ) -[1+ n] := rfl
| -[1+ m] (n+1:ℕ) := rfl
| -[1+ m] -[1+ n] := rfl

theorem mul_sign : ∀ (i : ℤ), i * sign i = nat_abs i
| (n+1:ℕ) := mul_one _
| 0       := mul_zero _
| -[1+ n] := mul_neg_one _

theorem of_nat_add_neg_succ_of_nat_of_lt {m n : ℕ} (h : m < n.succ) :
  of_nat m + -[1+n] = -[1+ n - m] :=
begin
  change sub_nat_nat _ _ = _,
  have h' : n.succ - m = (n - m).succ,
  apply succ_sub,
  apply le_of_lt_succ h,
  simp [*, sub_nat_nat]
end

@[simp] theorem neg_add_neg (m n : ℕ) : -[1+m] + -[1+n] = -[1+nat.succ(m+n)] := rfl

/-! ### to_nat -/

theorem to_nat_eq_max : ∀ (a : ℤ), (to_nat a : ℤ) = max a 0
| (n : ℕ) := (max_eq_left (coe_zero_le n)).symm
| -[1+ n] := (max_eq_right (le_of_lt (neg_succ_lt_zero n))).symm

@[simp] lemma to_nat_zero : (0 : ℤ).to_nat = 0 := rfl

@[simp] lemma to_nat_one : (1 : ℤ).to_nat = 1 := rfl

@[simp] theorem to_nat_of_nonneg {a : ℤ} (h : 0 ≤ a) : (to_nat a : ℤ) = a :=
by rw [to_nat_eq_max, max_eq_left h]

@[simp] theorem to_nat_coe_nat (n : ℕ) : to_nat ↑n = n := rfl

@[simp] lemma to_nat_coe_nat_add_one {n : ℕ} : ((n : ℤ) + 1).to_nat = n + 1 := rfl

theorem le_to_nat (a : ℤ) : a ≤ to_nat a :=
by rw [to_nat_eq_max]; apply le_max_left

@[simp]lemma le_to_nat_iff {n : ℕ} {z : ℤ} (h : 0 ≤ z) : n ≤ z.to_nat ↔ (n : ℤ) ≤ z :=
by rw [←int.coe_nat_le_coe_nat_iff, int.to_nat_of_nonneg h]

lemma to_nat_add {a b : ℤ} (ha : 0 ≤ a) (hb : 0 ≤ b) :
  (a + b).to_nat = a.to_nat + b.to_nat :=
begin
  lift a to ℕ using ha,
  lift b to ℕ using hb,
  norm_cast,
end

lemma to_nat_add_nat {a : ℤ} (ha : 0 ≤ a) (n : ℕ) : (a + n).to_nat = a.to_nat + n :=
begin
  lift a to ℕ using ha,
  norm_cast,
end

@[simp]
lemma pred_to_nat : ∀ (i : ℤ), (i - 1).to_nat = i.to_nat - 1
| (0:ℕ)   := rfl
| (n+1:ℕ) := by simp
| -[1+ n] := rfl

@[simp] lemma to_nat_sub_to_nat_neg : ∀ (n : ℤ), ↑n.to_nat - ↑((-n).to_nat) = n
| (0 : ℕ)   := rfl
| (n+1 : ℕ) := show ↑(n+1) - (0:ℤ) = n+1, from sub_zero _
| -[1+ n]   := show 0 - (n+1 : ℤ)  = _,   from zero_sub _

@[simp] lemma to_nat_add_to_nat_neg_eq_nat_abs : ∀ (n : ℤ), (n.to_nat) + ((-n).to_nat) = n.nat_abs
| (0 : ℕ)   := rfl
| (n+1 : ℕ) := show (n+1) + 0 = n+1, from add_zero _
| -[1+ n]   := show 0 + (n+1) = n+1, from zero_add _

/-- If `n : ℕ`, then `int.to_nat' n = some n`, if `n : ℤ` is negative, then `int.to_nat' n = none`.
-/
def to_nat' : ℤ → option ℕ
| (n : ℕ) := some n
| -[1+ n] := none

theorem mem_to_nat' : ∀ (a : ℤ) (n : ℕ), n ∈ to_nat' a ↔ a = n
| (m : ℕ) n := option.some_inj.trans coe_nat_inj'.symm
| -[1+ m] n := by split; intro h; cases h

@[simp]
lemma to_nat_neg_nat : ∀ (n : ℕ), (-(n : ℤ)).to_nat = 0
| 0       := rfl
| (n + 1) := rfl

end int

attribute [irreducible] int.nonneg


/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.data.nat.basic

universes u

namespace Mathlib

/-- `fin n` is the subtype of `ℕ` consisting of natural numbers strictly smaller than `n`. -/
def fin (n : ℕ) := Subtype fun (i : ℕ) => i < n

namespace fin

/-- Backwards-compatible constructor for `fin n`. -/
def mk {n : ℕ} (i : ℕ) (h : i < n) : fin n := { val := i, property := h }

protected def lt {n : ℕ} (a : fin n) (b : fin n) := subtype.val a < subtype.val b

protected def le {n : ℕ} (a : fin n) (b : fin n) := subtype.val a ≤ subtype.val b

protected instance has_lt {n : ℕ} : HasLess (fin n) := { Less := fin.lt }

protected instance has_le {n : ℕ} : HasLessEq (fin n) := { LessEq := fin.le }

protected instance decidable_lt {n : ℕ} (a : fin n) (b : fin n) : Decidable (a < b) :=
  nat.decidable_lt (subtype.val a) (subtype.val b)

protected instance decidable_le {n : ℕ} (a : fin n) (b : fin n) : Decidable (a ≤ b) :=
  nat.decidable_le (subtype.val a) (subtype.val b)

def elim0 {α : fin 0 → Sort u} (x : fin 0) : α x := sorry

theorem eq_of_veq {n : ℕ} {i : fin n} {j : fin n} : subtype.val i = subtype.val j → i = j := sorry

theorem veq_of_eq {n : ℕ} {i : fin n} {j : fin n} : i = j → subtype.val i = subtype.val j := sorry

theorem ne_of_vne {n : ℕ} {i : fin n} {j : fin n} (h : subtype.val i ≠ subtype.val j) : i ≠ j :=
  fun (h' : i = j) => absurd (veq_of_eq h') h

theorem vne_of_ne {n : ℕ} {i : fin n} {j : fin n} (h : i ≠ j) : subtype.val i ≠ subtype.val j :=
  fun (h' : subtype.val i = subtype.val j) => absurd (eq_of_veq h') h

end fin

protected instance fin.decidable_eq (n : ℕ) : DecidableEq (fin n) :=
  fun (i j : fin n) =>
    decidable_of_decidable_of_iff (nat.decidable_eq (subtype.val i) (subtype.val j)) sorry

end Mathlib


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes, Johannes Hölzl, Scott Morrison, Jens Wagemaker
-/
import data.polynomial.degree.definitions
import data.polynomial.induction

/-!
# Theory of univariate polynomials

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

The main defs here are `eval₂`, `eval`, and `map`.
We give several lemmas about their interaction with each other and with module operations.
-/

noncomputable theory

open finset add_monoid_algebra
open_locale big_operators polynomial

namespace polynomial
universes u v w y
variables {R : Type u} {S : Type v} {T : Type w} {ι : Type y} {a b : R} {m n : ℕ}

section semiring
variables [semiring R] {p q r : R[X]}

section
variables [semiring S]
variables (f : R →+* S) (x : S)

/-- Evaluate a polynomial `p` given a ring hom `f` from the scalar ring
  to the target and a value `x` for the variable in the target -/
@[irreducible] def eval₂ (p : R[X]) : S :=
p.sum (λ e a, f a * x ^ e)

lemma eval₂_eq_sum {f : R →+* S} {x : S} : p.eval₂ f x = p.sum (λ e a, f a * x ^ e) :=
by rw eval₂

lemma eval₂_congr {R S : Type*} [semiring R] [semiring S]
  {f g : R →+* S} {s t : S} {φ ψ : R[X]} :
  f = g → s = t → φ = ψ → eval₂ f s φ = eval₂ g t ψ :=
by rintro rfl rfl rfl; refl

@[simp] lemma eval₂_at_zero : p.eval₂ f 0 = f (coeff p 0) :=
by simp only [eval₂_eq_sum, zero_pow_eq, mul_ite, mul_zero, mul_one, sum, not_not, mem_support_iff,
  sum_ite_eq', ite_eq_left_iff, ring_hom.map_zero, implies_true_iff, eq_self_iff_true]
  {contextual := tt}

@[simp] lemma eval₂_zero : (0 : R[X]).eval₂ f x = 0 :=
by simp [eval₂_eq_sum]

@[simp] lemma eval₂_C : (C a).eval₂ f x = f a :=
by simp [eval₂_eq_sum]

@[simp] lemma eval₂_X : X.eval₂ f x = x :=
by simp [eval₂_eq_sum]

@[simp] lemma eval₂_monomial {n : ℕ} {r : R} : (monomial n r).eval₂ f x = (f r) * x^n :=
by simp [eval₂_eq_sum]

@[simp] lemma eval₂_X_pow {n : ℕ} : (X^n).eval₂ f x = x^n :=
begin
  rw X_pow_eq_monomial,
  convert eval₂_monomial f x,
  simp,
end

@[simp] lemma eval₂_add : (p + q).eval₂ f x = p.eval₂ f x + q.eval₂ f x :=
by { simp only [eval₂_eq_sum], apply sum_add_index; simp [add_mul] }

@[simp] lemma eval₂_one : (1 : R[X]).eval₂ f x = 1 :=
by rw [← C_1, eval₂_C, f.map_one]

@[simp] lemma eval₂_bit0 : (bit0 p).eval₂ f x = bit0 (p.eval₂ f x) :=
by rw [bit0, eval₂_add, bit0]

@[simp] lemma eval₂_bit1 : (bit1 p).eval₂ f x = bit1 (p.eval₂ f x) :=
by rw [bit1, eval₂_add, eval₂_bit0, eval₂_one, bit1]

@[simp] lemma eval₂_smul (g : R →+* S) (p : R[X]) (x : S) {s : R} :
  eval₂ g x (s • p) = g s * eval₂ g x p :=
begin
  have A : p.nat_degree < p.nat_degree.succ := nat.lt_succ_self _,
  have B : (s • p).nat_degree < p.nat_degree.succ := (nat_degree_smul_le _ _).trans_lt A,
  rw [eval₂_eq_sum, eval₂_eq_sum, sum_over_range' _ _ _ A, sum_over_range' _ _ _ B];
  simp [mul_sum, mul_assoc],
end

@[simp] lemma eval₂_C_X : eval₂ C X p = p :=
polynomial.induction_on' p (λ p q hp hq, by simp [hp, hq])
  (λ n x, by rw [eval₂_monomial, ← smul_X_eq_monomial, C_mul'])

/-- `eval₂_add_monoid_hom (f : R →+* S) (x : S)` is the `add_monoid_hom` from
`R[X]` to `S` obtained by evaluating the pushforward of `p` along `f` at `x`. -/
@[simps] def eval₂_add_monoid_hom : R[X] →+ S :=
{ to_fun := eval₂ f x,
  map_zero' := eval₂_zero _ _,
  map_add' := λ _ _, eval₂_add _ _ }

@[simp] lemma eval₂_nat_cast (n : ℕ) : (n : R[X]).eval₂ f x = n :=
begin
  induction n with n ih,
  { simp only [eval₂_zero, nat.cast_zero] },
  { rw [n.cast_succ, eval₂_add, ih, eval₂_one, n.cast_succ] }
end

variables [semiring T]
lemma eval₂_sum (p : T[X]) (g : ℕ → T → R[X]) (x : S) :
  (p.sum g).eval₂ f x = p.sum (λ n a, (g n a).eval₂ f x) :=
begin
  let T : R[X] →+ S :=
    { to_fun := eval₂ f x, map_zero' := eval₂_zero _ _, map_add' := λ p q, eval₂_add _ _ },
  have A : ∀ y, eval₂ f x y = T y := λ y, rfl,
  simp only [A],
  rw [sum, T.map_sum, sum]
end

lemma eval₂_list_sum (l : list R[X]) (x : S) :
  eval₂ f x l.sum = (l.map (eval₂ f x)).sum :=
map_list_sum (eval₂_add_monoid_hom f x) l

lemma eval₂_multiset_sum (s : multiset R[X]) (x : S) :
  eval₂ f x s.sum = (s.map (eval₂ f x)).sum :=
map_multiset_sum (eval₂_add_monoid_hom f x) s

lemma eval₂_finset_sum (s : finset ι) (g : ι → R[X]) (x : S) :
  (∑ i in s, g i).eval₂ f x = ∑ i in s, (g i).eval₂ f x :=
map_sum (eval₂_add_monoid_hom f x) _ _

lemma eval₂_of_finsupp {f : R →+* S} {x : S} {p : add_monoid_algebra R ℕ} :
  eval₂ f x (⟨p⟩ : R[X]) = lift_nc ↑f (powers_hom S x) p :=
by { simp only [eval₂_eq_sum, sum, to_finsupp_sum, support, coeff], refl }

lemma eval₂_mul_noncomm (hf : ∀ k, commute (f $ q.coeff k) x) :
  eval₂ f x (p * q) = eval₂ f x p * eval₂ f x q :=
begin
  rcases p, rcases q,
  simp only [coeff] at hf,
  simp only [←of_finsupp_mul, eval₂_of_finsupp],
  exact lift_nc_mul _ _ p q (λ k n hn, (hf k).pow_right n)
end

@[simp] lemma eval₂_mul_X : eval₂ f x (p * X) = eval₂ f x p * x :=
begin
  refine trans (eval₂_mul_noncomm _ _ $ λ k, _) (by rw eval₂_X),
  rcases em (k = 1) with (rfl|hk),
  { simp },
  { simp [coeff_X_of_ne_one hk] }
end

@[simp] lemma eval₂_X_mul : eval₂ f x (X * p) = eval₂ f x p * x :=
by rw [X_mul, eval₂_mul_X]

lemma eval₂_mul_C' (h : commute (f a) x) : eval₂ f x (p * C a) = eval₂ f x p * f a :=
begin
  rw [eval₂_mul_noncomm, eval₂_C],
  intro k,
  by_cases hk : k = 0,
  { simp only [hk, h, coeff_C_zero, coeff_C_ne_zero] },
  { simp only [coeff_C_ne_zero hk, ring_hom.map_zero, commute.zero_left] }
end

lemma eval₂_list_prod_noncomm (ps : list R[X])
  (hf : ∀ (p ∈ ps) k, commute (f $ coeff p k) x) :
  eval₂ f x ps.prod = (ps.map (polynomial.eval₂ f x)).prod :=
begin
  induction ps using list.reverse_rec_on with ps p ihp,
  { simp },
  { simp only [list.forall_mem_append, list.forall_mem_singleton] at hf,
    simp [eval₂_mul_noncomm _ _ hf.2, ihp hf.1] }
end

/-- `eval₂` as a `ring_hom` for noncommutative rings -/
def eval₂_ring_hom' (f : R →+* S) (x : S) (hf : ∀ a, commute (f a) x) : R[X] →+* S :=
{ to_fun := eval₂ f x,
  map_add' := λ _ _, eval₂_add _ _,
  map_zero' := eval₂_zero _ _,
  map_mul' := λ p q, eval₂_mul_noncomm f x (λ k, hf $ coeff q k),
  map_one' := eval₂_one _ _ }

end

/-!
We next prove that eval₂ is multiplicative
as long as target ring is commutative
(even if the source ring is not).
-/
section eval₂

section
variables [semiring S] (f : R →+* S) (x : S)

lemma eval₂_eq_sum_range :
  p.eval₂ f x = ∑ i in finset.range (p.nat_degree + 1), f (p.coeff i) * x^i :=
trans (congr_arg _ p.as_sum_range) (trans (eval₂_finset_sum f _ _ x) (congr_arg _ (by simp)))

lemma eval₂_eq_sum_range' (f : R →+* S) {p : R[X]} {n : ℕ} (hn : p.nat_degree < n) (x : S) :
  eval₂ f x p = ∑ i in finset.range n, f (p.coeff i) * x ^ i :=
begin
  rw [eval₂_eq_sum, p.sum_over_range' _ _ hn],
  intro i,
  rw [f.map_zero, zero_mul]
end

end

section
variables [comm_semiring S] (f : R →+* S) (x : S)

@[simp] lemma eval₂_mul : (p * q).eval₂ f x = p.eval₂ f x * q.eval₂ f x :=
eval₂_mul_noncomm _ _ $ λ k, commute.all _ _

lemma eval₂_mul_eq_zero_of_left (q : R[X]) (hp : p.eval₂ f x = 0) :
  (p * q).eval₂ f x = 0 :=
begin
  rw eval₂_mul f x,
  exact mul_eq_zero_of_left hp (q.eval₂ f x)
end

lemma eval₂_mul_eq_zero_of_right (p : R[X]) (hq : q.eval₂ f x = 0) :
  (p * q).eval₂ f x = 0 :=
begin
  rw eval₂_mul f x,
  exact mul_eq_zero_of_right (p.eval₂ f x) hq
end

/-- `eval₂` as a `ring_hom` -/
def eval₂_ring_hom (f : R →+* S) (x : S) : R[X] →+* S :=
{ map_one' := eval₂_one _ _,
  map_mul' := λ _ _, eval₂_mul _ _,
  ..eval₂_add_monoid_hom f x }

@[simp] lemma coe_eval₂_ring_hom (f : R →+* S) (x) : ⇑(eval₂_ring_hom f x) = eval₂ f x := rfl

lemma eval₂_pow (n : ℕ) : (p ^ n).eval₂ f x = p.eval₂ f x ^ n := (eval₂_ring_hom _ _).map_pow _ _

lemma eval₂_dvd : p ∣ q → eval₂ f x p ∣ eval₂ f x q :=
(eval₂_ring_hom f x).map_dvd

lemma eval₂_eq_zero_of_dvd_of_eval₂_eq_zero (h : p ∣ q) (h0 : eval₂ f x p = 0) :
  eval₂ f x q = 0 :=
zero_dvd_iff.mp (h0 ▸ eval₂_dvd f x h)

lemma eval₂_list_prod (l : list R[X]) (x : S) :
  eval₂ f x l.prod = (l.map (eval₂ f x)).prod :=
map_list_prod (eval₂_ring_hom f x) l

end

end eval₂

section eval
variables {x : R}

/-- `eval x p` is the evaluation of the polynomial `p` at `x` -/
def eval : R → R[X] → R := eval₂ (ring_hom.id _)

lemma eval_eq_sum : p.eval x = p.sum (λ e a, a * x ^ e) :=
by { rw [eval, eval₂_eq_sum], refl }

lemma eval_eq_sum_range {p : R[X]} (x : R) :
  p.eval x = ∑ i in finset.range (p.nat_degree + 1), p.coeff i * x ^ i :=
by rw [eval_eq_sum, sum_over_range]; simp

lemma eval_eq_sum_range' {p : R[X]} {n : ℕ} (hn : p.nat_degree < n) (x : R) :
  p.eval x = ∑ i in finset.range n, p.coeff i * x ^ i :=
by rw [eval_eq_sum, p.sum_over_range' _ _ hn]; simp

@[simp] lemma eval₂_at_apply {S : Type*} [semiring S] (f : R →+* S) (r : R) :
  p.eval₂ f (f r) = f (p.eval r) :=
begin
  rw [eval₂_eq_sum, eval_eq_sum, sum, sum, f.map_sum],
  simp only [f.map_mul, f.map_pow],
end

@[simp] lemma eval₂_at_one {S : Type*} [semiring S] (f : R →+* S) : p.eval₂ f 1 = f (p.eval 1) :=
begin
  convert eval₂_at_apply f 1,
  simp,
end

@[simp] lemma eval₂_at_nat_cast {S : Type*} [semiring S] (f : R →+* S) (n : ℕ) :
  p.eval₂ f n = f (p.eval n) :=
begin
  convert eval₂_at_apply f n,
  simp,
end

@[simp] lemma eval_C : (C a).eval x = a := eval₂_C _ _

@[simp] lemma eval_nat_cast {n : ℕ} : (n : R[X]).eval x = n :=
by simp only [←C_eq_nat_cast, eval_C]

@[simp] lemma eval_X : X.eval x = x := eval₂_X _ _

@[simp] lemma eval_monomial {n a} : (monomial n a).eval x = a * x^n :=
eval₂_monomial _ _

@[simp] lemma eval_zero : (0 : R[X]).eval x = 0 :=  eval₂_zero _ _

@[simp] lemma eval_add : (p + q).eval x = p.eval x + q.eval x := eval₂_add _ _

@[simp] lemma eval_one : (1 : R[X]).eval x = 1 := eval₂_one _ _

@[simp] lemma eval_bit0 : (bit0 p).eval x = bit0 (p.eval x) := eval₂_bit0 _ _

@[simp] lemma eval_bit1 : (bit1 p).eval x = bit1 (p.eval x) := eval₂_bit1 _ _

@[simp] lemma eval_smul [monoid S] [distrib_mul_action S R] [is_scalar_tower S R R]
  (s : S) (p : R[X]) (x : R) :
  (s • p).eval x = s • p.eval x :=
by rw [← smul_one_smul R s p, eval, eval₂_smul, ring_hom.id_apply, smul_one_mul]

@[simp] lemma eval_C_mul : (C a * p).eval x = a * p.eval x :=
begin
  apply polynomial.induction_on' p,
  { intros p q ph qh,
    simp only [mul_add, eval_add, ph, qh], },
  { intros n b,
    simp only [mul_assoc, C_mul_monomial, eval_monomial], }
end

/-- A reformulation of the expansion of (1 + y)^d:
$$(d + 1) (1 + y)^d - (d + 1)y^d = \sum_{i = 0}^d {d + 1 \choose i} \cdot i \cdot y^{i - 1}.$$
-/
lemma eval_monomial_one_add_sub [comm_ring S] (d : ℕ) (y : S) :
  eval (1 + y) (monomial d (d + 1 : S)) - eval y (monomial d (d + 1 : S)) =
  ∑ (x_1 : ℕ) in range (d + 1), ↑((d + 1).choose x_1) * (↑x_1 * y ^ (x_1 - 1)) :=
begin
  have cast_succ : (d + 1 : S) = ((d.succ : ℕ) : S),
  { simp only [nat.cast_succ], },
  rw [cast_succ, eval_monomial, eval_monomial, add_comm, add_pow],
  conv_lhs { congr, congr, skip, apply_congr, skip, rw [one_pow, mul_one, mul_comm], },
  rw [sum_range_succ, mul_add, nat.choose_self, nat.cast_one, one_mul, add_sub_cancel, mul_sum,
    sum_range_succ', nat.cast_zero, zero_mul, mul_zero, add_zero],
  apply sum_congr rfl (λ y hy, _),
  rw [←mul_assoc, ←mul_assoc, ←nat.cast_mul, nat.succ_mul_choose_eq,
    nat.cast_mul, nat.add_sub_cancel],
end

/-- `polynomial.eval` as linear map -/
@[simps] def leval {R : Type*} [semiring R] (r : R) : R[X] →ₗ[R] R :=
{ to_fun := λ f, f.eval r,
  map_add' := λ f g, eval_add,
  map_smul' := λ c f, eval_smul c f r }

@[simp] lemma eval_nat_cast_mul {n : ℕ} : ((n : R[X]) * p).eval x = n * p.eval x :=
by rw [←C_eq_nat_cast, eval_C_mul]

@[simp] lemma eval_mul_X : (p * X).eval x = p.eval x * x :=
begin
  apply polynomial.induction_on' p,
  { intros p q ph qh,
    simp only [add_mul, eval_add, ph, qh], },
  { intros n a,
    simp only [←monomial_one_one_eq_X, monomial_mul_monomial, eval_monomial,
      mul_one, pow_succ', mul_assoc], }
end

@[simp] lemma eval_mul_X_pow {k : ℕ} : (p * X^k).eval x = p.eval x * x^k :=
begin
  induction k with k ih,
  { simp, },
  { simp [pow_succ', ←mul_assoc, ih], }
end

lemma eval_sum (p : R[X]) (f : ℕ → R → R[X]) (x : R) :
  (p.sum f).eval x = p.sum (λ n a, (f n a).eval x) :=
eval₂_sum _ _ _ _

lemma eval_finset_sum (s : finset ι) (g : ι → R[X]) (x : R) :
  (∑ i in s, g i).eval x = ∑ i in s, (g i).eval x := eval₂_finset_sum _ _ _ _

/-- `is_root p x` implies `x` is a root of `p`. The evaluation of `p` at `x` is zero -/
def is_root (p : R[X]) (a : R) : Prop := p.eval a = 0

instance [decidable_eq R] : decidable (is_root p a) := by unfold is_root; apply_instance

@[simp] lemma is_root.def : is_root p a ↔ p.eval a = 0 := iff.rfl

lemma is_root.eq_zero (h : is_root p x) : eval x p = 0 := h

lemma coeff_zero_eq_eval_zero (p : R[X]) : coeff p 0 = p.eval 0 :=
calc coeff p 0 = coeff p 0 * 0 ^ 0 : by simp
... = p.eval 0 :
  begin
    symmetry,
    rw [eval_eq_sum],
    exact finset.sum_eq_single _ (λ b _ hb, by simp [zero_pow (nat.pos_of_ne_zero hb)]) (by simp)
  end

lemma zero_is_root_of_coeff_zero_eq_zero {p : R[X]} (hp : p.coeff 0 = 0) : is_root p 0 :=
by rwa coeff_zero_eq_eval_zero at hp

lemma is_root.dvd {R : Type*} [comm_semiring R] {p q : R[X]} {x : R}
  (h : p.is_root x) (hpq : p ∣ q) : q.is_root x :=
by rwa [is_root, eval, eval₂_eq_zero_of_dvd_of_eval₂_eq_zero _ _ hpq]

lemma not_is_root_C (r a : R) (hr : r ≠ 0) : ¬ is_root (C r) a := by simpa using hr

lemma eval_surjective (x : R) : function.surjective $ eval x := λ y, ⟨C y, eval_C⟩

end eval

section comp

/-- The composition of polynomials as a polynomial. -/
def comp (p q : R[X]) : R[X] := p.eval₂ C q

lemma comp_eq_sum_left : p.comp q = p.sum (λ e a, C a * q ^ e) :=
by rw [comp, eval₂_eq_sum]

@[simp] lemma comp_X : p.comp X = p :=
begin
  simp only [comp, eval₂, C_mul_X_pow_eq_monomial],
  exact sum_monomial_eq _
end

@[simp] lemma X_comp : X.comp p = p := eval₂_X _ _

@[simp] lemma comp_C : p.comp (C a) = C (p.eval a) := by simp [comp, (C : R →+* _).map_sum]

@[simp] lemma C_comp : (C a).comp p = C a := eval₂_C _ _

@[simp] lemma nat_cast_comp {n : ℕ} : (n : R[X]).comp p = n := by rw [←C_eq_nat_cast, C_comp]

@[simp] lemma comp_zero : p.comp (0 : R[X]) = C (p.eval 0) := by rw [← C_0, comp_C]

@[simp] lemma zero_comp : comp (0 : R[X]) p = 0 := by rw [← C_0, C_comp]

@[simp] lemma comp_one : p.comp 1 = C (p.eval 1) := by rw [← C_1, comp_C]

@[simp] lemma one_comp : comp (1 : R[X]) p = 1 := by rw [← C_1, C_comp]

@[simp] lemma add_comp : (p + q).comp r = p.comp r + q.comp r := eval₂_add _ _

@[simp] lemma monomial_comp (n : ℕ) : (monomial n a).comp p = C a * p^n := eval₂_monomial _ _

@[simp] lemma mul_X_comp : (p * X).comp r = p.comp r * r :=
begin
  apply polynomial.induction_on' p,
  { intros p q hp hq, simp only [hp, hq, add_mul, add_comp] },
  { intros n b, simp only [pow_succ', mul_assoc, monomial_mul_X, monomial_comp] }
end

@[simp] lemma X_pow_comp {k : ℕ} : (X^k).comp p = p^k :=
begin
  induction k with k ih,
  { simp, },
  { simp [pow_succ', mul_X_comp, ih], },
end

@[simp] lemma mul_X_pow_comp {k : ℕ} : (p * X^k).comp r = p.comp r * r^k :=
begin
  induction k with k ih,
  { simp, },
  { simp [ih, pow_succ', ←mul_assoc, mul_X_comp], },
end

@[simp] lemma C_mul_comp : (C a * p).comp r = C a * p.comp r :=
begin
  apply polynomial.induction_on' p,
  { intros p q hp hq, simp [hp, hq, mul_add], },
  { intros n b, simp [mul_assoc], }
end

@[simp] lemma nat_cast_mul_comp {n : ℕ} : ((n : R[X]) * p).comp r = n * p.comp r :=
by rw [←C_eq_nat_cast, C_mul_comp, C_eq_nat_cast]

@[simp] lemma mul_comp {R : Type*} [comm_semiring R] (p q r : R[X]) :
  (p * q).comp r = p.comp r * q.comp r := eval₂_mul _ _

@[simp] lemma pow_comp {R : Type*} [comm_semiring R] (p q : R[X]) (n : ℕ) :
  (p^n).comp q = (p.comp q)^n :=
((monoid_hom.mk (λ r : R[X], r.comp q)) one_comp (λ r s, mul_comp r s q)).map_pow p n

@[simp] lemma bit0_comp : comp (bit0 p : R[X]) q = bit0 (p.comp q) :=
by simp only [bit0, add_comp]

@[simp] lemma bit1_comp : comp (bit1 p : R[X]) q = bit1 (p.comp q) :=
by simp only [bit1, add_comp, bit0_comp, one_comp]

@[simp] lemma smul_comp [monoid S] [distrib_mul_action S R] [is_scalar_tower S R R]
  (s : S) (p q : R[X]) : (s • p).comp q = s • p.comp q :=
by rw [← smul_one_smul R s p, comp, comp, eval₂_smul, ← smul_eq_C_mul, smul_assoc, one_smul]

lemma comp_assoc {R : Type*} [comm_semiring R] (φ ψ χ : R[X]) :
  (φ.comp ψ).comp χ = φ.comp (ψ.comp χ) :=
begin
  apply polynomial.induction_on φ;
  { intros, simp only [add_comp, mul_comp, C_comp, X_comp, pow_succ', ← mul_assoc, *] at * }
end

lemma coeff_comp_degree_mul_degree (hqd0 : nat_degree q ≠ 0) :
  coeff (p.comp q) (nat_degree p * nat_degree q) =
  leading_coeff p * leading_coeff q ^ nat_degree p :=
begin
  rw [comp, eval₂, coeff_sum],
  convert finset.sum_eq_single p.nat_degree _ _,
  { simp only [coeff_nat_degree, coeff_C_mul, coeff_pow_mul_nat_degree] },
  { assume b hbs hbp,
    refine coeff_eq_zero_of_nat_degree_lt ((nat_degree_mul_le).trans_lt _),
    rw [nat_degree_C, zero_add],
    refine (nat_degree_pow_le).trans_lt ((mul_lt_mul_right (pos_iff_ne_zero.mpr hqd0)).mpr _),
    exact lt_of_le_of_ne (le_nat_degree_of_mem_supp _ hbs) hbp },
  { simp {contextual := tt} }
end

end comp

section map
variables [semiring S]
variables (f : R →+* S)

/-- `map f p` maps a polynomial `p` across a ring hom `f` -/
def map : R[X] → S[X] := eval₂ (C.comp f) X

@[simp] lemma map_C : (C a).map f = C (f a) := eval₂_C _ _

@[simp] lemma map_X : X.map f = X := eval₂_X _ _

@[simp] lemma map_monomial {n a} : (monomial n a).map f = monomial n (f a) :=
begin
  dsimp only [map],
  rw [eval₂_monomial, ← C_mul_X_pow_eq_monomial], refl,
end

@[simp] protected lemma map_zero : (0 : R[X]).map f = 0 :=  eval₂_zero _ _

@[simp] protected lemma map_add : (p + q).map f = p.map f + q.map f := eval₂_add _ _

@[simp] protected lemma map_one : (1 : R[X]).map f = 1 := eval₂_one _ _

@[simp] protected lemma map_mul : (p * q).map f = p.map f * q.map f :=
by { rw [map, eval₂_mul_noncomm], exact λ k, (commute_X _).symm }

@[simp] protected lemma map_smul (r : R) : (r • p).map f = f r • p.map f :=
by rw [map, eval₂_smul, ring_hom.comp_apply, C_mul']

/-- `polynomial.map` as a `ring_hom`. -/
-- `map` is a ring-hom unconditionally, and theoretically the definition could be replaced,
-- but this turns out not to be easy because `p.map f` does not resolve to `polynomial.map`
-- if `map` is a `ring_hom` instead of a plain function; the elaborator does not try to coerce
-- to a function before trying field (dot) notation (this may be technically infeasible);
-- the relevant code is (both lines): https://github.com/leanprover-community/
-- lean/blob/487ac5d7e9b34800502e1ddf3c7c806c01cf9d51/src/frontends/lean/elaborator.cpp#L1876-L1913
def map_ring_hom (f : R →+* S) : R[X] →+* S[X] :=
{ to_fun := polynomial.map f,
  map_add' := λ _ _, polynomial.map_add f,
  map_zero' := polynomial.map_zero f,
  map_mul' := λ _ _, polynomial.map_mul f,
  map_one' := polynomial.map_one f }

@[simp] lemma coe_map_ring_hom (f : R →+* S) : ⇑(map_ring_hom f) = map f := rfl

-- This is protected to not clash with the global `map_nat_cast`.
@[simp] protected theorem map_nat_cast (n : ℕ) : (n : R[X]).map f = n :=
map_nat_cast (map_ring_hom f) n

@[simp] protected lemma map_bit0 : (bit0 p).map f = bit0 (p.map f) :=
map_bit0 (map_ring_hom f) p

@[simp] protected lemma map_bit1 : (bit1 p).map f = bit1 (p.map f) :=
map_bit1 (map_ring_hom f) p

--TODO rename to `map_dvd_map`
lemma map_dvd (f : R →+* S) {x y : R[X]} : x ∣ y → x.map f ∣ y.map f :=
(map_ring_hom f).map_dvd

@[simp]
lemma coeff_map (n : ℕ) : coeff (p.map f) n = f (coeff p n) :=
begin
  rw [map, eval₂, coeff_sum, sum],
  conv_rhs { rw [← sum_C_mul_X_pow_eq p, coeff_sum, sum, ring_hom.map_sum], },
  refine finset.sum_congr rfl (λ x hx, _),
  simp [function.comp, coeff_C_mul_X_pow, f.map_mul],
  split_ifs; simp [f.map_zero],
end

/-- If `R` and `S` are isomorphic, then so are their polynomial rings. -/
@[simps] def map_equiv (e : R ≃+* S) : R[X] ≃+* S[X] :=
ring_equiv.of_hom_inv
  (map_ring_hom (e : R →+* S))
  (map_ring_hom (e.symm : S →+* R))
  (by ext; simp)
  (by ext; simp)

lemma map_map [semiring T] (g : S →+* T)
  (p : R[X]) : (p.map f).map g = p.map (g.comp f) :=
ext (by simp [coeff_map])

@[simp] lemma map_id : p.map (ring_hom.id _) = p := by simp [polynomial.ext_iff, coeff_map]

lemma eval₂_eq_eval_map {x : S} : p.eval₂ f x = (p.map f).eval x :=
begin
  apply polynomial.induction_on' p,
  { intros p q hp hq, simp [hp, hq], },
  { intros n r, simp, }
end

lemma map_injective (hf : function.injective f) : function.injective (map f) :=
λ p q h, ext $ λ m, hf $ by rw [← coeff_map f, ← coeff_map f, h]

lemma map_surjective (hf : function.surjective f) : function.surjective (map f) :=
λ p, polynomial.induction_on' p
 (λ p q hp hq, let ⟨p', hp'⟩ := hp, ⟨q', hq'⟩ := hq
                 in ⟨p' + q', by rw [polynomial.map_add f, hp', hq']⟩)
 (λ n s, let ⟨r, hr⟩ := hf s in ⟨monomial n r, by rw [map_monomial f, hr]⟩)

lemma degree_map_le (p : R[X]) : degree (p.map f) ≤ degree p :=
begin
  apply (degree_le_iff_coeff_zero _ _).2 (λ m hm, _),
  rw degree_lt_iff_coeff_zero at hm,
  simp [hm m le_rfl],
end

lemma nat_degree_map_le (p : R[X]) : nat_degree (p.map f) ≤ nat_degree p :=
nat_degree_le_nat_degree (degree_map_le f p)

variables {f}

protected lemma map_eq_zero_iff (hf : function.injective f) : p.map f = 0 ↔ p = 0 :=
map_eq_zero_iff (map_ring_hom f) (map_injective f hf)

protected lemma map_ne_zero_iff (hf : function.injective f) : p.map f ≠ 0 ↔ p ≠ 0 :=
(polynomial.map_eq_zero_iff hf).not

lemma map_monic_eq_zero_iff (hp : p.monic) : p.map f = 0 ↔ ∀ x, f x = 0 :=
⟨ λ hfp x, calc f x = f x * f p.leading_coeff : by simp only [mul_one, hp.leading_coeff, f.map_one]
                ... = f x * (p.map f).coeff p.nat_degree : congr_arg _ (coeff_map _ _).symm
                ... = 0 : by simp only [hfp, mul_zero, coeff_zero],
  λ h, ext (λ n, by simp only [h, coeff_map, coeff_zero]) ⟩

lemma map_monic_ne_zero (hp : p.monic) [nontrivial S] : p.map f ≠ 0 :=
λ h, f.map_one_ne_zero ((map_monic_eq_zero_iff hp).mp h _)

lemma degree_map_eq_of_leading_coeff_ne_zero (f : R →+* S)
  (hf : f (leading_coeff p) ≠ 0) : degree (p.map f) = degree p :=
le_antisymm (degree_map_le f _) $
  have hp0 : p ≠ 0, from leading_coeff_ne_zero.mp (λ hp0, hf (trans (congr_arg _ hp0) f.map_zero)),
  begin
    rw [degree_eq_nat_degree hp0],
    refine le_degree_of_ne_zero _,
    rw [coeff_map], exact hf
  end

lemma nat_degree_map_of_leading_coeff_ne_zero (f : R →+* S)
  (hf : f (leading_coeff p) ≠ 0) : nat_degree (p.map f) = nat_degree p :=
nat_degree_eq_of_degree_eq (degree_map_eq_of_leading_coeff_ne_zero f hf)

lemma leading_coeff_map_of_leading_coeff_ne_zero (f : R →+* S)
  (hf : f (leading_coeff p) ≠ 0) : leading_coeff (p.map f) = f (leading_coeff p) :=
begin
  unfold leading_coeff,
  rw [coeff_map, nat_degree_map_of_leading_coeff_ne_zero f hf],
end

variables (f)

@[simp] lemma map_ring_hom_id : map_ring_hom (ring_hom.id R) = ring_hom.id R[X] :=
ring_hom.ext $ λ x, map_id

@[simp] lemma map_ring_hom_comp [semiring T] (f : S →+* T) (g : R →+* S) :
  (map_ring_hom f).comp (map_ring_hom g) = map_ring_hom (f.comp g) :=
ring_hom.ext $ polynomial.map_map g f

protected lemma map_list_prod (L : list R[X]) : L.prod.map f = (L.map $ map f).prod :=
eq.symm $ list.prod_hom _ (map_ring_hom f).to_monoid_hom

@[simp] protected lemma map_pow (n : ℕ) : (p ^ n).map f = p.map f ^ n :=
(map_ring_hom f).map_pow _ _

lemma mem_map_srange {p : S[X]} :
  p ∈ (map_ring_hom f).srange ↔ ∀ n, p.coeff n ∈ f.srange :=
begin
  split,
  { rintro ⟨p, rfl⟩ n, rw [coe_map_ring_hom, coeff_map], exact set.mem_range_self _ },
  { intro h, rw p.as_sum_range_C_mul_X_pow,
    refine (map_ring_hom f).srange.sum_mem _,
    intros i hi,
    rcases h i with ⟨c, hc⟩,
    use [C c * X^i],
    rw [coe_map_ring_hom, polynomial.map_mul, map_C, hc, polynomial.map_pow, map_X] }
end

lemma mem_map_range {R S : Type*} [ring R] [ring S] (f : R →+* S)
  {p : S[X]} : p ∈ (map_ring_hom f).range ↔ ∀ n, p.coeff n ∈ f.range :=
mem_map_srange f

lemma eval₂_map [semiring T] (g : S →+* T) (x : T) :
  (p.map f).eval₂ g x = p.eval₂ (g.comp f) x :=
by rw [eval₂_eq_eval_map, eval₂_eq_eval_map, map_map]

lemma eval_map (x : S) : (p.map f).eval x = p.eval₂ f x :=
(eval₂_eq_eval_map f).symm

protected lemma map_sum {ι : Type*} (g : ι → R[X]) (s : finset ι) :
  (∑ i in s, g i).map f = ∑ i in s, (g i).map f :=
(map_ring_hom f).map_sum _ _

lemma map_comp (p q : R[X]) : map f (p.comp q) = (map f p).comp (map f q) :=
polynomial.induction_on p
  (by simp only [map_C, forall_const, C_comp, eq_self_iff_true])
  (by simp only [polynomial.map_add, add_comp, forall_const, implies_true_iff, eq_self_iff_true]
        {contextual := tt})
  (by simp only [pow_succ', ←mul_assoc, comp, forall_const, eval₂_mul_X, implies_true_iff,
        eq_self_iff_true, map_X, polynomial.map_mul] {contextual := tt})

@[simp]
lemma eval_zero_map (f : R →+* S) (p : R[X]) :
  (p.map f).eval 0 = f (p.eval 0) :=
by simp [←coeff_zero_eq_eval_zero]

@[simp]
lemma eval_one_map (f : R →+* S) (p : R[X]) :
  (p.map f).eval 1 = f (p.eval 1) :=
begin
  apply polynomial.induction_on' p,
  { intros p q hp hq, simp only [hp, hq, polynomial.map_add, ring_hom.map_add, eval_add] },
  { intros n r, simp only [one_pow, mul_one, eval_monomial, map_monomial] }
end

@[simp]
lemma eval_nat_cast_map (f : R →+* S) (p : R[X]) (n : ℕ) :
  (p.map f).eval n = f (p.eval n) :=
begin
  apply polynomial.induction_on' p,
  { intros p q hp hq, simp only [hp, hq, polynomial.map_add, ring_hom.map_add, eval_add] },
  { intros n r, simp only [map_nat_cast f, eval_monomial, map_monomial, f.map_pow, f.map_mul] }
end

@[simp]
lemma eval_int_cast_map {R S : Type*} [ring R] [ring S]
  (f : R →+* S) (p : R[X]) (i : ℤ) :
  (p.map f).eval i = f (p.eval i) :=
begin
  apply polynomial.induction_on' p,
  { intros p q hp hq, simp only [hp, hq, polynomial.map_add, ring_hom.map_add, eval_add] },
  { intros n r, simp only [map_int_cast, eval_monomial, map_monomial, map_pow, map_mul] }
end

end map

/-!
we have made `eval₂` irreducible from the start.

Perhaps we can make also `eval`, `comp`, and `map` irreducible too?
-/

section hom_eval₂

variables [semiring S] [semiring T]  (f : R →+* S) (g : S →+* T) (p)

lemma hom_eval₂ (x : S) : g (p.eval₂ f x) = p.eval₂ (g.comp f) (g x) :=
by rw [←eval₂_map, eval₂_at_apply, eval_map]

end hom_eval₂

end semiring

section comm_semiring

section eval

section
variables [semiring R] {p q : R[X]} {x : R} [semiring S] (f : R →+* S)

lemma eval₂_hom (x : R) :
  p.eval₂ f (f x) = f (p.eval x) :=
(ring_hom.comp_id f) ▸ (hom_eval₂ p (ring_hom.id R) f x).symm

end

section
variables [semiring R] {p q : R[X]} {x : R} [comm_semiring S] (f : R →+* S)

lemma eval₂_comp {x : S} :
  eval₂ f x (p.comp q) = eval₂ f (eval₂ f x q) p :=
by rw [comp, p.as_sum_range]; simp [eval₂_finset_sum, eval₂_pow]

@[simp]
lemma iterate_comp_eval₂ (k : ℕ) (t : S) :
  eval₂ f t (p.comp^[k] q) = ((λ x, eval₂ f x p)^[k] (eval₂ f t q)) :=
begin
  induction k with k IH,
  { simp },
  { rw [function.iterate_succ_apply', function.iterate_succ_apply', eval₂_comp, IH] }
end

end

section
variables [comm_semiring R] {p q : R[X]} {x : R} [comm_semiring S] (f : R →+* S)

@[simp] lemma eval_mul : (p * q).eval x = p.eval x * q.eval x := eval₂_mul _ _

/-- `eval r`, regarded as a ring homomorphism from `R[X]` to `R`. -/
def eval_ring_hom : R → R[X] →+* R := eval₂_ring_hom (ring_hom.id _)

@[simp] lemma coe_eval_ring_hom (r : R) : ((eval_ring_hom r) : R[X] → R) = eval r := rfl

lemma eval_ring_hom_zero : eval_ring_hom 0 = constant_coeff :=
fun_like.ext _ _ $ λ p, p.coeff_zero_eq_eval_zero.symm

@[simp] lemma eval_pow (n : ℕ) : (p ^ n).eval x = p.eval x ^ n := eval₂_pow _ _ _

@[simp]
lemma eval_comp : (p.comp q).eval x = p.eval (q.eval x) :=
begin
  apply polynomial.induction_on' p,
  { intros r s hr hs, simp [add_comp, hr, hs], },
  { intros n a, simp, }
end

@[simp]
lemma iterate_comp_eval : ∀ (k : ℕ) (t : R),
  (p.comp^[k] q).eval t = ((λ x, p.eval x)^[k] (q.eval t)) :=
iterate_comp_eval₂ _

/-- `comp p`, regarded as a ring homomorphism from `R[X]` to itself. -/
def comp_ring_hom : R[X] → R[X] →+* R[X] :=
eval₂_ring_hom C

@[simp] lemma coe_comp_ring_hom (q : R[X]) : (comp_ring_hom q : R[X] → R[X]) = λ p, comp p q := rfl

lemma coe_comp_ring_hom_apply (p q : R[X]) : (comp_ring_hom q : R[X] → R[X]) p = comp p q := rfl

lemma root_mul_left_of_is_root (p : R[X]) {q : R[X]} :
  is_root q a → is_root (p * q) a :=
λ H, by rw [is_root, eval_mul, is_root.def.1 H, mul_zero]

lemma root_mul_right_of_is_root {p : R[X]} (q : R[X]) :
  is_root p a → is_root (p * q) a :=
λ H, by rw [is_root, eval_mul, is_root.def.1 H, zero_mul]

lemma eval₂_multiset_prod (s : multiset R[X]) (x : S) :
  eval₂ f x s.prod = (s.map (eval₂ f x)).prod :=
map_multiset_prod (eval₂_ring_hom f x) s

lemma eval₂_finset_prod (s : finset ι) (g : ι → R[X]) (x : S) :
  (∏ i in s, g i).eval₂ f x = ∏ i in s, (g i).eval₂ f x :=
map_prod (eval₂_ring_hom f x) _ _

/--
Polynomial evaluation commutes with `list.prod`
-/
lemma eval_list_prod (l : list R[X]) (x : R) :
  eval x l.prod = (l.map (eval x)).prod :=
(eval_ring_hom x).map_list_prod l

/--
Polynomial evaluation commutes with `multiset.prod`
-/

/--
Polynomial evaluation commutes with `finset.prod`
-/
lemma eval_prod {ι : Type*} (s : finset ι) (p : ι → R[X]) (x : R) :
  eval x (∏ j in s, p j) = ∏ j in s, eval x (p j) :=
(eval_ring_hom x).map_prod _ _

lemma list_prod_comp (l : list R[X]) (q : R[X]) :
  l.prod.comp q = (l.map (λ p : R[X], p.comp q)).prod :=
map_list_prod (comp_ring_hom q) _

lemma multiset_prod_comp (s : multiset R[X]) (q : R[X]) :
  s.prod.comp q = (s.map (λ p : R[X], p.comp q)).prod :=
map_multiset_prod (comp_ring_hom q) _

lemma prod_comp {ι : Type*} (s : finset ι) (p : ι → R[X]) (q : R[X]) :
  (∏ j in s, p j).comp q = ∏ j in s, (p j).comp q :=
map_prod (comp_ring_hom q) _ _

lemma is_root_prod {R} [comm_ring R] [is_domain R] {ι : Type*}
  (s : finset ι) (p : ι → R[X]) (x : R) :
  is_root (∏ j in s, p j) x ↔ ∃ i ∈ s, is_root (p i) x :=
by simp only [is_root, eval_prod, finset.prod_eq_zero_iff]

lemma eval_dvd : p ∣ q → eval x p ∣ eval x q :=
eval₂_dvd _ _

lemma eval_eq_zero_of_dvd_of_eval_eq_zero : p ∣ q → eval x p = 0 → eval x q = 0 :=
eval₂_eq_zero_of_dvd_of_eval₂_eq_zero _ _

@[simp]
lemma eval_geom_sum {R} [comm_semiring R] {n : ℕ} {x : R} :
  eval x (∑ i in range n, X ^ i) = ∑ i in range n, x ^ i :=
by simp [eval_finset_sum]

end

end eval

section map

lemma support_map_subset [semiring R] [semiring S] (f : R →+* S) (p : R[X]) :
  (map f p).support ⊆ p.support :=
begin
  intros x,
  contrapose!,
  simp { contextual := tt },
end

lemma support_map_of_injective [semiring R] [semiring S]
  (p : R[X]) {f : R →+* S} (hf : function.injective f) :
  (map f p).support = p.support :=
by simp_rw [finset.ext_iff, mem_support_iff, coeff_map,
  ←map_zero f, hf.ne_iff, iff_self, forall_const]

variables [comm_semiring R] [comm_semiring S] (f : R →+* S)

protected lemma map_multiset_prod (m : multiset R[X]) : m.prod.map f = (m.map $ map f).prod :=
eq.symm $ multiset.prod_hom _ (map_ring_hom f).to_monoid_hom

protected lemma map_prod {ι : Type*} (g : ι → R[X]) (s : finset ι) :
  (∏ i in s, g i).map f = ∏ i in s, (g i).map f :=
(map_ring_hom f).map_prod _ _

lemma is_root.map {f : R →+* S} {x : R} {p : R[X]} (h : is_root p x) :
  is_root (p.map f) (f x) :=
by rw [is_root, eval_map, eval₂_hom, h.eq_zero, f.map_zero]

lemma is_root.of_map {R} [comm_ring R] {f : R →+* S} {x : R} {p : R[X]}
  (h : is_root (p.map f) (f x)) (hf : function.injective f) : is_root p x :=
by rwa [is_root, ←(injective_iff_map_eq_zero' f).mp hf, ←eval₂_hom, ←eval_map]

lemma is_root_map_iff {R : Type*} [comm_ring R] {f : R →+* S} {x : R} {p : R[X]}
  (hf : function.injective f) : is_root (p.map f) (f x) ↔ is_root p x :=
⟨λ h, h.of_map hf, λ h, h.map⟩

end map

end comm_semiring

section ring
variables [ring R] {p q r : R[X]}

lemma C_neg : C (-a) = -C a := ring_hom.map_neg C a

lemma C_sub : C (a - b) = C a - C b := ring_hom.map_sub C a b

@[simp] protected lemma map_sub {S} [ring S] (f : R →+* S) :
  (p - q).map f = p.map f - q.map f :=
(map_ring_hom f).map_sub p q

@[simp] protected lemma map_neg {S} [ring S] (f : R →+* S) :
  (-p).map f = -(p.map f) :=
(map_ring_hom f).map_neg p

@[simp] lemma map_int_cast {S} [ring S] (f : R →+* S) (n : ℤ) : map f ↑n = ↑n :=
map_int_cast (map_ring_hom f) n

@[simp] lemma eval_int_cast {n : ℤ} {x : R} : (n : R[X]).eval x = n :=
by simp only [←C_eq_int_cast, eval_C]

@[simp] lemma eval₂_neg {S} [ring S] (f : R →+* S) {x : S} :
  (-p).eval₂ f x = -p.eval₂ f x :=
by rw [eq_neg_iff_add_eq_zero, ←eval₂_add, add_left_neg, eval₂_zero]

@[simp] lemma eval₂_sub {S} [ring S] (f : R →+* S) {x : S} :
  (p - q).eval₂ f x = p.eval₂ f x - q.eval₂ f x :=
by rw [sub_eq_add_neg, eval₂_add, eval₂_neg, sub_eq_add_neg]

@[simp] lemma eval_neg (p : R[X]) (x : R) : (-p).eval x = -p.eval x :=
eval₂_neg _

@[simp] lemma eval_sub (p q : R[X]) (x : R) : (p - q).eval x = p.eval x - q.eval x :=
eval₂_sub _

lemma root_X_sub_C : is_root (X - C a) b ↔ a = b :=
by rw [is_root.def, eval_sub, eval_X, eval_C, sub_eq_zero, eq_comm]

@[simp] lemma neg_comp : (-p).comp q = -p.comp q := eval₂_neg _

@[simp] lemma sub_comp : (p - q).comp r = p.comp r - q.comp r := eval₂_sub _

@[simp] lemma cast_int_comp (i : ℤ) : comp (i : R[X]) p = i :=
by cases i; simp

end ring

end polynomial


/-
Copyright (c) 2021 Sébastien Gouëzel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sébastien Gouëzel

! This file was ported from Lean 3 source module analysis.subadditive
! leanprover-community/mathlib commit 69c6a5a12d8a2b159f20933e60115a4f2de62b58
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Topology.Instances.Real
import Mathbin.Order.Filter.Archimedean

/-!
# Convergence of subadditive sequences

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

A subadditive sequence `u : ℕ → ℝ` is a sequence satisfying `u (m + n) ≤ u m + u n` for all `m, n`.
We define this notion as `subadditive u`, and prove in `subadditive.tendsto_lim` that, if `u n / n`
is bounded below, then it converges to a limit (that we denote by `subadditive.lim` for
convenience). This result is known as Fekete's lemma in the literature.
-/

noncomputable section

open Set Filter

open Topology

#print Subadditive /-
/-- A real-valued sequence is subadditive if it satisfies the inequality `u (m + n) ≤ u m + u n`
for all `m, n`. -/
def Subadditive (u : ℕ → ℝ) : Prop :=
  ∀ m n, u (m + n) ≤ u m + u n
#align subadditive Subadditive
-/

namespace Subadditive

variable {u : ℕ → ℝ} (h : Subadditive u)

include h

#print Subadditive.lim /-
/-- The limit of a bounded-below subadditive sequence. The fact that the sequence indeed tends to
this limit is given in `subadditive.tendsto_lim` -/
@[nolint unused_arguments]
protected irreducible_def lim :=
  infₛ ((fun n : ℕ => u n / n) '' Ici 1)
#align subadditive.lim Subadditive.lim
-/

/- warning: subadditive.lim_le_div -> Subadditive.lim_le_div is a dubious translation:
lean 3 declaration is
  forall {u : Nat -> Real} (h : Subadditive u), (BddBelow.{0} Real Real.preorder (Set.range.{0, 1} Real Nat (fun (n : Nat) => HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (DivInvMonoid.toHasDiv.{0} Real (DivisionRing.toDivInvMonoid.{0} Real Real.divisionRing))) (u n) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Real (HasLiftT.mk.{1, 1} Nat Real (CoeTCₓ.coe.{1, 1} Nat Real (Nat.castCoe.{0} Real Real.hasNatCast))) n)))) -> (forall {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (LE.le.{0} Real Real.hasLe (Subadditive.lim u h) (HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (DivInvMonoid.toHasDiv.{0} Real (DivisionRing.toDivInvMonoid.{0} Real Real.divisionRing))) (u n) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Real (HasLiftT.mk.{1, 1} Nat Real (CoeTCₓ.coe.{1, 1} Nat Real (Nat.castCoe.{0} Real Real.hasNatCast))) n))))
but is expected to have type
  forall {u : Nat -> Real} (h : Subadditive u), (BddBelow.{0} Real Real.instPreorderReal (Set.range.{0, 1} Real Nat (fun (n : Nat) => HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (LinearOrderedField.toDiv.{0} Real Real.instLinearOrderedFieldReal)) (u n) (Nat.cast.{0} Real Real.natCast n)))) -> (forall {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (LE.le.{0} Real Real.instLEReal (Subadditive.lim u h) (HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (LinearOrderedField.toDiv.{0} Real Real.instLinearOrderedFieldReal)) (u n) (Nat.cast.{0} Real Real.natCast n))))
Case conversion may be inaccurate. Consider using '#align subadditive.lim_le_div Subadditive.lim_le_divₓ'. -/
theorem lim_le_div (hbdd : BddBelow (range fun n => u n / n)) {n : ℕ} (hn : n ≠ 0) :
    h.limUnder ≤ u n / n := by
  rw [Subadditive.lim]
  apply cinfₛ_le _ _
  · rcases hbdd with ⟨c, hc⟩
    exact ⟨c, fun x hx => hc (image_subset_range _ _ hx)⟩
  · apply mem_image_of_mem
    exact zero_lt_iff.2 hn
#align subadditive.lim_le_div Subadditive.lim_le_div

/- warning: subadditive.apply_mul_add_le -> Subadditive.apply_mul_add_le is a dubious translation:
lean 3 declaration is
  forall {u : Nat -> Real}, (Subadditive u) -> (forall (k : Nat) (n : Nat) (r : Nat), LE.le.{0} Real Real.hasLe (u (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat Nat.hasAdd) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat Nat.hasMul) k n) r)) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.hasAdd) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.hasMul) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Real (HasLiftT.mk.{1, 1} Nat Real (CoeTCₓ.coe.{1, 1} Nat Real (Nat.castCoe.{0} Real Real.hasNatCast))) k) (u n)) (u r)))
but is expected to have type
  forall {u : Nat -> Real}, (Subadditive u) -> (forall (k : Nat) (n : Nat) (r : Nat), LE.le.{0} Real Real.instLEReal (u (HAdd.hAdd.{0, 0, 0} Nat Nat Nat (instHAdd.{0} Nat instAddNat) (HMul.hMul.{0, 0, 0} Nat Nat Nat (instHMul.{0} Nat instMulNat) k n) r)) (HAdd.hAdd.{0, 0, 0} Real Real Real (instHAdd.{0} Real Real.instAddReal) (HMul.hMul.{0, 0, 0} Real Real Real (instHMul.{0} Real Real.instMulReal) (Nat.cast.{0} Real Real.natCast k) (u n)) (u r)))
Case conversion may be inaccurate. Consider using '#align subadditive.apply_mul_add_le Subadditive.apply_mul_add_leₓ'. -/
theorem apply_mul_add_le (k n r) : u (k * n + r) ≤ k * u n + u r :=
  by
  induction' k with k IH; · simp only [Nat.cast_zero, MulZeroClass.zero_mul, zero_add]
  calc
    u ((k + 1) * n + r) = u (n + (k * n + r)) :=
      by
      congr 1
      ring
    _ ≤ u n + u (k * n + r) := (h _ _)
    _ ≤ u n + (k * u n + u r) := (add_le_add_left IH _)
    _ = (k + 1 : ℕ) * u n + u r := by simp <;> ring

#align subadditive.apply_mul_add_le Subadditive.apply_mul_add_le

/- warning: subadditive.eventually_div_lt_of_div_lt -> Subadditive.eventually_div_lt_of_div_lt is a dubious translation:
lean 3 declaration is
  forall {u : Nat -> Real}, (Subadditive u) -> (forall {L : Real} {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (OfNat.mk.{0} Nat 0 (Zero.zero.{0} Nat Nat.hasZero)))) -> (LT.lt.{0} Real Real.hasLt (HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (DivInvMonoid.toHasDiv.{0} Real (DivisionRing.toDivInvMonoid.{0} Real Real.divisionRing))) (u n) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Real (HasLiftT.mk.{1, 1} Nat Real (CoeTCₓ.coe.{1, 1} Nat Real (Nat.castCoe.{0} Real Real.hasNatCast))) n)) L) -> (Filter.Eventually.{0} Nat (fun (p : Nat) => LT.lt.{0} Real Real.hasLt (HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (DivInvMonoid.toHasDiv.{0} Real (DivisionRing.toDivInvMonoid.{0} Real Real.divisionRing))) (u p) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Real (HasLiftT.mk.{1, 1} Nat Real (CoeTCₓ.coe.{1, 1} Nat Real (Nat.castCoe.{0} Real Real.hasNatCast))) p)) L) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring))))))
but is expected to have type
  forall {u : Nat -> Real}, (Subadditive u) -> (forall {L : Real} {n : Nat}, (Ne.{1} Nat n (OfNat.ofNat.{0} Nat 0 (instOfNatNat 0))) -> (LT.lt.{0} Real Real.instLTReal (HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (LinearOrderedField.toDiv.{0} Real Real.instLinearOrderedFieldReal)) (u n) (Nat.cast.{0} Real Real.natCast n)) L) -> (Filter.Eventually.{0} Nat (fun (p : Nat) => LT.lt.{0} Real Real.instLTReal (HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (LinearOrderedField.toDiv.{0} Real Real.instLinearOrderedFieldReal)) (u p) (Nat.cast.{0} Real Real.natCast p)) L) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring)))))
Case conversion may be inaccurate. Consider using '#align subadditive.eventually_div_lt_of_div_lt Subadditive.eventually_div_lt_of_div_ltₓ'. -/
theorem eventually_div_lt_of_div_lt {L : ℝ} {n : ℕ} (hn : n ≠ 0) (hL : u n / n < L) :
    ∀ᶠ p in atTop, u p / p < L :=
  by
  have I : ∀ i : ℕ, 0 < i → (i : ℝ) ≠ 0 := by
    intro i hi
    simp only [hi.ne', Ne.def, Nat.cast_eq_zero, not_false_iff]
  obtain ⟨w, nw, wL⟩ : ∃ w, u n / n < w ∧ w < L := exists_between hL
  obtain ⟨x, hx⟩ : ∃ x, ∀ i < n, u i - i * w ≤ x :=
    by
    obtain ⟨x, hx⟩ : BddAbove ↑(Finset.image (fun i => u i - i * w) (Finset.range n)) :=
      Finset.bddAbove _
    refine' ⟨x, fun i hi => _⟩
    simp only [upperBounds, mem_image, and_imp, forall_exists_index, mem_set_of_eq,
      forall_apply_eq_imp_iff₂, Finset.mem_range, Finset.mem_coe, Finset.coe_image] at hx
    exact hx _ hi
  have A : ∀ p : ℕ, u p ≤ p * w + x := by
    intro p
    let s := p / n
    let r := p % n
    have hp : p = s * n + r := by rw [mul_comm, Nat.div_add_mod]
    calc
      u p = u (s * n + r) := by rw [hp]
      _ ≤ s * u n + u r := (h.apply_mul_add_le _ _ _)
      _ = s * n * (u n / n) + u r := by
        field_simp [I _ hn.bot_lt]
        ring
      _ ≤ s * n * w + u r :=
        (add_le_add_right
          (mul_le_mul_of_nonneg_left nw.le (mul_nonneg (Nat.cast_nonneg _) (Nat.cast_nonneg _))) _)
      _ = (s * n + r) * w + (u r - r * w) := by ring
      _ = p * w + (u r - r * w) := by
        rw [hp]
        simp only [Nat.cast_add, Nat.cast_mul]
      _ ≤ p * w + x := add_le_add_left (hx _ (Nat.mod_lt _ hn.bot_lt)) _

  have B : ∀ᶠ p in at_top, u p / p ≤ w + x / p :=
    by
    refine' eventually_at_top.2 ⟨1, fun p hp => _⟩
    simp only [I p hp, Ne.def, not_false_iff, field_simps]
    refine' div_le_div_of_le_of_nonneg _ (Nat.cast_nonneg _)
    rw [mul_comm]
    exact A _
  have C : ∀ᶠ p : ℕ in at_top, w + x / p < L :=
    by
    have : tendsto (fun p : ℕ => w + x / p) at_top (𝓝 (w + 0)) :=
      tendsto_const_nhds.add (tendsto_const_nhds.div_at_top tendsto_nat_cast_atTop_atTop)
    rw [add_zero] at this
    exact (tendsto_order.1 this).2 _ wL
  filter_upwards [B, C]with _ hp h'p using hp.trans_lt h'p
#align subadditive.eventually_div_lt_of_div_lt Subadditive.eventually_div_lt_of_div_lt

/- warning: subadditive.tendsto_lim -> Subadditive.tendsto_lim is a dubious translation:
lean 3 declaration is
  forall {u : Nat -> Real} (h : Subadditive u), (BddBelow.{0} Real Real.preorder (Set.range.{0, 1} Real Nat (fun (n : Nat) => HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (DivInvMonoid.toHasDiv.{0} Real (DivisionRing.toDivInvMonoid.{0} Real Real.divisionRing))) (u n) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Real (HasLiftT.mk.{1, 1} Nat Real (CoeTCₓ.coe.{1, 1} Nat Real (Nat.castCoe.{0} Real Real.hasNatCast))) n)))) -> (Filter.Tendsto.{0, 0} Nat Real (fun (n : Nat) => HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (DivInvMonoid.toHasDiv.{0} Real (DivisionRing.toDivInvMonoid.{0} Real Real.divisionRing))) (u n) ((fun (a : Type) (b : Type) [self : HasLiftT.{1, 1} a b] => self.0) Nat Real (HasLiftT.mk.{1, 1} Nat Real (CoeTCₓ.coe.{1, 1} Nat Real (Nat.castCoe.{0} Real Real.hasNatCast))) n)) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (OrderedCancelAddCommMonoid.toPartialOrder.{0} Nat (StrictOrderedSemiring.toOrderedCancelAddCommMonoid.{0} Nat Nat.strictOrderedSemiring)))) (nhds.{0} Real (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (Subadditive.lim u h)))
but is expected to have type
  forall {u : Nat -> Real} (h : Subadditive u), (BddBelow.{0} Real Real.instPreorderReal (Set.range.{0, 1} Real Nat (fun (n : Nat) => HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (LinearOrderedField.toDiv.{0} Real Real.instLinearOrderedFieldReal)) (u n) (Nat.cast.{0} Real Real.natCast n)))) -> (Filter.Tendsto.{0, 0} Nat Real (fun (n : Nat) => HDiv.hDiv.{0, 0, 0} Real Real Real (instHDiv.{0} Real (LinearOrderedField.toDiv.{0} Real Real.instLinearOrderedFieldReal)) (u n) (Nat.cast.{0} Real Real.natCast n)) (Filter.atTop.{0} Nat (PartialOrder.toPreorder.{0} Nat (StrictOrderedSemiring.toPartialOrder.{0} Nat Nat.strictOrderedSemiring))) (nhds.{0} Real (UniformSpace.toTopologicalSpace.{0} Real (PseudoMetricSpace.toUniformSpace.{0} Real Real.pseudoMetricSpace)) (Subadditive.lim u h)))
Case conversion may be inaccurate. Consider using '#align subadditive.tendsto_lim Subadditive.tendsto_limₓ'. -/
/-- Fekete's lemma: a subadditive sequence which is bounded below converges. -/
theorem tendsto_lim (hbdd : BddBelow (range fun n => u n / n)) :
    Tendsto (fun n => u n / n) atTop (𝓝 h.limUnder) :=
  by
  refine' tendsto_order.2 ⟨fun l hl => _, fun L hL => _⟩
  ·
    refine'
      eventually_at_top.2
        ⟨1, fun n hn => hl.trans_le (h.lim_le_div hbdd (zero_lt_one.trans_le hn).ne')⟩
  · obtain ⟨n, npos, hn⟩ : ∃ n : ℕ, 0 < n ∧ u n / n < L :=
      by
      rw [Subadditive.lim] at hL
      rcases exists_lt_of_cinfₛ_lt (by simp) hL with ⟨x, hx, xL⟩
      rcases(mem_image _ _ _).1 hx with ⟨n, hn, rfl⟩
      exact ⟨n, zero_lt_one.trans_le hn, xL⟩
    exact h.eventually_div_lt_of_div_lt npos.ne' hn
#align subadditive.tendsto_lim Subadditive.tendsto_lim

end Subadditive


import tactic.gptf
import data.matrix.basic
import data.complex.basic
import field_theory.finite.basic

section sec1

lemma irrelevant (P : Prop) (pf1 pf2 : P) : pf1 = pf2 := by simp

universe u
variable a : Type u
#check a  -- prints `a : Type u`

#check unit
#check punit --universe polymorphic version H i₁ i₂ α β γ  Prop, ℕ, ℤ, ℚ, ℝ, ℕ → ℕ, ...

#check ℕ → ℕ

#check 2 -- prints `2 : ℕ` as Lean view `2` as a natural number by default
#check (2 : ℚ) -- prints `2 : ℚ` if we claim the given `2` is a rational number
#check 2 + 3  -- prints `2 + 3 : ℕ`

def f (x : ℕ) := 2 * x  -- this defines a function
#check f  -- prints `f : ℕ → ℕ`

#check (4, 5) -- prints `(4, 5) : ℕ × ℕ`,
              -- where `×` is the notation of the operation `prod`

#check ℕ -- prints `ℕ : Type`
#check ℕ × ℚ -- prints `ℕ × ℚ : Type`
#check bool -- prints `bool : Type`

def p := 2 + 3 = 5 -- defines `p` to be the statement `2 + 3 = 5`
#check p  -- prints `p : Prop`

lemma pf : p := by simp [p] -- this constructs `pf1` as a proof of `p`
#check pf  -- prints `pf : p`

#check Type -- Type : Type 1
#check Type 1 -- Type 1 : Type 2
-- ...

variables α : Type* -- declares variables `α`
variable β : Type 2 -- declares variables `b`

#check α -- prints `α : Type u_1`
#check β -- prints `β : Type 2`

#check list  -- prints `list : Type u_1 → Type u_1`,
             -- where `u_1` is a variable ranging over type levels
#check list ℕ  -- prints `list ℕ : Type`
#check list α -- prints `list α : Type u_1`

#check prod -- prints `prod : Type u_3 → Type u_4 → Type (max u_3 u_4)`
#check ℕ × ℕ -- prints `ℕ × ℕ : Type`
#check ℕ × β -- prints `ℕ × β : Type 2`
#check α × β -- prints `α × β : Type (max u_1 2)`

namespace foo -- declares we are working in the namespace `foo`

-- Now, we are working in the namespace `foo`

def b : ℕ := 10
def f (x : ℕ) : ℕ := x + 5
def fb : ℕ := f b

namespace bar -- declares we are working in the namespace `bar`

-- Now, we are working in the namespace `foo.bar`

def ffb : ℕ := f (f b)

#check fb
#check ffb

end bar

-- Now, we back to the namespace `foo`

#check fb
-- `#check ffb` will reports error: unknown identifier 'ffb'.
#check bar.ffb

end foo

#check foo.fb
#check foo.bar.ffb

namespace foo -- We can redo `namespace foo`.
def g (x : ℕ) : ℕ := b + 5
#check g
end foo

-- #check g -- error
#check foo.g

open foo -- `open foo` allows us to use the shorter name `name`
         -- for any identifier in form `foo.name`,
         -- but if we declare an identifier `g` after `open foo`,
         -- `g`'s full name is `g`, not `foo.g`.

#check g
#check bar.ffb

-- a term-style proof
lemma pf1 (p q : Prop) (hp : p) (hq : q) : p ∧ q ∧ p :=
and.intro hp (and.intro hq hp)

#check and.intro -- prints `and.intro : ?M_1 → ?M_2 → ?M_1 ∧ ?M_2`,
                 -- where `?M_1` and `?M_2` denotes indeterminate variables

-- a tactic-style proof
lemma pf2 (p q : Prop) (hp : p) (hq : q) : p ∧ q ∧ p :=
begin -- the `begin ... end` block allows us to write a list of tactics
  split, -- splits the goal `p ∧ q ∧ p` into two goals `p` and `q ∧ p`
  -- the first goal is `p`
  -- I.e. Lean expects us to provide an expression of type `p`
  exact hp, -- provides an exact proof term `hp`
            -- this closes the first goal as `hp` has type `p`
  split, -- splits `q ∧ p` into `q` and `p`
  exact hq, -- provides an exact proof term `hq`
  exact hp, -- provides an exact proof term `hp`
end

-- mix
lemma pf3 (p q : Prop) (hp : p) (hq : q) : p ∧ q ∧ p :=
begin
  split,
  exact hp, -- `hp` is a proof term
  exact and.intro hq hp -- `and.intro hq hp` is a proof term
end

#check int.of_nat

#check rat.of_int --ℤ

variables m n : ℕ
variables i : ℤ
variables j : ℚ
#check i + m -- i + ↑m : ℤ
#check i + m + n -- i + ↑m + ↑n : Z
#check j + i + m -- j + ↑i + ↑m : ℚ

-- #check m + i -- error
#check ↑m + i -- ↑m + i : ℤ
#check ↑(m + n) + i -- ↑(m + n) + i : ℤ
#check ↑m + ↑n + i -- ↑m + ↑n + i : ℤ
#check ↑i + ↑m + j -- ↑i + ↑m + j : ℚ  β

instance my_coe : has_coe ℝ ℂ := ⟨λ r, ⟨r, 0⟩⟩
variables (r : ℝ) (c : ℂ)
#check c + r -- c + ↑r : ℂ

end sec1

section str
-- the following is the definition of `prod` in mathlib
--structure prod (α : Type u) (β : Type v) := -- round brackets mark parameters explicitly supplied by the user
--(fst : α) (snd : β)
#check prod -- prod : Type u_1 → Type u_2 → Type (max u_1 u_2)

variables α β : Type*
#check prod α β -- α × β : Type (max u_1 u_2)
                -- α × β is a new type and itself has type Type (max u_1 u_2)

variables (a : α) (b : β)

#check prod.mk a b -- (a, b) : α × β
#check (⟨a, b⟩ : prod α β) -- (a, b) : α × β
#check (a, b) -- (a, b) : α × β

#check prod.fst -- prod.fst : ?M_1 × ?M_2 → ?M_1
#reduce prod.fst (10, 20) -- 10
#reduce (10, 20).1 -- 10

#check prod.snd -- prod.snd : ?M_1 × ?M_2 → ?M_2
#reduce prod.snd (10, 20) -- 20
#reduce (10, 20).2 -- 20

structure my_str := -- a new structure with thtree fields
(f1 : ℕ) (f2 : ℕ) (f3 : ℕ)

variables n : ℕ
-- defines `triple n` to be `⟨n, n + n, n + n + n⟩`
def triple : my_str := ⟨n, n + n, n + n + n⟩

#check triple n -- triple n : my_str
#reduce (triple n).1 -- n
#reduce (triple n).2 -- n.add n
#reduce (triple n).3 -- (n.add n).add n
#reduce my_str.f3 (triple 5) -- 15
#reduce (triple 5).3 -- 15

end str

#check has_add

-- class has_add (α : Type u) := (add : α → α → α)

#check nat.has_add -- nat.has_add : has_add ℕ

-- We construt an element `my_inst` of `has_add ℕ`.
def my_inst : has_add ℕ := ⟨nat.add⟩ -- `nat.add` is a function defined in mathlib
-- Next, we declare `my_inst` to be an instance of `has_add ℕ`.
attribute [instance] my_inst

-- @[instance] def my_inst : has_add ℕ := ⟨nat.add⟩
-- instance my_inst : has_add ℕ := ⟨nat.add⟩

instance my_inst' : has_add (ℕ × ℕ):= ⟨λ ⟨a, b⟩ ⟨c, d⟩, ⟨a + c, b + c⟩⟩

--variables α : Type*
--instance my_inst'' : has_add (α × α):= ⟨λ ⟨a, b⟩ ⟨c, d⟩, ⟨a + c, b + c⟩⟩

/-
/-- `finset α` is the type of finite sets of elements of `α`. It is implemented
  as a multiset (a list up to permutation) which has no duplicate elements.
  `finset.nodup` is the poof that `finset.val` has no duplicate elements. -/
structure finset (α : Type*) :=
(val : multiset α)
(nodup : nodup val)

/-- `fintype α` means that `α` is finite, i.e. there are only
  finitely many distinct elements of type `α`. The evidence of this
  is a finset `elems` (a list up to permutation without duplicates),
  together with a proof `complete` that everything of type `α` is in the list. -/
class fintype (α : Type*) :=
(elems [] : finset α)
(complete : ∀ x : α, x ∈ elems)
-/

open matrix

--variables (I α : Type*) [fintype I] [has_one α]
--#check (1 : I → α)

#check diagonal

variables (I α : Type*) [fintype I]
variables [decidable_eq I] [has_zero α] [has_one α]
#check matrix.has_one
#check (1 : matrix I I α)

#check semigroup

/-
class has_one      (α : Type u) := (one : α)

class has_mul      (α : Type u) := (mul : α → α → α)

class semigroup (G : Type u) extends has_mul G :=
(mul_assoc : ∀ a b c : G, a * b * c = a * (b * c))

class mul_one_class (M : Type u) extends has_one M, has_mul M :=
(one_mul : ∀ (a : M), 1 * a = a)
(mul_one : ∀ (a : M), a * 1 = a)

class monoid (M : Type u) extends semigroup M, mul_one_class M :=
(npow : ℕ → M → M := npow_rec)
(npow_zero' : ∀ x, npow 0 x = 1 . try_refl_tac)
(npow_succ' : ∀ (n : ℕ) x, npow n.succ x = x * npow n x . try_refl_tac)
-/

/-
lemma simple {a : ℕ} (h : a ∣ 4) : a = 1 ∨ a = 2 ∨ a = 4 :=
begin
  dec_trivial
end
-/

example (a : ℕ) (h : a > 0) : a = a - 1 + 1 := by rw nat.sub_add_cancel h

-- rw fintype.card_eq,
example {F : Type*} [fintype F] [has_zero F] [decidable_eq F] :
fintype.card {a : F // a = 0} = 1 :=
begin
  simp [fintype.card_eq_one_iff],
  /-
  have h: fintype.card {a : F // a = 0} = fintype.card ({0} : set F),
  { rw fintype.card_eq,
    have eq : {a:F // a = 0} ≃ ({0} : set F),
    { refine {..},
      exact λ a : {a:F // a = 0}, by tidy,
      exact λ a : ({0} : set F), by tidy,
      tidy,

    },
    exact ⟨eq⟩,},
  simp [h],
  -/
end

noncomputable
example {α : Type*} (h : ∃ a : α, true) : α :=
classical.some h


/-
Copyright (c) 2020 Heather Macbeth. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Heather Macbeth
-/
import analysis.specific_limits.normed

/-!
# The group of units of a complete normed ring

This file contains the basic theory for the group of units (invertible elements) of a complete
normed ring (Banach algebras being a notable special case).

## Main results

The constructions `one_sub`, `add` and `unit_of_nearby` state, in varying forms, that perturbations
of a unit are units.  The latter two are not stated in their optimal form; more precise versions
would use the spectral radius.

The first main result is `is_open`:  the group of units of a complete normed ring is an open subset
of the ring.

The function `inverse` (defined in `algebra.ring`), for a ring `R`, sends `a : R` to `a⁻¹` if `a` is
a unit and 0 if not.  The other major results of this file (notably `inverse_add`,
`inverse_add_norm` and `inverse_add_norm_diff_nth_order`) cover the asymptotic properties of
`inverse (x + t)` as `t → 0`.

-/

noncomputable theory
open_locale topological_space
variables {R : Type*} [normed_ring R] [complete_space R]

namespace units

/-- In a complete normed ring, a perturbation of `1` by an element `t` of distance less than `1`
from `1` is a unit.  Here we construct its `units` structure.  -/
@[simps coe]
def one_sub (t : R) (h : ∥t∥ < 1) : Rˣ :=
{ val := 1 - t,
  inv := ∑' n : ℕ, t ^ n,
  val_inv := mul_neg_geom_series t h,
  inv_val := geom_series_mul_neg t h }

/-- In a complete normed ring, a perturbation of a unit `x` by an element `t` of distance less than
`∥x⁻¹∥⁻¹` from `x` is a unit.  Here we construct its `units` structure. -/
@[simps coe]
def add (x : Rˣ) (t : R) (h : ∥t∥ < ∥(↑x⁻¹ : R)∥⁻¹) : Rˣ :=
units.copy  -- to make `coe_add` true definitionally, for convenience
  (x * (units.one_sub (-(↑x⁻¹ * t)) begin
      nontriviality R using [zero_lt_one],
      have hpos : 0 < ∥(↑x⁻¹ : R)∥ := units.norm_pos x⁻¹,
      calc ∥-(↑x⁻¹ * t)∥
          = ∥↑x⁻¹ * t∥                    : by { rw norm_neg }
      ... ≤ ∥(↑x⁻¹ : R)∥ * ∥t∥            : norm_mul_le ↑x⁻¹ _
      ... < ∥(↑x⁻¹ : R)∥ * ∥(↑x⁻¹ : R)∥⁻¹ : by nlinarith only [h, hpos]
      ... = 1                             : mul_inv_cancel (ne_of_gt hpos)
    end))
  (x + t) (by simp [mul_add]) _ rfl

/-- In a complete normed ring, an element `y` of distance less than `∥x⁻¹∥⁻¹` from `x` is a unit.
Here we construct its `units` structure. -/
@[simps coe]
def unit_of_nearby (x : Rˣ) (y : R) (h : ∥y - x∥ < ∥(↑x⁻¹ : R)∥⁻¹) : Rˣ :=
units.copy (x.add (y - x : R) h) y (by simp) _ rfl

/-- The group of units of a complete normed ring is an open subset of the ring. -/
protected lemma is_open : is_open {x : R | is_unit x} :=
begin
  nontriviality R,
  apply metric.is_open_iff.mpr,
  rintros x' ⟨x, rfl⟩,
  refine ⟨∥(↑x⁻¹ : R)∥⁻¹, _root_.inv_pos.mpr (units.norm_pos x⁻¹), _⟩,
  intros y hy,
  rw [metric.mem_ball, dist_eq_norm] at hy,
  exact (x.unit_of_nearby y hy).is_unit
end

protected lemma nhds (x : Rˣ) : {x : R | is_unit x} ∈ 𝓝 (x : R) :=
is_open.mem_nhds units.is_open x.is_unit

end units

namespace normed_ring
open_locale classical big_operators
open asymptotics filter metric finset ring

lemma inverse_one_sub (t : R) (h : ∥t∥ < 1) : inverse (1 - t) = ↑(units.one_sub t h)⁻¹ :=
by rw [← inverse_unit (units.one_sub t h), units.coe_one_sub]

/-- The formula `inverse (x + t) = inverse (1 + x⁻¹ * t) * x⁻¹` holds for `t` sufficiently small. -/
lemma inverse_add (x : Rˣ) :
  ∀ᶠ t in (𝓝 0), inverse ((x : R) + t) = inverse (1 + ↑x⁻¹ * t) * ↑x⁻¹ :=
begin
  nontriviality R,
  rw [eventually_iff, metric.mem_nhds_iff],
  have hinv : 0 < ∥(↑x⁻¹ : R)∥⁻¹, by cancel_denoms,
  use [∥(↑x⁻¹ : R)∥⁻¹, hinv],
  intros t ht,
  simp only [mem_ball, dist_zero_right] at ht,
  have ht' : ∥-↑x⁻¹ * t∥ < 1,
  { refine lt_of_le_of_lt (norm_mul_le _ _) _,
    rw norm_neg,
    refine lt_of_lt_of_le (mul_lt_mul_of_pos_left ht x⁻¹.norm_pos) _,
    cancel_denoms },
  have hright := inverse_one_sub (-↑x⁻¹ * t) ht',
  have hleft := inverse_unit (x.add t ht),
  simp only [neg_mul, sub_neg_eq_add] at hright,
  simp only [units.coe_add] at hleft,
  simp [hleft, hright, units.add]
end

lemma inverse_one_sub_nth_order (n : ℕ) :
  ∀ᶠ t in (𝓝 0), inverse ((1:R) - t) = (∑ i in range n, t ^ i) + (t ^ n) * inverse (1 - t) :=
begin
  simp only [eventually_iff, metric.mem_nhds_iff],
  use [1, by norm_num],
  intros t ht,
  simp only [mem_ball, dist_zero_right] at ht,
  simp only [inverse_one_sub t ht, set.mem_set_of_eq],
  have h : 1 = ((range n).sum (λ i, t ^ i)) * (units.one_sub t ht) + t ^ n,
  { simp only [units.coe_one_sub],
    rw [← geom_sum, geom_sum_mul_neg],
    simp },
  rw [← one_mul ↑(units.one_sub t ht)⁻¹, h, add_mul],
  congr,
  { rw [mul_assoc, (units.one_sub t ht).mul_inv],
    simp },
  { simp only [units.coe_one_sub],
    rw [← add_mul, ← geom_sum, geom_sum_mul_neg],
    simp }
end

/-- The formula
`inverse (x + t) = (∑ i in range n, (- x⁻¹ * t) ^ i) * x⁻¹ + (- x⁻¹ * t) ^ n * inverse (x + t)`
holds for `t` sufficiently small. -/
lemma inverse_add_nth_order (x : Rˣ) (n : ℕ) :
  ∀ᶠ t in (𝓝 0), inverse ((x : R) + t)
  = (∑ i in range n, (- ↑x⁻¹ * t) ^ i) * ↑x⁻¹ + (- ↑x⁻¹ * t) ^ n * inverse (x + t) :=
begin
  refine (inverse_add x).mp _,
  have hzero : tendsto (λ (t : R), - ↑x⁻¹ * t) (𝓝 0) (𝓝 0),
  { convert ((mul_left_continuous (- (↑x⁻¹ : R))).tendsto 0).comp tendsto_id,
    simp },
  refine (hzero.eventually (inverse_one_sub_nth_order n)).mp (eventually_of_forall _),
  simp only [neg_mul, sub_neg_eq_add],
  intros t h1 h2,
  have h := congr_arg (λ (a : R), a * ↑x⁻¹) h1,
  dsimp at h,
  convert h,
  rw [add_mul, mul_assoc],
  simp [h2.symm]
end

lemma inverse_one_sub_norm : is_O (λ t, inverse ((1:R) - t)) (λ t, (1:ℝ)) (𝓝 (0:R)) :=
begin
  simp only [is_O, is_O_with, eventually_iff, metric.mem_nhds_iff],
  refine ⟨∥(1:R)∥ + 1, (2:ℝ)⁻¹, by norm_num, _⟩,
  intros t ht,
  simp only [ball, dist_zero_right, set.mem_set_of_eq] at ht,
  have ht' : ∥t∥ < 1,
  { have : (2:ℝ)⁻¹ < 1 := by cancel_denoms,
    linarith },
  simp only [inverse_one_sub t ht', norm_one, mul_one, set.mem_set_of_eq],
  change ∥∑' n : ℕ, t ^ n∥ ≤ _,
  have := normed_ring.tsum_geometric_of_norm_lt_1 t ht',
  have : (1 - ∥t∥)⁻¹ ≤ 2,
  { rw ← inv_inv (2:ℝ),
    refine inv_le_inv_of_le (by norm_num) _,
    have : (2:ℝ)⁻¹ + (2:ℝ)⁻¹ = 1 := by ring,
    linarith },
  linarith
end

/-- The function `λ t, inverse (x + t)` is O(1) as `t → 0`. -/
lemma inverse_add_norm (x : Rˣ) : is_O (λ t, inverse (↑x + t)) (λ t, (1:ℝ)) (𝓝 (0:R)) :=
begin
  simp only [is_O_iff, norm_one, mul_one],
  cases is_O_iff.mp (@inverse_one_sub_norm R _ _) with C hC,
  use C * ∥((x⁻¹:Rˣ):R)∥,
  have hzero : tendsto (λ t, - (↑x⁻¹ : R) * t) (𝓝 0) (𝓝 0),
  { convert ((mul_left_continuous (-↑x⁻¹ : R)).tendsto 0).comp tendsto_id,
    simp },
  refine (inverse_add x).mp ((hzero.eventually hC).mp (eventually_of_forall _)),
  intros t bound iden,
  rw iden,
  simp at bound,
  have hmul := norm_mul_le (inverse (1 + ↑x⁻¹ * t)) ↑x⁻¹,
  nlinarith [norm_nonneg (↑x⁻¹ : R)]
end

/-- The function
`λ t, inverse (x + t) - (∑ i in range n, (- x⁻¹ * t) ^ i) * x⁻¹`
is `O(t ^ n)` as `t → 0`. -/
lemma inverse_add_norm_diff_nth_order (x : Rˣ) (n : ℕ) :
  is_O (λ (t : R), inverse (↑x + t) - (∑ i in range n, (- ↑x⁻¹ * t) ^ i) * ↑x⁻¹)
  (λ t, ∥t∥ ^ n) (𝓝 (0:R)) :=
begin
  by_cases h : n = 0,
  { simpa [h] using inverse_add_norm x },
  have hn : 0 < n := nat.pos_of_ne_zero h,
  simp [is_O_iff],
  cases (is_O_iff.mp (inverse_add_norm x)) with C hC,
  use C * ∥(1:ℝ)∥ * ∥(↑x⁻¹ : R)∥ ^ n,
  have h : eventually_eq (𝓝 (0:R))
    (λ t, inverse (↑x + t) - (∑ i in range n, (- ↑x⁻¹ * t) ^ i) * ↑x⁻¹)
    (λ t, ((- ↑x⁻¹ * t) ^ n) * inverse (x + t)),
  { refine (inverse_add_nth_order x n).mp (eventually_of_forall _),
    intros t ht,
    convert congr_arg (λ a, a - (range n).sum (pow (-↑x⁻¹ * t)) * ↑x⁻¹) ht,
    simp },
  refine h.mp (hC.mp (eventually_of_forall _)),
  intros t _ hLHS,
  simp only [neg_mul] at hLHS,
  rw hLHS,
  refine le_trans (norm_mul_le _ _ ) _,
  have h' : ∥(-(↑x⁻¹ * t)) ^ n∥ ≤ ∥(↑x⁻¹ : R)∥ ^ n * ∥t∥ ^ n,
  { calc ∥(-(↑x⁻¹ * t)) ^ n∥ ≤ ∥(-(↑x⁻¹ * t))∥ ^ n : norm_pow_le' _ hn
    ... = ∥↑x⁻¹ * t∥ ^ n : by rw norm_neg
    ... ≤ (∥(↑x⁻¹ : R)∥ * ∥t∥) ^ n : _
    ... =  ∥(↑x⁻¹ : R)∥ ^ n * ∥t∥ ^ n : mul_pow _ _ n,
    exact pow_le_pow_of_le_left (norm_nonneg _) (norm_mul_le ↑x⁻¹ t) n },
  have h'' : 0 ≤ ∥(↑x⁻¹ : R)∥ ^ n * ∥t∥ ^ n,
  { refine mul_nonneg _ _;
    exact pow_nonneg (norm_nonneg _) n },
  nlinarith [norm_nonneg (inverse (↑x + t))],
end

/-- The function `λ t, inverse (x + t) - x⁻¹` is `O(t)` as `t → 0`. -/
lemma inverse_add_norm_diff_first_order (x : Rˣ) :
  is_O (λ t, inverse (↑x + t) - ↑x⁻¹) (λ t, ∥t∥) (𝓝 (0:R)) :=
by simpa using inverse_add_norm_diff_nth_order x 1

/-- The function
`λ t, inverse (x + t) - x⁻¹ + x⁻¹ * t * x⁻¹`
is `O(t ^ 2)` as `t → 0`. -/
lemma inverse_add_norm_diff_second_order (x : Rˣ) :
  is_O (λ t, inverse (↑x + t) - ↑x⁻¹ + ↑x⁻¹ * t * ↑x⁻¹) (λ t, ∥t∥ ^ 2) (𝓝 (0:R)) :=
begin
  convert inverse_add_norm_diff_nth_order x 2,
  ext t,
  simp only [range_succ, range_one, sum_insert, mem_singleton, sum_singleton, not_false_iff,
    one_ne_zero, pow_zero, add_mul, pow_one, one_mul, neg_mul,
    sub_add_eq_sub_sub_swap, sub_neg_eq_add],
end

/-- The function `inverse` is continuous at each unit of `R`. -/
lemma inverse_continuous_at (x : Rˣ) : continuous_at inverse (x : R) :=
begin
  have h_is_o : is_o (λ (t : R), inverse (↑x + t) - ↑x⁻¹) (λ _, 1 : R → ℝ) (𝓝 0),
    from ((inverse_add_norm_diff_first_order x).trans_is_o
      (is_o_id_const (@one_ne_zero ℝ _ _)).norm_left),
  have h_lim : tendsto (λ (y:R), y - x) (𝓝 x) (𝓝 0),
  { refine tendsto_zero_iff_norm_tendsto_zero.mpr _,
    exact tendsto_iff_norm_tendsto_zero.mp tendsto_id },
  rw [continuous_at, tendsto_iff_norm_tendsto_zero, inverse_unit],
  simpa [(∘)] using h_is_o.norm_left.tendsto_div_nhds_zero.comp h_lim
end

end normed_ring

namespace units
open mul_opposite filter normed_ring

/-- In a normed ring, the coercion from `Rˣ` (equipped with the induced topology from the
embedding in `R × R`) to `R` is an open map. -/
lemma is_open_map_coe : is_open_map (coe : Rˣ → R) :=
begin
  rw is_open_map_iff_nhds_le,
  intros x s,
  rw [mem_map, mem_nhds_induced],
  rintros ⟨t, ht, hts⟩,
  obtain ⟨u, hu, v, hv, huvt⟩ :
    ∃ (u : set R), u ∈ 𝓝 ↑x ∧ ∃ (v : set Rᵐᵒᵖ), v ∈ 𝓝 (op ↑x⁻¹) ∧ u ×ˢ v ⊆ t,
  { simpa [embed_product, mem_nhds_prod_iff] using ht },
  have : u ∩ (op ∘ ring.inverse) ⁻¹' v ∩ (set.range (coe : Rˣ → R)) ∈ 𝓝 ↑x,
  { refine inter_mem (inter_mem hu _) (units.nhds x),
    refine (continuous_op.continuous_at.comp (inverse_continuous_at x)).preimage_mem_nhds _,
    simpa using hv },
  refine mem_of_superset this _,
  rintros _ ⟨⟨huy, hvy⟩, ⟨y, rfl⟩⟩,
  have : embed_product R y ∈ u ×ˢ v := ⟨huy, by simpa using hvy⟩,
  simpa using hts (huvt this)
end

/-- In a normed ring, the coercion from `Rˣ` (equipped with the induced topology from the
embedding in `R × R`) to `R` is an open embedding. -/
lemma open_embedding_coe : open_embedding (coe : Rˣ → R) :=
open_embedding_of_continuous_injective_open continuous_coe ext is_open_map_coe

end units