import data.complex.basic
open cau_seq

namespace real

lemma eq_lim_of_const_equiv {f : cau_seq ℝ abs} {x : ℝ} (h : cau_seq.const abs x ≈ f) : x = lim f :=
const_equiv.mp (setoid.trans h (equiv_lim f))

lemma lim_eq_of_equiv_const {f : cau_seq ℝ abs} {x : ℝ} (h : f ≈ cau_seq.const abs x) : lim f = x :=
(eq_lim_of_const_equiv (setoid.symm h)).symm

lemma lim_eq_lim_of_equiv {f g : cau_seq ℝ abs} (h : f ≈ g) : lim f = lim g :=
lim_eq_of_equiv_const (setoid.trans h (equiv_lim g))

@[simp] lemma lim_const (x : ℝ) : lim (const abs x) = x :=
lim_eq_of_equiv_const (setoid.refl _)

lemma lim_add (f g : cau_seq ℝ abs) : lim f + lim g = lim ⇑(f + g) :=
eq_lim_of_const_equiv (show lim_zero (const abs (lim ⇑f + lim ⇑g) - (f + g)),
  from by rw [const_add, add_sub_comm];
  exact add_lim_zero (setoid.symm (equiv_lim f)) (setoid.symm (equiv_lim g)))

lemma lim_mul_lim (f g : cau_seq ℝ abs) : lim f * lim g = lim ⇑(f * g) :=
eq_lim_of_const_equiv (show lim_zero (const abs (lim ⇑f * lim ⇑g) - f * g),
  from have h : const abs (lim ⇑f * lim ⇑g) - f * g = g * (const abs (lim f) - f)
      + const abs (lim f) * (const abs (lim g) - g) :=
    by simp [mul_sub, mul_comm, const_mul, mul_add],
  by rw h; exact add_lim_zero (mul_lim_zero _ (setoid.symm (equiv_lim f)))
      (mul_lim_zero _ (setoid.symm (equiv_lim g))))

lemma lim_mul (f : cau_seq ℝ abs) (x : ℝ) : lim f * x = lim ⇑(f * const abs x) :=
by rw [← lim_mul_lim, lim_const]

lemma lim_neg (f : cau_seq ℝ abs) : lim ⇑(-f) = -lim f :=
lim_eq_of_equiv_const (show lim_zero (-f - const abs (-lim ⇑f)),
  from by rw [const_neg, sub_neg_eq_add, add_comm];
  exact setoid.symm (equiv_lim f))

lemma lim_eq_zero_iff (f : cau_seq ℝ abs) : lim f = 0 ↔ lim_zero f :=
⟨assume h,
  by have hf := equiv_lim f;
  rw h at hf;
  exact (lim_zero_congr hf).mpr (const_lim_zero.mpr rfl),
assume h,
  have h₁ : f = (f - const abs 0) := ext (λ n, by simp [sub_apply, const_apply]),
  by rw h₁ at h; exact lim_eq_of_equiv_const h ⟩

end real

namespace complex

local notation `abs'` := _root_.abs

open cau_seq

