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

Direct sum of abelian groups, indexed by a discrete type.
-/

import data.dfinsupp

universes u v w u₁

variables (ι : Type v) [decidable_eq ι] (β : ι → Type w) [Π i, add_comm_group (β i)]

def direct_sum : Type* := Π₀ i, β i

namespace direct_sum

variables {ι β}

instance : add_comm_group (direct_sum ι β) :=
dfinsupp.add_comm_group

variables β
def mk : Π s : finset ι, (Π i : (↑s : set ι), β i.1) → direct_sum ι β :=
dfinsupp.mk

def of : Π i : ι, β i → direct_sum ι β :=
dfinsupp.single
variables {β}

instance mk.is_add_group_hom (s : finset ι) : is_add_group_hom (mk β s) :=
⟨λ _ _, dfinsupp.mk_add⟩

@[simp] lemma mk_zero (s : finset ι) : mk β s 0 = 0 :=
is_add_group_hom.zero _

@[simp] lemma mk_add (s : finset ι) (x y) : mk β s (x + y) = mk β s x + mk β s y :=
is_add_group_hom.add _ x y

@[simp] lemma mk_neg (s : finset ι) (x) : mk β s (-x) = -mk β s x :=
is_add_group_hom.neg _ x

@[simp] lemma mk_sub (s : finset ι) (x y) : mk β s (x - y) = mk β s x - mk β s y :=
is_add_group_hom.sub _ x y

instance of.is_add_group_hom (i : ι) : is_add_group_hom (of β i) :=
⟨λ _ _, dfinsupp.single_add⟩

@[simp] lemma of_zero (i : ι) : of β i 0 = 0 :=
is_add_group_hom.zero _

@[simp] lemma of_add (i : ι) (x y) : of β i (x + y) = of β i x + of β i y :=
is_add_group_hom.add _ x y

@[simp] lemma of_neg (i : ι) (x) : of β i (-x) = -of β i x :=
is_add_group_hom.neg _ x

@[simp] lemma of_sub (i : ι) (x y) : of β i (x - y) = of β i x - of β i y :=
is_add_group_hom.sub _ x y

theorem mk_inj (s : finset ι) : function.injective (mk β s) :=
dfinsupp.mk_inj s

theorem of_inj (i : ι) : function.injective (of β i) :=
λ x y H, congr_fun (mk_inj _ H) ⟨i, by simp [finset.to_set]⟩

@[elab_as_eliminator]
protected theorem induction_on {C : direct_sum ι β → Prop}
  (x : direct_sum ι β) (H_zero : C 0)
  (H_basic : ∀ (i : ι) (x : β i), C (of β i x))
  (H_plus : ∀ x y, C x → C y → C (x + y)) : C x :=
begin
  apply dfinsupp.induction x H_zero,
  intros i b f h1 h2 ih,
  solve_by_elim
end

variables {γ : Type u₁} [add_comm_group γ]
variables (φ : Π i, β i → γ) [Π i, is_add_group_hom (φ i)]

variables (φ)
def to_group (f : direct_sum ι β) : γ :=
quotient.lift_on f (λ x, x.2.to_finset.sum $ λ i, φ i (x.1 i)) $ λ x y H,
begin
  have H1 : x.2.to_finset ∩ y.2.to_finset ⊆ x.2.to_finset, from finset.inter_subset_left _ _,
  have H2 : x.2.to_finset ∩ y.2.to_finset ⊆ y.2.to_finset, from finset.inter_subset_right _ _,
  refine (finset.sum_subset H1 _).symm.trans ((finset.sum_congr rfl _).trans (finset.sum_subset H2 _)),
  { intros i H1 H2, rw finset.mem_inter at H2, rw H i,
    simp only [multiset.mem_to_finset] at H1 H2,
    rw [(y.3 i).resolve_left (mt (and.intro H1) H2), is_add_group_hom.zero (φ i)] },
  { intros i H1, rw H i },
  { intros i H1 H2, rw finset.mem_inter at H2, rw ← H i,
    simp only [multiset.mem_to_finset] at H1 H2,
    rw [(x.3 i).resolve_left (mt (λ H3, and.intro H3 H1) H2), is_add_group_hom.zero (φ i)] }
end
variables {φ}

instance to_group.is_add_group_hom : is_add_group_hom (to_group φ) :=
begin
  constructor, intros f g,
  refine quotient.induction_on f (λ x, _),
  refine quotient.induction_on g (λ y, _),
  change finset.sum _ _ = finset.sum _ _ + finset.sum _ _,
  simp only, conv { to_lhs, congr, skip, funext, rw is_add_group_hom.add (φ i) },
  simp only [finset.sum_add_distrib],
  congr' 1,
  { refine (finset.sum_subset _ _).symm,
    { intro i, simp only [multiset.mem_to_finset, multiset.mem_add], exact or.inl },
    { intros i H1 H2, simp only [multiset.mem_to_finset, multiset.mem_add] at H2,
      rw [(x.3 i).resolve_left H2, is_add_group_hom.zero (φ i)] } },
  { refine (finset.sum_subset _ _).symm,
    { intro i, simp only [multiset.mem_to_finset, multiset.mem_add], exact or.inr },
    { intros i H1 H2, simp only [multiset.mem_to_finset, multiset.mem_add] at H2,
      rw [(y.3 i).resolve_left H2, is_add_group_hom.zero (φ i)] } }
end

variables (φ)
@[simp] lemma to_group_zero : to_group φ 0 = 0 :=
is_add_group_hom.zero _

@[simp] lemma to_group_add (x y) : to_group φ (x + y) = to_group φ x + to_group φ y :=
is_add_group_hom.add _ x y

@[simp] lemma to_group_neg (x) : to_group φ (-x) = -to_group φ x :=
is_add_group_hom.neg _ x

@[simp] lemma to_group_sub (x y) : to_group φ (x - y) = to_group φ x - to_group φ y :=
is_add_group_hom.sub _ x y

@[simp] lemma to_group_of (i) (x : β i) : to_group φ (of β i x) = φ i x :=
(add_zero _).trans $ congr_arg (φ i) $ show (if H : i ∈ finset.singleton i then x else 0) = x,
from dif_pos $ finset.mem_singleton_self i

variables (ψ : direct_sum ι β → γ) [is_add_group_hom ψ]

theorem to_group.unique (f : direct_sum ι β) : ψ f = to_group (λ i, ψ ∘ of β i) f :=
direct_sum.induction_on f
  (by rw [is_add_group_hom.zero ψ, is_add_group_hom.zero (to_group (λ i, ψ ∘ of β i))])
  (λ i x, by rw [to_group_of])
  (λ x y ihx ihy, by rw [is_add_group_hom.add ψ, is_add_group_hom.add (to_group (λ i, ψ ∘ of β i)), ihx, ihy])

variables (β)
def set_to_set (S T : set ι) (H : S ⊆ T) :
  direct_sum S (β ∘ subtype.val) → direct_sum T (β ∘ subtype.val) :=
to_group $ λ i, of (β ∘ @subtype.val _ T) ⟨i.1, H i.2⟩
variables {β}

instance (S T : set ι) (H : S ⊆ T) : is_add_group_hom (set_to_set β S T H) :=
to_group.is_add_group_hom

protected def id (M : Type v) [add_comm_group M] : direct_sum punit (λ _, M) ≃ M :=
{ to_fun := direct_sum.to_group (λ _, id),
  inv_fun := of (λ _, M) punit.star,
  left_inv := λ x, direct_sum.induction_on x
    (by rw [to_group_zero, of_zero])
    (λ ⟨⟩ x, by rw [to_group_of]; refl)
    (λ x y ihx ihy, by rw [to_group_add, of_add, ihx, ihy]),
  right_inv := λ x, to_group_of _ _ _ }

instance : has_coe_to_fun (direct_sum ι β) :=
dfinsupp.has_coe_to_fun

end direct_sum


/-
Copyright (c) 2018 Chris Hughes. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Chris Hughes

! This file was ported from Lean 3 source module group_theory.group_action.basic
! leanprover-community/mathlib commit a11f9106a169dd302a285019e5165f8ab32ff433
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Fintype.Card
import Mathbin.GroupTheory.GroupAction.Defs
import Mathbin.GroupTheory.GroupAction.Group
import Mathbin.Data.Setoid.Basic
import Mathbin.Data.Set.Pointwise.Smul
import Mathbin.GroupTheory.Subgroup.Basic

/-!
# Basic properties of group actions

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

This file primarily concerns itself with orbits, stabilizers, and other objects defined in terms of
actions. Despite this file being called `basic`, low-level helper lemmas for algebraic manipulation
of `•` belong elsewhere.

## Main definitions

* `mul_action.orbit`
* `mul_action.fixed_points`
* `mul_action.fixed_by`
* `mul_action.stabilizer`

-/

universe u v w

variable {α : Type u} {β : Type v} {γ : Type w}

open Pointwise

open Function

namespace MulAction

variable (α) [Monoid α] [MulAction α β]

#print MulAction.orbit /-
/-- The orbit of an element under an action. -/
@[to_additive "The orbit of an element under an action."]
def orbit (b : β) :=
  Set.range fun x : α => x • b
#align mul_action.orbit MulAction.orbit
#align add_action.orbit AddAction.orbit
-/

variable {α}

#print MulAction.mem_orbit_iff /-
@[to_additive]
theorem mem_orbit_iff {b₁ b₂ : β} : b₂ ∈ orbit α b₁ ↔ ∃ x : α, x • b₁ = b₂ :=
  Iff.rfl
#align mul_action.mem_orbit_iff MulAction.mem_orbit_iff
#align add_action.mem_orbit_iff AddAction.mem_orbit_iff
-/

#print MulAction.mem_orbit /-
@[simp, to_additive]
theorem mem_orbit (b : β) (x : α) : x • b ∈ orbit α b :=
  ⟨x, rfl⟩
