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

Some proofs and docs came from `algebra/commute` (c) Neil Strickland

! This file was ported from Lean 3 source module algebra.group.semiconj
! 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.Units

/-!
# Semiconjugate elements of a semigroup

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

## Main definitions

We say that `x` is semiconjugate to `y` by `a` (`semiconj_by a x y`), if `a * x = y * a`.
In this file we  provide operations on `semiconj_by _ _ _`.

In the names of these operations, we treat `a` as the “left” argument, and both `x` and `y` as
“right” arguments. This way most names in this file agree with the names of the corresponding lemmas
for `commute a b = semiconj_by a b b`. As a side effect, some lemmas have only `_right` version.

Lean does not immediately recognise these terms as equations, so for rewriting we need syntax like
`rw [(h.pow_right 5).eq]` rather than just `rw [h.pow_right 5]`.

This file provides only basic operations (`mul_left`, `mul_right`, `inv_right` etc). Other
operations (`pow_right`, field inverse etc) are in the files that define corresponding notions.
-/

universe u v

variable {G : Type _}

#print SemiconjBy /-
/-- `x` is semiconjugate to `y` by `a`, if `a * x = y * a`. -/
@[to_additive AddSemiconjBy "`x` is additive semiconjugate to `y` by `a` if `a + x = y + a`"]
def SemiconjBy {M : Type u} [Mul M] (a x y : M) : Prop :=
  a * x = y * a
#align semiconj_by SemiconjBy
#align add_semiconj_by AddSemiconjBy
-/

namespace SemiconjBy

#print SemiconjBy.eq /-
/-- Equality behind `semiconj_by a x y`; useful for rewriting. -/
@[to_additive "Equality behind `add_semiconj_by a x y`; useful for rewriting."]
protected theorem eq {S : Type u} [Mul S] {a x y : S} (h : SemiconjBy a x y) : a * x = y * a :=
  h
#align semiconj_by.eq SemiconjBy.eq
#align add_semiconj_by.eq AddSemiconjBy.eq
-/

section Semigroup

variable {S : Type u} [Semigroup S] {a b x y z x' y' : S}

/- warning: semiconj_by.mul_right -> SemiconjBy.mul_right is a dubious translation:
lean 3 declaration is
  forall {S : Type.{u1}} [_inst_1 : Semigroup.{u1} S] {a : S} {x : S} {y : S} {x' : S} {y' : S}, (SemiconjBy.{u1} S (Semigroup.toHasMul.{u1} S _inst_1) a x y) -> (SemiconjBy.{u1} S (Semigroup.toHasMul.{u1} S _inst_1) a x' y') -> (SemiconjBy.{u1} S (Semigroup.toHasMul.{u1} S _inst_1) a (HMul.hMul.{u1, u1, u1} S S S (instHMul.{u1} S (Semigroup.toHasMul.{u1} S _inst_1)) x x') (HMul.hMul.{u1, u1, u1} S S S (instHMul.{u1} S (Semigroup.toHasMul.{u1} S _inst_1)) y y'))
but is expected to have type
  forall {S : Type.{u1}} [_inst_1 : Semigroup.{u1} S] {a : S} {x : S} {y : S} {x' : S} {y' : S}, (SemiconjBy.{u1} S (Semigroup.toMul.{u1} S _inst_1) a x y) -> (SemiconjBy.{u1} S (Semigroup.toMul.{u1} S _inst_1) a x' y') -> (SemiconjBy.{u1} S (Semigroup.toMul.{u1} S _inst_1) a (HMul.hMul.{u1, u1, u1} S S S (instHMul.{u1} S (Semigroup.toMul.{u1} S _inst_1)) x x') (HMul.hMul.{u1, u1, u1} S S S (instHMul.{u1} S (Semigroup.toMul.{u1} S _inst_1)) y y'))
Case conversion may be inaccurate. Consider using '#align semiconj_by.mul_right SemiconjBy.mul_rightₓ'. -/
/-- If `a` semiconjugates `x` to `y` and `x'` to `y'`,
then it semiconjugates `x * x'` to `y * y'`. -/
@[simp,
  to_additive
      "If `a` semiconjugates `x` to `y` and `x'` to `y'`, then it semiconjugates\n`x + x'` to `y + y'`."]
theorem mul_right (h : SemiconjBy a x y) (h' : SemiconjBy a x' y') :
    SemiconjBy a (x * x') (y * y') := by unfold SemiconjBy <;> assoc_rw [h.eq, h'.eq]
#align semiconj_by.mul_right SemiconjBy.mul_right
#align add_semiconj_by.add_right AddSemiconjBy.add_right

/- warning: semiconj_by.mul_left -> SemiconjBy.mul_left is a dubious translation:
lean 3 declaration is
  forall {S : Type.{u1}} [_inst_1 : Semigroup.{u1} S] {a : S} {b : S} {x : S} {y : S} {z : S}, (SemiconjBy.{u1} S (Semigroup.toHasMul.{u1} S _inst_1) a y z) -> (SemiconjBy.{u1} S (Semigroup.toHasMul.{u1} S _inst_1) b x y) -> (SemiconjBy.{u1} S (Semigroup.toHasMul.{u1} S _inst_1) (HMul.hMul.{u1, u1, u1} S S S (instHMul.{u1} S (Semigroup.toHasMul.{u1} S _inst_1)) a b) x z)
but is expected to have type
  forall {S : Type.{u1}} [_inst_1 : Semigroup.{u1} S] {a : S} {b : S} {x : S} {y : S} {z : S}, (SemiconjBy.{u1} S (Semigroup.toMul.{u1} S _inst_1) a y z) -> (SemiconjBy.{u1} S (Semigroup.toMul.{u1} S _inst_1) b x y) -> (SemiconjBy.{u1} S (Semigroup.toMul.{u1} S _inst_1) (HMul.hMul.{u1, u1, u1} S S S (instHMul.{u1} S (Semigroup.toMul.{u1} S _inst_1)) a b) x z)
Case conversion may be inaccurate. Consider using '#align semiconj_by.mul_left SemiconjBy.mul_leftₓ'. -/
/-- If both `a` and `b` semiconjugate `x` to `y`, then so does `a * b`. -/
@[to_additive "If both `a` and `b` semiconjugate `x` to `y`, then so does `a + b`."]
theorem mul_left (ha : SemiconjBy a y z) (hb : SemiconjBy b x y) : SemiconjBy (a * b) x z := by
  unfold SemiconjBy <;> assoc_rw [hb.eq, ha.eq, mul_assoc]
#align semiconj_by.mul_left SemiconjBy.mul_left
#align add_semiconj_by.add_left AddSemiconjBy.add_left

/- warning: semiconj_by.transitive -> SemiconjBy.transitive is a dubious translation:
lean 3 declaration is
  forall {S : Type.{u1}} [_inst_1 : Semigroup.{u1} S], Transitive.{succ u1} S (fun (a : S) (b : S) => Exists.{succ u1} S (fun (c : S) => SemiconjBy.{u1} S (Semigroup.toHasMul.{u1} S _inst_1) c a b))
but is expected to have type
  forall {S : Type.{u1}} [_inst_1 : Semigroup.{u1} S], Transitive.{succ u1} S (fun (a : S) (b : S) => Exists.{succ u1} S (fun (c : S) => SemiconjBy.{u1} S (Semigroup.toMul.{u1} S _inst_1) c a b))
Case conversion may be inaccurate. Consider using '#align semiconj_by.transitive SemiconjBy.transitiveₓ'. -/
/-- The relation “there exists an element that semiconjugates `a` to `b`” on a semigroup
is transitive. -/
@[to_additive
      "The relation “there exists an element that semiconjugates `a` to `b`” on an additive\nsemigroup is transitive."]
protected theorem transitive : Transitive fun a b : S => ∃ c, SemiconjBy c a b :=
  fun a b c ⟨x, hx⟩ ⟨y, hy⟩ => ⟨y * x, hy.mul_left hx⟩
#align semiconj_by.transitive SemiconjBy.transitive

end Semigroup

section MulOneClass

variable {M : Type u} [MulOneClass M]

/- warning: semiconj_by.one_right -> SemiconjBy.one_right is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : MulOneClass.{u1} M] (a : M), SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M _inst_1) a (OfNat.ofNat.{u1} M 1 (OfNat.mk.{u1} M 1 (One.one.{u1} M (MulOneClass.toHasOne.{u1} M _inst_1)))) (OfNat.ofNat.{u1} M 1 (OfNat.mk.{u1} M 1 (One.one.{u1} M (MulOneClass.toHasOne.{u1} M _inst_1))))
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : MulOneClass.{u1} M] (a : M), SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M _inst_1) a (OfNat.ofNat.{u1} M 1 (One.toOfNat1.{u1} M (MulOneClass.toOne.{u1} M _inst_1))) (OfNat.ofNat.{u1} M 1 (One.toOfNat1.{u1} M (MulOneClass.toOne.{u1} M _inst_1)))
Case conversion may be inaccurate. Consider using '#align semiconj_by.one_right SemiconjBy.one_rightₓ'. -/
/-- Any element semiconjugates `1` to `1`. -/
@[simp, to_additive "Any element additively semiconjugates `0` to `0`."]
theorem one_right (a : M) : SemiconjBy a 1 1 := by rw [SemiconjBy, mul_one, one_mul]
#align semiconj_by.one_right SemiconjBy.one_right
#align add_semiconj_by.zero_right AddSemiconjBy.zero_right

/- warning: semiconj_by.one_left -> SemiconjBy.one_left is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : MulOneClass.{u1} M] (x : M), SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M _inst_1) (OfNat.ofNat.{u1} M 1 (OfNat.mk.{u1} M 1 (One.one.{u1} M (MulOneClass.toHasOne.{u1} M _inst_1)))) x x
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : MulOneClass.{u1} M] (x : M), SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M _inst_1) (OfNat.ofNat.{u1} M 1 (One.toOfNat1.{u1} M (MulOneClass.toOne.{u1} M _inst_1))) x x
Case conversion may be inaccurate. Consider using '#align semiconj_by.one_left SemiconjBy.one_leftₓ'. -/
/-- One semiconjugates any element to itself. -/
@[simp, to_additive "Zero additively semiconjugates any element to itself."]
theorem one_left (x : M) : SemiconjBy 1 x x :=
  Eq.symm <| one_right x
#align semiconj_by.one_left SemiconjBy.one_left
#align add_semiconj_by.zero_left AddSemiconjBy.zero_left

/- warning: semiconj_by.reflexive -> SemiconjBy.reflexive is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : MulOneClass.{u1} M], Reflexive.{succ u1} M (fun (a : M) (b : M) => Exists.{succ u1} M (fun (c : M) => SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M _inst_1) c a b))
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : MulOneClass.{u1} M], Reflexive.{succ u1} M (fun (a : M) (b : M) => Exists.{succ u1} M (fun (c : M) => SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M _inst_1) c a b))
Case conversion may be inaccurate. Consider using '#align semiconj_by.reflexive SemiconjBy.reflexiveₓ'. -/
/-- The relation “there exists an element that semiconjugates `a` to `b`” on a monoid (or, more
generally, on ` mul_one_class` type) is reflexive. -/
@[to_additive
      "The relation “there exists an element that semiconjugates `a` to `b`” on an additive\nmonoid (or, more generally, on a `add_zero_class` type) is reflexive."]
protected theorem reflexive : Reflexive fun a b : M => ∃ c, SemiconjBy c a b := fun a =>
  ⟨1, one_left a⟩
#align semiconj_by.reflexive SemiconjBy.reflexive
#align add_semiconj_by.reflexive AddSemiconjBy.reflexive

end MulOneClass

section Monoid

variable {M : Type u} [Monoid M]

/- warning: semiconj_by.units_inv_right -> SemiconjBy.units_inv_right is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : M} {x : Units.{u1} M _inst_1} {y : Units.{u1} M _inst_1}, (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) x) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) y)) -> (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.hasInv.{u1} M _inst_1) x)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.hasInv.{u1} M _inst_1) y)))
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : M} {x : Units.{u1} M _inst_1} {y : Units.{u1} M _inst_1}, (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a (Units.val.{u1} M _inst_1 x) (Units.val.{u1} M _inst_1 y)) -> (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a (Units.val.{u1} M _inst_1 (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.instInvUnits.{u1} M _inst_1) x)) (Units.val.{u1} M _inst_1 (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.instInvUnits.{u1} M _inst_1) y)))
Case conversion may be inaccurate. Consider using '#align semiconj_by.units_inv_right SemiconjBy.units_inv_rightₓ'. -/
/-- If `a` semiconjugates a unit `x` to a unit `y`, then it semiconjugates `x⁻¹` to `y⁻¹`. -/
@[to_additive
      "If `a` semiconjugates an additive unit `x` to an additive unit `y`, then it\nsemiconjugates `-x` to `-y`."]
