/-
Copyright (c) 2018 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Callum Sutton, Yury Kudryashov
-/
import algebra.big_operators.basic
import algebra.field.basic
import algebra.hom.equiv
import algebra.ring.opposite

/-!
# (Semi)ring equivs

In this file we define extension of `equiv` called `ring_equiv`, which is a datatype representing an
isomorphism of `semiring`s, `ring`s, `division_ring`s, or `field`s. We also introduce the
corresponding group of automorphisms `ring_aut`.

## Notations

* ``infix ` ≃+* `:25 := ring_equiv``

The extended equiv have coercions to functions, and the coercion is the canonical notation when
treating the isomorphism as maps.

## Implementation notes

The fields for `ring_equiv` now avoid the unbundled `is_mul_hom` and `is_add_hom`, as these are
deprecated.

Definition of multiplication in the groups of automorphisms agrees with function composition,
multiplication in `equiv.perm`, and multiplication in `category_theory.End`, not with
`category_theory.comp`.

## Tags

equiv, mul_equiv, add_equiv, ring_equiv, mul_aut, add_aut, ring_aut
-/

open_locale big_operators

variables {F α β R S S' : Type*}

set_option old_structure_cmd true

/-- An equivalence between two (semi)rings that preserves the algebraic structure. -/
structure ring_equiv (R S : Type*) [has_mul R] [has_add R] [has_mul S] [has_add S]
  extends R ≃ S, R ≃* S, R ≃+ S

infix ` ≃+* `:25 := ring_equiv

/-- The "plain" equivalence of types underlying an equivalence of (semi)rings. -/
add_decl_doc ring_equiv.to_equiv

/-- The equivalence of additive monoids underlying an equivalence of (semi)rings. -/
add_decl_doc ring_equiv.to_add_equiv

/-- The equivalence of multiplicative monoids underlying an equivalence of (semi)rings. -/
add_decl_doc ring_equiv.to_mul_equiv

/-- `ring_equiv_class F R S` states that `F` is a type of ring structure preserving equivalences.
You should extend this class when you extend `ring_equiv`. -/
class ring_equiv_class (F : Type*) (R S : out_param Type*)
  [has_mul R] [has_add R] [has_mul S] [has_add S]
  extends mul_equiv_class F R S :=
(map_add : ∀ (f : F) a b, f (a + b) = f a + f b)

namespace ring_equiv_class

@[priority 100] -- See note [lower instance priority]
instance to_add_equiv_class (F R S : Type*)
  [has_mul R] [has_add R] [has_mul S] [has_add S] [h : ring_equiv_class F R S] :
  add_equiv_class F R S :=
{ coe := coe_fn,
  .. h }

@[priority 100] -- See note [lower instance priority]
instance to_ring_hom_class (F R S : Type*)
  [non_assoc_semiring R] [non_assoc_semiring S] [h : ring_equiv_class F R S] :
  ring_hom_class F R S :=
{ coe := coe_fn,
  coe_injective' := fun_like.coe_injective,
  map_zero := map_zero,
  map_one := map_one,
  .. h }

end ring_equiv_class

instance [has_mul α] [has_add α] [has_mul β] [has_add β] [ring_equiv_class F α β] :
  has_coe_t F (α ≃+* β) :=
⟨λ f, { to_fun := f, inv_fun := equiv_like.inv f, left_inv := equiv_like.left_inv f,
  right_inv := equiv_like.right_inv f, map_mul' := map_mul f, map_add' := map_add f }⟩

namespace ring_equiv

section basic

variables [has_mul R] [has_add R] [has_mul S] [has_add S] [has_mul S'] [has_add S']

instance : ring_equiv_class (R ≃+* S) R S :=
{ coe := to_fun,
  inv := inv_fun,
  coe_injective' := λ e f h₁ h₂, by { cases e, cases f, congr' },
  map_add := map_add',
  map_mul := map_mul',
  left_inv := ring_equiv.left_inv,
  right_inv := ring_equiv.right_inv }

instance : has_coe_to_fun (R ≃+* S) (λ _, R → S) := ⟨ring_equiv.to_fun⟩

@[simp] lemma to_equiv_eq_coe (f : R ≃+* S) : f.to_equiv = f := rfl

@[simp] lemma to_fun_eq_coe (f : R ≃+* S) : f.to_fun = f := rfl

@[simp] lemma coe_to_equiv (f : R ≃+* S) : ⇑(f : R ≃ S) = f := rfl

/-- A ring isomorphism preserves multiplication. -/
protected lemma map_mul (e : R ≃+* S) (x y : R) : e (x * y) = e x * e y := map_mul e x y

/-- A ring isomorphism preserves addition. -/
protected lemma map_add (e : R ≃+* S) (x y : R) : e (x + y) = e x + e y := map_add e x y

/-- Two ring isomorphisms agree if they are defined by the
    same underlying function. -/
@[ext] lemma ext {f g : R ≃+* S} (h : ∀ x, f x = g x) : f = g := fun_like.ext f g h

