/-
Copyright (c) 2018 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.big_operators.order
import Mathlib.PostPort

universes u_1 u_2 l

namespace Mathlib

/-!
# Cauchy sequences

A basic theory of Cauchy sequences, used in the construction of the reals and p-adic numbers. Where
applicable, lemmas that will be reused in other contexts have been stated in extra generality.

There are other "versions" of Cauchyness in the library, in particular Cauchy filters in topology.
This is a concrete implementation that is useful for simplicity and computability reasons.

## Important definitions

* `is_absolute_value`: a type class stating that `f : β → α` satisfies the axioms of an abs val
* `is_cau_seq`: a predicate that says `f : ℕ → β` is Cauchy.
* `cau_seq`: the type of Cauchy sequences valued in type `β` with respect to an absolute value
  function `abv`.

## Tags

sequence, cauchy, abs val, absolute value
-/

/-- A function `f` is an absolute value if it is nonnegative, zero only at 0, additive, and
multiplicative. -/
class is_absolute_value {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (f : β → α)
where
  abv_nonneg : ∀ (x : β), 0 ≤ f x
  abv_eq_zero : ∀ {x : β}, f x = 0 ↔ x = 0
  abv_add : ∀ (x y : β), f (x + y) ≤ f x + f y
  abv_mul : ∀ (x y : β), f (x * y) = f x * f y

namespace is_absolute_value

theorem abv_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] : abv 0 = 0 :=
  iff.mpr (abv_eq_zero abv) rfl

theorem abv_one {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] [nontrivial β] : abv 1 = 1 := sorry

theorem abv_pos {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] {a : β} : 0 < abv a ↔ a ≠ 0 := sorry

theorem abv_neg {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] (a : β) : abv (-a) = abv a := sorry

theorem abv_sub {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] (a : β) (b : β) : abv (a - b) = abv (b - a) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (abv (a - b) = abv (b - a))) (Eq.symm (neg_sub b a))))
    (eq.mpr (id (Eq._oldrec (Eq.refl (abv (-(b - a)) = abv (b - a))) (abv_neg abv (b - a)))) (Eq.refl (abv (b - a))))

/-- `abv` as a `monoid_with_zero_hom`. -/
def abv_hom {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] [nontrivial β] : monoid_with_zero_hom β α :=
  monoid_with_zero_hom.mk abv (abv_zero abv) (abv_one abv) (abv_mul abv)