theorem units_inv_right {a : M} {x y : Mˣ} (h : SemiconjBy a x y) : SemiconjBy a ↑x⁻¹ ↑y⁻¹ :=
  calc
    a * ↑x⁻¹ = ↑y⁻¹ * (y * a) * ↑x⁻¹ := by rw [Units.inv_mul_cancel_left]
    _ = ↑y⁻¹ * a := by rw [← h.eq, mul_assoc, Units.mul_inv_cancel_right]

#align semiconj_by.units_inv_right SemiconjBy.units_inv_right
#align add_semiconj_by.add_units_neg_right AddSemiconjBy.addUnits_neg_right

/- warning: semiconj_by.units_inv_right_iff -> SemiconjBy.units_inv_right_iff is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : M} {x : Units.{u1} M _inst_1} {y : Units.{u1} M _inst_1}, Iff (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.hasInv.{u1} M _inst_1) x)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.hasInv.{u1} M _inst_1) y))) (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) x) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) y))
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : M} {x : Units.{u1} M _inst_1} {y : Units.{u1} M _inst_1}, Iff (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a (Units.val.{u1} M _inst_1 (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.instInvUnits.{u1} M _inst_1) x)) (Units.val.{u1} M _inst_1 (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.instInvUnits.{u1} M _inst_1) y))) (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a (Units.val.{u1} M _inst_1 x) (Units.val.{u1} M _inst_1 y))
Case conversion may be inaccurate. Consider using '#align semiconj_by.units_inv_right_iff SemiconjBy.units_inv_right_iffₓ'. -/
@[simp, to_additive]
theorem units_inv_right_iff {a : M} {x y : Mˣ} : SemiconjBy a ↑x⁻¹ ↑y⁻¹ ↔ SemiconjBy a x y :=
  ⟨units_inv_right, units_inv_right⟩
#align semiconj_by.units_inv_right_iff SemiconjBy.units_inv_right_iff
#align add_semiconj_by.add_units_neg_right_iff AddSemiconjBy.addUnits_neg_right_iff

/- warning: semiconj_by.units_inv_symm_left -> SemiconjBy.units_inv_symm_left is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : Units.{u1} M _inst_1} {x : M} {y : M}, (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) a) x y) -> (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.hasInv.{u1} M _inst_1) a)) y x)
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : Units.{u1} M _inst_1} {x : M} {y : M}, (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Units.val.{u1} M _inst_1 a) x y) -> (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Units.val.{u1} M _inst_1 (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.instInvUnits.{u1} M _inst_1) a)) y x)
Case conversion may be inaccurate. Consider using '#align semiconj_by.units_inv_symm_left SemiconjBy.units_inv_symm_leftₓ'. -/
/-- If a unit `a` semiconjugates `x` to `y`, then `a⁻¹` semiconjugates `y` to `x`. -/
@[to_additive
      "If an additive unit `a` semiconjugates `x` to `y`, then `-a` semiconjugates `y` to\n`x`."]
theorem units_inv_symm_left {a : Mˣ} {x y : M} (h : SemiconjBy (↑a) x y) : SemiconjBy (↑a⁻¹) y x :=
  calc
    ↑a⁻¹ * y = ↑a⁻¹ * (y * a * ↑a⁻¹) := by rw [Units.mul_inv_cancel_right]
    _ = x * ↑a⁻¹ := by rw [← h.eq, ← mul_assoc, Units.inv_mul_cancel_left]

#align semiconj_by.units_inv_symm_left SemiconjBy.units_inv_symm_left
#align add_semiconj_by.add_units_neg_symm_left AddSemiconjBy.addUnits_neg_symm_left

/- warning: semiconj_by.units_inv_symm_left_iff -> SemiconjBy.units_inv_symm_left_iff is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : Units.{u1} M _inst_1} {x : M} {y : M}, Iff (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.hasInv.{u1} M _inst_1) a)) y x) (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) a) x y)
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : Units.{u1} M _inst_1} {x : M} {y : M}, Iff (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Units.val.{u1} M _inst_1 (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.instInvUnits.{u1} M _inst_1) a)) y x) (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Units.val.{u1} M _inst_1 a) x y)
Case conversion may be inaccurate. Consider using '#align semiconj_by.units_inv_symm_left_iff SemiconjBy.units_inv_symm_left_iffₓ'. -/
@[simp, to_additive]
theorem units_inv_symm_left_iff {a : Mˣ} {x y : M} : SemiconjBy (↑a⁻¹) y x ↔ SemiconjBy (↑a) x y :=
  ⟨units_inv_symm_left, units_inv_symm_left⟩
#align semiconj_by.units_inv_symm_left_iff SemiconjBy.units_inv_symm_left_iff
#align add_semiconj_by.add_units_neg_symm_left_iff AddSemiconjBy.addUnits_neg_symm_left_iff

/- warning: semiconj_by.units_coe -> SemiconjBy.units_val is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : Units.{u1} M _inst_1} {x : Units.{u1} M _inst_1} {y : Units.{u1} M _inst_1}, (SemiconjBy.{u1} (Units.{u1} M _inst_1) (MulOneClass.toHasMul.{u1} (Units.{u1} M _inst_1) (Units.mulOneClass.{u1} M _inst_1)) a x y) -> (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) a) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) x) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) y))
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : Units.{u1} M _inst_1} {x : Units.{u1} M _inst_1} {y : Units.{u1} M _inst_1}, (SemiconjBy.{u1} (Units.{u1} M _inst_1) (MulOneClass.toMul.{u1} (Units.{u1} M _inst_1) (Units.instMulOneClassUnits.{u1} M _inst_1)) a x y) -> (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Units.val.{u1} M _inst_1 a) (Units.val.{u1} M _inst_1 x) (Units.val.{u1} M _inst_1 y))
Case conversion may be inaccurate. Consider using '#align semiconj_by.units_coe SemiconjBy.units_valₓ'. -/
@[to_additive]
theorem units_val {a x y : Mˣ} (h : SemiconjBy a x y) : SemiconjBy (a : M) x y :=
  congr_arg Units.val h
#align semiconj_by.units_coe SemiconjBy.units_val
#align add_semiconj_by.add_units_coe AddSemiconjBy.addUnits_val

/- warning: semiconj_by.units_of_coe -> SemiconjBy.units_of_val is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : Units.{u1} M _inst_1} {x : Units.{u1} M _inst_1} {y : Units.{u1} M _inst_1}, (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) a) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) x) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) y)) -> (SemiconjBy.{u1} (Units.{u1} M _inst_1) (MulOneClass.toHasMul.{u1} (Units.{u1} M _inst_1) (Units.mulOneClass.{u1} M _inst_1)) a x y)
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : Units.{u1} M _inst_1} {x : Units.{u1} M _inst_1} {y : Units.{u1} M _inst_1}, (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Units.val.{u1} M _inst_1 a) (Units.val.{u1} M _inst_1 x) (Units.val.{u1} M _inst_1 y)) -> (SemiconjBy.{u1} (Units.{u1} M _inst_1) (MulOneClass.toMul.{u1} (Units.{u1} M _inst_1) (Units.instMulOneClassUnits.{u1} M _inst_1)) a x y)
Case conversion may be inaccurate. Consider using '#align semiconj_by.units_of_coe SemiconjBy.units_of_valₓ'. -/
@[to_additive]
theorem units_of_val {a x y : Mˣ} (h : SemiconjBy (a : M) x y) : SemiconjBy a x y :=
  Units.ext h
#align semiconj_by.units_of_coe SemiconjBy.units_of_val
#align add_semiconj_by.add_units_of_coe AddSemiconjBy.addUnits_of_val

/- warning: semiconj_by.units_coe_iff -> SemiconjBy.units_val_iff is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : Units.{u1} M _inst_1} {x : Units.{u1} M _inst_1} {y : Units.{u1} M _inst_1}, Iff (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) a) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) x) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) y)) (SemiconjBy.{u1} (Units.{u1} M _inst_1) (MulOneClass.toHasMul.{u1} (Units.{u1} M _inst_1) (Units.mulOneClass.{u1} M _inst_1)) a x y)
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : Units.{u1} M _inst_1} {x : Units.{u1} M _inst_1} {y : Units.{u1} M _inst_1}, Iff (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Units.val.{u1} M _inst_1 a) (Units.val.{u1} M _inst_1 x) (Units.val.{u1} M _inst_1 y)) (SemiconjBy.{u1} (Units.{u1} M _inst_1) (MulOneClass.toMul.{u1} (Units.{u1} M _inst_1) (Units.instMulOneClassUnits.{u1} M _inst_1)) a x y)
Case conversion may be inaccurate. Consider using '#align semiconj_by.units_coe_iff SemiconjBy.units_val_iffₓ'. -/
@[simp, to_additive]
theorem units_val_iff {a x y : Mˣ} : SemiconjBy (a : M) x y ↔ SemiconjBy a x y :=
  ⟨units_of_val, units_val⟩
#align semiconj_by.units_coe_iff SemiconjBy.units_val_iff
#align add_semiconj_by.add_units_coe_iff AddSemiconjBy.addUnits_val_iff

/- warning: semiconj_by.pow_right -> SemiconjBy.pow_right is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : M} {x : M} {y : M}, (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a x y) -> (forall (n : Nat), SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a (HPow.hPow.{u1, 0, u1} M Nat M (instHPow.{u1, 0} M Nat (Monoid.Pow.{u1} M _inst_1)) x n) (HPow.hPow.{u1, 0, u1} M Nat M (instHPow.{u1, 0} M Nat (Monoid.Pow.{u1} M _inst_1)) y n))
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] {a : M} {x : M} {y : M}, (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a x y) -> (forall (n : Nat), SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) a (HPow.hPow.{u1, 0, u1} M Nat M (instHPow.{u1, 0} M Nat (Monoid.Pow.{u1} M _inst_1)) x n) (HPow.hPow.{u1, 0, u1} M Nat M (instHPow.{u1, 0} M Nat (Monoid.Pow.{u1} M _inst_1)) y n))
Case conversion may be inaccurate. Consider using '#align semiconj_by.pow_right SemiconjBy.pow_rightₓ'. -/
@[simp, to_additive]
theorem pow_right {a x y : M} (h : SemiconjBy a x y) (n : ℕ) : SemiconjBy a (x ^ n) (y ^ n) :=
  by
  induction' n with n ih
  · rw [pow_zero, pow_zero]
    exact SemiconjBy.one_right _
  · rw [pow_succ, pow_succ]
    exact h.mul_right ih
#align semiconj_by.pow_right SemiconjBy.pow_right
#align add_semiconj_by.nsmul_right AddSemiconjBy.nsmul_right

end Monoid

section DivisionMonoid

variable [DivisionMonoid G] {a x y : G}

/- warning: semiconj_by.inv_inv_symm_iff -> SemiconjBy.inv_inv_symm_iff is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : DivisionMonoid.{u1} G] {a : G} {x : G} {y : G}, Iff (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)))) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)) a) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)) x) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)) y)) (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)))) a y x)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : DivisionMonoid.{u1} G] {a : G} {x : G} {y : G}, Iff (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)))) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G _inst_1))) a) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G _inst_1))) x) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G _inst_1))) y)) (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)))) a y x)
Case conversion may be inaccurate. Consider using '#align semiconj_by.inv_inv_symm_iff SemiconjBy.inv_inv_symm_iffₓ'. -/
@[simp, to_additive]
theorem inv_inv_symm_iff : SemiconjBy a⁻¹ x⁻¹ y⁻¹ ↔ SemiconjBy a y x :=
  inv_involutive.Injective.eq_iff.symm.trans <| by
    simp_rw [mul_inv_rev, inv_inv, eq_comm, SemiconjBy]
#align semiconj_by.inv_inv_symm_iff SemiconjBy.inv_inv_symm_iff
#align add_semiconj_by.neg_neg_symm_iff AddSemiconjBy.neg_neg_symm_iff