@[simp] theorem coe_mk (e e' h₁ h₂ h₃ h₄) :
  ⇑(⟨e, e', h₁, h₂, h₃, h₄⟩ : R ≃+* S) = e := rfl

@[simp] theorem mk_coe (e : R ≃+* S) (e' h₁ h₂ h₃ h₄) :
  (⟨e, e', h₁, h₂, h₃, h₄⟩ : R ≃+* S) = e := ext $ λ _, rfl

protected lemma congr_arg {f : R ≃+* S} {x x' : R} : x = x' → f x = f x' := fun_like.congr_arg f

protected lemma congr_fun {f g : R ≃+* S} (h : f = g) (x : R) : f x = g x := fun_like.congr_fun h x

protected lemma ext_iff {f g : R ≃+* S} : f = g ↔ ∀ x, f x = g x := fun_like.ext_iff

@[simp] lemma to_add_equiv_eq_coe (f : R ≃+* S) : f.to_add_equiv = ↑f := rfl

@[simp] lemma to_mul_equiv_eq_coe (f : R ≃+* S) : f.to_mul_equiv = ↑f := rfl

@[simp, norm_cast] lemma coe_to_mul_equiv (f : R ≃+* S) : ⇑(f : R ≃* S) = f := rfl

@[simp, norm_cast] lemma coe_to_add_equiv (f : R ≃+* S) : ⇑(f : R ≃+ S) = f := rfl

/-- The `ring_equiv` between two semirings with a unique element. -/
def ring_equiv_of_unique_of_unique {M N}
  [unique M] [unique N] [has_add M] [has_mul M] [has_add N] [has_mul N] : M ≃+* N :=
{ ..add_equiv.add_equiv_of_unique_of_unique,
  ..mul_equiv.mul_equiv_of_unique_of_unique}

instance {M N} [unique M] [unique N] [has_add M] [has_mul M] [has_add N] [has_mul N] :
  unique (M ≃+* N) :=
{ default := ring_equiv_of_unique_of_unique,
  uniq := λ _, ext $ λ x, subsingleton.elim _ _ }

variable (R)

/-- The identity map is a ring isomorphism. -/
@[refl] protected def refl : R ≃+* R := { .. mul_equiv.refl R, .. add_equiv.refl R }

@[simp] lemma refl_apply (x : R) : ring_equiv.refl R x = x := rfl

@[simp] lemma coe_add_equiv_refl : (ring_equiv.refl R : R ≃+ R) = add_equiv.refl R := rfl

@[simp] lemma coe_mul_equiv_refl : (ring_equiv.refl R : R ≃* R) = mul_equiv.refl R := rfl

instance : inhabited (R ≃+* R) := ⟨ring_equiv.refl R⟩

variables {R}

/-- The inverse of a ring isomorphism is a ring isomorphism. -/
@[symm] protected def symm (e : R ≃+* S) : S ≃+* R :=
{ .. e.to_mul_equiv.symm, .. e.to_add_equiv.symm }

/-- See Note [custom simps projection] -/
def simps.symm_apply (e : R ≃+* S) : S → R := e.symm

initialize_simps_projections ring_equiv (to_fun → apply, inv_fun → symm_apply)

@[simp] lemma inv_fun_eq_symm (f : R ≃+* S) : f.inv_fun = f.symm := rfl

@[simp] lemma symm_symm (e : R ≃+* S) : e.symm.symm = e := ext $ λ x, rfl

lemma symm_bijective : function.bijective (ring_equiv.symm : (R ≃+* S) → (S ≃+* R)) :=
equiv.bijective ⟨ring_equiv.symm, ring_equiv.symm, symm_symm, symm_symm⟩

@[simp] lemma mk_coe' (e : R ≃+* S) (f h₁ h₂ h₃ h₄) :
  (ring_equiv.mk f ⇑e h₁ h₂ h₃ h₄ : S ≃+* R) = e.symm :=
symm_bijective.injective $ ext $ λ x, rfl

@[simp] lemma symm_mk (f : R → S) (g h₁ h₂ h₃ h₄) :
  (mk f g h₁ h₂ h₃ h₄).symm =
  { to_fun := g, inv_fun := f, ..(mk f g h₁ h₂ h₃ h₄).symm} := rfl

/-- Transitivity of `ring_equiv`. -/
@[trans] protected def trans (e₁ : R ≃+* S) (e₂ : S ≃+* S') : R ≃+* S' :=
{ .. (e₁.to_mul_equiv.trans e₂.to_mul_equiv), .. (e₁.to_add_equiv.trans e₂.to_add_equiv) }

@[simp] lemma trans_apply (e₁ : R ≃+* S) (e₂ : S ≃+* S') (a : R) :
  e₁.trans e₂ a = e₂ (e₁ a) := rfl

protected lemma bijective (e : R ≃+* S) : function.bijective e := equiv_like.bijective e
protected lemma injective (e : R ≃+* S) : function.injective e := equiv_like.injective e
protected lemma surjective (e : R ≃+* S) : function.surjective e := equiv_like.surjective e

@[simp] lemma apply_symm_apply (e : R ≃+* S) : ∀ x, e (e.symm x) = x := e.to_equiv.apply_symm_apply
@[simp] lemma symm_apply_apply (e : R ≃+* S) : ∀ x, e.symm (e x) = x := e.to_equiv.symm_apply_apply

lemma image_eq_preimage (e : R ≃+* S) (s : set R) : e '' s = e.symm ⁻¹' s :=
e.to_equiv.image_eq_preimage s

end basic

section opposite
open mul_opposite

/-- A ring iso `α ≃+* β` can equivalently be viewed as a ring iso `αᵐᵒᵖ ≃+* βᵐᵒᵖ`. -/
@[simps]
protected def op {α β} [has_add α] [has_mul α] [has_add β] [has_mul β] :
  (α ≃+* β) ≃ (αᵐᵒᵖ ≃+* βᵐᵒᵖ) :=
{ to_fun    := λ f, { ..f.to_add_equiv.mul_op, ..f.to_mul_equiv.op},
  inv_fun   := λ f, { ..add_equiv.mul_op.symm f.to_add_equiv, ..mul_equiv.op.symm f.to_mul_equiv },
  left_inv  := λ f, by { ext, refl },
  right_inv := λ f, by { ext, refl } }

/-- The 'unopposite' of a ring iso `αᵐᵒᵖ ≃+* βᵐᵒᵖ`. Inverse to `ring_equiv.op`. -/
@[simp] protected def unop {α β} [has_add α] [has_mul α] [has_add β] [has_mul β] :
  (αᵐᵒᵖ ≃+* βᵐᵒᵖ) ≃ (α ≃+* β) := ring_equiv.op.symm

section comm_semiring

variables (R) [comm_semiring R]

/-- A commutative ring is isomorphic to its opposite. -/
def to_opposite : R ≃+* Rᵐᵒᵖ :=
{ map_add' := λ x y, rfl,
  map_mul' := λ x y, mul_comm (op y) (op x),
  .. mul_opposite.op_equiv }

@[simp]
lemma to_opposite_apply (r : R) : to_opposite R r = op r := rfl

@[simp]
lemma to_opposite_symm_apply (r : Rᵐᵒᵖ) : (to_opposite R).symm r = unop r := rfl

end comm_semiring

end opposite

section non_unital_semiring

variables [non_unital_non_assoc_semiring R] [non_unital_non_assoc_semiring S]
  (f : R ≃+* S) (x y : R)

/-- A ring isomorphism sends zero to zero. -/
protected lemma map_zero : f 0 = 0 := map_zero f

variable {x}

protected lemma map_eq_zero_iff : f x = 0 ↔ x = 0 := add_equiv_class.map_eq_zero_iff f

lemma map_ne_zero_iff : f x ≠ 0 ↔ x ≠ 0 := add_equiv_class.map_ne_zero_iff f

end non_unital_semiring

section semiring

variables [non_assoc_semiring R] [non_assoc_semiring S] (f : R ≃+* S) (x y : R)

/-- A ring isomorphism sends one to one. -/
protected lemma map_one : f 1 = 1 := map_one f

variable {x}

protected lemma map_eq_one_iff : f x = 1 ↔ x = 1 := mul_equiv_class.map_eq_one_iff f

lemma map_ne_one_iff : f x ≠ 1 ↔ x ≠ 1 := mul_equiv_class.map_ne_one_iff f

/-- Produce a ring isomorphism from a bijective ring homomorphism. -/
noncomputable def of_bijective (f : R →+* S) (hf : function.bijective f) : R ≃+* S :=
{ .. equiv.of_bijective f hf, .. f }

@[simp] lemma coe_of_bijective (f : R →+* S) (hf : function.bijective f) :
  (of_bijective f hf : R → S) = f := rfl

lemma of_bijective_apply (f : R →+* S) (hf : function.bijective f) (x : R) :
  of_bijective f hf x = f x := rfl

/-- A family of ring isomorphisms `Π j, (R j ≃+* S j)` generates a
ring isomorphisms between `Π j, R j` and `Π j, S j`.

This is the `ring_equiv` version of `equiv.Pi_congr_right`, and the dependent version of
`ring_equiv.arrow_congr`.
-/
@[simps apply]
def Pi_congr_right {ι : Type*} {R S : ι → Type*}
  [Π i, semiring (R i)] [Π i, semiring (S i)]
  (e : Π i, R i ≃+* S i) : (Π i, R i) ≃+* Π i, S i :=
{ to_fun := λ x j, e j (x j),
  inv_fun := λ x j, (e j).symm (x j),
  .. @mul_equiv.Pi_congr_right ι R S _ _ (λ i, (e i).to_mul_equiv),
  .. @add_equiv.Pi_congr_right ι R S _ _ (λ i, (e i).to_add_equiv) }

@[simp]
lemma Pi_congr_right_refl {ι : Type*} {R : ι → Type*} [Π i, semiring (R i)] :
  Pi_congr_right (λ i, ring_equiv.refl (R i)) = ring_equiv.refl _ := rfl

@[simp]
lemma Pi_congr_right_symm {ι : Type*} {R S : ι → Type*}
  [Π i, semiring (R i)] [Π i, semiring (S i)]
  (e : Π i, R i ≃+* S i) : (Pi_congr_right e).symm = (Pi_congr_right $ λ i, (e i).symm) := rfl

@[simp]
lemma Pi_congr_right_trans {ι : Type*} {R S T : ι → Type*}
  [Π i, semiring (R i)] [Π i, semiring (S i)] [Π i, semiring (T i)]
  (e : Π i, R i ≃+* S i) (f : Π i, S i ≃+* T i) :
  (Pi_congr_right e).trans (Pi_congr_right f) = (Pi_congr_right $ λ i, (e i).trans (f i)) := rfl

end semiring

section

variables [non_assoc_ring R] [non_assoc_ring S] (f : R ≃+* S) (x y : R)

protected lemma map_neg : f (-x) = -f x := map_neg f x

protected lemma map_sub : f (x - y) = f x - f y := map_sub f x y

@[simp] lemma map_neg_one : f (-1) = -1 := f.map_one ▸ f.map_neg 1

end

section semiring_hom

variables [non_assoc_semiring R] [non_assoc_semiring S] [non_assoc_semiring S']

/-- Reinterpret a ring equivalence as a ring homomorphism. -/
def to_ring_hom (e : R ≃+* S) : R →+* S :=
{ .. e.to_mul_equiv.to_monoid_hom, .. e.to_add_equiv.to_add_monoid_hom }

lemma to_ring_hom_injective : function.injective (to_ring_hom : (R ≃+* S) → R →+* S) :=
λ f g h, ring_equiv.ext (ring_hom.ext_iff.1 h)

instance has_coe_to_ring_hom : has_coe (R ≃+* S) (R →+* S) := ⟨ring_equiv.to_ring_hom⟩

lemma to_ring_hom_eq_coe (f : R ≃+* S) : f.to_ring_hom = ↑f := rfl

@[simp, norm_cast] lemma coe_to_ring_hom (f : R ≃+* S) : ⇑(f : R →+* S) = f := rfl

lemma coe_ring_hom_inj_iff {R S : Type*} [non_assoc_semiring R] [non_assoc_semiring S]
  (f g : R ≃+* S) :
  f = g ↔ (f : R →+* S) = g :=
⟨congr_arg _, λ h, ext $ ring_hom.ext_iff.mp h⟩

/-- Reinterpret a ring equivalence as a monoid homomorphism. -/
abbreviation to_monoid_hom (e : R ≃+* S) : R →* S := e.to_ring_hom.to_monoid_hom

/-- Reinterpret a ring equivalence as an `add_monoid` homomorphism. -/
abbreviation to_add_monoid_hom (e : R ≃+* S) : R →+ S := e.to_ring_hom.to_add_monoid_hom

/-- The two paths coercion can take to an `add_monoid_hom` are equivalent -/
lemma to_add_monoid_hom_commutes (f : R ≃+* S) :
  (f : R →+* S).to_add_monoid_hom = (f : R ≃+ S).to_add_monoid_hom :=
rfl

/-- The two paths coercion can take to an `monoid_hom` are equivalent -/
lemma to_monoid_hom_commutes (f : R ≃+* S) :
  (f : R →+* S).to_monoid_hom = (f : R ≃* S).to_monoid_hom :=
rfl

/-- The two paths coercion can take to an `equiv` are equivalent -/
lemma to_equiv_commutes (f : R ≃+* S) :
  (f : R ≃+ S).to_equiv = (f : R ≃* S).to_equiv :=
rfl

@[simp]
lemma to_ring_hom_refl : (ring_equiv.refl R).to_ring_hom = ring_hom.id R := rfl

@[simp]
lemma to_monoid_hom_refl : (ring_equiv.refl R).to_monoid_hom = monoid_hom.id R := rfl

@[simp]
lemma to_add_monoid_hom_refl : (ring_equiv.refl R).to_add_monoid_hom = add_monoid_hom.id R := rfl

@[simp]
lemma to_ring_hom_apply_symm_to_ring_hom_apply (e : R ≃+* S) :
  ∀ (y : S), e.to_ring_hom (e.symm.to_ring_hom y) = y :=
e.to_equiv.apply_symm_apply

@[simp]
lemma symm_to_ring_hom_apply_to_ring_hom_apply (e : R ≃+* S) :
  ∀ (x : R), e.symm.to_ring_hom (e.to_ring_hom x) = x :=
equiv.symm_apply_apply (e.to_equiv)

@[simp]
lemma to_ring_hom_trans (e₁ : R ≃+* S) (e₂ : S ≃+* S') :
  (e₁.trans e₂).to_ring_hom = e₂.to_ring_hom.comp e₁.to_ring_hom := rfl

@[simp]
lemma to_ring_hom_comp_symm_to_ring_hom (e : R ≃+* S) :
  e.to_ring_hom.comp e.symm.to_ring_hom = ring_hom.id _ :=
by { ext, simp }

@[simp]
lemma symm_to_ring_hom_comp_to_ring_hom (e : R ≃+* S) :
  e.symm.to_ring_hom.comp e.to_ring_hom = ring_hom.id _ :=
by { ext, simp }

/--
Construct an equivalence of rings from homomorphisms in both directions, which are inverses.
-/
def of_hom_inv (hom : R →+* S) (inv : S →+* R)
  (hom_inv_id : inv.comp hom = ring_hom.id R) (inv_hom_id : hom.comp inv = ring_hom.id S) :
  R ≃+* S :=
{ inv_fun := inv,
  left_inv := λ x, ring_hom.congr_fun hom_inv_id x,
  right_inv := λ x, ring_hom.congr_fun inv_hom_id x,
  ..hom }

@[simp]
lemma of_hom_inv_apply (hom : R →+* S) (inv : S →+* R) (hom_inv_id inv_hom_id) (r : R) :
  (of_hom_inv hom inv hom_inv_id inv_hom_id) r = hom r := rfl

@[simp]
lemma of_hom_inv_symm_apply (hom : R →+* S) (inv : S →+* R) (hom_inv_id inv_hom_id) (s : S) :
  (of_hom_inv hom inv hom_inv_id inv_hom_id).symm s = inv s := rfl

end semiring_hom

section big_operators

lemma map_list_prod [semiring R] [semiring S] (f : R ≃+* S) (l : list R) :
  f l.prod = (l.map f).prod := f.to_ring_hom.map_list_prod l

lemma map_list_sum [non_assoc_semiring R] [non_assoc_semiring S] (f : R ≃+* S) (l : list R) :
  f l.sum = (l.map f).sum := f.to_ring_hom.map_list_sum l

/-- An isomorphism into the opposite ring acts on the product by acting on the reversed elements -/
lemma unop_map_list_prod [semiring R] [semiring S] (f : R ≃+* Sᵐᵒᵖ) (l : list R) :
  mul_opposite.unop (f l.prod) = (l.map (mul_opposite.unop ∘ f)).reverse.prod :=
f.to_ring_hom.unop_map_list_prod l

lemma map_multiset_prod [comm_semiring R] [comm_semiring S] (f : R ≃+* S) (s : multiset R) :
  f s.prod = (s.map f).prod := f.to_ring_hom.map_multiset_prod s

lemma map_multiset_sum [non_assoc_semiring R] [non_assoc_semiring S]
  (f : R ≃+* S) (s : multiset R) : f s.sum = (s.map f).sum := f.to_ring_hom.map_multiset_sum s

lemma map_prod {α : Type*} [comm_semiring R] [comm_semiring S] (g : R ≃+* S) (f : α → R)
  (s : finset α) : g (∏ x in s, f x) = ∏ x in s, g (f x) :=
g.to_ring_hom.map_prod f s

lemma map_sum {α : Type*} [non_assoc_semiring R] [non_assoc_semiring S]
  (g : R ≃+* S) (f : α → R) (s : finset α) : g (∑ x in s, f x) = ∑ x in s, g (f x) :=
g.to_ring_hom.map_sum f s

end big_operators

section division_ring

variables {K K' : Type*} [division_ring K] [division_ring K']
  (g : K ≃+* K') (x y : K)

lemma map_inv : g x⁻¹ = (g x)⁻¹ := g.to_ring_hom.map_inv x

lemma map_div : g (x / y) = g x / g y := g.to_ring_hom.map_div x y

end division_ring

section group_power

variables [semiring R] [semiring S]

protected lemma map_pow (f : R ≃+* S) (a) : ∀ n : ℕ, f (a ^ n) = (f a) ^ n := map_pow f a

end group_power

end ring_equiv

namespace mul_equiv

/-- Gives a `ring_equiv` from a `mul_equiv` preserving addition.-/
def to_ring_equiv {R : Type*} {S : Type*} [has_add R] [has_add S] [has_mul R] [has_mul S]
  (h : R ≃* S) (H : ∀ x y : R, h (x + y) = h x + h y) : R ≃+* S :=
{..h.to_equiv, ..h, ..add_equiv.mk' h.to_equiv H }

end mul_equiv

namespace ring_equiv

variables [has_add R] [has_add S] [has_mul R] [has_mul S]

@[simp] theorem self_trans_symm (e : R ≃+* S) : e.trans e.symm = ring_equiv.refl R := ext e.3
@[simp] theorem symm_trans_self (e : R ≃+* S) : e.symm.trans e = ring_equiv.refl S := ext e.4

/-- If two rings are isomorphic, and the second is a domain, then so is the first. -/
protected lemma is_domain
  {A : Type*} (B : Type*) [ring A] [ring B] [is_domain B]
  (e : A ≃+* B) : is_domain A :=
{ eq_zero_or_eq_zero_of_mul_eq_zero := λ x y hxy,
    have e x * e y = 0, by rw [← e.map_mul, hxy, e.map_zero],
    by simpa using eq_zero_or_eq_zero_of_mul_eq_zero this,
  exists_pair_ne := ⟨e.symm 0, e.symm 1, e.symm.injective.ne zero_ne_one⟩ }

end ring_equiv


/-
Copyright (c) 2022 Antoine Chambert-Loir. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Antoine Chambert-Loir

! This file was ported from Lean 3 source module group_theory.group_action.fixing_subgroup
! leanprover-community/mathlib commit fac369018417f980cec5fcdafc766a69f88d8cfe
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.GroupTheory.Subgroup.Actions
import Mathbin.GroupTheory.GroupAction.Basic

/-!

# Fixing submonoid, fixing subgroup of an action

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

In the presence of of an action of a monoid or a group,
this file defines the fixing submonoid or the fixing subgroup,
and relates it to the set of fixed points via a Galois connection.

## Main definitions

* `fixing_submonoid M s` : in the presence of `mul_action M α` (with `monoid M`)
  it is the `submonoid M` consisting of elements which fix `s : set α` pointwise.

* `fixing_submonoid_fixed_points_gc M α` is the `galois_connection`
  that relates `fixing_submonoid` with `fixed_points`.

* `fixing_subgroup M s` : in the presence of `mul_action M α` (with `group M`)
  it is the `subgroup M` consisting of elements which fix `s : set α` pointwise.

* `fixing_subgroup_fixed_points_gc M α` is the `galois_connection`
  that relates `fixing_subgroup` with `fixed_points`.

TODO :

* Maybe other lemmas are useful

* Treat semigroups ?

-/

section Monoid

open MulAction

variable (M : Type _) {α : Type _} [Monoid M] [MulAction M α]

#print fixingSubmonoid /-
/-- The submonoid fixing a set under a `mul_action`. -/
@[to_additive " The additive submonoid fixing a set under an `add_action`. "]
def fixingSubmonoid (s : Set α) : Submonoid M
    where
  carrier := { ϕ : M | ∀ x : s, ϕ • (x : α) = x }
  one_mem' _ := one_smul _ _
  mul_mem' x y hx hy z := by rw [mul_smul, hy z, hx z]
#align fixing_submonoid fixingSubmonoid
#align fixing_add_submonoid fixingAddSubmonoid
-/

/- warning: mem_fixing_submonoid_iff -> mem_fixingSubmonoid_iff is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) {α : Type.{u2}} [_inst_1 : Monoid.{u1} M] [_inst_2 : MulAction.{u1, u2} M α _inst_1] {s : Set.{u2} α} {m : M}, Iff (Membership.Mem.{u1, u1} M (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (SetLike.hasMem.{u1, u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) M (Submonoid.setLike.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) m (fixingSubmonoid.{u1, u2} M α _inst_1 _inst_2 s)) (forall (y : α), (Membership.Mem.{u2, u2} α (Set.{u2} α) (Set.hasMem.{u2} α) y s) -> (Eq.{succ u2} α (SMul.smul.{u1, u2} M α (MulAction.toHasSmul.{u1, u2} M α _inst_1 _inst_2) m y) y))
but is expected to have type
  forall (M : Type.{u1}) {α : Type.{u2}} [_inst_1 : Monoid.{u1} M] [_inst_2 : MulAction.{u1, u2} M α _inst_1] {s : Set.{u2} α} {m : M}, Iff (Membership.mem.{u1, u1} M (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (SetLike.instMembership.{u1, u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) M (Submonoid.instSetLikeSubmonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) m (fixingSubmonoid.{u1, u2} M α _inst_1 _inst_2 s)) (forall (y : α), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) y s) -> (Eq.{succ u2} α (HSMul.hSMul.{u1, u2, u2} M α α (instHSMul.{u1, u2} M α (MulAction.toSMul.{u1, u2} M α _inst_1 _inst_2)) m y) y))
Case conversion may be inaccurate. Consider using '#align mem_fixing_submonoid_iff mem_fixingSubmonoid_iffₓ'. -/
theorem mem_fixingSubmonoid_iff {s : Set α} {m : M} :
    m ∈ fixingSubmonoid M s ↔ ∀ y ∈ s, m • y = y :=
  ⟨fun hg y hy => hg ⟨y, hy⟩, fun h ⟨y, hy⟩ => h y hy⟩
#align mem_fixing_submonoid_iff mem_fixingSubmonoid_iff

variable (α)

#print fixingSubmonoid_fixedPoints_gc /-
/-- The Galois connection between fixing submonoids and fixed points of a monoid action -/
theorem fixingSubmonoid_fixedPoints_gc :
    GaloisConnection (OrderDual.toDual ∘ fixingSubmonoid M)
      ((fun P : Submonoid M => fixedPoints P α) ∘ OrderDual.ofDual) :=
  fun s P => ⟨fun h s hs p => h p.2 ⟨s, hs⟩, fun h p hp s => h s.2 ⟨p, hp⟩⟩
#align fixing_submonoid_fixed_points_gc fixingSubmonoid_fixedPoints_gc
-/

/- warning: fixing_submonoid_antitone -> fixingSubmonoid_antitone is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Monoid.{u1} M] [_inst_2 : MulAction.{u1, u2} M α _inst_1], Antitone.{u2, u1} (Set.{u2} α) (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (PartialOrder.toPreorder.{u2} (Set.{u2} α) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} α) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.completeBooleanAlgebra.{u2} α))))))) (PartialOrder.toPreorder.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (SetLike.partialOrder.{u1, u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) M (Submonoid.setLike.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)))) (fun (s : Set.{u2} α) => fixingSubmonoid.{u1, u2} M α _inst_1 _inst_2 s)
but is expected to have type
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Monoid.{u1} M] [_inst_2 : MulAction.{u1, u2} M α _inst_1], Antitone.{u2, u1} (Set.{u2} α) (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (PartialOrder.toPreorder.{u2} (Set.{u2} α) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} α) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.instCompleteBooleanAlgebraSet.{u2} α))))))) (PartialOrder.toPreorder.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (CompleteSemilatticeInf.toPartialOrder.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Submonoid.instCompleteLatticeSubmonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))))) (fun (s : Set.{u2} α) => fixingSubmonoid.{u1, u2} M α _inst_1 _inst_2 s)
Case conversion may be inaccurate. Consider using '#align fixing_submonoid_antitone fixingSubmonoid_antitoneₓ'. -/
theorem fixingSubmonoid_antitone : Antitone fun s : Set α => fixingSubmonoid M s :=
  (fixingSubmonoid_fixedPoints_gc M α).monotone_l
