/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import algebra.category.Module.basic
import category_theory.linear
import category_theory.preadditive.additive_functor
import category_theory.preadditive.yoneda

/-!
# The Yoneda embedding for `R`-linear categories

The Yoneda embedding for `R`-linear categories `C`,
sends an object `X : C` to the `Module R`-valued presheaf on `C`,
with value on `Y : Cᵒᵖ` given by `Module.of R (unop Y ⟶ X)`.

TODO: `linear_yoneda R C` is `R`-linear.
TODO: In fact, `linear_yoneda` itself is additive and `R`-linear.
-/

universes w v u

open opposite

namespace category_theory

variables (R : Type w) [ring R] (C : Type u) [category.{v} C] [preadditive C] [linear R C]

/-- The Yoneda embedding for `R`-linear categories `C`,
sending an object `X : C` to the `Module R`-valued presheaf on `C`,
with value on `Y : Cᵒᵖ` given by `Module.of R (unop Y ⟶ X)`. -/
@[simps]
def linear_yoneda : C ⥤ Cᵒᵖ ⥤ Module R :=
{ obj := λ X,
  { obj := λ Y, Module.of R (unop Y ⟶ X),
    map := λ Y Y' f, linear.left_comp R _ f.unop,
    map_comp' := λ _ _ _ f g, begin ext, dsimp, erw [category.assoc] end,
    map_id' := λ Y, begin ext, dsimp, erw [category.id_comp] end },
  map := λ X X' f, { app := λ Y, linear.right_comp R _ f } }.

/-- The Yoneda embedding for `R`-linear categories `C`,
sending an object `Y : Cᵒᵖ` to the `Module R`-valued copresheaf on `C`,
with value on `X : C` given by `Module.of R (unop Y ⟶ X)`. -/
@[simps]
def linear_coyoneda : Cᵒᵖ ⥤ C ⥤ Module R :=
{ obj := λ Y,
  { obj := λ X, Module.of R (unop Y ⟶ X),
    map := λ Y Y', linear.right_comp _ _,
    map_id' := λ Y, by { ext, exact category.comp_id _ },
    map_comp' := λ _ _ _ f g, by { ext, exact eq.symm (category.assoc _ _ _) } },
  map := λ Y Y' f, { app := λ X, linear.left_comp _ _ f.unop } }

instance linear_yoneda_obj_additive (X : C) : ((linear_yoneda R C).obj X).additive := {}
instance linear_coyoneda_obj_additive (Y : Cᵒᵖ) : ((linear_coyoneda R C).obj Y).additive := {}

@[simp] lemma whiskering_linear_yoneda : linear_yoneda R C ⋙
  (whiskering_right _ _ _).obj (forget (Module.{v} R)) = yoneda :=
rfl

@[simp] lemma whiskering_linear_yoneda₂ : linear_yoneda R C ⋙
  (whiskering_right _ _ _).obj (forget₂ (Module.{v} R) AddCommGroup.{v}) = preadditive_yoneda :=
rfl

@[simp] lemma whiskering_linear_coyoneda : linear_coyoneda R C ⋙
  (whiskering_right _ _ _).obj (forget (Module.{v} R)) = coyoneda :=
rfl

@[simp] lemma whiskering_linear_coyoneda₂ : linear_coyoneda R C ⋙
  (whiskering_right _ _ _).obj (forget₂ (Module.{v} R) AddCommGroup.{v}) = preadditive_coyoneda :=
rfl

end category_theory


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

import ring_theory.witt_vector.basic
import ring_theory.witt_vector.is_poly

/-!
## The Verschiebung operator

## References

* [Hazewinkel, *Witt Vectors*][Haze09]

* [Commelin and Lewis, *Formalizing the Ring of Witt Vectors*][CL21]
-/

namespace witt_vector
open mv_polynomial

variables {p : ℕ} {R S : Type*} [hp : fact p.prime] [comm_ring R] [comm_ring S]
local notation `𝕎` := witt_vector p -- type as `\bbW`

noncomputable theory

/--
`verschiebung_fun x` shifts the coefficients of `x` up by one,
by inserting 0 as the 0th coefficient.
`x.coeff i` then becomes `(verchiebung_fun x).coeff (i + 1)`.

`verschiebung_fun` is the underlying function of the additive monoid hom `witt_vector.verschiebung`.
-/
def verschiebung_fun (x : 𝕎 R) : 𝕎 R :=
mk p $ λ n, if n = 0 then 0 else x.coeff (n - 1)

lemma verschiebung_fun_coeff (x : 𝕎 R) (n : ℕ) :
  (verschiebung_fun x).coeff n = if n = 0 then 0 else x.coeff (n - 1) :=
by rw [verschiebung_fun, coeff_mk]

lemma verschiebung_fun_coeff_zero (x : 𝕎 R) :
  (verschiebung_fun x).coeff 0 = 0 :=
by rw [verschiebung_fun_coeff, if_pos rfl]

@[simp] lemma verschiebung_fun_coeff_succ (x : 𝕎 R) (n : ℕ) :
  (verschiebung_fun x).coeff n.succ = x.coeff n := rfl

include hp

@[ghost_simps] lemma ghost_component_zero_verschiebung_fun (x : 𝕎 R) :
  ghost_component 0 (verschiebung_fun x) = 0 :=
by rw [ghost_component_apply, aeval_witt_polynomial, finset.range_one, finset.sum_singleton,
       verschiebung_fun_coeff_zero, pow_zero, pow_zero, pow_one, one_mul]

@[ghost_simps] lemma ghost_component_verschiebung_fun (x : 𝕎 R) (n : ℕ) :
  ghost_component (n + 1) (verschiebung_fun x) = p * ghost_component n x :=
begin
  simp only [ghost_component_apply, aeval_witt_polynomial],
  rw [finset.sum_range_succ', verschiebung_fun_coeff, if_pos rfl, zero_pow (pow_pos hp.1.pos _),
      mul_zero, add_zero, finset.mul_sum, finset.sum_congr rfl],
  rintro i -,
  simp only [pow_succ, mul_assoc, verschiebung_fun_coeff, if_neg (nat.succ_ne_zero i),
    nat.succ_sub_succ, tsub_zero]
end

omit hp

/--
The 0th Verschiebung polynomial is 0. For `n > 0`, the `n`th Verschiebung polynomial is the
variable `X (n-1)`.
-/
def verschiebung_poly (n : ℕ) : mv_polynomial ℕ ℤ :=
if n = 0 then 0 else X (n-1)

@[simp] lemma verschiebung_poly_zero :
  verschiebung_poly 0 = 0 := rfl

lemma aeval_verschiebung_poly' (x : 𝕎 R) (n : ℕ) :
  aeval x.coeff (verschiebung_poly n) = (verschiebung_fun x).coeff n :=
begin
  cases n,
  { simp only [verschiebung_poly, verschiebung_fun_coeff_zero, if_pos rfl, alg_hom.map_zero] },
  { rw [verschiebung_poly, verschiebung_fun_coeff_succ, if_neg (n.succ_ne_zero),
        aeval_X, nat.succ_eq_add_one, add_tsub_cancel_right] }
end

variable (p)

/--
`witt_vector.verschiebung` has polynomial structure given by `witt_vector.verschiebung_poly`.
-/
@[is_poly] lemma verschiebung_fun_is_poly : is_poly p (λ R _Rcr, @verschiebung_fun p R _Rcr) :=
begin
  use verschiebung_poly,
  simp only [aeval_verschiebung_poly', eq_self_iff_true, forall_3_true_iff]
end

variable {p}
include hp

/--
`verschiebung x` shifts the coefficients of `x` up by one, by inserting 0 as the 0th coefficient.
`x.coeff i` then becomes `(verchiebung x).coeff (i + 1)`.

This is a additive monoid hom with underlying function `verschiebung_fun`.
-/
noncomputable
def verschiebung : 𝕎 R →+ 𝕎 R :=
{ to_fun := verschiebung_fun,
  map_zero' :=
  by ext ⟨⟩; rw [verschiebung_fun_coeff]; simp only [if_true, eq_self_iff_true, zero_coeff, if_t_t],
  map_add' := by { ghost_calc _ _, rintro ⟨⟩; ghost_simp } }

omit hp

/-- `witt_vector.verschiebung` is a polynomial function. -/
@[is_poly] lemma verschiebung_is_poly : is_poly p (λ R _Rcr, @verschiebung p R hp _Rcr) :=
verschiebung_fun_is_poly p

include hp

/-- verschiebung is a natural transformation -/
@[simp] lemma map_verschiebung (f : R →+* S) (x : 𝕎 R) :
  map f (verschiebung x) = verschiebung (map f x) :=
by { ext ⟨-, -⟩, exact f.map_zero, refl }

@[ghost_simps] lemma ghost_component_zero_verschiebung (x : 𝕎 R) :
  ghost_component 0 (verschiebung x) = 0 :=
ghost_component_zero_verschiebung_fun _

@[ghost_simps] lemma ghost_component_verschiebung (x : 𝕎 R) (n : ℕ) :
  ghost_component (n + 1) (verschiebung x) = p * ghost_component n x :=
ghost_component_verschiebung_fun _ _

@[simp] lemma verschiebung_coeff_zero (x : 𝕎 R) :
  (verschiebung x).coeff 0 = 0 := rfl

-- simp_nf complains if this is simp
lemma verschiebung_coeff_add_one (x : 𝕎 R) (n : ℕ) :
  (verschiebung x).coeff (n + 1) = x.coeff n := rfl

@[simp] lemma verschiebung_coeff_succ (x : 𝕎 R) (n : ℕ) :
  (verschiebung x).coeff n.succ = x.coeff n := rfl

lemma aeval_verschiebung_poly (x : 𝕎 R) (n : ℕ) :
  aeval x.coeff (verschiebung_poly n) = (verschiebung x).coeff n :=
aeval_verschiebung_poly' x n

@[simp]
lemma bind₁_verschiebung_poly_witt_polynomial (n : ℕ) :
  bind₁ verschiebung_poly (witt_polynomial p ℤ n) =
  if n = 0 then 0 else p * witt_polynomial p ℤ (n-1) :=
begin
  apply mv_polynomial.funext,
  intro x,
  split_ifs with hn,
  { simp only [hn, verschiebung_poly_zero, witt_polynomial_zero, bind₁_X_right] },
  { obtain ⟨n, rfl⟩ := nat.exists_eq_succ_of_ne_zero hn,
    rw [nat.succ_eq_add_one, add_tsub_cancel_right, ring_hom.map_mul,
        map_nat_cast, hom_bind₁],
    calc  _
        = ghost_component (n + 1) (verschiebung $ mk p x) : _
    ... = _ : _,
    { apply eval₂_hom_congr (ring_hom.ext_int _ _) _ rfl,
      simp only [←aeval_verschiebung_poly, coeff_mk],
      funext k,
      exact eval₂_hom_congr (ring_hom.ext_int _ _) rfl rfl },
    { rw [ghost_component_verschiebung], refl } }
end

end witt_vector


/-
Copyright (c) 2014 Jeremy Avigad. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jeremy Avigad, Leonardo de Moura, Floris van Doorn, Amelia Livingston, Yury Kudryashov,
Neil Strickland, Aaron Anderson

! This file was ported from Lean 3 source module algebra.divisibility.basic
! leanprover-community/mathlib commit 448144f7ae193a8990cb7473c9e9a01990f64ac7
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Hom.Group

/-!
# Divisibility

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

This file defines the basics of the divisibility relation in the context of `(comm_)` `monoid`s.

## Main definitions

 * `monoid.has_dvd`

## Implementation notes

The divisibility relation is defined for all monoids, and as such, depends on the order of
  multiplication if the monoid is not commutative. There are two possible conventions for
  divisibility in the noncommutative context, and this relation follows the convention for ordinals,
  so `a | b` is defined as `∃ c, b = a * c`.

## Tags

divisibility, divides
-/

variable {α : Type _}

section Semigroup

variable [Semigroup α] {a b c : α}

#print semigroupDvd /-
/-- There are two possible conventions for divisibility, which coincide in a `comm_monoid`.
    This matches the convention for ordinals. -/
instance (priority := 100) semigroupDvd : Dvd α :=
  Dvd.mk fun a b => ∃ c, b = a * c
#align semigroup_has_dvd semigroupDvd
-/

/- warning: dvd.intro -> Dvd.intro is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {a : α} {b : α} (c : α), (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α _inst_1)) a c) b) -> (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a b)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {a : α} {b : α} (c : α), (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α _inst_1)) a c) b) -> (Dvd.dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a b)
Case conversion may be inaccurate. Consider using '#align dvd.intro Dvd.introₓ'. -/
-- TODO: this used to not have `c` explicit, but that seems to be important
--       for use with tactics, similar to `exists.intro`
theorem Dvd.intro (c : α) (h : a * c = b) : a ∣ b :=
  Exists.intro c h.symm
#align dvd.intro Dvd.intro

/- warning: dvd_of_mul_right_eq -> dvd_of_mul_right_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {a : α} {b : α} (c : α), (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α _inst_1)) a c) b) -> (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a b)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {a : α} {b : α} (c : α), (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α _inst_1)) a c) b) -> (Dvd.dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a b)
Case conversion may be inaccurate. Consider using '#align dvd_of_mul_right_eq dvd_of_mul_right_eqₓ'. -/
alias Dvd.intro ← dvd_of_mul_right_eq
#align dvd_of_mul_right_eq dvd_of_mul_right_eq