#align mul_action.mem_orbit MulAction.mem_orbit
#align add_action.mem_orbit AddAction.mem_orbit
-/

#print MulAction.mem_orbit_self /-
@[simp, to_additive]
theorem mem_orbit_self (b : β) : b ∈ orbit α b :=
  ⟨1, by simp [MulAction.one_smul]⟩
#align mul_action.mem_orbit_self MulAction.mem_orbit_self
#align add_action.mem_orbit_self AddAction.mem_orbit_self
-/

#print MulAction.orbit_nonempty /-
@[to_additive]
theorem orbit_nonempty (b : β) : Set.Nonempty (orbit α b) :=
  Set.range_nonempty _
#align mul_action.orbit_nonempty MulAction.orbit_nonempty
#align add_action.orbit_nonempty AddAction.orbit_nonempty
-/

#print MulAction.mapsTo_smul_orbit /-
@[to_additive]
theorem mapsTo_smul_orbit (a : α) (b : β) : Set.MapsTo ((· • ·) a) (orbit α b) (orbit α b) :=
  Set.range_subset_iff.2 fun a' => ⟨a * a', mul_smul _ _ _⟩
#align mul_action.maps_to_smul_orbit MulAction.mapsTo_smul_orbit
#align add_action.maps_to_vadd_orbit AddAction.mapsTo_vadd_orbit
-/

#print MulAction.smul_orbit_subset /-
@[to_additive]
theorem smul_orbit_subset (a : α) (b : β) : a • orbit α b ⊆ orbit α b :=
  (mapsTo_smul_orbit a b).image_subset
#align mul_action.smul_orbit_subset MulAction.smul_orbit_subset
#align add_action.vadd_orbit_subset AddAction.vadd_orbit_subset
-/

#print MulAction.orbit_smul_subset /-
@[to_additive]
theorem orbit_smul_subset (a : α) (b : β) : orbit α (a • b) ⊆ orbit α b :=
  Set.range_subset_iff.2 fun a' => mul_smul a' a b ▸ mem_orbit _ _
#align mul_action.orbit_smul_subset MulAction.orbit_smul_subset
#align add_action.orbit_vadd_subset AddAction.orbit_vadd_subset
-/

@[to_additive]
instance {b : β} : MulAction α (orbit α b)
    where
  smul a := (mapsTo_smul_orbit a b).restrict _ _ _
  one_smul a := Subtype.ext (one_smul α a)
  mul_smul a a' b' := Subtype.ext (mul_smul a a' b')