#align fixing_submonoid_antitone fixingSubmonoid_antitone

/- warning: fixed_points_antitone -> fixedPoints_antitone is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Monoid.{u1} M] [_inst_2 : MulAction.{u1, u2} M α _inst_1], Antitone.{u1, u2} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Set.{u2} α) (PartialOrder.toPreorder.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (SetLike.partialOrder.{u1, u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) M (Submonoid.setLike.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)))) (PartialOrder.toPreorder.{u2} (Set.{u2} α) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} α) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.completeBooleanAlgebra.{u2} α))))))) (fun (P : Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) => MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) M (Submonoid.setLike.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) P) α (Submonoid.toMonoid.{u1} M _inst_1 P) (Submonoid.mulAction.{u1, u2} M α _inst_1 _inst_2 P))
but is expected to have type
  forall (M : Type.{u2}) (α : Type.{u1}) [_inst_1 : Monoid.{u2} M] [_inst_2 : MulAction.{u2, u1} M α _inst_1], Antitone.{u2, u1} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (Set.{u1} α) (PartialOrder.toPreorder.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (CompleteSemilatticeInf.toPartialOrder.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (Submonoid.instCompleteLatticeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))))) (PartialOrder.toPreorder.{u1} (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α))))))) (fun (P : Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) => MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (SetLike.instMembership.{u2, u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) M (Submonoid.instSetLikeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))) x P)) α (Submonoid.toMonoid.{u2} M _inst_1 P) (Submonoid.mulAction.{u2, u1} M α _inst_1 _inst_2 P))
