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

import measure_theory.measure.vector_measure

/-!
# Complex measure

This file proves some elementary results about complex measures. In particular, we prove that
a complex measure is always in the form `s + it` where `s` and `t` are signed measures.

The complex measure is defined to be vector measure over `ℂ`, this definition can be found
in `measure_theory.measure.vector_measure` and is known as `measure_theory.complex_measure`.

## Main definitions

* `measure_theory.complex_measure.re`: obtains a signed measure `s` from a complex measure `c`
  such that `s i = (c i).re` for all measurable sets `i`.
* `measure_theory.complex_measure.im`: obtains a signed measure `s` from a complex measure `c`
  such that `s i = (c i).im` for all measurable sets `i`.
* `measure_theory.signed_measure.to_complex_measure`: given two signed measures `s` and `t`,
  `s.to_complex_measure t` provides a complex measure of the form `s + it`.
* `measure_theory.complex_measure.equiv_signed_measure`: is the equivalence between the complex
  measures and the type of the product of the signed measures with itself.

# Tags

Complex measure
-/

noncomputable theory
open_locale classical measure_theory ennreal nnreal

variables {α β : Type*} {m : measurable_space α}

namespace measure_theory

open vector_measure

namespace complex_measure

include m

/-- The real part of a complex measure is a signed measure. -/
@[simps apply]
def re : complex_measure α →ₗ[ℝ] signed_measure α :=
map_rangeₗ complex.re_clm complex.continuous_re

/-- The imaginary part of a complex measure is a signed measure. -/
@[simps apply]
def im : complex_measure α →ₗ[ℝ] signed_measure α :=
map_rangeₗ complex.im_clm complex.continuous_im

/-- Given `s` and `t` signed measures, `s + it` is a complex measure-/
@[simps]
def _root_.measure_theory.signed_measure.to_complex_measure (s t : signed_measure α) :
  complex_measure α :=
{ measure_of' := λ i, ⟨s i, t i⟩,
  empty' := by rw [s.empty, t.empty]; refl,
  not_measurable' := λ i hi, by rw [s.not_measurable hi, t.not_measurable hi]; refl,
  m_Union' := λ f hf hfdisj,
    (complex.has_sum_iff _ _).2 ⟨s.m_Union hf hfdisj, t.m_Union hf hfdisj⟩ }

lemma _root_.measure_theory.signed_measure.to_complex_measure_apply
  {s t : signed_measure α} {i : set α} : s.to_complex_measure t i = ⟨s i, t i⟩ := rfl

lemma to_complex_measure_to_signed_measure (c : complex_measure α) :
  c.re.to_complex_measure c.im = c :=
by { ext i hi; refl }

lemma _root_.measure_theory.signed_measure.re_to_complex_measure
  (s t : signed_measure α) : (s.to_complex_measure t).re = s :=
by { ext i hi, refl }

lemma _root_.measure_theory.signed_measure.im_to_complex_measure
  (s t : signed_measure α) : (s.to_complex_measure t).im = t :=
by { ext i hi, refl }

/-- The complex measures form an equivalence to the type of pairs of signed measures. -/
@[simps]
def equiv_signed_measure : complex_measure α ≃ signed_measure α × signed_measure α :=
{ to_fun := λ c, ⟨c.re, c.im⟩,
  inv_fun := λ ⟨s, t⟩, s.to_complex_measure t,
  left_inv := λ c, c.to_complex_measure_to_signed_measure,
  right_inv := λ ⟨s, t⟩,
    prod.mk.inj_iff.2 ⟨s.re_to_complex_measure t, s.im_to_complex_measure t⟩ }

section

variables {R : Type*} [semiring R] [module R ℝ]
variables [has_continuous_const_smul R ℝ] [has_continuous_const_smul R ℂ]

/-- The complex measures form an linear isomorphism to the type of pairs of signed measures. -/
@[simps]
def equiv_signed_measureₗ : complex_measure α ≃ₗ[R] signed_measure α × signed_measure α :=
{ map_add' := λ c d, by { ext i hi; refl },
  map_smul' :=
  begin
    intros r c, ext i hi,
    { change (r • c i).re = r • (c i).re,
      simp [complex.smul_re] },
    { ext i hi,
      change (r • c i).im = r • (c i).im,
      simp [complex.smul_im] }
  end, .. equiv_signed_measure }

end

lemma absolutely_continuous_ennreal_iff (c : complex_measure α) (μ : vector_measure α ℝ≥0∞) :
  c ≪ᵥ μ ↔ c.re ≪ᵥ μ ∧ c.im ≪ᵥ μ :=
begin
  split; intro h,
  { split; { intros i hi, simp [h hi] } },
  { intros i hi,
    rw [← complex.re_add_im (c i), (_ : (c i).re = 0), (_ : (c i).im = 0)],
    exacts [by simp, h.2 hi, h.1 hi] }
end

end complex_measure

end measure_theory


import .set
section
universe u

def rel (α : Type u) := set (α × α)
variable {α : Type u}
instance rel_has_mem : has_mem (α × α) (rel α) := set.has_mem
instance rel_has_inter : has_inter (rel α) := set.has_inter
instance rel_has_union : has_union (rel α) := set.has_union
instance rel_has_subset : has_subset (rel α) := set.has_subset

def comp : rel α  → rel α → rel α
|t s := {p | ∃ b:α, (p.1,b) ∈ t ∧ (b,p.2) ∈ s}

local infix `∙`:100 := comp

variables {s t r : rel α }

theorem comp.assoc : s ∙ (t ∙ r) = (s ∙ t) ∙ r :=
set.ext $ λ ⟨a,z⟩, ⟨λ ⟨b,h₁,⟨y,h₂,h₃⟩⟩, ⟨y,⟨b,h₁,h₂⟩,h₃⟩, λ ⟨y,⟨b,h₁,h₂⟩,h₃⟩, ⟨b,h₁,⟨y,h₂,h₃⟩⟩⟩

theorem T59b : r ∙ (s ∩ t) ⊆ r ∙ s := λ ⟨a,z⟩ ⟨b,h₁,⟨h₃,h₄⟩⟩, ⟨b,_,h₃⟩

end


import algebra.big_operators.basic

namespace coding_theory

section hamming

open_locale big_operators
variables {n : ℕ} {K : Type*} (x y z : fin n → K)

section decidable_eq
variable [decidable_eq K]

open finset function

def ham_dist : ℕ := (filter (λ i, x i ≠ y i) univ).card

@[simp]
lemma ham_dist_eq : ham_dist x y = (filter (λ i, x i ≠ y i) univ).card := rfl

lemma ham_dist_eq_sum_ite : ham_dist x y = ∑ i, if x i ≠ y i then 1 else 0 :=
by { simp, rw [sum_ite, ← card_eq_sum_ones, sum_const_zero, zero_add] }

@[simp]
lemma ham_dist_self_eq_zero : ham_dist x x = 0 := by simp

lemma ham_dist_comm : ham_dist x y = ham_dist y x :=
by { simp, congr, ext, exact ne_comm }

lemma zero_le_ham_dist : 0 ≤ ham_dist x y := zero_le _

lemma ham_dist_eq_zero_iff_eq : ham_dist x y = 0 ↔ x = y :=
begin
  split; intros h; [refine funext _, rw h]; [contrapose h, exact ham_dist_self_eq_zero _],
  rw [ham_dist_eq, card_eq_zero, filter_eq_empty_iff],
  simpa only [mem_univ, not_not, forall_true_left]
end

lemma ham_dist_pos_iff_neq : 0 < ham_dist x y ↔ x ≠ y :=
begin
  rw [ne.def, ← ham_dist_eq_zero_iff_eq], split; intros h;
  [intro c, exact lt_of_le_of_ne (zero_le_ham_dist _ _) (ne.symm h)],
  rw c at h, exact nat.not_lt_zero _ h
end

lemma ham_dist_le : ham_dist x y ≤ ham_dist x z + ham_dist z y :=
begin
  simp_rw ham_dist_eq,
  refine le_trans (card_le_of_subset _) (card_union_le _ _),
  intros i, simp_rw [mem_union, mem_filter],
  simp only [mem_univ, ne.def, true_and], contrapose!,
  exact λ ⟨_, _⟩, eq.trans (by assumption) (by assumption)
end

section has_zero
variable [has_zero K]

def ham_wt : ℕ := ham_dist x 0

@[simp]
lemma ham_wt_eq : ham_wt x = (filter (λ i, x i ≠ 0) univ).card := ham_dist_eq _ _

lemma ham_wt_eq_sum_ite : ham_wt x = ∑ i, if x i ≠ 0 then 1 else 0 := ham_dist_eq_sum_ite _ _

@[simp]
lemma ham_wt_zero_eq_zero : ham_wt (0 : fin n → K) = 0 := ham_dist_self_eq_zero _

lemma zero_le_ham_wt : 0 ≤ ham_wt x := zero_le_ham_dist _ _

lemma ham_wt_eq_zero_iff_zero : ham_wt x = 0 ↔ x = 0 := ham_dist_eq_zero_iff_eq _ _

lemma ham_wt_pos_iff_neq : 0 < ham_wt x ↔ x ≠ 0 := ham_dist_pos_iff_neq _ _

end has_zero

section add_group
variable [add_group K]

lemma ham_dist_eq_wt : ham_dist x y = ham_wt (x - y) :=
by simp_rw [ham_dist_eq, ham_wt_eq, pi.sub_apply, sub_ne_zero]

end add_group

section mul_zero_class

variables [mul_zero_class K] (t : K)

lemma wt_smul_le : ham_wt (t • x) ≤ (if t ≠ 0 then 1 else 0) * (ham_wt x) :=
begin
  split_ifs,
  { simp only [ ham_wt_eq, pi.smul_apply, smul_eq_mul, ne.def, one_mul],
    refine card_le_of_subset (λ _, _), simp only [mem_filter, mem_univ, true_and],
    exact right_ne_zero_of_mul },
  { rw not_not at h, simp only [h, zero_smul, ham_wt_zero_eq_zero, zero_mul] }
end

end mul_zero_class

end decidable_eq
end hamming

/-
namespace discrete

variables

def dist {K : Type*} [decidable_eq K] := λ x y : K, if x = y then (0 : ℝ) else 1

@[simp]
def dist_eq {K : Type*} [decidable_eq K] (x y : K) : dist x y = if x = y then 0 else 1 := rfl

def pseudo_metric_space (K : Type*) [decidable_eq K] : pseudo_metric_space K := {
  dist := discrete.dist,
  dist_self := λ _, by simp [dist_eq],
  dist_comm := λ x y, by simp_rw [dist, eq_comm],
  dist_triangle := λ x y z,
  by {  simp[dist], split_ifs;
        try {simp only [  add_zero, zero_add, zero_le_one,
                          one_add_one_eq_two, one_le_two, zero_le_two]},
        finish }
}

def metric_space (K : Type*) [decidable_eq K] [has_zero K] : metric_space K :=
{ ..(discrete.pseudo_metric_space K),
  eq_of_dist_eq_zero := λ x y h, by { by_contradiction c, simp at h,
                                      rw ite_eq_left_iff at h, exact one_ne_zero (h c) },

}

instance [add_comm_group K] : normed_group K := {
  norm := λ x, dist x 0,
  dist_eq := λ x y, by simp_rw [dist, sub_eq_zero] }

instance [non_unital_ring K] : non_unital_normed_ring K := {
  norm_mul := begin  end,
  }

end discrete

namespace fin

instance : uniform_space (fin n) := ⊥

noncomputable instance : has_dist (fin n) := ⟨λ x y, dist (x : ℝ) y⟩

lemma dist_eq (x y : fin n) : dist x y = |x - y| := rfl

lemma dist_coe_nat (x y : fin n) : dist (x : ℕ) (y : ℕ) = dist x y := rfl

@[norm_cast, simp] theorem dist_cast_real {n : ℕ} (x y : (fin n)) : dist (x : ℝ) y = dist x y := rfl

lemma pairwise_one_le_dist : pairwise (λ x y : fin n, 1 ≤ dist x y) :=
begin
  intros x y hxy,
  rw ← dist_coe_nat,
  apply nat.pairwise_one_le_dist,
  contrapose! hxy,
  apply fin.coe_injective hxy
end
open metric

lemma uniform_embedding_coe_real : uniform_embedding (coe : (fin n) → ℝ) :=
uniform_embedding_bot_of_pairwise_le_dist real.zero_lt_one pairwise_one_le_dist

lemma closed_embedding_coe_real : closed_embedding (coe : (fin n) → ℝ) :=
closed_embedding_of_pairwise_le_dist real.zero_lt_one pairwise_one_le_dist

noncomputable instance : metric_space (fin n) := uniform_embedding_coe_real.comap_metric_space _

lemma dist_0_eq {x : fin (n + 1)} (h : x ≤ n / 2) : dist x 0 = x :=
begin
  split_ifs; rw dist_eq; simp, norm_cast,
end

end fin

variables (p : ℕ) [fact p.prime]
instance : normed_field (zmod p) := {! !}
end zmod
def codeword (n : ℕ) := pi_Lp 1 (λ x : fin n, zmod 2)

#check (0 : pi_Lp 1 (λ x : fin 3, zmod 2))
-/

end coding_theory


-- Copyright © 2019 François G. Dorais. All rights reserved.
-- Released under Apache 2.0 license as described in the file LICENSE.

theorem not_exists_of_forall_not {α : Sort*} {p : α → Prop} :
(∀ x, ¬ p x) → ¬ (∃ x, p x) := λ h ⟨x, hx⟩, h x hx

theorem not_exists_iff_forall_not {α : Sort*} (p : α → Prop) :
¬ (∃ x, p x) ↔ (∀ x, ¬ p x) := ⟨forall_not_of_not_exists, not_exists_of_forall_not⟩