/- warning: semiconj_by.inv_inv_symm -> SemiconjBy.inv_inv_symm is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : DivisionMonoid.{u1} G] {a : G} {x : G} {y : G}, (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)))) a x y) -> (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)))) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)) a) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)) y) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)) x))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : DivisionMonoid.{u1} G] {a : G} {x : G} {y : G}, (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)))) a x y) -> (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (DivisionMonoid.toDivInvMonoid.{u1} G _inst_1)))) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G _inst_1))) a) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G _inst_1))) y) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G _inst_1))) x))
Case conversion may be inaccurate. Consider using '#align semiconj_by.inv_inv_symm SemiconjBy.inv_inv_symmₓ'. -/
@[to_additive]
theorem inv_inv_symm : SemiconjBy a x y → SemiconjBy a⁻¹ y⁻¹ x⁻¹ :=
  inv_inv_symm_iff.2
#align semiconj_by.inv_inv_symm SemiconjBy.inv_inv_symm
#align add_semiconj_by.neg_neg_symm AddSemiconjBy.neg_neg_symm

end DivisionMonoid

section Group

variable [Group G] {a x y : G}

/- warning: semiconj_by.inv_right_iff -> SemiconjBy.inv_right_iff is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {a : G} {x : G} {y : G}, Iff (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) x) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) y)) (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a x y)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {a : G} {x : G} {y : G}, Iff (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G (Group.toDivisionMonoid.{u1} G _inst_1)))) x) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G (Group.toDivisionMonoid.{u1} G _inst_1)))) y)) (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a x y)
Case conversion may be inaccurate. Consider using '#align semiconj_by.inv_right_iff SemiconjBy.inv_right_iffₓ'. -/
@[simp, to_additive]
theorem inv_right_iff : SemiconjBy a x⁻¹ y⁻¹ ↔ SemiconjBy a x y :=
  @units_inv_right_iff G _ a ⟨x, x⁻¹, mul_inv_self x, inv_mul_self x⟩
    ⟨y, y⁻¹, mul_inv_self y, inv_mul_self y⟩
#align semiconj_by.inv_right_iff SemiconjBy.inv_right_iff
#align add_semiconj_by.neg_right_iff AddSemiconjBy.neg_right_iff

/- warning: semiconj_by.inv_right -> SemiconjBy.inv_right is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {a : G} {x : G} {y : G}, (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a x y) -> (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) x) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) y))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {a : G} {x : G} {y : G}, (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a x y) -> (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G (Group.toDivisionMonoid.{u1} G _inst_1)))) x) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G (Group.toDivisionMonoid.{u1} G _inst_1)))) y))
Case conversion may be inaccurate. Consider using '#align semiconj_by.inv_right SemiconjBy.inv_rightₓ'. -/
@[to_additive]
theorem inv_right : SemiconjBy a x y → SemiconjBy a x⁻¹ y⁻¹ :=
  inv_right_iff.2
#align semiconj_by.inv_right SemiconjBy.inv_right
#align add_semiconj_by.neg_right AddSemiconjBy.neg_right

/- warning: semiconj_by.inv_symm_left_iff -> SemiconjBy.inv_symm_left_iff is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {a : G} {x : G} {y : G}, Iff (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) a) y x) (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a x y)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {a : G} {x : G} {y : G}, Iff (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G (Group.toDivisionMonoid.{u1} G _inst_1)))) a) y x) (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a x y)
Case conversion may be inaccurate. Consider using '#align semiconj_by.inv_symm_left_iff SemiconjBy.inv_symm_left_iffₓ'. -/
@[simp, to_additive]
theorem inv_symm_left_iff : SemiconjBy a⁻¹ y x ↔ SemiconjBy a x y :=
  @units_inv_symm_left_iff G _ ⟨a, a⁻¹, mul_inv_self a, inv_mul_self a⟩ _ _
#align semiconj_by.inv_symm_left_iff SemiconjBy.inv_symm_left_iff
#align add_semiconj_by.neg_symm_left_iff AddSemiconjBy.neg_symm_left_iff

/- warning: semiconj_by.inv_symm_left -> SemiconjBy.inv_symm_left is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {a : G} {x : G} {y : G}, (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a x y) -> (SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) a) y x)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {a : G} {x : G} {y : G}, (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a x y) -> (SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G (Group.toDivisionMonoid.{u1} G _inst_1)))) a) y x)
Case conversion may be inaccurate. Consider using '#align semiconj_by.inv_symm_left SemiconjBy.inv_symm_leftₓ'. -/
@[to_additive]
theorem inv_symm_left : SemiconjBy a x y → SemiconjBy a⁻¹ y x :=
  inv_symm_left_iff.2
#align semiconj_by.inv_symm_left SemiconjBy.inv_symm_left
#align add_semiconj_by.neg_symm_left AddSemiconjBy.neg_symm_left

/- warning: semiconj_by.conj_mk -> SemiconjBy.conj_mk is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (a : G) (x : G), SemiconjBy.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a x (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) a x) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) a))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (a : G) (x : G), SemiconjBy.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) a x (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) a x) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G (Group.toDivisionMonoid.{u1} G _inst_1)))) a))
Case conversion may be inaccurate. Consider using '#align semiconj_by.conj_mk SemiconjBy.conj_mkₓ'. -/
/-- `a` semiconjugates `x` to `a * x * a⁻¹`. -/
@[to_additive "`a` semiconjugates `x` to `a + x + -a`."]
theorem conj_mk (a x : G) : SemiconjBy a x (a * x * a⁻¹) := by
  unfold SemiconjBy <;> rw [mul_assoc, inv_mul_self, mul_one]
#align semiconj_by.conj_mk SemiconjBy.conj_mk
#align add_semiconj_by.conj_mk AddSemiconjBy.conj_mk

end Group

end SemiconjBy

/- warning: semiconj_by_iff_eq -> semiconjBy_iff_eq is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : CancelCommMonoid.{u1} M] {a : M} {x : M} {y : M}, Iff (SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M (RightCancelMonoid.toMonoid.{u1} M (CancelMonoid.toRightCancelMonoid.{u1} M (CancelCommMonoid.toCancelMonoid.{u1} M _inst_1))))) a x y) (Eq.{succ u1} M x y)
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : CancelCommMonoid.{u1} M] {a : M} {x : M} {y : M}, Iff (SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M (RightCancelMonoid.toMonoid.{u1} M (CancelMonoid.toRightCancelMonoid.{u1} M (CancelCommMonoid.toCancelMonoid.{u1} M _inst_1))))) a x y) (Eq.{succ u1} M x y)
Case conversion may be inaccurate. Consider using '#align semiconj_by_iff_eq semiconjBy_iff_eqₓ'. -/
@[simp, to_additive addSemiconjBy_iff_eq]
theorem semiconjBy_iff_eq {M : Type u} [CancelCommMonoid M] {a x y : M} :
    SemiconjBy a x y ↔ x = y :=
  ⟨fun h => mul_left_cancel (h.trans (mul_comm _ _)), fun h => by rw [h, SemiconjBy, mul_comm]⟩
#align semiconj_by_iff_eq semiconjBy_iff_eq
#align add_semiconj_by_iff_eq addSemiconjBy_iff_eq