Case conversion may be inaccurate. Consider using '#align fixed_points_antitone fixedPoints_antitoneₓ'. -/
theorem fixedPoints_antitone : Antitone fun P : Submonoid M => fixedPoints P α :=
  (fixingSubmonoid_fixedPoints_gc M α).monotone_u.dual_left
#align fixed_points_antitone fixedPoints_antitone

/- warning: fixing_submonoid_union -> fixingSubmonoid_union is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Monoid.{u1} M] [_inst_2 : MulAction.{u1, u2} M α _inst_1] {s : Set.{u2} α} {t : Set.{u2} α}, Eq.{succ u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (fixingSubmonoid.{u1, u2} M α _inst_1 _inst_2 (Union.union.{u2} (Set.{u2} α) (Set.hasUnion.{u2} α) s t)) (Inf.inf.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Submonoid.hasInf.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (fixingSubmonoid.{u1, u2} M α _inst_1 _inst_2 s) (fixingSubmonoid.{u1, u2} M α _inst_1 _inst_2 t))
but is expected to have type
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Monoid.{u1} M] [_inst_2 : MulAction.{u1, u2} M α _inst_1] {s : Set.{u2} α} {t : Set.{u2} α}, Eq.{succ u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (fixingSubmonoid.{u1, u2} M α _inst_1 _inst_2 (Union.union.{u2} (Set.{u2} α) (Set.instUnionSet.{u2} α) s t)) (Inf.inf.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Submonoid.instInfSubmonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (fixingSubmonoid.{u1, u2} M α _inst_1 _inst_2 s) (fixingSubmonoid.{u1, u2} M α _inst_1 _inst_2 t))
Case conversion may be inaccurate. Consider using '#align fixing_submonoid_union fixingSubmonoid_unionₓ'. -/
/-- Fixing submonoid of union is intersection -/
theorem fixingSubmonoid_union {s t : Set α} :
    fixingSubmonoid M (s ∪ t) = fixingSubmonoid M s ⊓ fixingSubmonoid M t :=
  (fixingSubmonoid_fixedPoints_gc M α).l_sup
#align fixing_submonoid_union fixingSubmonoid_union

#print fixingSubmonoid_unionᵢ /-
/-- Fixing submonoid of Union is intersection -/
theorem fixingSubmonoid_unionᵢ {ι : Sort _} {s : ι → Set α} :
    fixingSubmonoid M (⋃ i, s i) = ⨅ i, fixingSubmonoid M (s i) :=
  (fixingSubmonoid_fixedPoints_gc M α).l_supᵢ
#align fixing_submonoid_Union fixingSubmonoid_unionᵢ
-/

/- warning: fixed_points_submonoid_sup -> fixedPoints_submonoid_sup is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Monoid.{u1} M] [_inst_2 : MulAction.{u1, u2} M α _inst_1] {P : Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)} {Q : Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)}, Eq.{succ u2} (Set.{u2} α) (MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) M (Submonoid.setLike.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) (Sup.sup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (SemilatticeSup.toHasSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Lattice.toSemilatticeSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (CompleteLattice.toLattice.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Submonoid.completeLattice.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))))) P Q)) α (Submonoid.toMonoid.{u1} M _inst_1 (Sup.sup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (SemilatticeSup.toHasSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Lattice.toSemilatticeSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (CompleteLattice.toLattice.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Submonoid.completeLattice.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))))) P Q)) (Submonoid.mulAction.{u1, u2} M α _inst_1 _inst_2 (Sup.sup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (SemilatticeSup.toHasSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Lattice.toSemilatticeSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (CompleteLattice.toLattice.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Submonoid.completeLattice.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))))) P Q))) (Inter.inter.{u2} (Set.{u2} α) (Set.hasInter.{u2} α) (MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) M (Submonoid.setLike.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) P) α (Submonoid.toMonoid.{u1} M _inst_1 P) (Submonoid.mulAction.{u1, u2} M α _inst_1 _inst_2 P)) (MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) M (Submonoid.setLike.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) Q) α (Submonoid.toMonoid.{u1} M _inst_1 Q) (Submonoid.mulAction.{u1, u2} M α _inst_1 _inst_2 Q)))
but is expected to have type
  forall (M : Type.{u2}) (α : Type.{u1}) [_inst_1 : Monoid.{u2} M] [_inst_2 : MulAction.{u2, u1} M α _inst_1] {P : Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)} {Q : Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)}, Eq.{succ u1} (Set.{u1} α) (MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (SetLike.instMembership.{u2, u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) M (Submonoid.instSetLikeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))) x (Sup.sup.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (SemilatticeSup.toSup.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (Lattice.toSemilatticeSup.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (CompleteLattice.toLattice.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (Submonoid.instCompleteLatticeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))))) P Q))) α (Submonoid.toMonoid.{u2} M _inst_1 (Sup.sup.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (SemilatticeSup.toSup.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (Lattice.toSemilatticeSup.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (CompleteLattice.toLattice.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (Submonoid.instCompleteLatticeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))))) P Q)) (Submonoid.mulAction.{u2, u1} M α _inst_1 _inst_2 (Sup.sup.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (SemilatticeSup.toSup.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (Lattice.toSemilatticeSup.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (CompleteLattice.toLattice.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (Submonoid.instCompleteLatticeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))))) P Q))) (Inter.inter.{u1} (Set.{u1} α) (Set.instInterSet.{u1} α) (MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (SetLike.instMembership.{u2, u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) M (Submonoid.instSetLikeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))) x P)) α (Submonoid.toMonoid.{u2} M _inst_1 P) (Submonoid.mulAction.{u2, u1} M α _inst_1 _inst_2 P)) (MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (SetLike.instMembership.{u2, u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) M (Submonoid.instSetLikeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))) x Q)) α (Submonoid.toMonoid.{u2} M _inst_1 Q) (Submonoid.mulAction.{u2, u1} M α _inst_1 _inst_2 Q)))
Case conversion may be inaccurate. Consider using '#align fixed_points_submonoid_sup fixedPoints_submonoid_supₓ'. -/
/-- Fixed points of sup of submonoids is intersection -/
theorem fixedPoints_submonoid_sup {P Q : Submonoid M} :
    fixedPoints (↥(P ⊔ Q)) α = fixedPoints P α ∩ fixedPoints Q α :=
  (fixingSubmonoid_fixedPoints_gc M α).u_inf
#align fixed_points_submonoid_sup fixedPoints_submonoid_sup

/- warning: fixed_points_submonoid_supr -> fixedPoints_submonoid_supᵢ is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Monoid.{u1} M] [_inst_2 : MulAction.{u1, u2} M α _inst_1] {ι : Sort.{u3}} {P : ι -> (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))}, Eq.{succ u2} (Set.{u2} α) (MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) M (Submonoid.setLike.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) (supᵢ.{u1, u3} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (CompleteSemilatticeSup.toHasSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (CompleteLattice.toCompleteSemilatticeSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Submonoid.completeLattice.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)))) ι P)) α (Submonoid.toMonoid.{u1} M _inst_1 (supᵢ.{u1, u3} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (CompleteSemilatticeSup.toHasSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (CompleteLattice.toCompleteSemilatticeSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Submonoid.completeLattice.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)))) ι P)) (Submonoid.mulAction.{u1, u2} M α _inst_1 _inst_2 (supᵢ.{u1, u3} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (CompleteSemilatticeSup.toHasSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (CompleteLattice.toCompleteSemilatticeSup.{u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) (Submonoid.completeLattice.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)))) ι P))) (Set.interᵢ.{u2, u3} α ι (fun (i : ι) => MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Submonoid.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1)) M (Submonoid.setLike.{u1} M (Monoid.toMulOneClass.{u1} M _inst_1))) (P i)) α (Submonoid.toMonoid.{u1} M _inst_1 (P i)) (Submonoid.mulAction.{u1, u2} M α _inst_1 _inst_2 (P i))))
but is expected to have type
  forall (M : Type.{u2}) (α : Type.{u1}) [_inst_1 : Monoid.{u2} M] [_inst_2 : MulAction.{u2, u1} M α _inst_1] {ι : Sort.{u3}} {P : ι -> (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))}, Eq.{succ u1} (Set.{u1} α) (MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (SetLike.instMembership.{u2, u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) M (Submonoid.instSetLikeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))) x (supᵢ.{u2, u3} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (CompleteLattice.toSupSet.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (Submonoid.instCompleteLatticeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))) ι P))) α (Submonoid.toMonoid.{u2} M _inst_1 (supᵢ.{u2, u3} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (CompleteLattice.toSupSet.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (Submonoid.instCompleteLatticeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))) ι P)) (Submonoid.mulAction.{u2, u1} M α _inst_1 _inst_2 (supᵢ.{u2, u3} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (CompleteLattice.toSupSet.{u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (Submonoid.instCompleteLatticeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))) ι P))) (Set.interᵢ.{u1, u3} α ι (fun (i : ι) => MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) (SetLike.instMembership.{u2, u2} (Submonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1)) M (Submonoid.instSetLikeSubmonoid.{u2} M (Monoid.toMulOneClass.{u2} M _inst_1))) x (P i))) α (Submonoid.toMonoid.{u2} M _inst_1 (P i)) (Submonoid.mulAction.{u2, u1} M α _inst_1 _inst_2 (P i))))