import tactic
import data.int.basic

universe u
--local attribute [instance] classical.prop_decidable
def absVal (a : ℤ) : ℤ := if a < 0 then -a else a

theorem triIneqInt (a : ℤ) (b : ℤ)
: (absVal(b - a) ≤ absVal(a) + absVal(b)) :=
begin
rw absVal,
rw absVal,
rw absVal,
split_ifs,
linarith,
linarith,
linarith,
linarith,
linarith,
linarith,
linarith,
linarith,
end

--def absValAlt : ℤ → ℕ
--| (int.of_nat n) := n
--| (int.neg_succ_of_nat n) := n + 1


open classical

variables p q r : Prop

-- distributivity
example : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
  iff.intro
    (assume h : p ∧ (q ∨ r),
      have hp : p, from h.left,
      or.elim (h.right)
        (assume hq : q,
          show (p ∧ q) ∨ (p ∧ r), from or.inl ⟨hp, hq⟩)
        (assume hr : r,
          show (p ∧ q) ∨ (p ∧ r), from or.inr ⟨hp, hr⟩))
    (assume h : (p ∧ q) ∨ (p ∧ r),
      or.elim h
        (assume hpq : p ∧ q,
          have hp : p, from hpq.left,
          have hq : q, from hpq.right,
          show p ∧ (q ∨ r), from ⟨hp, or.inl hq⟩)
        (assume hpr : p ∧ r,
          have hp : p, from hpr.left,
          have hr : r, from hpr.right,
          show p ∧ (q ∨ r), from ⟨hp, or.inr hr⟩))

-- an example that requires classical reasoning
example : ¬(p ∧ ¬q) → (p → q) :=
  assume h : ¬(p ∧ ¬q),
  assume hp : p,
  show q, from
    or.elim (em q)
      (assume hq : q, hq)
      (assume hnq : ¬q, absurd (and.intro hp hnq) h)


variables A B C D : Prop

example : A ∧ (A → B) → B :=
assume h : A ∧ (A → B),
(and.right h) (and.left h)

example : A → ¬(¬A ∧ B) :=
assume h : A,
assume h2 : ¬A ∧ B,
show false, from (and.left h2) h

example : ¬(A ∧ B) → (A → ¬B) :=
assume h : ¬(A ∧ B),
assume h1 : A,
assume h2 : B, h (and.intro h1 h2)

example (h₁ : A ∨ B) (h₂ : A → C) (h₃ : B → D) : C ∨ D :=
  or.elim h₁
    (assume h1 : A,
      show C ∨ D, from or.inl (h₂ h1))
    (assume h1 : B,
      show C ∨ D, from or.inr (h₃ h1))

example (h : ¬A ∧ ¬B) : ¬(A ∨ B) :=
(assume h1 : (A ∨ B), show false, from or.elim h1
                (assume h2 : A, false.elim (h.left h2) )
                (assume h3 : B, false.elim (h.right h3) ))

example : ¬(A ↔ ¬A) :=
assume h : A ↔ ¬A,
have h1 : ¬A, from
    (show A → false, from
        assume h2 : A,
        false.elim ( (h.mp h2) h2)),
false.elim (h1 (h.mpr h1))


import algebra.group
import group_theory.subgroup

variables {G: Type*} {H K: set G}

theorem Q_01 [hG: group G] [hH: is_subgroup H] [hK: is_subgroup K]:
  is_subgroup (H ∩ K) := {

  inv_mem := λ a ha, ⟨
  is_subgroup.inv_mem ha.1,
  is_subgroup.inv_mem ha.2
  ⟩,

  one_mem := ⟨hH.one_mem, hK.one_mem⟩,

  mul_mem := λ a b ha hb, ⟨
  is_submonoid.mul_mem ha.1 hb.1,
  is_submonoid.mul_mem ha.2 hb.2,
  ⟩
}


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

! This file was ported from Lean 3 source module algebra.regular.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.Group.Commute
import Mathbin.Algebra.Order.Monoid.Lemmas
import Mathbin.Algebra.GroupWithZero.Basic

/-!
# Regular elements

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

We introduce left-regular, right-regular and regular elements, along with their `to_additive`
analogues add-left-regular, add-right-regular and add-regular elements.

By definition, a regular element in a commutative ring is a non-zero divisor.
Lemma `is_regular_of_ne_zero` implies that every non-zero element of an integral domain is regular.
Since it assumes that the ring is a `cancel_monoid_with_zero` it applies also, for instance, to `ℕ`.

The lemmas in Section `mul_zero_class` show that the `0` element is (left/right-)regular if and
only if the `mul_zero_class` is trivial.  This is useful when figuring out stopping conditions for
regular sequences: if `0` is ever an element of a regular sequence, then we can extend the sequence
by adding one further `0`.

The final goal is to develop part of the API to prove, eventually, results about non-zero-divisors.
-/

variable {R : Type _}

section Mul

variable [Mul R]

#print IsLeftRegular /-
/-- A left-regular element is an element `c` such that multiplication on the left by `c`
is injective. -/
@[to_additive
      "An add-left-regular element is an element `c` such that addition on the left by `c`\nis injective. -/\n"]
def IsLeftRegular (c : R) :=
  ((· * ·) c).Injective
#align is_left_regular IsLeftRegular
#align is_add_left_regular IsAddLeftRegular
-/

#print IsRightRegular /-
/-- A right-regular element is an element `c` such that multiplication on the right by `c`
is injective. -/
@[to_additive
      "An add-right-regular element is an element `c` such that addition on the right by `c`\nis injective."]
def IsRightRegular (c : R) :=
  (· * c).Injective
#align is_right_regular IsRightRegular
#align is_add_right_regular IsAddRightRegular
-/

#print IsAddRegular /-
/-- An add-regular element is an element `c` such that addition by `c` both on the left and
on the right is injective. -/
structure IsAddRegular {R : Type _} [Add R] (c : R) : Prop where
  left : IsAddLeftRegular c
  right : IsAddRightRegular c
#align is_add_regular IsAddRegular
-/

#print IsRegular /-
/-- A regular element is an element `c` such that multiplication by `c` both on the left and
on the right is injective. -/
structure IsRegular (c : R) : Prop where
  left : IsLeftRegular c
  right : IsRightRegular c
#align is_regular IsRegular
-/

attribute [to_additive] IsRegular

#print MulLECancellable.isLeftRegular /-
@[to_additive]
protected theorem MulLECancellable.isLeftRegular [PartialOrder R] {a : R}
    (ha : MulLECancellable a) : IsLeftRegular a :=
  ha.Injective
#align mul_le_cancellable.is_left_regular MulLECancellable.isLeftRegular
#align add_le_cancellable.is_add_left_regular AddLECancellable.isAddLeftRegular
-/

#print IsLeftRegular.right_of_commute /-
theorem IsLeftRegular.right_of_commute {a : R} (ca : ∀ b, Commute a b) (h : IsLeftRegular a) :
    IsRightRegular a := fun x y xy => h <| (ca x).trans <| xy.trans <| (ca y).symm
#align is_left_regular.right_of_commute IsLeftRegular.right_of_commute
-/

#print Commute.isRegular_iff /-
theorem Commute.isRegular_iff {a : R} (ca : ∀ b, Commute a b) : IsRegular a ↔ IsLeftRegular a :=
  ⟨fun h => h.left, fun h => ⟨h, h.right_of_commute ca⟩⟩
#align commute.is_regular_iff Commute.isRegular_iff
-/

end Mul

section Semigroup

variable [Semigroup R] {a b : R}

/- warning: is_left_regular.mul -> IsLeftRegular.mul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, (IsLeftRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) a) -> (IsLeftRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) b) -> (IsLeftRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R _inst_1)) a b))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, (IsLeftRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) a) -> (IsLeftRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) b) -> (IsLeftRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toMul.{u1} R _inst_1)) a b))
Case conversion may be inaccurate. Consider using '#align is_left_regular.mul IsLeftRegular.mulₓ'. -/
/-- In a semigroup, the product of left-regular elements is left-regular. -/
@[to_additive "In an additive semigroup, the sum of add-left-regular elements is add-left.regular."]
theorem IsLeftRegular.mul (lra : IsLeftRegular a) (lrb : IsLeftRegular b) : IsLeftRegular (a * b) :=
  show Function.Injective ((· * ·) (a * b)) from comp_mul_left a b ▸ lra.comp lrb
#align is_left_regular.mul IsLeftRegular.mul
#align is_add_left_regular.add IsAddLeftRegular.add

/- warning: is_right_regular.mul -> IsRightRegular.mul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, (IsRightRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) a) -> (IsRightRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) b) -> (IsRightRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R _inst_1)) a b))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, (IsRightRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) a) -> (IsRightRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) b) -> (IsRightRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toMul.{u1} R _inst_1)) a b))
Case conversion may be inaccurate. Consider using '#align is_right_regular.mul IsRightRegular.mulₓ'. -/
/-- In a semigroup, the product of right-regular elements is right-regular. -/
@[to_additive
      "In an additive semigroup, the sum of add-right-regular elements is add-right-regular."]
theorem IsRightRegular.mul (rra : IsRightRegular a) (rrb : IsRightRegular b) :
    IsRightRegular (a * b) :=
  show Function.Injective (· * (a * b)) from comp_mul_right b a ▸ rrb.comp rra
#align is_right_regular.mul IsRightRegular.mul
#align is_add_right_regular.add IsAddRightRegular.add

/- warning: is_left_regular.of_mul -> IsLeftRegular.of_mul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, (IsLeftRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R _inst_1)) a b)) -> (IsLeftRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) b)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, (IsLeftRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toMul.{u1} R _inst_1)) a b)) -> (IsLeftRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) b)
Case conversion may be inaccurate. Consider using '#align is_left_regular.of_mul IsLeftRegular.of_mulₓ'. -/
/-- If an element `b` becomes left-regular after multiplying it on the left by a left-regular
element, then `b` is left-regular. -/
@[to_additive
      "If an element `b` becomes add-left-regular after adding to it on the left a\nadd-left-regular element, then `b` is add-left-regular."]
theorem IsLeftRegular.of_mul (ab : IsLeftRegular (a * b)) : IsLeftRegular b :=
  Function.Injective.of_comp (by rwa [comp_mul_left a b])
#align is_left_regular.of_mul IsLeftRegular.of_mul
#align is_add_left_regular.of_add IsAddLeftRegular.of_add

/- warning: mul_is_left_regular_iff -> mul_isLeftRegular_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} (b : R), (IsLeftRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) a) -> (Iff (IsLeftRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R _inst_1)) a b)) (IsLeftRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) b))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} (b : R), (IsLeftRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) a) -> (Iff (IsLeftRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toMul.{u1} R _inst_1)) a b)) (IsLeftRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) b))
Case conversion may be inaccurate. Consider using '#align mul_is_left_regular_iff mul_isLeftRegular_iffₓ'. -/
/-- An element is left-regular if and only if multiplying it on the left by a left-regular element
is left-regular. -/
@[simp,
  to_additive
      "An element is add-left-regular if and only if adding to it on the left a\nadd-left-regular element is add-left-regular."]
theorem mul_isLeftRegular_iff (b : R) (ha : IsLeftRegular a) :
    IsLeftRegular (a * b) ↔ IsLeftRegular b :=
  ⟨fun ab => IsLeftRegular.of_mul ab, fun ab => IsLeftRegular.mul ha ab⟩
#align mul_is_left_regular_iff mul_isLeftRegular_iff
#align add_is_add_left_regular_iff add_isAddLeftRegular_iff

/- warning: is_right_regular.of_mul -> IsRightRegular.of_mul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, (IsRightRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R _inst_1)) b a)) -> (IsRightRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) b)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, (IsRightRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toMul.{u1} R _inst_1)) b a)) -> (IsRightRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) b)
Case conversion may be inaccurate. Consider using '#align is_right_regular.of_mul IsRightRegular.of_mulₓ'. -/
/-- If an element `b` becomes right-regular after multiplying it on the right by a right-regular
element, then `b` is right-regular. -/
@[to_additive
      "If an element `b` becomes add-right-regular after adding to it on the right a\nadd-right-regular element, then `b` is add-right-regular."]
theorem IsRightRegular.of_mul (ab : IsRightRegular (b * a)) : IsRightRegular b :=
  by
  refine' fun x y xy => ab (_ : x * (b * a) = y * (b * a))
  rw [← mul_assoc, ← mul_assoc]
  exact congr_fun (congr_arg (· * ·) xy) a
#align is_right_regular.of_mul IsRightRegular.of_mul
#align is_add_right_regular.of_add IsAddRightRegular.of_add

/- warning: mul_is_right_regular_iff -> mul_isRightRegular_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} (b : R), (IsRightRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) a) -> (Iff (IsRightRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R _inst_1)) b a)) (IsRightRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) b))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} (b : R), (IsRightRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) a) -> (Iff (IsRightRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toMul.{u1} R _inst_1)) b a)) (IsRightRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) b))
Case conversion may be inaccurate. Consider using '#align mul_is_right_regular_iff mul_isRightRegular_iffₓ'. -/
/-- An element is right-regular if and only if multiplying it on the right with a right-regular
element is right-regular. -/
@[simp,
  to_additive
      "An element is add-right-regular if and only if adding it on the right to a\nadd-right-regular element is add-right-regular."]
theorem mul_isRightRegular_iff (b : R) (ha : IsRightRegular a) :
    IsRightRegular (b * a) ↔ IsRightRegular b :=
  ⟨fun ab => IsRightRegular.of_mul ab, fun ab => IsRightRegular.mul ab ha⟩
#align mul_is_right_regular_iff mul_isRightRegular_iff
#align add_is_add_right_regular_iff add_isAddRightRegular_iff