/- warning: mul_action.orbit.coe_smul -> MulAction.orbit.coe_smul is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Monoid.{u1} α] [_inst_2 : MulAction.{u1, u2} α β _inst_1] {b : β} {a : α} {b' : coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)}, Eq.{succ u2} β ((fun (a : Type.{u2}) (b : Type.{u2}) [self : HasLiftT.{succ u2, succ u2} a b] => self.0) (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) β (HasLiftT.mk.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) β (CoeTCₓ.coe.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) β (coeBase.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) β (coeSubtype.{succ u2} β (fun (x : β) => Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)))))) (SMul.smul.{u1, u2} α (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) (MulAction.toHasSmul.{u1, u2} α (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) _inst_1 (MulAction.orbit.mulAction.{u1, u2} α β _inst_1 _inst_2 b)) a b')) (SMul.smul.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β _inst_1 _inst_2) a ((fun (a : Type.{u2}) (b : Type.{u2}) [self : HasLiftT.{succ u2, succ u2} a b] => self.0) (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) β (HasLiftT.mk.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) β (CoeTCₓ.coe.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) β (coeBase.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Set.{u2} β) Type.{u2} (Set.hasCoeToSort.{u2} β) (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) β (coeSubtype.{succ u2} β (fun (x : β) => Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)))))) b'))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Monoid.{u1} α] [_inst_2 : MulAction.{u1, u2} α β _inst_1] {b : β} {a : α} {b' : Set.Elem.{u2} β (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)}, Eq.{succ u2} β (Subtype.val.{succ u2} β (fun (x : β) => Membership.mem.{u2, u2} β (Set.{u2} β) (Set.instMembershipSet.{u2} β) x (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) (HSMul.hSMul.{u1, u2, u2} α (Set.Elem.{u2} β (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) (Set.Elem.{u2} β (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) (instHSMul.{u1, u2} α (Set.Elem.{u2} β (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) (MulAction.toSMul.{u1, u2} α (Set.Elem.{u2} β (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) _inst_1 (MulAction.instMulActionElemOrbit.{u1, u2} α β _inst_1 _inst_2 b))) a b')) (HSMul.hSMul.{u1, u2, u2} α β β (instHSMul.{u1, u2} α β (MulAction.toSMul.{u1, u2} α β _inst_1 _inst_2)) a (Subtype.val.{succ u2} β (fun (x : β) => Membership.mem.{u2, u2} β (Set.{u2} β) (Set.instMembershipSet.{u2} β) x (MulAction.orbit.{u1, u2} α β _inst_1 _inst_2 b)) b'))
Case conversion may be inaccurate. Consider using '#align mul_action.orbit.coe_smul MulAction.orbit.coe_smulₓ'. -/
@[simp, to_additive]
theorem orbit.coe_smul {b : β} {a : α} {b' : orbit α b} : ↑(a • b') = a • (b' : β) :=
  rfl
#align mul_action.orbit.coe_smul MulAction.orbit.coe_smul
#align add_action.orbit.coe_vadd AddAction.orbit.coe_vadd

variable (α) (β)

#print MulAction.fixedPoints /-
/-- The set of elements fixed under the whole action. -/
@[to_additive "The set of elements fixed under the whole action."]
def fixedPoints : Set β :=
  { b : β | ∀ x : α, x • b = b }
#align mul_action.fixed_points MulAction.fixedPoints
#align add_action.fixed_points AddAction.fixedPoints
-/

#print MulAction.fixedBy /-
/-- `fixed_by g` is the subfield of elements fixed by `g`. -/
@[to_additive "`fixed_by g` is the subfield of elements fixed by `g`."]
def fixedBy (g : α) : Set β :=
  { x | g • x = x }
#align mul_action.fixed_by MulAction.fixedBy
#align add_action.fixed_by AddAction.fixedBy
-/

#print MulAction.fixed_eq_interᵢ_fixedBy /-
@[to_additive]
theorem fixed_eq_interᵢ_fixedBy : fixedPoints α β = ⋂ g : α, fixedBy α β g :=
  Set.ext fun x =>
    ⟨fun hx => Set.mem_interᵢ.2 fun g => hx g, fun hx g => (Set.mem_interᵢ.1 hx g : _)⟩
#align mul_action.fixed_eq_Inter_fixed_by MulAction.fixed_eq_interᵢ_fixedBy
#align add_action.fixed_eq_Inter_fixed_by AddAction.fixed_eq_interᵢ_fixedBy
-/

variable {α} (β)

#print MulAction.mem_fixedPoints /-
@[simp, to_additive]
theorem mem_fixedPoints {b : β} : b ∈ fixedPoints α β ↔ ∀ x : α, x • b = b :=
  Iff.rfl
#align mul_action.mem_fixed_points MulAction.mem_fixedPoints
#align add_action.mem_fixed_points AddAction.mem_fixedPoints
-/

#print MulAction.mem_fixedBy /-
@[simp, to_additive]
theorem mem_fixedBy {g : α} {b : β} : b ∈ fixedBy α β g ↔ g • b = b :=
  Iff.rfl
#align mul_action.mem_fixed_by MulAction.mem_fixedBy
#align add_action.mem_fixed_by AddAction.mem_fixedBy
-/

#print MulAction.mem_fixedPoints' /-
@[to_additive]
theorem mem_fixedPoints' {b : β} : b ∈ fixedPoints α β ↔ ∀ b', b' ∈ orbit α b → b' = b :=
  ⟨fun h b h₁ =>
    let ⟨x, hx⟩ := mem_orbit_iff.1 h₁
    hx ▸ h x,
    fun h b => h _ (mem_orbit _ _)⟩
#align mul_action.mem_fixed_points' MulAction.mem_fixedPoints'
#align add_action.mem_fixed_points' AddAction.mem_fixedPoints'
-/

variable (α) {β}

#print MulAction.Stabilizer.submonoid /-
/-- The stabilizer of a point `b` as a submonoid of `α`. -/
@[to_additive "The stabilizer of a point `b` as an additive submonoid of `α`."]
def Stabilizer.submonoid (b : β) : Submonoid α
    where
  carrier := { a | a • b = b }
  one_mem' := one_smul _ b
  mul_mem' a a' (ha : a • b = b) (hb : a' • b = b) :=
    show (a * a') • b = b by rw [← smul_smul, hb, ha]
#align mul_action.stabilizer.submonoid MulAction.Stabilizer.submonoid
#align add_action.stabilizer.add_submonoid AddAction.Stabilizer.addSubmonoid
-/

/- warning: mul_action.mem_stabilizer_submonoid_iff -> MulAction.mem_stabilizer_submonoid_iff is a dubious translation:
lean 3 declaration is
  forall (α : Type.{u1}) {β : Type.{u2}} [_inst_1 : Monoid.{u1} α] [_inst_2 : MulAction.{u1, u2} α β _inst_1] {b : β} {a : α}, Iff (Membership.Mem.{u1, u1} α (Submonoid.{u1} α (Monoid.toMulOneClass.{u1} α _inst_1)) (SetLike.hasMem.{u1, u1} (Submonoid.{u1} α (Monoid.toMulOneClass.{u1} α _inst_1)) α (Submonoid.setLike.{u1} α (Monoid.toMulOneClass.{u1} α _inst_1))) a (MulAction.Stabilizer.submonoid.{u1, u2} α β _inst_1 _inst_2 b)) (Eq.{succ u2} β (SMul.smul.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β _inst_1 _inst_2) a b) b)
but is expected to have type
  forall (α : Type.{u1}) {β : Type.{u2}} [_inst_1 : Monoid.{u1} α] [_inst_2 : MulAction.{u1, u2} α β _inst_1] {b : β} {a : α}, Iff (Membership.mem.{u1, u1} α (Submonoid.{u1} α (Monoid.toMulOneClass.{u1} α _inst_1)) (SetLike.instMembership.{u1, u1} (Submonoid.{u1} α (Monoid.toMulOneClass.{u1} α _inst_1)) α (Submonoid.instSetLikeSubmonoid.{u1} α (Monoid.toMulOneClass.{u1} α _inst_1))) a (MulAction.Stabilizer.submonoid.{u1, u2} α β _inst_1 _inst_2 b)) (Eq.{succ u2} β (HSMul.hSMul.{u1, u2, u2} α β β (instHSMul.{u1, u2} α β (MulAction.toSMul.{u1, u2} α β _inst_1 _inst_2)) a b) b)
Case conversion may be inaccurate. Consider using '#align mul_action.mem_stabilizer_submonoid_iff MulAction.mem_stabilizer_submonoid_iffₓ'. -/
@[simp, to_additive]
theorem mem_stabilizer_submonoid_iff {b : β} {a : α} : a ∈ Stabilizer.submonoid α b ↔ a • b = b :=
  Iff.rfl
#align mul_action.mem_stabilizer_submonoid_iff MulAction.mem_stabilizer_submonoid_iff
#align add_action.mem_stabilizer_add_submonoid_iff AddAction.mem_stabilizer_addSubmonoid_iff

#print MulAction.orbit_eq_univ /-
@[to_additive]
theorem orbit_eq_univ [IsPretransitive α β] (x : β) : orbit α x = Set.univ :=
  (surjective_smul α x).range_eq
#align mul_action.orbit_eq_univ MulAction.orbit_eq_univ
#align add_action.orbit_eq_univ AddAction.orbit_eq_univ
-/

variable {α} {β}

#print MulAction.mem_fixedPoints_iff_card_orbit_eq_one /-
@[to_additive]
theorem mem_fixedPoints_iff_card_orbit_eq_one {a : β} [Fintype (orbit α a)] :
    a ∈ fixedPoints α β ↔ Fintype.card (orbit α a) = 1 :=
  by
  rw [Fintype.card_eq_one_iff, mem_fixed_points]
  constructor
  · exact fun h => ⟨⟨a, mem_orbit_self _⟩, fun ⟨b, ⟨x, hx⟩⟩ => Subtype.eq <| by simp [h x, hx.symm]⟩
  · intro h x
    rcases h with ⟨⟨z, hz⟩, hz₁⟩
    calc
      x • a = z := Subtype.mk.inj (hz₁ ⟨x • a, mem_orbit _ _⟩)
      _ = a := (Subtype.mk.inj (hz₁ ⟨a, mem_orbit_self _⟩)).symm

#align mul_action.mem_fixed_points_iff_card_orbit_eq_one MulAction.mem_fixedPoints_iff_card_orbit_eq_one
#align add_action.mem_fixed_points_iff_card_orbit_eq_zero AddAction.mem_fixedPoints_iff_card_orbit_eq_zero
-/

end MulAction

namespace MulAction

variable (α)

variable [Group α] [MulAction α β]

#print MulAction.stabilizer /-
/-- The stabilizer of an element under an action, i.e. what sends the element to itself.
A subgroup. -/
@[to_additive
      "The stabilizer of an element under an action, i.e. what sends the element to itself.\nAn additive subgroup."]
def stabilizer (b : β) : Subgroup α :=
  { Stabilizer.submonoid α b with
    inv_mem' := fun a (ha : a • b = b) => show a⁻¹ • b = b by rw [inv_smul_eq_iff, ha] }
#align mul_action.stabilizer MulAction.stabilizer
#align add_action.stabilizer AddAction.stabilizer
-/

variable {α} {β}

/- warning: mul_action.mem_stabilizer_iff -> MulAction.mem_stabilizer_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] {b : β} {a : α}, Iff (Membership.Mem.{u1, u1} α (Subgroup.{u1} α _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} α _inst_1) α (Subgroup.setLike.{u1} α _inst_1)) a (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 b)) (Eq.{succ u2} β (SMul.smul.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2) a b) b)
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] {b : β} {a : α}, Iff (Membership.mem.{u1, u1} α (Subgroup.{u1} α _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} α _inst_1) α (Subgroup.instSetLikeSubgroup.{u1} α _inst_1)) a (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 b)) (Eq.{succ u2} β (HSMul.hSMul.{u1, u2, u2} α β β (instHSMul.{u1, u2} α β (MulAction.toSMul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2)) a b) b)
Case conversion may be inaccurate. Consider using '#align mul_action.mem_stabilizer_iff MulAction.mem_stabilizer_iffₓ'. -/
@[simp, to_additive]
theorem mem_stabilizer_iff {b : β} {a : α} : a ∈ stabilizer α b ↔ a • b = b :=
  Iff.rfl
#align mul_action.mem_stabilizer_iff MulAction.mem_stabilizer_iff
#align add_action.mem_stabilizer_iff AddAction.mem_stabilizer_iff

#print MulAction.smul_orbit /-
@[simp, to_additive]
theorem smul_orbit (a : α) (b : β) : a • orbit α b = orbit α b :=
  (smul_orbit_subset a b).antisymm <|
    calc
      orbit α b = a • a⁻¹ • orbit α b := (smul_inv_smul _ _).symm
      _ ⊆ a • orbit α b := Set.image_subset _ (smul_orbit_subset _ _)

#align mul_action.smul_orbit MulAction.smul_orbit
#align add_action.vadd_orbit AddAction.vadd_orbit
-/

#print MulAction.orbit_smul /-
@[simp, to_additive]
theorem orbit_smul (a : α) (b : β) : orbit α (a • b) = orbit α b :=
  (orbit_smul_subset a b).antisymm <|
    calc
      orbit α b = orbit α (a⁻¹ • a • b) := by rw [inv_smul_smul]
      _ ⊆ orbit α (a • b) := orbit_smul_subset _ _

#align mul_action.orbit_smul MulAction.orbit_smul
#align add_action.orbit_vadd AddAction.orbit_vadd
-/

/-- The action of a group on an orbit is transitive. -/
@[to_additive "The action of an additive group on an orbit is transitive."]
instance (x : β) : IsPretransitive α (orbit α x) :=
  ⟨by
    rintro ⟨_, a, rfl⟩ ⟨_, b, rfl⟩
    use b * a⁻¹
    ext1
    simp [mul_smul]⟩

#print MulAction.orbit_eq_iff /-
@[to_additive]
theorem orbit_eq_iff {a b : β} : orbit α a = orbit α b ↔ a ∈ orbit α b :=
  ⟨fun h => h ▸ mem_orbit_self _, fun ⟨c, hc⟩ => hc ▸ orbit_smul _ _⟩
#align mul_action.orbit_eq_iff MulAction.orbit_eq_iff
#align add_action.orbit_eq_iff AddAction.orbit_eq_iff
-/

variable (α) {β}

#print MulAction.mem_orbit_smul /-
@[to_additive]
theorem mem_orbit_smul (g : α) (a : β) : a ∈ orbit α (g • a) := by
  simp only [orbit_smul, mem_orbit_self]
#align mul_action.mem_orbit_smul MulAction.mem_orbit_smul
#align add_action.mem_orbit_vadd AddAction.mem_orbit_vadd
-/

#print MulAction.smul_mem_orbit_smul /-
@[to_additive]
theorem smul_mem_orbit_smul (g h : α) (a : β) : g • a ∈ orbit α (h • a) := by
  simp only [orbit_smul, mem_orbit]
#align mul_action.smul_mem_orbit_smul MulAction.smul_mem_orbit_smul
#align add_action.vadd_mem_orbit_vadd AddAction.vadd_mem_orbit_vadd
-/

variable (α) (β)

#print MulAction.orbitRel /-
/-- The relation 'in the same orbit'. -/
@[to_additive "The relation 'in the same orbit'."]
def orbitRel : Setoid β where
  R a b := a ∈ orbit α b
  iseqv :=
    ⟨mem_orbit_self, fun a b => by simp [orbit_eq_iff.symm, eq_comm], fun a b => by
      simp (config := { contextual := true }) [orbit_eq_iff.symm, eq_comm]⟩
#align mul_action.orbit_rel MulAction.orbitRel
#align add_action.orbit_rel AddAction.orbitRel
-/

attribute [local instance] orbit_rel

variable {α} {β}

#print MulAction.quotient_preimage_image_eq_union_mul /-
/-- When you take a set `U` in `β`, push it down to the quotient, and pull back, you get the union
of the orbit of `U` under `α`. -/
@[to_additive
      "When you take a set `U` in `β`, push it down to the quotient, and pull back, you get\nthe union of the orbit of `U` under `α`."]
theorem quotient_preimage_image_eq_union_mul (U : Set β) :
    Quotient.mk' ⁻¹' (Quotient.mk' '' U) = ⋃ a : α, (· • ·) a '' U :=
  by
  set f : β → Quotient (MulAction.orbitRel α β) := Quotient.mk'
  ext
  constructor
  · rintro ⟨y, hy, hxy⟩
    obtain ⟨a, rfl⟩ := Quotient.exact hxy
    rw [Set.mem_unionᵢ]
    exact ⟨a⁻¹, a • x, hy, inv_smul_smul a x⟩
  · intro hx
    rw [Set.mem_unionᵢ] at hx
    obtain ⟨a, u, hu₁, hu₂⟩ := hx
    rw [Set.mem_preimage, Set.mem_image_iff_bex]
    refine' ⟨a⁻¹ • x, _, by simp only [Quotient.eq'] <;> use a⁻¹⟩
    rw [← hu₂]
    convert hu₁
    simp only [inv_smul_smul]
#align mul_action.quotient_preimage_image_eq_union_mul MulAction.quotient_preimage_image_eq_union_mul
#align add_action.quotient_preimage_image_eq_union_add AddAction.quotient_preimage_image_eq_union_add
-/

/- warning: mul_action.disjoint_image_image_iff -> MulAction.disjoint_image_image_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] {U : Set.{u2} β} {V : Set.{u2} β}, Iff (Disjoint.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Set.completeBooleanAlgebra.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)))))))) (GeneralizedBooleanAlgebra.toOrderBot.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (BooleanAlgebra.toGeneralizedBooleanAlgebra.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Set.booleanAlgebra.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))))) (Set.image.{u2, u2} β (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) (Quotient.mk'.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) U) (Set.image.{u2, u2} β (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) (Quotient.mk'.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) V)) (forall (x : β), (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x U) -> (forall (a : α), Not (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) (SMul.smul.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2) a x) V)))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] {U : Set.{u2} β} {V : Set.{u2} β}, Iff (Disjoint.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Set.instCompleteBooleanAlgebraSet.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)))))))) (BoundedOrder.toOrderBot.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Preorder.toLE.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (PartialOrder.toPreorder.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Set.instCompleteBooleanAlgebraSet.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)))))))))) (CompleteLattice.toBoundedOrder.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Set.instCompleteBooleanAlgebraSet.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)))))))) (Set.image.{u2, u2} β (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) (Quotient.mk'.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) U) (Set.image.{u2, u2} β (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) (Quotient.mk'.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) V)) (forall (x : β), (Membership.mem.{u2, u2} β (Set.{u2} β) (Set.instMembershipSet.{u2} β) x U) -> (forall (a : α), Not (Membership.mem.{u2, u2} β (Set.{u2} β) (Set.instMembershipSet.{u2} β) (HSMul.hSMul.{u1, u2, u2} α β β (instHSMul.{u1, u2} α β (MulAction.toSMul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2)) a x) V)))
Case conversion may be inaccurate. Consider using '#align mul_action.disjoint_image_image_iff MulAction.disjoint_image_image_iffₓ'. -/
@[to_additive]
theorem disjoint_image_image_iff {U V : Set β} :
    Disjoint (Quotient.mk' '' U) (Quotient.mk' '' V) ↔ ∀ x ∈ U, ∀ a : α, a • x ∉ V :=
  by
  set f : β → Quotient (MulAction.orbitRel α β) := Quotient.mk'
  refine'
    ⟨fun h x x_in_U a a_in_V =>
      h.le_bot ⟨⟨x, x_in_U, Quotient.sound ⟨a⁻¹, _⟩⟩, ⟨a • x, a_in_V, rfl⟩⟩, _⟩
  · simp
  · intro h
    rw [Set.disjoint_left]
    rintro x ⟨y, hy₁, hy₂⟩ ⟨z, hz₁, hz₂⟩
    obtain ⟨a, rfl⟩ := Quotient.exact (hz₂.trans hy₂.symm)
    exact h y hy₁ a hz₁