Case conversion may be inaccurate. Consider using '#align fixed_points_submonoid_supr fixedPoints_submonoid_supᵢₓ'. -/
/-- Fixed points of supr of submonoids is intersection -/
theorem fixedPoints_submonoid_supᵢ {ι : Sort _} {P : ι → Submonoid M} :
    fixedPoints (↥(supᵢ P)) α = ⋂ i, fixedPoints (P i) α :=
  (fixingSubmonoid_fixedPoints_gc M α).u_infᵢ
#align fixed_points_submonoid_supr fixedPoints_submonoid_supᵢ

end Monoid

section Group

open MulAction

variable (M : Type _) {α : Type _} [Group M] [MulAction M α]

#print fixingSubgroup /-
/-- The subgroup fixing a set under a `mul_action`. -/
@[to_additive " The additive subgroup fixing a set under an `add_action`. "]
def fixingSubgroup (s : Set α) : Subgroup M :=
  { fixingSubmonoid M s with inv_mem' := fun _ hx z => by rw [inv_smul_eq_iff, hx z] }
#align fixing_subgroup fixingSubgroup
#align fixing_add_subgroup fixingAddSubgroup
-/

/- warning: mem_fixing_subgroup_iff -> mem_fixingSubgroup_iff is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) {α : Type.{u2}} [_inst_1 : Group.{u1} M] [_inst_2 : MulAction.{u1, u2} M α (DivInvMonoid.toMonoid.{u1} M (Group.toDivInvMonoid.{u1} M _inst_1))] {s : Set.{u2} α} {m : M}, Iff (Membership.Mem.{u1, u1} M (Subgroup.{u1} M _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) m (fixingSubgroup.{u1, u2} M α _inst_1 _inst_2 s)) (forall (y : α), (Membership.Mem.{u2, u2} α (Set.{u2} α) (Set.hasMem.{u2} α) y s) -> (Eq.{succ u2} α (SMul.smul.{u1, u2} M α (MulAction.toHasSmul.{u1, u2} M α (DivInvMonoid.toMonoid.{u1} M (Group.toDivInvMonoid.{u1} M _inst_1)) _inst_2) m y) y))
but is expected to have type
  forall (M : Type.{u1}) {α : Type.{u2}} [_inst_1 : Group.{u1} M] [_inst_2 : MulAction.{u1, u2} M α (DivInvMonoid.toMonoid.{u1} M (Group.toDivInvMonoid.{u1} M _inst_1))] {s : Set.{u2} α} {m : M}, Iff (Membership.mem.{u1, u1} M (Subgroup.{u1} M _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.instSetLikeSubgroup.{u1} M _inst_1)) m (fixingSubgroup.{u1, u2} M α _inst_1 _inst_2 s)) (forall (y : α), (Membership.mem.{u2, u2} α (Set.{u2} α) (Set.instMembershipSet.{u2} α) y s) -> (Eq.{succ u2} α (HSMul.hSMul.{u1, u2, u2} M α α (instHSMul.{u1, u2} M α (MulAction.toSMul.{u1, u2} M α (DivInvMonoid.toMonoid.{u1} M (Group.toDivInvMonoid.{u1} M _inst_1)) _inst_2)) m y) y))
Case conversion may be inaccurate. Consider using '#align mem_fixing_subgroup_iff mem_fixingSubgroup_iffₓ'. -/
theorem mem_fixingSubgroup_iff {s : Set α} {m : M} : m ∈ fixingSubgroup M s ↔ ∀ y ∈ s, m • y = y :=
  ⟨fun hg y hy => hg ⟨y, hy⟩, fun h ⟨y, hy⟩ => h y hy⟩
#align mem_fixing_subgroup_iff mem_fixingSubgroup_iff

variable (α)

#print fixingSubgroup_fixedPoints_gc /-
/-- The Galois connection between fixing subgroups and fixed points of a group action -/
theorem fixingSubgroup_fixedPoints_gc :
    GaloisConnection (OrderDual.toDual ∘ fixingSubgroup M)
      ((fun P : Subgroup M => fixedPoints P α) ∘ OrderDual.ofDual) :=
  fun s P => ⟨fun h s hs p => h p.2 ⟨s, hs⟩, fun h p hp s => h s.2 ⟨p, hp⟩⟩
#align fixing_subgroup_fixed_points_gc fixingSubgroup_fixedPoints_gc
-/

/- warning: fixing_subgroup_antitone -> fixingSubgroup_antitone is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Group.{u1} M] [_inst_2 : MulAction.{u1, u2} M α (DivInvMonoid.toMonoid.{u1} M (Group.toDivInvMonoid.{u1} M _inst_1))], Antitone.{u2, u1} (Set.{u2} α) (Subgroup.{u1} M _inst_1) (PartialOrder.toPreorder.{u2} (Set.{u2} α) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} α) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.completeBooleanAlgebra.{u2} α))))))) (PartialOrder.toPreorder.{u1} (Subgroup.{u1} M _inst_1) (SetLike.partialOrder.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1))) (fixingSubgroup.{u1, u2} M α _inst_1 _inst_2)
but is expected to have type
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Group.{u1} M] [_inst_2 : MulAction.{u1, u2} M α (DivInvMonoid.toMonoid.{u1} M (Group.toDivInvMonoid.{u1} M _inst_1))], Antitone.{u2, u1} (Set.{u2} α) (Subgroup.{u1} M _inst_1) (PartialOrder.toPreorder.{u2} (Set.{u2} α) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} α) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.instCompleteBooleanAlgebraSet.{u2} α))))))) (PartialOrder.toPreorder.{u1} (Subgroup.{u1} M _inst_1) (CompleteSemilatticeInf.toPartialOrder.{u1} (Subgroup.{u1} M _inst_1) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.instCompleteLatticeSubgroup.{u1} M _inst_1)))) (fixingSubgroup.{u1, u2} M α _inst_1 _inst_2)
Case conversion may be inaccurate. Consider using '#align fixing_subgroup_antitone fixingSubgroup_antitoneₓ'. -/
theorem fixingSubgroup_antitone : Antitone (fixingSubgroup M : Set α → Subgroup M) :=
  (fixingSubgroup_fixedPoints_gc M α).monotone_l
#align fixing_subgroup_antitone fixingSubgroup_antitone

/- warning: fixed_points_subgroup_antitone -> fixedPoints_subgroup_antitone is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Group.{u1} M] [_inst_2 : MulAction.{u1, u2} M α (DivInvMonoid.toMonoid.{u1} M (Group.toDivInvMonoid.{u1} M _inst_1))], Antitone.{u1, u2} (Subgroup.{u1} M _inst_1) (Set.{u2} α) (PartialOrder.toPreorder.{u1} (Subgroup.{u1} M _inst_1) (SetLike.partialOrder.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1))) (PartialOrder.toPreorder.{u2} (Set.{u2} α) (CompleteSemilatticeInf.toPartialOrder.{u2} (Set.{u2} α) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Set.{u2} α) (Order.Coframe.toCompleteLattice.{u2} (Set.{u2} α) (CompleteDistribLattice.toCoframe.{u2} (Set.{u2} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u2} (Set.{u2} α) (Set.completeBooleanAlgebra.{u2} α))))))) (fun (P : Subgroup.{u1} M _inst_1) => MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) P) α (DivInvMonoid.toMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) P) (Group.toDivInvMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) P) (Subgroup.toGroup.{u1} M _inst_1 P))) (Subgroup.mulAction.{u1, u2} M _inst_1 α _inst_2 P))
but is expected to have type
  forall (M : Type.{u2}) (α : Type.{u1}) [_inst_1 : Group.{u2} M] [_inst_2 : MulAction.{u2, u1} M α (DivInvMonoid.toMonoid.{u2} M (Group.toDivInvMonoid.{u2} M _inst_1))], Antitone.{u2, u1} (Subgroup.{u2} M _inst_1) (Set.{u1} α) (PartialOrder.toPreorder.{u2} (Subgroup.{u2} M _inst_1) (CompleteSemilatticeInf.toPartialOrder.{u2} (Subgroup.{u2} M _inst_1) (CompleteLattice.toCompleteSemilatticeInf.{u2} (Subgroup.{u2} M _inst_1) (Subgroup.instCompleteLatticeSubgroup.{u2} M _inst_1)))) (PartialOrder.toPreorder.{u1} (Set.{u1} α) (CompleteSemilatticeInf.toPartialOrder.{u1} (Set.{u1} α) (CompleteLattice.toCompleteSemilatticeInf.{u1} (Set.{u1} α) (Order.Coframe.toCompleteLattice.{u1} (Set.{u1} α) (CompleteDistribLattice.toCoframe.{u1} (Set.{u1} α) (CompleteBooleanAlgebra.toCompleteDistribLattice.{u1} (Set.{u1} α) (Set.instCompleteBooleanAlgebraSet.{u1} α))))))) (fun (P : Subgroup.{u2} M _inst_1) => MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Subgroup.{u2} M _inst_1) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} M _inst_1) M (Subgroup.instSetLikeSubgroup.{u2} M _inst_1)) x P)) α (Submonoid.toMonoid.{u2} M (DivInvMonoid.toMonoid.{u2} M (Group.toDivInvMonoid.{u2} M _inst_1)) (Subgroup.toSubmonoid.{u2} M _inst_1 P)) (Subgroup.instMulActionSubtypeMemSubgroupInstMembershipInstSetLikeSubgroupToMonoidToMonoidToDivInvMonoidToSubmonoid.{u2, u1} M _inst_1 α _inst_2 P))