/- warning: units.mk_semiconj_by -> Units.mk_semiconjBy is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] (u : Units.{u1} M _inst_1) (x : M), SemiconjBy.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) u) x (HMul.hMul.{u1, u1, u1} M M M (instHMul.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) (HMul.hMul.{u1, u1, u1} M M M (instHMul.{u1} M (MulOneClass.toHasMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) u) x) ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (Units.{u1} M _inst_1) M (HasLiftT.mk.{succ u1, succ u1} (Units.{u1} M _inst_1) M (CoeTCₓ.coe.{succ u1, succ u1} (Units.{u1} M _inst_1) M (coeBase.{succ u1, succ u1} (Units.{u1} M _inst_1) M (Units.hasCoe.{u1} M _inst_1)))) (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.hasInv.{u1} M _inst_1) u)))
but is expected to have type
  forall {M : Type.{u1}} [_inst_1 : Monoid.{u1} M] (u : Units.{u1} M _inst_1) (x : M), SemiconjBy.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Units.val.{u1} M _inst_1 u) x (HMul.hMul.{u1, u1, u1} M M M (instHMul.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) (HMul.hMul.{u1, u1, u1} M M M (instHMul.{u1} M (MulOneClass.toMul.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) (Units.val.{u1} M _inst_1 u) x) (Units.val.{u1} M _inst_1 (Inv.inv.{u1} (Units.{u1} M _inst_1) (Units.instInvUnits.{u1} M _inst_1) u)))
Case conversion may be inaccurate. Consider using '#align units.mk_semiconj_by Units.mk_semiconjByₓ'. -/
/-- `a` semiconjugates `x` to `a * x * a⁻¹`. -/
@[to_additive "`a` semiconjugates `x` to `a + x + -a`."]
theorem Units.mk_semiconjBy {M : Type u} [Monoid M] (u : Mˣ) (x : M) :
    SemiconjBy (↑u) x (u * x * ↑u⁻¹) := by unfold SemiconjBy <;> rw [Units.inv_mul_cancel_right]
#align units.mk_semiconj_by Units.mk_semiconjBy
#align add_units.mk_semiconj_by AddUnits.mk_addSemiconjBy


/-
Copyright (c) 2020 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Kenny Lau.
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.ring_theory.polynomial.default
import Mathlib.algebra.big_operators.basic
import Mathlib.PostPort

universes u

namespace Mathlib

/-!
# Lagrange interpolation

## Main definitions

* `lagrange.basis s x` where `s : finset F` and `x : F`: the Lagrange basis polynomial
  that evaluates to `1` at `x` and `0` at other elements of `s`.
* `lagrange.interpolate s f` where `s : finset F` and `f : s → F`: the Lagrange interpolant
  that evaluates to `f x` at `x` for `x ∈ s`.

-/

namespace lagrange

/-- Lagrange basis polynomials that evaluate to 1 at `x` and 0 at other elements of `s`. -/
def basis {F : Type u} [DecidableEq F] [field F] (s : finset F) (x : F) : polynomial F :=
  finset.prod (finset.erase s x)
    fun (y : F) => coe_fn polynomial.C (x - y⁻¹) * (polynomial.X - coe_fn polynomial.C y)

@[simp] theorem basis_empty {F : Type u} [DecidableEq F] [field F] (x : F) : basis ∅ x = 1 := rfl

@[simp] theorem eval_basis_self {F : Type u} [DecidableEq F] [field F] (s : finset F) (x : F) :
    polynomial.eval x (basis s x) = 1 :=
  sorry

@[simp] theorem eval_basis_ne {F : Type u} [DecidableEq F] [field F] (s : finset F) (x : F) (y : F)
    (h1 : y ∈ s) (h2 : y ≠ x) : polynomial.eval y (basis s x) = 0 :=
  sorry

theorem eval_basis {F : Type u} [DecidableEq F] [field F] (s : finset F) (x : F) (y : F)
    (h : y ∈ s) : polynomial.eval y (basis s x) = ite (y = x) 1 0 :=
  sorry

@[simp] theorem nat_degree_basis {F : Type u} [DecidableEq F] [field F] (s : finset F) (x : F)
    (hx : x ∈ s) : polynomial.nat_degree (basis s x) = finset.card s - 1 :=
  sorry

/-- Lagrange interpolation: given a finset `s` and a function `f : s → F`,
`interpolate s f` is the unique polynomial of degree `< s.card`
that takes value `f x` on all `x` in `s`. -/
def interpolate {F : Type u} [DecidableEq F] [field F] (s : finset F) (f : ↥↑s → F) :
    polynomial F :=
  finset.sum (finset.attach s)
    fun (x : Subtype fun (x : F) => x ∈ s) => coe_fn polynomial.C (f x) * basis s ↑x

@[simp] theorem interpolate_empty {F : Type u} [DecidableEq F] [field F] (f : ↥↑∅ → F) :
    interpolate ∅ f = 0 :=
  rfl

@[simp] theorem eval_interpolate {F : Type u} [DecidableEq F] [field F] (s : finset F) (f : ↥↑s → F)
    (x : F) (H : x ∈ s) : polynomial.eval x (interpolate s f) = f { val := x, property := H } :=
  sorry

theorem degree_interpolate_lt {F : Type u} [DecidableEq F] [field F] (s : finset F) (f : ↥↑s → F) :
    polynomial.degree (interpolate s f) < ↑(finset.card s) :=
  sorry

/-- Linear version of `interpolate`. -/
def linterpolate {F : Type u} [DecidableEq F] [field F] (s : finset F) :
    linear_map F (↥↑s → F) (polynomial F) :=
  linear_map.mk (interpolate s) sorry sorry

@[simp] theorem interpolate_add {F : Type u} [DecidableEq F] [field F] (s : finset F) (f : ↥↑s → F)
    (g : ↥↑s → F) : interpolate s (f + g) = interpolate s f + interpolate s g :=
  linear_map.map_add (linterpolate s) f g

@[simp] theorem interpolate_zero {F : Type u} [DecidableEq F] [field F] (s : finset F) :
    interpolate s 0 = 0 :=
  linear_map.map_zero (linterpolate s)

@[simp] theorem interpolate_neg {F : Type u} [DecidableEq F] [field F] (s : finset F)
    (f : ↥↑s → F) : interpolate s (-f) = -interpolate s f :=
  linear_map.map_neg (linterpolate s) f

@[simp] theorem interpolate_sub {F : Type u} [DecidableEq F] [field F] (s : finset F) (f : ↥↑s → F)
    (g : ↥↑s → F) : interpolate s (f - g) = interpolate s f - interpolate s g :=
  linear_map.map_sub (linterpolate s) f g

@[simp] theorem interpolate_smul {F : Type u} [DecidableEq F] [field F] (s : finset F) (c : F)
    (f : ↥↑s → F) : interpolate s (c • f) = c • interpolate s f :=
  linear_map.map_smul (linterpolate s) c f

theorem eq_zero_of_eval_eq_zero {F' : Type u} [field F'] (s' : finset F') {f : polynomial F'}
    (hf1 : polynomial.degree f < ↑(finset.card s'))
    (hf2 : ∀ (x : F'), x ∈ s' → polynomial.eval x f = 0) : f = 0 :=
  sorry

theorem eq_of_eval_eq {F' : Type u} [field F'] (s' : finset F') {f : polynomial F'}
    {g : polynomial F'} (hf : polynomial.degree f < ↑(finset.card s'))
    (hg : polynomial.degree g < ↑(finset.card s'))
    (hfg : ∀ (x : F'), x ∈ s' → polynomial.eval x f = polynomial.eval x g) : f = g :=
  sorry

theorem eq_interpolate {F : Type u} [DecidableEq F] [field F] (s : finset F) (f : polynomial F)
    (hf : polynomial.degree f < ↑(finset.card s)) :
    (interpolate s fun (x : ↥↑s) => polynomial.eval (↑x) f) = f :=
  eq_of_eval_eq s (degree_interpolate_lt s fun (x : ↥↑s) => polynomial.eval (↑x) f) hf
    fun (x : F) (hx : x ∈ s) => eval_interpolate s (fun (x : ↥↑s) => polynomial.eval (↑x) f) x hx

/-- Lagrange interpolation induces isomorphism between functions from `s` and polynomials
of degree less than `s.card`. -/
def fun_equiv_degree_lt {F : Type u} [DecidableEq F] [field F] (s : finset F) :
    linear_equiv F (↥(polynomial.degree_lt F (finset.card s))) (↥↑s → F) :=
  linear_equiv.mk
    (fun (f : ↥(polynomial.degree_lt F (finset.card s))) (x : ↥↑s) =>
      polynomial.eval (↑x) (subtype.val f))
    sorry sorry (fun (f : ↥↑s → F) => { val := interpolate s f, property := sorry }) sorry sorry

end Mathlib


/-
Copyright (c) 2020 Devon Tuma. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Devon Tuma
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.ring_theory.ideal.operations
import Mathlib.ring_theory.polynomial.basic
import Mathlib.PostPort

universes u v

namespace Mathlib

/-!
# Jacobson radical

The Jacobson radical of a ring `R` is defined to be the intersection of all maximal ideals of `R`.
This is similar to how the nilradical is equal to the intersection of all prime ideals of `R`.

We can extend the idea of the nilradical to ideals of `R`,
by letting the radical of an ideal `I` be the intersection of prime ideals containing `I`.
Under this extension, the original nilradical is the radical of the zero ideal `⊥`.
Here we define the Jacobson radical of an ideal `I` in a similar way,
as the intersection of maximal ideals containing `I`.

## Main definitions

Let `R` be a commutative ring, and `I` be an ideal of `R`

* `jacobson I` is the jacobson radical, i.e. the infimum of all maximal ideals containing I.

* `is_local I` is the proposition that the jacobson radical of `I` is itself a maximal ideal

## Main statements

* `mem_jacobson_iff` gives a characterization of members of the jacobson of I

* `is_local_of_is_maximal_radical`: if the radical of I is maximal then so is the jacobson radical

## Tags

Jacobson, Jacobson radical, Local Ideal

-/

namespace ideal

/-- The Jacobson radical of `I` is the infimum of all maximal ideals containing `I`. -/
def jacobson {R : Type u} [comm_ring R] (I : ideal R) : ideal R :=
  Inf (set_of fun (J : ideal R) => I ≤ J ∧ is_maximal J)

theorem le_jacobson {R : Type u} [comm_ring R] {I : ideal R} : I ≤ jacobson I :=
  fun (x : R) (hx : x ∈ I) =>
    iff.mpr mem_Inf fun (J : ideal R) (hJ : J ∈ set_of fun (J : ideal R) => I ≤ J ∧ is_maximal J) => and.left hJ x hx

@[simp] theorem jacobson_idem {R : Type u} [comm_ring R] {I : ideal R} : jacobson (jacobson I) = jacobson I := sorry

theorem radical_le_jacobson {R : Type u} [comm_ring R] {I : ideal R} : radical I ≤ jacobson I :=
  le_Inf
    fun (J : ideal R) (hJ : J ∈ set_of fun (J : ideal R) => I ≤ J ∧ is_maximal J) =>
      Eq.symm (radical_eq_Inf I) ▸ Inf_le { left := and.left hJ, right := is_maximal.is_prime (and.right hJ) }

theorem eq_radical_of_eq_jacobson {R : Type u} [comm_ring R] {I : ideal R} : jacobson I = I → radical I = I :=
  fun (h : jacobson I = I) => le_antisymm (le_trans radical_le_jacobson (le_of_eq h)) le_radical

@[simp] theorem jacobson_top {R : Type u} [comm_ring R] : jacobson ⊤ = ⊤ :=
  iff.mpr eq_top_iff le_jacobson

@[simp] theorem jacobson_eq_top_iff {R : Type u} [comm_ring R] {I : ideal R} : jacobson I = ⊤ ↔ I = ⊤ := sorry

theorem jacobson_eq_bot {R : Type u} [comm_ring R] {I : ideal R} : jacobson I = ⊥ → I = ⊥ :=
  fun (h : jacobson I = ⊥) => iff.mpr eq_bot_iff (h ▸ le_jacobson)

theorem jacobson_eq_self_of_is_maximal {R : Type u} [comm_ring R] {I : ideal R} [H : is_maximal I] : jacobson I = I :=
  le_antisymm (Inf_le { left := le_of_eq rfl, right := H }) le_jacobson

protected instance jacobson.is_maximal {R : Type u} [comm_ring R] {I : ideal R} [H : is_maximal I] : is_maximal (jacobson I) :=
  { left := fun (htop : jacobson I = ⊤) => and.left H (iff.mp jacobson_eq_top_iff htop),
    right := fun (J : ideal R) (hJ : jacobson I < J) => and.right H J (lt_of_le_of_lt le_jacobson hJ) }

theorem mem_jacobson_iff {R : Type u} [comm_ring R] {I : ideal R} {x : R} : x ∈ jacobson I ↔ ∀ (y : R), ∃ (z : R), x * y * z + z - 1 ∈ I := sorry

/-- An ideal equals its Jacobson radical iff it is the intersection of a set of maximal ideals.
Allowing the set to include ⊤ is equivalent, and is included only to simplify some proofs. -/
theorem eq_jacobson_iff_Inf_maximal {R : Type u} [comm_ring R] {I : ideal R} : jacobson I = I ↔ ∃ (M : set (ideal R)), (∀ (J : ideal R), J ∈ M → is_maximal J ∨ J = ⊤) ∧ I = Inf M := sorry

theorem eq_jacobson_iff_Inf_maximal' {R : Type u} [comm_ring R] {I : ideal R} : jacobson I = I ↔ ∃ (M : set (ideal R)), (∀ (J : ideal R), J ∈ M → ∀ (K : ideal R), J < K → K = ⊤) ∧ I = Inf M := sorry

/-- An ideal `I` equals its Jacobson radical if and only if every element outside `I`
also lies outside of a maximal ideal containing `I`. -/
theorem eq_jacobson_iff_not_mem {R : Type u} [comm_ring R] {I : ideal R} : jacobson I = I ↔ ∀ (x : R), ¬x ∈ I → ∃ (M : ideal R), (I ≤ M ∧ is_maximal M) ∧ ¬x ∈ M := sorry

theorem map_jacobson_of_surjective {R : Type u} [comm_ring R] {I : ideal R} {S : Type v} [comm_ring S] {f : R →+* S} (hf : function.surjective ⇑f) : ring_hom.ker f ≤ I → map f (jacobson I) = jacobson (map f I) := sorry

theorem map_jacobson_of_bijective {R : Type u} [comm_ring R] {I : ideal R} {S : Type v} [comm_ring S] {f : R →+* S} (hf : function.bijective ⇑f) : map f (jacobson I) = jacobson (map f I) :=
  map_jacobson_of_surjective (and.right hf)
    (le_trans (le_of_eq (iff.mp (ring_hom.injective_iff_ker_eq_bot f) (and.left hf))) bot_le)

theorem comap_jacobson {R : Type u} [comm_ring R] {S : Type v} [comm_ring S] {f : R →+* S} {K : ideal S} : comap f (jacobson K) = Inf (comap f '' set_of fun (J : ideal S) => K ≤ J ∧ is_maximal J) :=
  trans (comap_Inf' f (set_of fun (J : ideal S) => K ≤ J ∧ is_maximal J)) (Eq.symm Inf_eq_infi)

theorem comap_jacobson_of_surjective {R : Type u} [comm_ring R] {S : Type v} [comm_ring S] {f : R →+* S} (hf : function.surjective ⇑f) {K : ideal S} : comap f (jacobson K) = jacobson (comap f K) := sorry

theorem mem_jacobson_bot {R : Type u} [comm_ring R] {x : R} : x ∈ jacobson ⊥ ↔ ∀ (y : R), is_unit (x * y + 1) := sorry

/-- An ideal `I` of `R` is equal to its Jacobson radical if and only if
the Jacobson radical of the quotient ring `R/I` is the zero ideal -/
theorem jacobson_eq_iff_jacobson_quotient_eq_bot {R : Type u} [comm_ring R] {I : ideal R} : jacobson I = I ↔ jacobson ⊥ = ⊥ := sorry

/-- The standard radical and Jacobson radical of an ideal `I` of `R` are equal if and only if
the nilradical and Jacobson radical of the quotient ring `R/I` coincide -/
theorem radical_eq_jacobson_iff_radical_quotient_eq_jacobson_bot {R : Type u} [comm_ring R] {I : ideal R} : radical I = jacobson I ↔ radical ⊥ = jacobson ⊥ := sorry

theorem jacobson_mono {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} : I ≤ J → jacobson I ≤ jacobson J := sorry

theorem jacobson_radical_eq_jacobson {R : Type u} [comm_ring R] {I : ideal R} : jacobson (radical I) = jacobson I := sorry

theorem jacobson_bot_polynomial_le_Inf_map_maximal {R : Type u} [comm_ring R] : jacobson ⊥ ≤ Inf (map polynomial.C '' set_of fun (J : ideal R) => is_maximal J) := sorry

theorem jacobson_bot_polynomial_of_jacobson_bot {R : Type u} [comm_ring R] (h : jacobson ⊥ = ⊥) : jacobson ⊥ = ⊥ := sorry

/-- An ideal `I` is local iff its Jacobson radical is maximal. -/
def is_local {R : Type u} [comm_ring R] (I : ideal R) :=
  is_maximal (jacobson I)

theorem is_local_of_is_maximal_radical {R : Type u} [comm_ring R] {I : ideal R} (hi : is_maximal (radical I)) : is_local I := sorry

theorem is_local.le_jacobson {R : Type u} [comm_ring R] {I : ideal R} {J : ideal R} (hi : is_local I) (hij : I ≤ J) (hj : J ≠ ⊤) : J ≤ jacobson I := sorry

theorem is_local.mem_jacobson_or_exists_inv {R : Type u} [comm_ring R] {I : ideal R} (hi : is_local I) (x : R) : x ∈ jacobson I ∨ ∃ (y : R), y * x - 1 ∈ I := sorry

theorem is_primary_of_is_maximal_radical {R : Type u} [comm_ring R] {I : ideal R} (hi : is_maximal (radical I)) : is_primary I := sorry


import data.list.func
import .list2d
import .boolset2d

variables {α : Type} {β : Type}
variables [inhabited α] [inhabited β]

def forall1_inh_bool (P : α → bool) : list α → bool
| [] := P (default α)
| (h::t) := (P h) && (forall1_inh_bool t)
def forall2_inh_bool (P : α → β → bool) : list α → list β → bool
| [] l2 := forall1_inh_bool (P (default α)) l2
| (h::t) l2 := (P h l2.head) && (forall2_inh_bool t l2.tail)

lemma forall1_inh_bool.correct (P : α → Prop) [Hdec : Π a, decidable (P a)]
: ∀ l1 : list α,
forall1_inh_bool (λ a, to_bool (P a)) l1 ↔ ∀ n, P (list.func.get n l1)
| [] := by simp [forall1_inh_bool]
| (h::t) := begin
  simp [forall1_inh_bool, forall1_inh_bool.correct],
  split, assume H n, cases n, exact H.1, exact H.2 n,
  assume H, split, exact H 0, assume n, exact H n.succ,
end

lemma forall2_inh_bool.correct (P : α → β → Prop) [Hdec : Π a b, decidable (P a b)]
: ∀ l1 : list α, ∀ l2 : list β,
forall2_inh_bool (λ a b, to_bool (P a b)) l1 l2
↔ ∀ n, P (list.func.get n l1) (list.func.get n l2)
| [] l2 := by simp [forall2_inh_bool, forall1_inh_bool.correct]
| (h::t) l2 := begin
  simp [forall2_inh_bool, forall2_inh_bool.correct],
  split, {
    assume H n, cases n,
    { cases l2, exact H.1, exact H.1 },
    { cases l2, simp, simp at H, exact H.2 n, simp [list.tail] at H, exact H.2 n, },
  }, {
    assume H, split,
    { cases l2, exact H 0, exact H 0, },
    { assume n, cases l2, simp, simp at H, exact H n.succ, simp [list.tail], exact H n.succ, },
  }
end

def forall2_inh_dec (P : α → β → Prop) [Hdec : Π a b, decidable (P a b)]
(l1 : list α) (l2 : list β)
: decidable (∀ n, P (list.func.get n l1) (list.func.get n l2))
:=
if C : forall2_inh_bool (λ a b, to_bool (P a b)) l1 l2 = tt then
  is_true ((forall2_inh_bool.correct P l1 l2).mp C)
else is_false (mt (forall2_inh_bool.correct P l1 l2).mpr C)

instance equiv_decidable [decidable_eq α] : @decidable_rel (list α) list.func.equiv
:= forall2_inh_dec eq

def forall2d2_inh_dec (P : α → β → Prop) [Hdec : Π a b, decidable (P a b)]
(l1 : list2d α) (l2 : list2d β) :
  decidable (∀ xy, P (list2d.get2d xy l1) (list2d.get2d xy l2))
:= begin
  let P2 := λ (as : list α) (bs : list β), ∀ x, P (list.func.get x as) (list.func.get x bs),
  haveI : ∀ a b, decidable (P2 a b) := forall2_inh_dec P,
  haveI := forall2_inh_dec P2 l1 l2,
  apply decidable_of_iff (∀ y, P2 (list.func.get y l1) (list.func.get y l2)),
  split,
  { intros H xy, cases xy with x y, exact H y x, },
  { intros H y x, exact H (x,y), },
end

instance list.equiv_decidable [decidable_eq α] : @decidable_rel (list α) list.func.equiv
:= forall2_inh_dec eq
instance list2d.equiv_decidable [decidable_eq α] : @decidable_rel (list2d α) list2d.equiv
:= forall2d2_inh_dec eq
instance : decidable_rel bset1d.subset
:= forall2_inh_dec (λ a b, a = tt → b = tt)
instance : decidable_rel bset2d.subset
:= forall2d2_inh_dec (λ a b, a = tt → b = tt)
instance : decidable_rel bset2d.disjoint
:= forall2d2_inh_dec (λ a b, a = tt → b = tt → false)


/-
Copyright (c) 2020 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Floris van Doorn
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.measure_theory.integration
import Mathlib.PostPort

universes u_1

namespace Mathlib

/-!
# Measures on Groups

We develop some properties of measures on (topological) groups

* We define properties on measures: left and right invariant measures.
* We define the measure `μ.inv : A ↦ μ(A⁻¹)` and show that it is right invariant iff
  `μ` is left invariant.
-/

namespace measure_theory

/-- A measure `μ` on a topological group is left invariant
  if the measure of left translations of a set are equal to the measure of the set itself.
  To left translate sets we use preimage under left multiplication,
  since preimages are nicer to work with than images. -/
def is_add_left_invariant {G : Type u_1} [measurable_space G] [Add G] (μ : set G → ennreal) :=
  ∀ (g : G) {A : set G} (h : is_measurable A), μ ((fun (h : G) => g + h) ⁻¹' A) = μ A

/-- A measure `μ` on a topological group is right invariant
  if the measure of right translations of a set are equal to the measure of the set itself.
  To right translate sets we use preimage under right multiplication,
  since preimages are nicer to work with than images. -/
def is_add_right_invariant {G : Type u_1} [measurable_space G] [Add G] (μ : set G → ennreal) :=
  ∀ (g : G) {A : set G} (h : is_measurable A), μ ((fun (h : G) => h + g) ⁻¹' A) = μ A

namespace measure

theorem map_mul_left_eq_self {G : Type u_1} [measurable_space G] [topological_space G] [Mul G]
    [has_continuous_mul G] [borel_space G] {μ : measure G} :
    (∀ (g : G), coe_fn (map (Mul.mul g)) μ = μ) ↔ is_mul_left_invariant ⇑μ :=
  sorry

theorem map_mul_right_eq_self {G : Type u_1} [measurable_space G] [topological_space G] [Mul G]
    [has_continuous_mul G] [borel_space G] {μ : measure G} :
    (∀ (g : G), coe_fn (map fun (h : G) => h * g) μ = μ) ↔ is_mul_right_invariant ⇑μ :=
  sorry

/-- The measure `A ↦ μ (A⁻¹)`, where `A⁻¹` is the pointwise inverse of `A`. -/
protected def neg {G : Type u_1} [measurable_space G] [Neg G] (μ : measure G) : measure G :=
  coe_fn (map Neg.neg) μ

theorem neg_apply {G : Type u_1} [measurable_space G] [add_group G] [topological_space G]
    [topological_add_group G] [borel_space G] (μ : measure G) {s : set G} (hs : is_measurable s) :
    coe_fn (measure.neg μ) s = coe_fn μ (-s) :=
  sorry

@[simp] protected theorem neg_neg {G : Type u_1} [measurable_space G] [add_group G]
    [topological_space G] [topological_add_group G] [borel_space G] (μ : measure G) :
    measure.neg (measure.neg μ) = μ :=
  sorry

theorem regular.neg {G : Type u_1} [measurable_space G] [add_group G] [topological_space G]
    [topological_add_group G] [borel_space G] {μ : measure G} [t2_space G] (hμ : regular μ) :
    regular (measure.neg μ) :=
  regular.map hμ (homeomorph.neg G)

end measure

@[simp] theorem regular_inv_iff {G : Type u_1} [measurable_space G] [group G] [topological_space G]
    [topological_group G] [borel_space G] {μ : measure G} [t2_space G] :
    measure.regular (measure.inv μ) ↔ measure.regular μ :=
  sorry

theorem is_add_left_invariant.neg {G : Type u_1} [measurable_space G] [add_group G]
    [topological_space G] [topological_add_group G] [borel_space G] {μ : measure G}
    (h : is_add_left_invariant ⇑μ) : is_add_right_invariant ⇑(measure.neg μ) :=
  sorry

theorem is_mul_right_invariant.inv {G : Type u_1} [measurable_space G] [group G]
    [topological_space G] [topological_group G] [borel_space G] {μ : measure G}
    (h : is_mul_right_invariant ⇑μ) : is_mul_left_invariant ⇑(measure.inv μ) :=
  sorry

@[simp] theorem is_mul_right_invariant_inv {G : Type u_1} [measurable_space G] [group G]
    [topological_space G] [topological_group G] [borel_space G] {μ : measure G} :
    is_mul_right_invariant ⇑(measure.inv μ) ↔ is_mul_left_invariant ⇑μ :=
  sorry

@[simp] theorem is_add_left_invariant_neg {G : Type u_1} [measurable_space G] [add_group G]
    [topological_space G] [topological_add_group G] [borel_space G] {μ : measure G} :
    is_add_left_invariant ⇑(measure.neg μ) ↔ is_add_right_invariant ⇑μ :=
  sorry

/-! Properties of regular left invariant measures -/

theorem is_mul_left_invariant.null_iff_empty {G : Type u_1} [measurable_space G]
    [topological_space G] [borel_space G] {μ : measure G} [group G] [topological_group G]
    (hμ : measure.regular μ) (h2μ : is_mul_left_invariant ⇑μ) (h3μ : μ ≠ 0) {s : set G}
    (hs : is_open s) : coe_fn μ s = 0 ↔ s = ∅ :=
  sorry

theorem is_mul_left_invariant.null_iff {G : Type u_1} [measurable_space G] [topological_space G]
    [borel_space G] {μ : measure G} [group G] [topological_group G] (hμ : measure.regular μ)
    (h2μ : is_mul_left_invariant ⇑μ) {s : set G} (hs : is_open s) :
    coe_fn μ s = 0 ↔ s = ∅ ∨ μ = 0 :=
  sorry

theorem is_mul_left_invariant.measure_ne_zero_iff_nonempty {G : Type u_1} [measurable_space G]
    [topological_space G] [borel_space G] {μ : measure G} [group G] [topological_group G]
    (hμ : measure.regular μ) (h2μ : is_mul_left_invariant ⇑μ) (h3μ : μ ≠ 0) {s : set G}
    (hs : is_open s) : coe_fn μ s ≠ 0 ↔ set.nonempty s :=
  sorry

/-- For nonzero regular left invariant measures, the integral of a continuous nonnegative function
  `f` is 0 iff `f` is 0. -/
-- @[to_additive] (fails for now)

theorem lintegral_eq_zero_of_is_mul_left_invariant {G : Type u_1} [measurable_space G]
    [topological_space G] [borel_space G] {μ : measure G} [group G] [topological_group G]
    (hμ : measure.regular μ) (h2μ : is_mul_left_invariant ⇑μ) (h3μ : μ ≠ 0) {f : G → ennreal}
    (hf : continuous f) : (lintegral μ fun (x : G) => f x) = 0 ↔ f = 0 :=
  sorry

end Mathlib


/-
Copyright (c) 2022 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import data.opposite
import data.set.image

/-!
# The opposite of a set

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

The opposite of a set `s` is simply the set obtained by taking the opposite of each member of `s`.
-/

variables {α : Type*}

open opposite

namespace set

/-- The opposite of a set `s` is the set obtained by taking the opposite of each member of `s`. -/
protected def op (s : set α) : set αᵒᵖ :=
unop ⁻¹' s

/-- The unop of a set `s` is the set obtained by taking the unop of each member of `s`. -/
protected def unop (s : set αᵒᵖ) : set α :=
op ⁻¹' s

@[simp] lemma mem_op {s : set α} {a : αᵒᵖ} : a ∈ s.op ↔ unop a ∈ s :=
iff.rfl

@[simp] lemma op_mem_op {s : set α} {a : α} : op a ∈ s.op ↔ a ∈ s :=
by rw [mem_op, unop_op]

@[simp] lemma mem_unop {s : set αᵒᵖ} {a : α} : a ∈ s.unop ↔ op a ∈ s :=
iff.rfl

@[simp] lemma unop_mem_unop {s : set αᵒᵖ} {a : αᵒᵖ} : unop a ∈ s.unop ↔ a ∈ s :=
by rw [mem_unop, op_unop]

@[simp] lemma op_unop (s : set α) : s.op.unop = s :=
ext (by simp only [mem_unop, op_mem_op, iff_self, implies_true_iff])

@[simp] lemma unop_op (s : set αᵒᵖ) : s.unop.op = s :=
ext (by simp only [mem_op, unop_mem_unop, iff_self, implies_true_iff])

/-- The members of the opposite of a set are in bijection with the members of the set itself. -/
@[simps] def op_equiv_self (s : set α) : s.op ≃ s :=
⟨λ x, ⟨unop x, x.2⟩, λ x, ⟨op x, x.2⟩, λ x, by simp, λ x, by simp⟩

/-- Taking opposites as an equivalence of powersets. -/
@[simps] def op_equiv : set α ≃ set αᵒᵖ :=
⟨set.op, set.unop, op_unop, unop_op⟩

@[simp] lemma singleton_op (x : α) : ({x} : set α).op = {op x} :=
ext $ λ y, by simpa only [mem_op, mem_singleton_iff] using unop_eq_iff_eq_op

@[simp] lemma singleton_unop (x : αᵒᵖ) : ({x} : set αᵒᵖ).unop = {unop x} :=
ext $ λ y, by simpa only [mem_unop, mem_singleton_iff] using op_eq_iff_eq_unop

@[simp] lemma singleton_op_unop (x : α) : ({op x} : set αᵒᵖ).unop = {x} :=
by simp only [singleton_unop, opposite.unop_op]

@[simp] lemma singleton_unop_op (x : αᵒᵖ) : ({unop x} : set α).op = {x} :=
by simp only [singleton_op, opposite.op_unop]

end set


-- Limite de la suma de dos sucesiones convergentes
-- ================================================

import data.real.basic

variables (u v : ℕ → ℝ)
variables (a b c : ℝ)

-- ----------------------------------------------------
-- Ejercicio 1. Definir la notación |x| para el valor
-- absoluto de x.
-- ----------------------------------------------------

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

-- ----------------------------------------------------
-- Ejercicio 2. Definir la función
--    limite : (ℕ → ℝ) → ℝ → Prop
-- tal que (limite u c) expresa que c es el límite de
-- la sucesión u.
-- ----------------------------------------------------

def limite : (ℕ → ℝ) → ℝ → Prop :=
λ u c, ∀ ε > 0, ∃ N, ∀ n ≥ N, |u n - c| ≤ ε

-- ----------------------------------------------------
-- Ejercicio 3. Demostrar que si a es el límite de la
-- sucesión u y b el de la v, entonces el límite de
-- (u + v) es (a + b).
-- ----------------------------------------------------

-- 1ª demostración
example
  (hu : limite u a)
  (hv : limite v b)
  : limite (u + v) (a + b) :=
begin
  intros ε hε,
  have hε2 : 0 < ε / 2,
    { linarith },
  cases hu (ε / 2) hε2 with Nu hNu,
  cases hv (ε / 2) hε2 with Nv hNv,
  clear hu hv hε2 hε,
  use max Nu Nv,
  intros n hn,
  have hn₁ : n ≥ Nu,
    { exact le_of_max_le_left hn },
  specialize hNu n hn₁,
  have hn₂ : n ≥ Nv,
    { exact le_of_max_le_right hn },
  specialize hNv n hn₂,
  clear hn hn₁ hn₂ Nu Nv,
  calc |(u + v) n - (a + b)|
       = |u n + v n - (a + b)|    : rfl
   ... = |(u n - a) + (v n -  b)| : by {congr, ring}
   ... ≤ |u n - a| + |v n -  b|   : by apply abs_add
   ... ≤ ε / 2 + ε / 2            : by linarith
   ... = ε                        : by apply add_halves,
end

-- 2ª demostración
-- ===============

lemma max_ge_iff
  {α : Type*}
  [linear_order α]
  {p q r : α}
  : r ≥ max p q  ↔ r ≥ p ∧ r ≥ q :=
max_le_iff

example
  (hu : limite u a)
  (hv : limite v b)
  : limite (u + v) (a + b) :=
begin
  intros ε hε,
  cases hu (ε/2) (by linarith) with Nu hNu,
  cases hv (ε/2) (by linarith) with Nv hNv,
  clear hu hv hε,
  use max Nu Nv,
  intros n hn,
  cases max_ge_iff.mp hn with hn₁ hn₂,
  have cota₁ : |u n - a| ≤ ε/2,
    from hNu n hn₁,
  have cota₂ : |v n - b| ≤ ε/2,
    from hNv n hn₂,
  calc |(u + v) n - (a + b)|
       = |u n + v n - (a + b)|   : rfl
   ... = |(u n - a) + (v n - b)| : by { congr, ring }
   ... ≤ |u n - a| + |v n - b|   : by apply abs_add
   ... ≤ ε                       : by linarith,
end

-- 3ª demostración
example
  (hu : limite u a)
  (hv : limite v b)
  : limite (u + v) (a + b) :=
begin
  intros ε hε,
  cases hu (ε/2) (by linarith) with Nu hNu,
  cases hv (ε/2) (by linarith) with Nv hNv,
  clear hu hv hε,
  use max Nu Nv,
  intros n hn,
  cases max_ge_iff.mp hn with hn₁ hn₂,
  calc |(u + v) n - (a + b)|
       = |u n + v n - (a + b)|   : rfl
   ... = |(u n - a) + (v n - b)| : by { congr, ring }
   ... ≤ |u n - a| + |v n - b|   : by apply abs_add
   ... ≤ ε/2 + ε/2               : add_le_add (hNu n hn₁) (hNv n hn₂)
   ... = ε                       : by simp
end

-- 4ª demostración
example
  (hu : limite u a)
  (hv : limite v b)
  : limite (u + v) (a + b) :=
begin
  intros ε hε,
  cases hu (ε/2) (by linarith) with Nu hNu,
  cases hv (ε/2) (by linarith) with Nv hNv,
  use max Nu Nv,
  intros n hn,
  rw max_ge_iff at hn,
  calc |(u + v) n - (a + b)|
       = |u n + v n - (a + b)|   : rfl
   ... = |(u n - a) + (v n - b)| : by { congr, ring }
   ... ≤ |u n - a| + |v n - b|   : by apply abs_add
   ... ≤ ε                       : by linarith [hNu n (by linarith), hNv n (by linarith)],
end


/-
Copyright (c) 2021 Alex J. Best. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Alex J. Best
-/
import data.set.pointwise.smul
import group_theory.group_action.pi

/-!
# Pointwise actions on sets in Pi types

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

This file contains lemmas about pointwise actions on sets in Pi types.

## Tags

set multiplication, set addition, pointwise addition, pointwise multiplication, pi

-/

open_locale pointwise
open set

variables {K ι : Type*} {R : ι → Type*}

@[to_additive]
lemma smul_pi_subset [∀ i, has_smul K (R i)] (r : K) (s : set ι) (t : Π i, set (R i)) :
  r • pi s t ⊆ pi s (r • t) :=
begin
  rintros x ⟨y, h, rfl⟩ i hi,
  exact smul_mem_smul_set (h i hi),
end

@[to_additive]
lemma smul_univ_pi [∀ i, has_smul K (R i)] (r : K) (t : Π i, set (R i)) :
  r • pi (univ : set ι) t = pi (univ : set ι) (r • t) :=
subset.antisymm (smul_pi_subset _ _ _) $ λ x h, begin
  refine ⟨λ i, classical.some (h i $ set.mem_univ _), λ i hi, _, funext $ λ i, _⟩,
  { exact (classical.some_spec (h i _)).left, },
  { exact (classical.some_spec (h i _)).right, },
end

@[to_additive]
lemma smul_pi [group K] [∀ i, mul_action K (R i)] (r : K) (S : set ι) (t : Π i, set (R i)) :
  r • S.pi t = S.pi (r • t) :=
subset.antisymm (smul_pi_subset _ _ _) $ λ x h,
  ⟨r⁻¹ • x, λ i hiS, mem_smul_set_iff_inv_smul_mem.mp (h i hiS), smul_inv_smul _ _⟩

lemma smul_pi₀ [group_with_zero K] [∀ i, mul_action K (R i)] {r : K} (S : set ι)
  (t : Π i, set (R i)) (hr : r ≠ 0) : r • S.pi t = S.pi (r • t) :=
smul_pi (units.mk0 r hr) S t


import set_theory.cardinal
import data.finset.basic
import data.stream.basic
import tactic
import cycles

open_locale classical
noncomputable theory

-- Definition 1: Prof V X represents the set of (V, X)-profiles
def Prof : Type → Type → Type := λ (V X : Type), V → X → X → Prop

-- Definition 2: Given a profile P and x, y ∈ X(P)
-- we say that x is majority preferred to y in P if
-- more voters rank x above y than rank y above x.
def majority_preferred {V X : Type} : Prof V X → X → X → Prop := λ P x y,
cardinal.mk {v : V // P v x y} > cardinal.mk {v : V // P v y x}

-- Definition 3: Given a profile P and x₁, x₂ ∈ X(P),
-- the margin of x₁ over x₂ in P, denoted Marginₚ(x₁, x₂), is
-- |{i ∈ V (P) | x₁Pᵢx₂}| − |{i ∈ V (P) | x₂Pᵢx₁}|.
def margin {V X : Type} [fintype V] : Prof V X → X → X → ℤ :=
    λ P x₁ x₂, ↑(finset.univ.filter (λ v, P v x₁ x₂)).card
    - ↑(finset.univ.filter (λ v, P v x₂ x₁)).card

-- The property of skew-symmetry takes in a function
-- and outputs the proposition stating that the
-- skew-symmetry equation holds for all pairs:
def skew_symmetric {X : Type} : (X → X → ℤ) → Prop :=
    λ M, ∀ x y, M x y = - M y x.

-- Proof that Marginₚ is skew-symmetric for any profile P.
lemma margin_skew_symmetric {V X : Type} (P : Prof V X) [fintype V] : skew_symmetric (margin P) :=
begin
    unfold margin,
    obviously,
end

-- We say that x is a majority winner in P if the number of voters
-- who rank x (and only x) in first place is greater than the number
-- of voters who do not rank x in first place.
def majority_winner {V X : Type} (P : Prof V X) (x : X) : Prop :=
    cardinal.mk {v : V // ∀ y ≠ x, P v x y} > cardinal.mk {v : V // ∃ y ≠ x, P v y x}

-- Definition 5: Let SCC be a function that assigns
-- to each pair (V, X) the set of all (V, X)-SCCs.
def SCC := λ (V X : Type), Prof V X → set X

def universal_domain_SCC {V X : Type} (F : SCC V X) : Prop :=
    ∀ P : Prof V X, F P ≠ ∅

-- Definition 6: A variable-election social choice correspondence (VSCC)
-- is a function F that assigns to each pair (V, X) a (V,X)-SCC.
def VSCC : Type 1 := Π (V X : Type), SCC V X.

def finite_universal_domain_VSCC (F : VSCC) : Prop :=
    ∀ V X [inhabited V] [inhabited X] [fintype V] [fintype X], universal_domain_SCC (F V X)

-- A collective choice rule for (V, X), or (V, X)-CCR, is
-- a function f : Prof(V, X) → B(X). Let CCR be a function
-- that assigns to each pair (V, X) of the set of all (V,X)-CCRs.
def CCR := λ (V X : Type), Prof V X → X → X → Prop

-- Definition 8: A variable-election collective choice rule (VCCR)
-- is a function that assigns to each pair (V, X) a (V, X)-CCR.
def VCCR := Π (V X : Type), CCR V X

-- Definition 9: Given an asymmetric VCCR F, we define the induced VSCC F*
-- such that for any V, X, and (V, X)-profile P, we have
-- F*(V,X)(P) = {x ∈ X(P) | ∀y ∈ X(P), (y, x) ∉ F(V, X)(P)}.
def max_el_VSCC : VCCR → VSCC := λ f V X P, {x | ∀ y : X, ¬ f V X P y x}

lemma max_el_VSCC_ineq {first second : ℕ} : first < second → (second - 1).succ = second := by omega
lemma max_el_VSCC_ineq2 (first second i : ℕ) : i < second - first - 1 → (first + (second - first - 1 - (i + 1))).succ = (first + (second - first - 1 - i)) := by omega

lemma false_of_sequence_acyclic_vccr {V X : Type} [fintype X] {F : VCCR} {P : Prof V X} (p₂ : acyclic (F V X P)) (seq : stream X) (property : ∀ x : ℕ, F V X P (seq.nth x.succ) (seq.nth x)) : false :=
begin
    have noninjective := not_injective_infinite_fintype seq,
    simp at noninjective,
    cases noninjective with first noninjective,
    cases noninjective with second noninjective,
    cases noninjective with noninjective ne,
    wlog h : first < second using [first second, second first],
    exact ne_iff_lt_or_gt.mp ne,
    let l := (list.drop first (stream.take seq second)).reverse,
    have not_acyclic : ¬ acyclic (F V X P),
    unfold acyclic,
    push_neg,
    use l,
    have e : l ≠ list.nil,
    refine list.ne_nil_of_length_pos _,
    rw list.length_reverse,
    rw list.length_drop,
    rw stream.length_take,
    omega,
    use e,
    refine list.chain_iff_nth_le.mpr _,
    split,
        {intro _,
        have eq1 : (l.last e) = seq second,
        rw ←noninjective,
        rw last_reverse,
        rw list.nth_le_drop',
        rw nth_take,
        rw nat.add_zero,
        rw nat.add_zero,
        exact h,
        have lengths_equal : (list.drop first (seq.take second)).length = l.length,
        rw list.length_reverse,
        rw lengths_equal,
        exact gt.lt h_1,
        rw eq1,
        have eq2 : (l.nth_le 0 h_1) = seq (second - 1),
        simp_rw [nth_reverse, list.length_drop, stream.length_take, list.nth_le_drop'],
        rw nth_take,
        congr,
        omega,
        omega,
        rw eq2,
        specialize property (second - 1),
        rw (max_el_VSCC_ineq h) at property,
        exact property,},

        {intro i, intro i_bound,
        simp_rw [nth_reverse, list.length_drop, stream.length_take, list.nth_le_drop'],
        rw nth_take, rw nth_take,
        specialize property (first + (second - first - 1 - (i + 1))),
        simp_rw [list.length_reverse, list.length_drop, stream.length_take] at i_bound,
        rw (max_el_VSCC_ineq2 first second i i_bound) at property,
        exact property,
        omega, omega,
        },

    exact not_acyclic p₂,
end

def max_el_VSCC_defined {V X : Type} (F : VCCR) (P : Prof V X) (p₁ : acyclic (F V X P)) [fintype X] [inhabited X] : ((max_el_VSCC F) V X P).nonempty :=
begin
    change ∃ x, ∀ (y : X), ¬ F V X P y x,
    by_contradiction,
    push_neg at h,
    let seq := stream.iterate (λ b, classical.some (h b)) _inst_2.default,
    have property : ∀ x : ℕ, F V X P (seq.nth x.succ) (seq.nth x),
    intro x,
    specialize h (seq.nth x),
    change F V X P (classical.some h) (seq.nth x),
    exact classical.some_spec h,
    exact false_of_sequence_acyclic_vccr p₁ seq property,
end

-- Any acyclic VCCR induces a VSCC satisfying (finite) universal domain
theorem max_el_VSCC_universal_domain (F : VCCR)
(a : ∀ V X [inhabited V] [inhabited X] [fintype V] [fintype X] (P : Prof V X), acyclic (F V X P)) :
finite_universal_domain_VSCC (max_el_VSCC F) :=
begin
    intros V' X',
    introsI _inst_1' _inst_2' _inst_3' _inst_4',
    specialize a V', specialize a X',
    intro P,
    specialize a P,
    rw set.ne_empty_iff_nonempty,
    exact max_el_VSCC_defined F P a,
end

variables {V X : Type}

def asymmetric (Q : X → X → Prop) : Prop := ∀ x y, Q x y → ¬ Q y x

class profile_irreflexive {V X : Type} (P : Prof V X) := (irreflexive: ∀ v, irreflexive (P v))
class profile_asymmetric {V X : Type} (P : Prof V X) := (asymmetric: ∀ v, asymmetric (P v))
class profile_transitive {V X : Type} (P : Prof V X) := (transitive: ∀ v, transitive (P v))
class profile_trichotomous {V X : Type} (P : Prof V X) := (trichotomous: ∀ v a b, P v a b ∨ a = b ∨ P v b a)

def irreflexive_of_asymmetric (P : Prof V X) [profile_asymmetric P] : profile_irreflexive P :=
begin
    split,
    intros v x,
    have p := _inst_1.asymmetric,
    specialize p v,
    specialize p x, specialize p x,
    by_cases P v x x,
    exact p h,
    exact h,
end

def antisymmetric : (X → X → ℤ) → Prop := λ P, ∀ x y, P x y = - P y x

lemma margin_antisymmetric (P : Prof V X) [fintype V] : antisymmetric (margin P) :=
begin
    unfold margin,
    obviously,
end

def margin_pos [fintype V] : Prof V X → X → X → Prop := λ P x y, 0 < (margin P) x y

lemma margin_eq_margin (x y : fintype V) : ∀ P (a b : X), @margin V X x P a b = @margin V X y P a b :=
begin
    intros P a b,
    congr,
end

lemma self_margin_zero [fintype V] (x : X) (P : Prof V X): margin P x x = 0 :=
begin
    unfold margin,
    simp,
end

lemma ne_of_margin_pos [fintype V] {P : Prof V X} {x y : X} (a : margin_pos P x y) : x ≠ y :=
begin
    by_contradiction,
    push_neg at h,
    rw h at a,
    unfold margin_pos at a,
    rw self_margin_zero at a,
    exact int.lt_irrefl 0 a,
end


/-
This file contains a formal computer proof of the linear convergence of gradient descent with
constant stepsize for strongly-convex functions defined on a real inner product.
It is self-contained , other than using properties of real, natural numbers, and real inner
product spaces defined in mathlib. I define the properties of strong convexity and
Lipschitz continuous gradient here and do not rely on the mathlib-defined gradient.

We closely follow the proof of
http://www.seas.ucla.edu/~vandenbe/236C/lectures/gradient.pdf
-/

import data.real.basic
import analysis.inner_product_space.pi_L2

-- H is an inner product space over the reals
-- It is an abstract type but [inner_product_space ℝ H]
-- ensures it satisfies all the properties of a real inner product space.
-- It is interesting to note that we do not need H
-- to be a Hilbert space. This is because, for
-- nonasymptotic analyses, we do not need the space
-- to be complete as we do not take limits anywhere.
variables {H : Type*} [inner_product_space ℝ H]

-- inner product notation, i.e. scalar product.
notation `⟪`x`, `y`⟫` := @inner ℝ _ _ x y

-- method under study. Note that • means "scalar multiply"
-- i.e. a scalar times a vector.
-- it is written "backslash+smul"
noncomputable def grad_descent
            (η : ℝ) (x0 : H) (gradf : H → H): (ℕ → H)
| 0      := x0
| (n+1)  := grad_descent(n) - η•gradf(grad_descent(n))

/- I take the below to be the **definition** of strongly convex + Lipschitz continuous
   functions. This property can be derived fairly easily.
-/
def is_strongconvex_LipGrad (gradf : H → H) (m L : ℝ): Prop :=
  ∀ (x y : H),  ⟪gradf(x)-gradf(y),x-y⟫ ≥
               (m*L/(m+L))*∥x-y∥^2 + (1/(m+L))*∥gradf(x)-gradf(y)∥^2

-- a one-step contraction lemma
lemma strongconvex_onestep (η m L : ℝ) (x xstar : H) (gradf : H → H)
                        (hη_low : η ≥ 0)
                        (hη_up : η ≤ 2/(m+L))
                        (h_strongconv_lip : is_strongconvex_LipGrad gradf m L)
                        (hxstar : gradf xstar = 0)
                        :
      ∥x - η•gradf x - xstar∥^2
      ≤ (1-2*η*m*L/(m+L))*∥x - xstar∥^2
   :=
begin
  have h : x - η • gradf x - xstar = x - xstar  - η • gradf x
      := by abel,
  rw h,clear h,

  rw norm_sub_sq_real,

  have h : inner (x - xstar) (η • gradf x) = η*inner (x - xstar) (gradf x)
    := inner_smul_real_right,
  rw h, clear h,

  let h := h_strongconv_lip x xstar,
  rw hxstar at h,
  rw sub_zero at h,

  have h2 : (2:ℝ)≥0 := by norm_num,
  have h0 : 2*η ≥ 0 := mul_nonneg h2 hη_low,

  have h1 : (2*η)*(inner (gradf x) (x - xstar))
            ≥ (2*η)*(m * L / (m + L) * ∥x - xstar∥ ^ 2
                    + 1 / (m + L) * ∥gradf x∥ ^ 2)
          := mul_le_mul_of_nonneg_left h h0,
  clear h,

  have h1_0 : (2*η)*(m * L / (m + L) * ∥x - xstar∥ ^ 2
                    + 1 / (m + L) * ∥gradf x∥ ^ 2)
              = (2*η)*m * L / (m + L) * ∥x - xstar∥ ^ 2
                    + (2*η)*1 / (m + L) * ∥gradf x∥ ^ 2
            := by ring,
  rw h1_0 at h1,
  clear h1_0,

  have h :  0 ≥ -2 * η * inner (gradf x) (x - xstar)
            +2 * η * m * L / (m + L) * ∥x - xstar∥ ^ 2
            + 2 * η * 1 / (m + L) * ∥gradf x∥ ^ 2
         := by linarith,

  clear h1,

  have h1 :  ∥x - xstar∥ ^ 2  ≥ -2 * η * inner (gradf x) (x - xstar)
            +2 * η * m * L / (m + L) * ∥x - xstar∥ ^ 2
            + 2 * η * 1 / (m + L) * ∥gradf x∥ ^ 2
            + ∥x - xstar∥ ^ 2
         := by linarith,

  clear h,
  have h :  ∥x - xstar∥ ^ 2  - 2 * η * m * L / (m + L) * ∥x - xstar∥ ^ 2
            ≥ -2 * η * inner (gradf x) (x - xstar)
            + 2 * η * 1 / (m + L) * ∥gradf x∥ ^ 2
            + ∥x - xstar∥ ^ 2
         := by linarith,
  clear h1,

  have h1 : -2 * η * inner (gradf x) (x - xstar)
            + 2 * η * 1 / (m + L) * ∥gradf x∥ ^ 2
            + ∥x - xstar∥ ^ 2
            ≤
            ∥x - xstar∥ ^ 2  - 2 * η * m * L / (m + L) * ∥x - xstar∥ ^ 2
         := by linarith,
  clear h,

   rw sub_mul,
   rw one_mul,

   have h : 2 * η * 1 / (m + L) = η * 2 / (m + L)
      := by ring,
   rw h at h1, clear h,

   have h : η*η ≤ η * (2 / (m + L)) := mul_le_mul_of_nonneg_left hη_up hη_low,

   have hgrad : ∥gradf x∥ ^ 2 ≥ 0 := sq_nonneg (∥gradf x∥),
   have h_2 : η*η*∥gradf x∥ ^ 2 ≤ η * (2 / (m + L))*∥gradf x∥ ^ 2
      := mul_mono_nonneg hgrad h,
   have h' :  η * (2 / (m + L)) = η * 2 / (m + L) := by ring,
   rw h' at h_2,

   clear h, clear hgrad, clear h',

   have h : (-2) * η * inner (gradf x) (x - xstar)
            + η * η * ∥gradf x∥ ^ 2
            + ∥x - xstar∥ ^ 2
            ≤ ∥x - xstar∥ ^ 2 - 2 * η * m * L / (m + L) * ∥x - xstar∥ ^ 2
         := by linarith,
   clear h_2, clear h1,

   rw ← sq η at h,
   rw ← mul_pow η _ at h,
   have h1 :|η|= η := abs_of_nonneg hη_low,
   have h_2 : (η * ∥gradf x∥) ^ 2 = (|η| * ∥gradf x∥) ^ 2,
   {
      rw h1,
   },
   clear h1,
   have h1 : ∥η•gradf x∥ = |η| * ∥gradf x∥,
   {exact norm_smul η (gradf x),},
   rw ← h1 at h_2,
   rw h_2 at h,
   clear h1, clear h_2,

   have h1 : (-2) * η * inner (gradf x) (x - xstar)
            =
            - 2 * (η * inner (gradf x) (x - xstar))
         := by ring,

   rw h1 at h,
   clear h1,
   rw real_inner_comm at h,
   linarith,

end

theorem strongconvex_grad_descent_conv_rate (n : ℕ) (η m L : ℝ) (x0 xstar : H)
                        (gradf : H → H)
                        (hη_low : η ≥ 0)
                        (hη_up : η ≤ 2/(m+L))
                        (hm : m ≥ 0)
                        (hL : L ≥ 0)
                        (hmL : m + L ≠ 0)
                        (h_strongconv_lip : is_strongconvex_LipGrad gradf m L)
                        (hxstar : gradf xstar = 0)
                        :
      ∥(grad_descent η x0 gradf n) - xstar∥^2
      ≤ ((1-2*η*m*L/(m+L))^n)*∥x0 - xstar∥^2
   :=
begin
   induction n with k hk,
   have h0 : grad_descent η x0 gradf 0 = x0 := by refl,
   rw h0,
   rw pow_zero,
   rw one_mul,

   have h1 : grad_descent η x0 gradf (k+1)
            = grad_descent η x0 gradf k - η•gradf(grad_descent η x0 gradf k),
   {
      simp [grad_descent],
   },
   rw h1,clear h1,
   have h := strongconvex_onestep η m L
                                 (grad_descent η x0 gradf k)
                                 xstar gradf hη_low hη_up h_strongconv_lip hxstar,
   rw pow_succ _ k,

   have h1 : L*η ≤ L*(2 / (m+L)) := mul_le_mul_of_nonneg_left hη_up hL,
   have h2 : m*(L*η) ≤ m*(L*(2 / (m+L))) := mul_le_mul_of_nonneg_left h1 hm,
   clear h1,
   have h1 : (2:ℝ)≥ 0 := by norm_num,
   have h3 : 2*(m*(L*η)) ≤ 2*(m*(L*(2 / (m+L)))) := mul_le_mul_of_nonneg_left h2 h1,
   clear h1, clear h2,
   have h1 : (m+L) ≥ 0 := by linarith,

   have h2 : 2*(m*(L*η))/(m+L) ≤ 2*(m*(L*(2 / (m+L))))/(m+L) := div_le_div_of_le h1 h3,
   clear h3, clear h1,
   have h3 : 2*(m*(L*η))/(m+L) = 2* m* L*η /(m+L) := by ring,
   rw h3 at h2, clear h3,
   have h3 : 2*(m*(L*(2 / (m+L))))/(m+L)  = 2*m*L* 2 /(m+L)/(m+L) := by ring,
   rw h3 at h2, clear h3,
   have h3 : 2*m*L* 2 /(m+L)/(m+L) = 4*m*L /(m+L)/(m+L) := by ring,
   rw h3 at h2, clear h3,

   have h3 : 4*m*L /(m+L)/(m+L) = 4*m*L /((m+L)*(m+L))
      := div_div_eq_div_mul (4 * m * L) (m + L) (m + L),
   rw h3 at h2, clear h3,

   have h3 : 4*m*L /((m+L)*(m+L)) = 4*m*L /((m+L)^2),
      rw ← pow_two,
   rw h3 at h2, clear h3,
   have h3 : 1 - 2 * m * L * η / (m + L) ≥ 1 -  4 * m * L / (m + L) ^ 2
      := by linarith,
   clear h2,

   have h2 : (m + L) ^ 2 ≠ 0 := pow_ne_zero 2 hmL,
   have h2_2 : 1 = (m + L) ^ 2 / (m + L) ^ 2 := by exact (div_self h2).symm,

   have h2_3 : 1 -  4 * m * L / (m + L) ^ 2 =
               (m + L) ^ 2 / (m + L) ^ 2 -  4 * m * L / (m + L) ^ 2
     := by linarith,
   rw h2_3 at h3,
   clear h2, clear h2_2, clear h2_3,

   have h2 : (m + L) ^ 2 / (m + L) ^ 2 -  4 * m * L / (m + L) ^ 2
          = ((m + L) ^ 2 -  4 * m * L) / (m + L) ^ 2
          := div_sub_div_same ((m + L) ^ 2) (4 * m * L) ((m + L) ^ 2),

   rw h2 at h3, clear h2,
   have h2 : (m + L) ^ 2 -  4 * m * L = m^2 + 2*m*L+L^2 - 4*m*L := by ring,
   rw h2 at h3, clear h2,

   have h2 : m^2 + 2*m*L+L^2 - 4*m*L  = m^2 - 2*m*L + L^2 := by ring,

   rw h2 at h3, clear h2,

   have h2 : m^2 - 2*m*L + L^2 = (m-L)^2 := by ring,

   rw h2 at h3, clear h2,

   have h2 : (m - L) ^ 2 / (m + L) ^ 2 = ((m - L) / (m + L) )^ 2
      := by exact (div_pow (m - L) (m + L) 2).symm,

   rw h2 at h3, clear h2,

   have h2 : ((m - L) / (m + L) )^ 2 ≥ 0 := sq_nonneg ((m - L) / (m + L)),

   have h4 : 1 - 2 * m * L * η / (m + L) ≥ 0 := by linarith,
   clear h2, clear h3,

   have h4_1 : 1 - 2 * m * L * η / (m + L)
          = 1 - 2 * η * m * L / (m + L) := by ring,

   rw h4_1 at h4, clear h4_1,

   have h5 : (1 - 2 * η * m * L / (m + L)) * ∥grad_descent η x0 gradf k - xstar∥^ 2
    ≤ (1 - 2 * η * m * L / (m + L))*((1 - 2 * η * m * L / (m + L)) ^ k * ∥x0 - xstar∥ ^ 2)
    := by exact mul_le_mul_of_nonneg_left hk h4,
    clear h4,

   have h5_1 : (1 - 2 * η * m * L / (m + L))*((1 - 2 * η * m * L / (m + L)) ^ k * ∥x0 - xstar∥^ 2)
      =
      (1 - 2 * η * m * L / (m + L))*(1 - 2 * η * m * L / (m + L)) ^ k * ∥x0 - xstar∥^ 2
      := by ring,
   rw h5_1 at h5,
   clear h5_1,

   linarith,

end


import Mathlib.Data.Rat.Order
import Mathlib.Tactic.Ring

/- 4 points -/
theorem problem1 {x : ℚ} (hx : x = 2/3) : 3 * x ≠ 1 := by
  apply ne_of_gt
  calc 3 * x = 3 * (2 / 3) := by rw [hx]
  _ > 1 := by rfl

/- 5 points -/
theorem problem2 {x y : ℚ} (h : x = 1 ∨ y = -1) :
    x * y + x = y + 1 := by
  cases' h with hx hy
  . calc x * y + x = 1 * y + 1 := by rw [hx]
    _ = y + 1 := by ring
  . calc x * y + x = x * -1 + x := by rw [hy]
    _ = -1 + 1 := by ring
    _ = y + 1 := by rw [hy]

/- 1 point -/
theorem problem3 {x : ℚ} (hx : 2 * x + 1 = 5) :
    2 * x = 2 ∨ 2 * x = 4 := by
  right
  calc 2 * x = (2 * x + 1) - 1 := by ring
  _ = 5 - 1 := by rw [hx]
  _ = 4 := by norm_num


/-
Copyright (c) 2021 OpenAI. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kunhao Zheng, Stanislas Polu, David Renshaw, OpenAI GPT-f
-/
import mathzoo.imports.miniF2F

open_locale nat rat real big_operators topological_space

theorem mathd_algebra_346
  (f g : ℝ → ℝ)
  (h₀ : ∀ x, f x = 2 * x - 3)
  (h₁ : ∀ x, g x = x + 1) :
  g (f 5 - 1) = 7 :=
begin
  rw [h₀, h₁],
  norm_num,
end


/-
Copyright (c) 2020 Heather Macbeth. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Heather Macbeth
-/
import data.set.intervals.basic
import data.set.function

/-!
# Monotone surjective functions are surjective on intervals

A monotone surjective function sends any interval in the domain onto the interval with corresponding
endpoints in the range.  This is expressed in this file using `set.surj_on`, and provided for all
permutations of interval endpoints.
-/

variables {α : Type*} {β : Type*} [linear_order α] [partial_order β] {f : α → β}

open set function order_dual (to_dual)

lemma surj_on_Ioo_of_monotone_surjective
  (h_mono : monotone f) (h_surj : function.surjective f) (a b : α) :
  surj_on f (Ioo a b) (Ioo (f a) (f b)) :=
begin
  classical,
  intros p hp,
  rcases h_surj p with ⟨x, rfl⟩,
  refine ⟨x, mem_Ioo.2 _, rfl⟩,
  by_contra h,
  cases not_and_distrib.mp h with ha hb,
  { exact has_lt.lt.false (lt_of_lt_of_le hp.1 (h_mono (not_lt.mp ha))) },
  { exact has_lt.lt.false (lt_of_le_of_lt (h_mono (not_lt.mp hb)) hp.2) }
end

lemma surj_on_Ico_of_monotone_surjective
  (h_mono : monotone f) (h_surj : function.surjective f) (a b : α) :
  surj_on f (Ico a b) (Ico (f a) (f b)) :=
begin
  obtain hab | hab := lt_or_le a b,
  { intros p hp,
    rcases mem_Ioo_or_eq_left_of_mem_Ico hp with hp'|hp',
    { rw hp',
      exact ⟨a, left_mem_Ico.mpr hab, rfl⟩ },
    { have := surj_on_Ioo_of_monotone_surjective h_mono h_surj a b hp',
      cases this with x hx,
      exact ⟨x, Ioo_subset_Ico_self hx.1, hx.2⟩ } },
  { rw Ico_eq_empty (h_mono hab).not_lt,
    exact surj_on_empty f _ }
end

lemma surj_on_Ioc_of_monotone_surjective
  (h_mono : monotone f) (h_surj : function.surjective f) (a b : α) :
  surj_on f (Ioc a b) (Ioc (f a) (f b)) :=
by simpa using surj_on_Ico_of_monotone_surjective h_mono.dual h_surj (to_dual b) (to_dual a)

-- to see that the hypothesis `a ≤ b` is necessary, consider a constant function
lemma surj_on_Icc_of_monotone_surjective
  (h_mono : monotone f) (h_surj : function.surjective f) {a b : α} (hab : a ≤ b) :
  surj_on f (Icc a b) (Icc (f a) (f b)) :=
begin
  rcases lt_or_eq_of_le hab with hab|hab,
  { intros p hp,
    rcases mem_Ioo_or_eq_endpoints_of_mem_Icc hp with hp'|⟨hp'|hp'⟩,
    { rw hp',
      refine ⟨a, left_mem_Icc.mpr (le_of_lt hab), rfl⟩ },
    { rw hp',
      refine ⟨b, right_mem_Icc.mpr (le_of_lt hab), rfl⟩ },
    { have := surj_on_Ioo_of_monotone_surjective h_mono h_surj a b hp',
      cases this with x hx,
      exact ⟨x, Ioo_subset_Icc_self hx.1, hx.2⟩ } },
  { simp only [hab, Icc_self],
    intros _ hp,
    exact ⟨b, mem_singleton _, (mem_singleton_iff.mp hp).symm⟩ }
end

lemma surj_on_Ioi_of_monotone_surjective
  (h_mono : monotone f) (h_surj : function.surjective f) (a : α) :
  surj_on f (Ioi a) (Ioi (f a)) :=
begin
  classical,
  intros p hp,
  rcases h_surj p with ⟨x, rfl⟩,
  refine ⟨x, _, rfl⟩,
  simp only [mem_Ioi],
  by_contra h,
  exact has_lt.lt.false (lt_of_lt_of_le hp (h_mono (not_lt.mp h)))
end

lemma surj_on_Iio_of_monotone_surjective
  (h_mono : monotone f) (h_surj : function.surjective f) (a : α) :
  surj_on f (Iio a) (Iio (f a)) :=
@surj_on_Ioi_of_monotone_surjective _ _ _ _ _ h_mono.dual h_surj a

lemma surj_on_Ici_of_monotone_surjective
  (h_mono : monotone f) (h_surj : function.surjective f) (a : α) :
  surj_on f (Ici a) (Ici (f a)) :=
begin
  intros p hp,
  rw [mem_Ici, le_iff_lt_or_eq] at hp,
  rcases hp with hp'|hp',
  { cases (surj_on_Ioi_of_monotone_surjective h_mono h_surj a hp') with x hx,
    exact ⟨x, Ioi_subset_Ici_self hx.1, hx.2⟩ },
  { rw ← hp',
    refine ⟨a, left_mem_Ici, rfl⟩ }
end

lemma surj_on_Iic_of_monotone_surjective
  (h_mono : monotone f) (h_surj : function.surjective f) (a : α) :
  surj_on f (Iic a) (Iic (f a)) :=
@surj_on_Ici_of_monotone_surjective _ _ _ _ _ h_mono.dual h_surj a