#align mul_action.disjoint_image_image_iff MulAction.disjoint_image_image_iff
#align add_action.disjoint_image_image_iff AddAction.disjoint_image_image_iff

/- warning: mul_action.image_inter_image_iff -> MulAction.image_inter_image_iff is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] (U : Set.{u2} β) (V : Set.{u2} β), Iff (Eq.{succ u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Inter.inter.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Set.hasInter.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Set.image.{u2, u2} β (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) (Quotient.mk'.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) U) (Set.image.{u2, u2} β (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) (Quotient.mk'.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) V)) (EmptyCollection.emptyCollection.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Set.hasEmptyc.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))))) (forall (x : β), (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) x U) -> (forall (a : α), Not (Membership.Mem.{u2, u2} β (Set.{u2} β) (Set.hasMem.{u2} β) (SMul.smul.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2) a x) V)))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] (U : Set.{u2} β) (V : Set.{u2} β), Iff (Eq.{succ u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Inter.inter.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Set.instInterSet.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Set.image.{u2, u2} β (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) (Quotient.mk'.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) U) (Set.image.{u2, u2} β (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) (Quotient.mk'.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2)) V)) (EmptyCollection.emptyCollection.{u2} (Set.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))) (Set.instEmptyCollectionSet.{u2} (Quotient.{succ u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2))))) (forall (x : β), (Membership.mem.{u2, u2} β (Set.{u2} β) (Set.instMembershipSet.{u2} β) x U) -> (forall (a : α), Not (Membership.mem.{u2, u2} β (Set.{u2} β) (Set.instMembershipSet.{u2} β) (HSMul.hSMul.{u1, u2, u2} α β β (instHSMul.{u1, u2} α β (MulAction.toSMul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2)) a x) V)))
Case conversion may be inaccurate. Consider using '#align mul_action.image_inter_image_iff MulAction.image_inter_image_iffₓ'. -/
@[to_additive]
theorem image_inter_image_iff (U V : Set β) :
    Quotient.mk' '' U ∩ Quotient.mk' '' V = ∅ ↔ ∀ x ∈ U, ∀ a : α, a • x ∉ V :=
  Set.disjoint_iff_inter_eq_empty.symm.trans disjoint_image_image_iff
#align mul_action.image_inter_image_iff MulAction.image_inter_image_iff
#align add_action.image_inter_image_iff AddAction.image_inter_image_iff

variable (α β)

#print MulAction.orbitRel.Quotient /-
/-- The quotient by `mul_action.orbit_rel`, given a name to enable dot notation. -/
@[reducible,
  to_additive "The quotient by `add_action.orbit_rel`, given a name to enable dot\nnotation."]
def orbitRel.Quotient : Type _ :=
  Quotient <| orbitRel α β
#align mul_action.orbit_rel.quotient MulAction.orbitRel.Quotient
#align add_action.orbit_rel.quotient AddAction.orbitRel.Quotient
-/

variable {α β}

#print MulAction.orbitRel.Quotient.orbit /-
/-- The orbit corresponding to an element of the quotient by `mul_action.orbit_rel` -/
@[to_additive "The orbit corresponding to an element of the quotient by `add_action.orbit_rel`"]
def orbitRel.Quotient.orbit (x : orbitRel.Quotient α β) : Set β :=
  Quotient.liftOn' x (orbit α) fun _ _ => MulAction.orbit_eq_iff.2
#align mul_action.orbit_rel.quotient.orbit MulAction.orbitRel.Quotient.orbit
#align add_action.orbit_rel.quotient.orbit AddAction.orbitRel.Quotient.orbit
-/

#print MulAction.orbitRel.Quotient.orbit_mk /-
@[simp, to_additive]
theorem orbitRel.Quotient.orbit_mk (b : β) :
    orbitRel.Quotient.orbit (Quotient.mk'' b : orbitRel.Quotient α β) = orbit α b :=
  rfl
#align mul_action.orbit_rel.quotient.orbit_mk MulAction.orbitRel.Quotient.orbit_mk
#align add_action.orbit_rel.quotient.orbit_mk AddAction.orbitRel.Quotient.orbit_mk
-/

#print MulAction.orbitRel.Quotient.mem_orbit /-
@[to_additive]
theorem orbitRel.Quotient.mem_orbit {b : β} {x : orbitRel.Quotient α β} :
    b ∈ x.orbit ↔ Quotient.mk'' b = x :=
  by
  induction x using Quotient.inductionOn'
  rw [Quotient.eq'']
  rfl
#align mul_action.orbit_rel.quotient.mem_orbit MulAction.orbitRel.Quotient.mem_orbit
#align add_action.orbit_rel.quotient.mem_orbit AddAction.orbitRel.Quotient.mem_orbit
-/

#print MulAction.orbitRel.Quotient.orbit_eq_orbit_out /-
/-- Note that `hφ = quotient.out_eq'` is a useful choice here. -/
@[to_additive "Note that `hφ = quotient.out_eq'` is a useful choice here."]
theorem orbitRel.Quotient.orbit_eq_orbit_out (x : orbitRel.Quotient α β)
    {φ : orbitRel.Quotient α β → β} (hφ : RightInverse φ Quotient.mk'') :
    orbitRel.Quotient.orbit x = orbit α (φ x) :=
  by
  conv_lhs => rw [← hφ x]
  induction x using Quotient.inductionOn'
  rfl
#align mul_action.orbit_rel.quotient.orbit_eq_orbit_out MulAction.orbitRel.Quotient.orbit_eq_orbit_out
#align add_action.orbit_rel.quotient.orbit_eq_orbit_out AddAction.orbitRel.Quotient.orbit_eq_orbit_out
-/

variable (α) (β)

-- mathport name: exprΩ
local notation "Ω" => orbitRel.Quotient α β

#print MulAction.selfEquivSigmaOrbits' /-
/-- Decomposition of a type `X` as a disjoint union of its orbits under a group action.

This version is expressed in terms of `mul_action.orbit_rel.quotient.orbit` instead of
`mul_action.orbit`, to avoid mentioning `quotient.out'`. -/
@[to_additive
      "Decomposition of a type `X` as a disjoint union of its orbits under an additive group\naction.\n\nThis version is expressed in terms of `add_action.orbit_rel.quotient.orbit` instead of\n`add_action.orbit`, to avoid mentioning `quotient.out'`. "]