Case conversion may be inaccurate. Consider using '#align fixed_points_subgroup_antitone fixedPoints_subgroup_antitoneₓ'. -/
theorem fixedPoints_subgroup_antitone : Antitone fun P : Subgroup M => fixedPoints P α :=
  (fixingSubgroup_fixedPoints_gc M α).monotone_u.dual_left
#align fixed_points_subgroup_antitone fixedPoints_subgroup_antitone

/- warning: fixing_subgroup_union -> fixingSubgroup_union is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Group.{u1} M] [_inst_2 : MulAction.{u1, u2} M α (DivInvMonoid.toMonoid.{u1} M (Group.toDivInvMonoid.{u1} M _inst_1))] {s : Set.{u2} α} {t : Set.{u2} α}, Eq.{succ u1} (Subgroup.{u1} M _inst_1) (fixingSubgroup.{u1, u2} M α _inst_1 _inst_2 (Union.union.{u2} (Set.{u2} α) (Set.hasUnion.{u2} α) s t)) (Inf.inf.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.hasInf.{u1} M _inst_1) (fixingSubgroup.{u1, u2} M α _inst_1 _inst_2 s) (fixingSubgroup.{u1, u2} M α _inst_1 _inst_2 t))
but is expected to have type
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Group.{u1} M] [_inst_2 : MulAction.{u1, u2} M α (DivInvMonoid.toMonoid.{u1} M (Group.toDivInvMonoid.{u1} M _inst_1))] {s : Set.{u2} α} {t : Set.{u2} α}, Eq.{succ u1} (Subgroup.{u1} M _inst_1) (fixingSubgroup.{u1, u2} M α _inst_1 _inst_2 (Union.union.{u2} (Set.{u2} α) (Set.instUnionSet.{u2} α) s t)) (Inf.inf.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.instInfSubgroup.{u1} M _inst_1) (fixingSubgroup.{u1, u2} M α _inst_1 _inst_2 s) (fixingSubgroup.{u1, u2} M α _inst_1 _inst_2 t))
Case conversion may be inaccurate. Consider using '#align fixing_subgroup_union fixingSubgroup_unionₓ'. -/
/-- Fixing subgroup of union is intersection -/
theorem fixingSubgroup_union {s t : Set α} :
    fixingSubgroup M (s ∪ t) = fixingSubgroup M s ⊓ fixingSubgroup M t :=
  (fixingSubgroup_fixedPoints_gc M α).l_sup
#align fixing_subgroup_union fixingSubgroup_union

#print fixingSubgroup_unionᵢ /-
/-- Fixing subgroup of Union is intersection -/
theorem fixingSubgroup_unionᵢ {ι : Sort _} {s : ι → Set α} :
    fixingSubgroup M (⋃ i, s i) = ⨅ i, fixingSubgroup M (s i) :=
  (fixingSubgroup_fixedPoints_gc M α).l_supᵢ
#align fixing_subgroup_Union fixingSubgroup_unionᵢ
-/

/- warning: fixed_points_subgroup_sup -> fixedPoints_subgroup_sup is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Group.{u1} M] [_inst_2 : MulAction.{u1, u2} M α (DivInvMonoid.toMonoid.{u1} M (Group.toDivInvMonoid.{u1} M _inst_1))] {P : Subgroup.{u1} M _inst_1} {Q : Subgroup.{u1} M _inst_1}, Eq.{succ u2} (Set.{u2} α) (MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) (Sup.sup.{u1} (Subgroup.{u1} M _inst_1) (SemilatticeSup.toHasSup.{u1} (Subgroup.{u1} M _inst_1) (Lattice.toSemilatticeSup.{u1} (Subgroup.{u1} M _inst_1) (CompleteLattice.toLattice.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.completeLattice.{u1} M _inst_1)))) P Q)) α (DivInvMonoid.toMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) (Sup.sup.{u1} (Subgroup.{u1} M _inst_1) (SemilatticeSup.toHasSup.{u1} (Subgroup.{u1} M _inst_1) (Lattice.toSemilatticeSup.{u1} (Subgroup.{u1} M _inst_1) (CompleteLattice.toLattice.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.completeLattice.{u1} M _inst_1)))) P Q)) (Group.toDivInvMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) (Sup.sup.{u1} (Subgroup.{u1} M _inst_1) (SemilatticeSup.toHasSup.{u1} (Subgroup.{u1} M _inst_1) (Lattice.toSemilatticeSup.{u1} (Subgroup.{u1} M _inst_1) (CompleteLattice.toLattice.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.completeLattice.{u1} M _inst_1)))) P Q)) (Subgroup.toGroup.{u1} M _inst_1 (Sup.sup.{u1} (Subgroup.{u1} M _inst_1) (SemilatticeSup.toHasSup.{u1} (Subgroup.{u1} M _inst_1) (Lattice.toSemilatticeSup.{u1} (Subgroup.{u1} M _inst_1) (CompleteLattice.toLattice.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.completeLattice.{u1} M _inst_1)))) P Q)))) (Subgroup.mulAction.{u1, u2} M _inst_1 α _inst_2 (Sup.sup.{u1} (Subgroup.{u1} M _inst_1) (SemilatticeSup.toHasSup.{u1} (Subgroup.{u1} M _inst_1) (Lattice.toSemilatticeSup.{u1} (Subgroup.{u1} M _inst_1) (CompleteLattice.toLattice.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.completeLattice.{u1} M _inst_1)))) P Q))) (Inter.inter.{u2} (Set.{u2} α) (Set.hasInter.{u2} α) (MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) P) α (DivInvMonoid.toMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) P) (Group.toDivInvMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) P) (Subgroup.toGroup.{u1} M _inst_1 P))) (Subgroup.mulAction.{u1, u2} M _inst_1 α _inst_2 P)) (MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) Q) α (DivInvMonoid.toMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) Q) (Group.toDivInvMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) Q) (Subgroup.toGroup.{u1} M _inst_1 Q))) (Subgroup.mulAction.{u1, u2} M _inst_1 α _inst_2 Q)))
but is expected to have type
  forall (M : Type.{u2}) (α : Type.{u1}) [_inst_1 : Group.{u2} M] [_inst_2 : MulAction.{u2, u1} M α (DivInvMonoid.toMonoid.{u2} M (Group.toDivInvMonoid.{u2} M _inst_1))] {P : Subgroup.{u2} M _inst_1} {Q : Subgroup.{u2} M _inst_1}, Eq.{succ u1} (Set.{u1} α) (MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Subgroup.{u2} M _inst_1) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} M _inst_1) M (Subgroup.instSetLikeSubgroup.{u2} M _inst_1)) x (Sup.sup.{u2} (Subgroup.{u2} M _inst_1) (SemilatticeSup.toSup.{u2} (Subgroup.{u2} M _inst_1) (Lattice.toSemilatticeSup.{u2} (Subgroup.{u2} M _inst_1) (CompleteLattice.toLattice.{u2} (Subgroup.{u2} M _inst_1) (Subgroup.instCompleteLatticeSubgroup.{u2} M _inst_1)))) P Q))) α (Submonoid.toMonoid.{u2} M (DivInvMonoid.toMonoid.{u2} M (Group.toDivInvMonoid.{u2} M _inst_1)) (Subgroup.toSubmonoid.{u2} M _inst_1 (Sup.sup.{u2} (Subgroup.{u2} M _inst_1) (SemilatticeSup.toSup.{u2} (Subgroup.{u2} M _inst_1) (Lattice.toSemilatticeSup.{u2} (Subgroup.{u2} M _inst_1) (CompleteLattice.toLattice.{u2} (Subgroup.{u2} M _inst_1) (Subgroup.instCompleteLatticeSubgroup.{u2} M _inst_1)))) P Q))) (Subgroup.instMulActionSubtypeMemSubgroupInstMembershipInstSetLikeSubgroupToMonoidToMonoidToDivInvMonoidToSubmonoid.{u2, u1} M _inst_1 α _inst_2 (Sup.sup.{u2} (Subgroup.{u2} M _inst_1) (SemilatticeSup.toSup.{u2} (Subgroup.{u2} M _inst_1) (Lattice.toSemilatticeSup.{u2} (Subgroup.{u2} M _inst_1) (CompleteLattice.toLattice.{u2} (Subgroup.{u2} M _inst_1) (Subgroup.instCompleteLatticeSubgroup.{u2} M _inst_1)))) P Q))) (Inter.inter.{u1} (Set.{u1} α) (Set.instInterSet.{u1} α) (MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Subgroup.{u2} M _inst_1) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} M _inst_1) M (Subgroup.instSetLikeSubgroup.{u2} M _inst_1)) x P)) α (Submonoid.toMonoid.{u2} M (DivInvMonoid.toMonoid.{u2} M (Group.toDivInvMonoid.{u2} M _inst_1)) (Subgroup.toSubmonoid.{u2} M _inst_1 P)) (Subgroup.instMulActionSubtypeMemSubgroupInstMembershipInstSetLikeSubgroupToMonoidToMonoidToDivInvMonoidToSubmonoid.{u2, u1} M _inst_1 α _inst_2 P)) (MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Subgroup.{u2} M _inst_1) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} M _inst_1) M (Subgroup.instSetLikeSubgroup.{u2} M _inst_1)) x Q)) α (Submonoid.toMonoid.{u2} M (DivInvMonoid.toMonoid.{u2} M (Group.toDivInvMonoid.{u2} M _inst_1)) (Subgroup.toSubmonoid.{u2} M _inst_1 Q)) (Subgroup.instMulActionSubtypeMemSubgroupInstMembershipInstSetLikeSubgroupToMonoidToMonoidToDivInvMonoidToSubmonoid.{u2, u1} M _inst_1 α _inst_2 Q)))
