/-
Copyright (c) 2019 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Kenny Lau
-/
import data.finsupp.interval
import data.mv_polynomial.basic
import data.polynomial.algebra_map
import data.polynomial.coeff
import linear_algebra.std_basis
import ring_theory.ideal.local_ring
import ring_theory.multiplicity
import tactic.linarith

/-!
# Formal power series

This file defines (multivariate) formal power series
and develops the basic properties of these objects.

A formal power series is to a polynomial like an infinite sum is to a finite sum.

We provide the natural inclusion from polynomials to formal power series.

## Generalities

The file starts with setting up the (semi)ring structure on multivariate power series.

`trunc n φ` truncates a formal power series to the polynomial
that has the same coefficients as `φ`, for all `m < n`, and `0` otherwise.

If the constant coefficient of a formal power series is invertible,
then this formal power series is invertible.

Formal power series over a local ring form a local ring.

## Formal power series in one variable

We prove that if the ring of coefficients is an integral domain,
then formal power series in one variable form an integral domain.

The `order` of a formal power series `φ` is the multiplicity of the variable `X` in `φ`.

If the coefficients form an integral domain, then `order` is a valuation
(`order_mul`, `le_order_add`).

## Implementation notes

In this file we define multivariate formal power series with
variables indexed by `σ` and coefficients in `R` as
`mv_power_series σ R := (σ →₀ ℕ) → R`.
Unfortunately there is not yet enough API to show that they are the completion
of the ring of multivariate polynomials. However, we provide most of the infrastructure
that is needed to do this. Once I-adic completion (topological or algebraic) is available
it should not be hard to fill in the details.

Formal power series in one variable are defined as
`power_series R := mv_power_series unit R`.

This allows us to port a lot of proofs and properties
from the multivariate case to the single variable case.
However, it means that formal power series are indexed by `unit →₀ ℕ`,
which is of course canonically isomorphic to `ℕ`.
We then build some glue to treat formal power series as if they are indexed by `ℕ`.
Occasionally this leads to proofs that are uglier than expected.
-/

noncomputable theory
open_locale classical big_operators polynomial

/-- Multivariate formal power series, where `σ` is the index set of the variables
and `R` is the coefficient ring.-/
def mv_power_series (σ : Type*) (R : Type*) := (σ →₀ ℕ) → R

namespace mv_power_series
open finsupp
variables {σ R : Type*}

instance [inhabited R]       : inhabited       (mv_power_series σ R) := ⟨λ _, default⟩
instance [has_zero R]        : has_zero        (mv_power_series σ R) := pi.has_zero
instance [add_monoid R]      : add_monoid      (mv_power_series σ R) := pi.add_monoid
instance [add_group R]       : add_group       (mv_power_series σ R) := pi.add_group
instance [add_comm_monoid R] : add_comm_monoid (mv_power_series σ R) := pi.add_comm_monoid
instance [add_comm_group R]  : add_comm_group  (mv_power_series σ R) := pi.add_comm_group
instance [nontrivial R]      : nontrivial      (mv_power_series σ R) := function.nontrivial

instance {A} [semiring R] [add_comm_monoid A] [module R A] :
  module R (mv_power_series σ A) := pi.module _ _ _

instance {A S} [semiring R] [semiring S] [add_comm_monoid A] [module R A] [module S A]
  [has_smul R S] [is_scalar_tower R S A] :
  is_scalar_tower R S (mv_power_series σ A) :=
pi.is_scalar_tower

section semiring
variables (R) [semiring R]

/-- The `n`th monomial with coefficient `a` as multivariate formal power series.-/
def monomial (n : σ →₀ ℕ) : R →ₗ[R] mv_power_series σ R :=
linear_map.std_basis R _ n

/-- The `n`th coefficient of a multivariate formal power series.-/
def coeff (n : σ →₀ ℕ) : (mv_power_series σ R) →ₗ[R] R := linear_map.proj n

variables {R}

/-- Two multivariate formal power series are equal if all their coefficients are equal.-/
@[ext] lemma ext {φ ψ} (h : ∀ (n : σ →₀ ℕ), coeff R n φ = coeff R n ψ) :
  φ = ψ :=
funext h

/-- Two multivariate formal power series are equal
 if and only if all their coefficients are equal.-/
lemma ext_iff {φ ψ : mv_power_series σ R} :
  φ = ψ ↔ (∀ (n : σ →₀ ℕ), coeff R n φ = coeff R n ψ) :=
function.funext_iff

lemma monomial_def [decidable_eq σ] (n : σ →₀ ℕ) :
  monomial R n = linear_map.std_basis R _ n :=
by convert rfl -- unify the `decidable` arguments

lemma coeff_monomial [decidable_eq σ] (m n : σ →₀ ℕ) (a : R) :
  coeff R m (monomial R n a) = if m = n then a else 0 :=
by rw [coeff, monomial_def, linear_map.proj_apply, linear_map.std_basis_apply,
  function.update_apply, pi.zero_apply]

@[simp] lemma coeff_monomial_same (n : σ →₀ ℕ) (a : R) :
  coeff R n (monomial R n a) = a :=
linear_map.std_basis_same R _ n a

lemma coeff_monomial_ne {m n : σ →₀ ℕ} (h : m ≠ n) (a : R) :
  coeff R m (monomial R n a) = 0 :=
linear_map.std_basis_ne R _ _ _ h a

lemma eq_of_coeff_monomial_ne_zero {m n : σ →₀ ℕ} {a : R} (h : coeff R m (monomial R n a) ≠ 0) :
  m = n :=
by_contra $ λ h', h $ coeff_monomial_ne h' a

@[simp] lemma coeff_comp_monomial (n : σ →₀ ℕ) :
  (coeff R n).comp (monomial R n) = linear_map.id :=
linear_map.ext $ coeff_monomial_same n

@[simp] lemma coeff_zero (n : σ →₀ ℕ) : coeff R n (0 : mv_power_series σ R) = 0 := rfl

variables (m n : σ →₀ ℕ) (φ ψ : mv_power_series σ R)

instance : has_one (mv_power_series σ R) := ⟨monomial R (0 : σ →₀ ℕ) 1⟩

lemma coeff_one [decidable_eq σ] :
  coeff R n (1 : mv_power_series σ R) = if n = 0 then 1 else 0 :=
coeff_monomial _ _ _

lemma coeff_zero_one : coeff R (0 : σ →₀ ℕ) 1 = 1 :=
coeff_monomial_same 0 1

lemma monomial_zero_one : monomial R (0 : σ →₀ ℕ) 1 = 1 := rfl

instance : add_monoid_with_one (mv_power_series σ R) :=
{ nat_cast := λ n, monomial R 0 n,
  nat_cast_zero := by simp [nat.cast],
  nat_cast_succ := by simp [nat.cast, monomial_zero_one],
  one := 1, .. mv_power_series.add_monoid }

instance : has_mul (mv_power_series σ R) :=
⟨λ φ ψ n, ∑ p in finsupp.antidiagonal n, coeff R p.1 φ * coeff R p.2 ψ⟩

lemma coeff_mul : coeff R n (φ * ψ) =
  ∑ p in finsupp.antidiagonal n, coeff R p.1 φ * coeff R p.2 ψ := rfl

protected lemma zero_mul : (0 : mv_power_series σ R) * φ = 0 :=
ext $ λ n, by simp [coeff_mul]

protected lemma mul_zero : φ * 0 = 0 :=
ext $ λ n, by simp [coeff_mul]

lemma coeff_monomial_mul (a : R) :
  coeff R m (monomial R n a * φ) = if n ≤ m then a * coeff R (m - n) φ else 0 :=
begin
  have : ∀ p ∈ antidiagonal m,
    coeff R (p : (σ →₀ ℕ) × (σ →₀ ℕ)).1 (monomial R n a) * coeff R p.2 φ ≠ 0 → p.1 = n :=
    λ p _ hp, eq_of_coeff_monomial_ne_zero (left_ne_zero_of_mul hp),
  rw [coeff_mul, ← finset.sum_filter_of_ne this, antidiagonal_filter_fst_eq,
    finset.sum_ite_index],
  simp only [finset.sum_singleton, coeff_monomial_same, finset.sum_empty]
end

lemma coeff_mul_monomial (a : R) :
  coeff R m (φ * monomial R n a) = if n ≤ m then coeff R (m - n) φ * a else 0 :=
begin
  have : ∀ p ∈ antidiagonal m,
    coeff R (p : (σ →₀ ℕ) × (σ →₀ ℕ)).1 φ * coeff R p.2 (monomial R n a) ≠ 0 → p.2 = n :=
    λ p _ hp, eq_of_coeff_monomial_ne_zero (right_ne_zero_of_mul hp),
  rw [coeff_mul, ← finset.sum_filter_of_ne this, antidiagonal_filter_snd_eq,
    finset.sum_ite_index],
  simp only [finset.sum_singleton, coeff_monomial_same, finset.sum_empty]
end

lemma coeff_add_monomial_mul (a : R) :
  coeff R (m + n) (monomial R m a * φ) = a * coeff R n φ :=
begin
  rw [coeff_monomial_mul, if_pos, add_tsub_cancel_left],
  exact le_add_right le_rfl
end

lemma coeff_add_mul_monomial (a : R) :
  coeff R (m + n) (φ * monomial R n a) = coeff R m φ * a :=
begin
  rw [coeff_mul_monomial, if_pos, add_tsub_cancel_right],
  exact le_add_left le_rfl
end

@[simp] lemma commute_monomial {a : R} {n} :
  commute φ (monomial R n a) ↔ ∀ m, commute (coeff R m φ) a :=
begin
  refine ext_iff.trans ⟨λ h m, _, λ h m, _⟩,
  { have := h (m + n),
    rwa [coeff_add_mul_monomial, add_comm, coeff_add_monomial_mul] at this },
  { rw [coeff_mul_monomial, coeff_monomial_mul],
    split_ifs; [apply h, refl] }
end

protected lemma one_mul : (1 : mv_power_series σ R) * φ = φ :=
ext $ λ n, by simpa using coeff_add_monomial_mul 0 n φ 1

protected lemma mul_one : φ * 1 = φ :=
ext $ λ n, by simpa using coeff_add_mul_monomial n 0 φ 1

protected lemma mul_add (φ₁ φ₂ φ₃ : mv_power_series σ R) :
  φ₁ * (φ₂ + φ₃) = φ₁ * φ₂ + φ₁ * φ₃ :=
ext $ λ n, by simp only [coeff_mul, mul_add, finset.sum_add_distrib, linear_map.map_add]

protected lemma add_mul (φ₁ φ₂ φ₃ : mv_power_series σ R) :
  (φ₁ + φ₂) * φ₃ = φ₁ * φ₃ + φ₂ * φ₃ :=
ext $ λ n, by simp only [coeff_mul, add_mul, finset.sum_add_distrib, linear_map.map_add]

protected lemma mul_assoc (φ₁ φ₂ φ₃ : mv_power_series σ R) :
  (φ₁ * φ₂) * φ₃ = φ₁ * (φ₂ * φ₃) :=