/- warning: is_regular_mul_and_mul_iff -> isRegular_mul_and_mul_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, Iff (And (IsRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R _inst_1)) a b)) (IsRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R _inst_1)) b a))) (And (IsRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) a) (IsRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) b))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, Iff (And (IsRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toMul.{u1} R _inst_1)) a b)) (IsRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toMul.{u1} R _inst_1)) b a))) (And (IsRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) a) (IsRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) b))
Case conversion may be inaccurate. Consider using '#align is_regular_mul_and_mul_iff isRegular_mul_and_mul_iffₓ'. -/
/-- Two elements `a` and `b` are regular if and only if both products `a * b` and `b * a`
are regular. -/
@[to_additive
      "Two elements `a` and `b` are add-regular if and only if both sums `a + b` and `b + a`\nare add-regular."]
theorem isRegular_mul_and_mul_iff :
    IsRegular (a * b) ∧ IsRegular (b * a) ↔ IsRegular a ∧ IsRegular b :=
  by
  refine' ⟨_, _⟩
  · rintro ⟨ab, ba⟩
    exact
      ⟨⟨IsLeftRegular.of_mul ba.left, IsRightRegular.of_mul ab.right⟩,
        ⟨IsLeftRegular.of_mul ab.left, IsRightRegular.of_mul ba.right⟩⟩
  · rintro ⟨ha, hb⟩
    exact
      ⟨⟨(mul_isLeftRegular_iff _ ha.left).mpr hb.left,
          (mul_isRightRegular_iff _ hb.right).mpr ha.right⟩,
        ⟨(mul_isLeftRegular_iff _ hb.left).mpr ha.left,
          (mul_isRightRegular_iff _ ha.right).mpr hb.right⟩⟩
#align is_regular_mul_and_mul_iff isRegular_mul_and_mul_iff
#align is_add_regular_add_and_add_iff isAddRegular_add_and_add_iff

/- warning: is_regular.and_of_mul_of_mul -> IsRegular.and_of_mul_of_mul is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, (IsRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R _inst_1)) a b)) -> (IsRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R _inst_1)) b a)) -> (And (IsRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) a) (IsRegular.{u1} R (Semigroup.toHasMul.{u1} R _inst_1) b))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Semigroup.{u1} R] {a : R} {b : R}, (IsRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toMul.{u1} R _inst_1)) a b)) -> (IsRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toMul.{u1} R _inst_1)) b a)) -> (And (IsRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) a) (IsRegular.{u1} R (Semigroup.toMul.{u1} R _inst_1) b))
Case conversion may be inaccurate. Consider using '#align is_regular.and_of_mul_of_mul IsRegular.and_of_mul_of_mulₓ'. -/
/-- The "most used" implication of `mul_and_mul_iff`, with split hypotheses, instead of `∧`. -/
@[to_additive
      "The \"most used\" implication of `add_and_add_iff`, with split hypotheses,\ninstead of `∧`."]
theorem IsRegular.and_of_mul_of_mul (ab : IsRegular (a * b)) (ba : IsRegular (b * a)) :
    IsRegular a ∧ IsRegular b :=
  isRegular_mul_and_mul_iff.mp ⟨ab, ba⟩
#align is_regular.and_of_mul_of_mul IsRegular.and_of_mul_of_mul
#align is_add_regular.and_of_add_of_add IsAddRegular.and_of_add_of_add

end Semigroup

section MulZeroClass

variable [MulZeroClass R] {a b : R}

/- warning: is_left_regular.subsingleton -> IsLeftRegular.subsingleton is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], (IsLeftRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1))))) -> (Subsingleton.{succ u1} R)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], (IsLeftRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1)))) -> (Subsingleton.{succ u1} R)
Case conversion may be inaccurate. Consider using '#align is_left_regular.subsingleton IsLeftRegular.subsingletonₓ'. -/
/-- The element `0` is left-regular if and only if `R` is trivial. -/
theorem IsLeftRegular.subsingleton (h : IsLeftRegular (0 : R)) : Subsingleton R :=
  ⟨fun a b => h <| Eq.trans (MulZeroClass.zero_mul a) (MulZeroClass.zero_mul b).symm⟩
#align is_left_regular.subsingleton IsLeftRegular.subsingleton

/- warning: is_right_regular.subsingleton -> IsRightRegular.subsingleton is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], (IsRightRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1))))) -> (Subsingleton.{succ u1} R)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], (IsRightRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1)))) -> (Subsingleton.{succ u1} R)
Case conversion may be inaccurate. Consider using '#align is_right_regular.subsingleton IsRightRegular.subsingletonₓ'. -/
/-- The element `0` is right-regular if and only if `R` is trivial. -/
theorem IsRightRegular.subsingleton (h : IsRightRegular (0 : R)) : Subsingleton R :=
  ⟨fun a b => h <| Eq.trans (MulZeroClass.mul_zero a) (MulZeroClass.mul_zero b).symm⟩
#align is_right_regular.subsingleton IsRightRegular.subsingleton

/- warning: is_regular.subsingleton -> IsRegular.subsingleton is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], (IsRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1))))) -> (Subsingleton.{succ u1} R)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], (IsRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1)))) -> (Subsingleton.{succ u1} R)
Case conversion may be inaccurate. Consider using '#align is_regular.subsingleton IsRegular.subsingletonₓ'. -/
/-- The element `0` is regular if and only if `R` is trivial. -/
theorem IsRegular.subsingleton (h : IsRegular (0 : R)) : Subsingleton R :=
  h.left.Subsingleton
#align is_regular.subsingleton IsRegular.subsingleton

/- warning: is_left_regular_zero_iff_subsingleton -> isLeftRegular_zero_iff_subsingleton is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], Iff (IsLeftRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1))))) (Subsingleton.{succ u1} R)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], Iff (IsLeftRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1)))) (Subsingleton.{succ u1} R)
Case conversion may be inaccurate. Consider using '#align is_left_regular_zero_iff_subsingleton isLeftRegular_zero_iff_subsingletonₓ'. -/
/-- The element `0` is left-regular if and only if `R` is trivial. -/
theorem isLeftRegular_zero_iff_subsingleton : IsLeftRegular (0 : R) ↔ Subsingleton R :=
  ⟨fun h => h.Subsingleton, fun H a b h => @Subsingleton.elim _ H a b⟩
#align is_left_regular_zero_iff_subsingleton isLeftRegular_zero_iff_subsingleton

/- warning: not_is_left_regular_zero_iff -> not_isLeftRegular_zero_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], Iff (Not (IsLeftRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1)))))) (Nontrivial.{u1} R)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], Iff (Not (IsLeftRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1))))) (Nontrivial.{u1} R)
Case conversion may be inaccurate. Consider using '#align not_is_left_regular_zero_iff not_isLeftRegular_zero_iffₓ'. -/
/-- In a non-trivial `mul_zero_class`, the `0` element is not left-regular. -/
theorem not_isLeftRegular_zero_iff : ¬IsLeftRegular (0 : R) ↔ Nontrivial R :=
  by
  rw [nontrivial_iff, not_iff_comm, isLeftRegular_zero_iff_subsingleton, subsingleton_iff]
  push_neg
  exact Iff.rfl
#align not_is_left_regular_zero_iff not_isLeftRegular_zero_iff

/- warning: is_right_regular_zero_iff_subsingleton -> isRightRegular_zero_iff_subsingleton is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], Iff (IsRightRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1))))) (Subsingleton.{succ u1} R)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], Iff (IsRightRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1)))) (Subsingleton.{succ u1} R)
Case conversion may be inaccurate. Consider using '#align is_right_regular_zero_iff_subsingleton isRightRegular_zero_iff_subsingletonₓ'. -/
/-- The element `0` is right-regular if and only if `R` is trivial. -/
theorem isRightRegular_zero_iff_subsingleton : IsRightRegular (0 : R) ↔ Subsingleton R :=
  ⟨fun h => h.Subsingleton, fun H a b h => @Subsingleton.elim _ H a b⟩
#align is_right_regular_zero_iff_subsingleton isRightRegular_zero_iff_subsingleton

/- warning: not_is_right_regular_zero_iff -> not_isRightRegular_zero_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], Iff (Not (IsRightRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1)))))) (Nontrivial.{u1} R)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], Iff (Not (IsRightRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1))))) (Nontrivial.{u1} R)
Case conversion may be inaccurate. Consider using '#align not_is_right_regular_zero_iff not_isRightRegular_zero_iffₓ'. -/
/-- In a non-trivial `mul_zero_class`, the `0` element is not right-regular. -/
theorem not_isRightRegular_zero_iff : ¬IsRightRegular (0 : R) ↔ Nontrivial R :=
  by
  rw [nontrivial_iff, not_iff_comm, isRightRegular_zero_iff_subsingleton, subsingleton_iff]
  push_neg
  exact Iff.rfl
#align not_is_right_regular_zero_iff not_isRightRegular_zero_iff

/- warning: is_regular_iff_subsingleton -> isRegular_iff_subsingleton is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], Iff (IsRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1))))) (Subsingleton.{succ u1} R)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R], Iff (IsRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1)))) (Subsingleton.{succ u1} R)
Case conversion may be inaccurate. Consider using '#align is_regular_iff_subsingleton isRegular_iff_subsingletonₓ'. -/
/-- The element `0` is regular if and only if `R` is trivial. -/
theorem isRegular_iff_subsingleton : IsRegular (0 : R) ↔ Subsingleton R :=
  ⟨fun h => h.left.Subsingleton, fun h =>
    ⟨isLeftRegular_zero_iff_subsingleton.mpr h, isRightRegular_zero_iff_subsingleton.mpr h⟩⟩
#align is_regular_iff_subsingleton isRegular_iff_subsingleton

/- warning: is_left_regular.ne_zero -> IsLeftRegular.ne_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] {a : R} [_inst_2 : Nontrivial.{u1} R], (IsLeftRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) a) -> (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1)))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] {a : R} [_inst_2 : Nontrivial.{u1} R], (IsLeftRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) a) -> (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align is_left_regular.ne_zero IsLeftRegular.ne_zeroₓ'. -/
/-- A left-regular element of a `nontrivial` `mul_zero_class` is non-zero. -/
theorem IsLeftRegular.ne_zero [Nontrivial R] (la : IsLeftRegular a) : a ≠ 0 :=
  by
  rintro rfl
  rcases exists_pair_ne R with ⟨x, y, xy⟩
  refine' xy (la _)
  rw [MulZeroClass.zero_mul, MulZeroClass.zero_mul]
#align is_left_regular.ne_zero IsLeftRegular.ne_zero

/- warning: is_right_regular.ne_zero -> IsRightRegular.ne_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] {a : R} [_inst_2 : Nontrivial.{u1} R], (IsRightRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) a) -> (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1)))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] {a : R} [_inst_2 : Nontrivial.{u1} R], (IsRightRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) a) -> (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align is_right_regular.ne_zero IsRightRegular.ne_zeroₓ'. -/
/-- A right-regular element of a `nontrivial` `mul_zero_class` is non-zero. -/
theorem IsRightRegular.ne_zero [Nontrivial R] (ra : IsRightRegular a) : a ≠ 0 :=
  by
  rintro rfl
  rcases exists_pair_ne R with ⟨x, y, xy⟩
  refine' xy (ra (_ : x * 0 = y * 0))
  rw [MulZeroClass.mul_zero, MulZeroClass.mul_zero]
#align is_right_regular.ne_zero IsRightRegular.ne_zero

/- warning: is_regular.ne_zero -> IsRegular.ne_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] {a : R} [_inst_2 : Nontrivial.{u1} R], (IsRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) a) -> (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1)))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] {a : R} [_inst_2 : Nontrivial.{u1} R], (IsRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) a) -> (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align is_regular.ne_zero IsRegular.ne_zeroₓ'. -/
/-- A regular element of a `nontrivial` `mul_zero_class` is non-zero. -/
theorem IsRegular.ne_zero [Nontrivial R] (la : IsRegular a) : a ≠ 0 :=
  la.left.NeZero
#align is_regular.ne_zero IsRegular.ne_zero

/- warning: not_is_left_regular_zero -> not_isLeftRegular_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] [nR : Nontrivial.{u1} R], Not (IsLeftRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1)))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] [nR : Nontrivial.{u1} R], Not (IsLeftRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align not_is_left_regular_zero not_isLeftRegular_zeroₓ'. -/
/-- In a non-trivial ring, the element `0` is not left-regular -- with typeclasses. -/
theorem not_isLeftRegular_zero [nR : Nontrivial R] : ¬IsLeftRegular (0 : R) :=
  not_isLeftRegular_zero_iff.mpr nR
#align not_is_left_regular_zero not_isLeftRegular_zero

/- warning: not_is_right_regular_zero -> not_isRightRegular_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] [nR : Nontrivial.{u1} R], Not (IsRightRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1)))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] [nR : Nontrivial.{u1} R], Not (IsRightRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align not_is_right_regular_zero not_isRightRegular_zeroₓ'. -/
/-- In a non-trivial ring, the element `0` is not right-regular -- with typeclasses. -/
theorem not_isRightRegular_zero [nR : Nontrivial R] : ¬IsRightRegular (0 : R) :=
  not_isRightRegular_zero_iff.mpr nR
#align not_is_right_regular_zero not_isRightRegular_zero

/- warning: not_is_regular_zero -> not_isRegular_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] [_inst_2 : Nontrivial.{u1} R], Not (IsRegular.{u1} R (MulZeroClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R _inst_1)))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulZeroClass.{u1} R] [_inst_2 : Nontrivial.{u1} R], Not (IsRegular.{u1} R (MulZeroClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R _inst_1))))
Case conversion may be inaccurate. Consider using '#align not_is_regular_zero not_isRegular_zeroₓ'. -/
/-- In a non-trivial ring, the element `0` is not regular -- with typeclasses. -/
theorem not_isRegular_zero [Nontrivial R] : ¬IsRegular (0 : R) := fun h => IsRegular.ne_zero h rfl
#align not_is_regular_zero not_isRegular_zero