lemma is_cau_seq_coe (f : cau_seq ℝ abs') : is_cau_seq abs (λ n, f n) :=
  show ∀ ε > 0, ∃ (i : ℕ), ∀ (j : ℕ), j ≥ i → abs (↑(f j) - ↑(f i)) < ε,
  from by simp only [(of_real_sub _ _).symm, abs_of_real]; exact f.2

lemma is_cau_seq_of_im_re {f : ℕ → ℂ} (hre : is_cau_seq abs' (λ n, (f n).re)) (him : is_cau_seq abs' (λ n, (f n).im)) :
    is_cau_seq abs f :=
have h : f = (λ n, (f n).re + (f n).im * I) := funext (λ n, (re_add_im (f n)).symm),
let g : cau_seq ℂ abs := ⟨_, is_cau_seq_coe ⟨_, hre⟩⟩ + ⟨_, is_cau_seq_coe ⟨_, him⟩⟩ * cau_seq.const abs I in
by rw h; exact g.2

namespace cau_seq

noncomputable def conj (f : cau_seq ℂ abs) : cau_seq ℂ abs := ⟨λ n, complex.conj (f n), is_cau_seq_of_im_re (is_cau_seq_re f) (is_cau_seq_im (-f))⟩

end cau_seq

open cau_seq

theorem const_equiv1 {α β : Type*} [discrete_linear_ordered_field α] [ring β] {abv : β → α} [is_absolute_value abv] {x y : β} : const abv x ≈ const abv y ↔ x = y :=
show lim_zero _ ↔ _, by rw [← const_sub, const_lim_zero, sub_eq_zero]

@[simp] lemma conj_apply (f : cau_seq ℂ abs) (n : ℕ) : (cau_seq.conj f) n = conj (f n) := rfl

lemma eq_lim_of_const_equiv {f : cau_seq ℂ abs} {x : ℂ} (h : cau_seq.const abs x ≈ f) : x = lim f :=
const_equiv1.mp (setoid.trans h (equiv_lim f))

lemma lim_eq_of_equiv_const {f : cau_seq ℂ abs} {x : ℂ} (h : f ≈ cau_seq.const abs x) : lim f = x :=
(eq_lim_of_const_equiv (setoid.symm h)).symm

lemma lim_eq_lim_of_equiv {f g : cau_seq ℂ abs} (h : f ≈ g) : lim f = lim g :=
lim_eq_of_equiv_const (setoid.trans h (equiv_lim g))

@[simp] lemma lim_const (x : ℂ) : lim (const abs x) = x :=
lim_eq_of_equiv_const (setoid.refl _)

lemma lim_add (f g : cau_seq ℂ abs) : lim f + lim g = lim ⇑(f + g) :=
eq_lim_of_const_equiv (show lim_zero (const abs (lim ⇑f + lim ⇑g) - (f + g)),
  from by rw [const_add, add_sub_comm];
  exact add_lim_zero (setoid.symm (equiv_lim f)) (setoid.symm (equiv_lim g)))

lemma lim_mul_lim (f g : cau_seq ℂ abs) : lim f * lim g = lim ⇑(f * g) :=
eq_lim_of_const_equiv (show lim_zero (const abs (lim ⇑f * lim ⇑g) - f * g),
  from have h : const abs (lim ⇑f * lim ⇑g) - f * g = g * (const abs (lim f) - f)
      + const abs (lim f) * (const abs (lim g) - g) :=
    by simp [mul_sub, mul_comm, const_mul, mul_add],
  by rw h; exact add_lim_zero (mul_lim_zero _ (setoid.symm (equiv_lim f)))
      (mul_lim_zero _ (setoid.symm (equiv_lim g))))

lemma lim_mul (f : cau_seq ℂ abs) (x : ℝ) : lim f * x = lim ⇑(f * const abs x) :=
by rw [← lim_mul_lim, lim_const]

lemma lim_neg (f : cau_seq ℂ abs) : lim ⇑(-f) = -lim f :=
lim_eq_of_equiv_const (show lim_zero (-f - const abs (-lim ⇑f)),
  from by rw [const_neg, sub_neg_eq_add, add_comm];
  exact setoid.symm (equiv_lim f))

lemma lim_conj (f : cau_seq ℂ abs) : lim (cau_seq.conj f) = conj (lim f) :=
begin
  unfold lim,
  apply ext,
  { simp },
  { simp,
  have := real.lim_neg ⟨_, is_cau_seq_im f⟩,
    exact this }
end
-- f : cau_seq ℝ abs, -f = (λ n, -f n)
lemma lim_eq_zero_iff (f : cau_seq ℂ abs) : lim f = 0 ↔ lim_zero f :=
⟨assume h,
  by have hf := equiv_lim f;
  rw h at hf;
  exact (lim_zero_congr hf).mpr (const_lim_zero.mpr rfl),
assume h,
  have h₁ : f = (f - const abs 0) := cau_seq.ext (λ n, by simp [sub_apply, const_apply]),
  by rw h₁ at h; exact lim_eq_of_equiv_const h ⟩

end complex


import tactic
import languages.basic
import languages.star
import regular.regex
import automata.dfa
import automata.epsnfa
import automata.epsnfa_append
import automata.epsnfa_star
import automata.epsnfa_triv

open languages regex DFA epsnfa

namespace regex.to_dfa

variables {S : Type} [fintype S]

theorem regex_to_dfa {L : set (list S)} : regex_lang L → dfa_lang L :=
begin
    rintro ⟨r, rfl⟩,
    induction r, {
        rw regex_empty_is_empty_lang,
        exact epsnfa_to_dfa_eq triv.empty_is_epsnfa_lang,
    }, {
        rw regex_eps_is_eps_lang,
        exact epsnfa_to_dfa_eq triv.eps_is_epsnfa_lang,
    }, {
        rw regex_one_is_one_lang,
        exact epsnfa_to_dfa_eq triv.one_is_epsnfa_lang,
    }, {
        rw regex_union_is_lang_union,
        convert union_is_dfa _ _;
        assumption,
    }, {
        rw regex_append_is_lang_append,
        apply epsnfa_to_dfa_eq,
        refine append.append_is_epsnfa (dfa_to_epsnfa_eq _) (dfa_to_epsnfa_eq _ );
        assumption,
    }, {
        rw regex_star_is_kleene_star,
        apply epsnfa_to_dfa_eq,
        refine star.star_is_epsnfa (dfa_to_epsnfa_eq r_ih),
    }
end

end regex.to_dfa


variables (x y z : ℕ)
variables (h₁ : x = y) (h₂ : y = z)

section include_hs
include h₁ h₂

theorem foo : x = z :=
begin
  rw [h₁, h₂]
end

end include_hs


/-
Copyright (c) 2020 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.algebra.category.Module.monoidal
import Mathlib.algebra.category.Algebra.basic
import Mathlib.category_theory.monoidal.Mon_
import Mathlib.PostPort

universes u

namespace Mathlib

/-!
# `Mon_ (Module R) ≌ Algebra R`

The category of internal monoid objects in `Module R`
is equivalent to the category of "native" bundled `R`-algebras.

Moreover, this equivalence is compatible with the forgetful functors to `Module R`.
-/

namespace Module

namespace Mon_Module_equivalence_Algebra

@[simp] theorem Mon_.X.ring_zero {R : Type u} [comm_ring R] (A : Mon_ (Module R)) : 0 = add_comm_group.zero :=
  Eq.refl 0

protected instance Mon_.X.algebra {R : Type u} [comm_ring R] (A : Mon_ (Module R)) : algebra R ↥(Mon_.X A) :=
  algebra.mk (ring_hom.mk (linear_map.to_fun (Mon_.one A)) sorry sorry sorry sorry) sorry sorry

@[simp] theorem algebra_map {R : Type u} [comm_ring R] (A : Mon_ (Module R)) (r : R) : coe_fn (algebra_map R ↥(Mon_.X A)) r = coe_fn (Mon_.one A) r :=
  rfl

/--
Converting a monoid object in `Module R` to a bundled algebra.
-/
@[simp] theorem functor_obj {R : Type u} [comm_ring R] (A : Mon_ (Module R)) : category_theory.functor.obj functor A = Algebra.of R ↥(Mon_.X A) :=
  Eq.refl (category_theory.functor.obj functor A)

/--
Converting a bundled algebra to a monoid object in `Module R`.
-/
def inverse_obj {R : Type u} [comm_ring R] (A : Algebra R) : Mon_ (Module R) :=
  Mon_.mk (of R ↥A) (algebra.linear_map R ↥A) (algebra.lmul' R)

/--
Converting a bundled algebra to a monoid object in `Module R`.
-/
@[simp] theorem inverse_map_hom {R : Type u} [comm_ring R] (A : Algebra R) (B : Algebra R) (f : A ⟶ B) : Mon_.hom.hom (category_theory.functor.map inverse f) = alg_hom.to_linear_map f :=
  Eq.refl (Mon_.hom.hom (category_theory.functor.map inverse f))

end Mon_Module_equivalence_Algebra

/--
The category of internal monoid objects in `Module R`
is equivalent to the category of "native" bundled `R`-algebras.
-/
def Mon_Module_equivalence_Algebra {R : Type u} [comm_ring R] : Mon_ (Module R) ≌ Algebra R :=
  category_theory.equivalence.mk' sorry sorry
    (category_theory.nat_iso.of_components
      (fun (A : Mon_ (Module R)) =>
        category_theory.iso.mk (Mon_.hom.mk (linear_map.mk id sorry sorry)) (Mon_.hom.mk (linear_map.mk id sorry sorry)))
      sorry)
    (category_theory.nat_iso.of_components
      (fun (A : Algebra R) =>
        category_theory.iso.mk (alg_hom.mk id sorry sorry sorry sorry sorry)
          (alg_hom.mk id sorry sorry sorry sorry sorry))
      sorry)

/--
The equivalence `Mon_ (Module R) ≌ Algebra R`
is naturally compatible with the forgetful functors to `Module R`.
-/
def Mon_Module_equivalence_Algebra_forget {R : Type u} [comm_ring R] : Mon_Module_equivalence_Algebra.functor ⋙ category_theory.forget₂ (Algebra R) (Module R) ≅ Mon_.forget (Module R) :=
  category_theory.nat_iso.of_components
    (fun (A : Mon_ (Module R)) => category_theory.iso.mk (linear_map.mk id sorry sorry) (linear_map.mk id sorry sorry))
    sorry


/-
Copyright © 2020 Nicolò Cavalleri. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Nicolò Cavalleri
-/

import geometry.manifold.algebra.smooth_functions
import ring_theory.derivation

/-!

# Derivation bundle

In this file we define the derivations at a point of a manifold on the algebra of smooth fuctions.
Moreover, we define the differential of a function in terms of derivations.

The content of this file is not meant to be regarded as an alternative definition to the current
tangent bundle but rather as a purely algebraic theory that provides a purely algebraic definition
of the Lie algebra for a Lie group.

-/

variables (𝕜 : Type*) [nondiscrete_normed_field 𝕜]
{E : Type*} [normed_group E] [normed_space 𝕜 E]
{H : Type*} [topological_space H] (I : model_with_corners 𝕜 E H)
(M : Type*) [topological_space M] [charted_space H M] (n : with_top ℕ)

open_locale manifold

-- the following two instances prevent poorly understood type class inference timeout problems
instance smooth_functions_algebra : algebra 𝕜 C^∞⟮I, M; 𝕜⟯ := by apply_instance
instance smooth_functions_tower : is_scalar_tower 𝕜 C^∞⟮I, M; 𝕜⟯ C^∞⟮I, M; 𝕜⟯ := by apply_instance

/-- Type synonym, introduced to put a different `has_smul` action on `C^n⟮I, M; 𝕜⟯`
which is defined as `f • r = f(x) * r`. -/
@[nolint unused_arguments] def pointed_smooth_map (x : M) := C^n⟮I, M; 𝕜⟯

localized "notation `C^` n `⟮` I `,` M `;` 𝕜 `⟯⟨` x `⟩` :=
  pointed_smooth_map 𝕜 I M n x" in derivation

variables {𝕜 M}

namespace pointed_smooth_map

instance {x : M} : has_coe_to_fun C^∞⟮I, M; 𝕜⟯⟨x⟩ (λ _, M → 𝕜) :=
cont_mdiff_map.has_coe_to_fun
instance {x : M} : comm_ring C^∞⟮I, M; 𝕜⟯⟨x⟩ := smooth_map.comm_ring
instance {x : M} : algebra 𝕜 C^∞⟮I, M; 𝕜⟯⟨x⟩ := smooth_map.algebra
instance {x : M} : inhabited C^∞⟮I, M; 𝕜⟯⟨x⟩ := ⟨0⟩
instance {x : M} : algebra C^∞⟮I, M; 𝕜⟯⟨x⟩ C^∞⟮I, M; 𝕜⟯ := algebra.id C^∞⟮I, M; 𝕜⟯
instance {x : M} : is_scalar_tower 𝕜 C^∞⟮I, M; 𝕜⟯⟨x⟩ C^∞⟮I, M; 𝕜⟯ := is_scalar_tower.right

variable {I}

/-- `smooth_map.eval_ring_hom` gives rise to an algebra structure of `C^∞⟮I, M; 𝕜⟯` on `𝕜`. -/
instance eval_algebra {x : M} : algebra C^∞⟮I, M; 𝕜⟯⟨x⟩ 𝕜 :=
(smooth_map.eval_ring_hom x : C^∞⟮I, M; 𝕜⟯⟨x⟩ →+* 𝕜).to_algebra

/-- With the `eval_algebra` algebra structure evaluation is actually an algebra morphism. -/
def eval (x : M) : C^∞⟮I, M; 𝕜⟯ →ₐ[C^∞⟮I, M; 𝕜⟯⟨x⟩] 𝕜 :=
algebra.of_id C^∞⟮I, M; 𝕜⟯⟨x⟩ 𝕜

lemma smul_def (x : M) (f : C^∞⟮I, M; 𝕜⟯⟨x⟩) (k : 𝕜) : f • k = f x * k := rfl

instance (x : M) : is_scalar_tower 𝕜 C^∞⟮I, M; 𝕜⟯⟨x⟩ 𝕜 :=
{ smul_assoc := λ k f h, by { simp only [smul_def, algebra.id.smul_eq_mul, smooth_map.coe_smul,
  pi.smul_apply, mul_assoc]} }

end pointed_smooth_map

open_locale derivation

/-- The derivations at a point of a manifold. Some regard this as a possible definition of the
tangent space -/
@[reducible] def point_derivation (x : M) := derivation 𝕜 (C^∞⟮I, M; 𝕜⟯⟨x⟩) 𝕜

section

variables (I) {M} (X Y : derivation 𝕜 C^∞⟮I, M; 𝕜⟯ C^∞⟮I, M; 𝕜⟯) (f g : C^∞⟮I, M; 𝕜⟯) (r : 𝕜)

/-- Evaluation at a point gives rise to a `C^∞⟮I, M; 𝕜⟯`-linear map between `C^∞⟮I, M; 𝕜⟯` and `𝕜`.
 -/
def smooth_function.eval_at (x : M) : C^∞⟮I, M; 𝕜⟯ →ₗ[C^∞⟮I, M; 𝕜⟯⟨x⟩] 𝕜 :=
(pointed_smooth_map.eval x).to_linear_map

namespace derivation

variable {I}

/-- The evaluation at a point as a linear map. -/
def eval_at (x : M) : (derivation 𝕜 C^∞⟮I, M; 𝕜⟯ C^∞⟮I, M; 𝕜⟯) →ₗ[𝕜] point_derivation I x :=
(smooth_function.eval_at I x).comp_der

lemma eval_at_apply (x : M) : eval_at x X f = (X f) x := rfl

end derivation

variables {I} {E' : Type*} [normed_group E'] [normed_space 𝕜 E']
{H' : Type*} [topological_space H'] {I' : model_with_corners 𝕜 E' H'}
{M' : Type*} [topological_space M'] [charted_space H' M']

/-- The heterogeneous differential as a linear map. Instead of taking a function as an argument this
differential takes `h : f x = y`. It is particularly handy to deal with situations where the points
on where it has to be evaluated are equal but not definitionally equal. -/
def hfdifferential {f : C^∞⟮I, M; I', M'⟯} {x : M} {y : M'} (h : f x = y) :
  point_derivation I x →ₗ[𝕜] point_derivation I' y :=
{ to_fun := λ v, derivation.mk'
    { to_fun := λ g, v (g.comp f),
      map_add' := λ g g', by rw [smooth_map.add_comp, derivation.map_add],
      map_smul' := λ k g,
        by simp only [smooth_map.smul_comp, derivation.map_smul, ring_hom.id_apply], }
    (λ g g', by simp only [derivation.leibniz, smooth_map.mul_comp, linear_map.coe_mk,
      pointed_smooth_map.smul_def, cont_mdiff_map.comp_apply, h]),
  map_smul' := λ k v, rfl,
  map_add' := λ v w, rfl }

/-- The homogeneous differential as a linear map. -/
def fdifferential (f : C^∞⟮I, M; I', M'⟯) (x : M) :
  point_derivation I x →ₗ[𝕜] point_derivation I' (f x) :=
hfdifferential (rfl : f x = f x)

/- Standard notation for the differential. The abbreviation is `MId`. -/
localized "notation `𝒅` := fdifferential" in manifold

/- Standard notation for the differential. The abbreviation is `MId`. -/
localized "notation `𝒅ₕ` := hfdifferential" in manifold

@[simp] lemma apply_fdifferential (f : C^∞⟮I, M; I', M'⟯) {x : M} (v : point_derivation I x)
  (g : C^∞⟮I', M'; 𝕜⟯) : 𝒅f x v g = v (g.comp f) := rfl

@[simp] lemma apply_hfdifferential {f : C^∞⟮I, M; I', M'⟯} {x : M} {y : M'} (h : f x = y)
  (v : point_derivation I x) (g : C^∞⟮I', M'; 𝕜⟯) : 𝒅ₕh v g = 𝒅f x v g := rfl

variables {E'' : Type*} [normed_group E''] [normed_space 𝕜 E'']
{H'' : Type*} [topological_space H''] {I'' : model_with_corners 𝕜 E'' H''}
{M'' : Type*} [topological_space M''] [charted_space H'' M'']

@[simp] lemma fdifferential_comp (g : C^∞⟮I', M'; I'', M''⟯) (f : C^∞⟮I, M; I', M'⟯) (x : M) :
  𝒅(g.comp f) x = (𝒅g (f x)).comp (𝒅f x) := rfl

end


/-
Copyright (c) 2019 Seul Baek. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Seul Baek
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.tactic.localized
import Mathlib.PostPort

namespace Mathlib

/-
Miscellaneous.
-/

namespace omega

theorem fun_mono_2 {α : Type} {β : Type} {γ : Type} {p : α → β → γ} {a1 : α} {a2 : α} {b1 : β}
    {b2 : β} : a1 = a2 → b1 = b2 → p a1 b1 = p a2 b2 :=
  fun (h1 : a1 = a2) (h2 : b1 = b2) =>
    eq.mpr (id (Eq._oldrec (Eq.refl (p a1 b1 = p a2 b2)) h1))
      (eq.mpr (id (Eq._oldrec (Eq.refl (p a2 b1 = p a2 b2)) h2)) (Eq.refl (p a2 b2)))

theorem pred_mono_2 {α : Type} {β : Type} {p : α → β → Prop} {a1 : α} {a2 : α} {b1 : β} {b2 : β} :
    a1 = a2 → b1 = b2 → (p a1 b1 ↔ p a2 b2) :=
  fun (h1 : a1 = a2) (h2 : b1 = b2) =>
    eq.mpr (id (Eq._oldrec (Eq.refl (p a1 b1 ↔ p a2 b2)) h1))
      (eq.mpr (id (Eq._oldrec (Eq.refl (p a2 b1 ↔ p a2 b2)) h2)) (iff.refl (p a2 b2)))

theorem pred_mono_2' {c : Prop → Prop → Prop} {a1 : Prop} {a2 : Prop} {b1 : Prop} {b2 : Prop} :
    (a1 ↔ a2) → (b1 ↔ b2) → (c a1 b1 ↔ c a2 b2) :=
  fun (h1 : a1 ↔ a2) (h2 : b1 ↔ b2) =>
    eq.mpr (id (Eq._oldrec (Eq.refl (c a1 b1 ↔ c a2 b2)) (propext h1)))
      (eq.mpr (id (Eq._oldrec (Eq.refl (c a2 b1 ↔ c a2 b2)) (propext h2))) (iff.refl (c a2 b2)))

/-- Update variable assignment for a specific variable
    and leave everything else unchanged -/
def update {α : Type} (m : ℕ) (a : α) (v : ℕ → α) : ℕ → α := sorry

theorem update_eq {α : Type} (m : ℕ) (a : α) (v : ℕ → α) : update m a v m = a := sorry

theorem update_eq_of_ne {α : Type} {m : ℕ} {a : α} {v : ℕ → α} (k : ℕ) :
    k ≠ m → update m a v k = v k :=
  sorry

/-- Assign a new value to the zeroth variable, and push all
    other assignments up by 1 -/
def update_zero {α : Type} (a : α) (v : ℕ → α) : ℕ → α := sorry

end Mathlib


/-
Copyright (c) 2019 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison

! This file was ported from Lean 3 source module algebra.category.Group.colimits
! leanprover-community/mathlib commit 70fd9563a21e7b963887c9360bd29b2393e6225a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Algebra.Category.Group.Preadditive
import Mathbin.GroupTheory.QuotientGroup
import Mathbin.CategoryTheory.Limits.Shapes.Kernels
import Mathbin.CategoryTheory.ConcreteCategory.Elementwise

/-!
# The category of additive commutative groups has all colimits.

This file uses a "pre-automated" approach, just as for `Mon/colimits.lean`.
It is a very uniform approach, that conceivably could be synthesised directly
by a tactic that analyses the shape of `add_comm_group` and `monoid_hom`.

TODO:
In fact, in `AddCommGroup` there is a much nicer model of colimits as quotients
of finitely supported functions, and we really should implement this as well (or instead).
-/

universe u v

open CategoryTheory

open CategoryTheory.Limits

-- [ROBOT VOICE]:
-- You should pretend for now that this file was automatically generated.
-- It follows the same template as colimits in Mon.
namespace AddCommGroupCat.Colimits

/-!
We build the colimit of a diagram in `AddCommGroup` by constructing the
free group on the disjoint union of all the abelian groups in the diagram,
then taking the quotient by the abelian group laws within each abelian group,
and the identifications given by the morphisms in the diagram.
-/

variable {J : Type v} [SmallCategory J] (F : J ⥤ AddCommGroupCat.{v})

/-- An inductive type representing all group expressions (without relations)
on a collection of types indexed by the objects of `J`.
-/
inductive Prequotient-- There's always `of`

  | of : ∀ (j : J) (x : F.obj j), prequotient-- Then one generator for each operation

  | zero : prequotient
  | neg : prequotient → prequotient
  | add : prequotient → prequotient → prequotient
#align AddCommGroup.colimits.prequotient AddCommGroupCat.Colimits.Prequotient

instance : Inhabited (Prequotient F) :=
  ⟨Prequotient.zero⟩

open Prequotient

/-- The relation on `prequotient` saying when two expressions are equal
because of the abelian group laws, or
because one element is mapped to another by a morphism in the diagram.
-/
inductive Relation : Prequotient F → Prequotient F → Prop-- Make it an equivalence relation:

  | refl : ∀ x, relation x x
  | symm : ∀ (x y) (h : relation x y), relation y x
  |
  trans :
    ∀ (x y z) (h : relation x y) (k : relation y z), relation x z-- There's always a `map` relation

  |
  map :
    ∀ (j j' : J) (f : j ⟶ j') (x : F.obj j),
      relation (of j' (F.map f x))
        (of j x)-- Then one relation per operation, describing the interaction with `of`

  | zero : ∀ j, relation (of j 0) zero
  | neg : ∀ (j) (x : F.obj j), relation (of j (-x)) (neg (of j x))
  |
  add :
    ∀ (j) (x y : F.obj j),
      relation (of j (x + y))
        (add (of j x) (of j y))-- Then one relation per argument of each operation

  | neg_1 : ∀ (x x') (r : relation x x'), relation (neg x) (neg x')
  | add_1 : ∀ (x x' y) (r : relation x x'), relation (add x y) (add x' y)
  |
  add_2 : ∀ (x y y') (r : relation y y'), relation (add x y) (add x y')-- And one relation per axiom

  | zero_add : ∀ x, relation (add zero x) x
  | add_zero : ∀ x, relation (add x zero) x
  | add_left_neg : ∀ x, relation (add (neg x) x) zero
  | add_comm : ∀ x y, relation (add x y) (add y x)
  | add_assoc : ∀ x y z, relation (add (add x y) z) (add x (add y z))
#align AddCommGroup.colimits.relation AddCommGroupCat.Colimits.Relation

/--
The setoid corresponding to group expressions modulo abelian group relations and identifications.
-/
def colimitSetoid : Setoid (Prequotient F)
    where
  R := Relation F
  iseqv := ⟨Relation.refl, Relation.symm, Relation.trans⟩
#align AddCommGroup.colimits.colimit_setoid AddCommGroupCat.Colimits.colimitSetoid

attribute [instance] colimit_setoid

/-- The underlying type of the colimit of a diagram in `AddCommGroup`.
-/
def ColimitType : Type v :=
  Quotient (colimitSetoid F)deriving Inhabited
#align AddCommGroup.colimits.colimit_type AddCommGroupCat.Colimits.ColimitType

instance : AddCommGroup (ColimitType F)
    where
  zero := Quot.mk _ zero
  neg := by
    fapply @Quot.lift
    · intro x
      exact Quot.mk _ (neg x)
    · intro x x' r
      apply Quot.sound
      exact relation.neg_1 _ _ r
  add := by
    fapply @Quot.lift _ _ (colimit_type F → colimit_type F)
    · intro x
      fapply @Quot.lift
      · intro y
        exact Quot.mk _ (add x y)
      · intro y y' r
        apply Quot.sound
        exact relation.add_2 _ _ _ r
    · intro x x' r
      funext y
      induction y
      dsimp
      apply Quot.sound
      · exact relation.add_1 _ _ _ r
      · rfl
  zero_add x := by
    induction x
    dsimp
    apply Quot.sound
    apply relation.zero_add
    rfl
  add_zero x := by
    induction x
    dsimp
    apply Quot.sound
    apply relation.add_zero
    rfl
  add_left_neg x := by
    induction x
    dsimp
    apply Quot.sound
    apply relation.add_left_neg
    rfl
  add_comm x y := by
    induction x
    induction y
    dsimp
    apply Quot.sound
    apply relation.add_comm
    rfl
    rfl
  add_assoc x y z := by
    induction x
    induction y
    induction z
    dsimp
    apply Quot.sound
    apply relation.add_assoc
    rfl
    rfl
    rfl

@[simp]
theorem quot_zero : Quot.mk Setoid.r zero = (0 : ColimitType F) :=
  rfl
#align AddCommGroup.colimits.quot_zero AddCommGroupCat.Colimits.quot_zero

@[simp]
theorem quot_neg (x) : Quot.mk Setoid.r (neg x) = (-Quot.mk Setoid.r x : ColimitType F) :=
  rfl
#align AddCommGroup.colimits.quot_neg AddCommGroupCat.Colimits.quot_neg

@[simp]
theorem quot_add (x y) :
    Quot.mk Setoid.r (add x y) = (Quot.mk Setoid.r x + Quot.mk Setoid.r y : ColimitType F) :=
  rfl
#align AddCommGroup.colimits.quot_add AddCommGroupCat.Colimits.quot_add

/-- The bundled abelian group giving the colimit of a diagram. -/
def colimit : AddCommGroupCat :=
  AddCommGroupCat.of (ColimitType F)
#align AddCommGroup.colimits.colimit AddCommGroupCat.Colimits.colimit

/-- The function from a given abelian group in the diagram to the colimit abelian group. -/
def coconeFun (j : J) (x : F.obj j) : ColimitType F :=
  Quot.mk _ (of j x)
#align AddCommGroup.colimits.cocone_fun AddCommGroupCat.Colimits.coconeFun

/-- The group homomorphism from a given abelian group in the diagram to the colimit abelian
group. -/
def coconeMorphism (j : J) : F.obj j ⟶ colimit F
    where
  toFun := coconeFun F j
  map_zero' := by apply Quot.sound <;> apply relation.zero
  map_add' := by intros <;> apply Quot.sound <;> apply relation.add
#align AddCommGroup.colimits.cocone_morphism AddCommGroupCat.Colimits.coconeMorphism

@[simp]
theorem cocone_naturality {j j' : J} (f : j ⟶ j') :
    F.map f ≫ coconeMorphism F j' = coconeMorphism F j :=
  by
  ext
  apply Quot.sound
  apply Relation.Map
#align AddCommGroup.colimits.cocone_naturality AddCommGroupCat.Colimits.cocone_naturality

@[simp]
theorem cocone_naturality_components (j j' : J) (f : j ⟶ j') (x : F.obj j) :
    (coconeMorphism F j') (F.map f x) = (coconeMorphism F j) x :=
  by
  rw [← cocone_naturality F f]
  rfl
#align AddCommGroup.colimits.cocone_naturality_components AddCommGroupCat.Colimits.cocone_naturality_components

/-- The cocone over the proposed colimit abelian group. -/
def colimitCocone : Cocone F where
  pt := colimit F
  ι := { app := coconeMorphism F }
#align AddCommGroup.colimits.colimit_cocone AddCommGroupCat.Colimits.colimitCocone

/-- The function from the free abelian group on the diagram to the cone point of any other
cocone. -/
@[simp]
def descFunLift (s : Cocone F) : Prequotient F → s.pt
  | of j x => (s.ι.app j) x
  | zero => 0
  | neg x => -desc_fun_lift x
  | add x y => desc_fun_lift x + desc_fun_lift y
#align AddCommGroup.colimits.desc_fun_lift AddCommGroupCat.Colimits.descFunLift

/-- The function from the colimit abelian group to the cone point of any other cocone. -/
def descFun (s : Cocone F) : ColimitType F → s.pt :=
  by
  fapply Quot.lift
  · exact desc_fun_lift F s
  · intro x y r
    induction r <;> try dsimp
    -- refl
    · rfl
    -- symm
    · exact r_ih.symm
    -- trans
    · exact Eq.trans r_ih_h r_ih_k
    -- map
    · simp
    -- zero
    · simp
    -- neg
    · simp
    -- add
    · simp
    -- neg_1
    · rw [r_ih]
    -- add_1
    · rw [r_ih]
    -- add_2
    · rw [r_ih]
    -- zero_add
    · rw [zero_add]
    -- add_zero
    · rw [add_zero]
    -- add_left_neg
    · rw [add_left_neg]
    -- add_comm
    · rw [add_comm]
    -- add_assoc
    · rw [add_assoc]
#align AddCommGroup.colimits.desc_fun AddCommGroupCat.Colimits.descFun

/-- The group homomorphism from the colimit abelian group to the cone point of any other cocone. -/
def descMorphism (s : Cocone F) : colimit F ⟶ s.pt
    where
  toFun := descFun F s
  map_zero' := rfl
  map_add' x y := by induction x <;> induction y <;> rfl
#align AddCommGroup.colimits.desc_morphism AddCommGroupCat.Colimits.descMorphism

/-- Evidence that the proposed colimit is the colimit. -/
def colimitCoconeIsColimit : IsColimit (colimitCocone F)
    where
  desc s := descMorphism F s
  uniq s m w := by
    ext
    induction x
    induction x
    · have w' :=
        congr_fun (congr_arg (fun f : F.obj x_j ⟶ s.X => (f : F.obj x_j → s.X)) (w x_j)) x_x
      erw [w']
      rfl
    · simp [*]
    · simp [*]
    · simp [*]
    rfl
#align AddCommGroup.colimits.colimit_cocone_is_colimit AddCommGroupCat.Colimits.colimitCoconeIsColimit

instance hasColimits_addCommGroupCat : HasColimits AddCommGroupCat
    where HasColimitsOfShape J 𝒥 :=
    {
      HasColimit := fun F =>
        has_colimit.mk
          { Cocone := colimit_cocone F
            IsColimit := colimit_cocone_is_colimit F } }
#align AddCommGroup.colimits.has_colimits_AddCommGroup AddCommGroupCat.Colimits.hasColimits_addCommGroupCat

end AddCommGroupCat.Colimits

namespace AddCommGroupCat

open quotientAddGroup

/-- The categorical cokernel of a morphism in `AddCommGroup`
agrees with the usual group-theoretical quotient.
-/
noncomputable def cokernelIsoQuotient {G H : AddCommGroupCat.{u}} (f : G ⟶ H) :
    cokernel f ≅ AddCommGroupCat.of (H ⧸ AddMonoidHom.range f)
    where
  Hom :=
    cokernel.desc f (mk' _)
      (by
        ext
        apply Quotient.sound
        apply left_rel_apply.mpr
        fconstructor
        exact -x
        simp only [add_zero, AddMonoidHom.map_neg])
  inv :=
    QuotientAddGroup.lift _ (cokernel.π f)
      (by
        intro x H_1
        cases H_1
        induction H_1_h
        simp only [cokernel.condition_apply, zero_apply])
  -- obviously can take care of the next goals, but it is really slow
  hom_inv_id' := by ext1;
    simp only [coequalizer_as_cokernel, category.comp_id, cokernel.π_desc_assoc]; ext1; rfl
  inv_hom_id' := by
    ext x : 2
    simp only [AddMonoidHom.coe_comp, Function.comp_apply, comp_apply, lift_mk,
      cokernel.π_desc_apply, mk'_apply, id_apply]
#align AddCommGroup.cokernel_iso_quotient AddCommGroupCat.cokernelIsoQuotient

end AddCommGroupCat


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

import data.finsupp.basic
import algebra.big_operators.pi
import algebra.big_operators.ring

/-!
# Big operators for finsupps

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

open_locale big_operators

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

theorem finset.sum_apply' : (∑ k in s, f k) i = ∑ k in s, f k i :=
(finsupp.apply_add_hom i : (ι →₀ A) →+ A).map_sum f s

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

section
include h0 h1

open_locale classical

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

end

section
variables {R S : Type*} [non_unital_non_assoc_semiring R] [non_unital_non_assoc_semiring S]

lemma finsupp.sum_mul (b : S) (s : α →₀ R) {f : α → R → S} :
  (s.sum f) * b = s.sum (λ a c, (f a c) * b) :=
by simp only [finsupp.sum, finset.sum_mul]

lemma finsupp.mul_sum (b : S) (s : α →₀ R) {f : α → R → S} :
  b * (s.sum f) = s.sum (λ a c, b * (f a c)) :=
by simp only [finsupp.sum, finset.mul_sum]

end


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

! This file was ported from Lean 3 source module group_theory.submonoid.pointwise
! leanprover-community/mathlib commit 2bbc7e3884ba234309d2a43b19144105a753292e
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Data.Set.Pointwise.SMul
import Mathlib.GroupTheory.Submonoid.Membership
import Mathlib.Order.WellFoundedSet

/-! # Pointwise instances on `Submonoid`s and `AddSubmonoid`s

This file provides:

* `Submonoid.inv`
* `AddSubmonoid.neg`

and the actions

* `Submonoid.pointwiseMulAction`
* `AddSubmonoid.pointwiseMulAction`

which matches the action of `Set.mulActionSet`.

These are all available in the `pointwise` locale.

Additionally, it provides various degrees of monoid structure:
* `AddSubmonoid.one`
* `AddSubmonoid.mul`
* `AddSubmonoid.mulOneClass`
* `AddSubmonoid.semigroup`
* `AddSubmonoid.monoid`
which is available globally to match the monoid structure implied by `Submodule.idemSemiring`.

## Implementation notes

Most of the lemmas in this file are direct copies of lemmas from `Algebra/Pointwise.lean`.
While the statements of these lemmas are defeq, we repeat them here due to them not being
syntactically equal. Before adding new lemmas here, consider if they would also apply to the action
on `Set`s.

-/

open Set Pointwise

variable {α : Type _} {G : Type _} {M : Type _} {R : Type _} {A : Type _}

variable [Monoid M] [AddMonoid A]

/-! Some lemmas about pointwise multiplication and submonoids. Ideally we put these in
  `GroupTheory.Submonoid.Basic`, but currently we cannot because that file is imported by this. -/

namespace Submonoid

variable {s t u : Set M}

@[to_additive]
theorem mul_subset {S : Submonoid M} (hs : s ⊆ S) (ht : t ⊆ S) : s * t ⊆ S := by
  rintro _ ⟨p, q, hp, hq, rfl⟩
  exact Submonoid.mul_mem _ (hs hp) (ht hq)
#align submonoid.mul_subset Submonoid.mul_subset
#align add_submonoid.add_subset AddSubmonoid.add_subset

@[to_additive]
theorem mul_subset_closure (hs : s ⊆ u) (ht : t ⊆ u) : s * t ⊆ Submonoid.closure u :=
  mul_subset (Subset.trans hs Submonoid.subset_closure) (Subset.trans ht Submonoid.subset_closure)
#align submonoid.mul_subset_closure Submonoid.mul_subset_closure
#align add_submonoid.add_subset_closure AddSubmonoid.add_subset_closure

@[to_additive]
theorem coe_mul_self_eq (s : Submonoid M) : (s : Set M) * s = s := by
  ext x
  refine' ⟨_, fun h => ⟨x, 1, h, s.one_mem, mul_one x⟩⟩
  rintro ⟨a, b, ha, hb, rfl⟩
  exact s.mul_mem ha hb
#align submonoid.coe_mul_self_eq Submonoid.coe_mul_self_eq
#align add_submonoid.coe_add_self_eq AddSubmonoid.coe_add_self_eq

@[to_additive]
theorem closure_mul_le (S T : Set M) : closure (S * T) ≤ closure S ⊔ closure T :=
  infₛ_le fun _x ⟨_s, _t, hs, ht, hx⟩ => hx ▸
    (closure S ⊔ closure T).mul_mem (SetLike.le_def.mp le_sup_left <| subset_closure hs)
      (SetLike.le_def.mp le_sup_right <| subset_closure ht)
#align submonoid.closure_mul_le Submonoid.closure_mul_le
#align add_submonoid.closure_add_le AddSubmonoid.closure_add_le

@[to_additive]
theorem sup_eq_closure (H K : Submonoid M) : H ⊔ K = closure ((H : Set M) * (K : Set M)) :=
  le_antisymm
    (sup_le (fun h hh => subset_closure ⟨h, 1, hh, K.one_mem, mul_one h⟩) fun k hk =>
      subset_closure ⟨1, k, H.one_mem, hk, one_mul k⟩)
    ((closure_mul_le _ _).trans <| by rw [closure_eq, closure_eq])
#align submonoid.sup_eq_closure Submonoid.sup_eq_closure
#align add_submonoid.sup_eq_closure AddSubmonoid.sup_eq_closure

@[to_additive]
theorem pow_smul_mem_closure_smul {N : Type _} [CommMonoid N] [MulAction M N] [IsScalarTower M N N]
    (r : M) (s : Set N) {x : N} (hx : x ∈ closure s) : ∃ n : ℕ, r ^ n • x ∈ closure (r • s) := by
  refine' @closure_induction N _ s (fun x : N => ∃ n : ℕ, r ^ n • x ∈ closure (r • s)) _ hx _ _ _
  · intro x hx
    exact ⟨1, subset_closure ⟨_, hx, by rw [pow_one]⟩⟩
  · exact ⟨0, by simpa using one_mem _⟩
  · rintro x y ⟨nx, hx⟩ ⟨ny, hy⟩
    use ny + nx
    rw [pow_add, mul_smul, ← smul_mul_assoc, mul_comm, ← smul_mul_assoc]
    exact mul_mem hy hx
#align submonoid.pow_smul_mem_closure_smul Submonoid.pow_smul_mem_closure_smul
#align add_submonoid.nsmul_vadd_mem_closure_vadd AddSubmonoid.nsmul_vadd_mem_closure_vadd

variable [Group G]

/-- The submonoid with every element inverted. -/
@[to_additive " The additive submonoid with every element negated. "]
protected def inv : Inv (Submonoid G) where
  inv S :=
    { carrier := (S : Set G)⁻¹
      mul_mem' := fun ha hb => by rw [mem_inv, mul_inv_rev]; exact mul_mem hb ha
      one_mem' := mem_inv.2 <| by rw [inv_one]; exact S.one_mem' }
#align submonoid.has_inv Submonoid.inv
#align add_submonoid.has_neg AddSubmonoid.neg

scoped[Pointwise] attribute [instance] Submonoid.inv AddSubmonoid.neg

@[to_additive (attr := simp)]
theorem coe_inv (S : Submonoid G) : ↑S⁻¹ = (S : Set G)⁻¹ :=
  rfl
#align submonoid.coe_inv Submonoid.coe_inv
#align add_submonoid.coe_neg AddSubmonoid.coe_neg

@[to_additive (attr := simp)]
theorem mem_inv {g : G} {S : Submonoid G} : g ∈ S⁻¹ ↔ g⁻¹ ∈ S :=
  Iff.rfl
#align submonoid.mem_inv Submonoid.mem_inv
#align add_submonoid.mem_neg AddSubmonoid.mem_neg

/-- Inversion is involutive on submonoids. -/
@[to_additive "Inversion is involutive on additive submonoids."]
def involutiveInv : InvolutiveInv (Submonoid G) :=
  SetLike.coe_injective.involutiveInv _ fun _ => rfl

scoped[Pointwise] attribute [instance] Submonoid.involutiveInv AddSubmonoid.involutiveNeg

@[to_additive (attr := simp)]
theorem inv_le_inv (S T : Submonoid G) : S⁻¹ ≤ T⁻¹ ↔ S ≤ T :=
  SetLike.coe_subset_coe.symm.trans Set.inv_subset_inv
#align submonoid.inv_le_inv Submonoid.inv_le_inv
#align add_submonoid.neg_le_neg AddSubmonoid.neg_le_neg

@[to_additive]
theorem inv_le (S T : Submonoid G) : S⁻¹ ≤ T ↔ S ≤ T⁻¹ :=
  SetLike.coe_subset_coe.symm.trans Set.inv_subset
#align submonoid.inv_le Submonoid.inv_le
#align add_submonoid.neg_le AddSubmonoid.neg_le

/-- Pointwise inversion of submonoids as an order isomorphism. -/
@[to_additive (attr := simps!) "Pointwise negation of additive submonoids as an order isomorphism"]
def invOrderIso : Submonoid G ≃o Submonoid G where
  toEquiv := Equiv.inv _
  map_rel_iff' := inv_le_inv _ _
#align submonoid.inv_order_iso Submonoid.invOrderIso
#align add_submonoid.neg_order_iso AddSubmonoid.negOrderIso

@[to_additive]
theorem closure_inv (s : Set G) : closure s⁻¹ = (closure s)⁻¹ := by
  apply le_antisymm
  · rw [closure_le, coe_inv, ← Set.inv_subset, inv_inv]
    exact subset_closure
  · rw [inv_le, closure_le, coe_inv, ← Set.inv_subset]
    exact subset_closure
#align submonoid.closure_inv Submonoid.closure_inv
#align add_submonoid.closure_neg AddSubmonoid.closure_neg

@[to_additive (attr := simp)]
theorem inv_inf (S T : Submonoid G) : (S ⊓ T)⁻¹ = S⁻¹ ⊓ T⁻¹ :=
  SetLike.coe_injective Set.inter_inv
#align submonoid.inv_inf Submonoid.inv_inf
#align add_submonoid.neg_inf AddSubmonoid.neg_inf

@[to_additive (attr := simp)]
theorem inv_sup (S T : Submonoid G) : (S ⊔ T)⁻¹ = S⁻¹ ⊔ T⁻¹ :=
  (invOrderIso : Submonoid G ≃o Submonoid G).map_sup S T
#align submonoid.inv_sup Submonoid.inv_sup
#align add_submonoid.neg_sup AddSubmonoid.neg_sup

@[to_additive (attr := simp)]
theorem inv_bot : (⊥ : Submonoid G)⁻¹ = ⊥ :=
  SetLike.coe_injective <| (Set.inv_singleton 1).trans <| congr_arg _ inv_one
#align submonoid.inv_bot Submonoid.inv_bot
#align add_submonoid.neg_bot AddSubmonoid.neg_bot

@[to_additive (attr := simp)]
theorem inv_top : (⊤ : Submonoid G)⁻¹ = ⊤ :=
  SetLike.coe_injective <| Set.inv_univ
#align submonoid.inv_top Submonoid.inv_top
#align add_submonoid.neg_top AddSubmonoid.neg_top

@[to_additive (attr := simp)]
theorem inv_infᵢ {ι : Sort _} (S : ι → Submonoid G) : (⨅ i, S i)⁻¹ = ⨅ i, (S i)⁻¹ :=
  (invOrderIso : Submonoid G ≃o Submonoid G).map_infᵢ _
#align submonoid.inv_infi Submonoid.inv_infᵢ
#align add_submonoid.neg_infi AddSubmonoid.neg_infᵢ

@[to_additive (attr := simp)]
theorem inv_supᵢ {ι : Sort _} (S : ι → Submonoid G) : (⨆ i, S i)⁻¹ = ⨆ i, (S i)⁻¹ :=
  (invOrderIso : Submonoid G ≃o Submonoid G).map_supᵢ _
#align submonoid.inv_supr Submonoid.inv_supᵢ
#align add_submonoid.neg_supr AddSubmonoid.neg_supᵢ

end Submonoid

namespace Submonoid

section Monoid

variable [Monoid α] [MulDistribMulAction α M]

-- todo: add `to_additive`?
/-- The action on a submonoid corresponding to applying the action to every element.

This is available as an instance in the `Pointwise` locale. -/
protected def pointwiseMulAction : MulAction α (Submonoid M) where
  smul a S := S.map (MulDistribMulAction.toMonoidEnd _ M a)
  one_smul S := by
    change S.map _ = S
    simpa only [map_one] using S.map_id
  mul_smul a₁ a₂ S :=
    (congr_arg (fun f : Monoid.End M => S.map f) (MonoidHom.map_mul _ _ _)).trans
      (S.map_map _ _).symm
#align submonoid.pointwise_mul_action Submonoid.pointwiseMulAction

scoped[Pointwise] attribute [instance] Submonoid.pointwiseMulAction

@[simp]
theorem coe_pointwise_smul (a : α) (S : Submonoid M) : ↑(a • S) = a • (S : Set M) :=
  rfl
#align submonoid.coe_pointwise_smul Submonoid.coe_pointwise_smul

theorem smul_mem_pointwise_smul (m : M) (a : α) (S : Submonoid M) : m ∈ S → a • m ∈ a • S :=
  (Set.smul_mem_smul_set : _ → _ ∈ a • (S : Set M))
#align submonoid.smul_mem_pointwise_smul Submonoid.smul_mem_pointwise_smul

theorem mem_smul_pointwise_iff_exists (m : M) (a : α) (S : Submonoid M) :
    m ∈ a • S ↔ ∃ s : M, s ∈ S ∧ a • s = m :=
  (Set.mem_smul_set : m ∈ a • (S : Set M) ↔ _)
#align submonoid.mem_smul_pointwise_iff_exists Submonoid.mem_smul_pointwise_iff_exists

@[simp]
theorem smul_bot (a : α) : a • (⊥ : Submonoid M) = ⊥ :=
  map_bot _
#align submonoid.smul_bot Submonoid.smul_bot

theorem smul_sup (a : α) (S T : Submonoid M) : a • (S ⊔ T) = a • S ⊔ a • T :=
  map_sup _ _ _
#align submonoid.smul_sup Submonoid.smul_sup

theorem smul_closure (a : α) (s : Set M) : a • closure s = closure (a • s) :=
  MonoidHom.map_mclosure _ _
#align submonoid.smul_closure Submonoid.smul_closure

lemma pointwise_isCentralScalar [MulDistribMulAction αᵐᵒᵖ M] [IsCentralScalar α M] :
    IsCentralScalar α (Submonoid M) :=
  ⟨fun _ S => (congr_arg fun f : Monoid.End M => S.map f) <| MonoidHom.ext <| op_smul_eq_smul _⟩
#align submonoid.pointwise_central_scalar Submonoid.pointwise_isCentralScalar

scoped[Pointwise] attribute [instance] Submonoid.pointwise_isCentralScalar

end Monoid

section Group

variable [Group α] [MulDistribMulAction α M]

@[simp]
theorem smul_mem_pointwise_smul_iff {a : α} {S : Submonoid M} {x : M} : a • x ∈ a • S ↔ x ∈ S :=
  smul_mem_smul_set_iff
#align submonoid.smul_mem_pointwise_smul_iff Submonoid.smul_mem_pointwise_smul_iff

theorem mem_pointwise_smul_iff_inv_smul_mem {a : α} {S : Submonoid M} {x : M} :
    x ∈ a • S ↔ a⁻¹ • x ∈ S :=
  mem_smul_set_iff_inv_smul_mem
#align submonoid.mem_pointwise_smul_iff_inv_smul_mem Submonoid.mem_pointwise_smul_iff_inv_smul_mem

@[simp]
theorem pointwise_smul_le_pointwise_smul_iff {a : α} {S T : Submonoid M} : a • S ≤ a • T ↔ S ≤ T :=
  set_smul_subset_set_smul_iff
#align submonoid.pointwise_smul_le_pointwise_smul_iff Submonoid.pointwise_smul_le_pointwise_smul_iff

theorem pointwise_smul_subset_iff {a : α} {S T : Submonoid M} : a • S ≤ T ↔ S ≤ a⁻¹ • T :=
  set_smul_subset_iff
#align submonoid.pointwise_smul_subset_iff Submonoid.pointwise_smul_subset_iff

theorem subset_pointwise_smul_iff {a : α} {S T : Submonoid M} : S ≤ a • T ↔ a⁻¹ • S ≤ T :=
  subset_set_smul_iff
#align submonoid.subset_pointwise_smul_iff Submonoid.subset_pointwise_smul_iff

end Group

section GroupWithZero

variable [GroupWithZero α] [MulDistribMulAction α M]

@[simp]
theorem smul_mem_pointwise_smul_iff₀ {a : α} (ha : a ≠ 0) (S : Submonoid M) (x : M) :
    a • x ∈ a • S ↔ x ∈ S :=
  smul_mem_smul_set_iff₀ ha (S : Set M) x
#align submonoid.smul_mem_pointwise_smul_iff₀ Submonoid.smul_mem_pointwise_smul_iff₀

theorem mem_pointwise_smul_iff_inv_smul_mem₀ {a : α} (ha : a ≠ 0) (S : Submonoid M) (x : M) :
    x ∈ a • S ↔ a⁻¹ • x ∈ S :=
  mem_smul_set_iff_inv_smul_mem₀ ha (S : Set M) x
#align submonoid.mem_pointwise_smul_iff_inv_smul_mem₀ Submonoid.mem_pointwise_smul_iff_inv_smul_mem₀

theorem mem_inv_pointwise_smul_iff₀ {a : α} (ha : a ≠ 0) (S : Submonoid M) (x : M) :
    x ∈ a⁻¹ • S ↔ a • x ∈ S :=
  mem_inv_smul_set_iff₀ ha (S : Set M) x
#align submonoid.mem_inv_pointwise_smul_iff₀ Submonoid.mem_inv_pointwise_smul_iff₀

@[simp]
theorem pointwise_smul_le_pointwise_smul_iff₀ {a : α} (ha : a ≠ 0) {S T : Submonoid M} :
    a • S ≤ a • T ↔ S ≤ T :=
  set_smul_subset_set_smul_iff₀ ha
#align submonoid.pointwise_smul_le_pointwise_smul_iff₀ Submonoid.pointwise_smul_le_pointwise_smul_iff₀

theorem pointwise_smul_le_iff₀ {a : α} (ha : a ≠ 0) {S T : Submonoid M} : a • S ≤ T ↔ S ≤ a⁻¹ • T :=
  set_smul_subset_iff₀ ha
#align submonoid.pointwise_smul_le_iff₀ Submonoid.pointwise_smul_le_iff₀

theorem le_pointwise_smul_iff₀ {a : α} (ha : a ≠ 0) {S T : Submonoid M} : S ≤ a • T ↔ a⁻¹ • S ≤ T :=
  subset_set_smul_iff₀ ha
#align submonoid.le_pointwise_smul_iff₀ Submonoid.le_pointwise_smul_iff₀

end GroupWithZero

@[to_additive]
theorem mem_closure_inv {G : Type _} [Group G] (S : Set G) (x : G) :
    x ∈ Submonoid.closure S⁻¹ ↔ x⁻¹ ∈ Submonoid.closure S := by rw [closure_inv, mem_inv]
#align submonoid.mem_closure_inv Submonoid.mem_closure_inv
#align add_submonoid.mem_closure_neg AddSubmonoid.mem_closure_neg

end Submonoid

namespace AddSubmonoid

section Monoid

variable [Monoid α] [DistribMulAction α A]

/-- The action on an additive submonoid corresponding to applying the action to every element.

This is available as an instance in the `Pointwise` locale. -/
protected def pointwiseMulAction : MulAction α (AddSubmonoid A) where
  smul a S := S.map (DistribMulAction.toAddMonoidEnd _ A a)
  one_smul S :=
    (congr_arg (fun f : AddMonoid.End A => S.map f) (MonoidHom.map_one _)).trans S.map_id
  mul_smul _ _ S :=
    (congr_arg (fun f : AddMonoid.End A => S.map f) (MonoidHom.map_mul _ _ _)).trans
      (S.map_map _ _).symm
#align add_submonoid.pointwise_mul_action AddSubmonoid.pointwiseMulAction

scoped[Pointwise] attribute [instance] AddSubmonoid.pointwiseMulAction

@[simp]
theorem coe_pointwise_smul (a : α) (S : AddSubmonoid A) : ↑(a • S) = a • (S : Set A) :=
  rfl
#align add_submonoid.coe_pointwise_smul AddSubmonoid.coe_pointwise_smul

theorem smul_mem_pointwise_smul (m : A) (a : α) (S : AddSubmonoid A) : m ∈ S → a • m ∈ a • S :=
  (Set.smul_mem_smul_set : _ → _ ∈ a • (S : Set A))
#align add_submonoid.smul_mem_pointwise_smul AddSubmonoid.smul_mem_pointwise_smul

theorem mem_smul_pointwise_iff_exists (m : A) (a : α) (S : AddSubmonoid A) :
    m ∈ a • S ↔ ∃ s : A, s ∈ S ∧ a • s = m :=
  (Set.mem_smul_set : m ∈ a • (S : Set A) ↔ _)
#align add_submonoid.mem_smul_pointwise_iff_exists AddSubmonoid.mem_smul_pointwise_iff_exists

@[simp]
theorem smul_bot (a : α) : a • (⊥ : AddSubmonoid A) = ⊥ :=
  map_bot _
#align add_submonoid.smul_bot AddSubmonoid.smul_bot

theorem smul_sup (a : α) (S T : AddSubmonoid A) : a • (S ⊔ T) = a • S ⊔ a • T :=
  map_sup _ _ _
#align add_submonoid.smul_sup AddSubmonoid.smul_sup

@[simp]
theorem smul_closure (a : α) (s : Set A) : a • closure s = closure (a • s) :=
  AddMonoidHom.map_mclosure _ _
#align add_submonoid.smul_closure AddSubmonoid.smul_closure

lemma pointwise_isCentralScalar [DistribMulAction αᵐᵒᵖ A] [IsCentralScalar α A] :
    IsCentralScalar α (AddSubmonoid A) :=
  ⟨fun _ S =>
    (congr_arg fun f : AddMonoid.End A => S.map f) <| AddMonoidHom.ext <| op_smul_eq_smul _⟩
#align add_submonoid.pointwise_central_scalar AddSubmonoid.pointwise_isCentralScalar

scoped[Pointwise] attribute [instance] AddSubmonoid.pointwise_isCentralScalar

end Monoid

section Group

variable [Group α] [DistribMulAction α A]

@[simp]
theorem smul_mem_pointwise_smul_iff {a : α} {S : AddSubmonoid A} {x : A} : a • x ∈ a • S ↔ x ∈ S :=
  smul_mem_smul_set_iff
#align add_submonoid.smul_mem_pointwise_smul_iff AddSubmonoid.smul_mem_pointwise_smul_iff

theorem mem_pointwise_smul_iff_inv_smul_mem {a : α} {S : AddSubmonoid A} {x : A} :
    x ∈ a • S ↔ a⁻¹ • x ∈ S :=
  mem_smul_set_iff_inv_smul_mem
#align add_submonoid.mem_pointwise_smul_iff_inv_smul_mem AddSubmonoid.mem_pointwise_smul_iff_inv_smul_mem

theorem mem_inv_pointwise_smul_iff {a : α} {S : AddSubmonoid A} {x : A} : x ∈ a⁻¹ • S ↔ a • x ∈ S :=
  mem_inv_smul_set_iff
#align add_submonoid.mem_inv_pointwise_smul_iff AddSubmonoid.mem_inv_pointwise_smul_iff

@[simp]
theorem pointwise_smul_le_pointwise_smul_iff {a : α} {S T : AddSubmonoid A} :
    a • S ≤ a • T ↔ S ≤ T :=
  set_smul_subset_set_smul_iff
#align add_submonoid.pointwise_smul_le_pointwise_smul_iff AddSubmonoid.pointwise_smul_le_pointwise_smul_iff

theorem pointwise_smul_le_iff {a : α} {S T : AddSubmonoid A} : a • S ≤ T ↔ S ≤ a⁻¹ • T :=
  set_smul_subset_iff
#align add_submonoid.pointwise_smul_le_iff AddSubmonoid.pointwise_smul_le_iff

theorem le_pointwise_smul_iff {a : α} {S T : AddSubmonoid A} : S ≤ a • T ↔ a⁻¹ • S ≤ T :=
  subset_set_smul_iff
#align add_submonoid.le_pointwise_smul_iff AddSubmonoid.le_pointwise_smul_iff

end Group

section GroupWithZero

variable [GroupWithZero α] [DistribMulAction α A]

@[simp]
theorem smul_mem_pointwise_smul_iff₀ {a : α} (ha : a ≠ 0) (S : AddSubmonoid A) (x : A) :
    a • x ∈ a • S ↔ x ∈ S :=
  smul_mem_smul_set_iff₀ ha (S : Set A) x
#align add_submonoid.smul_mem_pointwise_smul_iff₀ AddSubmonoid.smul_mem_pointwise_smul_iff₀

theorem mem_pointwise_smul_iff_inv_smul_mem₀ {a : α} (ha : a ≠ 0) (S : AddSubmonoid A) (x : A) :
    x ∈ a • S ↔ a⁻¹ • x ∈ S :=
  mem_smul_set_iff_inv_smul_mem₀ ha (S : Set A) x
#align add_submonoid.mem_pointwise_smul_iff_inv_smul_mem₀ AddSubmonoid.mem_pointwise_smul_iff_inv_smul_mem₀

theorem mem_inv_pointwise_smul_iff₀ {a : α} (ha : a ≠ 0) (S : AddSubmonoid A) (x : A) :
    x ∈ a⁻¹ • S ↔ a • x ∈ S :=
  mem_inv_smul_set_iff₀ ha (S : Set A) x
#align add_submonoid.mem_inv_pointwise_smul_iff₀ AddSubmonoid.mem_inv_pointwise_smul_iff₀

@[simp]
theorem pointwise_smul_le_pointwise_smul_iff₀ {a : α} (ha : a ≠ 0) {S T : AddSubmonoid A} :
    a • S ≤ a • T ↔ S ≤ T :=
  set_smul_subset_set_smul_iff₀ ha
#align add_submonoid.pointwise_smul_le_pointwise_smul_iff₀ AddSubmonoid.pointwise_smul_le_pointwise_smul_iff₀

theorem pointwise_smul_le_iff₀ {a : α} (ha : a ≠ 0) {S T : AddSubmonoid A} :
    a • S ≤ T ↔ S ≤ a⁻¹ • T :=
  set_smul_subset_iff₀ ha
#align add_submonoid.pointwise_smul_le_iff₀ AddSubmonoid.pointwise_smul_le_iff₀

theorem le_pointwise_smul_iff₀ {a : α} (ha : a ≠ 0) {S T : AddSubmonoid A} :
    S ≤ a • T ↔ a⁻¹ • S ≤ T :=
  subset_set_smul_iff₀ ha
#align add_submonoid.le_pointwise_smul_iff₀ AddSubmonoid.le_pointwise_smul_iff₀

end GroupWithZero

end AddSubmonoid

/-! ### Elementwise monoid structure of additive submonoids

These definitions are a cut-down versions of the ones around `Submodule.mul`, as that API is
usually more useful. -/

namespace AddSubmonoid

section AddMonoidWithOne

variable [AddMonoidWithOne R]

/-- If `R` is an additive monoid with one (e.g., a semiring), then `1 : AddSubmonoid R` is the range
of `Nat.cast : ℕ → R`. -/
protected def one : One (AddSubmonoid R) :=
  ⟨AddMonoidHom.mrange (Nat.castAddMonoidHom R)⟩
scoped[Pointwise] attribute [instance] AddSubmonoid.one

theorem one_eq_mrange : (1 : AddSubmonoid R) = AddMonoidHom.mrange (Nat.castAddMonoidHom R) :=
  rfl
#align add_submonoid.one_eq_mrange AddSubmonoid.one_eq_mrange

theorem natCast_mem_one (n : ℕ) : (n : R) ∈ (1 : AddSubmonoid R) :=
  ⟨_, rfl⟩
#align add_submonoid.nat_cast_mem_one AddSubmonoid.natCast_mem_one

@[simp]
theorem mem_one {x : R} : x ∈ (1 : AddSubmonoid R) ↔ ∃ n : ℕ, ↑n = x :=
  Iff.rfl
#align add_submonoid.mem_one AddSubmonoid.mem_one

theorem one_eq_closure : (1 : AddSubmonoid R) = closure {1} := by
  rw [closure_singleton_eq, one_eq_mrange]
  congr 1 with n
  simp
#align add_submonoid.one_eq_closure AddSubmonoid.one_eq_closure

theorem one_eq_closure_one_set : (1 : AddSubmonoid R) = closure 1 :=
  one_eq_closure
#align add_submonoid.one_eq_closure_one_set AddSubmonoid.one_eq_closure_one_set

end AddMonoidWithOne

section NonUnitalNonAssocSemiring

variable [NonUnitalNonAssocSemiring R]

/-- Multiplication of additive submonoids of a semiring R. The additive submonoid `S * T` is the
smallest R-submodule of `R` containing the elements `s * t` for `s ∈ S` and `t ∈ T`. -/
protected def mul : Mul (AddSubmonoid R) :=
  ⟨fun M N => ⨆ s : M, N.map <| AddMonoidHom.mul s.1⟩
scoped[Pointwise] attribute [instance] AddSubmonoid.mul

theorem mul_mem_mul {M N : AddSubmonoid R} {m n : R} (hm : m ∈ M) (hn : n ∈ N) : m * n ∈ M * N :=
  (le_supᵢ _ ⟨m, hm⟩ : _ ≤ M * N) ⟨n, hn, by rfl⟩
#align add_submonoid.mul_mem_mul AddSubmonoid.mul_mem_mul

theorem mul_le {M N P : AddSubmonoid R} : M * N ≤ P ↔ ∀ m ∈ M, ∀ n ∈ N, m * n ∈ P :=
  ⟨fun H _m hm _n hn => H <| mul_mem_mul hm hn, fun H =>
    supᵢ_le fun ⟨m, hm⟩ => map_le_iff_le_comap.2 fun n hn => H m hm n hn⟩
#align add_submonoid.mul_le AddSubmonoid.mul_le

@[elab_as_elim]
protected theorem mul_induction_on {M N : AddSubmonoid R} {C : R → Prop} {r : R} (hr : r ∈ M * N)
    (hm : ∀ m ∈ M, ∀ n ∈ N, C (m * n)) (ha : ∀ x y, C x → C y → C (x + y)) : C r :=
  (@mul_le _ _ _ _ ⟨⟨setOf C, ha _ _⟩, by
    simpa only [zero_mul] using hm _ (zero_mem _) _ (zero_mem _)⟩).2 hm hr
#align add_submonoid.mul_induction_on AddSubmonoid.mul_induction_on

-- this proof is copied directly from `Submodule.span_mul_span`
-- porting note: proof rewritten
theorem closure_mul_closure (S T : Set R) : closure S * closure T = closure (S * T) := by
  apply le_antisymm
  · refine mul_le.2 fun a ha b hb => ?_
    rw [← AddMonoidHom.mul_right_apply, ← AddSubmonoid.mem_comap]
    refine (closure_le.2 fun a' ha' => ?_) ha
    change b ∈ (closure (S * T)).comap (AddMonoidHom.mulLeft a')
    refine (closure_le.2 fun b' hb' => ?_) hb
    change a' * b' ∈ closure (S * T)
    exact subset_closure (Set.mul_mem_mul ha' hb')
  · rw [closure_le]
    rintro _ ⟨a, b, ha, hb, rfl⟩
    exact mul_mem_mul (subset_closure ha) (subset_closure hb)
#align add_submonoid.closure_mul_closure AddSubmonoid.closure_mul_closure

theorem mul_eq_closure_mul_set (M N : AddSubmonoid R) :
    M * N = closure ((M : Set R) * (N : Set R)) := by
  rw [← closure_mul_closure, closure_eq, closure_eq]
#align add_submonoid.mul_eq_closure_mul_set AddSubmonoid.mul_eq_closure_mul_set

@[simp]
theorem mul_bot (S : AddSubmonoid R) : S * ⊥ = ⊥ :=
  eq_bot_iff.2 <| mul_le.2 fun m _ n hn => by
    rw [AddSubmonoid.mem_bot] at hn ⊢; rw [hn, mul_zero]
#align add_submonoid.mul_bot AddSubmonoid.mul_bot

@[simp]
theorem bot_mul (S : AddSubmonoid R) : ⊥ * S = ⊥ :=
  eq_bot_iff.2 <| mul_le.2 fun m hm n hn => by
    rw [AddSubmonoid.mem_bot] at hm ⊢; rw [hm, zero_mul]
#align add_submonoid.bot_mul AddSubmonoid.bot_mul

@[mono]
theorem mul_le_mul {M N P Q : AddSubmonoid R} (hmp : M ≤ P) (hnq : N ≤ Q) : M * N ≤ P * Q :=
  mul_le.2 fun _m hm _n hn => mul_mem_mul (hmp hm) (hnq hn)
#align add_submonoid.mul_le_mul AddSubmonoid.mul_le_mul

theorem mul_le_mul_left {M N P : AddSubmonoid R} (h : M ≤ N) : M * P ≤ N * P :=
  mul_le_mul h (le_refl P)
#align add_submonoid.mul_le_mul_left AddSubmonoid.mul_le_mul_left

theorem mul_le_mul_right {M N P : AddSubmonoid R} (h : N ≤ P) : M * N ≤ M * P :=
  mul_le_mul (le_refl M) h
#align add_submonoid.mul_le_mul_right AddSubmonoid.mul_le_mul_right

theorem mul_subset_mul {M N : AddSubmonoid R} :
    (↑M : Set R) * (↑N : Set R) ⊆ (↑(M * N) : Set R) := by
  rintro _ ⟨i, j, hi, hj, rfl⟩
  exact mul_mem_mul hi hj
#align add_submonoid.mul_subset_mul AddSubmonoid.mul_subset_mul

end NonUnitalNonAssocSemiring

section NonUnitalNonAssocRing

variable [NonUnitalNonAssocRing R]

/-- `AddSubmonoid.neg` distributes over multiplication.

This is available as an instance in the `Pointwise` locale. -/
protected def hasDistribNeg : HasDistribNeg (AddSubmonoid R) :=
  { AddSubmonoid.involutiveNeg with
    neg := Neg.neg
    neg_mul := fun x y => by
      refine'
          le_antisymm (mul_le.2 fun m hm n hn => _)
            ((AddSubmonoid.neg_le _ _).2 <| mul_le.2 fun m hm n hn => _) <;>
        simp only [AddSubmonoid.mem_neg, ← neg_mul] at *
      · exact mul_mem_mul hm hn
      · exact mul_mem_mul (neg_mem_neg.2 hm) hn
    mul_neg := fun x y => by
      refine'
          le_antisymm (mul_le.2 fun m hm n hn => _)
            ((AddSubmonoid.neg_le _ _).2 <| mul_le.2 fun m hm n hn => _) <;>
        simp only [AddSubmonoid.mem_neg, ← mul_neg] at *
      · exact mul_mem_mul hm hn
      · exact mul_mem_mul hm (neg_mem_neg.2 hn) }
#align add_submonoid.has_distrib_neg AddSubmonoid.hasDistribNeg

scoped[Pointwise] attribute [instance] AddSubmonoid.hasDistribNeg

end NonUnitalNonAssocRing

section NonAssocSemiring

variable [NonAssocSemiring R]

/-- A `MulOneClass` structure on additive submonoids of a (possibly, non-associative) semiring. -/
protected def mulOneClass : MulOneClass (AddSubmonoid R) where
  one := 1
  mul := (· * ·)
  one_mul M := by rw [one_eq_closure_one_set, ← closure_eq M, closure_mul_closure, one_mul]
  mul_one M := by rw [one_eq_closure_one_set, ← closure_eq M, closure_mul_closure, mul_one]
scoped[Pointwise] attribute [instance] AddSubmonoid.mulOneClass

end NonAssocSemiring

section NonUnitalSemiring

variable [NonUnitalSemiring R]

/-- Semigroup structure on additive submonoids of a (possibly, non-unital) semiring. -/
protected def semigroup : Semigroup (AddSubmonoid R) where
  mul := (· * ·)
  mul_assoc M N P :=
    le_antisymm
      (mul_le.2 fun _mn hmn p hp =>
        suffices M * N ≤ (M * (N * P)).comap (AddMonoidHom.mulRight p) from this hmn
        mul_le.2 fun m hm n hn =>
          show m * n * p ∈ M * (N * P) from
            (mul_assoc m n p).symm ▸ mul_mem_mul hm (mul_mem_mul hn hp))
      (mul_le.2 fun m hm _np hnp =>
        suffices N * P ≤ (M * N * P).comap (AddMonoidHom.mulLeft m) from this hnp
        mul_le.2 fun n hn p hp =>
          show m * (n * p) ∈ M * N * P from mul_assoc m n p ▸ mul_mem_mul (mul_mem_mul hm hn) hp)
scoped[Pointwise] attribute [instance] AddSubmonoid.semigroup
end NonUnitalSemiring

section Semiring

variable [Semiring R]

/-- Monoid structure on additive submonoids of a semiring. -/
protected def monoid : Monoid (AddSubmonoid R) :=
  { AddSubmonoid.semigroup, AddSubmonoid.mulOneClass with
    one := 1
    mul := (· * ·) }
scoped[Pointwise] attribute [instance] AddSubmonoid.monoid

theorem closure_pow (s : Set R) : ∀ n : ℕ, closure s ^ n = closure (s ^ n)
  | 0 => by rw [pow_zero, pow_zero, one_eq_closure_one_set]
  | n + 1 => by rw [pow_succ, pow_succ, closure_pow s n, closure_mul_closure]
#align add_submonoid.closure_pow AddSubmonoid.closure_pow

theorem pow_eq_closure_pow_set (s : AddSubmonoid R) (n : ℕ) : s ^ n = closure ((s : Set R) ^ n) :=
  by rw [← closure_pow, closure_eq]
#align add_submonoid.pow_eq_closure_pow_set AddSubmonoid.pow_eq_closure_pow_set

theorem pow_subset_pow {s : AddSubmonoid R} {n : ℕ} : (↑s : Set R) ^ n ⊆ ↑(s ^ n) :=
  (pow_eq_closure_pow_set s n).symm ▸ subset_closure
#align add_submonoid.pow_subset_pow AddSubmonoid.pow_subset_pow

end Semiring

end AddSubmonoid

namespace Set.IsPwo

variable [OrderedCancelCommMonoid α] {s : Set α}

@[to_additive]
theorem submonoid_closure (hpos : ∀ x : α, x ∈ s → 1 ≤ x) (h : s.IsPwo) :
    IsPwo (Submonoid.closure s : Set α) := by
  rw [Submonoid.closure_eq_image_prod]
  refine' (h.partiallyWellOrderedOn_sublistForall₂ (· ≤ ·)).image_of_monotone_on _
  exact fun l1 _ l2 hl2 h12 => h12.prod_le_prod' fun x hx => hpos x <| hl2 x hx
#align set.is_pwo.submonoid_closure Set.IsPwo.submonoid_closure
#align set.is_pwo.add_submonoid_closure Set.IsPwo.addSubmonoid_closure

end Set.IsPwo


/-
Copyright (c) 2020 Johan Commelin, Damiano Testa. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin, Damiano Testa, Yaël Dillies

! This file was ported from Lean 3 source module order.synonym
! leanprover-community/mathlib commit c4658a649d216f57e99621708b09dcb3dcccbd23
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathlib.Logic.Equiv.Defs
import Mathlib.Logic.Nontrivial
import Mathlib.Order.Basic

/-!
# Type synonyms

This file provides two type synonyms for order theory:
* `OrderDual α`: Type synonym of `α` to equip it with the dual order (`a ≤ b` becomes `b ≤ a`).
* `Lex α`: Type synonym of `α` to equip it with its lexicographic order. The precise meaning depends
  on the type we take the lex of. Examples include `Prod`, `Sigma`, `List`, `Finset`.

## Notation

`αᵒᵈ` is notation for `OrderDual α`.

The general rule for notation of `Lex` types is to append `ₗ` to the usual notation.

## Implementation notes

One should not abuse definitional equality between `α` and `αᵒᵈ`/`Lex α`. Instead, explicit
coercions should be inserted:
* `OrderDual`: `OrderDual.toDual : α → αᵒᵈ` and `OrderDual.ofDual : αᵒᵈ → α`
* `Lex`: `toLex : α → Lex α` and `ofLex : Lex α → α`.

## See also

This file is similar to `Algebra.Group.TypeTags`.
-/

variable {α β γ : Type _}

/-! ### Order dual -/

namespace OrderDual

instance [h : Nontrivial α] : Nontrivial αᵒᵈ :=
  h

/-- `toDual` is the identity function to the `OrderDual` of a linear order.  -/
def toDual : α ≃ αᵒᵈ :=
  Equiv.refl _
#align order_dual.to_dual OrderDual.toDual

/-- `ofDual` is the identity function from the `OrderDual` of a linear order.  -/
def ofDual : αᵒᵈ ≃ α :=
  Equiv.refl _
#align order_dual.of_dual OrderDual.ofDual

@[simp]
theorem toDual_symm_eq : (@toDual α).symm = ofDual := rfl
#align order_dual.to_dual_symm_eq OrderDual.toDual_symm_eq

@[simp]
theorem ofDual_symm_eq : (@ofDual α).symm = toDual := rfl
#align order_dual.of_dual_symm_eq OrderDual.ofDual_symm_eq

@[simp]
theorem toDual_ofDual (a : αᵒᵈ) : toDual (ofDual a) = a :=
  rfl
#align order_dual.to_dual_of_dual OrderDual.toDual_ofDual

@[simp]

-- Porting note:
-- removed @[simp] since this already follows by `simp only [EmbeddingLike.apply_eq_iff_eq]`
theorem toDual_inj {a b : α} : toDual a = toDual b ↔ a = b :=
  Iff.rfl
#align order_dual.to_dual_inj OrderDual.toDual_inj

-- Porting note:
-- removed @[simp] since this already follows by `simp only [EmbeddingLike.apply_eq_iff_eq]`
theorem ofDual_inj {a b : αᵒᵈ} : ofDual a = ofDual b ↔ a = b :=
  Iff.rfl
#align order_dual.of_dual_inj OrderDual.ofDual_inj

@[simp]
theorem toDual_le_toDual [LE α] {a b : α} : toDual a ≤ toDual b ↔ b ≤ a :=
  Iff.rfl
#align order_dual.to_dual_le_to_dual OrderDual.toDual_le_toDual

@[simp]
theorem toDual_lt_toDual [LT α] {a b : α} : toDual a < toDual b ↔ b < a :=
  Iff.rfl
#align order_dual.to_dual_lt_to_dual OrderDual.toDual_lt_toDual

@[simp]
theorem ofDual_le_ofDual [LE α] {a b : αᵒᵈ} : ofDual a ≤ ofDual b ↔ b ≤ a :=
  Iff.rfl
#align order_dual.of_dual_le_of_dual OrderDual.ofDual_le_ofDual

@[simp]
theorem ofDual_lt_ofDual [LT α] {a b : αᵒᵈ} : ofDual a < ofDual b ↔ b < a :=
  Iff.rfl
#align order_dual.of_dual_lt_of_dual OrderDual.ofDual_lt_ofDual

theorem le_toDual [LE α] {a : αᵒᵈ} {b : α} : a ≤ toDual b ↔ b ≤ ofDual a :=
  Iff.rfl
#align order_dual.le_to_dual OrderDual.le_toDual

theorem lt_toDual [LT α] {a : αᵒᵈ} {b : α} : a < toDual b ↔ b < ofDual a :=
  Iff.rfl
#align order_dual.lt_to_dual OrderDual.lt_toDual

theorem toDual_le [LE α] {a : α} {b : αᵒᵈ} : toDual a ≤ b ↔ ofDual b ≤ a :=
  Iff.rfl
#align order_dual.to_dual_le OrderDual.toDual_le

theorem toDual_lt [LT α] {a : α} {b : αᵒᵈ} : toDual a < b ↔ ofDual b < a :=
  Iff.rfl
#align order_dual.to_dual_lt OrderDual.toDual_lt

/-- Recursor for `αᵒᵈ`. -/
@[elab_as_elim]
protected def rec {C : αᵒᵈ → Sort _} (h₂ : ∀ a : α, C (toDual a)) : ∀ a : αᵒᵈ, C a :=
  h₂
#align order_dual.rec OrderDual.rec

@[simp]
protected theorem «forall» {p : αᵒᵈ → Prop} : (∀ a, p a) ↔ ∀ a, p (toDual a) :=
  Iff.rfl
#align order_dual.forall OrderDual.forall

@[simp]
protected theorem «exists» {p : αᵒᵈ → Prop} : (∃ a, p a) ↔ ∃ a, p (toDual a) :=
  Iff.rfl
#align order_dual.exists OrderDual.exists

alias toDual_le_toDual ↔ _ _root_.LE.le.dual

alias toDual_lt_toDual ↔ _ _root_.LT.lt.dual

alias ofDual_le_ofDual ↔ _ _root_.LE.le.ofDual
#align has_le.le.of_dual LE.le.ofDual

alias ofDual_lt_ofDual ↔ _ _root_.LT.lt.ofDual
#align has_lt.lt.of_dual LT.lt.ofDual

end OrderDual

/-! ### Lexicographic order -/

/-- A type synonym to equip a type with its lexicographic order. -/
def Lex (α : Type _) :=
  α
#align lex Lex

/-- `toLex` is the identity function to the `Lex` of a type.  -/
@[match_pattern]
def toLex : α ≃ Lex α :=
  Equiv.refl _
#align to_lex toLex

/-- `ofLex` is the identity function from the `lex` of a type.  -/
@[match_pattern]
def ofLex : Lex α ≃ α :=
  Equiv.refl _
#align of_lex ofLex

@[simp]
theorem toLex_symm_eq : (@toLex α).symm = ofLex :=
  rfl
#align to_lex_symm_eq toLex_symm_eq

@[simp]
theorem ofLex_symm_eq : (@ofLex α).symm = toLex :=
  rfl
#align of_lex_symm_eq ofLex_symm_eq

@[simp]
theorem toLex_ofLex (a : Lex α) : toLex (ofLex a) = a :=
  rfl
#align to_lex_of_lex toLex_ofLex

@[simp]
theorem ofLex_toLex (a : α) : ofLex (toLex a) = a :=
  rfl
#align of_lex_to_lex ofLex_toLex

-- Porting note:
-- removed @[simp] since this already follows by `simp only [EmbeddingLike.apply_eq_iff_eq]`
theorem toLex_inj {a b : α} : toLex a = toLex b ↔ a = b :=
  Iff.rfl
#align to_lex_inj toLex_inj

-- Porting note:
-- removed @[simp] since this already follows by `simp only [EmbeddingLike.apply_eq_iff_eq]`
theorem ofLex_inj {a b : Lex α} : ofLex a = ofLex b ↔ a = b :=
  Iff.rfl
#align of_lex_inj ofLex_inj

/-- A recursor for `Lex`. Use as `induction x using Lex.rec`. -/
protected def Lex.rec {β : Lex α → Sort _} (h : ∀ a, β (toLex a)) : ∀ a, β a := fun a => h (ofLex a)
#align lex.rec Lex.rec