Case conversion may be inaccurate. Consider using '#align fixed_points_subgroup_sup fixedPoints_subgroup_supₓ'. -/
/-- Fixed points of sup of subgroups is intersection -/
theorem fixedPoints_subgroup_sup {P Q : Subgroup M} :
    fixedPoints (↥(P ⊔ Q)) α = fixedPoints P α ∩ fixedPoints Q α :=
  (fixingSubgroup_fixedPoints_gc M α).u_inf
#align fixed_points_subgroup_sup fixedPoints_subgroup_sup

/- warning: fixed_points_subgroup_supr -> fixedPoints_subgroup_supᵢ is a dubious translation:
lean 3 declaration is
  forall (M : Type.{u1}) (α : Type.{u2}) [_inst_1 : Group.{u1} M] [_inst_2 : MulAction.{u1, u2} M α (DivInvMonoid.toMonoid.{u1} M (Group.toDivInvMonoid.{u1} M _inst_1))] {ι : Sort.{u3}} {P : ι -> (Subgroup.{u1} M _inst_1)}, Eq.{succ u2} (Set.{u2} α) (MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) (supᵢ.{u1, u3} (Subgroup.{u1} M _inst_1) (CompleteSemilatticeSup.toHasSup.{u1} (Subgroup.{u1} M _inst_1) (CompleteLattice.toCompleteSemilatticeSup.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.completeLattice.{u1} M _inst_1))) ι P)) α (DivInvMonoid.toMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) (supᵢ.{u1, u3} (Subgroup.{u1} M _inst_1) (CompleteSemilatticeSup.toHasSup.{u1} (Subgroup.{u1} M _inst_1) (CompleteLattice.toCompleteSemilatticeSup.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.completeLattice.{u1} M _inst_1))) ι P)) (Group.toDivInvMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) (supᵢ.{u1, u3} (Subgroup.{u1} M _inst_1) (CompleteSemilatticeSup.toHasSup.{u1} (Subgroup.{u1} M _inst_1) (CompleteLattice.toCompleteSemilatticeSup.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.completeLattice.{u1} M _inst_1))) ι P)) (Subgroup.toGroup.{u1} M _inst_1 (supᵢ.{u1, u3} (Subgroup.{u1} M _inst_1) (CompleteSemilatticeSup.toHasSup.{u1} (Subgroup.{u1} M _inst_1) (CompleteLattice.toCompleteSemilatticeSup.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.completeLattice.{u1} M _inst_1))) ι P)))) (Subgroup.mulAction.{u1, u2} M _inst_1 α _inst_2 (supᵢ.{u1, u3} (Subgroup.{u1} M _inst_1) (CompleteSemilatticeSup.toHasSup.{u1} (Subgroup.{u1} M _inst_1) (CompleteLattice.toCompleteSemilatticeSup.{u1} (Subgroup.{u1} M _inst_1) (Subgroup.completeLattice.{u1} M _inst_1))) ι P))) (Set.interᵢ.{u2, u3} α ι (fun (i : ι) => MulAction.fixedPoints.{u1, u2} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) (P i)) α (DivInvMonoid.toMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) (P i)) (Group.toDivInvMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} M _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} M _inst_1) M (Subgroup.setLike.{u1} M _inst_1)) (P i)) (Subgroup.toGroup.{u1} M _inst_1 (P i)))) (Subgroup.mulAction.{u1, u2} M _inst_1 α _inst_2 (P i))))
but is expected to have type
  forall (M : Type.{u2}) (α : Type.{u1}) [_inst_1 : Group.{u2} M] [_inst_2 : MulAction.{u2, u1} M α (DivInvMonoid.toMonoid.{u2} M (Group.toDivInvMonoid.{u2} M _inst_1))] {ι : Sort.{u3}} {P : ι -> (Subgroup.{u2} M _inst_1)}, Eq.{succ u1} (Set.{u1} α) (MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Subgroup.{u2} M _inst_1) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} M _inst_1) M (Subgroup.instSetLikeSubgroup.{u2} M _inst_1)) x (supᵢ.{u2, u3} (Subgroup.{u2} M _inst_1) (CompleteLattice.toSupSet.{u2} (Subgroup.{u2} M _inst_1) (Subgroup.instCompleteLatticeSubgroup.{u2} M _inst_1)) ι P))) α (Submonoid.toMonoid.{u2} M (DivInvMonoid.toMonoid.{u2} M (Group.toDivInvMonoid.{u2} M _inst_1)) (Subgroup.toSubmonoid.{u2} M _inst_1 (supᵢ.{u2, u3} (Subgroup.{u2} M _inst_1) (CompleteLattice.toSupSet.{u2} (Subgroup.{u2} M _inst_1) (Subgroup.instCompleteLatticeSubgroup.{u2} M _inst_1)) ι P))) (Subgroup.instMulActionSubtypeMemSubgroupInstMembershipInstSetLikeSubgroupToMonoidToMonoidToDivInvMonoidToSubmonoid.{u2, u1} M _inst_1 α _inst_2 (supᵢ.{u2, u3} (Subgroup.{u2} M _inst_1) (CompleteLattice.toSupSet.{u2} (Subgroup.{u2} M _inst_1) (Subgroup.instCompleteLatticeSubgroup.{u2} M _inst_1)) ι P))) (Set.interᵢ.{u1, u3} α ι (fun (i : ι) => MulAction.fixedPoints.{u2, u1} (Subtype.{succ u2} M (fun (x : M) => Membership.mem.{u2, u2} M (Subgroup.{u2} M _inst_1) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} M _inst_1) M (Subgroup.instSetLikeSubgroup.{u2} M _inst_1)) x (P i))) α (Submonoid.toMonoid.{u2} M (DivInvMonoid.toMonoid.{u2} M (Group.toDivInvMonoid.{u2} M _inst_1)) (Subgroup.toSubmonoid.{u2} M _inst_1 (P i))) (Subgroup.instMulActionSubtypeMemSubgroupInstMembershipInstSetLikeSubgroupToMonoidToMonoidToDivInvMonoidToSubmonoid.{u2, u1} M _inst_1 α _inst_2 (P i))))
Case conversion may be inaccurate. Consider using '#align fixed_points_subgroup_supr fixedPoints_subgroup_supᵢₓ'. -/
/-- Fixed points of supr of subgroups is intersection -/
theorem fixedPoints_subgroup_supᵢ {ι : Sort _} {P : ι → Subgroup M} :
    fixedPoints (↥(supᵢ P)) α = ⋂ i, fixedPoints (P i) α :=
  (fixingSubgroup_fixedPoints_gc M α).u_infᵢ
#align fixed_points_subgroup_supr fixedPoints_subgroup_supᵢ

end Group


import M4R.Set.Finite.List
import M4R.Numbers

inductive M4R.Pairwise (r : α → α → Prop) : List α → Prop
| nil  : Pairwise r []
| cons : ∀ {x : α} {l : List α}, (∀ a ∈ l, r x a) → Pairwise r l → Pairwise r (x::l)

def List.nodup : List α → Prop := M4R.Pairwise (· ≠ ·)