end MulZeroClass

section MulOneClass

variable [MulOneClass R]

/- warning: is_regular_one -> isRegular_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : MulOneClass.{u1} R], IsRegular.{u1} R (MulOneClass.toHasMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (MulOneClass.toHasOne.{u1} R _inst_1))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : MulOneClass.{u1} R], IsRegular.{u1} R (MulOneClass.toMul.{u1} R _inst_1) (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (MulOneClass.toOne.{u1} R _inst_1)))
Case conversion may be inaccurate. Consider using '#align is_regular_one isRegular_oneₓ'. -/
/-- If multiplying by `1` on either side is the identity, `1` is regular. -/
@[to_additive "If adding `0` on either side is the identity, `0` is regular."]
theorem isRegular_one : IsRegular (1 : R) :=
  ⟨fun a b ab => (one_mul a).symm.trans (Eq.trans ab (one_mul b)), fun a b ab =>
    (mul_one a).symm.trans (Eq.trans ab (mul_one b))⟩
#align is_regular_one isRegular_one
#align is_add_regular_zero isAddRegular_zero

end MulOneClass

section CommSemigroup

variable [CommSemigroup R] {a b : R}

/- warning: is_regular_mul_iff -> isRegular_mul_iff is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CommSemigroup.{u1} R] {a : R} {b : R}, Iff (IsRegular.{u1} R (Semigroup.toHasMul.{u1} R (CommSemigroup.toSemigroup.{u1} R _inst_1)) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toHasMul.{u1} R (CommSemigroup.toSemigroup.{u1} R _inst_1))) a b)) (And (IsRegular.{u1} R (Semigroup.toHasMul.{u1} R (CommSemigroup.toSemigroup.{u1} R _inst_1)) a) (IsRegular.{u1} R (Semigroup.toHasMul.{u1} R (CommSemigroup.toSemigroup.{u1} R _inst_1)) b))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CommSemigroup.{u1} R] {a : R} {b : R}, Iff (IsRegular.{u1} R (Semigroup.toMul.{u1} R (CommSemigroup.toSemigroup.{u1} R _inst_1)) (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (Semigroup.toMul.{u1} R (CommSemigroup.toSemigroup.{u1} R _inst_1))) a b)) (And (IsRegular.{u1} R (Semigroup.toMul.{u1} R (CommSemigroup.toSemigroup.{u1} R _inst_1)) a) (IsRegular.{u1} R (Semigroup.toMul.{u1} R (CommSemigroup.toSemigroup.{u1} R _inst_1)) b))
Case conversion may be inaccurate. Consider using '#align is_regular_mul_iff isRegular_mul_iffₓ'. -/
/-- A product is regular if and only if the factors are. -/
@[to_additive "A sum is add-regular if and only if the summands are."]
theorem isRegular_mul_iff : IsRegular (a * b) ↔ IsRegular a ∧ IsRegular b :=
  by
  refine' Iff.trans _ isRegular_mul_and_mul_iff
  refine' ⟨fun ab => ⟨ab, by rwa [mul_comm]⟩, fun rab => rab.1⟩
#align is_regular_mul_iff isRegular_mul_iff
#align is_add_regular_add_iff isAddRegular_add_iff

end CommSemigroup

section Monoid

variable [Monoid R] {a b : R}

/- warning: is_left_regular_of_mul_eq_one -> isLeftRegular_of_mul_eq_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Monoid.{u1} R] {a : R} {b : R}, (Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1))) b a) (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (MulOneClass.toHasOne.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)))))) -> (IsLeftRegular.{u1} R (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)) a)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Monoid.{u1} R] {a : R} {b : R}, (Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1))) b a) (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (Monoid.toOne.{u1} R _inst_1)))) -> (IsLeftRegular.{u1} R (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)) a)
Case conversion may be inaccurate. Consider using '#align is_left_regular_of_mul_eq_one isLeftRegular_of_mul_eq_oneₓ'. -/
/-- An element admitting a left inverse is left-regular. -/
@[to_additive "An element admitting a left additive opposite is add-left-regular."]
theorem isLeftRegular_of_mul_eq_one (h : b * a = 1) : IsLeftRegular a :=
  @IsLeftRegular.of_mul R _ _ _
    (by
      rw [h]
      exact is_regular_one.left)
#align is_left_regular_of_mul_eq_one isLeftRegular_of_mul_eq_one
#align is_add_left_regular_of_add_eq_zero isAddLeftRegular_of_add_eq_zero

/- warning: is_right_regular_of_mul_eq_one -> isRightRegular_of_mul_eq_one is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Monoid.{u1} R] {a : R} {b : R}, (Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1))) a b) (OfNat.ofNat.{u1} R 1 (OfNat.mk.{u1} R 1 (One.one.{u1} R (MulOneClass.toHasOne.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)))))) -> (IsRightRegular.{u1} R (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)) a)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Monoid.{u1} R] {a : R} {b : R}, (Eq.{succ u1} R (HMul.hMul.{u1, u1, u1} R R R (instHMul.{u1} R (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1))) a b) (OfNat.ofNat.{u1} R 1 (One.toOfNat1.{u1} R (Monoid.toOne.{u1} R _inst_1)))) -> (IsRightRegular.{u1} R (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)) a)
Case conversion may be inaccurate. Consider using '#align is_right_regular_of_mul_eq_one isRightRegular_of_mul_eq_oneₓ'. -/
/-- An element admitting a right inverse is right-regular. -/
@[to_additive "An element admitting a right additive opposite is add-right-regular."]
theorem isRightRegular_of_mul_eq_one (h : a * b = 1) : IsRightRegular a :=
  IsRightRegular.of_mul
    (by
      rw [h]
      exact is_regular_one.right)
#align is_right_regular_of_mul_eq_one isRightRegular_of_mul_eq_one
#align is_add_right_regular_of_add_eq_zero isAddRightRegular_of_add_eq_zero

/- warning: units.is_regular -> Units.isRegular is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Monoid.{u1} R] (a : Units.{u1} R _inst_1), IsRegular.{u1} R (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} R _inst_1) R (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} R _inst_1) R (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} R _inst_1) R (coeBase.{succ u1, succ u1} (Units.{u1} R _inst_1) R (Units.hasCoe.{u1} R _inst_1)))) a)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Monoid.{u1} R] (a : Units.{u1} R _inst_1), IsRegular.{u1} R (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)) (Units.val.{u1} R _inst_1 a)
Case conversion may be inaccurate. Consider using '#align units.is_regular Units.isRegularₓ'. -/
/-- If `R` is a monoid, an element in `Rˣ` is regular. -/
@[to_additive "If `R` is an additive monoid, an element in `add_units R` is add-regular."]
theorem Units.isRegular (a : Rˣ) : IsRegular (a : R) :=
  ⟨isLeftRegular_of_mul_eq_one a.inv_mul, isRightRegular_of_mul_eq_one a.mul_inv⟩
#align units.is_regular Units.isRegular
#align add_units.is_add_regular AddUnits.isAddRegular

/- warning: is_unit.is_regular -> IsUnit.isRegular is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : Monoid.{u1} R] {a : R}, (IsUnit.{u1} R _inst_1 a) -> (IsRegular.{u1} R (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)) a)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : Monoid.{u1} R] {a : R}, (IsUnit.{u1} R _inst_1 a) -> (IsRegular.{u1} R (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R _inst_1)) a)
Case conversion may be inaccurate. Consider using '#align is_unit.is_regular IsUnit.isRegularₓ'. -/
/-- A unit in a monoid is regular. -/
@[to_additive "An additive unit in an additive monoid is add-regular."]
theorem IsUnit.isRegular (ua : IsUnit a) : IsRegular a :=
  by
  rcases ua with ⟨a, rfl⟩
  exact Units.isRegular a
#align is_unit.is_regular IsUnit.isRegular
#align is_add_unit.is_add_regular IsAddUnit.isAddRegular

end Monoid

/- warning: is_left_regular_of_left_cancel_semigroup -> isLeftRegular_of_leftCancelSemigroup is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : LeftCancelSemigroup.{u1} R] (g : R), IsLeftRegular.{u1} R (Semigroup.toHasMul.{u1} R (LeftCancelSemigroup.toSemigroup.{u1} R _inst_1)) g
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : LeftCancelSemigroup.{u1} R] (g : R), IsLeftRegular.{u1} R (Semigroup.toMul.{u1} R (LeftCancelSemigroup.toSemigroup.{u1} R _inst_1)) g
Case conversion may be inaccurate. Consider using '#align is_left_regular_of_left_cancel_semigroup isLeftRegular_of_leftCancelSemigroupₓ'. -/
/-- Elements of a left cancel semigroup are left regular. -/
@[to_additive "Elements of an add left cancel semigroup are add-left-regular."]
theorem isLeftRegular_of_leftCancelSemigroup [LeftCancelSemigroup R] (g : R) : IsLeftRegular g :=
  mul_right_injective g
#align is_left_regular_of_left_cancel_semigroup isLeftRegular_of_leftCancelSemigroup
#align is_add_left_regular_of_left_cancel_add_semigroup isAddLeftRegular_of_addLeftCancelSemigroup

/- warning: is_right_regular_of_right_cancel_semigroup -> isRightRegular_of_rightCancelSemigroup is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : RightCancelSemigroup.{u1} R] (g : R), IsRightRegular.{u1} R (Semigroup.toHasMul.{u1} R (RightCancelSemigroup.toSemigroup.{u1} R _inst_1)) g
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : RightCancelSemigroup.{u1} R] (g : R), IsRightRegular.{u1} R (Semigroup.toMul.{u1} R (RightCancelSemigroup.toSemigroup.{u1} R _inst_1)) g
Case conversion may be inaccurate. Consider using '#align is_right_regular_of_right_cancel_semigroup isRightRegular_of_rightCancelSemigroupₓ'. -/
/-- Elements of a right cancel semigroup are right regular. -/
@[to_additive "Elements of an add right cancel semigroup are add-right-regular"]
theorem isRightRegular_of_rightCancelSemigroup [RightCancelSemigroup R] (g : R) :
    IsRightRegular g :=
  mul_left_injective g
#align is_right_regular_of_right_cancel_semigroup isRightRegular_of_rightCancelSemigroup
#align is_add_right_regular_of_right_cancel_add_semigroup isAddRightRegular_of_addRightCancelSemigroup

section CancelMonoid

variable [CancelMonoid R]

/- warning: is_regular_of_cancel_monoid -> isRegular_of_cancelMonoid is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CancelMonoid.{u1} R] (g : R), IsRegular.{u1} R (MulOneClass.toHasMul.{u1} R (Monoid.toMulOneClass.{u1} R (RightCancelMonoid.toMonoid.{u1} R (CancelMonoid.toRightCancelMonoid.{u1} R _inst_1)))) g
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CancelMonoid.{u1} R] (g : R), IsRegular.{u1} R (MulOneClass.toMul.{u1} R (Monoid.toMulOneClass.{u1} R (RightCancelMonoid.toMonoid.{u1} R (CancelMonoid.toRightCancelMonoid.{u1} R _inst_1)))) g
Case conversion may be inaccurate. Consider using '#align is_regular_of_cancel_monoid isRegular_of_cancelMonoidₓ'. -/
/-- Elements of a cancel monoid are regular.  Cancel semigroups do not appear to exist. -/
@[to_additive
      "Elements of an add cancel monoid are regular.  Add cancel semigroups do not appear to exist."]
theorem isRegular_of_cancelMonoid (g : R) : IsRegular g :=
  ⟨mul_right_injective g, mul_left_injective g⟩
#align is_regular_of_cancel_monoid isRegular_of_cancelMonoid
#align is_add_regular_of_cancel_add_monoid isAddRegular_of_addCancelMonoid

end CancelMonoid

section CancelMonoidWithZero

variable [CancelMonoidWithZero R] {a : R}

/- warning: is_regular_of_ne_zero -> isRegular_of_ne_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CancelMonoidWithZero.{u1} R] {a : R}, (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CancelMonoidWithZero.toMonoidWithZero.{u1} R _inst_1)))))))) -> (IsRegular.{u1} R (MulZeroClass.toHasMul.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CancelMonoidWithZero.toMonoidWithZero.{u1} R _inst_1)))) a)
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CancelMonoidWithZero.{u1} R] {a : R}, (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MonoidWithZero.toZero.{u1} R (CancelMonoidWithZero.toMonoidWithZero.{u1} R _inst_1))))) -> (IsRegular.{u1} R (MulZeroClass.toMul.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CancelMonoidWithZero.toMonoidWithZero.{u1} R _inst_1)))) a)
Case conversion may be inaccurate. Consider using '#align is_regular_of_ne_zero isRegular_of_ne_zeroₓ'. -/
/-- Non-zero elements of an integral domain are regular. -/
theorem isRegular_of_ne_zero (a0 : a ≠ 0) : IsRegular a :=
  ⟨fun b c => (mul_right_inj' a0).mp, fun b c => (mul_left_inj' a0).mp⟩
#align is_regular_of_ne_zero isRegular_of_ne_zero

/- warning: is_regular_iff_ne_zero -> isRegular_iff_ne_zero is a dubious translation:
lean 3 declaration is
  forall {R : Type.{u1}} [_inst_1 : CancelMonoidWithZero.{u1} R] {a : R} [_inst_2 : Nontrivial.{u1} R], Iff (IsRegular.{u1} R (MulZeroClass.toHasMul.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CancelMonoidWithZero.toMonoidWithZero.{u1} R _inst_1)))) a) (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (OfNat.mk.{u1} R 0 (Zero.zero.{u1} R (MulZeroClass.toHasZero.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CancelMonoidWithZero.toMonoidWithZero.{u1} R _inst_1))))))))