begin
  ext1 n,
  simp only [coeff_mul, finset.sum_mul, finset.mul_sum, finset.sum_sigma'],
  refine finset.sum_bij (λ p _, ⟨(p.2.1, p.2.2 + p.1.2), (p.2.2, p.1.2)⟩) _ _ _ _;
    simp only [mem_antidiagonal, finset.mem_sigma, heq_iff_eq, prod.mk.inj_iff, and_imp,
      exists_prop],
  { rintros ⟨⟨i,j⟩, ⟨k,l⟩⟩, dsimp only, rintro rfl rfl,
    simp [add_assoc] },
  { rintros ⟨⟨a, b⟩, ⟨c, d⟩⟩, dsimp only, rintro rfl rfl,
    apply mul_assoc },
  { rintros ⟨⟨a, b⟩, ⟨c, d⟩⟩ ⟨⟨i, j⟩, ⟨k, l⟩⟩, dsimp only, rintro rfl rfl - rfl rfl - rfl rfl,
    refl },
  { rintro ⟨⟨i, j⟩, ⟨k, l⟩⟩, dsimp only, rintro rfl rfl,
    refine ⟨⟨(i + k, l), (i, k)⟩, _, _⟩; simp [add_assoc] }
end

instance : semiring (mv_power_series σ R) :=
{ mul_one := mv_power_series.mul_one,
  one_mul := mv_power_series.one_mul,
  mul_assoc := mv_power_series.mul_assoc,
  mul_zero := mv_power_series.mul_zero,
  zero_mul := mv_power_series.zero_mul,
  left_distrib := mv_power_series.mul_add,
  right_distrib := mv_power_series.add_mul,
  .. mv_power_series.add_monoid_with_one,
  .. mv_power_series.has_mul,
  .. mv_power_series.add_comm_monoid }

end semiring

instance [comm_semiring R] : comm_semiring (mv_power_series σ R) :=
{ mul_comm := λ φ ψ, ext $ λ n, by simpa only [coeff_mul, mul_comm]
    using sum_antidiagonal_swap n (λ a b, coeff R a φ * coeff R b ψ),
  .. mv_power_series.semiring }

instance [ring R] : ring (mv_power_series σ R) :=
{ .. mv_power_series.semiring,
  .. mv_power_series.add_comm_group }

instance [comm_ring R] : comm_ring (mv_power_series σ R) :=
{ .. mv_power_series.comm_semiring,
  .. mv_power_series.add_comm_group }

section semiring
variables [semiring R]

lemma monomial_mul_monomial (m n : σ →₀ ℕ) (a b : R) :
  monomial R m a * monomial R n b = monomial R (m + n) (a * b) :=
begin
  ext k,
  simp only [coeff_mul_monomial, coeff_monomial],
  split_ifs with h₁ h₂ h₃ h₃ h₂; try { refl },
  { rw [← h₂, tsub_add_cancel_of_le h₁] at h₃, exact (h₃ rfl).elim },
  { rw [h₃, add_tsub_cancel_right] at h₂, exact (h₂ rfl).elim },
  { exact zero_mul b },
  { rw h₂ at h₁, exact (h₁ $ le_add_left le_rfl).elim }
end

variables (σ) (R)

/-- The constant multivariate formal power series.-/
def C : R →+* mv_power_series σ R :=
{ map_one' := rfl,
  map_mul' := λ a b, (monomial_mul_monomial 0 0 a b).symm,
  map_zero' := (monomial R (0 : _)).map_zero,
  .. monomial R (0 : σ →₀ ℕ) }

variables {σ} {R}

@[simp] lemma monomial_zero_eq_C : ⇑(monomial R (0 : σ →₀ ℕ)) = C σ R := rfl

lemma monomial_zero_eq_C_apply (a : R) : monomial R (0 : σ →₀ ℕ) a = C σ R a := rfl

lemma coeff_C [decidable_eq σ] (n : σ →₀ ℕ) (a : R) :
  coeff R n (C σ R a) = if n = 0 then a else 0 :=
coeff_monomial _ _ _

lemma coeff_zero_C (a : R) : coeff R (0 : σ →₀ℕ) (C σ R a) = a :=
coeff_monomial_same 0 a

/-- The variables of the multivariate formal power series ring.-/
def X (s : σ) : mv_power_series σ R := monomial R (single s 1) 1

lemma coeff_X [decidable_eq σ] (n : σ →₀ ℕ) (s : σ) :
  coeff R n (X s : mv_power_series σ R) = if n = (single s 1) then 1 else 0 :=
coeff_monomial _ _ _

lemma coeff_index_single_X [decidable_eq σ] (s t : σ) :
  coeff R (single t 1) (X s : mv_power_series σ R) = if t = s then 1 else 0 :=
by simp only [coeff_X, single_left_inj one_ne_zero]

@[simp] lemma coeff_index_single_self_X (s : σ) :
  coeff R (single s 1) (X s : mv_power_series σ R) = 1 :=
coeff_monomial_same _ _

lemma coeff_zero_X (s : σ) : coeff R (0 : σ →₀ ℕ) (X s : mv_power_series σ R) = 0 :=
by { rw [coeff_X, if_neg], intro h, exact one_ne_zero (single_eq_zero.mp h.symm) }

lemma commute_X (φ : mv_power_series σ R) (s : σ) : commute φ (X s) :=
φ.commute_monomial.mpr $ λ m, commute.one_right _

lemma X_def (s : σ) : X s = monomial R (single s 1) 1 := rfl

lemma X_pow_eq (s : σ) (n : ℕ) :
  (X s : mv_power_series σ R)^n = monomial R (single s n) 1 :=
begin
  induction n with n ih,
  { rw [pow_zero, finsupp.single_zero, monomial_zero_one] },
  { rw [pow_succ', ih, nat.succ_eq_add_one, finsupp.single_add, X, monomial_mul_monomial, one_mul] }
end

lemma coeff_X_pow [decidable_eq σ] (m : σ →₀ ℕ) (s : σ) (n : ℕ) :
  coeff R m ((X s : mv_power_series σ R)^n) = if m = single s n then 1 else 0 :=
by rw [X_pow_eq s n, coeff_monomial]

@[simp] lemma coeff_mul_C (n : σ →₀ ℕ) (φ : mv_power_series σ R) (a : R) :
  coeff R n (φ * C σ R a) = coeff R n φ * a :=
by simpa using coeff_add_mul_monomial n 0 φ a

@[simp] lemma coeff_C_mul (n : σ →₀ ℕ) (φ : mv_power_series σ R) (a : R) :
  coeff R n (C σ R a * φ) = a * coeff R n φ :=
by simpa using coeff_add_monomial_mul 0 n φ a

lemma coeff_zero_mul_X (φ : mv_power_series σ R) (s : σ) :
  coeff R (0 : σ →₀ ℕ) (φ * X s) = 0 :=
begin
  have : ¬single s 1 ≤ 0, from λ h, by simpa using h s,
  simp only [X, coeff_mul_monomial, if_neg this]
end

lemma coeff_zero_X_mul (φ : mv_power_series σ R) (s : σ) :
  coeff R (0 : σ →₀ ℕ) (X s * φ) = 0 :=
by rw [← (φ.commute_X s).eq, coeff_zero_mul_X]

variables (σ) (R)

/-- The constant coefficient of a formal power series.-/
def constant_coeff : (mv_power_series σ R) →+* R :=
{ to_fun := coeff R (0 : σ →₀ ℕ),
  map_one' := coeff_zero_one,
  map_mul' := λ φ ψ, by simp [coeff_mul, support_single_ne_zero],
  map_zero' := linear_map.map_zero _,
  .. coeff R (0 : σ →₀ ℕ) }

variables {σ} {R}

@[simp] lemma coeff_zero_eq_constant_coeff :
  ⇑(coeff R (0 : σ →₀ ℕ)) = constant_coeff σ R := rfl

lemma coeff_zero_eq_constant_coeff_apply (φ : mv_power_series σ R) :
  coeff R (0 : σ →₀ ℕ) φ = constant_coeff σ R φ := rfl

@[simp] lemma constant_coeff_C (a : R) : constant_coeff σ R (C σ R a) = a := rfl
@[simp] lemma constant_coeff_comp_C :
  (constant_coeff σ R).comp (C σ R) = ring_hom.id R := rfl

@[simp] lemma constant_coeff_zero : constant_coeff σ R 0 = 0 := rfl
@[simp] lemma constant_coeff_one : constant_coeff σ R 1 = 1 := rfl
@[simp] lemma constant_coeff_X (s : σ) : constant_coeff σ R (X s) = 0 := coeff_zero_X s

/-- If a multivariate formal power series is invertible,
 then so is its constant coefficient.-/
lemma is_unit_constant_coeff (φ : mv_power_series σ R) (h : is_unit φ) :
  is_unit (constant_coeff σ R φ) :=
h.map _

@[simp]
lemma coeff_smul (f : mv_power_series σ R) (n) (a : R) :
  coeff _ n (a • f) = a * coeff _ n f :=
rfl

lemma smul_eq_C_mul (f : mv_power_series σ R) (a : R) :
  a • f = C σ R a * f :=
by { ext, simp }

lemma X_inj [nontrivial R] {s t : σ} : (X s : mv_power_series σ R) = X t ↔ s = t :=
⟨begin
  intro h, replace h := congr_arg (coeff R (single s 1)) h, rw [coeff_X, if_pos rfl, coeff_X] at h,
  split_ifs at h with H,
  { rw finsupp.single_eq_single_iff at H,
    cases H, { exact H.1 }, { exfalso, exact one_ne_zero H.1 } },
  { exfalso, exact one_ne_zero h }
end, congr_arg X⟩

end semiring

section map
variables {S T : Type*} [semiring R] [semiring S] [semiring T]
variables (f : R →+* S) (g : S →+* T)

variable (σ)

/-- The map between multivariate formal power series induced by a map on the coefficients.-/
def map : mv_power_series σ R →+* mv_power_series σ S :=
{ to_fun := λ φ n, f $ coeff R n φ,
  map_zero' := ext $ λ n, f.map_zero,
  map_one' := ext $ λ n, show f ((coeff R n) 1) = (coeff S n) 1,
    by { rw [coeff_one, coeff_one], split_ifs; simp [f.map_one, f.map_zero] },
  map_add' := λ φ ψ, ext $ λ n,
    show f ((coeff R n) (φ + ψ)) = f ((coeff R n) φ) + f ((coeff R n) ψ), by simp,
  map_mul' := λ φ ψ, ext $ λ n, show f _ = _,
  begin
    rw [coeff_mul, f.map_sum, coeff_mul, finset.sum_congr rfl],
    rintros ⟨i,j⟩ hij, rw [f.map_mul], refl,
  end }

variable {σ}

@[simp] lemma map_id : map σ (ring_hom.id R) = ring_hom.id _ := rfl

lemma map_comp : map σ (g.comp f) = (map σ g).comp (map σ f) := rfl

@[simp] lemma coeff_map (n : σ →₀ ℕ) (φ : mv_power_series σ R) :
  coeff S n (map σ f φ) = f (coeff R n φ) := rfl

@[simp] lemma constant_coeff_map (φ : mv_power_series σ R) :
  constant_coeff σ S (map σ f φ) = f (constant_coeff σ R φ) := rfl

@[simp] lemma map_monomial (n : σ →₀ ℕ) (a : R) :
  map σ f (monomial R n a) = monomial S n (f a) :=
by { ext m, simp [coeff_monomial, apply_ite f] }

@[simp] lemma map_C (a : R) : map σ f (C σ R a) = C σ S (f a) :=
map_monomial _ _ _

@[simp] lemma map_X (s : σ) : map σ f (X s) = X s := by simp [mv_power_series.X]

end map

section algebra
variables {A : Type*} [comm_semiring R] [semiring A] [algebra R A]

instance : algebra R (mv_power_series σ A) :=
{ commutes' := λ a φ, by { ext n, simp [algebra.commutes] },
  smul_def' := λ a σ, by { ext n, simp [(coeff A n).map_smul_of_tower a, algebra.smul_def] },
  to_ring_hom := (mv_power_series.map σ (algebra_map R A)).comp (C σ R),
  .. mv_power_series.module }

theorem C_eq_algebra_map : C σ R = (algebra_map R (mv_power_series σ R)) := rfl

theorem algebra_map_apply {r : R} :
  algebra_map R (mv_power_series σ A) r = C σ A (algebra_map R A r) :=
begin
  change (mv_power_series.map σ (algebra_map R A)).comp (C σ R) r = _,
  simp,
end

instance [nonempty σ] [nontrivial R] : nontrivial (subalgebra R (mv_power_series σ R)) :=
⟨⟨⊥, ⊤, begin
  rw [ne.def, set_like.ext_iff, not_forall],
  inhabit σ,
  refine ⟨X default, _⟩,
  simp only [algebra.mem_bot, not_exists, set.mem_range, iff_true, algebra.mem_top],
  intros x,
  rw [ext_iff, not_forall],
  refine ⟨finsupp.single default 1, _⟩,
  simp [algebra_map_apply, coeff_C],
end⟩⟩

end algebra

section trunc
variables [comm_semiring R] (n : σ →₀ ℕ)

/-- Auxiliary definition for the truncation function. -/
def trunc_fun (φ : mv_power_series σ R) : mv_polynomial σ R :=
∑ m in finset.Iio n, mv_polynomial.monomial m (coeff R m φ)

lemma coeff_trunc_fun (m : σ →₀ ℕ) (φ : mv_power_series σ R) :
  (trunc_fun n φ).coeff m = if m < n then coeff R m φ else 0 :=
by simp [trunc_fun, mv_polynomial.coeff_sum]

variable (R)

/-- The `n`th truncation of a multivariate formal power series to a multivariate polynomial -/
def trunc : mv_power_series σ R →+ mv_polynomial σ R :=
{ to_fun := trunc_fun n,
  map_zero' := by { ext, simp [coeff_trunc_fun] },
  map_add' := by { intros, ext, simp [coeff_trunc_fun, ite_add], split_ifs; refl } }

variable {R}

lemma coeff_trunc (m : σ →₀ ℕ) (φ : mv_power_series σ R) :
  (trunc R n φ).coeff m = if m < n then coeff R m φ else 0 :=
by simp [trunc, coeff_trunc_fun]

@[simp] lemma trunc_one (hnn : n ≠ 0) : trunc R n 1 = 1 :=
mv_polynomial.ext _ _ $ λ m,
begin
  rw [coeff_trunc, coeff_one],
  split_ifs with H H' H',
  { subst m, simp },
  { symmetry, rw mv_polynomial.coeff_one, exact if_neg (ne.symm H'), },
  { symmetry, rw mv_polynomial.coeff_one, refine if_neg _,
    rintro rfl, apply H, exact ne.bot_lt hnn, }
end

@[simp] lemma trunc_C (hnn : n ≠ 0) (a : R) : trunc R n (C σ R a) = mv_polynomial.C a :=
mv_polynomial.ext _ _ $ λ m,
begin
  rw [coeff_trunc, coeff_C, mv_polynomial.coeff_C],
  split_ifs with H; refl <|> try {simp * at *},
  exfalso, apply H, subst m, exact ne.bot_lt hnn,
end

end trunc

section semiring
variable [semiring R]

lemma X_pow_dvd_iff {s : σ} {n : ℕ} {φ : mv_power_series σ R} :
  (X s : mv_power_series σ R)^n ∣ φ ↔ ∀ m : σ →₀ ℕ, m s < n → coeff R m φ = 0 :=
begin
  split,
  { rintros ⟨φ, rfl⟩ m h,
    rw [coeff_mul, finset.sum_eq_zero],
    rintros ⟨i,j⟩ hij, rw [coeff_X_pow, if_neg, zero_mul],
    contrapose! h, subst i, rw finsupp.mem_antidiagonal at hij,
    rw [← hij, finsupp.add_apply, finsupp.single_eq_same], exact nat.le_add_right n _ },
  { intro h, refine ⟨λ m, coeff R (m + (single s n)) φ, _⟩,
    ext m, by_cases H : m - single s n + single s n = m,
    { rw [coeff_mul, finset.sum_eq_single (single s n, m - single s n)],
      { rw [coeff_X_pow, if_pos rfl, one_mul],
        simpa using congr_arg (λ (m : σ →₀ ℕ), coeff R m φ) H.symm },
      { rintros ⟨i,j⟩ hij hne, rw finsupp.mem_antidiagonal at hij,
        rw coeff_X_pow, split_ifs with hi,
        { exfalso, apply hne, rw [← hij, ← hi, prod.mk.inj_iff], refine ⟨rfl, _⟩,
          ext t, simp only [add_tsub_cancel_left, finsupp.add_apply, finsupp.tsub_apply] },
        { exact zero_mul _ } },
      { intro hni, exfalso, apply hni, rwa [finsupp.mem_antidiagonal, add_comm] } },
    { rw [h, coeff_mul, finset.sum_eq_zero],
      { rintros ⟨i,j⟩ hij, rw finsupp.mem_antidiagonal at hij,
        rw coeff_X_pow, split_ifs with hi,
        { exfalso, apply H, rw [← hij, hi], ext,
          rw [coe_add, coe_add, pi.add_apply, pi.add_apply, add_tsub_cancel_left, add_comm], },
        { exact zero_mul _ } },
      { classical, contrapose! H, ext t,
        by_cases hst : s = t,
        { subst t, simpa using tsub_add_cancel_of_le H },
        { simp [finsupp.single_apply, hst] } } } }
end

lemma X_dvd_iff {s : σ} {φ : mv_power_series σ R} :
  (X s : mv_power_series σ R) ∣ φ ↔ ∀ m : σ →₀ ℕ, m s = 0 → coeff R m φ = 0 :=
begin
  rw [← pow_one (X s : mv_power_series σ R), X_pow_dvd_iff],
  split; intros h m hm,
  { exact h m (hm.symm ▸ zero_lt_one) },
  { exact h m (nat.eq_zero_of_le_zero $ nat.le_of_succ_le_succ hm) }
end

end semiring

section ring
variables [ring R]

/-
The inverse of a multivariate formal power series is defined by
well-founded recursion on the coeffients of the inverse.
-/

/-- Auxiliary definition that unifies
 the totalised inverse formal power series `(_)⁻¹` and
 the inverse formal power series that depends on
 an inverse of the constant coefficient `inv_of_unit`.-/
protected noncomputable def inv.aux (a : R) (φ : mv_power_series σ R) : mv_power_series σ R
| n := if n = 0 then a else
- a * ∑ x in n.antidiagonal,
    if h : x.2 < n then coeff R x.1 φ * inv.aux x.2 else 0
using_well_founded
{ rel_tac := λ _ _, `[exact ⟨_, finsupp.lt_wf σ⟩],
  dec_tac := tactic.assumption }

lemma coeff_inv_aux [decidable_eq σ] (n : σ →₀ ℕ) (a : R) (φ : mv_power_series σ R) :
  coeff R n (inv.aux a φ) = if n = 0 then a else
  - a * ∑ x in n.antidiagonal,
    if x.2 < n then coeff R x.1 φ * coeff R x.2 (inv.aux a φ) else 0 :=
show inv.aux a φ n = _,
begin
  rw inv.aux,
  convert rfl -- unify `decidable` instances
end

/-- A multivariate formal power series is invertible if the constant coefficient is invertible.-/
def inv_of_unit (φ : mv_power_series σ R) (u : Rˣ) : mv_power_series σ R :=
inv.aux (↑u⁻¹) φ

lemma coeff_inv_of_unit [decidable_eq σ] (n : σ →₀ ℕ) (φ : mv_power_series σ R) (u : Rˣ) :
  coeff R n (inv_of_unit φ u) = if n = 0 then ↑u⁻¹ else
  - ↑u⁻¹ * ∑ x in n.antidiagonal,
    if x.2 < n then coeff R x.1 φ * coeff R x.2 (inv_of_unit φ u) else 0 :=
coeff_inv_aux n (↑u⁻¹) φ

@[simp] lemma constant_coeff_inv_of_unit (φ : mv_power_series σ R) (u : Rˣ) :
  constant_coeff σ R (inv_of_unit φ u) = ↑u⁻¹ :=
by rw [← coeff_zero_eq_constant_coeff_apply, coeff_inv_of_unit, if_pos rfl]

lemma mul_inv_of_unit (φ : mv_power_series σ R) (u : Rˣ) (h : constant_coeff σ R φ = u) :
  φ * inv_of_unit φ u = 1 :=
ext $ λ n, if H : n = 0 then by { rw H, simp [coeff_mul, support_single_ne_zero, h], }
else
begin
  have : ((0 : σ →₀ ℕ), n) ∈ n.antidiagonal,
  { rw [finsupp.mem_antidiagonal, zero_add] },
  rw [coeff_one, if_neg H, coeff_mul,
    ← finset.insert_erase this, finset.sum_insert (finset.not_mem_erase _ _),
    coeff_zero_eq_constant_coeff_apply, h, coeff_inv_of_unit, if_neg H,
    neg_mul, mul_neg, units.mul_inv_cancel_left,
    ← finset.insert_erase this, finset.sum_insert (finset.not_mem_erase _ _),
    finset.insert_erase this, if_neg (not_lt_of_ge $ le_rfl), zero_add, add_comm,
    ← sub_eq_add_neg, sub_eq_zero, finset.sum_congr rfl],
  rintros ⟨i,j⟩ hij, rw [finset.mem_erase, finsupp.mem_antidiagonal] at hij,
  cases hij with h₁ h₂,
  subst n, rw if_pos,
  suffices : (0 : _) + j < i + j, {simpa},
  apply add_lt_add_right,
  split,
  { intro s, exact nat.zero_le _ },
  { intro H, apply h₁,
    suffices : i = 0, {simp [this]},
    ext1 s, exact nat.eq_zero_of_le_zero (H s) }
end

end ring

section comm_ring
variable [comm_ring R]

/-- Multivariate formal power series over a local ring form a local ring. -/
instance [local_ring R] : local_ring (mv_power_series σ R) :=
local_ring.of_is_unit_or_is_unit_one_sub_self $ by
{ intro φ,
  rcases local_ring.is_unit_or_is_unit_one_sub_self (constant_coeff σ R φ) with ⟨u,h⟩|⟨u,h⟩;
    [left, right];
    { refine is_unit_of_mul_eq_one _ _ (mul_inv_of_unit _ u _),
      simpa using h.symm } }

-- TODO(jmc): once adic topology lands, show that this is complete

end comm_ring

section local_ring
variables {S : Type*} [comm_ring R] [comm_ring S] (f : R →+* S)
  [is_local_ring_hom f]

-- Thanks to the linter for informing us that  this instance does
-- not actually need R and S to be local rings!

/-- The map `A[[X]] → B[[X]]` induced by a local ring hom `A → B` is local -/
instance map.is_local_ring_hom : is_local_ring_hom (map σ f) :=
⟨begin
  rintros φ ⟨ψ, h⟩,
  replace h := congr_arg (constant_coeff σ S) h,
  rw constant_coeff_map at h,
  have : is_unit (constant_coeff σ S ↑ψ) := @is_unit_constant_coeff σ S _ (↑ψ) ψ.is_unit,
  rw h at this,
  rcases is_unit_of_map_unit f _ this with ⟨c, hc⟩,
  exact is_unit_of_mul_eq_one φ (inv_of_unit φ c) (mul_inv_of_unit φ c hc.symm)
end⟩

end local_ring

section field
variables {k : Type*} [field k]

/-- The inverse `1/f` of a multivariable power series `f` over a field -/
protected def inv (φ : mv_power_series σ k) : mv_power_series σ k :=
inv.aux (constant_coeff σ k φ)⁻¹ φ

instance : has_inv (mv_power_series σ k) := ⟨mv_power_series.inv⟩

lemma coeff_inv [decidable_eq σ] (n : σ →₀ ℕ) (φ : mv_power_series σ k) :
  coeff k n (φ⁻¹) = if n = 0 then (constant_coeff σ k φ)⁻¹ else
  - (constant_coeff σ k φ)⁻¹ * ∑ x in n.antidiagonal,
    if x.2 < n then coeff k x.1 φ * coeff k x.2 (φ⁻¹) else 0 :=
coeff_inv_aux n _ φ

@[simp] lemma constant_coeff_inv (φ : mv_power_series σ k) :
  constant_coeff σ k (φ⁻¹) = (constant_coeff σ k φ)⁻¹ :=
by rw [← coeff_zero_eq_constant_coeff_apply, coeff_inv, if_pos rfl]

lemma inv_eq_zero {φ : mv_power_series σ k} :
  φ⁻¹ = 0 ↔ constant_coeff σ k φ = 0 :=
⟨λ h, by simpa using congr_arg (constant_coeff σ k) h,
 λ h, ext $ λ n, by { rw coeff_inv, split_ifs;
  simp only [h, mv_power_series.coeff_zero, zero_mul, inv_zero, neg_zero] }⟩

@[simp] lemma zero_inv : (0 : mv_power_series σ k)⁻¹ = 0 :=
by rw [inv_eq_zero, constant_coeff_zero]

@[simp, priority 1100]
lemma inv_of_unit_eq (φ : mv_power_series σ k) (h : constant_coeff σ k φ ≠ 0) :
  inv_of_unit φ (units.mk0 _ h) = φ⁻¹ := rfl

@[simp]
lemma inv_of_unit_eq' (φ : mv_power_series σ k) (u : units k) (h : constant_coeff σ k φ = u) :
  inv_of_unit φ u = φ⁻¹ :=
begin
  rw ← inv_of_unit_eq φ (h.symm ▸ u.ne_zero),
  congr' 1, rw [units.ext_iff], exact h.symm,
end

@[simp] protected lemma mul_inv_cancel (φ : mv_power_series σ k) (h : constant_coeff σ k φ ≠ 0) :
  φ * φ⁻¹ = 1 :=
by rw [← inv_of_unit_eq φ h, mul_inv_of_unit φ (units.mk0 _ h) rfl]

@[simp] protected lemma inv_mul_cancel (φ : mv_power_series σ k) (h : constant_coeff σ k φ ≠ 0) :
  φ⁻¹ * φ = 1 :=
by rw [mul_comm, φ.mul_inv_cancel h]

protected lemma eq_mul_inv_iff_mul_eq {φ₁ φ₂ φ₃ : mv_power_series σ k}
  (h : constant_coeff σ k φ₃ ≠ 0) :
  φ₁ = φ₂ * φ₃⁻¹ ↔ φ₁ * φ₃ = φ₂ :=
⟨λ k, by simp [k, mul_assoc, mv_power_series.inv_mul_cancel _ h],
 λ k, by simp [← k, mul_assoc, mv_power_series.mul_inv_cancel _ h]⟩

protected lemma eq_inv_iff_mul_eq_one {φ ψ : mv_power_series σ k} (h : constant_coeff σ k ψ ≠ 0) :
  φ = ψ⁻¹ ↔ φ * ψ = 1 :=
by rw [← mv_power_series.eq_mul_inv_iff_mul_eq h, one_mul]

protected lemma inv_eq_iff_mul_eq_one {φ ψ : mv_power_series σ k} (h : constant_coeff σ k ψ ≠ 0) :
  ψ⁻¹ = φ ↔ φ * ψ = 1 :=
by rw [eq_comm, mv_power_series.eq_inv_iff_mul_eq_one h]

@[simp] protected lemma mul_inv_rev (φ ψ : mv_power_series σ k) :
  (φ * ψ)⁻¹ = ψ⁻¹ * φ⁻¹ :=
begin
  by_cases h : constant_coeff σ k (φ * ψ) = 0,
  { rw inv_eq_zero.mpr h,
    simp only [map_mul, mul_eq_zero] at h,
    -- we don't have `no_zero_divisors (mw_power_series σ k)` yet,
    cases h;
    simp [inv_eq_zero.mpr h] },
  { rw [mv_power_series.inv_eq_iff_mul_eq_one h],
    simp only [not_or_distrib, map_mul, mul_eq_zero] at h,
    rw [←mul_assoc, mul_assoc _⁻¹, mv_power_series.inv_mul_cancel _ h.left, mul_one,
        mv_power_series.inv_mul_cancel _ h.right] }
end

instance : inv_one_class (mv_power_series σ k) :=
{ inv_one := by { rw [mv_power_series.inv_eq_iff_mul_eq_one, mul_one], simp },
  ..mv_power_series.has_one,
  ..mv_power_series.has_inv }

@[simp] lemma C_inv (r : k) : (C σ k r)⁻¹ = C σ k r⁻¹ :=
begin
  rcases eq_or_ne r 0 with rfl|hr,
  { simp },
  rw [mv_power_series.inv_eq_iff_mul_eq_one, ←map_mul, inv_mul_cancel hr, map_one],
  simpa using hr
end

@[simp] lemma X_inv (s : σ) : (X s : mv_power_series σ k)⁻¹ = 0 :=
by rw [inv_eq_zero, constant_coeff_X]

@[simp]

end field

end mv_power_series

namespace mv_polynomial
open finsupp
variables {σ : Type*} {R : Type*} [comm_semiring R] (φ ψ : mv_polynomial σ R)

/-- The natural inclusion from multivariate polynomials into multivariate formal power series.-/
instance coe_to_mv_power_series : has_coe (mv_polynomial σ R) (mv_power_series σ R) :=
⟨λ φ n, coeff n φ⟩

lemma coe_def : (φ : mv_power_series σ R) = λ n, coeff n φ := rfl

@[simp, norm_cast] lemma coeff_coe (n : σ →₀ ℕ) :
mv_power_series.coeff R n ↑φ = coeff n φ := rfl

@[simp, norm_cast] lemma coe_monomial (n : σ →₀ ℕ) (a : R) :
  (monomial n a : mv_power_series σ R) = mv_power_series.monomial R n a :=
mv_power_series.ext $ λ m,
begin
  rw [coeff_coe, coeff_monomial, mv_power_series.coeff_monomial],
  split_ifs with h₁ h₂; refl <|> subst m; contradiction
end

@[simp, norm_cast] lemma coe_zero : ((0 : mv_polynomial σ R) : mv_power_series σ R) = 0 := rfl

@[simp, norm_cast] lemma coe_one : ((1 : mv_polynomial σ R) : mv_power_series σ R) = 1 :=
coe_monomial _ _

@[simp, norm_cast] lemma coe_add :
  ((φ + ψ : mv_polynomial σ R) : mv_power_series σ R) = φ + ψ := rfl

@[simp, norm_cast] lemma coe_mul :
  ((φ * ψ : mv_polynomial σ R) : mv_power_series σ R) = φ * ψ :=
mv_power_series.ext $ λ n,
by simp only [coeff_coe, mv_power_series.coeff_mul, coeff_mul]

@[simp, norm_cast] lemma coe_C (a : R) :
  ((C a : mv_polynomial σ R) : mv_power_series σ R) = mv_power_series.C σ R a :=
coe_monomial _ _

@[simp, norm_cast] lemma coe_bit0 :
  ((bit0 φ : mv_polynomial σ R) : mv_power_series σ R) = bit0 (φ : mv_power_series σ R) :=
coe_add _ _

@[simp, norm_cast] lemma coe_bit1 :
  ((bit1 φ : mv_polynomial σ R) : mv_power_series σ R) = bit1 (φ : mv_power_series σ R) :=
by rw [bit1, bit1, coe_add, coe_one, coe_bit0]

@[simp, norm_cast] lemma coe_X (s : σ) :
  ((X s : mv_polynomial σ R) : mv_power_series σ R) = mv_power_series.X s :=
coe_monomial _ _

variables (σ R)

lemma coe_injective : function.injective (coe : mv_polynomial σ R → mv_power_series σ R) :=
λ x y h, by { ext, simp_rw [←coeff_coe, h] }

variables {σ R φ ψ}

@[simp, norm_cast] lemma coe_inj : (φ : mv_power_series σ R) = ψ ↔ φ = ψ :=
(coe_injective σ R).eq_iff

@[simp] lemma coe_eq_zero_iff : (φ : mv_power_series σ R) = 0 ↔ φ = 0 :=
by rw [←coe_zero, coe_inj]

@[simp] lemma coe_eq_one_iff : (φ : mv_power_series σ R) = 1 ↔ φ = 1 :=
by rw [←coe_one, coe_inj]

/--
The coercion from multivariable polynomials to multivariable power series
as a ring homomorphism.
-/
def coe_to_mv_power_series.ring_hom : mv_polynomial σ R →+* mv_power_series σ R :=
{ to_fun := (coe : mv_polynomial σ R → mv_power_series σ R),
  map_zero' := coe_zero,
  map_one' := coe_one,
  map_add' := coe_add,
  map_mul' := coe_mul }

@[simp, norm_cast] lemma coe_pow (n : ℕ) :
  ((φ ^ n : mv_polynomial σ R) : mv_power_series σ R) = (φ : mv_power_series σ R) ^ n :=
coe_to_mv_power_series.ring_hom.map_pow _ _

variables (φ ψ)

@[simp] lemma coe_to_mv_power_series.ring_hom_apply : coe_to_mv_power_series.ring_hom φ = φ := rfl

section algebra

variables (A : Type*) [comm_semiring A] [algebra R A]

/--
The coercion from multivariable polynomials to multivariable power series
as an algebra homomorphism.
-/
def coe_to_mv_power_series.alg_hom : mv_polynomial σ R →ₐ[R] mv_power_series σ A :=
{ commutes' := λ r, by simp [algebra_map_apply, mv_power_series.algebra_map_apply],
  ..(mv_power_series.map σ (algebra_map R A)).comp coe_to_mv_power_series.ring_hom}

@[simp] lemma coe_to_mv_power_series.alg_hom_apply : (coe_to_mv_power_series.alg_hom A φ) =
  mv_power_series.map σ (algebra_map R A) ↑φ := rfl

end algebra

end mv_polynomial

namespace mv_power_series

variables {σ R A : Type*} [comm_semiring R] [comm_semiring A] [algebra R A]
  (f : mv_power_series σ R)

instance algebra_mv_polynomial : algebra (mv_polynomial σ R) (mv_power_series σ A) :=
ring_hom.to_algebra (mv_polynomial.coe_to_mv_power_series.alg_hom A).to_ring_hom

instance algebra_mv_power_series : algebra (mv_power_series σ R) (mv_power_series σ A) :=
(map σ (algebra_map R A)).to_algebra

variables (A)

lemma algebra_map_apply' (p : mv_polynomial σ R):
  algebra_map (mv_polynomial σ R) (mv_power_series σ A) p = map σ (algebra_map R A) p := rfl

lemma algebra_map_apply'' :
  algebra_map (mv_power_series σ R) (mv_power_series σ A) f = map σ (algebra_map R A) f := rfl

end mv_power_series

/-- Formal power series over the coefficient ring `R`.-/
def power_series (R : Type*) := mv_power_series unit R

namespace power_series
open finsupp (single)
variable {R : Type*}

section
local attribute [reducible] power_series

instance [inhabited R]       : inhabited       (power_series R) := by apply_instance
instance [add_monoid R]      : add_monoid      (power_series R) := by apply_instance
instance [add_group R]       : add_group       (power_series R) := by apply_instance
instance [add_comm_monoid R] : add_comm_monoid (power_series R) := by apply_instance
instance [add_comm_group R]  : add_comm_group  (power_series R) := by apply_instance
instance [semiring R]        : semiring        (power_series R) := by apply_instance
instance [comm_semiring R]   : comm_semiring   (power_series R) := by apply_instance
instance [ring R]            : ring            (power_series R) := by apply_instance
instance [comm_ring R]       : comm_ring       (power_series R) := by apply_instance
instance [nontrivial R]      : nontrivial      (power_series R) := by apply_instance

instance {A} [semiring R] [add_comm_monoid A] [module R A] :
  module R (power_series A) := by apply_instance

instance {A S} [semiring R] [semiring S] [add_comm_monoid A] [module R A] [module S A]
  [has_smul R S] [is_scalar_tower R S A] :
  is_scalar_tower R S (power_series A) :=
pi.is_scalar_tower

instance {A} [semiring A] [comm_semiring R] [algebra R A] :
  algebra R (power_series A) := by apply_instance

end

section semiring
variables (R) [semiring R]

/-- The `n`th coefficient of a formal power series.-/
def coeff (n : ℕ) : power_series R →ₗ[R] R := mv_power_series.coeff R (single () n)

/-- The `n`th monomial with coefficient `a` as formal power series.-/
def monomial (n : ℕ) : R →ₗ[R] power_series R := mv_power_series.monomial R (single () n)

variables {R}

lemma coeff_def {s : unit →₀ ℕ} {n : ℕ} (h : s () = n) :
  coeff R n = mv_power_series.coeff R s :=
by erw [coeff, ← h, ← finsupp.unique_single s]

/-- Two formal power series are equal if all their coefficients are equal.-/
@[ext] lemma ext {φ ψ : power_series R} (h : ∀ n, coeff R n φ = coeff R n ψ) :
  φ = ψ :=
mv_power_series.ext $ λ n,
by { rw ← coeff_def, { apply h }, refl }

/-- Two formal power series are equal if all their coefficients are equal.-/
lemma ext_iff {φ ψ : power_series R} : φ = ψ ↔ (∀ n, coeff R n φ = coeff R n ψ) :=
⟨λ h n, congr_arg (coeff R n) h, ext⟩

/-- Constructor for formal power series.-/
def mk {R} (f : ℕ → R) : power_series R := λ s, f (s ())

@[simp] lemma coeff_mk (n : ℕ) (f : ℕ → R) : coeff R n (mk f) = f n :=
congr_arg f finsupp.single_eq_same

lemma coeff_monomial (m n : ℕ) (a : R) :
  coeff R m (monomial R n a) = if m = n then a else 0 :=
calc coeff R m (monomial R n a) = _ : mv_power_series.coeff_monomial _ _ _
    ... = if m = n then a else 0 :
by simp only [finsupp.unique_single_eq_iff]

lemma monomial_eq_mk (n : ℕ) (a : R) :
  monomial R n a = mk (λ m, if m = n then a else 0) :=
ext $ λ m, by { rw [coeff_monomial, coeff_mk] }

@[simp] lemma coeff_monomial_same (n : ℕ) (a : R) :
  coeff R n (monomial R n a) = a :=
mv_power_series.coeff_monomial_same _ _

@[simp] lemma coeff_comp_monomial (n : ℕ) :
  (coeff R n).comp (monomial R n) = linear_map.id :=
linear_map.ext $ coeff_monomial_same n

variable (R)

/--The constant coefficient of a formal power series. -/
def constant_coeff : power_series R →+* R := mv_power_series.constant_coeff unit R

/-- The constant formal power series.-/
def C : R →+* power_series R := mv_power_series.C unit R

variable {R}

/-- The variable of the formal power series ring.-/
def X : power_series R := mv_power_series.X ()

lemma commute_X (φ : power_series R) : commute φ X := φ.commute_X _

@[simp] lemma coeff_zero_eq_constant_coeff :
  ⇑(coeff R 0) = constant_coeff R :=
by { rw [coeff, finsupp.single_zero], refl }

lemma coeff_zero_eq_constant_coeff_apply (φ : power_series R) :
  coeff R 0 φ = constant_coeff R φ :=
by rw [coeff_zero_eq_constant_coeff]; refl

@[simp] lemma monomial_zero_eq_C : ⇑(monomial R 0) = C R :=
by rw [monomial, finsupp.single_zero, mv_power_series.monomial_zero_eq_C, C]

lemma monomial_zero_eq_C_apply (a : R) : monomial R 0 a = C R a :=
by simp

lemma coeff_C (n : ℕ) (a : R) :
  coeff R n (C R a : power_series R) = if n = 0 then a else 0 :=
by rw [← monomial_zero_eq_C_apply, coeff_monomial]

@[simp] lemma coeff_zero_C (a : R) : coeff R 0 (C R a) = a :=
by rw [← monomial_zero_eq_C_apply, coeff_monomial_same 0 a]

lemma X_eq : (X : power_series R) = monomial R 1 1 := rfl

lemma coeff_X (n : ℕ) :
  coeff R n (X : power_series R) = if n = 1 then 1 else 0 :=
by rw [X_eq, coeff_monomial]

@[simp] lemma coeff_zero_X : coeff R 0 (X : power_series R) = 0 :=
by rw [coeff, finsupp.single_zero, X, mv_power_series.coeff_zero_X]

@[simp] lemma coeff_one_X : coeff R 1 (X : power_series R) = 1 :=
by rw [coeff_X, if_pos rfl]

@[simp] lemma X_ne_zero [nontrivial R] : (X : power_series R) ≠ 0 :=
λ H, by simpa only [coeff_one_X, one_ne_zero, map_zero] using congr_arg (coeff R 1) H

lemma X_pow_eq (n : ℕ) : (X : power_series R)^n = monomial R n 1 :=
mv_power_series.X_pow_eq _ n

lemma coeff_X_pow (m n : ℕ) :
  coeff R m ((X : power_series R)^n) = if m = n then 1 else 0 :=
by rw [X_pow_eq, coeff_monomial]

@[simp] lemma coeff_X_pow_self (n : ℕ) :
  coeff R n ((X : power_series R)^n) = 1 :=
by rw [coeff_X_pow, if_pos rfl]

@[simp] lemma coeff_one (n : ℕ) :
  coeff R n (1 : power_series R) = if n = 0 then 1 else 0 :=
coeff_C n 1

lemma coeff_zero_one : coeff R 0 (1 : power_series R) = 1 :=
coeff_zero_C 1

lemma coeff_mul (n : ℕ) (φ ψ : power_series R) :
  coeff R n (φ * ψ) = ∑ p in finset.nat.antidiagonal n, coeff R p.1 φ * coeff R p.2 ψ :=
begin
  symmetry,
  apply finset.sum_bij (λ (p : ℕ × ℕ) h, (single () p.1, single () p.2)),
  { rintros ⟨i,j⟩ hij, rw finset.nat.mem_antidiagonal at hij,
    rw [finsupp.mem_antidiagonal, ← finsupp.single_add, hij], },
  { rintros ⟨i,j⟩ hij, refl },
  { rintros ⟨i,j⟩ ⟨k,l⟩ hij hkl,
    simpa only [prod.mk.inj_iff, finsupp.unique_single_eq_iff] using id },
  { rintros ⟨f,g⟩ hfg,
    refine ⟨(f (), g ()), _, _⟩,
    { rw finsupp.mem_antidiagonal at hfg,
      rw [finset.nat.mem_antidiagonal, ← finsupp.add_apply, hfg, finsupp.single_eq_same] },
    { rw prod.mk.inj_iff, dsimp,
      exact ⟨finsupp.unique_single f, finsupp.unique_single g⟩ } }
end

@[simp] lemma coeff_mul_C (n : ℕ) (φ : power_series R) (a : R) :
  coeff R n (φ * C R a) = coeff R n φ * a :=
mv_power_series.coeff_mul_C _ φ a

@[simp] lemma coeff_C_mul (n : ℕ) (φ : power_series R) (a : R) :
  coeff R n (C R a * φ) = a * coeff R n φ :=
mv_power_series.coeff_C_mul _ φ a

@[simp] lemma coeff_smul {S : Type*} [semiring S] [module R S]
  (n : ℕ) (φ : power_series S) (a : R) : coeff S n (a • φ) = a • coeff S n φ :=
rfl

lemma smul_eq_C_mul (f : power_series R) (a : R) :
  a • f = C R a * f :=
by { ext, simp }

@[simp] lemma coeff_succ_mul_X (n : ℕ) (φ : power_series R) :
  coeff R (n+1) (φ * X) = coeff R n φ :=
begin
  simp only [coeff, finsupp.single_add],
  convert φ.coeff_add_mul_monomial (single () n) (single () 1) _,
  rw mul_one
end

@[simp] lemma coeff_succ_X_mul (n : ℕ) (φ : power_series R) :
  coeff R (n + 1) (X * φ) = coeff R n φ :=
begin
  simp only [coeff, finsupp.single_add, add_comm n 1],
  convert φ.coeff_add_monomial_mul (single () 1) (single () n) _,
  rw one_mul,
end

@[simp] lemma constant_coeff_C (a : R) : constant_coeff R (C R a) = a := rfl
@[simp] lemma constant_coeff_comp_C :
  (constant_coeff R).comp (C R) = ring_hom.id R := rfl

@[simp] lemma constant_coeff_zero : constant_coeff R 0 = 0 := rfl
@[simp] lemma constant_coeff_one : constant_coeff R 1 = 1 := rfl
@[simp] lemma constant_coeff_X : constant_coeff R X = 0 := mv_power_series.coeff_zero_X _

lemma coeff_zero_mul_X (φ : power_series R) : coeff R 0 (φ * X) = 0 := by simp

lemma coeff_zero_X_mul (φ : power_series R) : coeff R 0 (X * φ) = 0 := by simp

-- The following section duplicates the api of `data.polynomial.coeff` and should attempt to keep
-- up to date with that
section
lemma coeff_C_mul_X_pow (x : R) (k n : ℕ) :
  coeff R n (C R x * X ^ k : power_series R) = if n = k then x else 0 :=
by simp [X_pow_eq, coeff_monomial]

@[simp]
theorem coeff_mul_X_pow (p : power_series R) (n d : ℕ) :
  coeff R (d + n) (p * X ^ n) = coeff R d p :=
begin
  rw [coeff_mul, finset.sum_eq_single (d, n), coeff_X_pow, if_pos rfl, mul_one],
  { rintros ⟨i,j⟩ h1 h2, rw [coeff_X_pow, if_neg, mul_zero], rintro rfl, apply h2,
    rw [finset.nat.mem_antidiagonal, add_right_cancel_iff] at h1, subst h1 },
  { exact λ h1, (h1 (finset.nat.mem_antidiagonal.2 rfl)).elim }
end

@[simp]
theorem coeff_X_pow_mul (p : power_series R) (n d : ℕ) :
  coeff R (d + n) (X ^ n * p) = coeff R d p :=
begin
  rw [coeff_mul, finset.sum_eq_single (n,d), coeff_X_pow, if_pos rfl, one_mul],
  { rintros ⟨i,j⟩ h1 h2, rw [coeff_X_pow, if_neg, zero_mul], rintro rfl, apply h2,
    rw [finset.nat.mem_antidiagonal, add_comm, add_right_cancel_iff] at h1, subst h1 },
  { rw add_comm,
    exact λ h1, (h1 (finset.nat.mem_antidiagonal.2 rfl)).elim }
end

lemma coeff_mul_X_pow' (p : power_series R) (n d : ℕ) :
  coeff R d (p * X ^ n) = ite (n ≤ d) (coeff R (d - n) p) 0 :=
begin
  split_ifs,
  { rw [← tsub_add_cancel_of_le h, coeff_mul_X_pow, add_tsub_cancel_right] },
  { refine (coeff_mul _ _ _).trans (finset.sum_eq_zero (λ x hx, _)),
    rw [coeff_X_pow, if_neg, mul_zero],
    exact ((le_of_add_le_right (finset.nat.mem_antidiagonal.mp hx).le).trans_lt $ not_le.mp h).ne }
end

lemma coeff_X_pow_mul' (p : power_series R) (n d : ℕ) :
  coeff R d (X ^ n * p) = ite (n ≤ d) (coeff R (d - n) p) 0 :=
begin
  split_ifs,
  { rw [← tsub_add_cancel_of_le h, coeff_X_pow_mul], simp, },
  { refine (coeff_mul _ _ _).trans (finset.sum_eq_zero (λ x hx, _)),
    rw [coeff_X_pow, if_neg, zero_mul],
    have := finset.nat.mem_antidiagonal.mp hx,
    rw add_comm at this,
    exact ((le_of_add_le_right this.le).trans_lt $ not_le.mp h).ne }
end

end

/-- If a formal power series is invertible, then so is its constant coefficient.-/
lemma is_unit_constant_coeff (φ : power_series R) (h : is_unit φ) :
  is_unit (constant_coeff R φ) :=
mv_power_series.is_unit_constant_coeff φ h

/-- Split off the constant coefficient. -/
lemma eq_shift_mul_X_add_const (φ : power_series R) :
  φ = mk (λ p, coeff R (p + 1) φ) * X + C R (constant_coeff R φ) :=
begin
  ext (_ | n),
  { simp only [ring_hom.map_add, constant_coeff_C, constant_coeff_X, coeff_zero_eq_constant_coeff,
      zero_add, mul_zero, ring_hom.map_mul], },
  { simp only [coeff_succ_mul_X, coeff_mk, linear_map.map_add, coeff_C, n.succ_ne_zero, sub_zero,
      if_false, add_zero], }
end

/-- Split off the constant coefficient. -/
lemma eq_X_mul_shift_add_const (φ : power_series R) :
  φ = X * mk (λ p, coeff R (p + 1) φ) + C R (constant_coeff R φ) :=
begin
  ext (_ | n),
  { simp only [ring_hom.map_add, constant_coeff_C, constant_coeff_X, coeff_zero_eq_constant_coeff,
      zero_add, zero_mul, ring_hom.map_mul], },
  { simp only [coeff_succ_X_mul, coeff_mk, linear_map.map_add, coeff_C, n.succ_ne_zero, sub_zero,
      if_false, add_zero], }
end

section map
variables {S : Type*} {T : Type*} [semiring S] [semiring T]
variables (f : R →+* S) (g : S →+* T)

/-- The map between formal power series induced by a map on the coefficients.-/
def map : power_series R →+* power_series S :=
mv_power_series.map _ f

@[simp] lemma map_id : (map (ring_hom.id R) :
  power_series R → power_series R) = id := rfl

lemma map_comp : map (g.comp f) = (map g).comp (map f) := rfl

@[simp] lemma coeff_map (n : ℕ) (φ : power_series R) :
  coeff S n (map f φ) = f (coeff R n φ) := rfl

@[simp] lemma map_C (r : R) : map f (C _ r) = C _ (f r) :=
by { ext, simp [coeff_C, apply_ite f] }

@[simp] lemma map_X : map f X = X :=
by { ext, simp [coeff_X, apply_ite f] }

end map

lemma X_pow_dvd_iff {n : ℕ} {φ : power_series R} :
  (X : power_series R)^n ∣ φ ↔ ∀ m, m < n → coeff R m φ = 0 :=
begin
  convert @mv_power_series.X_pow_dvd_iff unit R _ () n φ, apply propext,
  classical, split; intros h m hm,
  { rw finsupp.unique_single m, convert h _ hm },
  { apply h, simpa only [finsupp.single_eq_same] using hm }
end

lemma X_dvd_iff {φ : power_series R} :
  (X : power_series R) ∣ φ ↔ constant_coeff R φ = 0 :=
begin
  rw [← pow_one (X : power_series R), X_pow_dvd_iff, ← coeff_zero_eq_constant_coeff_apply],
  split; intro h,
  { exact h 0 zero_lt_one },
  { intros m hm, rwa nat.eq_zero_of_le_zero (nat.le_of_succ_le_succ hm) }
end

end semiring

section comm_semiring
variables [comm_semiring R]

open finset nat

/-- The ring homomorphism taking a power series `f(X)` to `f(aX)`. -/
noncomputable def rescale (a : R) : power_series R →+* power_series R :=
{ to_fun :=  λ f, power_series.mk $ λ n, a^n * (power_series.coeff R n f),
  map_zero' := by { ext, simp only [linear_map.map_zero, power_series.coeff_mk, mul_zero], },
  map_one' := by { ext1, simp only [mul_boole, power_series.coeff_mk, power_series.coeff_one],
                split_ifs, { rw [h, pow_zero], }, refl, },
  map_add' := by { intros, ext, exact mul_add _ _ _, },
  map_mul' := λ f g, by
  { ext,
    rw [power_series.coeff_mul, power_series.coeff_mk, power_series.coeff_mul, finset.mul_sum],
    apply sum_congr rfl,
    simp only [coeff_mk, prod.forall, nat.mem_antidiagonal],
    intros b c H,
    rw [←H, pow_add, mul_mul_mul_comm] }, }

@[simp] lemma coeff_rescale (f : power_series R) (a : R) (n : ℕ) :
  coeff R n (rescale a f) = a^n * coeff R n f := coeff_mk n _

@[simp] lemma rescale_zero : rescale 0 = (C R).comp (constant_coeff R) :=
begin
  ext,
  simp only [function.comp_app, ring_hom.coe_comp, rescale, ring_hom.coe_mk,
    power_series.coeff_mk _ _, coeff_C],
  split_ifs,
  { simp only [h, one_mul, coeff_zero_eq_constant_coeff, pow_zero], },
  { rw [zero_pow' n h, zero_mul], },
end

lemma rescale_zero_apply : rescale 0 X = C R (constant_coeff R X) :=
by simp

@[simp] lemma rescale_one : rescale 1 = ring_hom.id (power_series R) :=
by { ext, simp only [ring_hom.id_apply, rescale, one_pow, coeff_mk, one_mul,
  ring_hom.coe_mk], }

lemma rescale_mk (f : ℕ → R) (a : R) :
  rescale a (mk f) = mk (λ n : ℕ, a^n * (f n)) :=
by { ext, rw [coeff_rescale, coeff_mk, coeff_mk], }

lemma rescale_rescale (f : power_series R) (a b : R) :
  rescale b (rescale a f) = rescale (a * b) f :=
begin
  ext,
  repeat { rw coeff_rescale, },
  rw [mul_pow, mul_comm _ (b^n), mul_assoc],
end

lemma rescale_mul (a b : R) : rescale (a * b) = (rescale b).comp (rescale a) :=
by { ext, simp [← rescale_rescale], }

section trunc

/-- The `n`th truncation of a formal power series to a polynomial -/
def trunc (n : ℕ) (φ : power_series R) : R[X] :=
∑ m in Ico 0 n, polynomial.monomial m (coeff R m φ)

lemma coeff_trunc (m) (n) (φ : power_series R) :
  (trunc n φ).coeff m = if m < n then coeff R m φ else 0 :=
by simp [trunc, polynomial.coeff_sum, polynomial.coeff_monomial, nat.lt_succ_iff]

@[simp] lemma trunc_zero (n) : trunc n (0 : power_series R) = 0 :=
polynomial.ext $ λ m,
begin
  rw [coeff_trunc, linear_map.map_zero, polynomial.coeff_zero],
  split_ifs; refl
end

@[simp] lemma trunc_one (n) : trunc (n + 1) (1 : power_series R) = 1 :=
polynomial.ext $ λ m,
begin
  rw [coeff_trunc, coeff_one],
  split_ifs with H H' H'; rw [polynomial.coeff_one],
  { subst m, rw [if_pos rfl] },
  { symmetry, exact if_neg (ne.elim (ne.symm H')) },
  { symmetry, refine if_neg _,
    rintro rfl, apply H, exact nat.zero_lt_succ _ }
end

@[simp] lemma trunc_C (n) (a : R) : trunc (n + 1) (C R a) = polynomial.C a :=
polynomial.ext $ λ m,
begin
  rw [coeff_trunc, coeff_C, polynomial.coeff_C],
  split_ifs with H; refl <|> try {simp * at *}
end

@[simp] lemma trunc_add (n) (φ ψ : power_series R) :
  trunc n (φ + ψ) = trunc n φ + trunc n ψ :=
polynomial.ext $ λ m,
begin
  simp only [coeff_trunc, add_monoid_hom.map_add, polynomial.coeff_add],
  split_ifs with H, {refl}, {rw [zero_add]}
end

end trunc

end comm_semiring

section ring
variables [ring R]

/-- Auxiliary function used for computing inverse of a power series -/
protected def inv.aux : R → power_series R → power_series R :=
mv_power_series.inv.aux

lemma coeff_inv_aux (n : ℕ) (a : R) (φ : power_series R) :
  coeff R n (inv.aux a φ) = if n = 0 then a else
  - a * ∑ x in finset.nat.antidiagonal n,
    if x.2 < n then coeff R x.1 φ * coeff R x.2 (inv.aux a φ) else 0 :=
begin
  rw [coeff, inv.aux, mv_power_series.coeff_inv_aux],
  simp only [finsupp.single_eq_zero],
  split_ifs, {refl},
  congr' 1,
  symmetry,
  apply finset.sum_bij (λ (p : ℕ × ℕ) h, (single () p.1, single () p.2)),
  { rintros ⟨i,j⟩ hij, rw finset.nat.mem_antidiagonal at hij,
    rw [finsupp.mem_antidiagonal, ← finsupp.single_add, hij], },
  { rintros ⟨i,j⟩ hij,
    by_cases H : j < n,
    { rw [if_pos H, if_pos], {refl},
      split,
      { rintro ⟨⟩, simpa [finsupp.single_eq_same] using le_of_lt H },
      { intro hh, rw lt_iff_not_ge at H, apply H,
        simpa [finsupp.single_eq_same] using hh () } },
    { rw [if_neg H, if_neg], rintro ⟨h₁, h₂⟩, apply h₂, rintro ⟨⟩,
     simpa [finsupp.single_eq_same] using not_lt.1 H } },
  { rintros ⟨i,j⟩ ⟨k,l⟩ hij hkl,
    simpa only [prod.mk.inj_iff, finsupp.unique_single_eq_iff] using id },
  { rintros ⟨f,g⟩ hfg,
    refine ⟨(f (), g ()), _, _⟩,
    { rw finsupp.mem_antidiagonal at hfg,
      rw [finset.nat.mem_antidiagonal, ← finsupp.add_apply, hfg, finsupp.single_eq_same] },
    { rw prod.mk.inj_iff, dsimp,
      exact ⟨finsupp.unique_single f, finsupp.unique_single g⟩ } }
end

/-- A formal power series is invertible if the constant coefficient is invertible.-/
def inv_of_unit (φ : power_series R) (u : Rˣ) : power_series R :=
mv_power_series.inv_of_unit φ u

lemma coeff_inv_of_unit (n : ℕ) (φ : power_series R) (u : Rˣ) :
  coeff R n (inv_of_unit φ u) = if n = 0 then ↑u⁻¹ else
  - ↑u⁻¹ * ∑ x in finset.nat.antidiagonal n,
    if x.2 < n then coeff R x.1 φ * coeff R x.2 (inv_of_unit φ u) else 0 :=
coeff_inv_aux n ↑u⁻¹ φ

@[simp] lemma constant_coeff_inv_of_unit (φ : power_series R) (u : Rˣ) :
  constant_coeff R (inv_of_unit φ u) = ↑u⁻¹ :=
by rw [← coeff_zero_eq_constant_coeff_apply, coeff_inv_of_unit, if_pos rfl]

lemma mul_inv_of_unit (φ : power_series R) (u : Rˣ) (h : constant_coeff R φ = u) :
  φ * inv_of_unit φ u = 1 :=
mv_power_series.mul_inv_of_unit φ u $ h

/-- Two ways of removing the constant coefficient of a power series are the same. -/
lemma sub_const_eq_shift_mul_X (φ : power_series R) :
  φ - C R (constant_coeff R φ) = power_series.mk (λ p, coeff R (p + 1) φ) * X :=
sub_eq_iff_eq_add.mpr (eq_shift_mul_X_add_const φ)

lemma sub_const_eq_X_mul_shift (φ : power_series R) :
  φ - C R (constant_coeff R φ) = X * power_series.mk (λ p, coeff R (p + 1) φ) :=
sub_eq_iff_eq_add.mpr (eq_X_mul_shift_add_const φ)

end ring

section comm_ring
variables {A : Type*} [comm_ring A]

@[simp] lemma rescale_X (a : A) : rescale a X = C A a * X :=
begin
  ext,
  simp only [coeff_rescale, coeff_C_mul, coeff_X],
  split_ifs with h;
  simp [h],
end

lemma rescale_neg_one_X : rescale (-1 : A) X = -X :=
by rw [rescale_X, map_neg, map_one, neg_one_mul]

/-- The ring homomorphism taking a power series `f(X)` to `f(-X)`. -/
noncomputable def eval_neg_hom : power_series A →+* power_series A :=
rescale (-1 : A)

@[simp] lemma eval_neg_hom_X : eval_neg_hom (X : power_series A) = -X :=
rescale_neg_one_X

end comm_ring

section domain
variables [ring R]

lemma eq_zero_or_eq_zero_of_mul_eq_zero [no_zero_divisors R] (φ ψ : power_series R)
  (h : φ * ψ = 0) : φ = 0 ∨ ψ = 0 :=
begin
  rw or_iff_not_imp_left, intro H,
  have ex : ∃ m, coeff R m φ ≠ 0, { contrapose! H, exact ext H },
  let m := nat.find ex,
  have hm₁ : coeff R m φ ≠ 0 := nat.find_spec ex,
  have hm₂ : ∀ k < m, ¬coeff R k φ ≠ 0 := λ k, nat.find_min ex,
  ext n, rw (coeff R n).map_zero, apply nat.strong_induction_on n,
  clear n, intros n ih,
  replace h := congr_arg (coeff R (m + n)) h,
  rw [linear_map.map_zero, coeff_mul, finset.sum_eq_single (m,n)] at h,
  { replace h := eq_zero_or_eq_zero_of_mul_eq_zero h,
    rw or_iff_not_imp_left at h, exact h hm₁ },
  { rintro ⟨i,j⟩ hij hne,
    by_cases hj : j < n, { rw [ih j hj, mul_zero] },
    by_cases hi : i < m,
    { specialize hm₂ _ hi, push_neg at hm₂, rw [hm₂, zero_mul] },
    rw finset.nat.mem_antidiagonal at hij,
    push_neg at hi hj,
    suffices : m < i,
    { have : m + n < i + j := add_lt_add_of_lt_of_le this hj,
      exfalso, exact ne_of_lt this hij.symm },
    contrapose! hne,
    obtain rfl := le_antisymm hi hne,
    simpa [ne.def, prod.mk.inj_iff] using (add_right_inj m).mp hij },
  { contrapose!, intro h, rw finset.nat.mem_antidiagonal }
end

instance [no_zero_divisors R] : no_zero_divisors (power_series R) :=
{ eq_zero_or_eq_zero_of_mul_eq_zero := eq_zero_or_eq_zero_of_mul_eq_zero }

instance [is_domain R] : is_domain (power_series R) :=
no_zero_divisors.to_is_domain _

end domain

section is_domain

variables [comm_ring R] [is_domain R]

/-- The ideal spanned by the variable in the power series ring
 over an integral domain is a prime ideal.-/
lemma span_X_is_prime : (ideal.span ({X} : set (power_series R))).is_prime :=
begin
  suffices : ideal.span ({X} : set (power_series R)) = (constant_coeff R).ker,
  { rw this, exact ring_hom.ker_is_prime _ },
  apply ideal.ext, intro φ,
  rw [ring_hom.mem_ker, ideal.mem_span_singleton, X_dvd_iff]
end

/-- The variable of the power series ring over an integral domain is prime.-/
lemma X_prime : prime (X : power_series R) :=
begin
  rw ← ideal.span_singleton_prime,
  { exact span_X_is_prime },
  { intro h, simpa using congr_arg (coeff R 1) h }
end

lemma rescale_injective {a : R} (ha : a ≠ 0) : function.injective (rescale a) :=
begin
  intros p q h,
  rw power_series.ext_iff at *,
  intros n,
  specialize h n,
  rw [coeff_rescale, coeff_rescale, mul_eq_mul_left_iff] at h,
  apply h.resolve_right,
  intro h',
  exact ha (pow_eq_zero h'),
end

end is_domain

section local_ring
variables {S : Type*} [comm_ring R] [comm_ring S]
  (f : R →+* S) [is_local_ring_hom f]

instance map.is_local_ring_hom : is_local_ring_hom (map f) :=
mv_power_series.map.is_local_ring_hom f

variables [local_ring R] [local_ring S]

instance : local_ring (power_series R) :=
mv_power_series.local_ring

end local_ring

section algebra
variables {A : Type*} [comm_semiring R] [semiring A] [algebra R A]

theorem C_eq_algebra_map {r : R} : C R r = (algebra_map R (power_series R)) r := rfl

theorem algebra_map_apply {r : R} :
  algebra_map R (power_series A) r = C A (algebra_map R A r) :=
mv_power_series.algebra_map_apply

instance [nontrivial R] : nontrivial (subalgebra R (power_series R)) :=
mv_power_series.subalgebra.nontrivial

end algebra

section field
variables {k : Type*} [field k]

/-- The inverse 1/f of a power series f defined over a field -/
protected def inv : power_series k → power_series k :=
mv_power_series.inv

instance : has_inv (power_series k) := ⟨power_series.inv⟩

lemma inv_eq_inv_aux (φ : power_series k) :
  φ⁻¹ = inv.aux (constant_coeff k φ)⁻¹ φ := rfl

lemma coeff_inv (n) (φ : power_series k) :
  coeff k n (φ⁻¹) = if n = 0 then (constant_coeff k φ)⁻¹ else
  - (constant_coeff k φ)⁻¹ * ∑ x in finset.nat.antidiagonal n,
    if x.2 < n then coeff k x.1 φ * coeff k x.2 (φ⁻¹) else 0 :=
by rw [inv_eq_inv_aux, coeff_inv_aux n (constant_coeff k φ)⁻¹ φ]

@[simp] lemma constant_coeff_inv (φ : power_series k) :
  constant_coeff k (φ⁻¹) = (constant_coeff k φ)⁻¹ :=
mv_power_series.constant_coeff_inv φ

lemma inv_eq_zero {φ : power_series k} :
  φ⁻¹ = 0 ↔ constant_coeff k φ = 0 :=
mv_power_series.inv_eq_zero

@[simp] lemma zero_inv : (0 : power_series k)⁻¹ = 0 := mv_power_series.zero_inv

@[simp, priority 1100] lemma inv_of_unit_eq (φ : power_series k) (h : constant_coeff k φ ≠ 0) :
  inv_of_unit φ (units.mk0 _ h) = φ⁻¹ :=
mv_power_series.inv_of_unit_eq _ _

@[simp] lemma inv_of_unit_eq' (φ : power_series k) (u : units k) (h : constant_coeff k φ = u) :
  inv_of_unit φ u = φ⁻¹ :=
mv_power_series.inv_of_unit_eq' φ _ h

@[simp] protected lemma mul_inv_cancel (φ : power_series k) (h : constant_coeff k φ ≠ 0) :
  φ * φ⁻¹ = 1 :=
mv_power_series.mul_inv_cancel φ h

@[simp] protected lemma inv_mul_cancel (φ : power_series k) (h : constant_coeff k φ ≠ 0) :
  φ⁻¹ * φ = 1 :=
mv_power_series.inv_mul_cancel φ h

lemma eq_mul_inv_iff_mul_eq {φ₁ φ₂ φ₃ : power_series k} (h : constant_coeff k φ₃ ≠ 0) :
  φ₁ = φ₂ * φ₃⁻¹ ↔ φ₁ * φ₃ = φ₂ :=
mv_power_series.eq_mul_inv_iff_mul_eq h

lemma eq_inv_iff_mul_eq_one {φ ψ : power_series k} (h : constant_coeff k ψ ≠ 0) :
  φ = ψ⁻¹ ↔ φ * ψ = 1 :=
mv_power_series.eq_inv_iff_mul_eq_one h

lemma inv_eq_iff_mul_eq_one {φ ψ : power_series k} (h : constant_coeff k ψ ≠ 0) :
  ψ⁻¹ = φ ↔ φ * ψ = 1 :=
mv_power_series.inv_eq_iff_mul_eq_one h

@[simp] protected lemma mul_inv_rev (φ ψ : power_series k) :
  (φ * ψ)⁻¹ = ψ⁻¹ * φ⁻¹ :=
mv_power_series.mul_inv_rev _ _

instance : inv_one_class (power_series k) := mv_power_series.inv_one_class

@[simp] lemma C_inv (r : k) : (C k r)⁻¹ = C k r⁻¹ :=
mv_power_series.C_inv _

@[simp] lemma X_inv : (X : power_series k)⁻¹ = 0 :=
mv_power_series.X_inv _

@[simp] lemma smul_inv (r : k) (φ : power_series k) :
  (r • φ)⁻¹ = r⁻¹ • φ⁻¹ :=
mv_power_series.smul_inv _ _

end field

end power_series

namespace power_series
variable {R : Type*}

local attribute [instance, priority 1] classical.prop_decidable
noncomputable theory

section order_basic
open multiplicity
variables [semiring R] {φ : power_series R}

lemma exists_coeff_ne_zero_iff_ne_zero : (∃ (n : ℕ), coeff R n φ ≠ 0) ↔ φ ≠ 0 :=
begin
  refine not_iff_not.mp _,
  push_neg,
  simp [power_series.ext_iff]
end

/-- The order of a formal power series `φ` is the greatest `n : part_enat`
such that `X^n` divides `φ`. The order is `⊤` if and only if `φ = 0`. -/
def order (φ : power_series R) : part_enat :=
if h : φ = 0 then ⊤ else nat.find (exists_coeff_ne_zero_iff_ne_zero.mpr h)

/-- The order of the `0` power series is infinite.-/
@[simp] lemma order_zero : order (0 : power_series R) = ⊤ := dif_pos rfl

lemma order_finite_iff_ne_zero : (order φ).dom ↔ φ ≠ 0 :=
begin
  simp only [order],
  split,
  { split_ifs with h h; intro H,
    { contrapose! H,
      simpa [←part.eq_none_iff'] },
    { exact h } },
  { intro h,
    simp [h] }
end

/-- If the order of a formal power series is finite,
then the coefficient indexed by the order is nonzero.-/
lemma coeff_order (h : (order φ).dom) :
  coeff R (φ.order.get h) φ ≠ 0 :=
begin
  simp only [order, order_finite_iff_ne_zero.mp h, not_false_iff, dif_neg, part_enat.get_coe'],
  generalize_proofs h,
  exact nat.find_spec h
end

/-- If the `n`th coefficient of a formal power series is nonzero,
then the order of the power series is less than or equal to `n`.-/
lemma order_le (n : ℕ) (h : coeff R n φ ≠ 0) :
  order φ ≤ n :=
begin
  have := exists.intro n h,
  rw [order, dif_neg],
  { simp only [part_enat.coe_le_coe, nat.find_le_iff],
    exact ⟨n, le_rfl, h⟩ },
  { exact exists_coeff_ne_zero_iff_ne_zero.mp ⟨n, h⟩ }
end

/-- The `n`th coefficient of a formal power series is `0` if `n` is strictly
smaller than the order of the power series.-/
lemma coeff_of_lt_order (n : ℕ) (h: ↑n < order φ) :
  coeff R n φ = 0 :=
by { contrapose! h, exact order_le _ h }

/-- The `0` power series is the unique power series with infinite order.-/
@[simp] lemma order_eq_top {φ : power_series R} :
  φ.order = ⊤ ↔ φ = 0 :=
begin
  split,
  { intro h, ext n, rw [(coeff R n).map_zero, coeff_of_lt_order], simp [h] },
  { rintros rfl, exact order_zero }
end

/-- The order of a formal power series is at least `n` if
the `i`th coefficient is `0` for all `i < n`.-/
lemma nat_le_order (φ : power_series R) (n : ℕ) (h : ∀ i < n, coeff R i φ = 0) :
  ↑n ≤ order φ :=
begin
  by_contra H, rw not_le at H,
  have : (order φ).dom := part_enat.dom_of_le_coe H.le,
  rw [← part_enat.coe_get this, part_enat.coe_lt_coe] at H,
  exact coeff_order this (h _ H)
end

/-- The order of a formal power series is at least `n` if
the `i`th coefficient is `0` for all `i < n`.-/
lemma le_order (φ : power_series R) (n : part_enat) (h : ∀ i : ℕ, ↑i < n → coeff R i φ = 0) :
  n ≤ order φ :=
begin
  induction n using part_enat.cases_on,
  { show _ ≤ _, rw [top_le_iff, order_eq_top],
    ext i, exact h _ (part_enat.coe_lt_top i) },
  { apply nat_le_order, simpa only [part_enat.coe_lt_coe] using h }
end

/-- The order of a formal power series is exactly `n` if the `n`th coefficient is nonzero,
and the `i`th coefficient is `0` for all `i < n`.-/
lemma order_eq_nat {φ : power_series R} {n : ℕ} :
  order φ = n ↔ (coeff R n φ ≠ 0) ∧ (∀ i, i < n → coeff R i φ = 0) :=
begin
  rcases eq_or_ne φ 0 with rfl|hφ,
  { simpa using (part_enat.coe_ne_top _).symm },
  simp [order, dif_neg hφ, nat.find_eq_iff]
end

/-- The order of a formal power series is exactly `n` if the `n`th coefficient is nonzero,
and the `i`th coefficient is `0` for all `i < n`.-/
lemma order_eq {φ : power_series R} {n : part_enat} :
  order φ = n ↔ (∀ i:ℕ, ↑i = n → coeff R i φ ≠ 0) ∧ (∀ i:ℕ, ↑i < n → coeff R i φ = 0) :=
begin
  induction n using part_enat.cases_on,
  { rw order_eq_top, split,
    { rintro rfl, split; intros,
      { exfalso, exact part_enat.coe_ne_top ‹_› ‹_› },
      { exact (coeff _ _).map_zero } },
    { rintro ⟨h₁, h₂⟩, ext i, exact h₂ i (part_enat.coe_lt_top i) } },
  { simpa [part_enat.coe_inj] using order_eq_nat }
end

/-- The order of the sum of two formal power series
 is at least the minimum of their orders.-/
lemma le_order_add (φ ψ : power_series R) :
  min (order φ) (order ψ) ≤ order (φ + ψ) :=
begin
  refine le_order _ _ _,
  simp [coeff_of_lt_order] {contextual := tt}
end

private lemma order_add_of_order_eq.aux (φ ψ : power_series R)
  (h : order φ ≠ order ψ) (H : order φ < order ψ) :
  order (φ + ψ) ≤ order φ ⊓ order ψ :=
begin
  suffices : order (φ + ψ) = order φ,
  { rw [le_inf_iff, this], exact ⟨le_rfl, le_of_lt H⟩ },
  { rw order_eq, split,
    { intros i hi, rw ←hi at H, rw [(coeff _ _).map_add, coeff_of_lt_order i H, add_zero],
      exact (order_eq_nat.1 hi.symm).1 },
    { intros i hi,
      rw [(coeff _ _).map_add, coeff_of_lt_order i hi,
        coeff_of_lt_order i (lt_trans hi H), zero_add] } }
end

/-- The order of the sum of two formal power series
 is the minimum of their orders if their orders differ.-/
lemma order_add_of_order_eq (φ ψ : power_series R) (h : order φ ≠ order ψ) :
  order (φ + ψ) = order φ ⊓ order ψ :=
begin
  refine le_antisymm _ (le_order_add _ _),
  by_cases H₁ : order φ < order ψ,
  { apply order_add_of_order_eq.aux _ _ h H₁ },
  by_cases H₂ : order ψ < order φ,
  { simpa only [add_comm, inf_comm] using order_add_of_order_eq.aux _ _ h.symm H₂ },
  exfalso, exact h (le_antisymm (not_lt.1 H₂) (not_lt.1 H₁))
end

/-- The order of the product of two formal power series
 is at least the sum of their orders.-/
lemma order_mul_ge (φ ψ : power_series R) :
  order φ + order ψ ≤ order (φ * ψ) :=
begin
  apply le_order,
  intros n hn, rw [coeff_mul, finset.sum_eq_zero],
  rintros ⟨i,j⟩ hij,
  by_cases hi : ↑i < order φ,
  { rw [coeff_of_lt_order i hi, zero_mul] },
  by_cases hj : ↑j < order ψ,
  { rw [coeff_of_lt_order j hj, mul_zero] },
  rw not_lt at hi hj, rw finset.nat.mem_antidiagonal at hij,
  exfalso,
  apply ne_of_lt (lt_of_lt_of_le hn $ add_le_add hi hj),
  rw [← nat.cast_add, hij]
end

/-- The order of the monomial `a*X^n` is infinite if `a = 0` and `n` otherwise.-/
lemma order_monomial (n : ℕ) (a : R) [decidable (a = 0)] :
  order (monomial R n a) = if a = 0 then ⊤ else n :=
begin
  split_ifs with h,
  { rw [h, order_eq_top, linear_map.map_zero] },
  { rw [order_eq], split; intros i hi,
    { rw [part_enat.coe_inj] at hi, rwa [hi, coeff_monomial_same] },
    { rw [part_enat.coe_lt_coe] at hi, rw [coeff_monomial, if_neg], exact ne_of_lt hi } }
end

/-- The order of the monomial `a*X^n` is `n` if `a ≠ 0`.-/
lemma order_monomial_of_ne_zero (n : ℕ) (a : R) (h : a ≠ 0) :
  order (monomial R n a) = n :=
by rw [order_monomial, if_neg h]

/-- If `n` is strictly smaller than the order of `ψ`, then the `n`th coefficient of its product
with any other power series is `0`. -/
lemma coeff_mul_of_lt_order {φ ψ : power_series R} {n : ℕ} (h : ↑n < ψ.order) :
  coeff R n (φ * ψ) = 0 :=
begin
  suffices : coeff R n (φ * ψ) = ∑ p in finset.nat.antidiagonal n, 0,
    rw [this, finset.sum_const_zero],
  rw [coeff_mul],
  apply finset.sum_congr rfl (λ x hx, _),
  refine mul_eq_zero_of_right (coeff R x.fst φ) (coeff_of_lt_order x.snd (lt_of_le_of_lt _ h)),
  rw finset.nat.mem_antidiagonal at hx,
  norm_cast,
  linarith,
end

lemma coeff_mul_one_sub_of_lt_order {R : Type*} [comm_ring R] {φ ψ : power_series R}
  (n : ℕ) (h : ↑n < ψ.order) :
  coeff R n (φ * (1 - ψ)) = coeff R n φ :=
by simp [coeff_mul_of_lt_order h, mul_sub]

lemma coeff_mul_prod_one_sub_of_lt_order {R ι : Type*} [comm_ring R] (k : ℕ) (s : finset ι)
  (φ : power_series R) (f : ι → power_series R) :
  (∀ i ∈ s, ↑k < (f i).order) → coeff R k (φ * ∏ i in s, (1 - f i)) = coeff R k φ :=
begin
  apply finset.induction_on s,
  { simp },
  { intros a s ha ih t,
    simp only [finset.mem_insert, forall_eq_or_imp] at t,
    rw [finset.prod_insert ha, ← mul_assoc, mul_right_comm, coeff_mul_one_sub_of_lt_order _ t.1],
    exact ih t.2 },
end

-- TODO: link with `X_pow_dvd_iff`
lemma X_pow_order_dvd (h : (order φ).dom) : X ^ ((order φ).get h) ∣ φ :=
begin
  refine ⟨power_series.mk (λ n, coeff R (n + (order φ).get h) φ), _⟩,
  ext n,
  simp only [coeff_mul, coeff_X_pow, coeff_mk, boole_mul, finset.sum_ite,
             finset.nat.filter_fst_eq_antidiagonal, finset.sum_const_zero, add_zero],
  split_ifs with hn hn,
  { simp [tsub_add_cancel_of_le hn] },
  { simp only [finset.sum_empty],
    refine coeff_of_lt_order _ _,
    simpa [part_enat.coe_lt_iff] using λ _, hn }
end

lemma order_eq_multiplicity_X {R : Type*} [semiring R] (φ : power_series R) :
  order φ = multiplicity X φ :=
begin
  rcases eq_or_ne φ 0 with rfl|hφ,
  { simp },
  induction ho : order φ using part_enat.cases_on with n,
  { simpa [hφ] using ho },
  have hn : φ.order.get (order_finite_iff_ne_zero.mpr hφ) = n,
  { simp [ho] },
  rw ←hn,
  refine le_antisymm (le_multiplicity_of_pow_dvd $ X_pow_order_dvd
    (order_finite_iff_ne_zero.mpr hφ)) (part_enat.find_le _ _ _),
  rintro ⟨ψ, H⟩,
  have := congr_arg (coeff R n) H,
  rw [← (ψ.commute_X.pow_right _).eq, coeff_mul_of_lt_order, ←hn] at this,
  { exact coeff_order _ this },
  { rw [X_pow_eq, order_monomial],
    split_ifs,
    { exact part_enat.coe_lt_top _ },
    { rw [←hn, part_enat.coe_lt_coe],
      exact nat.lt_succ_self _ } }
end

end order_basic

section order_zero_ne_one
variables [semiring R] [nontrivial R]

/-- The order of the formal power series `1` is `0`.-/
@[simp] lemma order_one : order (1 : power_series R) = 0 :=
by simpa using order_monomial_of_ne_zero 0 (1:R) one_ne_zero

/-- The order of the formal power series `X` is `1`.-/
@[simp] lemma order_X : order (X : power_series R) = 1 :=
by simpa only [nat.cast_one] using order_monomial_of_ne_zero 1 (1:R) one_ne_zero

/-- The order of the formal power series `X^n` is `n`.-/
@[simp] lemma order_X_pow (n : ℕ) : order ((X : power_series R)^n) = n :=
by { rw [X_pow_eq, order_monomial_of_ne_zero], exact one_ne_zero }

end order_zero_ne_one

section order_is_domain
-- TODO: generalize to `[semiring R] [no_zero_divisors R]`
variables [comm_ring R] [is_domain R]

/-- The order of the product of two formal power series over an integral domain
 is the sum of their orders.-/
lemma order_mul (φ ψ : power_series R) :
  order (φ * ψ) = order φ + order ψ :=
begin
  simp_rw [order_eq_multiplicity_X],
  exact multiplicity.mul X_prime
end

end order_is_domain

end power_series

namespace polynomial
open finsupp
variables {σ : Type*} {R : Type*} [comm_semiring R] (φ ψ : R[X])

/-- The natural inclusion from polynomials into formal power series.-/
instance coe_to_power_series : has_coe R[X] (power_series R) :=
⟨λ φ, power_series.mk $ λ n, coeff φ n⟩

lemma coe_def : (φ : power_series R) = power_series.mk (coeff φ) := rfl

@[simp, norm_cast] lemma coeff_coe (n) :
  power_series.coeff R n φ = coeff φ n :=
congr_arg (coeff φ) (finsupp.single_eq_same)

@[simp, norm_cast] lemma coe_monomial (n : ℕ) (a : R) :
  (monomial n a : power_series R) = power_series.monomial R n a :=
by { ext, simp [coeff_coe, power_series.coeff_monomial, polynomial.coeff_monomial, eq_comm] }

@[simp, norm_cast] lemma coe_zero : ((0 : R[X]) : power_series R) = 0 := rfl

@[simp, norm_cast] lemma coe_one : ((1 : R[X]) : power_series R) = 1 :=
begin
  have := coe_monomial 0 (1:R),
  rwa power_series.monomial_zero_eq_C_apply at this,
end

@[simp, norm_cast] lemma coe_add :
  ((φ + ψ : R[X]) : power_series R) = φ + ψ :=
by { ext, simp }

@[simp, norm_cast] lemma coe_mul :
  ((φ * ψ : R[X]) : power_series R) = φ * ψ :=
power_series.ext $ λ n,
by simp only [coeff_coe, power_series.coeff_mul, coeff_mul]

@[simp, norm_cast] lemma coe_C (a : R) :
  ((C a : R[X]) : power_series R) = power_series.C R a :=
begin
  have := coe_monomial 0 a,
  rwa power_series.monomial_zero_eq_C_apply at this,
end

@[simp, norm_cast] lemma coe_bit0 :
  ((bit0 φ : R[X]) : power_series R) = bit0 (φ : power_series R) :=
coe_add φ φ

@[simp, norm_cast] lemma coe_bit1 :
  ((bit1 φ : R[X]) : power_series R) = bit1 (φ : power_series R) :=
by rw [bit1, bit1, coe_add, coe_one, coe_bit0]

@[simp, norm_cast] lemma coe_X :
  ((X : R[X]) : power_series R) = power_series.X :=
coe_monomial _ _

@[simp] lemma constant_coeff_coe : power_series.constant_coeff R φ = φ.coeff 0 := rfl

variables (R)

lemma coe_injective : function.injective (coe : R[X] → power_series R) :=
λ x y h, by { ext, simp_rw [←coeff_coe, h] }

variables {R φ ψ}

@[simp, norm_cast] lemma coe_inj : (φ : power_series R) = ψ ↔ φ = ψ :=
(coe_injective R).eq_iff

@[simp] lemma coe_eq_zero_iff : (φ : power_series R) = 0 ↔ φ = 0 :=
by rw [←coe_zero, coe_inj]

@[simp] lemma coe_eq_one_iff : (φ : power_series R) = 1 ↔ φ = 1 :=
by rw [←coe_one, coe_inj]

variables (φ ψ)

/--
The coercion from polynomials to power series
as a ring homomorphism.
-/
def coe_to_power_series.ring_hom : R[X] →+* power_series R :=
{ to_fun := (coe : R[X] → power_series R),
  map_zero' := coe_zero,
  map_one' := coe_one,
  map_add' := coe_add,
  map_mul' := coe_mul }

@[simp] lemma coe_to_power_series.ring_hom_apply : coe_to_power_series.ring_hom φ = φ := rfl

@[simp, norm_cast] lemma coe_pow (n : ℕ):
  ((φ ^ n : R[X]) : power_series R) = (φ : power_series R) ^ n :=
coe_to_power_series.ring_hom.map_pow _ _

variables (A : Type*) [semiring A] [algebra R A]

/--
The coercion from polynomials to power series
as an algebra homomorphism.
-/
def coe_to_power_series.alg_hom : R[X] →ₐ[R] power_series A :=
{ commutes' := λ r, by simp [algebra_map_apply, power_series.algebra_map_apply],
  ..(power_series.map (algebra_map R A)).comp coe_to_power_series.ring_hom }

@[simp] lemma coe_to_power_series.alg_hom_apply : (coe_to_power_series.alg_hom A φ) =
  power_series.map (algebra_map R A) ↑φ := rfl

end polynomial

namespace power_series

variables {R A : Type*} [comm_semiring R] [comm_semiring A] [algebra R A] (f : power_series R)

instance algebra_polynomial : algebra R[X] (power_series A) :=
ring_hom.to_algebra (polynomial.coe_to_power_series.alg_hom A).to_ring_hom

instance algebra_power_series : algebra (power_series R) (power_series A) :=
(map (algebra_map R A)).to_algebra

@[priority 100] -- see Note [lower instance priority]
instance algebra_polynomial' {A : Type*} [comm_semiring A] [algebra R A[X]] :
  algebra R (power_series A) :=
ring_hom.to_algebra $ polynomial.coe_to_power_series.ring_hom.comp (algebra_map R A[X])

variables (A)

lemma algebra_map_apply' (p : R[X]) :
  algebra_map R[X] (power_series A) p = map (algebra_map R A) p := rfl

lemma algebra_map_apply'' :
  algebra_map (power_series R) (power_series A) f = map (algebra_map R A) f := rfl

end power_series


import data.vector

@[pattern] def vector.mk {α : Type*} {n : ℕ} (l : list α) (pr : l.length = n) :
  vector α n := ⟨l, pr⟩

notation `![` l:(foldr `, ` (h t, list.cons h t) list.nil `]`) :=
  vector.mk l rfl

def {u} fast_matrix (m n : ℕ) (α : Type u) : Type u := vector (vector α n) m

example : fast_matrix 2 3 ℤ :=
![![ 1 , 1,  5 ],
  ![ 0 , 1, -2 ]]

example : fast_matrix 2 2 ℤ → ℤ |
![![a,b],
  ![c,d]] := a + b + c + d


import ring_theory.polynomial.cyclotomic.basic
import ring_theory.polynomial.cyclotomic.eval
import algebra
import data.finset
import tactic

variables {n : ℕ}

noncomputable theory

lemma nat_ne_zero_of_pos {n : ℕ} : 0 < n → n ≠ 0 :=
begin
  intros h1 h2,
  subst h2,
  simp at h1,
  exact h1,
end

lemma unit_of_coprime_of_val [ne_zero n] (a : (zmod n)ˣ) :
zmod.unit_of_coprime (a : zmod n).val (zmod.val_coe_unit_coprime _) = a :=
begin
  rw ← units.eq_iff,
  simp,
end

lemma polynomial_dvd_of_mul_dvd_mul {R : Type} [field R] {p q r : polynomial R}
(hr : r ≠ 0) : p * r ∣ q * r → p ∣ q :=
begin
  intro hdvd,
  cases hdvd with t hdvd,
  use t,
  have h_qr_eq_ptr : q * r = p * t * r := by simp [hdvd, mul_comm, mul_assoc],
  rw mul_eq_mul_right_iff at h_qr_eq_ptr,
  cases h_qr_eq_ptr,
  { exact h_qr_eq_ptr, },
  {
    exfalso,
    exact hr h_qr_eq_ptr,
  }
end

theorem cyclotomic_dvd_X_pow_sub_one_frac (R : Type) [comm_ring R] [is_domain R]
{k : ℕ} (hdvd : k ∣ n) (hposn : n ≠ 0) (hne : k ≠ n) :
polynomial.cyclotomic n R * (polynomial.X ^ k - 1) ∣ polynomial.X ^ n - 1 :=
begin
  cases em (k = 0) with h_k_zero hposk,
  {
    subst h_k_zero,
    rw zero_dvd_iff at hdvd,
    exfalso,
    exact hposn hdvd,
  },
  rw [← polynomial.prod_cyclotomic_eq_X_pow_sub_one (nat.pos_of_ne_zero hposk),
  ← polynomial.prod_cyclotomic_eq_X_pow_sub_one (nat.pos_of_ne_zero hposn)],
  have h_subset : k.divisors ⊆ n.divisors,
  { exact nat.divisors_subset_of_dvd hposn hdvd, },
  have h_in_n_divisors : n ∈ n.divisors,
  { simp [hposn], },
  have h_not_in_k_divisors : n ∉ k.divisors,
  {
    intro h_in_divisors,
    apply hne,
    apply le_antisymm,
    { exact nat.le_of_dvd (nat.pos_of_ne_zero hposn) hdvd, },
    { exact nat.divisor_le h_in_divisors, }
  },
  use (n.divisors.erase n \ k.divisors).prod (λ (i : ℕ), polynomial.cyclotomic i R),
  rw [mul_assoc, ← finset.prod_union, finset.union_comm,
  finset.sdiff_union_of_subset, mul_comm, finset.prod_erase_mul],
  exact h_in_n_divisors,
  {
    rw finset.subset_erase,
    split,
    { exact h_subset, },
    { exact h_not_in_k_divisors }
  },
  {
    rw disjoint.comm,
    exact finset.sdiff_disjoint,
  }
end

theorem prime_dvd_cyclotomic (hpos : 0 < n)
{p : ℕ} (hprime : fact (nat.prime p)) {a : (zmod p)ˣ}
(hroot : (polynomial.cyclotomic n (zmod p)).is_root a) :
∃ (t : ℕ), n = order_of a * p ^ t :=
begin
  let a' : ℕ := (a : zmod p).val,
  have hroot' : (polynomial.cyclotomic n (zmod p)).is_root a',
  {
    rw zmod.nat_cast_zmod_val,
    exact hroot,
  },
  have hdvd : ∃ (k : ℕ), n = order_of a * k,
  {
    cases polynomial.order_of_root_cyclotomic_dvd hpos hroot' with l hdvd,
    use l,
    rw hdvd,
    simp,
    left,
    rw unit_of_coprime_of_val,
  },
  cases hdvd with k,
  rw hdvd_h,
  use k.factors.length,
  simp,
  left,
  apply nat.eq_prime_pow_of_unique_prime_dvd,
  {
    cases k,
    {
      rw hdvd_h at hpos,
      simp at hpos,
      exfalso,
      exact hpos,
    },
    { simp, }
  },
  {
    intros q h_q_prime hdvd,
    have hdvd_qn : q ∣ n,
    {
      cases hdvd with l h,
      use order_of a * l,
      simp [h, hdvd_h],
      linarith,
    },
    cases hdvd_qn with m h_qm_eq_n,
    by_contra,
    have h_ndvd :
    ¬ ((finset.range q).sum (λ (i : ℕ), (polynomial.X ^ m) ^ i)).is_root a.val,
    {
      simp,
      intro h_root_of_sum,
      rw polynomial.eval_finset_sum at h_root_of_sum,
      simp at h_root_of_sum,
      have h_dvd_order : ∀ (i : ℕ), order_of a ∣ m * i,
      {
        cases hdvd with t hdvd_t,
        intro i,
        use t * i,
        subst hdvd_h,
        subst hdvd_t,
        rw [mul_comm, mul_assoc, mul_eq_mul_left_iff] at h_qm_eq_n,
        cases h_qm_eq_n,
        {
          rw [← h_qm_eq_n, ← mul_assoc],
          nth_rewrite 3  mul_comm,
        },
        {
          exfalso,
          exact nat.prime.ne_zero h_q_prime h_qm_eq_n,
        }
      },

      repeat { simp_rw ← units.coe_pow _ _ at h_root_of_sum, },
      simp_rw ← pow_mul at h_root_of_sum,
      simp_rw order_of_dvd_iff_pow_eq_one at h_dvd_order,
      simp_rw h_dvd_order at h_root_of_sum,
      rw finset.sum_const at h_root_of_sum,
      rw finset.card_range at h_root_of_sum,
      dsimp at h_root_of_sum,
      simp at h_root_of_sum,
      rw [zmod.nat_coe_zmod_eq_zero_iff_dvd,
      nat.prime.dvd_iff_eq h_q_prime (nat.prime.ne_one hprime.out)] at h_root_of_sum,
      exact h h_root_of_sum,
    },
    apply h_ndvd,
    have h_dvd_cyclotomic : polynomial.cyclotomic n (zmod p) ∣
    (finset.range q).sum (λ (i : ℕ), (polynomial.X ^ m) ^ i),
    {
      have h_m_pos : 0 < m,
      {
        apply nat.pos_of_ne_zero,
        intro h_m_is_zero,
        rw h_m_is_zero at h_qm_eq_n,
        subst h_qm_eq_n,
        simp at hpos,
        exact hpos,
      },
      apply polynomial_dvd_of_mul_dvd_mul (polynomial.X_pow_sub_C_ne_zero h_m_pos (1 : zmod p)),
      rw mul_comm at h_qm_eq_n,
      rw [polynomial.C_1, geom_sum_mul _ q, ← pow_mul, ← h_qm_eq_n],
      have hdvd_mn : m ∣ n,
      {
        use q,
        exact h_qm_eq_n,
      },
      have h_ne_mn : m ≠ n,
      {
        intro h_eq_mn,
        rw h_eq_mn at h_qm_eq_n,
        apply nat.prime.ne_one h_q_prime,
        rw ← nat.mul_right_eq_self_iff hpos,
        symmetry,
        exact h_qm_eq_n,
      },
      exact cyclotomic_dvd_X_pow_sub_one_frac _ hdvd_mn (nat_ne_zero_of_pos hpos) h_ne_mn,
    },
    cases h_dvd_cyclotomic with P h_dvd_polynoms,
    rw h_dvd_polynoms,
    simp at hroot ⊢,
    left,
    exact hroot,
  }
end

lemma cyclotomic_eval_int_eq_eval_real {a : ℤ} :
↑(polynomial.eval (a : ℤ) (polynomial.cyclotomic n ℤ)) =
        polynomial.eval (a : ℝ) (polynomial.cyclotomic n ℝ) :=
begin
  simp only [← polynomial.map_cyclotomic_int n ℝ, polynomial.eval_int_cast_map,
          int.cast_id, eq_int_cast],
end

theorem cyclotomic_le_X_add_one_pow {a : ℤ} (hposn : 0 < n) (ha : 1 < a) :
polynomial.eval a (polynomial.cyclotomic n ℤ) ≤ (a + 1) ^ (n.totient) :=
begin
  cases em (n = 1) with n_is_one n_ne_one,
  {
    unfreezingI { subst n_is_one, },
    simp only [polynomial.cyclotomic_one, polynomial.eval_sub, polynomial.eval_X,
    polynomial.eval_one, nat.totient_one, pow_one, tsub_le_iff_right],
    linarith,
  },
  {
    cases em (n = 2) with n_is_two n_ne_two,
    {
      unfreezingI { subst n_is_two, },
      simp only [polynomial.cyclotomic_two, polynomial.eval_add, polynomial.eval_X,
      polynomial.eval_one, nat.totient_two, pow_one],
    },
    {
      have hn : 3 ≤ n,
      {
        rw nat.add_one_le_iff,
        exact nat.two_lt_of_ne (nat_ne_zero_of_pos hposn) n_ne_one n_ne_two,
      },
      have ha' :  1 < (a : ℝ),
      {
        rw [← int.cast_one, int.cast_lt],
        exact ha,
      },
      have h_le_in_R : ↑(polynomial.eval (a : ℤ) (polynomial.cyclotomic n ℤ)) ≤
      ((a + 1) ^ (n.totient) : ℝ),
      {
        rw cyclotomic_eval_int_eq_eval_real,
        apply le_of_lt,
        exact polynomial.cyclotomic_eval_lt_sub_one_pow_totient hn ha',
      },
      rw [← int.cast_one, ← int.cast_add, ← int.cast_pow, int.cast_le] at h_le_in_R,
      exact h_le_in_R,
    }
  }
end

theorem X_sub_one_pow_lt_cyclotomic {a : ℤ} (hposn : 1 < n) (ha : 1 < a) :
(a - 1) ^ (n.totient) < polynomial.eval a (polynomial.cyclotomic n ℤ) :=
begin
  cases em (n = 1) with n_is_one n_ne_one,
  {
    unfreezingI { subst n_is_one, },
    linarith,
  },
  {
    cases em (n = 2) with n_is_two n_ne_two,
    {
      unfreezingI { subst n_is_two, },
      simp only [polynomial.cyclotomic_two, polynomial.eval_add, polynomial.eval_X,
      polynomial.eval_one, nat.totient_two, pow_one],
      linarith,
    },
    {
      have hn : 2 ≤ n,
      {
        rw [nat.add_one_le_iff, nat.one_lt_iff_ne_zero_and_ne_one],
        split,
        {
          intro h,
          subst h,
          linarith,
        },
        exact n_ne_one,
      },
      have ha' :  1 < (a : ℝ),
      {
        rw [← int.cast_one, int.cast_lt],
        exact ha,
      },
      have h_le_in_R : ((a - 1) ^ (n.totient) : ℝ) <
      ↑(polynomial.eval (a : ℤ) (polynomial.cyclotomic n ℤ)),
      {
        rw cyclotomic_eval_int_eq_eval_real,
        exact polynomial.sub_one_pow_totient_lt_cyclotomic_eval hn ha',
      },
      rw [← int.cast_one, ← int.cast_sub, ← int.cast_pow, int.cast_lt] at h_le_in_R,
      exact h_le_in_R,
    }
  }
end

theorem cyclotomic_expand_pow_eq_cyclotomic_mul
{p t m : ℕ} (hprime : nat.prime p)
(hpost : 0 < t) (h_not_dvd : ¬p ∣ m) :
(polynomial.expand ℤ (p ^ t)) (polynomial.cyclotomic m ℤ) =
(polynomial.cyclotomic (p ^ t * m) ℤ) *
(polynomial.expand ℤ (p ^ (t - 1)) (polynomial.cyclotomic m ℤ)) :=
begin
  induction t with t ht,
  {
    exfalso,
    exact nat.not_lt_zero 0 hpost,
  },
  {
    cases em (t = 0) with h_t_eq_zero h_t_ne_zero,
    {
      subst h_t_eq_zero,
      simp only [pow_one, pow_zero, polynomial.expand_one, mul_comm p m,
      polynomial.cyclotomic_expand_eq_cyclotomic_mul hprime h_not_dvd],
    },
    {
      have h_pow : p * p ^ (t - 1) = p ^ t,
      {
        rw [← ne.def, ← nat.one_le_iff_ne_zero] at h_t_ne_zero,
        rw [← nat.sub_add_cancel h_t_ne_zero, pow_add, mul_comm],
        simp only [nat.add_succ_sub_one, add_zero, pow_one],
      },
      simp only [nat.succ_eq_add_one, nat.succ_sub_succ_eq_sub, tsub_zero, pow_add, pow_one],
      have h_expand : (polynomial.expand ℤ (p ^ t)) (polynomial.cyclotomic m ℤ) =
      (polynomial.expand ℤ p) ((polynomial.expand ℤ (p ^ (t - 1))) (polynomial.cyclotomic m ℤ)),
      { rw [← polynomial.expand_mul, h_pow], },
      rw [mul_comm, polynomial.expand_mul, mul_comm p _, mul_assoc, mul_comm p _,
      ← mul_assoc, ← polynomial.cyclotomic_expand_eq_cyclotomic hprime, h_expand,
      ← alg_hom.map_mul, polynomial.expand_inj (nat.prime.pos hprime), ← h_expand],
      exact ht (nat.pos_of_ne_zero h_t_ne_zero),
      {
        use p ^ (t - 1) * m,
        rw [← mul_assoc, h_pow],
      }
    }
  }
end

theorem order_of_eq_iff_is_root_of_cyclotomic {a p : ℕ} (h_prime : fact p.prime)
(h_ndvd : ¬ p ∣ n) (h_pos : 0 < n) (h_a : 1 ≠ a) :
↑p ∣ (polynomial.cyclotomic n ℤ).eval ↑a → ∃ (h_coprime : a.coprime p),
order_of(zmod.unit_of_coprime a h_coprime) = n :=
begin
  intro hdvd,
  have h_coprime: a.coprime p,
  {
    rw ← zmod.int_coe_zmod_eq_zero_iff_dvd at hdvd,
    apply polynomial.coprime_of_root_cyclotomic h_pos,
    rw [polynomial.is_root.def, ← polynomial.map_cyclotomic_int, eq_nat_cast,
    polynomial.eval_nat_cast_map, eq_int_cast],
    exact hdvd,
  },
  use h_coprime,
  have h_one_le : ∀ (k : ℕ), 1 ≤ a ^ k,
  {
    intro k,
    apply nat.one_le_pow,
    apply nat.pos_of_ne_zero,
    intro h,
    subst h,
    rw nat.coprime_zero_left at h_coprime,
    exact nat.prime.ne_one h_prime.out h_coprime,
  },
  have h_one_ne : ∀ (k : ℕ) (h_ne_zero : k ≠ 0), 1 ≠ a ^ k,
  {
    intros k h_ne_zero h,
    apply h_a,
    cases k,
    { exfalso, apply h_ne_zero, refl, },
    rw [nat.succ_eq_add_one, pow_add] at h,
    symmetry' at h,
    rw [nat.mul_eq_one_iff, pow_one] at h,
    symmetry,
    exact h.2,
  },
  have h_one_ne' : ∀ (k : ℕ) (h_ne_zero : k ≠ 0), a ^ k - 1 ≠ 0,
  {
    intros k h_ne_zero h,
    rw [nat.sub_eq_zero_iff_le] at h,
    apply h_one_ne k h_ne_zero,
    exact le_antisymm (h_one_le k) h,
  },
  have h_one_ne_int : ∀ (k : ℕ) (h_ne_zero : k ≠ 0), (a : ℤ)^k - 1 ≠ 0,
  {
    intros k h_ne_zero h,
    apply h_one_ne' k h_ne_zero,
    rw [← int.coe_nat_eq_coe_nat_iff, int.coe_nat_zero,
     int.coe_nat_sub (h_one_le k), int.coe_nat_pow, int.coe_nat_one],
    exact h,
  },
  have hdvd' : ∀ (d : ℕ) (h_dvdn : d ∣ n) (hd : d ≠ n), p * (a ^ d - 1) ∣ (a ^ n - 1),
  {
    intros d h_dvdn hd,
    suffices h_dvd_eval : ↑p * polynomial.eval (a : ℤ) (polynomial.X ^ d - 1)
    ∣ polynomial.eval (a : ℤ) (polynomial.X ^ n - 1),
    {
      simp only [eq_int_cast, int.cast_one, polynomial.eval_sub,
      polynomial.eval_pow, polynomial.eval_X, polynomial.eval_one] at h_dvd_eval,
      rw [← int.coe_nat_dvd, nat.cast_mul],
      rw [int.coe_nat_sub (h_one_le d), int.coe_nat_sub (h_one_le n),
      int.coe_nat_pow, nat.cast_one, int.coe_nat_pow],
      exact h_dvd_eval,
    },
    {
      transitivity polynomial.eval ↑a (polynomial.cyclotomic n ℤ) *
      (polynomial.eval ↑a (polynomial.X ^ d - 1)),
      { exact mul_dvd_mul_right hdvd _, },
      {
        rw ← polynomial.eval_mul,
        apply polynomial.eval_dvd,
        exact cyclotomic_dvd_X_pow_sub_one_frac ℤ h_dvdn (nat_ne_zero_of_pos h_pos) hd,
      }
    }
  },
  have h_order_dvd_n : order_of (zmod.unit_of_coprime a h_coprime) ∣ n,
  {
    apply order_of_dvd_of_pow_eq_one,
    rw [← units.eq_iff, ← sub_eq_zero, units.coe_pow,
    zmod.coe_unit_of_coprime, units.coe_one, ← nat.cast_one, ← nat.cast_pow,
    ← nat.cast_sub (h_one_le n), ← int.cast_coe_nat, zmod.int_coe_zmod_eq_zero_iff_dvd,
    int.coe_nat_dvd],
    cases em (1 = n),
    {
      symmetry' at h,
      subst h,
      simp only [polynomial.cyclotomic_one, polynomial.eval_sub,
      polynomial.eval_X, polynomial.eval_one] at hdvd,
      rw [← int.coe_nat_dvd, nat.cast_sub (h_one_le 1), pow_one, nat.cast_one],
      exact hdvd,
    },
    exact dvd_of_mul_right_dvd (hdvd' 1 (one_dvd n) h),
  },
  by_contra,
  let ord := order_of (zmod.unit_of_coprime a h_coprime),
  have ord_def : order_of (zmod.unit_of_coprime a h_coprime) = ord := by refl,
  have h_ord_pos : ord ≠ 0,
  {
    intro h_zero,
    rw [← ord_def, order_of_eq_zero_iff] at h_zero,
    apply h_zero,
    rw is_of_fin_order_iff_pow_eq_one,
    use fintype.card((zmod p)ˣ),
    split,
    { exact fintype.card_pos, },
    { rw pow_card_eq_one, }
  },
  have hdvd'' : p * (a ^ ord - 1) ∣ a ^ n - 1,
  { exact hdvd' ord h_order_dvd_n h, },
  rw ord_def at h_order_dvd_n,
  cases h_order_dvd_n with t h_mul,
  subst h_mul,
  rw [← int.coe_nat_dvd, nat.cast_mul,
  nat.cast_sub (h_one_le ord), nat.cast_sub (h_one_le (ord * t))] at hdvd'',
  simp only [int.coe_nat_pow, nat.cast_one] at hdvd'',
  rw [pow_mul, ← geom_sum_mul (↑a ^ ord) t] at hdvd'',
  have hdvd''' : (p : ℤ) ∣ (finset.range t).sum (λ (i : ℕ), (↑a ^ ord) ^ i),
  {
    exact int.dvd_of_mul_dvd_mul_right (h_one_ne_int ord h_ord_pos) hdvd'',
  },
  simp only [← zmod.int_coe_zmod_eq_zero_iff_dvd,
  int.cast_sum, int.cast_pow, int.cast_coe_nat, ← ord_def,
  ← zmod.coe_unit_of_coprime a h_coprime, ← units.coe_pow,
  pow_order_of_eq_one, units.coe_one, one_pow, finset.sum_const,
  finset.card_range, nat.smul_one_eq_coe] at hdvd''',
  rw zmod.nat_coe_zmod_eq_zero_iff_dvd at hdvd''',
  rw mul_comm at h_ndvd,
  exact h_ndvd (dvd_mul_of_dvd_left hdvd''' ord),
end


/-
Copyright (c) 2020 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau
-/

import algebra.big_operators.pi
import data.finsupp

/-!
# Big operators for finsupps

This file contains theorems relevant to big operators in finitely supported functions.
-/

open_locale big_operators

variables {α ι γ A B C : Type*} [add_comm_monoid A] [add_comm_monoid B] [add_comm_monoid C]
variables {t : ι → A → C} (h0 : ∀ i, t i 0 = 0) (h1 : ∀ i x y, t i (x + y) = t i x + t i y)
variables {s : finset α} {f : α → (ι →₀ A)} (i : ι)
variables (g : ι →₀ A) (k : ι → A → γ → B) (x : γ)

theorem finset.sum_apply' : (∑ k in s, f k) i = ∑ k in s, f k i :=
(s.sum_hom $ finsupp.apply_add_hom i).symm

theorem finsupp.sum_apply' : g.sum k x = g.sum (λ i b, k i b x) :=
finset.sum_apply _ _ _

include h0 h1

open_locale classical

theorem finsupp.sum_sum_index' : (∑ x in s, f x).sum t = ∑ x in s, (f x).sum t :=
finset.induction_on s rfl $ λ a s has ih,
by simp_rw [finset.sum_insert has, finsupp.sum_add_index h0 h1, ih]


import data.real.basic

def converges_to (s : ℕ → ℝ) (a : ℝ) :=
  ∀ ε > 0, ∃ N, ∀ n ≥ N, abs (s n - a) < ε

example : (λ x y : ℝ, (x + y)^2) = (λ x y : ℝ, x^2 + 2*x*y + y^2) :=
begin
  ext x y,
  ring,
end

example (a b : ℝ) : abs a = abs (a - b + b) :=
begin
  congr,
  ring,
end

example {a : ℝ} (h : 1 < a) : a < a * a :=
begin
  convert (mul_lt_mul_right _).2 h,
    rw one_mul,
    linarith,
end

theorem converges_to_const (a : ℝ) : converges_to (λ x : ℕ, a) a :=
begin
  intros ε epos,
  use 0,
  intros n nge,
  dsimp,
  rw [sub_self, abs_zero],
  apply epos,
end

theorem converges_to_add {s t : ℕ → ℝ} {a b : ℝ}
  (cs : converges_to s a) (ct : converges_to t b):
converges_to (λ n, s n + t n) (a + b) :=
begin
  intros ε epos,
  dsimp,
  have e2pos : 0 < ε / 2 := by linarith,
  cases cs (ε / 2) e2pos with Ns fs,
  cases ct (ε / 2) e2pos with Nt ft,

  let mN := max Ns Nt,
  use mN,
  intros n nge,
  have : |s n - a| < ε / 2 := fs n (le_of_max_le_left nge),
  have : |t n - b| < ε / 2 := ft n (le_of_max_le_right nge),
  apply lt_of_le_of_lt,

  show |s n + t n - (a + b)| ≤ |s n - a| + |t n - b|,
  calc
    |s n + t n - (a + b)| = |(s n - a) + (t n - b)| : by congr; ring
    ... ≤ |s n - a| + |t n - b| : by apply abs_add,

  show |s n - a| + |t n - b| < ε,
  have : ε = ε / 2 + ε / 2 := by norm_num,
  rw this,
  apply add_lt_add; assumption,
end

theorem converges_to_mul_const {s : ℕ → ℝ} {a : ℝ}
    (c : ℝ) (cs : converges_to s a) :
  converges_to (λ n, c * s n) (c * a) :=
begin
  by_cases h : c = 0,
  { convert converges_to_const 0,
    ext,
    rw [h, zero_mul],
    rw [h, zero_mul],
  },
  { have acpos : |c| > 0,
      from abs_pos.mpr h,

    intros ε epos,
    have : ε / |c| > 0,
      from div_pos epos acpos,
    dsimp,
    cases cs (ε / |c|) this with N fn,

    use N,
    intros n nge,
    have hec : |s n - a| < ε / |c| := fn n nge,

    have hmul : |c * s n - c * a| = |c| * |s n - a|,
      calc
        |c * s n - c * a| = |c * (s n - a)| : by congr; ring
        ... = |c| * |s n - a| : by apply abs_mul,

    have : ε = ε * |c| / |c|,
      calc
        ε = ε * 1 : (mul_one ε).symm
        ... = ε * (|c| / |c|) : by rw div_self (ne_of_gt acpos)
        ... = ε * |c| / |c| : by ring,
    rw this,
    calc
      |c * s n - c * a| = |c| * |s n - a| : by assumption
      ... < |c| * (ε / |c|) : by apply (mul_lt_mul_left acpos).mpr hec
      ... = ε * |c| / |c| : by ring,
  }
end

theorem exists_abs_le_of_converges_to {s : ℕ → ℝ} {a : ℝ}
    (cs : converges_to s a) :
  ∃ N b, ∀ n, N ≤ n → abs (s n) < b :=
begin
  cases cs 1 zero_lt_one with N h,
  use [N, |a| + 1],
  intros n nge,
  have h := h n nge,
  apply lt_of_le_of_lt,

  show |s n| ≤ |a| + |s n - a|,
  calc
    |s n| = |a + (s n - a)| : by congr; ring_nf
    ... ≤ |a| + |s n - a| : by apply abs_add,

  show |a| + |s n - a| < |a| + 1,
  apply add_lt_add_of_le_of_lt,
    apply le_refl,
    exact h,
end

lemma aux {s t : ℕ → ℝ} {a : ℝ}
    (cs : converges_to s a) (ct : converges_to t 0) :
  converges_to (λ n, s n * t n) 0 :=
begin
  intros ε epos, dsimp,
  rcases exists_abs_le_of_converges_to cs with ⟨N, b, h⟩,
  have bpos : 0 < b,
    from lt_of_le_of_lt (abs_nonneg _) (h N (le_refl _)),
  have pos : ε / b > 0,
    from div_pos epos bpos,

  cases ct (ε / b) pos with N' h',
  let mN := max N N',
  use mN,
  intros n nge,

  have : |s n * t n - 0| = |s n * t n|,
    by congr; apply sub_zero,
  rw this,

  have ht : |t n - 0| = |t n|,
    by congr; apply sub_zero,

  rw abs_mul,
  have : ε = b * (ε / b),
    calc
      ε = ε * 1 : (mul_one ε).symm
      ... = ε * (b / b) : by rw div_self (ne_of_gt bpos)
      ... = b * (ε / b) : by ring,
  rw this,

  apply mul_lt_mul',

  show |s n| ≤ b,
    apply le_of_lt,
    apply h n,
    from le_trans (le_max_left N N') nge,

  show |t n| < ε / b,
    rw ht.symm,
    apply h' n,
    from le_trans (le_max_right N N') nge,

  show 0 ≤ |t n|,
    from abs_nonneg (t n),

  show 0 < b,
    from bpos,
end

theorem converges_to_mul {s t : ℕ → ℝ} {a b : ℝ}
    (cs : converges_to s a) (ct : converges_to t b):
  converges_to (λ n, s n * t n) (a * b) :=
begin
  have h₁ : converges_to (λ n, s n * (t n - b)) 0,
    apply aux cs,
    convert converges_to_add ct (converges_to_const (-b)),
    ring,

  convert converges_to_add h₁ (converges_to_mul_const b cs),
  ext, ring,
  ring,
end

theorem converges_to_unique {s : ℕ → ℝ} {a b : ℝ}
    (sa : converges_to s a) (sb : converges_to s b) :
  a = b :=
begin
  by_contra abne,
  have : |a - b| > 0,
    apply abs_pos.mpr,
    intro h',
    apply abne,
    linarith,
    exact has_add.to_covariant_class_left ℝ,

  let ε := |a - b| / 2,
  have epos : ε > 0,
    change |a - b| / 2 > 0,
    linarith,

  cases sa ε epos with Na hNa,
  cases sb ε epos with Nb hNb,

  let N := max Na Nb,
  have ha : | 2 * (s N - a)| < |a - b|,
    have : |s N - a| < |a - b| / 2,
      apply hNa N,
      apply le_max_left,
    have : |2| * |s N - a| < |2| * (|a - b| / 2),
      apply mul_lt_mul',
      apply le_refl,
      from this,
      apply abs_nonneg,
      norm_num,
    rw abs_mul,
    have : |2| * (|a - b| / 2) = |a - b|,
      rw mul_div,
      rw mul_comm,
      rw ←mul_div,
      norm_num,
    linarith,

  have hb : | 2 * (s N - b)| < |a - b|,
    have : |s N - b| < |a - b| / 2,
      apply hNb N,
      apply le_max_right,
    have : |2| * |s N - b| < |2| * (|a - b| / 2),
      apply mul_lt_mul',
      apply le_refl,
      from this,
      apply abs_nonneg,
      norm_num,
    rw abs_mul,
    have : |2| * (|a - b| / 2) = |a - b|,
      rw mul_div,
      rw mul_comm,
      rw ←mul_div,
      norm_num,
    linarith,

  have ha : |s N - a| < |a - b| / 2,
      apply hNa N,
      apply le_max_left,

  have hb : |s N - b| < |a - b| / 2,
      apply hNb N,
      apply le_max_right,

  have ab : |a - b| = |a - b|/2 + |a - b|/2,
      norm_num,

  have : |a - b| = | -(s N - a) + (s N - b)|,
    congr,
    linarith,

  have : |a - b| < |a - b|,
    apply lt_of_le_of_lt,
    show |a - b| ≤ | -(s N - a) + (s N - b)|,
    rw this,
    show | -(s N - a) + (s N - b)| < |a - b|,
    apply lt_of_le_of_lt,
    apply abs_add,
    rw ab,
    apply add_lt_add,
      rw abs_neg,
      from ha,
      from hb,

  from lt_irrefl _ this,
end

variables {α : Type*} [linear_order α]

def converges_to' (s : α → ℝ) (a : ℝ) :=
∀ ε > 0, ∃ N, ∀ n ≥ N, abs (s n - a) < ε


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Gabriel Ebner
-/
import data.nat.cast.defs

/-!
# Cast of integers

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

This file defines the *canonical* homomorphism from the integers into an
additive group with a one (typically a `ring`).  In additive groups with a one
element, there exists a unique such homomorphism and we store it in the
`int_cast : ℤ → R` field.

Preferentially, the homomorphism is written as a coercion.

## Main declarations

* `int.cast`: Canonical homomorphism `ℤ → R`.
* `add_group_with_one`: Type class for `int.cast`.
-/

universes u
set_option old_structure_cmd true

attribute [simp] int.of_nat_eq_coe

/-- Default value for `add_group_with_one.int_cast`. -/
protected def int.cast_def {R : Type u} [has_nat_cast R] [has_neg R] : ℤ → R
| (n : ℕ) := n
| -[1+ n] := -(n+1 : ℕ)

/--
Type class for the canonical homomorphism `ℤ → R`.
-/
class has_int_cast (R : Type u) :=
(int_cast : ℤ → R)

/--
An `add_group_with_one` is an `add_group` with a `1`.
It also contains data for the unique homomorphisms `ℕ → R` and `ℤ → R`.
-/
@[protect_proj, ancestor has_int_cast add_group add_monoid_with_one]
class add_group_with_one (R : Type u)
  extends has_int_cast R, add_group R, add_monoid_with_one R :=
(int_cast := int.cast_def)
(int_cast_of_nat : ∀ n : ℕ, int_cast n = (n : R) . control_laws_tac)
(int_cast_neg_succ_of_nat : ∀ n : ℕ, int_cast (-(n+1 : ℕ)) = -((n+1 : ℕ) : R) . control_laws_tac)

/-- An `add_comm_group_with_one` is an `add_group_with_one` satisfying `a + b = b + a`. -/
@[protect_proj, ancestor add_comm_group add_group_with_one add_comm_monoid_with_one]
class add_comm_group_with_one (R : Type u)
  extends add_comm_group R, add_group_with_one R, add_comm_monoid_with_one R

/-- Canonical homomorphism from the integers to any ring(-like) structure `R` -/
protected def int.cast {R : Type u} [has_int_cast R] (i : ℤ) : R := has_int_cast.int_cast i

open nat

namespace int
variables {R : Type u} [add_group_with_one R]

-- see Note [coercion into rings]
@[priority 900] instance cast_coe {R} [has_int_cast R] : has_coe_t ℤ R := ⟨int.cast⟩

theorem cast_of_nat (n : ℕ) : (of_nat n : R) = n := add_group_with_one.int_cast_of_nat n

end int