namespace M4R
  namespace Pairwise

    @[simp] theorem consIff {r : α → α → Prop} {a : α} {l : List α} :
      Pairwise r (a::l) ↔ (∀ a' ∈ l, r a a') ∧ Pairwise r l :=
        ⟨(fun x => by cases x with | cons p rl => exact ⟨p, rl⟩), (fun ⟨p, rl⟩ => rl.cons p)⟩

    theorem singleton (r : α → α → Prop) (a : α) : Pairwise r [a] := by
      simp only [consIff]; exact ⟨by intros; contradiction, nil⟩

    theorem appendIff {r : α → α → Prop} {l₁ l₂ : List α} : Pairwise r (l₁++l₂) ↔
      Pairwise r l₁ ∧ Pairwise r l₂ ∧ ∀ x ∈ l₁, ∀ y ∈ l₂, r x y := by
      induction l₁ with
      | nil =>
        apply Iff.intro; intro prl; apply And.intro Pairwise.nil; simp only [nil] at prl
        apply And.intro prl; exact (fun x _ _ _ => by have := List.not_mem_nil x; contradiction)
        exact (fun x => x.right.left)
      | cons x l₁ IH =>
        simp; apply Iff.intro
        intro h; apply And.intro
        exact And.intro (fun y yl => h.left y (Or.inl yl)) (IH.mp h.right).left
        apply And.intro; exact (IH.mp h.right).right.left
        intro a axl b; apply Or.elim axl; intro ax bl; rw [ax]; exact h.left b (Or.inr bl)
        intro al bl; exact (IH.mp h.right).right.right a al b bl
        intro h; apply And.intro; intro a all; apply Or.elim all (h.left.left a)
        apply h.right.right x (List.mem_cons_self x l₁) a
        apply IH.mpr; apply And.intro h.left.right; apply And.intro h.right.left
        intro _ al _ bl; apply h.right.right; exact Or.inr al; exact bl

    theorem appendComm {r : α → α → Prop} (h : ∀ a b, r a b → r b a) {l₁ l₂ : List α} :
      Pairwise r (l₁++l₂) ↔ Pairwise r (l₂++l₁) := by
      have : ∀ s t : List α, Pairwise r (s++t) → Pairwise r (t++s) := by
        intro s t p; rw [appendIff] at *; apply And.intro p.right.left; apply And.intro p.left
        intro x xt y ys; exact h y x (p.right.right y ys x xt)
      exact ⟨this l₁ l₂, this l₂ l₁⟩

    theorem middle {r : α → α → Prop} (h : ∀ a b, r a b → r b a) {a : α} {l₁ l₂ : List α} :
      Pairwise r (l₁ ++ a::l₂) ↔ Pairwise r (a::(l₁++l₂)) := by
        have : Pairwise r (l₁ ++ ([a] ++ l₂)) ↔ Pairwise r ([a] ++ l₁ ++ l₂) := by
          {rw [←List.append_assoc, appendIff, @appendIff _ _ ([a] ++ l₁), appendComm h]
          simp only [List.mem_append, Or.comm']; exact Iff.rfl}
        simp only [List.appendSingleton] at this; exact this

    theorem imp_of_mem {r s : α → α → Prop} {l : List α}
      (h : ∀ {a b}, a ∈ l → b ∈ l → r a b → s a b) (p : Pairwise r l) : Pairwise s l := by
      induction p with
      | nil => exact nil
      | @cons a l lr p ih =>
        constructor; exact fun b bl => h (List.mem_cons_self a l) (Or.inr bl) (lr b bl)
        exact ih (fun xl yl => h (Or.inr xl) (Or.inr yl))

    theorem imp {r s : α → α → Prop} (h : ∀ a b, r a b → s a b) {l : List α} :
      Pairwise r l → Pairwise s l := imp_of_mem (fun _ _ => h _ _)

    theorem iff_of_mem {r s : α → α → Prop} {l : List α} (H : ∀ {a b}, a ∈ l → b ∈ l → (r a b ↔ s a b)) :
      Pairwise r l ↔ Pairwise s l :=
        ⟨imp_of_mem (fun al bl => (H al bl).mp),
        imp_of_mem (fun al bl => (H al bl).mpr)⟩

    theorem and_mem {r : α → α → Prop} {l : List α} : Pairwise r l ↔ Pairwise (fun x y => x ∈ l ∧ y ∈ l ∧ r x y) l := by
      apply iff_of_mem (fun al bl => ⟨fun rab => ⟨al, bl, rab⟩, fun ⟨_, _, rab⟩ => rab⟩)

    theorem map {r : α → α → Prop} (f : β → α) : ∀ {l : List β},
      Pairwise r (l.map f) ↔ Pairwise (fun a b : β => r (f a) (f b)) l
    | []     => by simp only [List.map, Pairwise.nil]
    | b::l => by
      have : (∀ a b', b' ∈ l → f b' = a → r (f b) a) ↔ ∀ (b' : β), b' ∈ l → r (f b) (f b') := by
        apply Iff.intro (fun h x xl => h (f x) x xl rfl)
        intro h _ y yl eq; rw [←eq]; exact h y yl
      simp [List.map, consIff, List.mem_map, exists_imp_distrib, and_imp, this, map]

    theorem of_pairwise_map {r : α → α → Prop} {S : β → β → Prop} (f : α → β) (H : ∀ a b : α, S (f a) (f b) → r a b)
      {l : List α} (p : Pairwise S (l.map f)) : Pairwise r l :=
        ((map f).mp p).imp H

    theorem map_of_pairwise {r : α → α → Prop} {s : β → β → Prop} (f : α → β)
      (H : ∀ a b : α, r a b → s (f a) (f b)) {l : List α}
      (p : Pairwise r l) : Pairwise s (l.map f) := (map f).mpr (p.imp H)

    theorem pairwise_of_sublist {r : α → α → Prop} {l₁ l₂ : List α} : l₁ <+ l₂ → Pairwise r l₂ → Pairwise r l₁
    | List.Sublist.nil, h => h
    | List.Sublist.cons l₁ l₂ a s, Pairwise.cons i n => pairwise_of_sublist s n
    | List.Sublist.cons' l₁ l₂ a s, Pairwise.cons i n  =>
      (pairwise_of_sublist s n).cons fun a ha => i a (s.subset ha)

    theorem filter'_of_pairwise {r : α → α → Prop} (p : α → Prop) {l : List α}
      : Pairwise r l → Pairwise r (l.filter' p) := pairwise_of_sublist (List.filter'_sublist _)

  end Pairwise
end M4R

open M4R

namespace List

  @[simp] theorem nodup_nil : @nodup α [] := Pairwise.nil

  @[simp] theorem nodup_cons {a : α} {l : List α} : nodup (a::l) ↔ a ∉ l ∧ nodup l := by
    simp only [nodup, Pairwise.consIff, forall_mem_ne]; exact Iff.rfl

  theorem nodup_of_nodup_map (f : α → β) {l : List α} : nodup (map f l) → nodup l :=
    Pairwise.of_pairwise_map f fun a b => mt (congrArg f)

  theorem nodup_map_on {f : α → β} {l : List α} (H : ∀ x ∈ l, ∀ y ∈ l, f x = f y → x = y)
    (d : List.nodup l) : List.nodup (l.map f) :=
      Pairwise.map_of_pairwise f (fun a b ⟨ma, mb, n⟩ e => n (H a ma b mb e)) (Pairwise.and_mem.mp d)

  theorem nodup_of_sublist {l₁ l₂ : List α} : l₁ <+ l₂ → l₂.nodup → l₁.nodup :=
    Pairwise.pairwise_of_sublist

  @[simp] theorem nodup_attach {l : List α} : nodup (attach l) ↔ nodup l :=
    ⟨fun h => attach_map_val l ▸ nodup_map_on (fun ⟨_, _⟩ _ ⟨_, _⟩ _ h => by rw [Subtype.mk.injEq]; exact h) h,
      fun h => nodup_of_nodup_map Subtype.val ((attach_map_val l).symm ▸ h)⟩

  theorem nodup_pmap {p : α → Prop} {f : ∀ a, p a → β} {l : List α} {H : ∀ a ∈ l, p a}
    (hf : ∀ a ha b hb, f a ha = f b hb → a = b) (h : l.nodup) : nodup (l.pmap f H) := by
      rw [pmap_eq_map_attach]; exact nodup_map_on
        (fun ⟨a, ha⟩ ha' ⟨b, hb⟩ hb' h => by rw [Subtype.mk.injEq]; exact hf a (H _ ha) b (H _ hb) h)
        (nodup_attach.mpr h)

  open Classical

  theorem nodup_insert {a : α} {l : List α} (h : nodup l) : nodup (l.insert a) :=
    if h' : a ∈ l then by
      rw [insert_of_mem h']; exact h
    else by
      rw [insert_of_not_mem h', nodup_cons]; exact ⟨h', h⟩

  theorem nodup_union (l₁ : List α) {l₂ : List α} (h : l₂.nodup) : nodup (l₁ ∪ l₂) := by
    induction l₁ generalizing l₂ with
    | nil => exact h
    | cons a l ih =>
      apply nodup_insert; exact ih h

  theorem nodup_filter' (p : α → Prop) {l : List α} : nodup l → nodup (filter' p l) :=
    Pairwise.filter'_of_pairwise p

  theorem nodup_cons_of_nodup {a : α} {l : List α} (m : a ∉ l) (n : nodup l) : nodup (a::l) :=
    nodup_cons.mpr ⟨m, n⟩

  theorem not_mem_of_nodup_cons {a : α} {l : List α} (h : nodup (a::l)) : a ∉ l :=
    (nodup_cons.mp h).left

  theorem not_nodup_cons_of_mem {a : α} {l : List α} : a ∈ l → ¬ nodup (a :: l) :=
    imp_not_comm.mp not_mem_of_nodup_cons

  theorem not_nodup_pair (a : α) : ¬ nodup [a, a] :=
    not_nodup_cons_of_mem (List.self_singleton _)

  theorem nodup_iff_sublist {l : List α} : nodup l ↔ ∀ a, ¬ [a, a] <+ l :=
    ⟨fun d a h => not_nodup_pair a (nodup_of_sublist h d), by
      induction l with
      | nil => intros; exact nodup_nil
      | cons a l ih =>
        intro h; exact nodup_cons_of_nodup (fun al => h a ((Sublist.singleton_sublist.mpr al).cons_cons a))
          (ih fun a s => h a (Sublist.sublist_cons_of_sublist _ s))⟩

  theorem nodup_iff_count_le_one {l : List α} : l.nodup ↔ ∀ a : α, List.count a l ≤ 1 := by
    apply nodup_iff_sublist.trans
    have : (∀ (a : α), ¬[a, a] <+ l) = ∀ (a : α), count a l ≤ 1 := forall_congr fun a =>
      have : [a, a] <+ l ↔ 1 < count a l := (@count.le_count_iff_repeat_sublist _ a l (Nat.succ 1)).symm
      propext ((not_iff_not.mpr this).trans Nat.not_lt)
    rw [this]; exact Iff.rfl

  theorem count_eq_one_of_mem {a : α} {l : List α} (d : nodup l) (h : a ∈ l) : count a l = 1 :=
    Nat.le_antisymm (nodup_iff_count_le_one.mp d a) (count.count_pos.mpr h)

  theorem count_eq_zero_of_not_mem {a : α} {l : List α} (h : a ∉ l) : count a l = 0 :=
    Decidable.byContradiction $ λ h' => h $ count.count_pos.mp (Nat.pos_iff_ne_zero.mpr h')

  theorem nodup_erase_eq_filter' (a : α) {l : List α} (d : nodup l) : l.erase a = filter' (· ≠ a) l := by
    induction d with
    | nil => rfl
    | @cons b l m d ih =>
      byCases h : b = a
      { subst h; rw [erase_cons_head, filter'_cons_of_neg _ (by exact iff_not_not.mpr rfl)]
        apply Eq.symm; rw [filter'_eq_self]; exact fun a ha => (m a ha).symm }
      { rw [erase_cons_tail _ h, filter'_cons_of_pos, ih]; exact h }

  theorem pairwise_of_pw_filter (r : α → α → Prop) : ∀ (l : List α), Pairwise r (pw_filter r l)
  | []     => Pairwise.nil
  | x :: l => by
    byCases h : ∀ y ∈ pw_filter r l, r x y
    { rw [List.pw_filter_cons_of_pos h]
      exact Pairwise.cons h (pairwise_of_pw_filter r l) }
    { rw [List.pw_filter_cons_of_neg h]
      exact pairwise_of_pw_filter r l }

  theorem nodup_dedup : ∀ l : List α, l.dedup.nodup := pairwise_of_pw_filter _

  theorem nodup_append {l₁ l₂ : List α} : nodup (l₁++l₂) ↔ nodup l₁ ∧ nodup l₂ ∧ disjoint l₁ l₂ := by
    simp only [nodup, Pairwise.appendIff, disjoint_iff_ne]; exact Iff.rfl

end List