but is expected to have type
  forall {R : Type.{u1}} [_inst_1 : CancelMonoidWithZero.{u1} R] {a : R} [_inst_2 : Nontrivial.{u1} R], Iff (IsRegular.{u1} R (MulZeroClass.toMul.{u1} R (MulZeroOneClass.toMulZeroClass.{u1} R (MonoidWithZero.toMulZeroOneClass.{u1} R (CancelMonoidWithZero.toMonoidWithZero.{u1} R _inst_1)))) a) (Ne.{succ u1} R a (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MonoidWithZero.toZero.{u1} R (CancelMonoidWithZero.toMonoidWithZero.{u1} R _inst_1)))))
Case conversion may be inaccurate. Consider using '#align is_regular_iff_ne_zero isRegular_iff_ne_zeroₓ'. -/
/-- In a non-trivial integral domain, an element is regular iff it is non-zero. -/
theorem isRegular_iff_ne_zero [Nontrivial R] : IsRegular a ↔ a ≠ 0 :=
  ⟨IsRegular.ne_zero, isRegular_of_ne_zero⟩
#align is_regular_iff_ne_zero isRegular_iff_ne_zero

end CancelMonoidWithZero


/-
Copyright (c) 2020 Oliver Nash. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Oliver Nash
-/
import data.fintype.prod
import data.int.parity
import algebra.big_operators.order
import tactic.ring
import tactic.noncomm_ring

/-!
# IMO 1998 Q2
In a competition, there are `a` contestants and `b` judges, where `b ≥ 3` is an odd integer. Each
judge rates each contestant as either "pass" or "fail". Suppose `k` is a number such that, for any
two judges, their ratings coincide for at most `k` contestants. Prove that `k / a ≥ (b - 1) / (2b)`.

## Solution
The problem asks us to think about triples consisting of a contestant and two judges whose ratings
agree for that contestant. We thus consider the subset `A ⊆ C × JJ` of all such incidences of
agreement, where `C` and `J` are the sets of contestants and judges, and `JJ = J × J - {(j, j)}`. We
have natural maps: `left : A → C` and `right: A → JJ`. We count the elements of `A` in two ways: as
the sum of the cardinalities of the fibres of `left` and as the sum of the cardinalities of the
fibres of `right`. We obtain an upper bound on the cardinality of `A` from the count for `right`,
and a lower bound from the count for `left`. These two bounds combine to the required result.

First consider the map `right : A → JJ`. Since the size of any fibre over a point in JJ is bounded
by `k` and since `|JJ| = b^2 - b`, we obtain the upper bound: `|A| ≤ k(b^2-b)`.

Now consider the map `left : A → C`. The fibre over a given contestant `c ∈ C` is the set of
ordered pairs of (distinct) judges who agree about `c`. We seek to bound the cardinality of this
fibre from below. Minimum agreement for a contestant occurs when the judges' ratings are split as
evenly as possible. Since `b` is odd, this occurs when they are divided into groups of size
`(b-1)/2` and `(b+1)/2`. This corresponds to a fibre of cardinality `(b-1)^2/2` and so we obtain
the lower bound: `a(b-1)^2/2 ≤ |A|`.

Rearranging gives the result.
-/

open_locale classical
noncomputable theory

/-- An ordered pair of judges. -/
abbreviation judge_pair (J : Type*) := J × J

/-- A triple consisting of contestant together with an ordered pair of judges. -/
abbreviation agreed_triple (C J : Type*) := C × (judge_pair J)

variables {C J : Type*} (r : C → J → Prop)

/-- The first judge from an ordered pair of judges. -/
abbreviation judge_pair.judge₁ : judge_pair J → J := prod.fst

/-- The second judge from an ordered pair of judges. -/
abbreviation judge_pair.judge₂ : judge_pair J → J := prod.snd

/-- The proposition that the judges in an ordered pair are distinct. -/
abbreviation judge_pair.distinct (p : judge_pair J) := p.judge₁ ≠ p.judge₂

/-- The proposition that the judges in an ordered pair agree about a contestant's rating. -/
abbreviation judge_pair.agree (p : judge_pair J) (c : C) := r c p.judge₁ ↔ r c p.judge₂

/-- The contestant from the triple consisting of a contestant and an ordered pair of judges. -/
abbreviation agreed_triple.contestant : agreed_triple C J → C := prod.fst

/-- The ordered pair of judges from the triple consisting of a contestant and an ordered pair of
judges. -/
abbreviation agreed_triple.judge_pair : agreed_triple C J → judge_pair J := prod.snd

@[simp] lemma judge_pair.agree_iff_same_rating (p : judge_pair J) (c : C) :
  p.agree r c ↔ (r c p.judge₁ ↔ r c p.judge₂) := iff.rfl

/-- The set of contestants on which two judges agree. -/
def agreed_contestants [fintype C] (p : judge_pair J) : finset C :=
finset.univ.filter (λ c, p.agree r c)

section

variables [fintype J] [fintype C]

/-- All incidences of agreement. -/
def A : finset (agreed_triple C J) := finset.univ.filter (λ (a : agreed_triple C J),
  a.judge_pair.agree r a.contestant ∧ a.judge_pair.distinct)

lemma A_maps_to_off_diag_judge_pair (a : agreed_triple C J) :
  a ∈ A r → a.judge_pair ∈ finset.off_diag (@finset.univ J _) :=
by simp [A, finset.mem_off_diag]

lemma A_fibre_over_contestant (c : C) :
  finset.univ.filter (λ (p : judge_pair J), p.agree r c ∧ p.distinct) =
  ((A r).filter (λ (a : agreed_triple C J), a.contestant = c)).image prod.snd :=
begin
  ext p,
  simp only [A, finset.mem_univ, finset.mem_filter, finset.mem_image, true_and, exists_prop],
  split,
  { rintros ⟨h₁, h₂⟩, refine ⟨(c, p), _⟩, finish, },
  { intros h, finish, },
end

lemma A_fibre_over_contestant_card (c : C) :
  (finset.univ.filter (λ (p : judge_pair J), p.agree r c ∧ p.distinct)).card =
  ((A r).filter (λ (a : agreed_triple C J), a.contestant = c)).card :=
by { rw A_fibre_over_contestant r, apply finset.card_image_of_inj_on, tidy, }

lemma A_fibre_over_judge_pair {p : judge_pair J} (h : p.distinct) :
  agreed_contestants r p =
  ((A r).filter(λ (a : agreed_triple C J), a.judge_pair = p)).image agreed_triple.contestant :=
begin
  dunfold A agreed_contestants, ext c, split; intros h,
  { rw finset.mem_image, refine ⟨⟨c, p⟩, _⟩, finish, },
  { finish, },
end

lemma A_fibre_over_judge_pair_card {p : judge_pair J} (h : p.distinct) :
  (agreed_contestants r p).card =
  ((A r).filter(λ (a : agreed_triple C J), a.judge_pair = p)).card :=
by { rw A_fibre_over_judge_pair r h, apply finset.card_image_of_inj_on, tidy, }

lemma A_card_upper_bound
  {k : ℕ} (hk : ∀ (p : judge_pair J), p.distinct → (agreed_contestants r p).card ≤ k) :
  (A r).card ≤ k * ((fintype.card J) * (fintype.card J) - (fintype.card J)) :=
begin
  change _ ≤ k * ((finset.card _ ) * (finset.card _ ) - (finset.card _ )),
  rw ← finset.off_diag_card,
  apply finset.card_le_mul_card_image_of_maps_to (A_maps_to_off_diag_judge_pair r),
  intros p hp,
  have hp' : p.distinct, { simp [finset.mem_off_diag] at hp, exact hp, },
  rw ← A_fibre_over_judge_pair_card r hp', apply hk, exact hp',
end

end

lemma add_sq_add_sq_sub {α : Type*} [ring α] (x y : α) :
  (x + y) * (x + y) + (x - y) * (x - y) = 2*x*x + 2*y*y :=
by noncomm_ring

lemma norm_bound_of_odd_sum {x y z : ℤ} (h : x + y = 2*z + 1) :
  2*z*z + 2*z + 1 ≤ x*x + y*y :=