def selfEquivSigmaOrbits' : β ≃ Σω : Ω, ω.orbit :=
  calc
    β ≃ Σω : Ω, { b // Quotient.mk'' b = ω } := (Equiv.sigmaFiberEquiv Quotient.mk'').symm
    _ ≃ Σω : Ω, ω.orbit :=
      Equiv.sigmaCongrRight fun ω =>
        Equiv.subtypeEquivRight fun x => orbitRel.Quotient.mem_orbit.symm

#align mul_action.self_equiv_sigma_orbits' MulAction.selfEquivSigmaOrbits'
#align add_action.self_equiv_sigma_orbits' AddAction.selfEquivSigmaOrbits'
-/

#print MulAction.selfEquivSigmaOrbits /-
/-- Decomposition of a type `X` as a disjoint union of its orbits under a group action. -/
@[to_additive
      "Decomposition of a type `X` as a disjoint union of its orbits under an additive group\naction."]
def selfEquivSigmaOrbits : β ≃ Σω : Ω, orbit α ω.out' :=
  (selfEquivSigmaOrbits' α β).trans <|
    Equiv.sigmaCongrRight fun i =>
      Equiv.Set.ofEq <| orbitRel.Quotient.orbit_eq_orbit_out _ Quotient.out_eq'
#align mul_action.self_equiv_sigma_orbits MulAction.selfEquivSigmaOrbits
#align add_action.self_equiv_sigma_orbits AddAction.selfEquivSigmaOrbits
-/

variable {α β}

/- warning: mul_action.stabilizer_smul_eq_stabilizer_map_conj -> MulAction.stabilizer_smul_eq_stabilizer_map_conj is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] (g : α) (x : β), Eq.{succ u1} (Subgroup.{u1} α _inst_1) (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 (SMul.smul.{u1, u2} α β (MulAction.toHasSmul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2) g x)) (Subgroup.map.{u1, u1} α _inst_1 α _inst_1 (MulEquiv.toMonoidHom.{u1, u1} α α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (coeFn.{succ u1, succ u1} (MonoidHom.{u1, u1} α (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (Monoid.toMulOneClass.{u1} (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (MulAut.group.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))))))) (fun (_x : MonoidHom.{u1, u1} α (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (Monoid.toMulOneClass.{u1} (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (MulAut.group.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))))))) => α -> (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)))))) (MonoidHom.hasCoeToFun.{u1, u1} α (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (Monoid.toMulOneClass.{u1} (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (MulAut.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (MulAut.group.{u1} α (MulOneClass.toHasMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))))))) (MulAut.conj.{u1} α _inst_1) g)) (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] (g : α) (x : β), Eq.{succ u1} (Subgroup.{u1} α _inst_1) (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 (HSMul.hSMul.{u1, u2, u2} α β β (instHSMul.{u1, u2} α β (MulAction.toSMul.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)) _inst_2)) g x)) (Subgroup.map.{u1, u1} α _inst_1 α _inst_1 (MulEquiv.toMonoidHom.{u1, u1} α α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (FunLike.coe.{succ u1, succ u1, succ u1} (MonoidHom.{u1, u1} α (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (Monoid.toMulOneClass.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (MulAut.instGroupMulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))))))) α (fun (_x : α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : α) => MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) _x) (MulHomClass.toFunLike.{u1, u1, u1} (MonoidHom.{u1, u1} α (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (Monoid.toMulOneClass.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (MulAut.instGroupMulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))))))) α (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)))) (MulOneClass.toMul.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (MulAut.instGroupMulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))))))) (MonoidHomClass.toMulHomClass.{u1, u1, u1} (MonoidHom.{u1, u1} α (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (Monoid.toMulOneClass.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (MulAut.instGroupMulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))))))) α (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (Monoid.toMulOneClass.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (MulAut.instGroupMulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1)))))))) (MonoidHom.monoidHomClass.{u1, u1} α (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))) (Monoid.toMulOneClass.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (MulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))) (MulAut.instGroupMulAut.{u1} α (MulOneClass.toMul.{u1} α (Monoid.toMulOneClass.{u1} α (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))))))))))) (MulAut.conj.{u1} α _inst_1) g)) (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x))
Case conversion may be inaccurate. Consider using '#align mul_action.stabilizer_smul_eq_stabilizer_map_conj MulAction.stabilizer_smul_eq_stabilizer_map_conjₓ'. -/
/-- If the stabilizer of `x` is `S`, then the stabilizer of `g • x` is `gSg⁻¹`. -/
theorem stabilizer_smul_eq_stabilizer_map_conj (g : α) (x : β) :
    stabilizer α (g • x) = (stabilizer α x).map (MulAut.conj g).toMonoidHom :=
  by
  ext h
  rw [mem_stabilizer_iff, ← smul_left_cancel_iff g⁻¹, smul_smul, smul_smul, smul_smul, mul_left_inv,
    one_smul, ← mem_stabilizer_iff, Subgroup.mem_map_equiv, MulAut.conj_symm_apply]
#align mul_action.stabilizer_smul_eq_stabilizer_map_conj MulAction.stabilizer_smul_eq_stabilizer_map_conj

/- warning: mul_action.stabilizer_equiv_stabilizer_of_orbit_rel -> MulAction.stabilizerEquivStabilizerOfOrbitRel is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] {x : β} {y : β}, (Setoid.Rel.{u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2) x y) -> (MulEquiv.{u1, u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} α _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} α _inst_1) α (Subgroup.setLike.{u1} α _inst_1)) (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x)) (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} α _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} α _inst_1) α (Subgroup.setLike.{u1} α _inst_1)) (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 y)) (Subgroup.mul.{u1} α _inst_1 (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x)) (Subgroup.mul.{u1} α _inst_1 (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 y)))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : Group.{u1} α] [_inst_2 : MulAction.{u1, u2} α β (DivInvMonoid.toMonoid.{u1} α (Group.toDivInvMonoid.{u1} α _inst_1))] {x : β} {y : β}, (Setoid.Rel.{u2} β (MulAction.orbitRel.{u1, u2} α β _inst_1 _inst_2) x y) -> (MulEquiv.{u1, u1} (Subtype.{succ u1} α (fun (x_1 : α) => Membership.mem.{u1, u1} α (Subgroup.{u1} α _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} α _inst_1) α (Subgroup.instSetLikeSubgroup.{u1} α _inst_1)) x_1 (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x))) (Subtype.{succ u1} α (fun (x : α) => Membership.mem.{u1, u1} α (Subgroup.{u1} α _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} α _inst_1) α (Subgroup.instSetLikeSubgroup.{u1} α _inst_1)) x (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 y))) (Subgroup.mul.{u1} α _inst_1 (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x)) (Subgroup.mul.{u1} α _inst_1 (MulAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 y)))
Case conversion may be inaccurate. Consider using '#align mul_action.stabilizer_equiv_stabilizer_of_orbit_rel MulAction.stabilizerEquivStabilizerOfOrbitRelₓ'. -/
/-- A bijection between the stabilizers of two elements in the same orbit. -/
noncomputable def stabilizerEquivStabilizerOfOrbitRel {x y : β} (h : (orbitRel α β).Rel x y) :
    stabilizer α x ≃* stabilizer α y :=
  let g : α := Classical.choose h
  have hg : g • y = x := Classical.choose_spec h
  have this : stabilizer α x = (stabilizer α y).map (MulAut.conj g).toMonoidHom := by
    rw [← hg, stabilizer_smul_eq_stabilizer_map_conj]
  (MulEquiv.subgroupCongr this).trans ((MulAut.conj g).subgroupMap <| stabilizer α y).symm
#align mul_action.stabilizer_equiv_stabilizer_of_orbit_rel MulAction.stabilizerEquivStabilizerOfOrbitRel

end MulAction

namespace AddAction

variable [AddGroup α] [AddAction α β]

/- warning: add_action.stabilizer_vadd_eq_stabilizer_map_conj -> AddAction.stabilizer_vadd_eq_stabilizer_map_conj is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : AddGroup.{u1} α] [_inst_2 : AddAction.{u1, u2} α β (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))] (g : α) (x : β), Eq.{succ u1} (AddSubgroup.{u1} α _inst_1) (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 (VAdd.vadd.{u1, u2} α β (AddAction.toHasVadd.{u1, u2} α β (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)) _inst_2) g x)) (AddSubgroup.map.{u1, u1} α _inst_1 α _inst_1 (AddEquiv.toAddMonoidHom.{u1, u1} α α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (coeFn.{succ u1, succ u1} (AddMonoidHom.{u1, u1} α (Additive.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))) (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (Additive.addZeroClass.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (AddAut.group.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))))))) (fun (_x : AddMonoidHom.{u1, u1} α (Additive.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))) (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (Additive.addZeroClass.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (AddAut.group.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))))))) => α -> (Additive.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))))) (AddMonoidHom.hasCoeToFun.{u1, u1} α (Additive.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))) (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (Additive.addZeroClass.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (AddAut.group.{u1} α (AddZeroClass.toHasAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))))))) (AddAut.conj.{u1} α _inst_1) g)) (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : AddGroup.{u1} α] [_inst_2 : AddAction.{u1, u2} α β (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))] (g : α) (x : β), Eq.{succ u1} (AddSubgroup.{u1} α _inst_1) (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 (HVAdd.hVAdd.{u1, u2, u2} α β β (instHVAdd.{u1, u2} α β (AddAction.toVAdd.{u1, u2} α β (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)) _inst_2)) g x)) (AddSubgroup.map.{u1, u1} α _inst_1 α _inst_1 (AddEquiv.toAddMonoidHom.{u1, u1} α α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (FunLike.coe.{succ u1, succ u1, succ u1} (AddMonoidHom.{u1, u1} α (Additive.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))) (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (Additive.addZeroClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (AddAut.group.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))))))) α (fun (_x : α) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.403 : α) => Additive.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))) _x) (AddHomClass.toFunLike.{u1, u1, u1} (AddMonoidHom.{u1, u1} α (Additive.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))) (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (Additive.addZeroClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (AddAut.group.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))))))) α (Additive.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))) (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))) (AddZeroClass.toAdd.{u1} (Additive.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))) (Additive.addZeroClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (AddAut.group.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))))))) (AddMonoidHomClass.toAddHomClass.{u1, u1, u1} (AddMonoidHom.{u1, u1} α (Additive.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))) (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (Additive.addZeroClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (AddAut.group.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))))))) α (Additive.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))) (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (Additive.addZeroClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (AddAut.group.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))))))) (AddMonoidHom.addMonoidHomClass.{u1, u1} α (Additive.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))) (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))) (Additive.addZeroClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Monoid.toMulOneClass.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (DivInvMonoid.toMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (Group.toDivInvMonoid.{u1} (AddAut.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))))) (AddAut.group.{u1} α (AddZeroClass.toAdd.{u1} α (AddMonoid.toAddZeroClass.{u1} α (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1)))))))))))) (AddAut.conj.{u1} α _inst_1) g)) (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x))
Case conversion may be inaccurate. Consider using '#align add_action.stabilizer_vadd_eq_stabilizer_map_conj AddAction.stabilizer_vadd_eq_stabilizer_map_conjₓ'. -/
/-- If the stabilizer of `x` is `S`, then the stabilizer of `g +ᵥ x` is `g + S + (-g)`. -/
theorem stabilizer_vadd_eq_stabilizer_map_conj (g : α) (x : β) :
    stabilizer α (g +ᵥ x) = (stabilizer α x).map (AddAut.conj g).toAddMonoidHom :=
  by
  ext h
  rw [mem_stabilizer_iff, ← vadd_left_cancel_iff (-g), vadd_vadd, vadd_vadd, vadd_vadd,
    add_left_neg, zero_vadd, ← mem_stabilizer_iff, AddSubgroup.mem_map_equiv,
    AddAut.conj_symm_apply]