/- warning: exists_eq_mul_right_of_dvd -> exists_eq_mul_right_of_dvd is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {a : α} {b : α}, (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a b) -> (Exists.{succ u1} α (fun (c : α) => Eq.{succ u1} α b (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α _inst_1)) a c)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {a : α} {b : α}, (Dvd.dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a b) -> (Exists.{succ u1} α (fun (c : α) => Eq.{succ u1} α b (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α _inst_1)) a c)))
Case conversion may be inaccurate. Consider using '#align exists_eq_mul_right_of_dvd exists_eq_mul_right_of_dvdₓ'. -/
theorem exists_eq_mul_right_of_dvd (h : a ∣ b) : ∃ c, b = a * c :=
  h
#align exists_eq_mul_right_of_dvd exists_eq_mul_right_of_dvd

/- warning: dvd.elim -> Dvd.elim is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {P : Prop} {a : α} {b : α}, (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a b) -> (forall (c : α), (Eq.{succ u1} α b (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α _inst_1)) a c)) -> P) -> P
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {P : Prop} {a : α} {b : α}, (Dvd.dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a b) -> (forall (c : α), (Eq.{succ u1} α b (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α _inst_1)) a c)) -> P) -> P
Case conversion may be inaccurate. Consider using '#align dvd.elim Dvd.elimₓ'. -/
theorem Dvd.elim {P : Prop} {a b : α} (H₁ : a ∣ b) (H₂ : ∀ c, b = a * c → P) : P :=
  Exists.elim H₁ H₂
#align dvd.elim Dvd.elim

attribute [local simp] mul_assoc mul_comm mul_left_comm

#print dvd_trans /-
@[trans]
theorem dvd_trans : a ∣ b → b ∣ c → a ∣ c
  | ⟨d, h₁⟩, ⟨e, h₂⟩ => ⟨d * e, h₁ ▸ h₂.trans <| mul_assoc a d e⟩
#align dvd_trans dvd_trans
-/

alias dvd_trans ← Dvd.Dvd.trans
#align has_dvd.dvd.trans Dvd.Dvd.trans

instance : IsTrans α (· ∣ ·) :=
  ⟨fun a b c => dvd_trans⟩

/- warning: dvd_mul_right -> dvd_mul_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] (a : α) (b : α), Dvd.Dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α _inst_1)) a b)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] (a : α) (b : α), Dvd.dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α _inst_1)) a b)
Case conversion may be inaccurate. Consider using '#align dvd_mul_right dvd_mul_rightₓ'. -/
@[simp]
theorem dvd_mul_right (a b : α) : a ∣ a * b :=
  Dvd.intro b rfl
#align dvd_mul_right dvd_mul_right

/- warning: dvd_mul_of_dvd_left -> dvd_mul_of_dvd_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {a : α} {b : α}, (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a b) -> (forall (c : α), Dvd.Dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α _inst_1)) b c))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {a : α} {b : α}, (Dvd.dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a b) -> (forall (c : α), Dvd.dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α _inst_1)) b c))
Case conversion may be inaccurate. Consider using '#align dvd_mul_of_dvd_left dvd_mul_of_dvd_leftₓ'. -/
theorem dvd_mul_of_dvd_left (h : a ∣ b) (c : α) : a ∣ b * c :=
  h.trans (dvd_mul_right b c)
#align dvd_mul_of_dvd_left dvd_mul_of_dvd_left

alias dvd_mul_of_dvd_left ← Dvd.Dvd.mul_right
#align has_dvd.dvd.mul_right Dvd.Dvd.mul_right

/- warning: dvd_of_mul_right_dvd -> dvd_of_mul_right_dvd is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {a : α} {b : α} {c : α}, (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α _inst_1) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α _inst_1)) a b) c) -> (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a c)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Semigroup.{u1} α] {a : α} {b : α} {c : α}, (Dvd.dvd.{u1} α (semigroupDvd.{u1} α _inst_1) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α _inst_1)) a b) c) -> (Dvd.dvd.{u1} α (semigroupDvd.{u1} α _inst_1) a c)
Case conversion may be inaccurate. Consider using '#align dvd_of_mul_right_dvd dvd_of_mul_right_dvdₓ'. -/
theorem dvd_of_mul_right_dvd (h : a * b ∣ c) : a ∣ c :=
  (dvd_mul_right a b).trans h
#align dvd_of_mul_right_dvd dvd_of_mul_right_dvd

section map_dvd

variable {M N : Type _} [Monoid M] [Monoid N]

/- warning: map_dvd -> map_dvd is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} {N : Type.{u2}} [_inst_2 : Monoid.{u1} M] [_inst_3 : Monoid.{u2} N] {F : Type.{u3}} [_inst_4 : MulHomClass.{u3, u1, u2} F M N (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_2)) (MulOneClass.toHasMul.{u2} N (Monoid.toMulOneClass.{u2} N _inst_3))] (f : F) {a : M} {b : M}, (Dvd.Dvd.{u1} M (semigroupDvd.{u1} M (Monoid.toSemigroup.{u1} M _inst_2)) a b) -> (Dvd.Dvd.{u2} N (semigroupDvd.{u2} N (Monoid.toSemigroup.{u2} N _inst_3)) (coeFn.{succ u3, max (succ u1) (succ u2)} F (fun (_x : F) => M -> N) (FunLike.hasCoeToFun.{succ u3, succ u1, succ u2} F M (fun (_x : M) => N) (MulHomClass.toFunLike.{u3, u1, u2} F M N (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_2)) (MulOneClass.toHasMul.{u2} N (Monoid.toMulOneClass.{u2} N _inst_3)) _inst_4)) f a) (coeFn.{succ u3, max (succ u1) (succ u2)} F (fun (_x : F) => M -> N) (FunLike.hasCoeToFun.{succ u3, succ u1, succ u2} F M (fun (_x : M) => N) (MulHomClass.toFunLike.{u3, u1, u2} F M N (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_2)) (MulOneClass.toHasMul.{u2} N (Monoid.toMulOneClass.{u2} N _inst_3)) _inst_4)) f b))
but is expected to have type
  forall {M : Type.{u2}} {N : Type.{u1}} [_inst_2 : Monoid.{u2} M] [_inst_3 : Monoid.{u1} N] {F : Type.{u3}} [_inst_4 : MulHomClass.{u3, u2, u1} F M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3))] (f : F) {a : M} {b : M}, (Dvd.dvd.{u2} M (semigroupDvd.{u2} M (Monoid.toSemigroup.{u2} M _inst_2)) a b) -> (Dvd.dvd.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) a) (semigroupDvd.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) a) (Monoid.toSemigroup.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) a) _inst_3)) (FunLike.coe.{succ u3, succ u2, succ u1} F M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) _x) (MulHomClass.toFunLike.{u3, u2, u1} F M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3)) _inst_4) f a) (FunLike.coe.{succ u3, succ u2, succ u1} F M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) _x) (MulHomClass.toFunLike.{u3, u2, u1} F M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3)) _inst_4) f b))
Case conversion may be inaccurate. Consider using '#align map_dvd map_dvdₓ'. -/
theorem map_dvd {F : Type _} [MulHomClass F M N] (f : F) {a b} : a ∣ b → f a ∣ f b
  | ⟨c, h⟩ => ⟨f c, h.symm ▸ map_mul f a c⟩
#align map_dvd map_dvd

/- warning: mul_hom.map_dvd -> MulHom.map_dvd is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} {N : Type.{u2}} [_inst_2 : Monoid.{u1} M] [_inst_3 : Monoid.{u2} N] (f : MulHom.{u1, u2} M N (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_2)) (MulOneClass.toHasMul.{u2} N (Monoid.toMulOneClass.{u2} N _inst_3))) {a : M} {b : M}, (Dvd.Dvd.{u1} M (semigroupDvd.{u1} M (Monoid.toSemigroup.{u1} M _inst_2)) a b) -> (Dvd.Dvd.{u2} N (semigroupDvd.{u2} N (Monoid.toSemigroup.{u2} N _inst_3)) (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MulHom.{u1, u2} M N (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_2)) (MulOneClass.toHasMul.{u2} N (Monoid.toMulOneClass.{u2} N _inst_3))) (fun (_x : MulHom.{u1, u2} M N (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_2)) (MulOneClass.toHasMul.{u2} N (Monoid.toMulOneClass.{u2} N _inst_3))) => M -> N) (MulHom.hasCoeToFun.{u1, u2} M N (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_2)) (MulOneClass.toHasMul.{u2} N (Monoid.toMulOneClass.{u2} N _inst_3))) f a) (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MulHom.{u1, u2} M N (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_2)) (MulOneClass.toHasMul.{u2} N (Monoid.toMulOneClass.{u2} N _inst_3))) (fun (_x : MulHom.{u1, u2} M N (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_2)) (MulOneClass.toHasMul.{u2} N (Monoid.toMulOneClass.{u2} N _inst_3))) => M -> N) (MulHom.hasCoeToFun.{u1, u2} M N (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_2)) (MulOneClass.toHasMul.{u2} N (Monoid.toMulOneClass.{u2} N _inst_3))) f b))
but is expected to have type
  forall {M : Type.{u2}} {N : Type.{u1}} [_inst_2 : Monoid.{u2} M] [_inst_3 : Monoid.{u1} N] (f : MulHom.{u2, u1} M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3))) {a : M} {b : M}, (Dvd.dvd.{u2} M (semigroupDvd.{u2} M (Monoid.toSemigroup.{u2} M _inst_2)) a b) -> (Dvd.dvd.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) a) (semigroupDvd.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) a) (Monoid.toSemigroup.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) a) _inst_3)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MulHom.{u2, u1} M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3))) M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MulHom.{u2, u1} M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3))) M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3)) (MulHom.mulHomClass.{u2, u1} M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3)))) f a) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MulHom.{u2, u1} M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3))) M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MulHom.{u2, u1} M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3))) M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3)) (MulHom.mulHomClass.{u2, u1} M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3)))) f b))
Case conversion may be inaccurate. Consider using '#align mul_hom.map_dvd MulHom.map_dvdₓ'. -/
theorem MulHom.map_dvd (f : M →ₙ* N) {a b} : a ∣ b → f a ∣ f b :=
  map_dvd f
#align mul_hom.map_dvd MulHom.map_dvd

/- warning: monoid_hom.map_dvd -> MonoidHom.map_dvd is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} {N : Type.{u2}} [_inst_2 : Monoid.{u1} M] [_inst_3 : Monoid.{u2} N] (f : MonoidHom.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_2) (Monoid.toMulOneClass.{u2} N _inst_3)) {a : M} {b : M}, (Dvd.Dvd.{u1} M (semigroupDvd.{u1} M (Monoid.toSemigroup.{u1} M _inst_2)) a b) -> (Dvd.Dvd.{u2} N (semigroupDvd.{u2} N (Monoid.toSemigroup.{u2} N _inst_3)) (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_2) (Monoid.toMulOneClass.{u2} N _inst_3)) (fun (_x : MonoidHom.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_2) (Monoid.toMulOneClass.{u2} N _inst_3)) => M -> N) (MonoidHom.hasCoeToFun.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_2) (Monoid.toMulOneClass.{u2} N _inst_3)) f a) (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_2) (Monoid.toMulOneClass.{u2} N _inst_3)) (fun (_x : MonoidHom.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_2) (Monoid.toMulOneClass.{u2} N _inst_3)) => M -> N) (MonoidHom.hasCoeToFun.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_2) (Monoid.toMulOneClass.{u2} N _inst_3)) f b))
but is expected to have type
  forall {M : Type.{u2}} {N : Type.{u1}} [_inst_2 : Monoid.{u2} M] [_inst_3 : Monoid.{u1} N] (f : MonoidHom.{u2, u1} M N (Monoid.toMulOneClass.{u2} M _inst_2) (Monoid.toMulOneClass.{u1} N _inst_3)) {a : M} {b : M}, (Dvd.dvd.{u2} M (semigroupDvd.{u2} M (Monoid.toSemigroup.{u2} M _inst_2)) a b) -> (Dvd.dvd.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) a) (semigroupDvd.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) a) (Monoid.toSemigroup.{u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) a) _inst_3)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MonoidHom.{u2, u1} M N (Monoid.toMulOneClass.{u2} M _inst_2) (Monoid.toMulOneClass.{u1} N _inst_3)) M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} M N (Monoid.toMulOneClass.{u2} M _inst_2) (Monoid.toMulOneClass.{u1} N _inst_3)) M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3)) (MonoidHomClass.toMulHomClass.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} M N (Monoid.toMulOneClass.{u2} M _inst_2) (Monoid.toMulOneClass.{u1} N _inst_3)) M N (Monoid.toMulOneClass.{u2} M _inst_2) (Monoid.toMulOneClass.{u1} N _inst_3) (MonoidHom.monoidHomClass.{u2, u1} M N (Monoid.toMulOneClass.{u2} M _inst_2) (Monoid.toMulOneClass.{u1} N _inst_3)))) f a) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MonoidHom.{u2, u1} M N (Monoid.toMulOneClass.{u2} M _inst_2) (Monoid.toMulOneClass.{u1} N _inst_3)) M (fun (_x : M) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : M) => N) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} M N (Monoid.toMulOneClass.{u2} M _inst_2) (Monoid.toMulOneClass.{u1} N _inst_3)) M N (MulOneClass.toMul.{u2} M (Monoid.toMulOneClass.{u2} M _inst_2)) (MulOneClass.toMul.{u1} N (Monoid.toMulOneClass.{u1} N _inst_3)) (MonoidHomClass.toMulHomClass.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} M N (Monoid.toMulOneClass.{u2} M _inst_2) (Monoid.toMulOneClass.{u1} N _inst_3)) M N (Monoid.toMulOneClass.{u2} M _inst_2) (Monoid.toMulOneClass.{u1} N _inst_3) (MonoidHom.monoidHomClass.{u2, u1} M N (Monoid.toMulOneClass.{u2} M _inst_2) (Monoid.toMulOneClass.{u1} N _inst_3)))) f b))