begin
  suffices : 4*z*z + 4*z + 1 + 1 ≤ 2*x*x + 2*y*y,
  { rw ← mul_le_mul_left (zero_lt_two' ℤ), convert this; ring, },
  have h' : (x + y) * (x + y) = 4*z*z + 4*z + 1, { rw h, ring, },
  rw [← add_sq_add_sq_sub, h', add_le_add_iff_left],
  suffices : 0 < (x - y) * (x - y), { apply int.add_one_le_of_lt this, },
  rw [mul_self_pos, sub_ne_zero], apply int.ne_of_odd_add ⟨z, h⟩,
end

section

variables [fintype J]

lemma judge_pairs_card_lower_bound {z : ℕ} (hJ : fintype.card J = 2*z + 1) (c : C) :
  2*z*z + 2*z + 1 ≤ (finset.univ.filter (λ (p : judge_pair J), p.agree r c)).card :=
begin
  let x := (finset.univ.filter (λ j, r c j)).card,
  let y := (finset.univ.filter (λ j, ¬ r c j)).card,
  have h : (finset.univ.filter (λ (p : judge_pair J), p.agree r c)).card = x*x + y*y,
  { simp [← finset.filter_product_card], },
  rw h, apply int.le_of_coe_nat_le_coe_nat, simp only [int.coe_nat_add, int.coe_nat_mul],
  apply norm_bound_of_odd_sum,
  suffices : x + y = 2*z + 1, { simp [← int.coe_nat_add, this], },
  rw [finset.filter_card_add_filter_neg_card_eq_card, ← hJ], refl,
end

lemma distinct_judge_pairs_card_lower_bound {z : ℕ} (hJ : fintype.card J = 2*z + 1) (c : C) :
  2*z*z ≤ (finset.univ.filter (λ (p : judge_pair J), p.agree r c ∧ p.distinct)).card :=
begin
  let s := finset.univ.filter (λ (p : judge_pair J), p.agree r c),
  let t := finset.univ.filter (λ (p : judge_pair J), p.distinct),
  have hs : 2*z*z + 2*z + 1 ≤ s.card, { exact judge_pairs_card_lower_bound r hJ c, },
  have hst : s \ t = finset.univ.diag,
  { ext p, split; intros,
    { finish, },
    { suffices : p.judge₁ = p.judge₂, { simp [this], }, finish, }, },
  have hst' : (s \ t).card = 2*z + 1, { rw [hst, finset.diag_card, ← hJ], refl, },
  rw [finset.filter_and, ← finset.sdiff_sdiff_self_left s t, finset.card_sdiff],
  { rw hst', rw add_assoc at hs, apply le_tsub_of_add_le_right hs, },
  { apply finset.sdiff_subset, },
end

lemma A_card_lower_bound [fintype C] {z : ℕ} (hJ : fintype.card J = 2*z + 1) :
  2*z*z * (fintype.card C) ≤ (A r).card :=
begin
  have h : ∀ a, a ∈ A r → prod.fst a ∈ @finset.univ C _, { intros, apply finset.mem_univ, },
  apply finset.mul_card_image_le_card_of_maps_to h,
  intros c hc,
  rw ← A_fibre_over_contestant_card,
  apply distinct_judge_pairs_card_lower_bound r hJ,
end

end

lemma clear_denominators {a b k : ℕ} (ha : 0 < a) (hb : 0 < b) :
  (b - 1 : ℚ) / (2 * b) ≤ k / a ↔ (b - 1) * a ≤ k * (2 * b) :=
by rw div_le_div_iff; norm_cast; simp [ha, hb]

theorem imo1998_q2 [fintype J] [fintype C]
  (a b k : ℕ) (hC : fintype.card C = a) (hJ : fintype.card J = b) (ha : 0 < a) (hb : odd b)
  (hk : ∀ (p : judge_pair J), p.distinct → (agreed_contestants r p).card ≤ k) :
  (b - 1 : ℚ) / (2 * b) ≤ k / a :=
begin
  rw clear_denominators ha hb.pos,
  obtain ⟨z, hz⟩ := hb, rw hz at hJ, rw hz,
  have h := le_trans (A_card_lower_bound r hJ) (A_card_upper_bound r hk),
  rw [hC, hJ] at h,
  -- We are now essentially done; we just need to bash `h` into exactly the right shape.
  have hl : k * ((2 * z + 1) * (2 * z + 1) - (2 * z + 1)) = (k * (2 * (2 * z + 1))) * z,
  { simp only [add_mul, two_mul, mul_comm, mul_assoc], finish, },
  have hr : 2 * z * z * a = 2 * z * a * z, { ring, },
  rw [hl, hr] at h,
  cases z,
  { simp, },
  { exact le_of_mul_le_mul_right h z.succ_pos, },
end


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

import data.polynomial.eval
import ring_theory.ideal.quotient

/-!
# modular equivalence for submodule
-/

open submodule
open_locale polynomial

variables {R : Type*} [ring R]
variables {M : Type*} [add_comm_group M] [module R M] (U U₁ U₂ : submodule R M)
variables {x x₁ x₂ y y₁ y₂ z z₁ z₂ : M}
variables {N : Type*} [add_comm_group N] [module R N] (V V₁ V₂ : submodule R N)

/-- A predicate saying two elements of a module are equivalent modulo a submodule. -/
def smodeq (x y : M) : Prop :=
(submodule.quotient.mk x : M ⧸ U) = submodule.quotient.mk y

notation x ` ≡ `:50 y ` [SMOD `:50 N `]`:0 := smodeq N x y

variables {U U₁ U₂}

protected lemma smodeq.def : x ≡ y [SMOD U] ↔
  (submodule.quotient.mk x : M ⧸ U) = submodule.quotient.mk y := iff.rfl

namespace smodeq

lemma sub_mem : x ≡ y [SMOD U] ↔ x - y ∈ U :=
by rw [smodeq.def, submodule.quotient.eq]

@[simp] theorem top : x ≡ y [SMOD (⊤ : submodule R M)] :=
(submodule.quotient.eq ⊤).2 mem_top

@[simp] theorem bot : x ≡ y [SMOD (⊥ : submodule R M)] ↔ x = y :=
by rw [smodeq.def, submodule.quotient.eq, mem_bot, sub_eq_zero]

@[mono] theorem mono (HU : U₁ ≤ U₂) (hxy : x ≡ y [SMOD U₁]) : x ≡ y [SMOD U₂] :=
(submodule.quotient.eq U₂).2 $ HU $ (submodule.quotient.eq U₁).1 hxy

@[refl] theorem refl : x ≡ x [SMOD U] := eq.refl _

@[symm] theorem symm (hxy : x ≡ y [SMOD U]) : y ≡ x [SMOD U] := hxy.symm

@[trans] theorem trans (hxy : x ≡ y [SMOD U]) (hyz : y ≡ z [SMOD U]) : x ≡ z [SMOD U] :=
hxy.trans hyz

theorem add (hxy₁ : x₁ ≡ y₁ [SMOD U]) (hxy₂ : x₂ ≡ y₂ [SMOD U]) : x₁ + x₂ ≡ y₁ + y₂ [SMOD U] :=
by { rw smodeq.def at hxy₁ hxy₂ ⊢, simp_rw [quotient.mk_add, hxy₁, hxy₂] }

theorem smul (hxy : x ≡ y [SMOD U]) (c : R) : c • x ≡ c • y [SMOD U] :=
by { rw smodeq.def at hxy ⊢, simp_rw [quotient.mk_smul, hxy] }

theorem zero : x ≡ 0 [SMOD U] ↔ x ∈ U :=
by rw [smodeq.def, submodule.quotient.eq, sub_zero]

theorem map (hxy : x ≡ y [SMOD U]) (f : M →ₗ[R] N) : f x ≡ f y [SMOD U.map f] :=
(submodule.quotient.eq _).2 $ f.map_sub x y ▸ mem_map_of_mem $ (submodule.quotient.eq _).1 hxy

theorem comap {f : M →ₗ[R] N} (hxy : f x ≡ f y [SMOD V]) : x ≡ y [SMOD V.comap f] :=
(submodule.quotient.eq _).2 $ show f (x - y) ∈ V,
from (f.map_sub x y).symm ▸ (submodule.quotient.eq _).1 hxy

lemma eval {R : Type*} [comm_ring R] {I : ideal R} {x y : R} (h : x ≡ y [SMOD I])
  (f : R[X]) : f.eval x ≡ f.eval y [SMOD I] :=
begin
  rw [smodeq.def] at h ⊢,
  show ideal.quotient.mk I (f.eval x) = ideal.quotient.mk I (f.eval y),
  change ideal.quotient.mk I x = ideal.quotient.mk I y at h,
  rw [← polynomial.eval₂_at_apply, ← polynomial.eval₂_at_apply, h],
end

end smodeq


/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
import algebra.order
import order.lattice

/-!
# `max` and `min`

This file proves basic properties about maxima and minima on a `linear_order`.

## Tags

min, max
-/

universes u v
variables {α : Type u} {β : Type v}

attribute [simp] max_eq_left max_eq_right min_eq_left min_eq_right

section
variables [linear_order α] [linear_order β] {f : α → β} {a b c d : α}

-- translate from lattices to linear orders (sup → max, inf → min)
@[simp] lemma le_min_iff : c ≤ min a b ↔ c ≤ a ∧ c ≤ b := le_inf_iff
@[simp] lemma max_le_iff : max a b ≤ c ↔ a ≤ c ∧ b ≤ c := sup_le_iff
lemma max_le_max : a ≤ c → b ≤ d → max a b ≤ max c d := sup_le_sup
lemma min_le_min : a ≤ c → b ≤ d → min a b ≤ min c d := inf_le_inf
lemma le_max_left_of_le : a ≤ b → a ≤ max b c := le_sup_left_of_le
lemma le_max_right_of_le : a ≤ c → a ≤ max b c := le_sup_right_of_le
lemma min_le_left_of_le : a ≤ c → min a b ≤ c := inf_le_left_of_le
lemma min_le_right_of_le : b ≤ c → min a b ≤ c := inf_le_right_of_le
lemma max_min_distrib_left : max a (min b c) = min (max a b) (max a c) := sup_inf_left
lemma max_min_distrib_right : max (min a b) c = min (max a c) (max b c) := sup_inf_right
lemma min_max_distrib_left : min a (max b c) = max (min a b) (min a c) := inf_sup_left
lemma min_max_distrib_right : min (max a b) c = max (min a c) (min b c) := inf_sup_right
lemma min_le_max : min a b ≤ max a b := le_trans (min_le_left a b) (le_max_left a b)

@[simp] lemma min_eq_left_iff : min a b = a ↔ a ≤ b := inf_eq_left
@[simp] lemma min_eq_right_iff : min a b = b ↔ b ≤ a := inf_eq_right
@[simp] lemma max_eq_left_iff : max a b = a ↔ b ≤ a := sup_eq_left
@[simp] lemma max_eq_right_iff : max a b = b ↔ a ≤ b := sup_eq_right

/-- An instance asserting that `max a a = a` -/
instance max_idem : is_idempotent α max := by apply_instance -- short-circuit type class inference

/-- An instance asserting that `min a a = a` -/
instance min_idem : is_idempotent α min := by apply_instance -- short-circuit type class inference

@[simp] lemma max_lt_iff : max a b < c ↔ (a < c ∧ b < c) :=
sup_lt_iff

@[simp] lemma lt_min_iff : a < min b c ↔ (a < b ∧ a < c) :=
lt_inf_iff

@[simp] lemma lt_max_iff : a < max b c ↔ a < b ∨ a < c :=
lt_sup_iff

@[simp] lemma min_lt_iff : min a b < c ↔ a < c ∨ b < c :=
@lt_max_iff (order_dual α) _ _ _ _

@[simp] lemma min_le_iff : min a b ≤ c ↔ a ≤ c ∨ b ≤ c :=
inf_le_iff

@[simp] lemma le_max_iff : a ≤ max b c ↔ a ≤ b ∨ a ≤ c :=
@min_le_iff (order_dual α) _ _ _ _

lemma max_lt_max (h₁ : a < c) (h₂ : b < d) : max a b < max c d :=
by simp [lt_max_iff, max_lt_iff, *]

lemma min_lt_min (h₁ : a < c) (h₂ : b < d) : min a b < min c d :=
@max_lt_max (order_dual α) _ _ _ _ _ h₁ h₂

theorem min_right_comm (a b c : α) : min (min a b) c = min (min a c) b :=
right_comm min min_comm min_assoc a b c

theorem max.left_comm (a b c : α) : max a (max b c) = max b (max a c) :=
left_comm max max_comm max_assoc a b c

theorem max.right_comm (a b c : α) : max (max a b) c = max (max a c) b :=
right_comm max max_comm max_assoc a b c

lemma monotone.map_max (hf : monotone f) : f (max a b) = max (f a) (f b) :=
by cases le_total a b; simp [h, hf h]

lemma monotone.map_min (hf : monotone f) : f (min a b) = min (f a) (f b) :=
hf.order_dual.map_max

theorem min_choice (a b : α) : min a b = a ∨ min a b = b :=
by cases le_total a b; simp *

theorem max_choice (a b : α) : max a b = a ∨ max a b = b :=
@min_choice (order_dual α) _ a b

lemma le_of_max_le_left {a b c : α} (h : max a b ≤ c) : a ≤ c :=
le_trans (le_max_left _ _) h

lemma le_of_max_le_right {a b c : α} (h : max a b ≤ c) : b ≤ c :=
le_trans (le_max_right _ _) h

end


-- Reglas de la negación
-- =====================

import tactic

variables (P Q : Prop)

-- Eliminación del falso
-- ---------------------

-- ----------------------------------------------------
-- Ej. 1. (p. 14) Demostrar que
--    ⊥ ⊢ P
-- ----------------------------------------------------

-- 1ª demostración
example
  (h : false)
  : Q :=
false.elim h

-- 2ª demostración
example
  (h : false)
  : Q :=
-- by library_search
false.rec Q h

-- 3ª demostración
example
  (h : false)
  : Q :=
begin
  exfalso,
  exact h,
end

-- 4ª demostración
example
  (h : false)
  : P :=
-- by hint
by tauto

-- 5ª demostración
example
  (h : false)
  : P :=
by cases h

-- 6ª demostración
example
  (h : false)
  : P :=
by finish

-- 7ª demostración
example
  (h : false)
  : P :=
by solve_by_elim

-- Definición de la negación
-- -------------------------

-- #reduce ¬P
-- ¬P ≡ (P → false)

-- Eliminación de la negación
-- --------------------------

-- ----------------------------------------------------
-- Ej. 2. Demostrar que
--    P, ¬P ⊢ ⊥
-- ----------------------------------------------------

-- 1ª demostración
example
  (h1: P)
  (h2: ¬P)
  : false :=
not.elim h2 h1

-- 2ª demostración
example
  (h1: P)
  (h2: ¬P)
  : false :=
begin
  apply h2,
  exact h1,
end

-- 3ª demostración
example
  (h1: P)
  (h2: ¬P)
  : false :=
absurd h1 h2

-- 4ª demostración
example
  (h1: P)
  (h2: ¬P)
  : false :=
-- by library_search
h2 h1

-- Introducción de la negación
-- ---------------------------

-- ----------------------------------------------------
-- Ej. 3. Demostrar
--    ¬(P ∧ ¬P)
-- ----------------------------------------------------

-- 1ª demostración
example : ¬(P ∧ ¬P) :=
not.intro
  ( assume h : P ∧ ¬P,
    have h1 : P  := h.1,
    have h2 : ¬P := h.2,
    show false, from h2 h1 )

-- 2ª demostración
example : ¬(P ∧ ¬P) :=
not.intro
  ( assume h : P ∧ ¬P,
    show false, from h.2 h.1 )

-- 3ª demostración
example : ¬(P ∧ ¬P) :=
not.intro
  ( assume h : P ∧ ¬P, h.2 h.1 )

-- 4ª demostración
example : ¬(P ∧ ¬P) :=
not.intro (λ h, h.2 h.1)

-- 5ª demostración
example : ¬(P ∧ ¬P) :=
begin
  intro h,
  cases h with h1 h2,
  apply h2,
  exact h1,
end

-- 6ª demostración
example : ¬(P ∧ ¬P) :=
begin
  rintro ⟨h1, h2⟩,
  exact h2 h1,
end

-- 7ª demostración
example : ¬(P ∧ ¬P) :=
λ ⟨h1, h2⟩, h2 h1

-- 8ª demostración
example : ¬(P ∧ ¬P) :=
-- by suggest
(and_not_self P).mp

-- 9ª demostración
example : ¬(P ∧ ¬P) :=
-- by hint
by tauto

-- 10ª demostración
example : ¬(P ∧ ¬P) :=
by finish

-- 11ª demostración
example : ¬(P ∧ ¬P) :=
by simp


/-
Copyright (c) 2021 Johan Commelin. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johan Commelin
-/
import algebra.algebra.basic
import topology.locally_constant.basic

/-!
# Algebraic structure on locally constant functions

This file puts algebraic structure (`add_group`, etc)
on the type of locally constant functions.

-/

namespace locally_constant

variables {X Y : Type*} [topological_space X]

@[to_additive] instance [has_one Y] : has_one (locally_constant X Y) :=
{ one := const X 1 }

@[simp, to_additive] lemma coe_one [has_one Y] : ⇑(1 : locally_constant X Y) = (1 : X → Y) := rfl

@[to_additive] lemma one_apply [has_one Y] (x : X) : (1 : locally_constant X Y) x = 1 := rfl

@[to_additive] instance [has_inv Y] : has_inv (locally_constant X Y) :=
{ inv := λ f, ⟨f⁻¹ , f.is_locally_constant.inv⟩ }

@[simp, to_additive] lemma coe_inv [has_inv Y] (f : locally_constant X Y) : ⇑(f⁻¹) = f⁻¹ := rfl

@[to_additive] lemma inv_apply [has_inv Y] (f : locally_constant X Y) (x : X) :
  f⁻¹ x = (f x)⁻¹ := rfl

@[to_additive] instance [has_mul Y] : has_mul (locally_constant X Y) :=
{ mul := λ f g, ⟨f * g, f.is_locally_constant.mul g.is_locally_constant⟩ }

@[simp, to_additive] lemma coe_mul [has_mul Y] (f g : locally_constant X Y) :
  ⇑(f * g) = f * g :=
rfl

@[to_additive] lemma mul_apply [has_mul Y] (f g : locally_constant X Y) (x : X) :
  (f * g) x = f x * g x := rfl

@[to_additive] instance [mul_one_class Y] : mul_one_class (locally_constant X Y) :=
{ one_mul := by { intros, ext, simp only [mul_apply, one_apply, one_mul] },
  mul_one := by { intros, ext, simp only [mul_apply, one_apply, mul_one] },
  .. locally_constant.has_one,
  .. locally_constant.has_mul }

/-- `coe_fn` is a `monoid_hom`. -/
@[to_additive "`coe_fn` is an `add_monoid_hom`.", simps]
def coe_fn_monoid_hom [mul_one_class Y] : locally_constant X Y →* (X → Y) :=
{ to_fun   := coe_fn,
  map_one' := rfl,
  map_mul' := λ _ _, rfl }

/-- The constant-function embedding, as a multiplicative monoid hom. -/
@[to_additive "The constant-function embedding, as an additive monoid hom.", simps]
def const_monoid_hom [mul_one_class Y] : Y →* locally_constant X Y :=
{ to_fun   := const X,
  map_one' := rfl,
  map_mul' := λ _ _, rfl, }

instance [mul_zero_class Y] : mul_zero_class (locally_constant X Y) :=
{ zero_mul := by { intros, ext, simp only [mul_apply, zero_apply, zero_mul] },
  mul_zero := by { intros, ext, simp only [mul_apply, zero_apply, mul_zero] },
  .. locally_constant.has_zero,
  .. locally_constant.has_mul }

instance [mul_zero_one_class Y] : mul_zero_one_class (locally_constant X Y) :=
{ .. locally_constant.mul_zero_class, .. locally_constant.mul_one_class }

section char_fn

variables (Y) [mul_zero_one_class Y] {U V : set X}

/-- Characteristic functions are locally constant functions taking `x : X` to `1` if `x ∈ U`,
  where `U` is a clopen set, and `0` otherwise. -/
noncomputable def char_fn (hU : is_clopen U) : locally_constant X Y := indicator 1 hU

lemma coe_char_fn (hU : is_clopen U) : (char_fn Y hU : X → Y) = set.indicator U 1 :=
rfl

lemma char_fn_eq_one [nontrivial Y] (x : X) (hU : is_clopen U) :
  char_fn Y hU x = (1 : Y) ↔ x ∈ U := set.indicator_eq_one_iff_mem _

lemma char_fn_eq_zero [nontrivial Y] (x : X) (hU : is_clopen U) :
  char_fn Y hU x = (0 : Y) ↔ x ∉ U := set.indicator_eq_zero_iff_not_mem _

lemma char_fn_inj [nontrivial Y] (hU : is_clopen U) (hV : is_clopen V)
  (h : char_fn Y hU = char_fn Y hV) : U = V :=
set.indicator_one_inj Y $ coe_inj.mpr h

end char_fn

@[to_additive] instance [has_div Y] : has_div (locally_constant X Y) :=
{ div := λ f g, ⟨f / g, f.is_locally_constant.div g.is_locally_constant⟩ }

@[to_additive] lemma coe_div [has_div Y] (f g : locally_constant X Y) :
  ⇑(f / g) = f / g := rfl

@[to_additive] lemma div_apply [has_div Y] (f g : locally_constant X Y) (x : X) :
  (f / g) x = f x / g x := rfl

@[to_additive] instance [semigroup Y] : semigroup (locally_constant X Y) :=
{ mul_assoc := by { intros, ext, simp only [mul_apply, mul_assoc] },
  .. locally_constant.has_mul }

instance [semigroup_with_zero Y] : semigroup_with_zero (locally_constant X Y) :=
{ .. locally_constant.mul_zero_class,
  .. locally_constant.semigroup }

@[to_additive] instance [comm_semigroup Y] : comm_semigroup (locally_constant X Y) :=
{ mul_comm := by { intros, ext, simp only [mul_apply, mul_comm] },
  .. locally_constant.semigroup }

@[to_additive] instance [monoid Y] : monoid (locally_constant X Y) :=
{ mul := (*),
  .. locally_constant.semigroup, .. locally_constant.mul_one_class }

instance [add_monoid_with_one Y] : add_monoid_with_one (locally_constant X Y) :=
{ nat_cast := λ n, const X n,
  nat_cast_zero := by ext; simp [nat.cast],
  nat_cast_succ := λ _, by ext; simp [nat.cast],
  .. locally_constant.add_monoid, .. locally_constant.has_one }

@[to_additive] instance [comm_monoid Y] : comm_monoid (locally_constant X Y) :=
{ .. locally_constant.comm_semigroup, .. locally_constant.monoid }

@[to_additive] instance [group Y] : group (locally_constant X Y) :=
{ mul_left_inv := by { intros, ext, simp only [mul_apply, inv_apply, one_apply, mul_left_inv] },
  div_eq_mul_inv := by { intros, ext, simp only [mul_apply, inv_apply, div_apply, div_eq_mul_inv] },
  .. locally_constant.monoid, .. locally_constant.has_inv, .. locally_constant.has_div }

@[to_additive] instance [comm_group Y] : comm_group (locally_constant X Y) :=
{ .. locally_constant.comm_monoid, .. locally_constant.group }

instance [distrib Y] : distrib (locally_constant X Y) :=
{ left_distrib := by { intros, ext, simp only [mul_apply, add_apply, mul_add] },
  right_distrib := by { intros, ext, simp only [mul_apply, add_apply, add_mul] },
  .. locally_constant.has_add, .. locally_constant.has_mul }

instance [non_unital_non_assoc_semiring Y] : non_unital_non_assoc_semiring (locally_constant X Y) :=
{ .. locally_constant.add_comm_monoid, .. locally_constant.has_mul,
  .. locally_constant.distrib, .. locally_constant.mul_zero_class }

instance [non_unital_semiring Y] : non_unital_semiring (locally_constant X Y) :=
{ .. locally_constant.semigroup, .. locally_constant.non_unital_non_assoc_semiring }

instance [non_assoc_semiring Y] : non_assoc_semiring (locally_constant X Y) :=
{ .. locally_constant.mul_one_class, .. locally_constant.add_monoid_with_one,
  .. locally_constant.non_unital_non_assoc_semiring }

/-- The constant-function embedding, as a ring hom.  -/
@[simps] def const_ring_hom [non_assoc_semiring Y] : Y →+* locally_constant X Y :=
{ to_fun    := const X,
  .. const_monoid_hom,
  .. const_add_monoid_hom, }

instance [semiring Y] : semiring (locally_constant X Y) :=
{ .. locally_constant.non_assoc_semiring, .. locally_constant.monoid }

instance [non_unital_comm_semiring Y] : non_unital_comm_semiring (locally_constant X Y) :=
{ .. locally_constant.non_unital_semiring, .. locally_constant.comm_semigroup }

instance [comm_semiring Y] : comm_semiring (locally_constant X Y) :=
{ .. locally_constant.semiring, .. locally_constant.comm_monoid }

instance [non_unital_non_assoc_ring Y] : non_unital_non_assoc_ring (locally_constant X Y) :=
{ .. locally_constant.add_comm_group, .. locally_constant.has_mul,
  .. locally_constant.distrib, .. locally_constant.mul_zero_class }

instance [non_unital_ring Y] : non_unital_ring (locally_constant X Y) :=
{ .. locally_constant.semigroup, .. locally_constant.non_unital_non_assoc_ring }

instance [non_assoc_ring Y] : non_assoc_ring (locally_constant X Y) :=
{ .. locally_constant.mul_one_class, .. locally_constant.non_unital_non_assoc_ring }

instance [ring Y] : ring (locally_constant X Y) :=
{ .. locally_constant.semiring, .. locally_constant.add_comm_group }

instance [non_unital_comm_ring Y] : non_unital_comm_ring (locally_constant X Y) :=
{ .. locally_constant.non_unital_comm_semiring, .. locally_constant.non_unital_ring }

instance [comm_ring Y] : comm_ring (locally_constant X Y) :=
{ .. locally_constant.comm_semiring, .. locally_constant.ring }

variables {R : Type*}

instance [has_smul R Y] : has_smul R (locally_constant X Y) :=
{ smul := λ r f,
  { to_fun := r • f,
    is_locally_constant := ((is_locally_constant f).comp ((•) r) : _), } }

@[simp] lemma coe_smul [has_smul R Y] (r : R) (f : locally_constant X Y) : ⇑(r • f) = r • f := rfl

lemma smul_apply [has_smul R Y] (r : R) (f : locally_constant X Y) (x : X) :
  (r • f) x = r • (f x) :=
rfl

instance [monoid R] [mul_action R Y] : mul_action R (locally_constant X Y) :=
function.injective.mul_action _ coe_injective (λ _ _, rfl)

instance [monoid R] [add_monoid Y] [distrib_mul_action R Y] :
  distrib_mul_action R (locally_constant X Y) :=
function.injective.distrib_mul_action coe_fn_add_monoid_hom coe_injective (λ _ _, rfl)

instance [semiring R] [add_comm_monoid Y] [module R Y] : module R (locally_constant X Y) :=
function.injective.module R coe_fn_add_monoid_hom coe_injective (λ _ _, rfl)

section algebra

variables [comm_semiring R] [semiring Y] [algebra R Y]

instance : algebra R (locally_constant X Y) :=
{ to_ring_hom := const_ring_hom.comp $ algebra_map R Y,
  commutes'   := by { intros, ext, exact algebra.commutes' _ _, },
  smul_def'   := by { intros, ext, exact algebra.smul_def' _ _, }, }

@[simp] lemma coe_algebra_map (r : R) :
  ⇑(algebra_map R (locally_constant X Y) r) = algebra_map R (X → Y) r :=
rfl

end algebra

end locally_constant


-- Los_limites_son_menores_o_iguales_que_las_cotas_superiores.lean
-- Los límites son menores o iguales que las cotas superiores
-- José A. Alonso Jiménez
-- Sevilla, 16 de septiembre de 2021
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- En Lean, se puede definir que a es el límite de la sucesión u por
--    def limite (u : ℕ → ℝ) (a : ℝ) :=
--      ∀ ε > 0, ∃ k, ∀ n ≥ k, |u n - a| < ε
-- y que a es una cota superior de  u por
--    def cota_superior (u : ℕ → ℝ) (a : ℝ) :=
--      ∀ n, u n ≤ a
--
-- Demostrar que si x es el límite de la sucesión u e y es una cota
-- superior de u, entonces x ≤ y.
-- ---------------------------------------------------------------------

import data.real.basic

variable  (u : ℕ → ℝ)
variables (x y : ℝ)

notation `|`x`|` := abs x

def limite (u : ℕ → ℝ) (a : ℝ) :=
  ∀ ε > 0, ∃ k, ∀ n ≥ k, |u n - a| < ε

def cota_superior (u : ℕ → ℝ) (a : ℝ) :=
  ∀ n, u n ≤ a

lemma aux :
  (∀ ε > 0, y ≤ x + ε) → y ≤ x :=
begin
  contrapose!,
  intro h,
  use (y-x)/2,
  split ; linarith,
end

-- 1º demostración
example
  (hx : limite u x)
  (hy : cota_superior u y)
  : x ≤ y :=
begin
  apply aux,
  intros ε hε,
  cases hx ε hε with k hk,
  specialize hk k rfl.ge,
  replace hk : -ε < u k - x := neg_lt_of_abs_lt hk,
  replace hk : x < u k + ε := neg_lt_sub_iff_lt_add'.mp hk,
  apply le_of_lt,
  exact lt_add_of_lt_add_right hk (hy k),
end

-- 2º demostración
example
  (hx : limite u x)
  (hy : cota_superior u y)
  : x ≤ y :=
begin
  apply aux,
  intros ε hε,
  cases hx ε hε with k hk,
  specialize hk k rfl.ge,
  apply le_of_lt,
  calc x < u k + ε : neg_lt_sub_iff_lt_add'.mp (neg_lt_of_abs_lt hk)
     ... ≤ y + ε   : add_le_add_right (hy k) ε,
end

-- 3º demostración
example
  (hx : limite u x)
  (hy : cota_superior u y)
  : x ≤ y :=
begin
  apply aux,
  intros ε hε,
  cases hx ε hε with k hk,
  specialize hk k (by linarith),
  rw abs_lt at hk,
  linarith [hy k],
end


import algebra.group.basic
import tactic
import group_theory.subgroup.basic
import data.set_like.basic
import chapter2.set_theory_cosets
import chapter2.set_theory_cosets_continued_w_semigroups
import chapter2.set_theory_cosets_continued_w_monoids
import chapter2.set_theory_cosets_continued_w_groups

variables {A:Type} [group A] {G:subgroup A}

lemma lcoset_eq_self_of_mem {a:A} (Ha: a ∈ G) : lcoset (A) (a) (G) = G :=
begin
unfold lcoset, ext x, split,
{intro hx, cases hx with x2 hx2, dsimp at hx2, cases hx2 with hx3 hx4,
have hx5 : a*x2 ∈ G,
exact G.mul_mem Ha hx3, rw hx4 at hx5, exact hx5,},
{intro hx, simp,
have h2 : a⁻¹ ∈ G,
exact G.inv_mem Ha,
have h3 : a⁻¹ * x ∈ G,
exact G.mul_mem h2 hx, exact h3,}
end

--cool because it requires the subgroup to be closed under multiplication which is obviously part of the subgroups definition.

lemma rcoset_eq_self_of_mem {a:A} (Ha: a ∈ G) : rcoset (A) (G) (a) = G :=
begin
unfold rcoset, ext x, split,
{intro hx, simp at hx,
have h2: x*a⁻¹*a ∈ G, exact G.mul_mem hx Ha,
simp at h2, exact h2,},
{intro hx, simp,
have h2: a⁻¹ ∈ G, exact G.inv_mem Ha,
have h3: x* a⁻¹ ∈ G, exact G.mul_mem hx h2, exact h3,}
end

lemma mem_lcoset_self (a:A) : a ∈ lcoset (A) (a) (G) :=
begin
unfold lcoset, use 1, split,
{simp, exact G.one_mem, },
{simp, }
end

lemma mem_rcoset_self (a:A) : a ∈ rcoset (A) (G) (a) :=
begin
unfold rcoset, use 1, split,
{simp, exact G.one_mem, },
{simp, }
end

lemma lcoset_eq_self_iff {a:A} : lcoset (A) (a) (G) = G ↔ a ∈ G :=
begin
unfold lcoset, split,
{intro h, have h2 : a ∈ lcoset (A) (a) (G), exact mem_lcoset_self a, unfold lcoset at h2,
rw h at h2, exact h2,},
{intro h, exact lcoset_eq_self_of_mem h,}
end

lemma rcoset_eq_self_iff {a:A} : rcoset (A) (G) (a) = G ↔ a ∈ G :=
begin
split,
{intro h, have h2 : a ∈ rcoset (A) (G) (a), exact mem_rcoset_self a, rw h at h2, exact h2,},
{intro h, exact rcoset_eq_self_of_mem h,}
end

lemma lcoset_eq_lcoset (a b : A) (Hab : b⁻¹ * a ∈ G) : lcoset (A) (b) (G) = lcoset (A) (a) (G) :=
begin
have h1: lcoset (A) (b⁻¹ * a) (G) = G, exact lcoset_eq_self_of_mem Hab,
have h2: lcoset (A) (1) (G) = G, exact one_lcoset (G),
have h3: b⁻¹*b = 1, exact inv_mul_self b,
rw ← h3 at h2,
nth_rewrite 1 ← h1 at h2,
rw ← lcoset_lcoset at h2,
rw ← lcoset_lcoset at h2,
exact eq_of_lcoset_eq_lcoset _ _ _ h2, -- was feeling a bit lazy ;)
end

lemma inv_mul_mem_of_lcoset_eq_lcoset (a b : A) (Hab : lcoset (A) (b) (G) = lcoset (A) (a) (G)) : b⁻¹ * a ∈ G :=
begin
have h : lcoset (A) (1) (G) = G,
exact one_lcoset (G),
have h2 : b⁻¹*b = 1,
exact inv_mul_self b,
rw ← h2 at h,
rw ← lcoset_lcoset at h,
rw Hab at h,
rw lcoset_lcoset at h,
rw lcoset_eq_self_iff at h,
exact h,
end

lemma lcoset_eq_lcoset_iff (a b : A) : lcoset (A) (b) (G) = lcoset (A) (a) (G) ↔ b⁻¹*a ∈ G :=
begin
split,
{intro h, exact inv_mul_mem_of_lcoset_eq_lcoset a b h,},
{intro h, exact lcoset_eq_lcoset a b h,}
end

lemma rcoset_eq_rcoset {a b : A} (Hab : a * b⁻¹ ∈ G) : rcoset (A) (G) (a) = rcoset (A) (G) (b) :=
begin
have h : rcoset (A) (G) (a*b⁻¹) = G,
exact rcoset_eq_self_of_mem Hab,
have h2 : rcoset (A) (G) (1) = G,
exact rcoset_one (G),
have h3: b*b⁻¹ = 1,
exact mul_inv_self b,
rw ← h3 at h2,
nth_rewrite 1 ← h2 at h,
rw ← rcoset_rcoset at h,
rw ← rcoset_rcoset at h,
exact eq_of_rcoset_eq_rcoset _ _ _ h,
end

lemma mul_inv_mem_of_rcoset_eq_rcoset {a b : A} (H : rcoset (A) (G) (a) = rcoset (A) (G) (b)) : a * b⁻¹ ∈ G :=
begin
have h : rcoset (A) (G) (1) = G,
exact rcoset_one (G),
have h2 : b*b⁻¹ = 1,
exact mul_inv_self b,
rw ← h2 at h,
rw ← rcoset_rcoset at h,
rw ← H at h,
rw rcoset_rcoset at h,
rw rcoset_eq_self_iff at h,
exact h,
end

lemma rcoset_eq_lcoset_iff (a b : A) : rcoset (A) (G) (a) = rcoset (A) (G) (b) ↔ a*b⁻¹ ∈ G :=
begin
split,
{intro h, exact mul_inv_mem_of_rcoset_eq_rcoset h,},
{intro h, exact rcoset_eq_rcoset h,}
end


import data.real.basic

import analysis.mean_inequalities

open_locale big_operators

/-
Iranian Mathematical Olympiad 1998, problem 3

Let x₁, x₂, x₃, x₄ be positive real numbers such that

  x₁ ⬝ x₂ ⬝ x₃ ⬝ x₄ = 1.

Prove that
  x₁³ + x₂³ + x₃³ + x₄³ ≥ max(x₁ + x₂ + x₃ + x₄, 1/x₁ + 1/x₂ + 1/x₃ + 1/x₄).

-/

lemma cube_root_cube (x : ℝ) (h: 0 ≤ x): (x^(3:ℝ)) ^ ((1:ℝ)/3) = x :=
begin
  rw[←real.rpow_mul h, mul_div_cancel' (1:ℝ) three_ne_zero],
  exact real.rpow_one x,
end

/- seems like there should be something like this in mathlib... -/

lemma prod_pow' (S : finset ℕ) (e : ℝ) (f : ℕ → ℝ) (hf : ∀ s ∈ S, (0:ℝ) ≤ f s)  :
S.prod (λ (s : ℕ), f s ^ e) = S.prod (λ (s : ℕ), f s) ^ e :=
begin
  suffices : 0 ≤ S.prod (λ (s : ℕ), f s ) ∧
   S.prod (λ (s : ℕ), f s ^ e) = S.prod (λ (s : ℕ), f s) ^ e,
  { exact this.2 },
  induction S using finset.induction with s S' hs ih,
  { exact ⟨zero_le_one, (real.one_rpow e).symm⟩ },
  { rw[finset.prod_insert hs, finset.prod_insert hs],
    obtain ⟨hs0, hs⟩ := ih (λ s hs', hf s (finset.mem_insert_of_mem hs')),
    rw [hs],
    have hsnn := hf s (finset.mem_insert_self s S'),
    split,
    { rw [mul_comm],
      exact mul_nonneg hs0 hsnn },
    { exact (real.mul_rpow hsnn hs0).symm } }
end

theorem iran1998_q3
  (x : ℕ → ℝ)
  (x_positive : ∀ i, 0 < x i)
  (h : ∏ (i : ℕ) in finset.range 4, x i = 1)
  : max (∑(i : ℕ) in finset.range 4, x i) (∑(i : ℕ) in finset.range 4, 1 / x i)
     ≤ ∑ (i : ℕ) in finset.range 4, (x i)^(3 : ℝ) :=
begin
  rw[max_le_iff],
  split,
  { have amgm' := real.geom_mean_le_arith_mean_weighted
                    (finset.range 4)
                    (λ ii, (1:ℝ)/4)
                    (λ ii, x ii)
                    (by {intros i hi, norm_num})
                    (by simp)
                    (by {intros j hj, exact le_of_lt (x_positive j) }),
    have xnonneg : ∀ i ∈ finset.range 4, 0 ≤ x i,
    { intros i _, exact le_of_lt (x_positive i)},
    rw[prod_pow' (finset.range 4) (1/4) x xnonneg, h, real.one_rpow] at amgm',
    dsimp at amgm',
    rw [←finset.mul_sum] at amgm',

    let C := 1/4 * ∑ (i : ℕ) in finset.range 4, x i,
    have hcp' : 0 ≤ ∑ (i : ℕ) in finset.range 4, x i := finset.sum_nonneg xnonneg,
    have hcp : 0 ≤ C := mul_nonneg (by norm_num) hcp',
    have hccp : 0 ≤ C * C := mul_nonneg hcp hcp,

    have hCC : C * C * C = C ^(3:ℕ) := by ring,
    rw[←real.rpow_nat_cast] at hCC,
    simp only [nat.cast_bit1, nat.cast_one] at hCC,

    have hC := calc C
                ≤ C * C : le_mul_of_one_le_left hcp amgm'
            ... ≤ C * C * C : le_mul_of_one_le_right hccp amgm'
            ... = C^(3 : ℝ) : hCC,

    have h13 : (1:ℝ) ≤ 3 := by norm_num,
    have holder := real.rpow_sum_le_const_mul_sum_rpow (finset.range 4) x h13,

    have habs : ∀ i ∈ finset.range 4, |x i| = x i,
    {intros i hi, exact abs_of_pos (x_positive i)},
    rw[finset.sum_congr rfl habs] at holder,

    have habs3 : ∀ i ∈ finset.range 4, |x i| ^ (3:ℝ) = x i ^ (3:ℝ),
    { intros i hi, have := habs i hi, exact congr_fun (congr_arg pow this) 3},
    rw[finset.sum_congr rfl habs3] at holder,
    have hccc: (4:ℝ) * C =  ∑ (i : ℕ) in finset.range 4, x i := by {field_simp[C], ring},
    rw[←hccc] at holder,

    rw[real.mul_rpow zero_le_four hcp] at holder,
    have h43nn : (0:ℝ) ≤ 4 ^ (3:ℝ) := by norm_num,
    rw[finset.card_range 4] at holder,
    have hss: C ^ (3:ℝ) ≤ ((1:ℝ) / 4) * ∑ (i : ℕ) in finset.range 4, x i ^ (3:ℝ),
    { have h32: (3:ℝ) - 1 = 2 := by norm_num,
      rw[h32] at holder,
      clear_except holder,
      have hknn : (0:ℝ) ≤ (4:ℝ) ^ (-3 : ℝ) := by norm_num,
      have hh := mul_le_mul_of_nonneg_left holder hknn,
      rw[←mul_assoc] at hh,
      have h4mm: (4:ℝ) ^ (-3: ℝ) * (4:ℝ) ^ (3:ℝ) = 1 := by norm_num,
      rw[h4mm, one_mul, ←mul_assoc] at hh,
      have h4mm': (4:ℝ) ^ (-3: ℝ) * ((4:ℕ):ℝ) ^ (2:ℝ) = 1/4 := by norm_num,
      rw[h4mm'] at hh,
      exact hh },
    have htrans := le_trans hC hss,
    have hm4 : 4 * C ≤ 4 * ((1/4) * ∑ (i : ℕ) in finset.range 4, x i ^ (3:ℝ)) :=
      mul_le_mul_of_nonneg_left htrans zero_le_four,
    have h4c: 4 * C = ∑ (i : ℕ) in finset.range 4, x i,
    { field_simp[C], ring },
    rw[h4c] at hm4,
    have hro : 4 * (1 / 4 * ∑ (i : ℕ) in finset.range 4, x i ^ (3:ℝ)) =
                    ∑ (i : ℕ) in finset.range 4, x i ^ (3:ℝ),
    { field_simp, ring },
    rw[hro] at hm4,
    exact hm4 },
  { let A := ∑ (i : ℕ) in finset.range 4, (x i)^(3:ℝ),
    let B : ℕ → ℝ := λ j, (∑ (i : ℕ) in (finset.range 4).erase j, (x i)^(3:ℝ)),
    have hab : A = (1/3) * (∑ (i : ℕ) in finset.range 4, B i),
    { simp[finset.sum_range_succ, A], ring },
    have h2 : ∀ j ∈ (finset.range 4), ∏ (i : ℕ) in (finset.range 4).erase j, x i ≤ (1/3) * B j,
    { intros j hj,
      have hcard1: (finset.range 4).card = 4 := finset.card_range 4,
      have hcard: ((finset.range 4).erase j).card = (finset.range 4).card - 1 :=
        finset.card_erase_of_mem hj,
      rw[hcard1] at hcard,
      norm_num at hcard,

      have amgm := real.geom_mean_le_arith_mean_weighted
                    ((finset.range 4).erase j)
                    (λ ii, (1:ℝ)/3)
                    (λ ii, x ii ^ (3:ℝ))
                    (by { intros i hi, simp only [one_div, inv_nonneg], exact zero_le_three})
                    (by { simp[finset.sum_range_succ, hcard]})
                    (by {intros i _,
                         exact real.rpow_nonneg_of_nonneg (le_of_lt (x_positive i)) 3}),
      have hr : ∀ i ∈ ((finset.range 4).erase j),
                   (λ (ii : ℕ), x ii ^ (3:ℝ)) i ^ (λ (ii : ℕ), (1:ℝ) / 3) i = x i,
      { intros i _, exact cube_root_cube _ (le_of_lt (x_positive i)) },
      rw [finset.prod_congr rfl hr] at amgm,
      have hs : ∀ i ∈ ((finset.range 4).erase j),
        (λ (ii : ℕ), (1:ℝ) / 3) i * (λ (ii : ℕ), x ii ^ (3:ℝ)) i =
         ((1:ℝ)/3) * x i ^ (3:ℝ) := by simp,
      have := finset.sum_congr rfl hs,
      rw [finset.sum_congr rfl hs] at amgm,
      rw [←finset.mul_sum] at amgm,
      have hrfl : (λ (j : ℕ), ∑ (i : ℕ) in (finset.range 4).erase j, x i ^ (3:ℝ)) j =
                    ∑ (i : ℕ) in (finset.range 4).erase j, x i ^ (3:ℝ) := rfl,
      rw [←hrfl] at amgm,
      exact amgm },
    have h3 : ∀ j ∈ (finset.range 4), ∏ (i : ℕ) in (finset.range 4).erase j, x i = 1 / x j,
    { intros j hj,
      rw [←h, ←finset.prod_erase_mul _ _ hj],
      have : x j ≠ 0 := ne_of_gt (x_positive j),
      field_simp },
    have h4 : ∀ j ∈ finset.range 4, 1 / x j ≤ 1 / 3 * B j,
    { intros j hj,
      have h2j := h2 j hj,
      rw[h3 j hj] at h2j,
      exact h2j },
    have h5 : ∑ (i : ℕ) in finset.range 4, 1 / x i ≤ A,
    { have h5': ∑ (i : ℕ) in finset.range 4, 1 / x i ≤ ∑ (i : ℕ) in finset.range 4, (1 / 3) * B i,
      { exact finset.sum_le_sum h4 },
      rw [←finset.mul_sum] at h5',
      rw [hab],
      exact h5' },
    exact h5 }
end