#align add_action.stabilizer_vadd_eq_stabilizer_map_conj AddAction.stabilizer_vadd_eq_stabilizer_map_conj

/- warning: add_action.stabilizer_equiv_stabilizer_of_orbit_rel -> AddAction.stabilizerEquivStabilizerOfOrbitRel is a dubious translation:
lean 3 declaration is
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : AddGroup.{u1} α] [_inst_2 : AddAction.{u1, u2} α β (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))] {x : β} {y : β}, (Setoid.Rel.{u2} β (AddAction.orbitRel.{u1, u2} α β _inst_1 _inst_2) x y) -> (AddEquiv.{u1, u1} (coeSort.{succ u1, succ (succ u1)} (AddSubgroup.{u1} α _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (AddSubgroup.{u1} α _inst_1) α (AddSubgroup.setLike.{u1} α _inst_1)) (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x)) (coeSort.{succ u1, succ (succ u1)} (AddSubgroup.{u1} α _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (AddSubgroup.{u1} α _inst_1) α (AddSubgroup.setLike.{u1} α _inst_1)) (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 y)) (AddSubgroup.add.{u1} α _inst_1 (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x)) (AddSubgroup.add.{u1} α _inst_1 (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 y)))
but is expected to have type
  forall {α : Type.{u1}} {β : Type.{u2}} [_inst_1 : AddGroup.{u1} α] [_inst_2 : AddAction.{u1, u2} α β (SubNegMonoid.toAddMonoid.{u1} α (AddGroup.toSubNegMonoid.{u1} α _inst_1))] {x : β} {y : β}, (Setoid.Rel.{u2} β (AddAction.orbitRel.{u1, u2} α β _inst_1 _inst_2) x y) -> (AddEquiv.{u1, u1} (Subtype.{succ u1} α (fun (x_1 : α) => Membership.mem.{u1, u1} α (AddSubgroup.{u1} α _inst_1) (SetLike.instMembership.{u1, u1} (AddSubgroup.{u1} α _inst_1) α (AddSubgroup.instSetLikeAddSubgroup.{u1} α _inst_1)) x_1 (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x))) (Subtype.{succ u1} α (fun (x : α) => Membership.mem.{u1, u1} α (AddSubgroup.{u1} α _inst_1) (SetLike.instMembership.{u1, u1} (AddSubgroup.{u1} α _inst_1) α (AddSubgroup.instSetLikeAddSubgroup.{u1} α _inst_1)) x (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 y))) (AddSubgroup.add.{u1} α _inst_1 (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 x)) (AddSubgroup.add.{u1} α _inst_1 (AddAction.stabilizer.{u1, u2} α β _inst_1 _inst_2 y)))
Case conversion may be inaccurate. Consider using '#align add_action.stabilizer_equiv_stabilizer_of_orbit_rel AddAction.stabilizerEquivStabilizerOfOrbitRelₓ'. -/
/-- A bijection between the stabilizers of two elements in the same orbit. -/
noncomputable def stabilizerEquivStabilizerOfOrbitRel {x y : β} (h : (orbitRel α β).Rel x y) :
    stabilizer α x ≃+ stabilizer α y :=
  let g : α := Classical.choose h
  have hg : g +ᵥ y = x := Classical.choose_spec h
  have this : stabilizer α x = (stabilizer α y).map (AddAut.conj g).toAddMonoidHom := by
    rw [← hg, stabilizer_vadd_eq_stabilizer_map_conj]
  (AddEquiv.addSubgroupCongr this).trans ((AddAut.conj g).addSubgroupMap <| stabilizer α y).symm
#align add_action.stabilizer_equiv_stabilizer_of_orbit_rel AddAction.stabilizerEquivStabilizerOfOrbitRel

end AddAction

/- warning: smul_cancel_of_non_zero_divisor -> smul_cancel_of_non_zero_divisor is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} {R : Type.{u2}} [_inst_1 : Monoid.{u1} M] [_inst_2 : NonUnitalNonAssocRing.{u2} R] [_inst_3 : DistribMulAction.{u1, u2} M R _inst_1 (SubNegMonoid.toAddMonoid.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddCommGroup.toAddGroup.{u2} R (NonUnitalNonAssocRing.toAddCommGroup.{u2} R _inst_2))))] (k : M), (forall (x : R), (Eq.{succ u2} R (SMul.smul.{u1, u2} M R (SMulZeroClass.toHasSmul.{u1, u2} M R (AddZeroClass.toHasZero.{u2} R (AddMonoid.toAddZeroClass.{u2} R (SubNegMonoid.toAddMonoid.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddCommGroup.toAddGroup.{u2} R (NonUnitalNonAssocRing.toAddCommGroup.{u2} R _inst_2)))))) (DistribSMul.toSmulZeroClass.{u1, u2} M R (AddMonoid.toAddZeroClass.{u2} R (SubNegMonoid.toAddMonoid.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddCommGroup.toAddGroup.{u2} R (NonUnitalNonAssocRing.toAddCommGroup.{u2} R _inst_2))))) (DistribMulAction.toDistribSMul.{u1, u2} M R _inst_1 (SubNegMonoid.toAddMonoid.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddCommGroup.toAddGroup.{u2} R (NonUnitalNonAssocRing.toAddCommGroup.{u2} R _inst_2)))) _inst_3))) k x) (OfNat.ofNat.{u2} R 0 (OfNat.mk.{u2} R 0 (Zero.zero.{u2} R (MulZeroClass.toHasZero.{u2} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u2} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u2} R _inst_2))))))) -> (Eq.{succ u2} R x (OfNat.ofNat.{u2} R 0 (OfNat.mk.{u2} R 0 (Zero.zero.{u2} R (MulZeroClass.toHasZero.{u2} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u2} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u2} R _inst_2)))))))) -> (forall {a : R} {b : R}, (Eq.{succ u2} R (SMul.smul.{u1, u2} M R (SMulZeroClass.toHasSmul.{u1, u2} M R (AddZeroClass.toHasZero.{u2} R (AddMonoid.toAddZeroClass.{u2} R (SubNegMonoid.toAddMonoid.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddCommGroup.toAddGroup.{u2} R (NonUnitalNonAssocRing.toAddCommGroup.{u2} R _inst_2)))))) (DistribSMul.toSmulZeroClass.{u1, u2} M R (AddMonoid.toAddZeroClass.{u2} R (SubNegMonoid.toAddMonoid.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddCommGroup.toAddGroup.{u2} R (NonUnitalNonAssocRing.toAddCommGroup.{u2} R _inst_2))))) (DistribMulAction.toDistribSMul.{u1, u2} M R _inst_1 (SubNegMonoid.toAddMonoid.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddCommGroup.toAddGroup.{u2} R (NonUnitalNonAssocRing.toAddCommGroup.{u2} R _inst_2)))) _inst_3))) k a) (SMul.smul.{u1, u2} M R (SMulZeroClass.toHasSmul.{u1, u2} M R (AddZeroClass.toHasZero.{u2} R (AddMonoid.toAddZeroClass.{u2} R (SubNegMonoid.toAddMonoid.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddCommGroup.toAddGroup.{u2} R (NonUnitalNonAssocRing.toAddCommGroup.{u2} R _inst_2)))))) (DistribSMul.toSmulZeroClass.{u1, u2} M R (AddMonoid.toAddZeroClass.{u2} R (SubNegMonoid.toAddMonoid.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddCommGroup.toAddGroup.{u2} R (NonUnitalNonAssocRing.toAddCommGroup.{u2} R _inst_2))))) (DistribMulAction.toDistribSMul.{u1, u2} M R _inst_1 (SubNegMonoid.toAddMonoid.{u2} R (AddGroup.toSubNegMonoid.{u2} R (AddCommGroup.toAddGroup.{u2} R (NonUnitalNonAssocRing.toAddCommGroup.{u2} R _inst_2)))) _inst_3))) k b)) -> (Eq.{succ u2} R a b))
but is expected to have type
  forall {M : Type.{u2}} {R : Type.{u1}} [_inst_1 : Monoid.{u2} M] [_inst_2 : NonUnitalNonAssocRing.{u1} R] [_inst_3 : DistribMulAction.{u2, u1} M R _inst_1 (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddCommGroup.toAddGroup.{u1} R (NonUnitalNonAssocRing.toAddCommGroup.{u1} R _inst_2))))] (k : M), (forall (x : R), (Eq.{succ u1} R (HSMul.hSMul.{u2, u1, u1} M R R (instHSMul.{u2, u1} M R (SMulZeroClass.toSMul.{u2, u1} M R (MulZeroClass.toZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R _inst_2))) (DistribSMul.toSMulZeroClass.{u2, u1} M R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddCommGroup.toAddGroup.{u1} R (NonUnitalNonAssocRing.toAddCommGroup.{u1} R _inst_2))))) (DistribMulAction.toDistribSMul.{u2, u1} M R _inst_1 (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddCommGroup.toAddGroup.{u1} R (NonUnitalNonAssocRing.toAddCommGroup.{u1} R _inst_2)))) _inst_3)))) k x) (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R _inst_2)))))) -> (Eq.{succ u1} R x (OfNat.ofNat.{u1} R 0 (Zero.toOfNat0.{u1} R (MulZeroClass.toZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R _inst_2))))))) -> (forall {a : R} {b : R}, (Eq.{succ u1} R (HSMul.hSMul.{u2, u1, u1} M R R (instHSMul.{u2, u1} M R (SMulZeroClass.toSMul.{u2, u1} M R (MulZeroClass.toZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R _inst_2))) (DistribSMul.toSMulZeroClass.{u2, u1} M R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddCommGroup.toAddGroup.{u1} R (NonUnitalNonAssocRing.toAddCommGroup.{u1} R _inst_2))))) (DistribMulAction.toDistribSMul.{u2, u1} M R _inst_1 (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddCommGroup.toAddGroup.{u1} R (NonUnitalNonAssocRing.toAddCommGroup.{u1} R _inst_2)))) _inst_3)))) k a) (HSMul.hSMul.{u2, u1, u1} M R R (instHSMul.{u2, u1} M R (SMulZeroClass.toSMul.{u2, u1} M R (MulZeroClass.toZero.{u1} R (NonUnitalNonAssocSemiring.toMulZeroClass.{u1} R (NonUnitalNonAssocRing.toNonUnitalNonAssocSemiring.{u1} R _inst_2))) (DistribSMul.toSMulZeroClass.{u2, u1} M R (AddMonoid.toAddZeroClass.{u1} R (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddCommGroup.toAddGroup.{u1} R (NonUnitalNonAssocRing.toAddCommGroup.{u1} R _inst_2))))) (DistribMulAction.toDistribSMul.{u2, u1} M R _inst_1 (SubNegMonoid.toAddMonoid.{u1} R (AddGroup.toSubNegMonoid.{u1} R (AddCommGroup.toAddGroup.{u1} R (NonUnitalNonAssocRing.toAddCommGroup.{u1} R _inst_2)))) _inst_3)))) k b)) -> (Eq.{succ u1} R a b))