Case conversion may be inaccurate. Consider using '#align monoid_hom.map_dvd MonoidHom.map_dvdₓ'. -/
theorem MonoidHom.map_dvd (f : M →* N) {a b} : a ∣ b → f a ∣ f b :=
  map_dvd f
#align monoid_hom.map_dvd MonoidHom.map_dvd

end map_dvd

end Semigroup

section Monoid

variable [Monoid α]

#print dvd_refl /-
@[refl, simp]
theorem dvd_refl (a : α) : a ∣ a :=
  Dvd.intro 1 (mul_one a)
#align dvd_refl dvd_refl
-/

#print dvd_rfl /-
theorem dvd_rfl : ∀ {a : α}, a ∣ a :=
  dvd_refl
#align dvd_rfl dvd_rfl
-/

instance : IsRefl α (· ∣ ·) :=
  ⟨dvd_refl⟩

/- warning: one_dvd -> one_dvd is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : Monoid.{u1} α] (a : α), Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (Monoid.toSemigroup.{u1} α _inst_1)) (OfNat.ofNat.{u1} α 1 (OfNat.mk.{u1} α 1 (One.one.{u1} α (MulOneClass.toHasOne.{u1} α (Monoid.toMulOneClass.{u1} α _inst_1))))) a
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : Monoid.{u1} α] (a : α), Dvd.dvd.{u1} α (semigroupDvd.{u1} α (Monoid.toSemigroup.{u1} α _inst_1)) (OfNat.ofNat.{u1} α 1 (One.toOfNat1.{u1} α (Monoid.toOne.{u1} α _inst_1))) a
Case conversion may be inaccurate. Consider using '#align one_dvd one_dvdₓ'. -/
theorem one_dvd (a : α) : 1 ∣ a :=
  Dvd.intro a (one_mul a)
#align one_dvd one_dvd

end Monoid

section CommSemigroup

variable [CommSemigroup α] {a b c : α}

/- warning: dvd.intro_left -> Dvd.intro_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α} (c : α), (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c a) b) -> (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α} (c : α), (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c a) b) -> (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b)
Case conversion may be inaccurate. Consider using '#align dvd.intro_left Dvd.intro_leftₓ'. -/
theorem Dvd.intro_left (c : α) (h : c * a = b) : a ∣ b :=
  Dvd.intro _ (by rw [mul_comm] at h; apply h)
#align dvd.intro_left Dvd.intro_left

/- warning: dvd_of_mul_left_eq -> dvd_of_mul_left_eq is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α} (c : α), (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c a) b) -> (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α} (c : α), (Eq.{succ u1} α (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c a) b) -> (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b)
Case conversion may be inaccurate. Consider using '#align dvd_of_mul_left_eq dvd_of_mul_left_eqₓ'. -/
alias Dvd.intro_left ← dvd_of_mul_left_eq
#align dvd_of_mul_left_eq dvd_of_mul_left_eq

/- warning: exists_eq_mul_left_of_dvd -> exists_eq_mul_left_of_dvd is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α}, (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b) -> (Exists.{succ u1} α (fun (c : α) => Eq.{succ u1} α b (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c a)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α}, (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b) -> (Exists.{succ u1} α (fun (c : α) => Eq.{succ u1} α b (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c a)))
Case conversion may be inaccurate. Consider using '#align exists_eq_mul_left_of_dvd exists_eq_mul_left_of_dvdₓ'. -/
theorem exists_eq_mul_left_of_dvd (h : a ∣ b) : ∃ c, b = c * a :=
  Dvd.elim h fun c => fun H1 : b = a * c => Exists.intro c (Eq.trans H1 (mul_comm a c))
#align exists_eq_mul_left_of_dvd exists_eq_mul_left_of_dvd

/- warning: dvd_iff_exists_eq_mul_left -> dvd_iff_exists_eq_mul_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α}, Iff (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b) (Exists.{succ u1} α (fun (c : α) => Eq.{succ u1} α b (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c a)))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α}, Iff (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b) (Exists.{succ u1} α (fun (c : α) => Eq.{succ u1} α b (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c a)))
Case conversion may be inaccurate. Consider using '#align dvd_iff_exists_eq_mul_left dvd_iff_exists_eq_mul_leftₓ'. -/
theorem dvd_iff_exists_eq_mul_left : a ∣ b ↔ ∃ c, b = c * a :=
  ⟨exists_eq_mul_left_of_dvd, by
    rintro ⟨c, rfl⟩
    exact ⟨c, mul_comm _ _⟩⟩
#align dvd_iff_exists_eq_mul_left dvd_iff_exists_eq_mul_left

/- warning: dvd.elim_left -> Dvd.elim_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α} {P : Prop}, (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b) -> (forall (c : α), (Eq.{succ u1} α b (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c a)) -> P) -> P
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α} {P : Prop}, (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b) -> (forall (c : α), (Eq.{succ u1} α b (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c a)) -> P) -> P
Case conversion may be inaccurate. Consider using '#align dvd.elim_left Dvd.elim_leftₓ'. -/
theorem Dvd.elim_left {P : Prop} (h₁ : a ∣ b) (h₂ : ∀ c, b = c * a → P) : P :=
  Exists.elim (exists_eq_mul_left_of_dvd h₁) fun c => fun h₃ : b = c * a => h₂ c h₃
#align dvd.elim_left Dvd.elim_left

/- warning: dvd_mul_left -> dvd_mul_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] (a : α) (b : α), Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) b a)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] (a : α) (b : α), Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) b a)
Case conversion may be inaccurate. Consider using '#align dvd_mul_left dvd_mul_leftₓ'. -/
@[simp]
theorem dvd_mul_left (a b : α) : a ∣ b * a :=
  Dvd.intro b (mul_comm a b)
#align dvd_mul_left dvd_mul_left

/- warning: dvd_mul_of_dvd_right -> dvd_mul_of_dvd_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α}, (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b) -> (forall (c : α), Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c b))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α}, (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b) -> (forall (c : α), Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) c b))
Case conversion may be inaccurate. Consider using '#align dvd_mul_of_dvd_right dvd_mul_of_dvd_rightₓ'. -/
theorem dvd_mul_of_dvd_right (h : a ∣ b) (c : α) : a ∣ c * b := by rw [mul_comm];
  exact h.mul_right _
#align dvd_mul_of_dvd_right dvd_mul_of_dvd_right

alias dvd_mul_of_dvd_right ← Dvd.Dvd.mul_left
#align has_dvd.dvd.mul_left Dvd.Dvd.mul_left

attribute [local simp] mul_assoc mul_comm mul_left_comm

/- warning: mul_dvd_mul -> mul_dvd_mul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α} {c : α} {d : α}, (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b) -> (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) c d) -> (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) a c) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) b d))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α} {c : α} {d : α}, (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) a b) -> (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) c d) -> (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) a c) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) b d))
Case conversion may be inaccurate. Consider using '#align mul_dvd_mul mul_dvd_mulₓ'. -/
theorem mul_dvd_mul : ∀ {a b c d : α}, a ∣ b → c ∣ d → a * c ∣ b * d
  | a, _, c, _, ⟨e, rfl⟩, ⟨f, rfl⟩ => ⟨e * f, by simp⟩
#align mul_dvd_mul mul_dvd_mul

/- warning: dvd_of_mul_left_dvd -> dvd_of_mul_left_dvd is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α} {c : α}, (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toHasMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) a b) c) -> (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) b c)
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CommSemigroup.{u1} α] {a : α} {b : α} {c : α}, (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (Semigroup.toMul.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1))) a b) c) -> (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (CommSemigroup.toSemigroup.{u1} α _inst_1)) b c)
Case conversion may be inaccurate. Consider using '#align dvd_of_mul_left_dvd dvd_of_mul_left_dvdₓ'. -/
theorem dvd_of_mul_left_dvd (h : a * b ∣ c) : b ∣ c :=
  Dvd.elim h fun d ceq => Dvd.intro (a * d) (by simp [ceq])
#align dvd_of_mul_left_dvd dvd_of_mul_left_dvd

end CommSemigroup

section CommMonoid

variable [CommMonoid α] {a b : α}

/- warning: mul_dvd_mul_left -> mul_dvd_mul_left is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CommMonoid.{u1} α] (a : α) {b : α} {c : α}, (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (Monoid.toSemigroup.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1))) b c) -> (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (Monoid.toSemigroup.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1))) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1)))) a b) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1)))) a c))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CommMonoid.{u1} α] (a : α) {b : α} {c : α}, (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (Monoid.toSemigroup.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1))) b c) -> (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (Monoid.toSemigroup.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1))) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1)))) a b) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1)))) a c))
Case conversion may be inaccurate. Consider using '#align mul_dvd_mul_left mul_dvd_mul_leftₓ'. -/
theorem mul_dvd_mul_left (a : α) {b c : α} (h : b ∣ c) : a * b ∣ a * c :=
  mul_dvd_mul (dvd_refl a) h
#align mul_dvd_mul_left mul_dvd_mul_left

/- warning: mul_dvd_mul_right -> mul_dvd_mul_right is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} [_inst_1 : CommMonoid.{u1} α] {a : α} {b : α}, (Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (Monoid.toSemigroup.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1))) a b) -> (forall (c : α), Dvd.Dvd.{u1} α (semigroupDvd.{u1} α (Monoid.toSemigroup.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1))) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1)))) a c) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1)))) b c))
but is expected to have type
  forall {α : Type.{u1}} [_inst_1 : CommMonoid.{u1} α] {a : α} {b : α}, (Dvd.dvd.{u1} α (semigroupDvd.{u1} α (Monoid.toSemigroup.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1))) a b) -> (forall (c : α), Dvd.dvd.{u1} α (semigroupDvd.{u1} α (Monoid.toSemigroup.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1))) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1)))) a c) (HMul.hMul.{u1, u1, u1} α α α (instHMul.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (CommMonoid.toMonoid.{u1} α _inst_1)))) b c))
Case conversion may be inaccurate. Consider using '#align mul_dvd_mul_right mul_dvd_mul_rightₓ'. -/
theorem mul_dvd_mul_right (h : a ∣ b) (c : α) : a * c ∣ b * c :=
  mul_dvd_mul h (dvd_refl c)
#align mul_dvd_mul_right mul_dvd_mul_right

#print pow_dvd_pow_of_dvd /-
theorem pow_dvd_pow_of_dvd {a b : α} (h : a ∣ b) : ∀ n : ℕ, a ^ n ∣ b ^ n
  | 0 => by rw [pow_zero, pow_zero]
  | n + 1 => by
    rw [pow_succ, pow_succ]
    exact mul_dvd_mul h (pow_dvd_pow_of_dvd n)
#align pow_dvd_pow_of_dvd pow_dvd_pow_of_dvd
-/

end CommMonoid


/-
Copyright (c) 2023 Kevin Buzzard. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author : Kevin Buzzard
-/

import tactic
import ring_theory.noetherian -- theory of Noetherian rings
import ring_theory.polynomial

/-

# Commutative algebra

More Conrad, again from

https://kconrad.math.uconn.edu/blurbs/ringtheory/noetherian-ring.pdf

Let's *start* to prove Theorem 3.6 following Conrad: if R is Noetherian then R[X] is
Noetherian.

It's not impossible, but it's also not advisable, to make a complex recursive
definition in the middle of a proof. So we factor it out and do it first.
The set-up is: R is a commutative ring and I ⊆ R[X] is an ideal which
is *not* finitely-generated. We then define a sequence fₙ of elements of R[X]
by strong recursion: fₙ is an element of smallest degree in `I - (f₀,f₁,…fₙ₋₁)`;
note that such an element must exist as `I` is not finitely-generated (and ℕ is
well-ordered).

-/

open_locale polynomial -- for R[X] notation

-- If I is a non-finitely-generated ideal of a commutative ring A,
-- and f₀,f₁,...,fₙ₋₁ are elements of I, then I - (f₀,f₁,…,fₙ₋₁) is nonempty

lemma lemma1 {A : Type} [comm_ring A] [decidable_eq A] {I : ideal A} (hInonfg : ¬ I.fg) (n : ℕ)
  (g : Π m, m < n → I) :
  set.nonempty ((I : set A) \ (ideal.span (finset.image (λ m : fin n, (g m.1 m.2).1) finset.univ : set A))) :=
begin
  rw set.nonempty_iff_ne_empty,
  intro h,
  rw set.diff_eq_empty at h,
  apply hInonfg,
  refine ⟨finset.image (λ (m : fin n), (g m.1 m.2).val) finset.univ, _⟩,
  refine le_antisymm _ h,
  rw ideal.span_le,
  intros a ha,
  simp only [fin.val_eq_coe, subtype.val_eq_coe, finset.coe_image, finset.coe_univ, set.image_univ, set.mem_range] at ha,
  rcases ha with ⟨y, rfl⟩,
  exact (g y.1 y.2).2,