theorem abv_inv {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [field β] (abv : β → α) [is_absolute_value abv] (a : β) : abv (a⁻¹) = (abv a⁻¹) :=
  monoid_with_zero_hom.map_inv' (abv_hom abv) a

theorem abv_div {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [field β] (abv : β → α) [is_absolute_value abv] (a : β) (b : β) : abv (a / b) = abv a / abv b :=
  monoid_with_zero_hom.map_div (abv_hom abv) a b

theorem abv_sub_le {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] (a : β) (b : β) (c : β) : abv (a - c) ≤ abv (a - b) + abv (b - c) := sorry

theorem sub_abv_le_abv_sub {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] (a : β) (b : β) : abv a - abv b ≤ abv (a - b) := sorry

theorem abs_abv_sub_le_abv_sub {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] (a : β) (b : β) : abs (abv a - abv b) ≤ abv (a - b) := sorry

theorem abv_pow {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] [nontrivial β] (abv : β → α) [is_absolute_value abv] (a : β) (n : ℕ) : abv (a ^ n) = abv a ^ n :=
  monoid_hom.map_pow (monoid_with_zero_hom.to_monoid_hom (abv_hom abv)) a n

end is_absolute_value

protected instance abs_is_absolute_value {α : Type u_1} [linear_ordered_field α] : is_absolute_value abs :=
  is_absolute_value.mk abs_nonneg (fun (_x : α) => abs_eq_zero) abs_add abs_mul

theorem exists_forall_ge_and {α : Type u_1} [linear_order α] {P : α → Prop} {Q : α → Prop} : (∃ (i : α), ∀ (j : α), j ≥ i → P j) → (∃ (i : α), ∀ (j : α), j ≥ i → Q j) → ∃ (i : α), ∀ (j : α), j ≥ i → P j ∧ Q j := sorry

theorem rat_add_continuous_lemma {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] {ε : α} (ε0 : 0 < ε) : ∃ (δ : α), ∃ (H : δ > 0), ∀ {a₁ a₂ b₁ b₂ : β}, abv (a₁ - b₁) < δ → abv (a₂ - b₂) < δ → abv (a₁ + a₂ - (b₁ + b₂)) < ε := sorry

theorem rat_mul_continuous_lemma {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] {ε : α} {K₁ : α} {K₂ : α} (ε0 : 0 < ε) : ∃ (δ : α),
  ∃ (H : δ > 0),
    ∀ {a₁ a₂ b₁ b₂ : β}, abv a₁ < K₁ → abv b₂ < K₂ → abv (a₁ - b₁) < δ → abv (a₂ - b₂) < δ → abv (a₁ * a₂ - b₁ * b₂) < ε := sorry

theorem rat_inv_continuous_lemma {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [field β] (abv : β → α) [is_absolute_value abv] {ε : α} {K : α} (ε0 : 0 < ε) (K0 : 0 < K) : ∃ (δ : α), ∃ (H : δ > 0), ∀ {a b : β}, K ≤ abv a → K ≤ abv b → abv (a - b) < δ → abv (a⁻¹ - (b⁻¹)) < ε := sorry

/-- A sequence is Cauchy if the distance between its entries tends to zero. -/
def is_cau_seq {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) (f : ℕ → β) :=
  ∀ (ε : α), ε > 0 → ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → abv (f j - f i) < ε

namespace is_cau_seq

theorem cauchy₂ {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : ℕ → β} (hf : is_cau_seq abv f) {ε : α} (ε0 : 0 < ε) : ∃ (i : ℕ), ∀ (j k : ℕ), j ≥ i → k ≥ i → abv (f j - f k) < ε := sorry

theorem cauchy₃ {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : ℕ → β} (hf : is_cau_seq abv f) {ε : α} (ε0 : 0 < ε) : ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → ∀ (k : ℕ), k ≥ j → abv (f k - f j) < ε := sorry

end is_cau_seq

/-- `cau_seq β abv` is the type of `β`-valued Cauchy sequences, with respect to the absolute value
function `abv`. -/
def cau_seq {α : Type u_1} [linear_ordered_field α] (β : Type u_2) [ring β] (abv : β → α) :=
  Subtype fun (f : ℕ → β) => is_cau_seq abv f

namespace cau_seq

protected instance has_coe_to_fun {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} : has_coe_to_fun (cau_seq β abv) :=
  has_coe_to_fun.mk (fun (x : cau_seq β abv) => ℕ → β) subtype.val

@[simp] theorem mk_to_fun {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} (f : ℕ → β) (hf : is_cau_seq abv f) : ⇑{ val := f, property := hf } = f :=
  rfl

theorem ext {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} {f : cau_seq β abv} {g : cau_seq β abv} (h : ∀ (i : ℕ), coe_fn f i = coe_fn g i) : f = g :=
  subtype.eq (funext h)

theorem is_cau {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} (f : cau_seq β abv) : is_cau_seq abv ⇑f :=
  subtype.property f

theorem cauchy {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} (f : cau_seq β abv) {ε : α} : 0 < ε → ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → abv (coe_fn f j - coe_fn f i) < ε :=
  subtype.property f

/-- Given a Cauchy sequence `f`, create a Cauchy sequence from a sequence `g` with
the same values as `f`. -/
def of_eq {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} (f : cau_seq β abv) (g : ℕ → β) (e : ∀ (i : ℕ), coe_fn f i = g i) : cau_seq β abv :=
  { val := g, property := sorry }

theorem cauchy₂ {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (f : cau_seq β abv) {ε : α} : 0 < ε → ∃ (i : ℕ), ∀ (j k : ℕ), j ≥ i → k ≥ i → abv (coe_fn f j - coe_fn f k) < ε :=
  is_cau_seq.cauchy₂ (subtype.property f)

theorem cauchy₃ {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (f : cau_seq β abv) {ε : α} : 0 < ε → ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → ∀ (k : ℕ), k ≥ j → abv (coe_fn f k - coe_fn f j) < ε :=
  is_cau_seq.cauchy₃ (subtype.property f)

theorem bounded {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (f : cau_seq β abv) : ∃ (r : α), ∀ (i : ℕ), abv (coe_fn f i) < r := sorry

theorem bounded' {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (f : cau_seq β abv) (x : α) : ∃ (r : α), ∃ (H : r > x), ∀ (i : ℕ), abv (coe_fn f i) < r := sorry

protected instance has_add {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] : Add (cau_seq β abv) :=
  { add := fun (f g : cau_seq β abv) => { val := fun (i : ℕ) => coe_fn f i + coe_fn g i, property := sorry } }

@[simp] theorem add_apply {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (f : cau_seq β abv) (g : cau_seq β abv) (i : ℕ) : coe_fn (f + g) i = coe_fn f i + coe_fn g i :=
  rfl

/-- The constant Cauchy sequence. -/
def const {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] (abv : β → α) [is_absolute_value abv] (x : β) : cau_seq β abv :=
  { val := fun (i : ℕ) => x, property := sorry }

@[simp] theorem const_apply {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (x : β) (i : ℕ) : coe_fn (const abv x) i = x :=
  rfl

theorem const_inj {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {x : β} {y : β} : const abv x = const abv y ↔ x = y :=
  { mp := fun (h : const abv x = const abv y) => congr_arg (fun (f : cau_seq β abv) => coe_fn f 0) h,
    mpr := congr_arg fun {x : β} => const abv x }

protected instance has_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] : HasZero (cau_seq β abv) :=
  { zero := const abv 0 }

protected instance has_one {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] : HasOne (cau_seq β abv) :=
  { one := const abv 1 }

protected instance inhabited {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] : Inhabited (cau_seq β abv) :=
  { default := 0 }

@[simp] theorem zero_apply {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (i : ℕ) : coe_fn 0 i = 0 :=
  rfl

@[simp] theorem one_apply {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (i : ℕ) : coe_fn 1 i = 1 :=
  rfl

theorem const_add {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (x : β) (y : β) : const abv (x + y) = const abv x + const abv y :=
  ext fun (i : ℕ) => rfl

protected instance has_mul {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] : Mul (cau_seq β abv) :=
  { mul := fun (f g : cau_seq β abv) => { val := fun (i : ℕ) => coe_fn f i * coe_fn g i, property := sorry } }

@[simp] theorem mul_apply {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (f : cau_seq β abv) (g : cau_seq β abv) (i : ℕ) : coe_fn (f * g) i = coe_fn f i * coe_fn g i :=
  rfl

theorem const_mul {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (x : β) (y : β) : const abv (x * y) = const abv x * const abv y :=
  ext fun (i : ℕ) => rfl

protected instance has_neg {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] : Neg (cau_seq β abv) :=
  { neg := fun (f : cau_seq β abv) => of_eq (const abv (-1) * f) (fun (x : ℕ) => -coe_fn f x) sorry }

@[simp] theorem neg_apply {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (f : cau_seq β abv) (i : ℕ) : coe_fn (-f) i = -coe_fn f i :=
  rfl

theorem const_neg {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (x : β) : const abv (-x) = -const abv x :=
  ext fun (i : ℕ) => rfl

protected instance has_sub {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] : Sub (cau_seq β abv) :=
  { sub := fun (f g : cau_seq β abv) => of_eq (f + -g) (fun (x : ℕ) => coe_fn f x - coe_fn g x) sorry }

@[simp] theorem sub_apply {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (f : cau_seq β abv) (g : cau_seq β abv) (i : ℕ) : coe_fn (f - g) i = coe_fn f i - coe_fn g i :=
  rfl

theorem const_sub {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (x : β) (y : β) : const abv (x - y) = const abv x - const abv y :=
  ext fun (i : ℕ) => rfl

protected instance ring {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] : ring (cau_seq β abv) :=
  ring.mk Add.add sorry 0 sorry sorry Neg.neg Sub.sub sorry sorry Mul.mul sorry 1 sorry sorry sorry sorry

protected instance comm_ring {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [comm_ring β] {abv : β → α} [is_absolute_value abv] : comm_ring (cau_seq β abv) :=
  comm_ring.mk ring.add sorry ring.zero sorry sorry ring.neg ring.sub sorry sorry ring.mul sorry ring.one sorry sorry
    sorry sorry sorry

/-- `lim_zero f` holds when `f` approaches 0. -/
def lim_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} (f : cau_seq β abv) :=
  ∀ (ε : α), ε > 0 → ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → abv (coe_fn f j) < ε

theorem add_lim_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} {g : cau_seq β abv} (hf : lim_zero f) (hg : lim_zero g) : lim_zero (f + g) := sorry

theorem mul_lim_zero_right {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (f : cau_seq β abv) {g : cau_seq β abv} (hg : lim_zero g) : lim_zero (f * g) := sorry

theorem mul_lim_zero_left {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} (g : cau_seq β abv) (hg : lim_zero f) : lim_zero (f * g) := sorry

theorem neg_lim_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} (hf : lim_zero f) : lim_zero (-f) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (lim_zero (-f))) (Eq.symm (neg_one_mul f)))) (mul_lim_zero_right (-1) hf)

theorem sub_lim_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} {g : cau_seq β abv} (hf : lim_zero f) (hg : lim_zero g) : lim_zero (f - g) :=
  eq.mpr
    (id ((fun (f f_1 : cau_seq β abv) (e_1 : f = f_1) => congr_arg lim_zero e_1) (f - g) (f + -g) (sub_eq_add_neg f g)))
    (eq.mp (Eq.refl (lim_zero (f + -g))) (add_lim_zero hf (neg_lim_zero hg)))

theorem lim_zero_sub_rev {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} {g : cau_seq β abv} (hfg : lim_zero (f - g)) : lim_zero (g - f) :=
  eq.mpr (id (Eq.refl (lim_zero (g - f))))
    (eq.mp ((fun (f f_1 : cau_seq β abv) (e_1 : f = f_1) => congr_arg lim_zero e_1) (-(f - g)) (g - f) (neg_sub f g))
      (neg_lim_zero hfg))

theorem zero_lim_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] : lim_zero 0 := sorry

theorem const_lim_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {x : β} : lim_zero (const abv x) ↔ x = 0 := sorry

protected instance equiv {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] : setoid (cau_seq β abv) :=
  setoid.mk (fun (f g : cau_seq β abv) => lim_zero (f - g)) sorry

theorem add_equiv_add {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f1 : cau_seq β abv} {f2 : cau_seq β abv} {g1 : cau_seq β abv} {g2 : cau_seq β abv} (hf : f1 ≈ f2) (hg : g1 ≈ g2) : f1 + g1 ≈ f2 + g2 := sorry

theorem neg_equiv_neg {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} {g : cau_seq β abv} (hf : f ≈ g) : -f ≈ -g := sorry

theorem equiv_def₃ {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} {g : cau_seq β abv} (h : f ≈ g) {ε : α} (ε0 : 0 < ε) : ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → ∀ (k : ℕ), k ≥ j → abv (coe_fn f k - coe_fn g j) < ε := sorry

theorem lim_zero_congr {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} {g : cau_seq β abv} (h : f ≈ g) : lim_zero f ↔ lim_zero g := sorry

theorem abv_pos_of_not_lim_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} (hf : ¬lim_zero f) : ∃ (K : α), ∃ (H : K > 0), ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → K ≤ abv (coe_fn f j) := sorry

theorem of_near {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (f : ℕ → β) (g : cau_seq β abv) (h : ∀ (ε : α), ε > 0 → ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → abv (f j - coe_fn g j) < ε) : is_cau_seq abv f := sorry

theorem not_lim_zero_of_not_congr_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} (hf : ¬f ≈ 0) : ¬lim_zero f := sorry

theorem mul_equiv_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] (g : cau_seq β abv) {f : cau_seq β abv} (hf : f ≈ 0) : g * f ≈ 0 := sorry

theorem mul_not_equiv_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} {g : cau_seq β abv} (hf : ¬f ≈ 0) (hg : ¬g ≈ 0) : ¬f * g ≈ 0 := sorry

theorem const_equiv {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [ring β] {abv : β → α} [is_absolute_value abv] {x : β} {y : β} : const abv x ≈ const abv y ↔ x = y := sorry

theorem mul_equiv_zero' {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [comm_ring β] {abv : β → α} [is_absolute_value abv] (g : cau_seq β abv) {f : cau_seq β abv} (hf : f ≈ 0) : f * g ≈ 0 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (f * g ≈ 0)) (mul_comm f g))) (mul_equiv_zero g hf)

theorem one_not_equiv_zero {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [integral_domain β] (abv : β → α) [is_absolute_value abv] : ¬const abv 1 ≈ const abv 0 := sorry

theorem inv_aux {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [field β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} (hf : ¬lim_zero f) (ε : α) (H : ε > 0) : ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → abv (coe_fn f j⁻¹ - (coe_fn f i⁻¹)) < ε := sorry

/-- Given a Cauchy sequence `f` with nonzero limit, create a Cauchy sequence with values equal to
the inverses of the values of `f`. -/
def inv {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [field β] {abv : β → α} [is_absolute_value abv] (f : cau_seq β abv) (hf : ¬lim_zero f) : cau_seq β abv :=
  { val := fun (j : ℕ) => coe_fn f j⁻¹, property := inv_aux hf }

@[simp] theorem inv_apply {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [field β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} (hf : ¬lim_zero f) (i : ℕ) : coe_fn (inv f hf) i = (coe_fn f i⁻¹) :=
  rfl

theorem inv_mul_cancel {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [field β] {abv : β → α} [is_absolute_value abv] {f : cau_seq β abv} (hf : ¬lim_zero f) : inv f hf * f ≈ 1 := sorry

theorem const_inv {α : Type u_1} [linear_ordered_field α] {β : Type u_2} [field β] {abv : β → α} [is_absolute_value abv] {x : β} (hx : x ≠ 0) : const abv (x⁻¹) =
  inv (const abv x) (eq.mpr (id (Eq._oldrec (Eq.refl (¬lim_zero (const abv x))) (propext const_lim_zero))) hx) := sorry

/-- The entries of a positive Cauchy sequence eventually have a positive lower bound. -/
def pos {α : Type u_1} [linear_ordered_field α] (f : cau_seq α abs) :=
  ∃ (K : α), ∃ (H : K > 0), ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → K ≤ coe_fn f j

theorem not_lim_zero_of_pos {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} : pos f → ¬lim_zero f := sorry

theorem const_pos {α : Type u_1} [linear_ordered_field α] {x : α} : pos (const abs x) ↔ 0 < x := sorry

theorem add_pos {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} {g : cau_seq α abs} : pos f → pos g → pos (f + g) := sorry

theorem pos_add_lim_zero {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} {g : cau_seq α abs} : pos f → lim_zero g → pos (f + g) := sorry

protected theorem mul_pos {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} {g : cau_seq α abs} : pos f → pos g → pos (f * g) := sorry

theorem trichotomy {α : Type u_1} [linear_ordered_field α] (f : cau_seq α abs) : pos f ∨ lim_zero f ∨ pos (-f) := sorry

protected instance has_lt {α : Type u_1} [linear_ordered_field α] : HasLess (cau_seq α abs) :=
  { Less := fun (f g : cau_seq α abs) => pos (g - f) }

protected instance has_le {α : Type u_1} [linear_ordered_field α] : HasLessEq (cau_seq α abs) :=
  { LessEq := fun (f g : cau_seq α abs) => f < g ∨ f ≈ g }

theorem lt_of_lt_of_eq {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} {g : cau_seq α abs} {h : cau_seq α abs} (fg : f < g) (gh : g ≈ h) : f < h := sorry

theorem lt_of_eq_of_lt {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} {g : cau_seq α abs} {h : cau_seq α abs} (fg : f ≈ g) (gh : g < h) : f < h :=
  eq.mp (Eq._oldrec (Eq.refl (pos (h - g - (f - g)))) (sub_sub_sub_cancel_right h f g))
    (eq.mp (Eq._oldrec (Eq.refl (pos (h - g + -(f - g)))) (Eq.symm (sub_eq_add_neg (h - g) (f - g))))
      (pos_add_lim_zero gh (neg_lim_zero fg)))

theorem lt_trans {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} {g : cau_seq α abs} {h : cau_seq α abs} (fg : f < g) (gh : g < h) : f < h := sorry

theorem lt_irrefl {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} : ¬f < f := sorry

theorem le_of_eq_of_le {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} {g : cau_seq α abs} {h : cau_seq α abs} (hfg : f ≈ g) (hgh : g ≤ h) : f ≤ h :=
  or.elim hgh (Or.inl ∘ lt_of_eq_of_lt hfg) (Or.inr ∘ setoid.trans hfg)

theorem le_of_le_of_eq {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} {g : cau_seq α abs} {h : cau_seq α abs} (hfg : f ≤ g) (hgh : g ≈ h) : f ≤ h :=
  or.elim hfg (fun (h_1 : f < g) => Or.inl (lt_of_lt_of_eq h_1 hgh)) fun (h_1 : f ≈ g) => Or.inr (setoid.trans h_1 hgh)

protected instance preorder {α : Type u_1} [linear_ordered_field α] : preorder (cau_seq α abs) :=
  preorder.mk (fun (f g : cau_seq α abs) => f < g ∨ f ≈ g) Less sorry sorry

theorem le_antisymm {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} {g : cau_seq α abs} (fg : f ≤ g) (gf : g ≤ f) : f ≈ g :=
  or.resolve_left fg (not_lt_of_le gf)

theorem lt_total {α : Type u_1} [linear_ordered_field α] (f : cau_seq α abs) (g : cau_seq α abs) : f < g ∨ f ≈ g ∨ g < f := sorry

theorem le_total {α : Type u_1} [linear_ordered_field α] (f : cau_seq α abs) (g : cau_seq α abs) : f ≤ g ∨ g ≤ f :=
  or.imp_right Or.inl (iff.mpr or.assoc (lt_total f g))

theorem const_lt {α : Type u_1} [linear_ordered_field α] {x : α} {y : α} : const abs x < const abs y ↔ x < y := sorry

theorem const_le {α : Type u_1} [linear_ordered_field α] {x : α} {y : α} : const abs x ≤ const abs y ↔ x ≤ y :=
  eq.mpr (id (Eq._oldrec (Eq.refl (const abs x ≤ const abs y ↔ x ≤ y)) (propext le_iff_lt_or_eq)))
    (or_congr const_lt const_equiv)

theorem le_of_exists {α : Type u_1} [linear_ordered_field α] {f : cau_seq α abs} {g : cau_seq α abs} (h : ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → coe_fn f j ≤ coe_fn g j) : f ≤ g := sorry

theorem exists_gt {α : Type u_1} [linear_ordered_field α] (f : cau_seq α abs) : ∃ (a : α), f < const abs a := sorry

theorem exists_lt {α : Type u_1} [linear_ordered_field α] (f : cau_seq α abs) : ∃ (a : α), const abs a < f := sorry


/-
Copyright (c) 2017 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Mario Carneiro, Neil Strickland
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.data.nat.basic
import Mathlib.PostPort

namespace Mathlib

/-!
# The positive natural numbers

This file defines the type `ℕ+` or `pnat`, the subtype of natural numbers that are positive.
-/

/-- `ℕ+` is the type of positive natural numbers. It is defined as a subtype,
  and the VM representation of `ℕ+` is the same as `ℕ` because the proof
  is not stored. -/
def pnat := Subtype fun (n : ℕ) => 0 < n

notation:1024 "ℕ+" => Mathlib.pnat

protected instance coe_pnat_nat : has_coe ℕ+ ℕ := has_coe.mk subtype.val

protected instance pnat.has_repr : has_repr ℕ+ := has_repr.mk fun (n : ℕ+) => repr (subtype.val n)

/-- Predecessor of a `ℕ+`, as a `ℕ`. -/
def pnat.nat_pred (i : ℕ+) : ℕ := ↑i - 1

namespace nat

/-- Convert a natural number to a positive natural number. The
  positivity assumption is inferred by `dec_trivial`. -/
def to_pnat (n : ℕ)
    (h :
      autoParam (0 < n)
        (Lean.Syntax.ident Lean.SourceInfo.none
          (String.toSubstring "Mathlib.tactic.exact_dec_trivial")
          (Lean.Name.mkStr
            (Lean.Name.mkStr (Lean.Name.mkStr Lean.Name.anonymous "Mathlib") "tactic")
            "exact_dec_trivial")
          [])) :
    ℕ+ :=
  { val := n, property := h }

/-- Write a successor as an element of `ℕ+`. -/
def succ_pnat (n : ℕ) : ℕ+ := { val := Nat.succ n, property := succ_pos n }

@[simp] theorem succ_pnat_coe (n : ℕ) : ↑(succ_pnat n) = Nat.succ n := rfl

theorem succ_pnat_inj {n : ℕ} {m : ℕ} : succ_pnat n = succ_pnat m → n = m :=
  fun (h : succ_pnat n = succ_pnat m) =>
    let h' : ↑(succ_pnat n) = ↑(succ_pnat m) := congr_arg coe h;
    succ.inj h'

/-- Convert a natural number to a pnat. `n+1` is mapped to itself,
  and `0` becomes `1`. -/
def to_pnat' (n : ℕ) : ℕ+ := succ_pnat (Nat.pred n)

@[simp] theorem to_pnat'_coe (n : ℕ) : ↑(to_pnat' n) = ite (0 < n) n 1 := sorry

end nat

namespace pnat

/-- We now define a long list of structures on ℕ+ induced by
 similar structures on ℕ. Most of these behave in a completely
 obvious way, but there are a few things to be said about
 subtraction, division and powers.
-/
protected instance decidable_eq : DecidableEq ℕ+ := fun (a b : ℕ+) => subtype.decidable_eq a b

protected instance linear_order : linear_order ℕ+ := subtype.linear_order fun (n : ℕ) => 0 < n

@[simp] theorem mk_le_mk (n : ℕ) (k : ℕ) (hn : 0 < n) (hk : 0 < k) :
    { val := n, property := hn } ≤ { val := k, property := hk } ↔ n ≤ k :=
  iff.rfl

@[simp] theorem mk_lt_mk (n : ℕ) (k : ℕ) (hn : 0 < n) (hk : 0 < k) :
    { val := n, property := hn } < { val := k, property := hk } ↔ n < k :=
  iff.rfl

@[simp] theorem coe_le_coe (n : ℕ+) (k : ℕ+) : ↑n ≤ ↑k ↔ n ≤ k := iff.rfl

@[simp] theorem coe_lt_coe (n : ℕ+) (k : ℕ+) : ↑n < ↑k ↔ n < k := iff.rfl

@[simp] theorem pos (n : ℕ+) : 0 < ↑n := subtype.property n

theorem eq {m : ℕ+} {n : ℕ+} : ↑m = ↑n → m = n := subtype.eq

@[simp] theorem coe_inj {m : ℕ+} {n : ℕ+} : ↑m = ↑n ↔ m = n := set_coe.ext_iff

@[simp] theorem mk_coe (n : ℕ) (h : 0 < n) : ↑{ val := n, property := h } = n := rfl

protected instance add_comm_semigroup : add_comm_semigroup ℕ+ :=
  add_comm_semigroup.mk (fun (a b : ℕ+) => { val := ↑a + ↑b, property := sorry }) sorry sorry

@[simp] theorem add_coe (m : ℕ+) (n : ℕ+) : ↑(m + n) = ↑m + ↑n := rfl

protected instance coe_add_hom : is_add_hom coe := is_add_hom.mk add_coe

protected instance add_left_cancel_semigroup : add_left_cancel_semigroup ℕ+ :=
  add_left_cancel_semigroup.mk add_comm_semigroup.add add_comm_semigroup.add_assoc sorry

protected instance add_right_cancel_semigroup : add_right_cancel_semigroup ℕ+ :=
  add_right_cancel_semigroup.mk add_comm_semigroup.add add_comm_semigroup.add_assoc sorry

@[simp] theorem ne_zero (n : ℕ+) : ↑n ≠ 0 := ne_of_gt (subtype.property n)

theorem to_pnat'_coe {n : ℕ} : 0 < n → ↑(nat.to_pnat' n) = n := nat.succ_pred_eq_of_pos

@[simp] theorem coe_to_pnat' (n : ℕ+) : nat.to_pnat' ↑n = n := eq (to_pnat'_coe (pos n))

protected instance comm_monoid : comm_monoid ℕ+ :=
  comm_monoid.mk (fun (m n : ℕ+) => { val := subtype.val m * subtype.val n, property := sorry })
    sorry (nat.succ_pnat 0) sorry sorry sorry

theorem lt_add_one_iff {a : ℕ+} {b : ℕ+} : a < b + 1 ↔ a ≤ b := nat.lt_add_one_iff

theorem add_one_le_iff {a : ℕ+} {b : ℕ+} : a + 1 ≤ b ↔ a < b := nat.add_one_le_iff

@[simp] theorem one_le (n : ℕ+) : 1 ≤ n := subtype.property n

protected instance order_bot : order_bot ℕ+ :=
  order_bot.mk 1 partial_order.le partial_order.lt sorry sorry sorry sorry

@[simp] theorem bot_eq_zero : ⊥ = 1 := rfl

protected instance inhabited : Inhabited ℕ+ := { default := 1 }

-- Some lemmas that rewrite `pnat.mk n h`, for `n` an explicit numeral, into explicit numerals.

@[simp] theorem mk_one {h : 0 < 1} : { val := 1, property := h } = 1 := rfl

@[simp] theorem mk_bit0 (n : ℕ) {h : 0 < bit0 n} :
    { val := bit0 n, property := h } = bit0 { val := n, property := nat.pos_of_bit0_pos h } :=
  rfl

@[simp] theorem mk_bit1 (n : ℕ) {h : 0 < bit1 n} {k : 0 < n} :
    { val := bit1 n, property := h } = bit1 { val := n, property := k } :=
  rfl

-- Some lemmas that rewrite inequalities between explicit numerals in `pnat`

-- into the corresponding inequalities in `nat`.

-- TODO: perhaps this should not be attempted by `simp`,

-- and instead we should expect `norm_num` to take care of these directly?

-- TODO: these lemmas are perhaps incomplete:

-- * 1 is not represented as a bit0 or bit1

-- * strict inequalities?

@[simp] theorem bit0_le_bit0 (n : ℕ+) (m : ℕ+) : bit0 n ≤ bit0 m ↔ bit0 ↑n ≤ bit0 ↑m := iff.rfl

@[simp] theorem bit0_le_bit1 (n : ℕ+) (m : ℕ+) : bit0 n ≤ bit1 m ↔ bit0 ↑n ≤ bit1 ↑m := iff.rfl

@[simp] theorem bit1_le_bit0 (n : ℕ+) (m : ℕ+) : bit1 n ≤ bit0 m ↔ bit1 ↑n ≤ bit0 ↑m := iff.rfl

@[simp] theorem bit1_le_bit1 (n : ℕ+) (m : ℕ+) : bit1 n ≤ bit1 m ↔ bit1 ↑n ≤ bit1 ↑m := iff.rfl

@[simp] theorem one_coe : ↑1 = 1 := rfl

@[simp] theorem mul_coe (m : ℕ+) (n : ℕ+) : ↑(m * n) = ↑m * ↑n := rfl

protected instance coe_mul_hom : is_monoid_hom coe := is_monoid_hom.mk one_coe

@[simp] theorem coe_eq_one_iff {m : ℕ+} : ↑m = 1 ↔ m = 1 := sorry

@[simp] theorem coe_bit0 (a : ℕ+) : ↑(bit0 a) = bit0 ↑a := rfl

@[simp] theorem coe_bit1 (a : ℕ+) : ↑(bit1 a) = bit1 ↑a := rfl

@[simp] theorem pow_coe (m : ℕ+) (n : ℕ) : ↑(m ^ n) = ↑m ^ n := sorry

protected instance left_cancel_semigroup : left_cancel_semigroup ℕ+ :=
  left_cancel_semigroup.mk comm_monoid.mul comm_monoid.mul_assoc sorry

protected instance right_cancel_semigroup : right_cancel_semigroup ℕ+ :=
  right_cancel_semigroup.mk comm_monoid.mul comm_monoid.mul_assoc sorry

protected instance ordered_cancel_comm_monoid : ordered_cancel_comm_monoid ℕ+ :=
  ordered_cancel_comm_monoid.mk left_cancel_semigroup.mul left_cancel_semigroup.mul_assoc
    left_cancel_semigroup.mul_left_cancel comm_monoid.one comm_monoid.one_mul comm_monoid.mul_one
    comm_monoid.mul_comm right_cancel_semigroup.mul_right_cancel linear_order.le linear_order.lt
    linear_order.le_refl linear_order.le_trans linear_order.le_antisymm sorry sorry

protected instance distrib : distrib ℕ+ :=
  distrib.mk comm_monoid.mul add_comm_semigroup.add sorry sorry

/-- Subtraction a - b is defined in the obvious way when
  a > b, and by a - b = 1 if a ≤ b.
-/
protected instance has_sub : Sub ℕ+ := { sub := fun (a b : ℕ+) => nat.to_pnat' (↑a - ↑b) }

theorem sub_coe (a : ℕ+) (b : ℕ+) : ↑(a - b) = ite (b < a) (↑a - ↑b) 1 := sorry

theorem add_sub_of_lt {a : ℕ+} {b : ℕ+} : a < b → a + (b - a) = b := sorry

protected instance has_well_founded : has_well_founded ℕ+ := has_well_founded.mk Less sorry

/-- Strong induction on `pnat`. -/
theorem strong_induction_on {p : ℕ+ → Prop} (n : ℕ+)
    (h : ∀ (k : ℕ+), (∀ (m : ℕ+), m < k → p m) → p k) : p n :=
  sorry

/-- If `(n : pnat)` is different from `1`, then it is the successor of some `(k : pnat)`. -/
theorem exists_eq_succ_of_ne_one {n : ℕ+} (h1 : n ≠ 1) : ∃ (k : ℕ+), n = k + 1 := sorry

theorem case_strong_induction_on {p : ℕ+ → Prop} (a : ℕ+) (hz : p 1)
    (hi : ∀ (n : ℕ+), (∀ (m : ℕ+), m ≤ n → p m) → p (n + 1)) : p a :=
  sorry

/-- We define `m % k` and `m / k` in the same way as for `ℕ`
  except that when `m = n * k` we take `m % k = k` and
  `m / k = n - 1`.  This ensures that `m % k` is always positive
  and `m = (m % k) + k * (m / k)` in all cases.  Later we
  define a function `div_exact` which gives the usual `m / k`
  in the case where `k` divides `m`.
-/
def mod_div_aux : ℕ+ → ℕ → ℕ → ℕ+ × ℕ := sorry

theorem mod_div_aux_spec (k : ℕ+) (r : ℕ) (q : ℕ) (h : ¬(r = 0 ∧ q = 0)) :
    ↑(prod.fst (mod_div_aux k r q)) + ↑k * prod.snd (mod_div_aux k r q) = r + ↑k * q :=
  sorry

/-- `mod_div m k = (m % k, m / k)`.
  We define `m % k` and `m / k` in the same way as for `ℕ`
  except that when `m = n * k` we take `m % k = k` and
  `m / k = n - 1`.  This ensures that `m % k` is always positive
  and `m = (m % k) + k * (m / k)` in all cases.  Later we
  define a function `div_exact` which gives the usual `m / k`
  in the case where `k` divides `m`.
-/
def mod_div (m : ℕ+) (k : ℕ+) : ℕ+ × ℕ := mod_div_aux k (↑m % ↑k) (↑m / ↑k)

/-- We define `m % k` in the same way as for `ℕ`
  except that when `m = n * k` we take `m % k = k` This ensures that `m % k` is always positive.
-/
def mod (m : ℕ+) (k : ℕ+) : ℕ+ := prod.fst (mod_div m k)

/-- We define `m / k` in the same way as for `ℕ` except that when `m = n * k` we take
  `m / k = n - 1`. This ensures that `m = (m % k) + k * (m / k)` in all cases. Later we
  define a function `div_exact` which gives the usual `m / k` in the case where `k` divides `m`.
-/
def div (m : ℕ+) (k : ℕ+) : ℕ := prod.snd (mod_div m k)

theorem mod_add_div (m : ℕ+) (k : ℕ+) : ↑(mod m k) + ↑k * div m k = ↑m := sorry

theorem div_add_mod (m : ℕ+) (k : ℕ+) : ↑k * div m k + ↑(mod m k) = ↑m :=
  Eq.trans (add_comm (↑k * div m k) ↑(mod m k)) (mod_add_div m k)

theorem mod_coe (m : ℕ+) (k : ℕ+) : ↑(mod m k) = ite (↑m % ↑k = 0) (↑k) (↑m % ↑k) := sorry

theorem div_coe (m : ℕ+) (k : ℕ+) : div m k = ite (↑m % ↑k = 0) (Nat.pred (↑m / ↑k)) (↑m / ↑k) :=
  sorry

theorem mod_le (m : ℕ+) (k : ℕ+) : mod m k ≤ m ∧ mod m k ≤ k := sorry

theorem dvd_iff {k : ℕ+} {m : ℕ+} : k ∣ m ↔ ↑k ∣ ↑m := sorry

theorem dvd_iff' {k : ℕ+} {m : ℕ+} : k ∣ m ↔ mod m k = k := sorry

theorem le_of_dvd {m : ℕ+} {n : ℕ+} : m ∣ n → m ≤ n :=
  eq.mpr (id (Eq._oldrec (Eq.refl (m ∣ n → m ≤ n)) (propext dvd_iff')))
    fun (h : mod n m = m) =>
      eq.mpr (id (Eq._oldrec (Eq.refl (m ≤ n)) (Eq.symm h))) (and.left (mod_le n m))

/-- If `h : k | m`, then `k * (div_exact m k) = m`. Note that this is not equal to `m / k`. -/
def div_exact (m : ℕ+) (k : ℕ+) : ℕ+ := { val := Nat.succ (div m k), property := sorry }

theorem mul_div_exact {m : ℕ+} {k : ℕ+} (h : k ∣ m) : k * div_exact m k = m := sorry

theorem dvd_antisymm {m : ℕ+} {n : ℕ+} : m ∣ n → n ∣ m → m = n :=
  fun (hmn : m ∣ n) (hnm : n ∣ m) => le_antisymm (le_of_dvd hmn) (le_of_dvd hnm)

theorem dvd_one_iff (n : ℕ+) : n ∣ 1 ↔ n = 1 :=
  { mp := fun (h : n ∣ 1) => dvd_antisymm h (one_dvd n),
    mpr := fun (h : n = 1) => Eq.symm h ▸ dvd_refl 1 }

theorem pos_of_div_pos {n : ℕ+} {a : ℕ} (h : a ∣ ↑n) : 0 < a :=
  iff.mpr pos_iff_ne_zero
    (id
      fun (hzero : a = 0) =>
        ne_zero n (eq_zero_of_zero_dvd (eq.mp (Eq._oldrec (Eq.refl (a ∣ ↑n)) hzero) h)))

end Mathlib


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

import algebra.module.basic
import algebra.ring.equiv
import algebra.ring.prod
import data.set.finite
import group_theory.submonoid.centralizer
import group_theory.submonoid.membership

/-!
# Bundled subsemirings

We define bundled subsemirings and some standard constructions: `complete_lattice` structure,
`subtype` and `inclusion` ring homomorphisms, subsemiring `map`, `comap` and range (`srange`) of
a `ring_hom` etc.
-/

open_locale big_operators

universes u v w

section add_submonoid_with_one_class

/-- `add_submonoid_with_one_class S R` says `S` is a type of subsets `s ≤ R` that contain `0`, `1`,
and are closed under `(+)` -/
class add_submonoid_with_one_class (S : Type*) (R : out_param $ Type*)
  [add_monoid_with_one R] [set_like S R]
  extends add_submonoid_class S R, one_mem_class S R

variables {S R : Type*} [add_monoid_with_one R] [set_like S R] (s : S)

lemma nat_cast_mem [add_submonoid_with_one_class S R] (n : ℕ) : (n : R) ∈ s :=
by induction n; simp [zero_mem, add_mem, one_mem, *]

@[priority 74]
instance add_submonoid_with_one_class.to_add_monoid_with_one [add_submonoid_with_one_class S R] :
  add_monoid_with_one s :=
{ one := ⟨_, one_mem s⟩,
  nat_cast := λ n, ⟨n, nat_cast_mem s n⟩,
  nat_cast_zero := subtype.ext nat.cast_zero,
  nat_cast_succ := λ n, subtype.ext (nat.cast_succ _),
  .. add_submonoid_class.to_add_monoid s }

end add_submonoid_with_one_class

variables {R : Type u} {S : Type v} {T : Type w} [non_assoc_semiring R] (M : submonoid R)

section subsemiring_class

/-- `subsemiring_class S R` states that `S` is a type of subsets `s ⊆ R` that
are both a multiplicative and an additive submonoid. -/
class subsemiring_class (S : Type*) (R : out_param $ Type u) [non_assoc_semiring R] [set_like S R]
  extends submonoid_class S R :=
(add_mem : ∀ {s : S} {a b : R}, a ∈ s → b ∈ s → a + b ∈ s)
(zero_mem : ∀ (s : S), (0 : R) ∈ s)

@[priority 100] -- See note [lower instance priority]
instance subsemiring_class.add_submonoid_with_one_class (S : Type*) (R : out_param $ Type u)
  [non_assoc_semiring R] [set_like S R] [h : subsemiring_class S R] :
  add_submonoid_with_one_class S R :=
{ .. h }

variables [set_like S R] [hSR : subsemiring_class S R] (s : S)
include hSR

lemma coe_nat_mem (n : ℕ) : (n : R) ∈ s :=
by { rw ← nsmul_one, exact nsmul_mem (one_mem _) _ }

namespace subsemiring_class

/-- A subsemiring of a `non_assoc_semiring` inherits a `non_assoc_semiring` structure -/
@[priority 75] -- Prefer subclasses of `non_assoc_semiring` over subclasses of `subsemiring_class`.
instance to_non_assoc_semiring : non_assoc_semiring s :=
subtype.coe_injective.non_assoc_semiring coe rfl rfl (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)
  (λ _, rfl)

instance nontrivial [nontrivial R] : nontrivial s :=
nontrivial_of_ne 0 1 $ λ H, zero_ne_one (congr_arg subtype.val H)

instance no_zero_divisors [no_zero_divisors R] : no_zero_divisors s :=
{ eq_zero_or_eq_zero_of_mul_eq_zero := λ x y h,
  or.cases_on (eq_zero_or_eq_zero_of_mul_eq_zero $ subtype.ext_iff.mp h)
    (λ h, or.inl $ subtype.eq h) (λ h, or.inr $ subtype.eq h) }

/-- The natural ring hom from a subsemiring of semiring `R` to `R`. -/
def subtype : s →+* R :=
{ to_fun := coe, .. submonoid_class.subtype s, .. add_submonoid_class.subtype s }

@[simp] theorem coe_subtype : (subtype s : s → R) = coe := rfl

omit hSR

/-- A subsemiring of a `semiring` is a `semiring`. -/
@[priority 75] -- Prefer subclasses of `semiring` over subclasses of `subsemiring_class`.
instance to_semiring {R} [semiring R] [set_like S R] [subsemiring_class S R] : semiring s :=
subtype.coe_injective.semiring coe rfl rfl (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)
  (λ _, rfl)

@[simp, norm_cast] lemma coe_pow {R} [semiring R] [set_like S R] [subsemiring_class S R]
  (x : s) (n : ℕ) :
  ((x^n : s) : R) = (x^n : R) :=
begin
  induction n with n ih,
  { simp, },
  { simp [pow_succ, ih], },
end

/-- A subsemiring of a `comm_semiring` is a `comm_semiring`. -/
instance to_comm_semiring {R} [comm_semiring R] [set_like S R] [subsemiring_class S R] :
  comm_semiring s :=
subtype.coe_injective.comm_semiring coe
  rfl rfl (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _, rfl)

/-- A subsemiring of an `ordered_semiring` is an `ordered_semiring`. -/
instance to_ordered_semiring {R} [ordered_semiring R] [set_like S R] [subsemiring_class S R] :
  ordered_semiring s :=
subtype.coe_injective.ordered_semiring coe
  rfl rfl (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _, rfl)

/-- A subsemiring of an `ordered_comm_semiring` is an `ordered_comm_semiring`. -/
instance to_ordered_comm_semiring {R} [ordered_comm_semiring R] [set_like S R]
  [subsemiring_class S R] : ordered_comm_semiring s :=
subtype.coe_injective.ordered_comm_semiring coe
  rfl rfl (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _, rfl)

/-- A subsemiring of a `linear_ordered_semiring` is a `linear_ordered_semiring`. -/
instance to_linear_ordered_semiring {R} [linear_ordered_semiring R] [set_like S R]
  [subsemiring_class S R] : linear_ordered_semiring s :=
subtype.coe_injective.linear_ordered_semiring coe
  rfl rfl (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _, rfl)

/-! Note: currently, there is no `linear_ordered_comm_semiring`. -/

end subsemiring_class

end subsemiring_class

variables [non_assoc_semiring S] [non_assoc_semiring T]

set_option old_structure_cmd true

/-- A subsemiring of a semiring `R` is a subset `s` that is both a multiplicative and an additive
submonoid. -/
structure subsemiring (R : Type u) [non_assoc_semiring R] extends submonoid R, add_submonoid R

/-- Reinterpret a `subsemiring` as a `submonoid`. -/
add_decl_doc subsemiring.to_submonoid

/-- Reinterpret a `subsemiring` as an `add_submonoid`. -/
add_decl_doc subsemiring.to_add_submonoid

namespace subsemiring

instance : set_like (subsemiring R) R :=
{ coe := subsemiring.carrier,
  coe_injective' := λ p q h, by cases p; cases q; congr' }

instance : subsemiring_class (subsemiring R) R :=
{ zero_mem := zero_mem',
  add_mem := add_mem',
  one_mem := one_mem',
  mul_mem := mul_mem' }

@[simp]
lemma mem_carrier {s : subsemiring R} {x : R} : x ∈ s.carrier ↔ x ∈ s := iff.rfl

/-- Two subsemirings are equal if they have the same elements. -/
@[ext] theorem ext {S T : subsemiring R} (h : ∀ x, x ∈ S ↔ x ∈ T) : S = T := set_like.ext h

/-- Copy of a subsemiring with a new `carrier` equal to the old one. Useful to fix definitional
equalities.-/
protected def copy (S : subsemiring R) (s : set R) (hs : s = ↑S) : subsemiring R :=
{ carrier := s,
  ..S.to_add_submonoid.copy s hs,
  ..S.to_submonoid.copy s hs }

@[simp] lemma coe_copy (S : subsemiring R) (s : set R) (hs : s = ↑S) :
  (S.copy s hs : set R) = s := rfl

lemma copy_eq (S : subsemiring R) (s : set R) (hs : s = ↑S) : S.copy s hs = S :=
set_like.coe_injective hs

lemma to_submonoid_injective : function.injective (to_submonoid : subsemiring R → submonoid R)
| r s h := ext (set_like.ext_iff.mp h : _)

@[mono] lemma to_submonoid_strict_mono : strict_mono (to_submonoid : subsemiring R → submonoid R) :=
λ _ _, id

@[mono] lemma to_submonoid_mono : monotone (to_submonoid : subsemiring R → submonoid R) :=
to_submonoid_strict_mono.monotone

lemma to_add_submonoid_injective :
  function.injective (to_add_submonoid : subsemiring R → add_submonoid R)
| r s h := ext (set_like.ext_iff.mp h : _)

@[mono] lemma to_add_submonoid_strict_mono :
  strict_mono (to_add_submonoid : subsemiring R → add_submonoid R) := λ _ _, id

@[mono]
lemma to_add_submonoid_mono : monotone (to_add_submonoid : subsemiring R → add_submonoid R) :=
to_add_submonoid_strict_mono.monotone

/-- Construct a `subsemiring R` from a set `s`, a submonoid `sm`, and an additive
submonoid `sa` such that `x ∈ s ↔ x ∈ sm ↔ x ∈ sa`. -/
protected def mk' (s : set R) (sm : submonoid R) (hm : ↑sm = s)
  (sa : add_submonoid R) (ha : ↑sa = s) :
  subsemiring R :=
{ carrier := s,
  zero_mem' := ha ▸ sa.zero_mem,
  one_mem' := hm ▸ sm.one_mem,
  add_mem' := λ x y, by simpa only [← ha] using sa.add_mem,
  mul_mem' := λ x y, by simpa only [← hm] using sm.mul_mem }

@[simp] lemma coe_mk' {s : set R} {sm : submonoid R} (hm : ↑sm = s)
  {sa : add_submonoid R} (ha : ↑sa = s) :
  (subsemiring.mk' s sm hm sa ha : set R) = s := rfl

@[simp] lemma mem_mk' {s : set R} {sm : submonoid R} (hm : ↑sm = s)
  {sa : add_submonoid R} (ha : ↑sa = s) {x : R} :
  x ∈ subsemiring.mk' s sm hm sa ha ↔ x ∈ s :=
iff.rfl

@[simp] lemma mk'_to_submonoid {s : set R} {sm : submonoid R} (hm : ↑sm = s)
  {sa : add_submonoid R} (ha : ↑sa = s) :
  (subsemiring.mk' s sm hm sa ha).to_submonoid = sm :=
set_like.coe_injective hm.symm

@[simp] lemma mk'_to_add_submonoid {s : set R} {sm : submonoid R} (hm : ↑sm = s)
  {sa : add_submonoid R} (ha : ↑sa  =s) :
  (subsemiring.mk' s sm hm sa ha).to_add_submonoid = sa :=
set_like.coe_injective ha.symm

end subsemiring

namespace subsemiring

variables (s : subsemiring R)

/-- A subsemiring contains the semiring's 1. -/
protected theorem one_mem : (1 : R) ∈ s := one_mem s

/-- A subsemiring contains the semiring's 0. -/
protected theorem zero_mem : (0 : R) ∈ s := zero_mem s

/-- A subsemiring is closed under multiplication. -/
protected theorem mul_mem {x y : R} : x ∈ s → y ∈ s → x * y ∈ s := mul_mem

/-- A subsemiring is closed under addition. -/
protected theorem add_mem {x y : R} : x ∈ s → y ∈ s → x + y ∈ s := add_mem

/-- Product of a list of elements in a `subsemiring` is in the `subsemiring`. -/
lemma list_prod_mem {R : Type*} [semiring R] (s : subsemiring R) {l : list R} :
  (∀x ∈ l, x ∈ s) → l.prod ∈ s :=
list_prod_mem

/-- Sum of a list of elements in a `subsemiring` is in the `subsemiring`. -/
protected lemma list_sum_mem {l : list R} : (∀x ∈ l, x ∈ s) → l.sum ∈ s := list_sum_mem

/-- Product of a multiset of elements in a `subsemiring` of a `comm_semiring`
    is in the `subsemiring`. -/
protected lemma multiset_prod_mem {R} [comm_semiring R] (s : subsemiring R) (m : multiset R) :
  (∀a ∈ m, a ∈ s) → m.prod ∈ s :=
multiset_prod_mem m

/-- Sum of a multiset of elements in a `subsemiring` of a `semiring` is
in the `add_subsemiring`. -/
protected lemma multiset_sum_mem (m : multiset R) :
  (∀a ∈ m, a ∈ s) → m.sum ∈ s :=
multiset_sum_mem m

/-- Product of elements of a subsemiring of a `comm_semiring` indexed by a `finset` is in the
    subsemiring. -/
protected lemma prod_mem {R : Type*} [comm_semiring R] (s : subsemiring R)
  {ι : Type*} {t : finset ι} {f : ι → R} (h : ∀c ∈ t, f c ∈ s) :
  ∏ i in t, f i ∈ s :=
prod_mem h

/-- Sum of elements in an `subsemiring` of an `semiring` indexed by a `finset`
is in the `add_subsemiring`. -/
protected lemma sum_mem (s : subsemiring R)
  {ι : Type*} {t : finset ι} {f : ι → R} (h : ∀c ∈ t, f c ∈ s) :
  ∑ i in t, f i ∈ s :=
sum_mem h
/-- A subsemiring of a `non_assoc_semiring` inherits a `non_assoc_semiring` structure -/
instance to_non_assoc_semiring : non_assoc_semiring s :=
{ mul_zero := λ x, subtype.eq $ mul_zero x,
  zero_mul := λ x, subtype.eq $ zero_mul x,
  right_distrib := λ x y z, subtype.eq $ right_distrib x y z,
  left_distrib := λ x y z, subtype.eq $ left_distrib x y z,
  nat_cast := λ n, ⟨n, coe_nat_mem s n⟩,
  nat_cast_zero := by simp [nat.cast]; refl,
  nat_cast_succ := λ _, by simp [nat.cast]; refl,
  .. s.to_submonoid.to_mul_one_class, .. s.to_add_submonoid.to_add_comm_monoid }

@[simp, norm_cast] lemma coe_one : ((1 : s) : R) = (1 : R) := rfl
@[simp, norm_cast] lemma coe_zero : ((0 : s) : R) = (0 : R) := rfl
@[simp, norm_cast] lemma coe_add (x y : s) : ((x + y : s) : R) = (x + y : R) := rfl
@[simp, norm_cast] lemma coe_mul (x y : s) : ((x * y : s) : R) = (x * y : R) := rfl

instance nontrivial [nontrivial R] : nontrivial s :=
nontrivial_of_ne 0 1 $ λ H, zero_ne_one (congr_arg subtype.val H)

protected lemma pow_mem {R : Type*} [semiring R] (s : subsemiring R) {x : R} (hx : x ∈ s) (n : ℕ) :
  x^n ∈ s := pow_mem hx n

instance no_zero_divisors [no_zero_divisors R] : no_zero_divisors s :=
{ eq_zero_or_eq_zero_of_mul_eq_zero := λ x y h,
  or.cases_on (eq_zero_or_eq_zero_of_mul_eq_zero $ subtype.ext_iff.mp h)
    (λ h, or.inl $ subtype.eq h) (λ h, or.inr $ subtype.eq h) }

/-- A subsemiring of a `semiring` is a `semiring`. -/
instance to_semiring {R} [semiring R] (s : subsemiring R) : semiring s :=
{ ..s.to_non_assoc_semiring, ..s.to_submonoid.to_monoid }

@[simp, norm_cast] lemma coe_pow {R} [semiring R] (s : subsemiring R) (x : s) (n : ℕ) :
  ((x^n : s) : R) = (x^n : R) :=
begin
  induction n with n ih,
  { simp, },
  { simp [pow_succ, ih], },
end

/-- A subsemiring of a `comm_semiring` is a `comm_semiring`. -/
instance to_comm_semiring {R} [comm_semiring R] (s : subsemiring R) : comm_semiring s :=
{ mul_comm := λ _ _, subtype.eq $ mul_comm _ _, ..s.to_semiring}

/-- The natural ring hom from a subsemiring of semiring `R` to `R`. -/
def subtype : s →+* R :=
{ to_fun := coe, .. s.to_submonoid.subtype, .. s.to_add_submonoid.subtype }

@[simp] theorem coe_subtype : ⇑s.subtype = coe := rfl

/-- A subsemiring of an `ordered_semiring` is an `ordered_semiring`. -/
instance to_ordered_semiring {R} [ordered_semiring R] (s : subsemiring R) : ordered_semiring s :=
subtype.coe_injective.ordered_semiring coe
  rfl rfl (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _, rfl)

/-- A subsemiring of an `ordered_comm_semiring` is an `ordered_comm_semiring`. -/
instance to_ordered_comm_semiring {R} [ordered_comm_semiring R] (s : subsemiring R) :
  ordered_comm_semiring s :=
subtype.coe_injective.ordered_comm_semiring coe
  rfl rfl (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _, rfl)

/-- A subsemiring of a `linear_ordered_semiring` is a `linear_ordered_semiring`. -/
instance to_linear_ordered_semiring {R} [linear_ordered_semiring R] (s : subsemiring R) :
  linear_ordered_semiring s :=
subtype.coe_injective.linear_ordered_semiring coe
  rfl rfl (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _, rfl)

/-! Note: currently, there is no `linear_ordered_comm_semiring`. -/

protected lemma nsmul_mem {x : R} (hx : x ∈ s) (n : ℕ) :
  n • x ∈ s := nsmul_mem hx n

@[simp] lemma mem_to_submonoid {s : subsemiring R} {x : R} : x ∈ s.to_submonoid ↔ x ∈ s := iff.rfl
@[simp] lemma coe_to_submonoid (s : subsemiring R) : (s.to_submonoid : set R) = s := rfl
@[simp] lemma mem_to_add_submonoid {s : subsemiring R} {x : R} :
  x ∈ s.to_add_submonoid ↔ x ∈ s := iff.rfl
@[simp] lemma coe_to_add_submonoid (s : subsemiring R) : (s.to_add_submonoid : set R) = s := rfl

/-- The subsemiring `R` of the semiring `R`. -/
instance : has_top (subsemiring R) :=
⟨{ .. (⊤ : submonoid R), .. (⊤ : add_submonoid R) }⟩

@[simp] lemma mem_top (x : R) : x ∈ (⊤ : subsemiring R) := set.mem_univ x

@[simp] lemma coe_top : ((⊤ : subsemiring R) : set R) = set.univ := rfl

/-- The preimage of a subsemiring along a ring homomorphism is a subsemiring. -/
def comap (f : R →+* S) (s : subsemiring S) : subsemiring R :=
{ carrier := f ⁻¹' s,
  .. s.to_submonoid.comap (f : R →* S), .. s.to_add_submonoid.comap (f : R →+ S) }

@[simp]

@[simp]
lemma mem_comap {s : subsemiring S} {f : R →+* S} {x : R} : x ∈ s.comap f ↔ f x ∈ s := iff.rfl

lemma comap_comap (s : subsemiring T) (g : S →+* T) (f : R →+* S) :
  (s.comap g).comap f = s.comap (g.comp f) :=
rfl

/-- The image of a subsemiring along a ring homomorphism is a subsemiring. -/
def map (f : R →+* S) (s : subsemiring R) : subsemiring S :=
{ carrier := f '' s,
  .. s.to_submonoid.map (f : R →* S), .. s.to_add_submonoid.map (f : R →+ S) }

@[simp] lemma coe_map (f : R →+* S) (s : subsemiring R) : (s.map f : set S) = f '' s := rfl

@[simp] lemma mem_map {f : R →+* S} {s : subsemiring R} {y : S} :
  y ∈ s.map f ↔ ∃ x ∈ s, f x = y :=
set.mem_image_iff_bex

@[simp] lemma map_id : s.map (ring_hom.id R) = s :=
set_like.coe_injective $ set.image_id _

lemma map_map (g : S →+* T) (f : R →+* S) : (s.map f).map g = s.map (g.comp f) :=
set_like.coe_injective $ set.image_image _ _ _

lemma map_le_iff_le_comap {f : R →+* S} {s : subsemiring R} {t : subsemiring S} :
  s.map f ≤ t ↔ s ≤ t.comap f :=
set.image_subset_iff

lemma gc_map_comap (f : R →+* S) : galois_connection (map f) (comap f) :=
λ S T, map_le_iff_le_comap

/-- A subsemiring is isomorphic to its image under an injective function -/
noncomputable def equiv_map_of_injective
  (f : R →+* S) (hf : function.injective f) : s ≃+* s.map f :=
{ map_mul' := λ _ _, subtype.ext (f.map_mul _ _),
  map_add' := λ _ _, subtype.ext (f.map_add _ _),
  ..equiv.set.image f s hf }

@[simp] lemma coe_equiv_map_of_injective_apply
  (f : R →+* S) (hf : function.injective f) (x : s) :
  (equiv_map_of_injective s f hf x : S) = f x := rfl

end subsemiring

namespace ring_hom

variables (g : S →+* T) (f : R →+* S)

/-- The range of a ring homomorphism is a subsemiring. See Note [range copy pattern]. -/
def srange : subsemiring S :=
((⊤ : subsemiring R).map f).copy (set.range f) set.image_univ.symm

@[simp] lemma coe_srange : (f.srange : set S) = set.range f := rfl

@[simp] lemma mem_srange {f : R →+* S} {y : S} : y ∈ f.srange ↔ ∃ x, f x = y :=
iff.rfl

lemma srange_eq_map (f : R →+* S) : f.srange = (⊤ : subsemiring R).map f :=
by { ext, simp }

lemma mem_srange_self (f : R →+* S) (x : R) : f x ∈ f.srange :=
mem_srange.mpr ⟨x, rfl⟩

lemma map_srange : f.srange.map g = (g.comp f).srange :=
by simpa only [srange_eq_map] using (⊤ : subsemiring R).map_map g f

/-- The range of a morphism of semirings is a fintype, if the domain is a fintype.
Note: this instance can form a diamond with `subtype.fintype` in the
  presence of `fintype S`.-/
instance fintype_srange [fintype R] [decidable_eq S] (f : R →+* S) : fintype (srange f) :=
set.fintype_range f

end ring_hom

namespace subsemiring

instance : has_bot (subsemiring R) := ⟨(nat.cast_ring_hom R).srange⟩

instance : inhabited (subsemiring R) := ⟨⊥⟩

lemma coe_bot : ((⊥ : subsemiring R) : set R) = set.range (coe : ℕ → R) :=
(nat.cast_ring_hom R).coe_srange

lemma mem_bot {x : R} : x ∈ (⊥ : subsemiring R) ↔ ∃ n : ℕ, ↑n=x := ring_hom.mem_srange

/-- The inf of two subsemirings is their intersection. -/
instance : has_inf (subsemiring R) :=
⟨λ s t,
  { carrier := s ∩ t,
    .. s.to_submonoid ⊓ t.to_submonoid,
    .. s.to_add_submonoid ⊓ t.to_add_submonoid }⟩

@[simp] lemma coe_inf (p p' : subsemiring R) : ((p ⊓ p' : subsemiring R) : set R) = p ∩ p' := rfl

@[simp] lemma mem_inf {p p' : subsemiring R} {x : R} : x ∈ p ⊓ p' ↔ x ∈ p ∧ x ∈ p' := iff.rfl

instance : has_Inf (subsemiring R) :=
⟨λ s, subsemiring.mk' (⋂ t ∈ s, ↑t) (⨅ t ∈ s, subsemiring.to_submonoid t) (by simp)
  (⨅ t ∈ s, subsemiring.to_add_submonoid t) (by simp)⟩

@[simp, norm_cast] lemma coe_Inf (S : set (subsemiring R)) :
  ((Inf S : subsemiring R) : set R) = ⋂ s ∈ S, ↑s := rfl

lemma mem_Inf {S : set (subsemiring R)} {x : R} : x ∈ Inf S ↔ ∀ p ∈ S, x ∈ p := set.mem_Inter₂

@[simp] lemma Inf_to_submonoid (s : set (subsemiring R)) :
  (Inf s).to_submonoid = ⨅ t ∈ s, subsemiring.to_submonoid t :=
mk'_to_submonoid _ _

@[simp] lemma Inf_to_add_submonoid (s : set (subsemiring R)) :
  (Inf s).to_add_submonoid = ⨅ t ∈ s, subsemiring.to_add_submonoid t :=
mk'_to_add_submonoid _ _

/-- Subsemirings of a semiring form a complete lattice. -/
instance : complete_lattice (subsemiring R) :=
{ bot := (⊥),
  bot_le := λ s x hx, let ⟨n, hn⟩ := mem_bot.1 hx in hn ▸ coe_nat_mem s n,
  top := (⊤),
  le_top := λ s x hx, trivial,
  inf := (⊓),
  inf_le_left := λ s t x, and.left,
  inf_le_right := λ s t x, and.right,
  le_inf := λ s t₁ t₂ h₁ h₂ x hx, ⟨h₁ hx, h₂ hx⟩,
  .. complete_lattice_of_Inf (subsemiring R)
    (λ s, is_glb.of_image (λ s t,
      show (s : set R) ≤ t ↔ s ≤ t, from set_like.coe_subset_coe) is_glb_binfi)}

lemma eq_top_iff' (A : subsemiring R) : A = ⊤ ↔ ∀ x : R, x ∈ A :=
eq_top_iff.trans ⟨λ h m, h $ mem_top m, λ h m _, h m⟩

section center

/-- The center of a semiring `R` is the set of elements that commute with everything in `R` -/
def center (R) [semiring R] : subsemiring R :=
{ carrier := set.center R,
  zero_mem' := set.zero_mem_center R,
  add_mem' := λ a b, set.add_mem_center,
  .. submonoid.center R }

lemma coe_center (R) [semiring R] : ↑(center R) = set.center R := rfl

@[simp]
lemma center_to_submonoid (R) [semiring R] : (center R).to_submonoid = submonoid.center R := rfl

lemma mem_center_iff {R} [semiring R] {z : R} : z ∈ center R ↔ ∀ g, g * z = z * g :=
iff.rfl

instance decidable_mem_center {R} [semiring R] [decidable_eq R] [fintype R] :
  decidable_pred (∈ center R) :=
λ _, decidable_of_iff' _ mem_center_iff

@[simp] lemma center_eq_top (R) [comm_semiring R] : center R = ⊤ :=
set_like.coe_injective (set.center_eq_univ R)

/-- The center is commutative. -/
instance {R} [semiring R] : comm_semiring (center R) :=
{ ..submonoid.center.comm_monoid,
  ..(center R).to_semiring}

end center

section centralizer

/-- The centralizer of a set as subsemiring. -/
def centralizer {R} [semiring R] (s : set R) : subsemiring R :=
{ carrier := s.centralizer,
  zero_mem' := set.zero_mem_centralizer _,
  add_mem' := λ x y hx hy, set.add_mem_centralizer hx hy,
  ..submonoid.centralizer s }

@[simp, norm_cast]
lemma coe_centralizer {R} [semiring R] (s : set R) : (centralizer s : set R) = s.centralizer := rfl

lemma centralizer_to_submonoid {R} [semiring R] (s : set R) :
  (centralizer s).to_submonoid = submonoid.centralizer s := rfl

lemma mem_centralizer_iff {R} [semiring R] {s : set R} {z : R} :
  z ∈ centralizer s ↔ ∀ g ∈ s, g * z = z * g :=
iff.rfl

lemma centralizer_le {R} [semiring R] (s t : set R) (h : s ⊆ t) :
  centralizer t ≤ centralizer s :=
set.centralizer_subset h

@[simp]
lemma centralizer_univ {R} [semiring R] : centralizer set.univ = center R :=
set_like.ext' (set.centralizer_univ R)

end centralizer

/-- The `subsemiring` generated by a set. -/
def closure (s : set R) : subsemiring R := Inf {S | s ⊆ S}

lemma mem_closure {x : R} {s : set R} : x ∈ closure s ↔ ∀ S : subsemiring R, s ⊆ S → x ∈ S :=
mem_Inf

/-- The subsemiring generated by a set includes the set. -/
@[simp] lemma subset_closure {s : set R} : s ⊆ closure s := λ x hx, mem_closure.2 $ λ S hS, hS hx

lemma not_mem_of_not_mem_closure {s : set R} {P : R} (hP : P ∉ closure s) : P ∉ s :=
λ h, hP (subset_closure h)

/-- A subsemiring `S` includes `closure s` if and only if it includes `s`. -/
@[simp]
lemma closure_le {s : set R} {t : subsemiring R} : closure s ≤ t ↔ s ⊆ t :=
⟨set.subset.trans subset_closure, λ h, Inf_le h⟩

/-- Subsemiring closure of a set is monotone in its argument: if `s ⊆ t`,
then `closure s ≤ closure t`. -/
lemma closure_mono ⦃s t : set R⦄ (h : s ⊆ t) : closure s ≤ closure t :=
closure_le.2 $ set.subset.trans h subset_closure

lemma closure_eq_of_le {s : set R} {t : subsemiring R} (h₁ : s ⊆ t) (h₂ : t ≤ closure s) :
  closure s = t :=
le_antisymm (closure_le.2 h₁) h₂

lemma mem_map_equiv {f : R ≃+* S} {K : subsemiring R} {x : S} :
  x ∈ K.map (f : R →+* S) ↔ f.symm x ∈ K :=
@set.mem_image_equiv _ _ ↑K f.to_equiv x

lemma map_equiv_eq_comap_symm (f : R ≃+* S) (K : subsemiring R) :
  K.map (f : R →+* S) = K.comap f.symm :=
set_like.coe_injective (f.to_equiv.image_eq_preimage K)

lemma comap_equiv_eq_map_symm (f : R ≃+* S) (K : subsemiring S) :
  K.comap (f : R →+* S) = K.map f.symm :=
(map_equiv_eq_comap_symm f.symm K).symm

end subsemiring

namespace submonoid

/-- The additive closure of a submonoid is a subsemiring. -/
def subsemiring_closure (M : submonoid R) : subsemiring R :=
{ one_mem' := add_submonoid.mem_closure.mpr (λ y hy, hy M.one_mem),
  mul_mem' := λ x y, mul_mem_class.mul_mem_add_closure,
  ..add_submonoid.closure (M : set R)}

lemma subsemiring_closure_coe :
  (M.subsemiring_closure : set R) = add_submonoid.closure (M : set R) := rfl

lemma subsemiring_closure_to_add_submonoid :
  M.subsemiring_closure.to_add_submonoid = add_submonoid.closure (M : set R) := rfl

/-- The `subsemiring` generated by a multiplicative submonoid coincides with the
`subsemiring.closure` of the submonoid itself . -/
lemma subsemiring_closure_eq_closure : M.subsemiring_closure = subsemiring.closure (M : set R) :=
begin
  ext,
  refine ⟨λ hx, _, λ hx, (subsemiring.mem_closure.mp hx) M.subsemiring_closure (λ s sM, _)⟩;
  rintros - ⟨H1, rfl⟩;
  rintros - ⟨H2, rfl⟩,
  { exact add_submonoid.mem_closure.mp hx H1.to_add_submonoid H2 },
  { exact H2 sM }
end

end submonoid

namespace subsemiring

@[simp]
lemma closure_submonoid_closure (s : set R) : closure ↑(submonoid.closure s) = closure s :=
le_antisymm
  (closure_le.mpr (λ y hy, (submonoid.mem_closure.mp hy) (closure s).to_submonoid subset_closure))
  (closure_mono (submonoid.subset_closure))

/-- The elements of the subsemiring closure of `M` are exactly the elements of the additive closure
of a multiplicative submonoid `M`. -/
lemma coe_closure_eq (s : set R) :
  (closure s : set R) = add_submonoid.closure (submonoid.closure s : set R) :=
by simp [← submonoid.subsemiring_closure_to_add_submonoid, submonoid.subsemiring_closure_eq_closure]

lemma mem_closure_iff {s : set R} {x} :
  x ∈ closure s ↔ x ∈ add_submonoid.closure (submonoid.closure s : set R) :=
set.ext_iff.mp (coe_closure_eq s) x

@[simp]
lemma closure_add_submonoid_closure {s : set R} : closure ↑(add_submonoid.closure s) = closure s :=
begin
  ext x,
  refine ⟨λ hx, _, λ hx, closure_mono add_submonoid.subset_closure hx⟩,
  rintros - ⟨H, rfl⟩,
  rintros - ⟨J, rfl⟩,
  refine (add_submonoid.mem_closure.mp (mem_closure_iff.mp hx)) H.to_add_submonoid (λ y hy, _),
  refine (submonoid.mem_closure.mp hy) H.to_submonoid (λ z hz, _),
  exact (add_submonoid.mem_closure.mp hz) H.to_add_submonoid (λ w hw, J hw),
end

/-- An induction principle for closure membership. If `p` holds for `0`, `1`, and all elements
of `s`, and is preserved under addition and multiplication, then `p` holds for all elements
of the closure of `s`. -/
@[elab_as_eliminator]
lemma closure_induction {s : set R} {p : R → Prop} {x} (h : x ∈ closure s)
  (Hs : ∀ x ∈ s, p x) (H0 : p 0) (H1 : p 1)
  (Hadd : ∀ x y, p x → p y → p (x + y)) (Hmul : ∀ x y, p x → p y → p (x * y)) : p x :=
(@closure_le _ _ _ ⟨p, Hmul, H1, Hadd, H0⟩).2 Hs h

/-- An induction principle for closure membership for predicates with two arguments. -/
@[elab_as_eliminator]
lemma closure_induction₂ {s : set R} {p : R → R → Prop} {x} {y : R} (hx : x ∈ closure s)
  (hy : y ∈ closure s)
  (Hs : ∀ (x ∈ s) (y ∈ s), p x y)
  (H0_left : ∀ x, p 0 x)
  (H0_right : ∀ x, p x 0)
  (H1_left : ∀ x, p 1 x)
  (H1_right : ∀ x, p x 1)
  (Hadd_left : ∀ x₁ x₂ y, p x₁ y → p x₂ y → p (x₁ + x₂) y)
  (Hadd_right : ∀ x y₁ y₂, p x y₁ → p x y₂ → p x (y₁ + y₂))
  (Hmul_left : ∀ x₁ x₂ y, p x₁ y → p x₂ y → p (x₁ * x₂) y)
  (Hmul_right : ∀ x y₁ y₂, p x y₁ → p x y₂ → p x (y₁ * y₂))
  : p x y :=
closure_induction hx
  (λ x₁ x₁s, closure_induction hy (Hs x₁ x₁s) (H0_right x₁) (H1_right x₁) (Hadd_right x₁)
                                                                                (Hmul_right x₁))
  (H0_left y) (H1_left y) (λ z z', Hadd_left z z' y) (λ z z', Hmul_left z z' y)

lemma mem_closure_iff_exists_list {R} [semiring R] {s : set R} {x} : x ∈ closure s ↔
  ∃ L : list (list R), (∀ t ∈ L, ∀ y ∈ t, y ∈ s) ∧ (L.map list.prod).sum = x :=
⟨λ hx, add_submonoid.closure_induction (mem_closure_iff.1 hx)
  (λ x hx, suffices ∃ t : list R, (∀ y ∈ t, y ∈ s) ∧ t.prod = x,
    from let ⟨t, ht1, ht2⟩ := this in ⟨[t], list.forall_mem_singleton.2 ht1,
      by rw [list.map_singleton, list.sum_singleton, ht2]⟩,
    submonoid.closure_induction hx
      (λ x hx, ⟨[x], list.forall_mem_singleton.2 hx, one_mul x⟩)
      ⟨[], list.forall_mem_nil _, rfl⟩
      (λ x y ⟨t, ht1, ht2⟩ ⟨u, hu1, hu2⟩, ⟨t ++ u, list.forall_mem_append.2 ⟨ht1, hu1⟩,
        by rw [list.prod_append, ht2, hu2]⟩))
  ⟨[], list.forall_mem_nil _, rfl⟩
  (λ x y ⟨L, HL1, HL2⟩ ⟨M, HM1, HM2⟩, ⟨L ++ M, list.forall_mem_append.2 ⟨HL1, HM1⟩,
    by rw [list.map_append, list.sum_append, HL2, HM2]⟩),
λ ⟨L, HL1, HL2⟩, HL2 ▸ list_sum_mem (λ r hr, let ⟨t, ht1, ht2⟩ := list.mem_map.1 hr in
  ht2 ▸ list_prod_mem _ (λ y hy, subset_closure $ HL1 t ht1 y hy))⟩

variable (R)

/-- `closure` forms a Galois insertion with the coercion to set. -/
protected def gi : galois_insertion (@closure R _) coe :=
{ choice := λ s _, closure s,
  gc := λ s t, closure_le,
  le_l_u := λ s, subset_closure,
  choice_eq := λ s h, rfl }

variable {R}

/-- Closure of a subsemiring `S` equals `S`. -/
lemma closure_eq (s : subsemiring R) : closure (s : set R) = s := (subsemiring.gi R).l_u_eq s

@[simp] lemma closure_empty : closure (∅ : set R) = ⊥ := (subsemiring.gi R).gc.l_bot

@[simp] lemma closure_univ : closure (set.univ : set R) = ⊤ := @coe_top R _ ▸ closure_eq ⊤

lemma closure_union (s t : set R) : closure (s ∪ t) = closure s ⊔ closure t :=
(subsemiring.gi R).gc.l_sup

lemma closure_Union {ι} (s : ι → set R) : closure (⋃ i, s i) = ⨆ i, closure (s i) :=
(subsemiring.gi R).gc.l_supr

lemma closure_sUnion (s : set (set R)) : closure (⋃₀ s) = ⨆ t ∈ s, closure t :=
(subsemiring.gi R).gc.l_Sup

lemma map_sup (s t : subsemiring R) (f : R →+* S) : (s ⊔ t).map f = s.map f ⊔ t.map f :=
(gc_map_comap f).l_sup

lemma map_supr {ι : Sort*} (f : R →+* S) (s : ι → subsemiring R) :
  (supr s).map f = ⨆ i, (s i).map f :=
(gc_map_comap f).l_supr

lemma comap_inf (s t : subsemiring S) (f : R →+* S) : (s ⊓ t).comap f = s.comap f ⊓ t.comap f :=
(gc_map_comap f).u_inf

lemma comap_infi {ι : Sort*} (f : R →+* S) (s : ι → subsemiring S) :
  (infi s).comap f = ⨅ i, (s i).comap f :=
(gc_map_comap f).u_infi

@[simp] lemma map_bot (f : R →+* S) : (⊥ : subsemiring R).map f = ⊥ :=
(gc_map_comap f).l_bot

@[simp] lemma comap_top (f : R →+* S) : (⊤ : subsemiring S).comap f = ⊤ :=
(gc_map_comap f).u_top

/-- Given `subsemiring`s `s`, `t` of semirings `R`, `S` respectively, `s.prod t` is `s × t`
as a subsemiring of `R × S`. -/
def prod (s : subsemiring R) (t : subsemiring S) : subsemiring (R × S) :=
{ carrier := (s : set R) ×ˢ (t : set S),
  .. s.to_submonoid.prod t.to_submonoid, .. s.to_add_submonoid.prod t.to_add_submonoid}

@[norm_cast]
lemma coe_prod (s : subsemiring R) (t : subsemiring S) :
  (s.prod t : set (R × S)) = (s : set R) ×ˢ (t : set S) :=
rfl

lemma mem_prod {s : subsemiring R} {t : subsemiring S} {p : R × S} :
  p ∈ s.prod t ↔ p.1 ∈ s ∧ p.2 ∈ t := iff.rfl

@[mono] lemma prod_mono ⦃s₁ s₂ : subsemiring R⦄ (hs : s₁ ≤ s₂) ⦃t₁ t₂ : subsemiring S⦄
  (ht : t₁ ≤ t₂) : s₁.prod t₁ ≤ s₂.prod t₂ :=
set.prod_mono hs ht

lemma prod_mono_right (s : subsemiring R) : monotone (λ t : subsemiring S, s.prod t) :=
prod_mono (le_refl s)

lemma prod_mono_left (t : subsemiring S) : monotone (λ s : subsemiring R, s.prod t) :=
λ s₁ s₂ hs, prod_mono hs (le_refl t)

lemma prod_top (s : subsemiring R) :
  s.prod (⊤ : subsemiring S) = s.comap (ring_hom.fst R S) :=
ext $ λ x, by simp [mem_prod, monoid_hom.coe_fst]

lemma top_prod (s : subsemiring S) :
  (⊤ : subsemiring R).prod s = s.comap (ring_hom.snd R S) :=
ext $ λ x, by simp [mem_prod, monoid_hom.coe_snd]

@[simp]
lemma top_prod_top : (⊤ : subsemiring R).prod (⊤ : subsemiring S) = ⊤ :=
(top_prod _).trans $ comap_top _

/-- Product of subsemirings is isomorphic to their product as monoids. -/
def prod_equiv (s : subsemiring R) (t : subsemiring S) : s.prod t ≃+* s × t :=
{ map_mul' := λ x y, rfl, map_add' := λ x y, rfl, .. equiv.set.prod ↑s ↑t }

lemma mem_supr_of_directed {ι} [hι : nonempty ι] {S : ι → subsemiring R} (hS : directed (≤) S)
  {x : R} :
  x ∈ (⨆ i, S i) ↔ ∃ i, x ∈ S i :=
begin
  refine ⟨_, λ ⟨i, hi⟩, (set_like.le_def.1 $ le_supr S i) hi⟩,
  let U : subsemiring R := subsemiring.mk' (⋃ i, (S i : set R))
    (⨆ i, (S i).to_submonoid) (submonoid.coe_supr_of_directed $ hS.mono_comp _ (λ _ _, id))
    (⨆ i, (S i).to_add_submonoid) (add_submonoid.coe_supr_of_directed $ hS.mono_comp _ (λ _ _, id)),
  suffices : (⨆ i, S i) ≤ U, by simpa using @this x,
  exact supr_le (λ i x hx, set.mem_Union.2 ⟨i, hx⟩),
end

lemma coe_supr_of_directed {ι} [hι : nonempty ι] {S : ι → subsemiring R} (hS : directed (≤) S) :
  ((⨆ i, S i : subsemiring R) : set R) = ⋃ i, ↑(S i) :=
set.ext $ λ x, by simp [mem_supr_of_directed hS]

lemma mem_Sup_of_directed_on {S : set (subsemiring R)} (Sne : S.nonempty)
  (hS : directed_on (≤) S) {x : R} :
  x ∈ Sup S ↔ ∃ s ∈ S, x ∈ s :=
begin
  haveI : nonempty S := Sne.to_subtype,
  simp only [Sup_eq_supr', mem_supr_of_directed hS.directed_coe, set_coe.exists, subtype.coe_mk]
end

lemma coe_Sup_of_directed_on {S : set (subsemiring R)} (Sne : S.nonempty) (hS : directed_on (≤) S) :
  (↑(Sup S) : set R) = ⋃ s ∈ S, ↑s :=
set.ext $ λ x, by simp [mem_Sup_of_directed_on Sne hS]

end subsemiring

namespace ring_hom

variables [non_assoc_semiring T] {s : subsemiring R}
variables {σR σS : Type*}
variables [set_like σR R] [set_like σS S] [subsemiring_class σR R] [subsemiring_class σS S]

open subsemiring

/-- Restriction of a ring homomorphism to a subsemiring of the domain. -/
def restrict (f : R →+* S) (s : σR) : s →+* S := f.comp $ subsemiring_class.subtype s

@[simp] lemma restrict_apply (f : R →+* S) {s : σR} (x : s) : f.restrict s x = f x := rfl

/-- Restriction of a ring homomorphism to a subsemiring of the codomain. -/
def cod_restrict (f : R →+* S) (s : σS) (h : ∀ x, f x ∈ s) : R →+* s :=
{ to_fun := λ n, ⟨f n, h n⟩,
  .. (f : R →* S).cod_restrict s h,
  .. (f : R →+ S).cod_restrict s h }

/-- Restriction of a ring homomorphism to its range interpreted as a subsemiring.

This is the bundled version of `set.range_factorization`. -/
def srange_restrict (f : R →+* S) : R →+* f.srange :=
f.cod_restrict f.srange f.mem_srange_self

@[simp] lemma coe_srange_restrict (f : R →+* S) (x : R) :
  (f.srange_restrict x : S) = f x :=
rfl

lemma srange_restrict_surjective (f : R →+* S) : function.surjective f.srange_restrict :=
λ ⟨y, hy⟩, let ⟨x, hx⟩ := mem_srange.mp hy in ⟨x, subtype.ext hx⟩

lemma srange_top_iff_surjective {f : R →+* S} :
  f.srange = (⊤ : subsemiring S) ↔ function.surjective f :=
set_like.ext'_iff.trans $ iff.trans (by rw [coe_srange, coe_top]) set.range_iff_surjective

/-- The range of a surjective ring homomorphism is the whole of the codomain. -/
lemma srange_top_of_surjective (f : R →+* S) (hf : function.surjective f) :
  f.srange = (⊤ : subsemiring S) :=
srange_top_iff_surjective.2 hf

/-- The subsemiring of elements `x : R` such that `f x = g x` -/
def eq_slocus (f g : R →+* S) : subsemiring R :=
{ carrier := {x | f x = g x}, .. (f : R →* S).eq_mlocus g, .. (f : R →+ S).eq_mlocus g }

/-- If two ring homomorphisms are equal on a set, then they are equal on its subsemiring closure. -/
lemma eq_on_sclosure {f g : R →+* S} {s : set R} (h : set.eq_on f g s) :
  set.eq_on f g (closure s) :=
show closure s ≤ f.eq_slocus g, from closure_le.2 h

lemma eq_of_eq_on_stop {f g : R →+* S} (h : set.eq_on f g (⊤ : subsemiring R)) :
  f = g :=
ext $ λ x, h trivial

lemma eq_of_eq_on_sdense {s : set R} (hs : closure s = ⊤) {f g : R →+* S} (h : s.eq_on f g) :
  f = g :=
eq_of_eq_on_stop $ hs ▸ eq_on_sclosure h

lemma sclosure_preimage_le (f : R →+* S) (s : set S) :
  closure (f ⁻¹' s) ≤ (closure s).comap f :=
closure_le.2 $ λ x hx, set_like.mem_coe.2 $ mem_comap.2 $ subset_closure hx

/-- The image under a ring homomorphism of the subsemiring generated by a set equals
the subsemiring generated by the image of the set. -/
lemma map_sclosure (f : R →+* S) (s : set R) :
  (closure s).map f = closure (f '' s) :=
le_antisymm
  (map_le_iff_le_comap.2 $ le_trans (closure_mono $ set.subset_preimage_image _ _)
    (sclosure_preimage_le _ _))
  (closure_le.2 $ set.image_subset _ subset_closure)

end ring_hom

namespace subsemiring

open ring_hom

/-- The ring homomorphism associated to an inclusion of subsemirings. -/
def inclusion {S T : subsemiring R} (h : S ≤ T) : S →+* T :=
S.subtype.cod_restrict _ (λ x, h x.2)

@[simp] lemma srange_subtype (s : subsemiring R) : s.subtype.srange = s :=
set_like.coe_injective $ (coe_srange _).trans subtype.range_coe

@[simp]
lemma range_fst : (fst R S).srange = ⊤ :=
(fst R S).srange_top_of_surjective $ prod.fst_surjective

@[simp]
lemma range_snd : (snd R S).srange = ⊤ :=
(snd R S).srange_top_of_surjective $ prod.snd_surjective

@[simp]
lemma prod_bot_sup_bot_prod (s : subsemiring R) (t : subsemiring S) :
  (s.prod ⊥) ⊔ (prod ⊥ t) = s.prod t :=
le_antisymm (sup_le (prod_mono_right s bot_le) (prod_mono_left t bot_le)) $
assume p hp, prod.fst_mul_snd p ▸ mul_mem
  ((le_sup_left : s.prod ⊥ ≤ s.prod ⊥ ⊔ prod ⊥ t) ⟨hp.1, set_like.mem_coe.2 $ one_mem ⊥⟩)
  ((le_sup_right : prod ⊥ t ≤ s.prod ⊥ ⊔ prod ⊥ t) ⟨set_like.mem_coe.2 $ one_mem ⊥, hp.2⟩)

end subsemiring

namespace ring_equiv

variables {s t : subsemiring R}

/-- Makes the identity isomorphism from a proof two subsemirings of a multiplicative
    monoid are equal. -/
def subsemiring_congr (h : s = t) : s ≃+* t :=
{ map_mul' :=  λ _ _, rfl, map_add' := λ _ _, rfl, ..equiv.set_congr $ congr_arg _ h }

/-- Restrict a ring homomorphism with a left inverse to a ring isomorphism to its
`ring_hom.srange`. -/
def sof_left_inverse {g : S → R} {f : R →+* S} (h : function.left_inverse g f) :
  R ≃+* f.srange :=
{ to_fun := λ x, f.srange_restrict x,
  inv_fun := λ x, (g ∘ f.srange.subtype) x,
  left_inv := h,
  right_inv := λ x, subtype.ext $
    let ⟨x', hx'⟩ := ring_hom.mem_srange.mp x.prop in
    show f (g x) = x, by rw [←hx', h x'],
  ..f.srange_restrict }

@[simp] lemma sof_left_inverse_apply
  {g : S → R} {f : R →+* S} (h : function.left_inverse g f) (x : R) :
  ↑(sof_left_inverse h x) = f x := rfl

@[simp] lemma sof_left_inverse_symm_apply
  {g : S → R} {f : R →+* S} (h : function.left_inverse g f) (x : f.srange) :
  (sof_left_inverse h).symm x = g x := rfl

/-- Given an equivalence `e : R ≃+* S` of semirings and a subsemiring `s` of `R`,
`subsemiring_map e s` is the induced equivalence between `s` and `s.map e` -/
@[simps] def subsemiring_map (e : R ≃+* S) (s : subsemiring R) :
  s ≃+* s.map e.to_ring_hom :=
{ ..e.to_add_equiv.add_submonoid_map s.to_add_submonoid,
  ..e.to_mul_equiv.submonoid_map s.to_submonoid }

end ring_equiv

/-! ### Actions by `subsemiring`s

These are just copies of the definitions about `submonoid` starting from `submonoid.mul_action`.
The only new result is `subsemiring.module`.

When `R` is commutative, `algebra.of_subsemiring` provides a stronger result than those found in
this file, which uses the same scalar action.
-/
section actions

namespace subsemiring

variables {R' α β : Type*}

section non_assoc_semiring
variables [non_assoc_semiring R']

/-- The action by a subsemiring is the action by the underlying semiring. -/
instance [has_scalar R' α] (S : subsemiring R') : has_scalar S α := S.to_submonoid.has_scalar

lemma smul_def [has_scalar R' α] {S : subsemiring R'} (g : S) (m : α) : g • m = (g : R') • m := rfl

instance smul_comm_class_left
  [has_scalar R' β] [has_scalar α β] [smul_comm_class R' α β] (S : subsemiring R') :
  smul_comm_class S α β :=
S.to_submonoid.smul_comm_class_left

instance smul_comm_class_right
  [has_scalar α β] [has_scalar R' β] [smul_comm_class α R' β] (S : subsemiring R') :
  smul_comm_class α S β :=
S.to_submonoid.smul_comm_class_right

/-- Note that this provides `is_scalar_tower S R R` which is needed by `smul_mul_assoc`. -/
instance [has_scalar α β] [has_scalar R' α] [has_scalar R' β] [is_scalar_tower R' α β]
  (S : subsemiring R') :
  is_scalar_tower S α β :=
S.to_submonoid.is_scalar_tower

instance [has_scalar R' α] [has_faithful_smul R' α] (S : subsemiring R') :
  has_faithful_smul S α :=
S.to_submonoid.has_faithful_smul

/-- The action by a subsemiring is the action by the underlying semiring. -/
instance [has_zero α] [smul_with_zero R' α] (S : subsemiring R') : smul_with_zero S α :=
smul_with_zero.comp_hom _ S.subtype.to_monoid_with_zero_hom.to_zero_hom

end non_assoc_semiring

variables [semiring R']

/-- The action by a subsemiring is the action by the underlying semiring. -/
instance [mul_action R' α] (S : subsemiring R') : mul_action S α :=
S.to_submonoid.mul_action

/-- The action by a subsemiring is the action by the underlying semiring. -/
instance [add_monoid α] [distrib_mul_action R' α] (S : subsemiring R') : distrib_mul_action S α :=
S.to_submonoid.distrib_mul_action

/-- The action by a subsemiring is the action by the underlying semiring. -/
instance [monoid α] [mul_distrib_mul_action R' α] (S : subsemiring R') :
  mul_distrib_mul_action S α :=
S.to_submonoid.mul_distrib_mul_action

/-- The action by a subsemiring is the action by the underlying semiring. -/
instance [has_zero α] [mul_action_with_zero R' α] (S : subsemiring R') : mul_action_with_zero S α :=
mul_action_with_zero.comp_hom _ S.subtype.to_monoid_with_zero_hom

/-- The action by a subsemiring is the action by the underlying semiring. -/
instance [add_comm_monoid α] [module R' α] (S : subsemiring R') : module S α :=
{ smul := (•), .. module.comp_hom _ S.subtype }

/-- The center of a semiring acts commutatively on that semiring. -/
instance center.smul_comm_class_left : smul_comm_class (center R') R' R' :=
submonoid.center.smul_comm_class_left

/-- The center of a semiring acts commutatively on that semiring. -/
instance center.smul_comm_class_right : smul_comm_class R' (center R') R' :=
submonoid.center.smul_comm_class_right

/-- If all the elements of a set `s` commute, then `closure s` is a commutative monoid. -/
def closure_comm_semiring_of_comm {s : set R'} (hcomm : ∀ (a ∈ s) (b ∈ s), a * b = b * a) :
  comm_semiring (closure s) :=
{ mul_comm := λ x y,
  begin
    ext,
    simp only [subsemiring.coe_mul],
    refine closure_induction₂ x.prop y.prop hcomm
    (λ x, by simp only [zero_mul, mul_zero])
    (λ x, by simp only [zero_mul, mul_zero])
    (λ x, by simp only [one_mul, mul_one])
    (λ x, by simp only [one_mul, mul_one])
    (λ x y z h₁ h₂, by simp only [add_mul, mul_add, h₁, h₂])
    (λ x y z h₁ h₂, by simp only [add_mul, mul_add, h₁, h₂])
    (λ x y z h₁ h₂, by rw [mul_assoc, h₂, ←mul_assoc, h₁, mul_assoc])
    (λ x y z h₁ h₂, by rw [←mul_assoc, h₁, mul_assoc, h₂, ←mul_assoc])
  end,
  ..(closure s).to_semiring }

end subsemiring

end actions

-- While this definition is not about `subsemiring`s, this is the earliest we have
-- both `ordered_semiring` and `submonoid` available.

/-- Submonoid of positive elements of an ordered semiring. -/
def pos_submonoid (R : Type*) [ordered_semiring R] [nontrivial R] : submonoid R :=
{ carrier := {x | 0 < x},
  one_mem' := show (0 : R) < 1, from zero_lt_one,
  mul_mem' := λ x y (hx : 0 < x) (hy : 0 < y), mul_pos hx hy }

@[simp] lemma mem_pos_monoid {R : Type*} [ordered_semiring R] [nontrivial R] (u : Rˣ) :
  ↑u ∈ pos_submonoid R ↔ (0 : R) < u := iff.rfl