Case conversion may be inaccurate. Consider using '#align smul_cancel_of_non_zero_divisor smul_cancel_of_non_zero_divisorₓ'. -/
/-- `smul` by a `k : M` over a ring is injective, if `k` is not a zero divisor.
The general theory of such `k` is elaborated by `is_smul_regular`.
The typeclass that restricts all terms of `M` to have this property is `no_zero_smul_divisors`. -/
theorem smul_cancel_of_non_zero_divisor {M R : Type _} [Monoid M] [NonUnitalNonAssocRing R]
    [DistribMulAction M R] (k : M) (h : ∀ x : R, k • x = 0 → x = 0) {a b : R} (h' : k • a = k • b) :
    a = b := by
  rw [← sub_eq_zero]
  refine' h _ _
  rw [smul_sub, h', sub_self]
#align smul_cancel_of_non_zero_divisor smul_cancel_of_non_zero_divisor


/-
Copyright (c) 2021 Yaël Dillies. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yaël Dillies
-/
import data.int.basic
import algebra.char_zero
import order.locally_finite
import data.finset.locally_finite

/-!
# Finite intervals of integers

This file proves that `ℤ` is a `locally_finite_order` and calculates the cardinality of its
intervals as finsets and fintypes.
-/

open finset int

instance : locally_finite_order ℤ :=
{ finset_Icc := λ a b, (finset.range (b + 1 - a).to_nat).map $
    nat.cast_embedding.trans $ add_left_embedding a,
  finset_Ico := λ a b, (finset.range (b - a).to_nat).map $
    nat.cast_embedding.trans $ add_left_embedding a,
  finset_Ioc := λ a b, (finset.range (b - a).to_nat).map $
    nat.cast_embedding.trans $ add_left_embedding (a + 1),
  finset_Ioo := λ a b, (finset.range (b - a - 1).to_nat).map $
    nat.cast_embedding.trans $ add_left_embedding (a + 1),
  finset_mem_Icc := λ a b x, begin
    simp_rw [mem_map, exists_prop, mem_range, int.lt_to_nat, function.embedding.trans_apply,
      nat.cast_embedding_apply, add_left_embedding_apply, nat_cast_eq_coe_nat],
    split,
    { rintro ⟨a, h, rfl⟩,
      rw [lt_sub_iff_add_lt, int.lt_add_one_iff, add_comm] at h,
      exact ⟨int.le.intro rfl, h⟩ },
    { rintro ⟨ha, hb⟩,
      use (x - a).to_nat,
      rw ←lt_add_one_iff at hb,
      rw to_nat_sub_of_le ha,
      exact ⟨sub_lt_sub_right hb _, add_sub_cancel'_right _ _⟩ }
  end,
  finset_mem_Ico := λ a b x, begin
    simp_rw [mem_map, exists_prop, mem_range, int.lt_to_nat, function.embedding.trans_apply,
      nat.cast_embedding_apply, add_left_embedding_apply, nat_cast_eq_coe_nat],
    split,
    { rintro ⟨a, h, rfl⟩,
      exact ⟨int.le.intro rfl, lt_sub_iff_add_lt'.mp h⟩ },
    { rintro ⟨ha, hb⟩,
      use (x - a).to_nat,
      rw to_nat_sub_of_le ha,
      exact ⟨sub_lt_sub_right hb _, add_sub_cancel'_right _ _⟩ }
  end,
  finset_mem_Ioc := λ a b x, begin
    simp_rw [mem_map, exists_prop, mem_range, int.lt_to_nat, function.embedding.trans_apply,
      nat.cast_embedding_apply, add_left_embedding_apply, nat_cast_eq_coe_nat],
    split,
    { rintro ⟨a, h, rfl⟩,
      rw [←add_one_le_iff, le_sub_iff_add_le', add_comm _ (1 : ℤ), ←add_assoc] at h,
      exact ⟨int.le.intro rfl, h⟩ },
    { rintro ⟨ha, hb⟩,
      use (x - (a + 1)).to_nat,
      rw [to_nat_sub_of_le ha, ←add_one_le_iff, sub_add, add_sub_cancel],
      exact ⟨sub_le_sub_right hb _, add_sub_cancel'_right _ _⟩ }
  end,
  finset_mem_Ioo := λ a b x, begin
    simp_rw [mem_map, exists_prop, mem_range, int.lt_to_nat, function.embedding.trans_apply,
      nat.cast_embedding_apply, add_left_embedding_apply, nat_cast_eq_coe_nat],
    split,
    { rintro ⟨a, h, rfl⟩,
      rw [sub_sub, lt_sub_iff_add_lt'] at h,
      exact ⟨int.le.intro rfl, h⟩ },
    { rintro ⟨ha, hb⟩,
      use (x - (a + 1)).to_nat,
      rw [to_nat_sub_of_le ha, sub_sub],
      exact ⟨sub_lt_sub_right hb _, add_sub_cancel'_right _ _⟩ }
  end }

namespace int
variables (a b : ℤ)

lemma Icc_eq_finset_map :
  Icc a b = (finset.range (b + 1 - a).to_nat).map
    (nat.cast_embedding.trans $ add_left_embedding a) := rfl
lemma Ico_eq_finset_map :
  Ico a b = (finset.range (b - a).to_nat).map
    (nat.cast_embedding.trans $ add_left_embedding a) := rfl
lemma Ioc_eq_finset_map :
  Ioc a b = (finset.range (b - a).to_nat).map
    (nat.cast_embedding.trans $ add_left_embedding (a + 1)) := rfl
lemma Ioo_eq_finset_map :
  Ioo a b = (finset.range (b - a - 1).to_nat).map
    (nat.cast_embedding.trans $ add_left_embedding (a + 1)) := rfl

@[simp] lemma card_Icc : (Icc a b).card = (b + 1 - a).to_nat :=
by { change (finset.map _ _).card = _, rw [finset.card_map, finset.card_range] }

@[simp] lemma card_Ico : (Ico a b).card = (b - a).to_nat :=
by { change (finset.map _ _).card = _, rw [finset.card_map, finset.card_range] }

@[simp] lemma card_Ioc : (Ioc a b).card = (b - a).to_nat :=
by { change (finset.map _ _).card = _, rw [finset.card_map, finset.card_range] }

@[simp] lemma card_Ioo : (Ioo a b).card = (b - a - 1).to_nat :=
by { change (finset.map _ _).card = _, rw [finset.card_map, finset.card_range] }

lemma card_Icc_of_le (h : a ≤ b + 1) : ((Icc a b).card : ℤ) = b + 1 - a :=
by rw [card_Icc, to_nat_sub_of_le h]

lemma card_Ico_of_le (h : a ≤ b) : ((Ico a b).card : ℤ) = b - a :=
by rw [card_Ico, to_nat_sub_of_le h]

lemma card_Ioc_of_le (h : a ≤ b) : ((Ioc a b).card : ℤ) = b - a :=
by rw [card_Ioc, to_nat_sub_of_le h]

lemma card_Ioo_of_lt (h : a < b) : ((Ioo a b).card : ℤ) = b - a - 1 :=
by rw [card_Ioo, sub_sub, to_nat_sub_of_le h]

@[simp] lemma card_fintype_Icc : fintype.card (set.Icc a b) = (b + 1 - a).to_nat :=
by rw [←card_Icc, fintype.card_of_finset]

@[simp] lemma card_fintype_Ico : fintype.card (set.Ico a b) = (b - a).to_nat :=
by rw [←card_Ico, fintype.card_of_finset]

@[simp] lemma card_fintype_Ioc : fintype.card (set.Ioc a b) = (b - a).to_nat :=
by rw [←card_Ioc, fintype.card_of_finset]

@[simp] lemma card_fintype_Ioo : fintype.card (set.Ioo a b) = (b - a - 1).to_nat :=
by rw [←card_Ioo, fintype.card_of_finset]

lemma card_fintype_Icc_of_le (h : a ≤ b + 1) : (fintype.card (set.Icc a b) : ℤ) = b + 1 - a :=
by rw [card_fintype_Icc, to_nat_sub_of_le h]

lemma card_fintype_Ico_of_le (h : a ≤ b) : (fintype.card (set.Ico a b) : ℤ) = b - a :=
by rw [card_fintype_Ico, to_nat_sub_of_le h]

lemma card_fintype_Ioc_of_le (h : a ≤ b) : (fintype.card (set.Ioc a b) : ℤ) = b - a :=
by rw [card_fintype_Ioc, to_nat_sub_of_le h]

lemma card_fintype_Ioo_of_lt (h : a < b) : (fintype.card (set.Ioo a b) : ℤ) = b - a - 1 :=
by rw [card_fintype_Ioo, sub_sub, to_nat_sub_of_le h]

lemma image_Ico_mod (n a : ℤ) (h : 0 ≤ a) :
  (Ico n (n+a)).image (% a) = Ico 0 a :=
begin
  obtain rfl | ha := eq_or_lt_of_le h,
  { simp, },
  ext i,
  simp only [mem_image, exists_prop, mem_range, mem_Ico],
  split,
  { rintro ⟨i, h, rfl⟩, exact ⟨mod_nonneg i (ne_of_gt ha), mod_lt_of_pos i ha⟩, },
  intro hia,
  have hn := int.mod_add_div n a,
  obtain hi | hi := lt_or_le i (n % a),
  { refine ⟨i + a * (n/a + 1), ⟨_, _⟩, _⟩,
    { rw [add_comm (n/a), mul_add, mul_one, ← add_assoc],
      refine hn.symm.le.trans (add_le_add_right _ _),
      simpa only [zero_add] using add_le_add (hia.left) (int.mod_lt_of_pos n ha).le, },
    { refine lt_of_lt_of_le (add_lt_add_right hi (a * (n/a + 1))) _,
      rw [mul_add, mul_one, ← add_assoc, hn], },
    { rw [int.add_mul_mod_self_left, int.mod_eq_of_lt hia.left hia.right], } },
  { refine ⟨i + a * (n/a), ⟨_, _⟩, _⟩,
    { exact hn.symm.le.trans (add_le_add_right hi _), },
    { rw [add_comm n a],
      refine add_lt_add_of_lt_of_le hia.right (le_trans _ hn.le),
      simp only [zero_le, le_add_iff_nonneg_left],
      exact int.mod_nonneg n (ne_of_gt ha), },
    { rw [int.add_mul_mod_self_left, int.mod_eq_of_lt hia.left hia.right], } },
end

end int


/-
Copyright (c) 2020 Markus Himmel. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Markus Himmel
-/
import abelian
import exact
import pseudoelements

/-!
The construction used by mathlib to define homology is as follows: Find a map
`im_to_ker : image (C.d i) ⟶ kernel (C.d (i + 1))` and set `H^i := coker im_to_ker`.

However, we could also use the dual construction: Find a map
`coker_to_coim : cokernel (C.d i) ⟶ coimage (C.d i)` and set `H'^i := ker coker_to_coim`.

In this file, we show that these two constructions yield isomorphic cohomology objects. This result
is needed in the construction of the LES in cohomology via the snake lemma.
-/

open category_theory
open category_theory.limits

universes v u

namespace category_theory.abelian

open pseudoelements

section
variables {V : Type u} [𝒱 : category.{v} V] [abelian.{v} V]
include 𝒱

variables {A B C : V} {f : A ⟶ B} {g : B ⟶ C} (h : f ≫ g = 0)
include h

def im_to_ker : kernel (cokernel.π f) ⟶ kernel g :=
kernel.lift g (kernel.ι (cokernel.π f)) $
begin
  apply (preadditive.cancel_zero_iff_epi (factor_thru_image f)).1 (by apply_instance),
  rw ←category.assoc,
  rw image.fac,
  exact h,
end

def H := cokernel (im_to_ker h)

def coker_to_coim : cokernel f ⟶ cokernel (kernel.ι g) :=
cokernel.desc f (cokernel.π (kernel.ι g)) $
begin
  apply (preadditive.cancel_zero_iff_mono (factor_thru_coimage g)).1 (by apply_instance),
  rw category.assoc,
  rw coimage.fac,
  exact h,
end

def H' := kernel (coker_to_coim h)

def H_to_coker : (H h) ⟶ cokernel f :=
cokernel.desc (im_to_ker h) (kernel.ι g ≫ cokernel.π f)
begin
  rw ←category.assoc,
  erw limit.lift_π,
  erw kernel.condition,
end

def H_to_H' : H h ⟶ H' h :=
kernel.lift (coker_to_coim h) (H_to_coker h)
begin
  apply (preadditive.cancel_zero_iff_epi (cokernel.π (im_to_ker h))).1 (by apply_instance),
  rw ←category.assoc,
  erw colimit.ι_desc,
  dsimp,
  rw category.assoc,
  erw colimit.ι_desc,
  erw cokernel.condition,
end

local attribute [instance] object_to_sort
local attribute [instance] hom_to_fun

def ker_to_H' : kernel g ⟶ H' h :=
kernel.lift (coker_to_coim h) (kernel.ι g ≫ cokernel.π f)
begin
  rw category.assoc,
  erw colimit.ι_desc,
  erw cokernel.condition,
end

def H_to_H'_other : H h ⟶ H' h :=
cokernel.desc (im_to_ker h) (ker_to_H' h)
begin
  apply (preadditive.cancel_zero_iff_mono (kernel.ι (coker_to_coim h))).1 (by apply_instance),
  rw category.assoc,
  erw limit.lift_π,
  dsimp,
  rw ←category.assoc,
  erw limit.lift_π,
  erw kernel.condition,
end

lemma H_to_H'_eq_H_to_H'_other : H_to_H' h = H_to_H'_other h :=
begin
  apply (cancel_mono (kernel.ι (coker_to_coim h))).1,
  apply (cancel_epi (cokernel.π (im_to_ker h))).1,
  erw limit.lift_π,
  dsimp,
  erw colimit.ι_desc,
  dsimp,
  rw ←category.assoc,
  erw colimit.ι_desc,
  dsimp,
  erw limit.lift_π,
  refl,
end

instance H_to_H'_epi : epi (H_to_H' h) :=
begin
  rw H_to_H'_eq_H_to_H'_other,
  apply epi_of_pseudo_surjective,
  intro x,
  let y := (kernel.ι (coker_to_coim h) : _ ⟶ _) x,
  obtain ⟨z, hz⟩ := pseudo_surjective_of_epi (cokernel.π f) y,
  have : cokernel.π f ≫ coker_to_coim h = cokernel.π (kernel.ι g),
  { erw colimit.ι_desc, refl, },
  have : (cokernel.π (kernel.ι g) : _ ⟶ _) z = 0,
  { erw [←this, comp_apply, hz, ←comp_apply, kernel.condition, zero_apply], },
  obtain ⟨a, ha⟩ := (pseudo_exact_of_exact (cokernel_exact _)).2 _ this,
  use (cokernel.π (im_to_ker h) : _ ⟶ _) a,
  erw [←comp_apply, colimit.ι_desc],
  dsimp,
  apply pseudo_injective_of_mono (kernel.ι (coker_to_coim h)),
  erw [←comp_apply, limit.lift_π],
  dsimp,
  rw [comp_apply, ha, hz],
end

instance H_to_H'_mono : mono (H_to_H' h) :=
begin
  apply mono_of_zero_of_map_zero,
  intros a ha,
  obtain ⟨b, hb⟩ := pseudo_surjective_of_epi (cokernel.π (im_to_ker h)) a,
  let c := (kernel.ι g : _ ⟶ _) b,
  suffices : (cokernel.π f : _ ⟶ _) c = 0,
  { obtain ⟨d, hd⟩ := (pseudo_exact_of_exact (kernel_exact _)).2 _ this,
    have hc : c = (kernel.ι g : _ ⟶ _) b := rfl,
    rw ←hb,
    have : (im_to_ker h) d = b,
    { apply pseudo_injective_of_mono (kernel.ι g) _,
      rw ←comp_apply,
      erw limit.lift_π,
      dsimp,
      rw [hd, hc], },
    rw [←this, ←comp_apply, cokernel.condition, zero_apply] },
  have : (kernel.ι (coker_to_coim h) : _ ⟶ _) ((H_to_H' h) a) = 0,
  { erw [ha, apply_zero], },
  conv_lhs at this { erw [←comp_apply, limit.lift_π], dsimp,
    erw [←hb, ←comp_apply, colimit.ι_desc], dsimp,
    rw comp_apply },
  exact this,
end

instance H_to_H'_iso : is_iso (H_to_H' h) :=
mono_epi_iso _

def H_iso_H' : H h ≅ H' h :=
as_iso $ H_to_H' h

end

end category_theory.abelian