end

#check function.argmin_on

open_locale classical

-- I still haven't sorted out this definition and right now I need to prepare the curves
-- and surfaces lecture :-/ Anyway, the moral of this sheet is that making complicated
-- definitions is perhaps more annoying than you might think :-/
noncomputable def f {R : Type} [comm_ring R] {I : ideal R[X]} [decidable_eq I]
  (hInonfg : ¬ I.fg) (n : ℕ) : R[X] := nat.strong_rec_on' n $
  λ m hm, function.argmin_on (polynomial.nat_degree : R[X] → ℕ) (is_well_founded.wf) _ $
  lemma1 hInonfg n sorry

#exit
(λ n h, (⟨((lemma2 (polynomial.nat_degree) (lemma1 I hInonfg n h)).some : R[X]), begin
  have := (lemma2 (polynomial.nat_degree) (lemma1 I hInonfg n h)).some_spec,
  have this2 := set.mem_of_mem_diff this.1,
  exact this2,
end⟩ : I))

lemma hf1 {R : Type} [comm_ring R] {I : ideal R[X]} (hInonfg : ¬ I.fg)  (n : ℕ) :
  (f hInonfg n).1 ∈ (I : set R[X]) \ (ideal.span (finset.image (λ m : fin n, (f hInonfg m).1) finset.univ : set R[X])) :=
begin
  unfold f,
  rw nat.strong_rec_on_beta',
  dsimp,
  sorry,
end

lemma hf2 {R : Type} [comm_ring R] {I : ideal R[X]} (hInonfg : ¬ I.fg) (n : ℕ)
  (i : R[X]) (hi : i ∈ (I : set R[X]) \ (ideal.span (finset.image (λ m : fin n, (f hInonfg m).1) finset.univ : set R[X]))) :
  polynomial.nat_degree (f hInonfg n).1 ≤ polynomial.nat_degree i :=
begin
  sorry
end

example (R : Type) [comm_ring R] [is_noetherian_ring R] :
  is_noetherian_ring R[X] :=
begin
  -- Suffices to prove all ideals are finitely generated
  rw is_noetherian_ring_iff_ideal_fg,
  -- By contradiction. Assume `I` isn't.
  by_contra h, push_neg at h, rcases h with ⟨I, hInotfg⟩,
  -- Define a sequence fₙ of elements of `I` by strong recursion:
  -- fₙ is an element of smallest degree in I - (f₀,f₁,…,fₙ₋₁)
  sorry,
end


/-
Copyright (c) 2020 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alexander Bentkamp, Yury Kudriashov
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.analysis.convex.basic
import Mathlib.analysis.normed_space.finite_dimension
import Mathlib.topology.path_connected
import Mathlib.PostPort

universes u_1 u_2

namespace Mathlib

/-!
# Topological and metric properties of convex sets

We prove the following facts:

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

theorem real.convex_iff_is_preconnected {s : set ℝ} : convex s ↔ is_preconnected s :=
  iff.trans real.convex_iff_ord_connected (iff.symm is_preconnected_iff_ord_connected)

theorem is_preconnected.convex {s : set ℝ} : is_preconnected s → convex s :=
  iff.mpr real.convex_iff_is_preconnected

/-! ### Standard simplex -/

/-- Every vector in `std_simplex ι` has `max`-norm at most `1`. -/
theorem std_simplex_subset_closed_ball {ι : Type u_1} [fintype ι] : std_simplex ι ⊆ metric.closed_ball 0 1 := sorry

/-- `std_simplex ι` is bounded. -/
theorem bounded_std_simplex (ι : Type u_1) [fintype ι] : metric.bounded (std_simplex ι) :=
  iff.mpr (metric.bounded_iff_subset_ball 0) (Exists.intro 1 std_simplex_subset_closed_ball)

/-- `std_simplex ι` is closed. -/
theorem is_closed_std_simplex (ι : Type u_1) [fintype ι] : is_closed (std_simplex ι) := sorry

/-- `std_simplex ι` is compact. -/
theorem compact_std_simplex (ι : Type u_1) [fintype ι] : is_compact (std_simplex ι) :=
  iff.mpr metric.compact_iff_closed_bounded { left := is_closed_std_simplex ι, right := bounded_std_simplex ι }

/-! ### Topological vector space -/

/-- In a topological vector space, the interior of a convex set is convex. -/
theorem convex.interior {E : Type u_2} [add_comm_group E] [vector_space ℝ E] [topological_space E] [topological_add_group E] [topological_vector_space ℝ E] {s : set E} (hs : convex s) : convex (interior s) := sorry

/-- In a topological vector space, the closure of a convex set is convex. -/
theorem convex.closure {E : Type u_2} [add_comm_group E] [vector_space ℝ E] [topological_space E] [topological_add_group E] [topological_vector_space ℝ E] {s : set E} (hs : convex s) : convex (closure s) := sorry

/-- Convex hull of a finite set is compact. -/
theorem set.finite.compact_convex_hull {E : Type u_2} [add_comm_group E] [vector_space ℝ E] [topological_space E] [topological_add_group E] [topological_vector_space ℝ E] {s : set E} (hs : set.finite s) : is_compact (convex_hull s) := sorry

/-- Convex hull of a finite set is closed. -/
theorem set.finite.is_closed_convex_hull {E : Type u_2} [add_comm_group E] [vector_space ℝ E] [topological_space E] [topological_add_group E] [topological_vector_space ℝ E] [t2_space E] {s : set E} (hs : set.finite s) : is_closed (convex_hull s) :=
  is_compact.is_closed (set.finite.compact_convex_hull hs)

/-! ### Normed vector space -/

theorem convex_on_dist {E : Type u_2} [normed_group E] [normed_space ℝ E] (z : E) (s : set E) (hs : convex s) : convex_on s fun (z' : E) => dist z' z := sorry

theorem convex_ball {E : Type u_2} [normed_group E] [normed_space ℝ E] (a : E) (r : ℝ) : convex (metric.ball a r) := sorry

theorem convex_closed_ball {E : Type u_2} [normed_group E] [normed_space ℝ E] (a : E) (r : ℝ) : convex (metric.closed_ball a r) := sorry

/-- Given a point `x` in the convex hull of `s` and a point `y`, there exists a point
of `s` at distance at least `dist x y` from `y`. -/
theorem convex_hull_exists_dist_ge {E : Type u_2} [normed_group E] [normed_space ℝ E] {s : set E} {x : E} (hx : x ∈ convex_hull s) (y : E) : ∃ (x' : E), ∃ (H : x' ∈ s), dist x y ≤ dist x' y :=
  convex_on.exists_ge_of_mem_convex_hull (convex_on_dist y (convex_hull s) (convex_convex_hull s)) hx

/-- Given a point `x` in the convex hull of `s` and a point `y` in the convex hull of `t`,
there exist points `x' ∈ s` and `y' ∈ t` at distance at least `dist x y`. -/
theorem convex_hull_exists_dist_ge2 {E : Type u_2} [normed_group E] [normed_space ℝ E] {s : set E} {t : set E} {x : E} {y : E} (hx : x ∈ convex_hull s) (hy : y ∈ convex_hull t) : ∃ (x' : E), ∃ (H : x' ∈ s), ∃ (y' : E), ∃ (H : y' ∈ t), dist x y ≤ dist x' y' := sorry

/-- Emetric diameter of the convex hull of a set `s` equals the emetric diameter of `s. -/
@[simp] theorem convex_hull_ediam {E : Type u_2} [normed_group E] [normed_space ℝ E] (s : set E) : emetric.diam (convex_hull s) = emetric.diam s := sorry

/-- Diameter of the convex hull of a set `s` equals the emetric diameter of `s. -/
@[simp] theorem convex_hull_diam {E : Type u_2} [normed_group E] [normed_space ℝ E] (s : set E) : metric.diam (convex_hull s) = metric.diam s := sorry

/-- Convex hull of `s` is bounded if and only if `s` is bounded. -/
@[simp] theorem bounded_convex_hull {E : Type u_2} [normed_group E] [normed_space ℝ E] {s : set E} : metric.bounded (convex_hull s) ↔ metric.bounded s := sorry

theorem convex.is_path_connected {E : Type u_2} [normed_group E] [normed_space ℝ E] {s : set E} (hconv : convex s) (hne : set.nonempty s) : is_path_connected s := sorry

protected instance normed_space.path_connected {E : Type u_2} [normed_group E] [normed_space ℝ E] : path_connected_space E :=
  iff.mpr path_connected_space_iff_univ (convex.is_path_connected convex_univ (Exists.intro 0 trivial))

protected instance normed_space.loc_path_connected {E : Type u_2} [normed_group E] [normed_space ℝ E] : loc_path_connected_space E :=
  loc_path_connected_of_bases (fun (x : E) => metric.nhds_basis_ball)
    fun (x : E) (r : ℝ) (r_pos : 0 < r) =>
      convex.is_path_connected (convex_ball x r)
        (eq.mpr
          (id
            (propext
              ((fun {α : Type u_2} [_inst_1 : metric_space α] {x : α} {ε : ℝ} (h : 0 < ε) =>
                  iff_true_intro (metric.nonempty_ball h))
                (iff.mpr (iff_true_intro r_pos) True.intro))))
          trivial)


/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Mario Carneiro, Johan Commelin, Amelia Livingston, Anne Baanen
-/
import group_theory.submonoid.inverses
import ring_theory.finite_type
import ring_theory.localization.basic
import tactic.ring_exp

/-!
# Submonoid of inverses

## Main definitions

 * `is_localization.inv_submonoid M S` is the submonoid of `S = M⁻¹R` consisting of inverses of
   each element `x ∈ M`

## Implementation notes

See `src/ring_theory/localization/basic.lean` for a design overview.

## Tags
localization, ring localization, commutative ring localization, characteristic predicate,
commutative ring, field of fractions
-/

variables {R : Type*} [comm_ring R] (M : submonoid R) (S : Type*) [comm_ring S]
variables [algebra R S] {P : Type*} [comm_ring P]

open function
open_locale big_operators

namespace is_localization

section inv_submonoid

variables (M S)

/-- The submonoid of `S = M⁻¹R` consisting of `{ 1 / x | x ∈ M }`. -/
def inv_submonoid : submonoid S := (M.map (algebra_map R S)).left_inv

variable [is_localization M S]

lemma submonoid_map_le_is_unit : M.map (algebra_map R S) ≤ is_unit.submonoid S :=
by { rintros _ ⟨a, ha, rfl⟩, exact is_localization.map_units S ⟨_, ha⟩ }

/-- There is an equivalence of monoids between the image of `M` and `inv_submonoid`. -/
noncomputable
abbreviation equiv_inv_submonoid : M.map (algebra_map R S) ≃* inv_submonoid M S :=
((M.map (algebra_map R S)).left_inv_equiv (submonoid_map_le_is_unit M S)).symm

/-- There is a canonical map from `M` to `inv_submonoid` sending `x` to `1 / x`. -/
noncomputable
def to_inv_submonoid : M →* inv_submonoid M S :=
(equiv_inv_submonoid M S).to_monoid_hom.comp ((algebra_map R S : R →* S).submonoid_map M)

lemma to_inv_submonoid_surjective : function.surjective (to_inv_submonoid M S) :=
function.surjective.comp (equiv.surjective _) (monoid_hom.submonoid_map_surjective _ _)

@[simp]
lemma to_inv_submonoid_mul (m : M) : (to_inv_submonoid M S m : S) * (algebra_map R S m) = 1 :=
submonoid.left_inv_equiv_symm_mul _ _ _

@[simp]
lemma mul_to_inv_submonoid (m : M) : (algebra_map R S m) * (to_inv_submonoid M S m : S) = 1 :=
submonoid.mul_left_inv_equiv_symm _ _ ⟨_, _⟩

@[simp]
lemma smul_to_inv_submonoid (m : M) : m • (to_inv_submonoid M S m : S) = 1 :=
by { convert mul_to_inv_submonoid M S m, rw ← algebra.smul_def, refl }

variables {S}

lemma surj' (z : S) : ∃ (r : R) (m : M), z = r • to_inv_submonoid M S m :=
begin
  rcases is_localization.surj M z with ⟨⟨r, m⟩, e : z * _ = algebra_map R S r⟩,
  refine ⟨r, m, _⟩,
  rw [algebra.smul_def, ← e, mul_assoc],
  simp,
end

lemma to_inv_submonoid_eq_mk' (x : M) :
  (to_inv_submonoid M S x : S) = mk' S 1 x :=
by { rw ← (is_localization.map_units S x).mul_left_inj, simp }

lemma mem_inv_submonoid_iff_exists_mk' (x : S) :
  x ∈ inv_submonoid M S ↔ ∃ m : M, mk' S 1 m = x :=
begin
  simp_rw ← to_inv_submonoid_eq_mk',
  exact ⟨λ h, ⟨_, congr_arg subtype.val (to_inv_submonoid_surjective M S ⟨x, h⟩).some_spec⟩,
    λ h, h.some_spec ▸ (to_inv_submonoid M S h.some).prop⟩
end

variables (S)

lemma span_inv_submonoid : submodule.span R (inv_submonoid M S : set S) = ⊤ :=
begin
  rw eq_top_iff,
  rintros x -,
  rcases is_localization.surj' M x with ⟨r, m, rfl⟩,
  exact submodule.smul_mem _ _ (submodule.subset_span (to_inv_submonoid M S m).prop),
end

lemma finite_type_of_monoid_fg [monoid.fg M] : algebra.finite_type R S :=
begin
  have := monoid.fg_of_surjective _ (to_inv_submonoid_surjective M S),
  rw monoid.fg_iff_submonoid_fg at this,
  rcases this with ⟨s, hs⟩,
  refine ⟨⟨s, _⟩⟩,
  rw eq_top_iff,
  rintro x -,
  change x ∈ ((algebra.adjoin R _ : subalgebra R S).to_submodule : set S),
  rw [algebra.adjoin_eq_span, hs, span_inv_submonoid],
  trivial
end

end inv_submonoid

end is_localization


/-
Copyright (c) 2022 Jireh Loreaux. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Jireh Loreaux
-/
import topology.continuous_function.bounded
import topology.continuous_function.cocompact_map

/-!
# Continuous functions vanishing at infinity

The type of continuous functions vanishing at infinity. When the domain is compact
`C(α, β) ≃ C₀(α, β)` via the identity map. When the codomain is a metric space, every continuous
map which vanishes at infinity is a bounded continuous function. When the domain is a locally
compact space, this type has nice properties.

## TODO

* Create more intances of algebraic structures (e.g., `non_unital_semiring`) once the necessary
  type classes (e.g., `topological_ring`) are sufficiently generalized.
* Relate the unitization of `C₀(α, β)` to the Alexandroff compactification.
-/
universes u v w

variables {F : Type*} {α : Type u} {β : Type v} {γ : Type w} [topological_space α]

open_locale bounded_continuous_function topology
open filter metric

/-- `C₀(α, β)` is the type of continuous functions `α → β` which vanish at infinity from a
topological space to a metric space with a zero element.

When possible, instead of parametrizing results over `(f : C₀(α, β))`,
you should parametrize over `(F : Type*) [zero_at_infty_continuous_map_class F α β] (f : F)`.

When you extend this structure, make sure to extend `zero_at_infty_continuous_map_class`. -/
structure zero_at_infty_continuous_map (α : Type u) (β : Type v)
  [topological_space α] [has_zero β] [topological_space β] extends continuous_map α β :
  Type (max u v) :=
(zero_at_infty' : tendsto to_fun (cocompact α) (𝓝 0))

localized "notation [priority 2000] (name := zero_at_infty_continuous_map)
  `C₀(` α `, ` β `)` := zero_at_infty_continuous_map α β" in zero_at_infty
localized "notation (name := zero_at_infty_continuous_map.arrow)
  α ` →C₀ ` β := zero_at_infty_continuous_map α β" in zero_at_infty

section
set_option old_structure_cmd true

/-- `zero_at_infty_continuous_map_class F α β` states that `F` is a type of continuous maps which
vanish at infinity.

You should also extend this typeclass when you extend `zero_at_infty_continuous_map`. -/
class zero_at_infty_continuous_map_class (F : Type*) (α β : out_param $ Type*) [topological_space α]
  [has_zero β] [topological_space β] extends continuous_map_class F α β :=
(zero_at_infty (f : F) : tendsto f (cocompact α) (𝓝 0))

end

export zero_at_infty_continuous_map_class (zero_at_infty)

namespace zero_at_infty_continuous_map

section basics

variables [topological_space β] [has_zero β] [zero_at_infty_continuous_map_class F α β]

instance : zero_at_infty_continuous_map_class C₀(α, β) α β :=
{ coe := λ f, f.to_fun,
  coe_injective' := λ f g h, by { obtain ⟨⟨_, _⟩, _⟩ := f, obtain ⟨⟨_, _⟩, _⟩ := g, congr' },
  map_continuous := λ f, f.continuous_to_fun,
  zero_at_infty := λ f, f.zero_at_infty' }

/-- Helper instance for when there's too many metavariables to apply `fun_like.has_coe_to_fun`
directly. -/
instance : has_coe_to_fun C₀(α, β) (λ _, α → β) := fun_like.has_coe_to_fun

instance : has_coe_t F C₀(α, β) :=
⟨λ f, { to_fun := f, continuous_to_fun := map_continuous f, zero_at_infty' := zero_at_infty f }⟩

@[simp] lemma coe_to_continuous_fun (f : C₀(α, β)) : (f.to_continuous_map : α → β) = f := rfl

@[ext] lemma ext {f g : C₀(α, β)} (h : ∀ x, f x = g x) : f = g := fun_like.ext _ _ h

/-- Copy of a `zero_at_infinity_continuous_map` with a new `to_fun` equal to the old one. Useful
to fix definitional equalities. -/
protected def copy (f : C₀(α, β)) (f' : α → β) (h : f' = f) : C₀(α, β) :=
{ to_fun := f',
  continuous_to_fun := by { rw h, exact f.continuous_to_fun },
  zero_at_infty' := by { simp_rw h, exact f.zero_at_infty' } }

@[simp] lemma coe_copy (f : C₀(α, β)) (f' : α → β) (h : f' = f) : ⇑(f.copy f' h) = f' := rfl
lemma copy_eq (f : C₀(α, β)) (f' : α → β) (h : f' = f) : f.copy f' h = f := fun_like.ext' h

lemma eq_of_empty [is_empty α] (f g : C₀(α, β)) : f = g :=
ext $ is_empty.elim ‹_›

/-- A continuous function on a compact space is automatically a continuous function vanishing at
infinity. -/
@[simps]
def continuous_map.lift_zero_at_infty [compact_space α] : C(α, β) ≃ C₀(α, β) :=
{ to_fun := λ f, { to_fun := f, continuous_to_fun := f.continuous, zero_at_infty' := by simp },
  inv_fun := λ f, f,
  left_inv := λ f, by { ext, refl },
  right_inv := λ f, by { ext, refl } }

/-- A continuous function on a compact space is automatically a continuous function vanishing at
infinity. This is not an instance to avoid type class loops. -/
@[simps]
def zero_at_infty_continuous_map_class.of_compact {G : Type*} [continuous_map_class G α β]
  [compact_space α] : zero_at_infty_continuous_map_class G α β :=
{ coe := λ g, g,
  coe_injective' := λ f g h, fun_like.coe_fn_eq.mp h,
  map_continuous := map_continuous,
  zero_at_infty := by simp }

end basics

/-! ### Algebraic structure

Whenever `β` has suitable algebraic structure and a compatible topological structure, then
`C₀(α, β)` inherits a corresponding algebraic structure. The primary exception to this is that
`C₀(α, β)` will not have a multiplicative identity.
-/

section algebraic_structure

variables [topological_space β] (x : α)

instance [has_zero β] : has_zero C₀(α, β) := ⟨⟨0, tendsto_const_nhds⟩⟩

instance [has_zero β] : inhabited C₀(α, β) := ⟨0⟩

@[simp] lemma coe_zero [has_zero β] : ⇑(0 : C₀(α, β)) = 0 := rfl
lemma zero_apply [has_zero β] : (0 : C₀(α, β)) x = 0 := rfl

instance [mul_zero_class β] [has_continuous_mul β] : has_mul C₀(α, β) :=
⟨λ f g, ⟨f * g, by simpa only [mul_zero] using (zero_at_infty f).mul (zero_at_infty g)⟩⟩

@[simp] lemma coe_mul [mul_zero_class β] [has_continuous_mul β] (f g : C₀(α, β)) :
  ⇑(f * g) = f * g := rfl
lemma mul_apply [mul_zero_class β] [has_continuous_mul β] (f g : C₀(α, β)) :
  (f * g) x = f x * g x := rfl

instance [mul_zero_class β] [has_continuous_mul β] : mul_zero_class C₀(α, β) :=
fun_like.coe_injective.mul_zero_class _ coe_zero coe_mul

instance [semigroup_with_zero β] [has_continuous_mul β] : semigroup_with_zero C₀(α, β) :=
fun_like.coe_injective.semigroup_with_zero _ coe_zero coe_mul

instance [add_zero_class β] [has_continuous_add β] : has_add C₀(α, β) :=
⟨λ f g, ⟨f + g, by simpa only [add_zero] using (zero_at_infty f).add (zero_at_infty g)⟩⟩

@[simp] lemma coe_add [add_zero_class β] [has_continuous_add β] (f g : C₀(α, β)) :
  ⇑(f + g) = f + g := rfl
lemma add_apply [add_zero_class β] [has_continuous_add β] (f g : C₀(α, β)) :
  (f + g) x = f x + g x := rfl

instance [add_zero_class β] [has_continuous_add β] : add_zero_class C₀(α, β) :=
fun_like.coe_injective.add_zero_class _ coe_zero coe_add

section add_monoid

variables [add_monoid β] [has_continuous_add β] (f g : C₀(α, β))

@[simp] lemma coe_nsmul_rec : ∀ n, ⇑(nsmul_rec n f) = n • f
| 0 := by rw [nsmul_rec, zero_smul, coe_zero]
| (n + 1) := by rw [nsmul_rec, succ_nsmul, coe_add, coe_nsmul_rec]

instance has_nat_scalar : has_smul ℕ C₀(α, β) :=
⟨λ n f, ⟨n • f, by simpa [coe_nsmul_rec] using zero_at_infty (nsmul_rec n f)⟩⟩

instance : add_monoid C₀(α, β) :=
fun_like.coe_injective.add_monoid _ coe_zero coe_add (λ _ _, rfl)

end add_monoid

instance [add_comm_monoid β] [has_continuous_add β] : add_comm_monoid C₀(α, β) :=
fun_like.coe_injective.add_comm_monoid _ coe_zero coe_add (λ _ _, rfl)

section add_group

variables [add_group β] [topological_add_group β] (f g : C₀(α, β))

instance : has_neg C₀(α, β) :=
⟨λ f, ⟨-f, by simpa only [neg_zero] using (zero_at_infty f).neg⟩⟩

@[simp] lemma coe_neg : ⇑(-f) = -f := rfl
lemma neg_apply : (-f) x = -f x := rfl

instance : has_sub C₀(α, β) :=
⟨λ f g, ⟨f - g, by simpa only [sub_zero] using (zero_at_infty f).sub (zero_at_infty g)⟩⟩

@[simp]

@[simp] lemma coe_zsmul_rec : ∀ z, ⇑(zsmul_rec z f) = z • f
| (int.of_nat n) := by rw [zsmul_rec, int.of_nat_eq_coe, coe_nsmul_rec, coe_nat_zsmul]
| -[1+ n] := by rw [zsmul_rec, zsmul_neg_succ_of_nat, coe_neg, coe_nsmul_rec]

instance has_int_scalar : has_smul ℤ C₀(α, β) :=
⟨λ n f, ⟨n • f, by simpa using zero_at_infty (zsmul_rec n f)⟩⟩

instance : add_group C₀(α, β) :=
fun_like.coe_injective.add_group _ coe_zero coe_add coe_neg coe_sub (λ _ _, rfl) (λ _ _, rfl)

end add_group

instance [add_comm_group β] [topological_add_group β] : add_comm_group C₀(α, β) :=
fun_like.coe_injective.add_comm_group _ coe_zero coe_add coe_neg coe_sub (λ _ _, rfl) (λ _ _, rfl)

instance [has_zero β] {R : Type*} [has_zero R] [smul_with_zero R β]
  [has_continuous_const_smul R β] : has_smul R C₀(α, β) :=
⟨λ r f, ⟨r • f, by simpa [smul_zero] using (zero_at_infty f).const_smul r⟩⟩

@[simp] lemma coe_smul [has_zero β] {R : Type*} [has_zero R] [smul_with_zero R β]
  [has_continuous_const_smul R β] (r : R) (f : C₀(α, β)) : ⇑(r • f) = r • f := rfl

lemma smul_apply [has_zero β] {R : Type*} [has_zero R] [smul_with_zero R β]
  [has_continuous_const_smul R β] (r : R) (f : C₀(α, β)) (x : α) : (r • f) x = r • f x := rfl

instance [has_zero β] {R : Type*} [has_zero R] [smul_with_zero R β] [smul_with_zero Rᵐᵒᵖ β]
  [has_continuous_const_smul R β] [is_central_scalar R β] : is_central_scalar R C₀(α, β) :=
⟨λ r f, ext $ λ x, op_smul_eq_smul _ _⟩

instance [has_zero β] {R : Type*} [has_zero R] [smul_with_zero R β]
  [has_continuous_const_smul R β] : smul_with_zero R C₀(α, β) :=
function.injective.smul_with_zero ⟨_, coe_zero⟩ fun_like.coe_injective coe_smul

instance [has_zero β] {R : Type*} [monoid_with_zero R] [mul_action_with_zero R β]
  [has_continuous_const_smul R β] : mul_action_with_zero R C₀(α, β) :=
function.injective.mul_action_with_zero ⟨_, coe_zero⟩ fun_like.coe_injective coe_smul

instance [add_comm_monoid β] [has_continuous_add β] {R : Type*} [semiring R] [module R β]
  [has_continuous_const_smul R β] : module R C₀(α, β) :=
function.injective.module R ⟨_, coe_zero, coe_add⟩ fun_like.coe_injective coe_smul

instance [non_unital_non_assoc_semiring β] [topological_semiring β] :
  non_unital_non_assoc_semiring C₀(α, β) :=
fun_like.coe_injective.non_unital_non_assoc_semiring _ coe_zero coe_add coe_mul (λ _ _, rfl)

instance [non_unital_semiring β] [topological_semiring β] :
  non_unital_semiring C₀(α, β) :=
fun_like.coe_injective.non_unital_semiring _ coe_zero coe_add coe_mul (λ _ _, rfl)

instance [non_unital_comm_semiring β] [topological_semiring β] :
  non_unital_comm_semiring C₀(α, β) :=
fun_like.coe_injective.non_unital_comm_semiring _ coe_zero coe_add coe_mul (λ _ _, rfl)

instance [non_unital_non_assoc_ring β] [topological_ring β] :
  non_unital_non_assoc_ring C₀(α, β) :=
fun_like.coe_injective.non_unital_non_assoc_ring _ coe_zero coe_add coe_mul coe_neg coe_sub
  (λ _ _, rfl) (λ _ _, rfl)

instance [non_unital_ring β] [topological_ring β] :
  non_unital_ring C₀(α, β) :=
fun_like.coe_injective.non_unital_ring _ coe_zero coe_add coe_mul coe_neg coe_sub (λ _ _, rfl)
  (λ _ _, rfl)

instance [non_unital_comm_ring β] [topological_ring β] :
  non_unital_comm_ring C₀(α, β) :=
fun_like.coe_injective.non_unital_comm_ring _ coe_zero coe_add coe_mul coe_neg coe_sub (λ _ _, rfl)
  (λ _ _, rfl)

instance {R : Type*} [semiring R] [non_unital_non_assoc_semiring β] [topological_semiring β]
  [module R β] [has_continuous_const_smul R β] [is_scalar_tower R β β] :
  is_scalar_tower R C₀(α, β) C₀(α, β) :=
{ smul_assoc := λ r f g,
  begin
    ext,
    simp only [smul_eq_mul, coe_mul, coe_smul, pi.mul_apply, pi.smul_apply],
    rw [←smul_eq_mul, ←smul_eq_mul, smul_assoc],
  end }

instance {R : Type*} [semiring R] [non_unital_non_assoc_semiring β] [topological_semiring β]
  [module R β] [has_continuous_const_smul R β] [smul_comm_class R β β] :
  smul_comm_class R C₀(α, β) C₀(α, β) :=
{ smul_comm := λ r f g,
  begin
    ext,
    simp only [smul_eq_mul, coe_smul, coe_mul, pi.smul_apply, pi.mul_apply],
    rw [←smul_eq_mul, ←smul_eq_mul, smul_comm],
  end }

end algebraic_structure

section uniform

variables [uniform_space β] [uniform_space γ] [has_zero γ]
  [zero_at_infty_continuous_map_class F β γ]

lemma uniform_continuous (f : F) : uniform_continuous (f : β → γ) :=
(map_continuous f).uniform_continuous_of_tendsto_cocompact (zero_at_infty f)

end uniform

/-! ### Metric structure

When `β` is a metric space, then every element of `C₀(α, β)` is bounded, and so there is a natural
inclusion map `zero_at_infty_continuous_map.to_bcf : C₀(α, β) → (α →ᵇ β)`. Via this map `C₀(α, β)`
inherits a metric as the pullback of the metric on `α →ᵇ β`. Moreover, this map has closed range
in `α →ᵇ β` and consequently `C₀(α, β)` is a complete space whenever `β` is complete.
-/

section metric

open metric set

variables [metric_space β] [has_zero β] [zero_at_infty_continuous_map_class F α β]

protected lemma bounded (f : F) : ∃ C, ∀ x y : α, dist ((f : α → β) x) (f y) ≤ C :=
begin
  obtain ⟨K : set α, hK₁, hK₂⟩ := mem_cocompact.mp (tendsto_def.mp (zero_at_infty (f : F)) _
    (closed_ball_mem_nhds (0 : β) zero_lt_one)),
  obtain ⟨C, hC⟩ := (hK₁.image (map_continuous f)).bounded.subset_ball (0 : β),
  refine ⟨max C 1 + max C 1, (λ x y, _)⟩,
  have : ∀ x, f x ∈ closed_ball (0 : β) (max C 1),
  { intro x,
    by_cases hx : x ∈ K,
    { exact (mem_closed_ball.mp $ hC ⟨x, hx, rfl⟩).trans (le_max_left _ _) },
    { exact (mem_closed_ball.mp $ mem_preimage.mp (hK₂ hx)).trans (le_max_right _ _) } },
  exact (dist_triangle (f x) 0 (f y)).trans
    (add_le_add (mem_closed_ball.mp $ this x) (mem_closed_ball'.mp $ this y)),
end

lemma bounded_range (f : C₀(α, β)) : bounded (range f) :=
bounded_range_iff.2 f.bounded

lemma bounded_image (f : C₀(α, β)) (s : set α) : bounded (f '' s) :=
f.bounded_range.mono $ image_subset_range _ _

@[priority 100]
instance : bounded_continuous_map_class F α β :=
{ map_bounded := λ f, zero_at_infty_continuous_map.bounded f,
  ..‹zero_at_infty_continuous_map_class F α β› }

/-- Construct a bounded continuous function from a continuous function vanishing at infinity. -/
@[simps]
def to_bcf (f : C₀(α, β)) : α →ᵇ β :=
⟨f, map_bounded f⟩

section
variables (α) (β)
lemma to_bcf_injective :
  function.injective (to_bcf : C₀(α, β) → α →ᵇ β) :=
λ f g h, by { ext, simpa only using fun_like.congr_fun h x, }
end

variables {C : ℝ} {f g : C₀(α, β)}

/-- The type of continuous functions vanishing at infinity, with the uniform distance induced by the
inclusion `zero_at_infinity_continuous_map.to_bcf`, is a metric space. -/
noncomputable instance : metric_space C₀(α, β) :=
metric_space.induced _ (to_bcf_injective α β) (by apply_instance)

@[simp]
lemma dist_to_bcf_eq_dist {f g : C₀(α, β)} : dist f.to_bcf g.to_bcf = dist f g := rfl

open bounded_continuous_function

/-- Convergence in the metric on `C₀(α, β)` is uniform convergence. -/
lemma tendsto_iff_tendsto_uniformly {ι : Type*} {F : ι → C₀(α, β)} {f : C₀(α, β)} {l : filter ι} :
  tendsto F l (𝓝 f) ↔ tendsto_uniformly (λ i, F i) f l :=
by simpa only [metric.tendsto_nhds] using @bounded_continuous_function.tendsto_iff_tendsto_uniformly
  _ _ _ _ _ (λ i, (F i).to_bcf) f.to_bcf l

lemma isometry_to_bcf : isometry (to_bcf : C₀(α, β) → α →ᵇ β) := by tauto

lemma closed_range_to_bcf : is_closed (range (to_bcf : C₀(α, β) → α →ᵇ β)) :=
begin
  refine is_closed_iff_cluster_pt.mpr (λ f hf, _),
  rw cluster_pt_principal_iff at hf,
  have : tendsto f (cocompact α) (𝓝 0),
  { refine metric.tendsto_nhds.mpr (λ ε hε, _),
    obtain ⟨_, hg, g, rfl⟩ := hf (ball f (ε / 2)) (ball_mem_nhds f $ half_pos hε),
    refine (metric.tendsto_nhds.mp (zero_at_infty g) (ε / 2)
      (half_pos hε)).mp (eventually_of_forall $ λ x hx, _),
    calc dist (f x) 0 ≤ dist (g.to_bcf x) (f x) + dist (g x) 0 : dist_triangle_left _ _ _
    ...               < dist g.to_bcf f + ε / 2 : add_lt_add_of_le_of_lt (dist_coe_le_dist x) hx
    ...               < ε : by simpa [add_halves ε] using add_lt_add_right hg (ε / 2) },
  exact ⟨⟨f.to_continuous_map, this⟩, by {ext, refl}⟩,
end

/-- Continuous functions vanishing at infinity taking values in a complete space form a
complete space. -/
instance [complete_space β] : complete_space C₀(α, β) :=
(complete_space_iff_is_complete_range isometry_to_bcf.uniform_inducing).mpr
  closed_range_to_bcf.is_complete

end metric

section norm

/-! ### Normed space

The norm structure on `C₀(α, β)` is the one induced by the inclusion `to_bcf : C₀(α, β) → (α →ᵇ b)`,
viewed as an additive monoid homomorphism. Then `C₀(α, β)` is naturally a normed space over a normed
field `𝕜` whenever `β` is as well.
-/

section normed_space

variables [normed_add_comm_group β] {𝕜 : Type*} [normed_field 𝕜] [normed_space 𝕜 β]

noncomputable instance : normed_add_comm_group C₀(α, β) :=
normed_add_comm_group.induced C₀(α, β) (α →ᵇ β) (⟨to_bcf, rfl, λ x y, rfl⟩ : C₀(α, β) →+ (α →ᵇ β))
  (to_bcf_injective α β)

@[simp]
lemma norm_to_bcf_eq_norm {f : C₀(α, β)} : ‖f.to_bcf‖ = ‖f‖ := rfl

instance : normed_space 𝕜 C₀(α, β) :=
{ norm_smul_le := λ k f, norm_smul_le k f.to_bcf }

end normed_space

section normed_ring

variables [non_unital_normed_ring β]

noncomputable instance : non_unital_normed_ring C₀(α, β) :=
{ norm_mul := λ f g, norm_mul_le f.to_bcf g.to_bcf,
  ..zero_at_infty_continuous_map.non_unital_ring,
  ..zero_at_infty_continuous_map.normed_add_comm_group }

end normed_ring

end norm

section star

/-! ### Star structure

It is possible to equip `C₀(α, β)` with a pointwise `star` operation whenever there is a continuous
`star : β → β` for which `star (0 : β) = 0`. We don't have quite this weak a typeclass, but
`star_add_monoid` is close enough.

The `star_add_monoid` and `normed_star_group` classes on `C₀(α, β)` are inherited from their
counterparts on `α →ᵇ β`. Ultimately, when `β` is a C⋆-ring, then so is `C₀(α, β)`.
-/

variables [topological_space β] [add_monoid β] [star_add_monoid β] [has_continuous_star β]

instance : has_star C₀(α, β) :=
{ star := λ f,
  { to_fun := λ x, star (f x),
    continuous_to_fun := (map_continuous f).star,
    zero_at_infty' := by simpa only [star_zero]
      using (continuous_star.tendsto (0 : β)).comp (zero_at_infty f) } }

@[simp]
lemma coe_star (f : C₀(α, β)) : ⇑(star f) = star f := rfl

lemma star_apply (f : C₀(α, β)) (x : α) :
  (star f) x = star (f x) := rfl

instance [has_continuous_add β] : star_add_monoid C₀(α, β) :=
{ star_involutive := λ f, ext $ λ x, star_star (f x),
  star_add := λ f g, ext $ λ x, star_add (f x) (g x) }

end star

section normed_star

variables [normed_add_comm_group β] [star_add_monoid β] [normed_star_group β]

instance : normed_star_group C₀(α, β) :=
{ norm_star := λ f, (norm_star f.to_bcf : _) }

end normed_star

section star_module

variables {𝕜 : Type*} [has_zero 𝕜] [has_star 𝕜]
  [add_monoid β] [star_add_monoid β] [topological_space β] [has_continuous_star β]
  [smul_with_zero 𝕜 β] [has_continuous_const_smul 𝕜 β] [star_module 𝕜 β]

instance : star_module 𝕜 C₀(α, β) :=
{ star_smul := λ k f, ext $ λ x, star_smul k (f x) }

end star_module

section star_ring

variables [non_unital_semiring β] [star_ring β] [topological_space β] [has_continuous_star β]
  [topological_semiring β]

instance : star_ring C₀(α, β) :=
{ star_mul := λ f g, ext $ λ x, star_mul (f x) (g x),
  ..zero_at_infty_continuous_map.star_add_monoid }

end star_ring

section cstar_ring

instance [non_unital_normed_ring β] [star_ring β] [cstar_ring β] : cstar_ring C₀(α, β) :=
{ norm_star_mul_self := λ f, @cstar_ring.norm_star_mul_self _ _ _ _ f.to_bcf }

end cstar_ring

/-! ### C₀ as a functor

For each `β` with sufficient structure, there is a contravariant functor `C₀(-, β)` from the
category of topological spaces with morphisms given by `cocompact_map`s.
-/

variables {δ : Type*} [topological_space β] [topological_space γ] [topological_space δ]

local notation α ` →co ` β := cocompact_map α β

section
variables [has_zero δ]

/-- Composition of a continuous function vanishing at infinity with a cocompact map yields another
continuous function vanishing at infinity. -/
def comp (f : C₀(γ, δ)) (g : β →co γ) : C₀(β, δ) :=
{ to_continuous_map := (f : C(γ, δ)).comp g,
  zero_at_infty' := (zero_at_infty f).comp (cocompact_tendsto g) }

@[simp] lemma coe_comp_to_continuous_fun (f : C₀(γ, δ)) (g : β →co γ) :
  ((f.comp g).to_continuous_map : β → δ) = f ∘ g := rfl

@[simp] lemma comp_id (f : C₀(γ, δ)) : f.comp (cocompact_map.id γ) = f := ext (λ x, rfl)

@[simp] lemma comp_assoc (f : C₀(γ, δ)) (g : β →co γ) (h : α →co β) :
  (f.comp g).comp h = f.comp (g.comp h) := rfl

@[simp] lemma zero_comp (g : β →co γ) : (0 : C₀(γ, δ)).comp g = 0 := rfl

end

/-- Composition as an additive monoid homomorphism. -/
def comp_add_monoid_hom [add_monoid δ] [has_continuous_add δ] (g : β →co γ) :
  C₀(γ, δ) →+ C₀(β, δ) :=
{ to_fun := λ f, f.comp g,
  map_zero' := zero_comp g,
  map_add' := λ f₁ f₂, rfl }

/-- Composition as a semigroup homomorphism. -/
def comp_mul_hom [mul_zero_class δ] [has_continuous_mul δ]
  (g : β →co γ) : C₀(γ, δ) →ₙ* C₀(β, δ) :=
{ to_fun := λ f, f.comp g,
  map_mul' := λ f₁ f₂, rfl }

/-- Composition as a linear map. -/
def comp_linear_map [add_comm_monoid δ] [has_continuous_add δ] {R : Type*}
  [semiring R] [module R δ] [has_continuous_const_smul R δ] (g : β →co γ) :
  C₀(γ, δ) →ₗ[R] C₀(β, δ) :=
{ to_fun := λ f, f.comp g,
  map_add' := λ f₁ f₂, rfl,
  map_smul' := λ r f, rfl }

/-- Composition as a non-unital algebra homomorphism. -/
def comp_non_unital_alg_hom {R : Type*} [semiring R] [non_unital_non_assoc_semiring δ]
  [topological_semiring δ] [module R δ] [has_continuous_const_smul R δ] (g : β →co γ) :
  C₀(γ, δ) →ₙₐ[R] C₀(β, δ) :=
{ to_fun := λ f, f.comp g,
  map_smul' := λ r f, rfl,
  map_zero' := rfl,
  map_add' := λ f₁ f₂, rfl,
  map_mul' := λ f₁ f₂, rfl }

end zero_at_infty_continuous_map


import analysis.topology.topological_structures
import valuation_spectrum

universes u₁ u₂ u₃

namespace valuation
variables {R : Type u₁} [comm_ring R] [topological_space R] [topological_ring R]
variables {Γ : Type u₂} [linear_ordered_comm_group Γ]

-- this definition should only be applied to valuations such that Gamma is the value group;
-- without this assumption the definition is meaningless (e.g. one can have two equivalent
-- valuations, one continuous and one not).
def function_is_continuous (v : R → with_zero Γ) [is_valuation v] : Prop :=
∀ x : Γ, x ∈ value_group_v v → is_open {r : R | v r < x}

-- This definition is the correct definition of continuity of a valuation. It's constant
-- across equivalence classes (although at the time of writing I've not proved this)
def is_continuous (v : valuation R Γ) : Prop := function_is_continuous v

lemma is_continuous_of_equiv_is_continuous {Γ₁ : Type u₂} [linear_ordered_comm_group Γ₁] {Γ₂ : Type u₃} [linear_ordered_comm_group Γ₂]
 {v₁ : valuation R Γ₁} {v₂ : valuation R Γ₂} (heq : valuation.is_equiv v₁ v₂) (H : v₁.is_continuous) : v₂.is_continuous :=
begin
  dsimp [is_continuous,valuation.function_is_continuous] at H ⊢,
  intros x hx,
  sorry
end

end valuation

namespace Valuation
variables {R : Type u₁} [comm_ring R] [topological_space R] [topological_ring R] [decidable_eq R]

def is_continuous (v : Valuation R) : Prop := valuation.function_is_continuous v

lemma is_continuous_of_equiv_is_continuous {v₁ v₂ : Valuation R} (heq : v₁ ≈ v₂) (H : v₁.is_continuous) : v₂.is_continuous :=
valuation.is_continuous_of_equiv_is_continuous heq H

end Valuation

namespace Spv

variables {R : Type u₁} [comm_ring R] [topological_space R] [topological_ring R] [decidable_eq R]

/-
theorem forall_continuous {R : Type*} [comm_ring R] [topological_space R] [topological_ring R]
  (vs : Spv R) : Spv.is_continuous vs ↔ ∀ (Γ : Type*) [linear_ordered_comm_group Γ],
  by exactI ∀ (v : valuation R Γ), (∀ r s : R, vs.val r s ↔ v r ≤ v s) → valuation.is_continuous v :=
begin
  split,
  { intros Hvs Γ iΓ v Hv,
    cases Hvs with Δ HΔ,
    cases HΔ with iΔ HΔ,
    cases HΔ with w Hw,
    -- this is the hard part
    -- our given w is continuous -> all v are continuous
    intros g Hg,
    sorry
  },
  { intro H,
    cases vs with ineq Hineq,
    cases Hineq with Γ HΓ,
    cases HΓ with iΓ HΓ,
    cases HΓ with v Hv,
    unfold is_continuous,
    existsi Γ,
    existsi iΓ,
    existsi v,
    split,
      exact Hv,
    apply H,
    exact Hv
  }
end
-/

variable (R)
def Cont := {v : Spv R | (v : Valuation R).is_continuous}
variable {R}

def mk_mem_Cont {v : Valuation R} : mk v ∈ Cont R ↔ v.is_continuous :=
begin
split; intro h; refine Valuation.is_continuous_of_equiv_is_continuous _ h,
exact out_mk, exact (setoid.symm out_mk),
end

instance Cont.topological_space : topological_space (Cont R) := by apply_instance

end Spv

/-
Wedhorn p59:
  A valuation v on A is continuous if and only if for all γ ∈ Γ_v (the value group),
  the set A_{≤γ} := { a ∈ A ; v(a) ≥ γ } is open in A.

  This is a typo -- should be v(a) ≤ γ.
-/


/-
Copyright (c) 2020 Ruben Van de Velde. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Ruben Van de Velde
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.analysis.complex.basic
import Mathlib.analysis.normed_space.operator_norm
import Mathlib.data.complex.is_R_or_C
import Mathlib.PostPort

universes u_1 u_2

namespace Mathlib

/-!
# Extending a continuous `ℝ`-linear map to a continuous `𝕜`-linear map

In this file we provide a way to extend a continuous `ℝ`-linear map to a continuous `𝕜`-linear map
in a way that bounds the norm by the norm of the original map, when `𝕜` is either `ℝ` (the
extension is trivial) or `ℂ`. We formulate the extension uniformly, by assuming `is_R_or_C 𝕜`.

We motivate the form of the extension as follows. Note that `fc : F →ₗ[𝕜] 𝕜` is determined fully by
`Re fc`: for all `x : F`, `fc (I • x) = I * fc x`, so `Im (fc x) = -Re (fc (I • x))`. Therefore,
given an `fr : F →ₗ[ℝ] ℝ`, we define `fc x = fr x - fr (I • x) * I`.
-/

/-- Extend `fr : F →ₗ[ℝ] ℝ` to `F →ₗ[𝕜] 𝕜` in a way that will also be continuous and have its norm
bounded by `∥fr∥` if `fr` is continuous. -/
def linear_map.extend_to_𝕜 {𝕜 : Type u_1} [is_R_or_C 𝕜] {F : Type u_2} [normed_group F] [normed_space 𝕜 F] (fr : linear_map ℝ (restrict_scalars ℝ 𝕜 F) ℝ) : linear_map 𝕜 F 𝕜 :=
  let fc : F → 𝕜 := fun (x : F) => ↑(coe_fn fr x) - is_R_or_C.I * ↑(coe_fn fr (is_R_or_C.I • x));
  linear_map.mk fc sorry sorry

/-- The norm of the extension is bounded by `∥fr∥`. -/
theorem norm_bound {𝕜 : Type u_1} [is_R_or_C 𝕜] {F : Type u_2} [normed_group F] [normed_space 𝕜 F] (fr : continuous_linear_map ℝ (restrict_scalars ℝ 𝕜 F) ℝ) (x : F) : norm (coe_fn (linear_map.extend_to_𝕜 (continuous_linear_map.to_linear_map fr)) x) ≤ norm fr * norm x := sorry

/-- Extend `fr : F →L[ℝ] ℝ` to `F →L[𝕜] 𝕜`. -/
def continuous_linear_map.extend_to_𝕜 {𝕜 : Type u_1} [is_R_or_C 𝕜] {F : Type u_2} [normed_group F] [normed_space 𝕜 F] (fr : continuous_linear_map ℝ (restrict_scalars ℝ 𝕜 F) ℝ) : continuous_linear_map 𝕜 F 𝕜 :=
  linear_map.mk_continuous (linear_map.extend_to_𝕜 (continuous_linear_map.to_linear_map fr)) (norm fr) (norm_bound fr)


/-
Copyright (c) 2019 Patrick Massot. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Patrick Massot
-/
import topology.uniform_space.uniform_embedding
import topology.uniform_space.equiv

/-!
# Abstract theory of Hausdorff completions of uniform spaces

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

This file characterizes Hausdorff completions of a uniform space α as complete Hausdorff spaces
equipped with a map from α which has dense image and induce the original uniform structure on α.
Assuming these properties we "extend" uniformly continuous maps from α to complete Hausdorff spaces
to the completions of α. This is the universal property expected from a completion.
It is then used to extend uniformly continuous maps from α to α' to maps between
completions of α and α'.

This file does not construct any such completion, it only study consequences of their existence.
The first advantage is that formal properties are clearly highlighted without interference from
construction details. The second advantage is that this framework can then be used to compare
different completion constructions. See `topology/uniform_space/compare_reals` for an example.
Of course the comparison comes from the universal property as usual.

A general explicit construction of completions is done in `uniform_space/completion`, leading
to a functor from uniform spaces to complete Hausdorff uniform spaces that is left adjoint to the
inclusion, see `uniform_space/UniformSpace` for the category packaging.

## Implementation notes

A tiny technical advantage of using a characteristic predicate such as the properties listed in
`abstract_completion` instead of stating the universal property is that the universal property
derived from the predicate is more universe polymorphic.

## References

We don't know any traditional text discussing this. Real world mathematics simply silently
identify the results of any two constructions that lead to something one could reasonably
call a completion.

## Tags

uniform spaces, completion, universal property
-/

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

open filter set function

universes u
/-- A completion of `α` is the data of a complete separated uniform space (from the same universe)
and a map from `α` with dense range and inducing the original uniform structure on `α`. -/
structure abstract_completion (α : Type u) [uniform_space α] :=
(space : Type u)
(coe : α → space)
(uniform_struct : uniform_space space)
(complete : complete_space space)
(separation : separated_space space)
(uniform_inducing : uniform_inducing coe)
(dense : dense_range coe)

local attribute [instance]
abstract_completion.uniform_struct abstract_completion.complete abstract_completion.separation

namespace abstract_completion
variables {α : Type*} [uniform_space α] (pkg : abstract_completion α)
local notation `hatα` := pkg.space
local notation `ι` := pkg.coe

/-- If `α` is complete, then it is an abstract completion of itself. -/
def of_complete [separated_space α] [complete_space α] : abstract_completion α :=
mk α id infer_instance infer_instance infer_instance uniform_inducing_id dense_range_id

lemma closure_range : closure (range ι) = univ :=
pkg.dense.closure_range

lemma dense_inducing : dense_inducing ι :=
⟨pkg.uniform_inducing.inducing, pkg.dense⟩

lemma uniform_continuous_coe : uniform_continuous ι :=
uniform_inducing.uniform_continuous pkg.uniform_inducing

lemma continuous_coe : continuous ι :=
pkg.uniform_continuous_coe.continuous

@[elab_as_eliminator]
lemma induction_on {p : hatα → Prop}
  (a : hatα) (hp : is_closed {a | p a}) (ih : ∀ a, p (ι a)) : p a :=
is_closed_property pkg.dense hp ih a

variables {β : Type*}

protected lemma funext [topological_space β] [t2_space β] {f g : hatα → β}
  (hf : continuous f) (hg : continuous g)
  (h : ∀ a, f (ι a) = g (ι a)) : f = g :=
funext $ assume a, pkg.induction_on a (is_closed_eq hf hg) h

variables [uniform_space β]

section extend
/-- Extension of maps to completions -/
protected def extend (f : α → β) : hatα → β :=
if uniform_continuous f then
  pkg.dense_inducing.extend f
else
  λ x, f (pkg.dense.some x)

variables {f : α → β}

lemma extend_def (hf : uniform_continuous f) : pkg.extend f = pkg.dense_inducing.extend f :=
if_pos hf

lemma extend_coe [t2_space β] (hf : uniform_continuous f) (a : α) :
  (pkg.extend f) (ι a) = f a :=
begin
  rw pkg.extend_def hf,
  exact pkg.dense_inducing.extend_eq hf.continuous a
end

variables [complete_space β]

lemma uniform_continuous_extend : uniform_continuous (pkg.extend f) :=
begin
  by_cases hf : uniform_continuous f,
  { rw pkg.extend_def hf,
    exact uniform_continuous_uniformly_extend (pkg.uniform_inducing)
      (pkg.dense) hf },
  { change uniform_continuous (ite _ _ _),
    rw if_neg hf,
    exact uniform_continuous_of_const (assume a b, by congr) }
end

lemma continuous_extend : continuous (pkg.extend f) :=
pkg.uniform_continuous_extend.continuous

variables [separated_space β]

lemma extend_unique (hf : uniform_continuous f) {g : hatα → β} (hg : uniform_continuous g)
  (h : ∀ a : α, f a = g (ι a)) : pkg.extend f = g :=
begin
  apply pkg.funext pkg.continuous_extend hg.continuous,
  simpa only [pkg.extend_coe hf] using h
end

@[simp] lemma extend_comp_coe {f : hatα → β} (hf : uniform_continuous f) :
  pkg.extend (f ∘ ι) = f :=
funext $ λ x, pkg.induction_on x (is_closed_eq pkg.continuous_extend hf.continuous)
    (λ y, pkg.extend_coe (hf.comp $ pkg.uniform_continuous_coe) y)

end extend

section map_sec

variables (pkg' : abstract_completion β)
local notation `hatβ` := pkg'.space
local notation `ι'` := pkg'.coe

/-- Lifting maps to completions -/
protected def map (f : α → β) : hatα → hatβ := pkg.extend (ι' ∘ f)

local notation `map` := pkg.map pkg'

variables (f : α → β)

lemma uniform_continuous_map : uniform_continuous (map f) :=
pkg.uniform_continuous_extend

lemma continuous_map : continuous (map f) := pkg.continuous_extend

variables {f}

@[simp] lemma map_coe (hf : uniform_continuous f) (a : α) : map f (ι a) = ι' (f a) :=
pkg.extend_coe (pkg'.uniform_continuous_coe.comp hf) a

lemma map_unique {f : α → β} {g : hatα → hatβ}
  (hg : uniform_continuous g) (h : ∀ a, ι' (f a) = g (ι a)) : map f = g :=
pkg.funext (pkg.continuous_map _ _) hg.continuous $
begin
  intro a,
  change pkg.extend (ι' ∘ f) _ = _,
  simp only [(∘), h],
  rw [pkg.extend_coe (hg.comp pkg.uniform_continuous_coe)]
end

@[simp] lemma map_id : pkg.map pkg id = id :=
pkg.map_unique pkg uniform_continuous_id (assume a, rfl)

variables {γ : Type*} [uniform_space γ]

lemma extend_map [complete_space γ] [separated_space γ] {f : β → γ} {g : α → β}
  (hf : uniform_continuous f) (hg : uniform_continuous g) :
  pkg'.extend f ∘ map g = pkg.extend (f ∘ g) :=
pkg.funext (pkg'.continuous_extend.comp (pkg.continuous_map pkg' _)) pkg.continuous_extend $ λ a,
  by rw [pkg.extend_coe (hf.comp hg), comp_app, pkg.map_coe pkg' hg, pkg'.extend_coe hf]

variables (pkg'' : abstract_completion γ)

lemma map_comp {g : β → γ} {f : α → β} (hg : uniform_continuous g) (hf : uniform_continuous f) :
  (pkg'.map pkg'' g) ∘ (pkg.map pkg' f) = pkg.map pkg'' (g ∘ f) :=
pkg.extend_map pkg' (pkg''.uniform_continuous_coe.comp hg) hf

end map_sec

section compare
-- We can now compare two completion packages for the same uniform space

variables (pkg' : abstract_completion α)

/-- The comparison map between two completions of the same uniform space. -/
def compare : pkg.space → pkg'.space :=
pkg.extend pkg'.coe

lemma compare_coe (a : α) : pkg.compare pkg' (pkg.coe a) = pkg'.coe a :=
pkg.extend_coe pkg'.uniform_continuous_coe a

lemma inverse_compare : (pkg.compare pkg') ∘ (pkg'.compare pkg) = id :=
begin
  have uc := pkg.uniform_continuous_compare pkg',
  have uc' := pkg'.uniform_continuous_compare pkg,
  apply pkg'.funext (uc.comp uc').continuous continuous_id,
  intro a,
  rw [comp_app, pkg'.compare_coe pkg, pkg.compare_coe pkg'],
  refl
end

/-- The uniform bijection between two completions of the same uniform space. -/
def compare_equiv : pkg.space ≃ᵤ pkg'.space :=
{ to_fun := pkg.compare pkg',
  inv_fun := pkg'.compare pkg,
  left_inv := congr_fun (pkg'.inverse_compare pkg),
  right_inv := congr_fun (pkg.inverse_compare pkg'),
  uniform_continuous_to_fun := uniform_continuous_compare _ _,
  uniform_continuous_inv_fun := uniform_continuous_compare _ _, }

lemma uniform_continuous_compare_equiv : uniform_continuous (pkg.compare_equiv pkg') :=
pkg.uniform_continuous_compare pkg'

lemma uniform_continuous_compare_equiv_symm : uniform_continuous (pkg.compare_equiv pkg').symm :=
pkg'.uniform_continuous_compare pkg

end compare

section prod
variables (pkg' : abstract_completion β)
local notation `hatβ` := pkg'.space
local notation `ι'` := pkg'.coe

/-- Products of completions -/
protected def prod : abstract_completion (α × β) :=
{ space := hatα × hatβ,
  coe := λ p, ⟨ι p.1, ι' p.2⟩,
  uniform_struct := prod.uniform_space,
  complete := by apply_instance,
  separation := by apply_instance,
  uniform_inducing := uniform_inducing.prod pkg.uniform_inducing pkg'.uniform_inducing,
  dense := pkg.dense.prod_map pkg'.dense }
end prod

section extension₂
variables (pkg' : abstract_completion β)
local notation `hatβ` := pkg'.space
local notation `ι'` := pkg'.coe

variables {γ : Type*} [uniform_space γ]

open function

/-- Extend two variable map to completions. -/
protected def extend₂ (f : α → β → γ) : hatα → hatβ → γ :=
curry $ (pkg.prod pkg').extend (uncurry f)

section separated_space
variables [separated_space γ] {f : α → β → γ}

lemma extension₂_coe_coe (hf : uniform_continuous $ uncurry f) (a : α) (b : β) :
  pkg.extend₂ pkg' f (ι a) (ι' b) = f a b :=
show (pkg.prod pkg').extend (uncurry f) ((pkg.prod pkg').coe (a, b)) = uncurry f (a, b),
  from (pkg.prod pkg').extend_coe hf _

end separated_space

variables {f : α → β → γ}

variables [complete_space γ] (f)

lemma uniform_continuous_extension₂ : uniform_continuous₂ (pkg.extend₂ pkg' f) :=
begin
  rw [uniform_continuous₂_def, abstract_completion.extend₂, uncurry_curry],
  apply uniform_continuous_extend
end

end extension₂

section map₂
variables (pkg' : abstract_completion β)
local notation `hatβ` := pkg'.space
local notation `ι'` := pkg'.coe

variables {γ : Type*} [uniform_space γ] (pkg'' : abstract_completion γ)
local notation `hatγ` := pkg''.space
local notation `ι''` := pkg''.coe

local notation f ` ∘₂ ` g := bicompr f g

/-- Lift two variable maps to completions. -/
protected def map₂ (f : α → β → γ) : hatα → hatβ → hatγ :=
pkg.extend₂ pkg' (pkg''.coe ∘₂ f)

lemma uniform_continuous_map₂ (f : α → β → γ) : uniform_continuous₂ (pkg.map₂ pkg' pkg'' f) :=
pkg.uniform_continuous_extension₂ pkg' _

lemma continuous_map₂ {δ} [topological_space δ] {f : α → β → γ}
  {a : δ → hatα} {b : δ → hatβ} (ha : continuous a) (hb : continuous b) :
  continuous (λd:δ, pkg.map₂ pkg' pkg'' f (a d) (b d)) :=
((pkg.uniform_continuous_map₂ pkg' pkg'' f).continuous.comp (continuous.prod_mk ha hb) : _)

lemma map₂_coe_coe (a : α) (b : β) (f : α → β → γ) (hf : uniform_continuous₂ f) :
  pkg.map₂ pkg' pkg'' f (ι a) (ι' b) = ι'' (f a b) :=
pkg.extension₂_coe_coe pkg' (pkg''.uniform_continuous_coe.comp hf) a b

end map₂
end abstract_completion


import mam.Cislo1
import Mathlib.Data.Rat.Basic
import Mathlib.Data.Real.Basic
import Mathlib.Tactic.Linarith
import Mathlib.Tactic.LibrarySearch

theorem konjunkce_komutativni_i1 {P Q : Prop} (predpoklad : P ∧ Q) : Q ∧ P := by
  cases' predpoklad with p q
  constructor
  · exact q
  · exact p

theorem konjunkce_komutativni_i2 {P Q : Prop} : P ∧ Q → Q ∧ P := by
  intro predpoklad
  rcases predpoklad with ⟨p,q⟩
  constructor
  · exact q
  · exact p

theorem konjunkce_komutativni_i3 {P Q : Prop} : P ∧ Q → Q ∧ P := by
  rintro ⟨p,q⟩
  constructor
  · exact q
  · exact p

theorem konjunkce_komutativni_i4 {P Q : Prop} : P ∧ Q → Q ∧ P := by
  intro ⟨p,q⟩
  exact ⟨q,p⟩

theorem konjunkce_komutativni_i5 {P Q : Prop} : P ∧ Q → Q ∧ P := by
  intro predpoklad
  exact And.symm predpoklad

theorem konjunkce_komutativni_i6 {P Q : Prop} : P ∧ Q → Q ∧ P := by
  exact And.symm

theorem konjunkce_komutativni_i7 {P Q : Prop} : P ∧ Q → Q ∧ P :=
And.symm

theorem konjunkce_komutativni_e1 {P Q : Prop} : P ∧ Q ↔ Q ∧ P := by
  constructor
  · intro ⟨p,q⟩
    exact ⟨q,p⟩
  · intro ⟨q,p⟩
    exact ⟨p,q⟩

theorem konjunkce_komutativni_e2 {P Q : Prop} : P ∧ Q ↔ Q ∧ P := by
  constructor
  · intro predpoklad
    exact konjunkce_komutativni_i1 predpoklad
  · intro predpoklad
    exact konjunkce_komutativni_i1 predpoklad

theorem konjunkce_komutativni_e3 {P Q : Prop} : P ∧ Q ↔ Q ∧ P := by
  constructor
  · apply konjunkce_komutativni_i7
  · apply konjunkce_komutativni_i7

theorem konjunkce_komutativni_r {P Q : Prop} : (P ∧ Q) = (Q ∧ P) := by
  rw [konjunkce_komutativni_e3]

theorem blbina : 1 + 1 ≠ 3 := by ring

example : ¬ (1 + 1 = 3) := blbina

example : (1 + 1 = 3) → False := blbina

theorem nemozna_ekvivalence {P : Prop} : (P ↔ ¬ P) → False := by
  intro hyp
  by_cases p : P
  · have negace : ¬ P
    · rw [hyp] at p
      exact p
    exact negace p
  apply p
  rw [hyp]
  exact p

theorem krat_dva : ∀ n : ℕ, n * 2 = n + n := by
  intro x
  ring

theorem cislo_55_je_fibonacciho : ∃ n : ℕ, fibonacci n = 55 := by
  use 10
  rfl

theorem tesne : ∀ n : ℕ, ∃ m : ℕ, ∀ k : ℕ, (k ≤ n → k < m) ∧ (n < k → m ≤ k) := by
  intro n
  use n + 1
  intro k
  constructor
  · intro k_le_n
    rw [Nat.lt_succ]
    exact k_le_n
  · intro n_lt_k
    rw [Nat.succ_le]
    exact n_lt_k

theorem tesne' (n : ℕ) : ∃ m : ℕ, ∀ k : ℕ, (k ≤ n → k < m) ∧ (n < k → m ≤ k) := by
  use n + 1
  intro k
  constructor
  · apply Nat.lt_succ_of_le
  · apply Nat.succ_le.mpr

theorem racionalni_cisla_jsou_husta (x z : ℚ) : x < z → ∃ y : ℚ, x < y ∧ y < z := by
  intro mensi
  use (x + z) / 2
  constructor
  · convert_to x / 2 + x / 2 < x / 2 + z / 2
    · ring
    · rw [add_div]
    · apply add_lt_add_left
      exact div_lt_div_of_lt two_pos mensi
  · convert_to x / 2 + z / 2 < z / 2 + z / 2
    · rw [add_div]
    · ring
    · apply add_lt_add_right
      exact div_lt_div_of_lt two_pos mensi

theorem realna_cisla_jsou_husta : ∀ x z : ℝ, x < z → ∃ y : ℝ, x < y ∧ y < z := by
  intro x z mensi
  use (x + z) / 2
  constructor
  · convert_to x / 2 + x / 2 < x / 2 + z / 2
    · ring
    · rw [add_div]
    · apply add_lt_add_left
      exact div_lt_div_of_lt two_pos mensi
  · convert_to x / 2 + z / 2 < z / 2 + z / 2
    · rw [add_div]
    · ring
    · apply add_lt_add_right
      exact div_lt_div_of_lt two_pos mensi

theorem Cantorova_veta (T : Type) : ¬ (∃ f : T → Set T, Function.Surjective f) := by
  intro pro_spor
  cases' pro_spor with f surjektivni
  cases' surjektivni (fun x => x ∉ f x) with a sporne
  have paradox : (a ∈ f a) ↔ (a ∉ f a)
  · exact of_eq (congrArg (Membership.mem a) sporne)
  exact nemozna_ekvivalence paradox

theorem Cantoruv_dusledek (T : Type) : ¬ (∃ g : Set T → T, Function.Injective g) := by
  intro pro_spor
  cases' pro_spor with f prosta
  apply Cantorova_veta
  use Function.invFun f
  exact Function.invFun_surjective prosta