text:
/-
Copyright (c) 2020 Kexing Ying. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kexing Ying
-/
import group_theory.submonoid.pointwise
import group_theory.submonoid.membership
import group_theory.submonoid.centralizer
import algebra.group.conj
import algebra.module.basic
import order.atoms
import order.sup_indep

/-!
# Subgroups

This file defines multiplicative and additive subgroups as an extension of submonoids, in a bundled
form (unbundled subgroups are in `deprecated/subgroups.lean`).

We prove subgroups of a group form a complete lattice, and results about images and preimages of
subgroups under group homomorphisms. The bundled subgroups use bundled monoid homomorphisms.

There are also theorems about the subgroups generated by an element or a subset of a group,
defined both inductively and as the infimum of the set of subgroups containing a given
element/subset.

Special thanks goes to Amelia Livingston and Yury Kudryashov for their help and inspiration.

## Main definitions

Notation used here:

- `G N` are `group`s

- `A` is an `add_group`

- `H K` are `subgroup`s of `G` or `add_subgroup`s of `A`

- `x` is an element of type `G` or type `A`

- `f g : N →* G` are group homomorphisms

- `s k` are sets of elements of type `G`

Definitions in the file:

* `subgroup G` : the type of subgroups of a group `G`

* `add_subgroup A` : the type of subgroups of an additive group `A`

* `complete_lattice (subgroup G)` : the subgroups of `G` form a complete lattice

* `subgroup.closure k` : the minimal subgroup that includes the set `k`

* `subgroup.subtype` : the natural group homomorphism from a subgroup of group `G` to `G`

* `subgroup.gi` : `closure` forms a Galois insertion with the coercion to set

* `subgroup.comap H f` : the preimage of a subgroup `H` along the group homomorphism `f` is also a
  subgroup

* `subgroup.map f H` : the image of a subgroup `H` along the group homomorphism `f` is also a
  subgroup

* `subgroup.prod H K` : the product of subgroups `H`, `K` of groups `G`, `N` respectively, `H × K`
  is a subgroup of `G × N`

* `monoid_hom.range f` : the range of the group homomorphism `f` is a subgroup

* `monoid_hom.ker f` : the kernel of a group homomorphism `f` is the subgroup of elements `x : G`
  such that `f x = 1`

* `monoid_hom.eq_locus f g` : given group homomorphisms `f`, `g`, the elements of `G` such that
  `f x = g x` form a subgroup of `G`

* `is_simple_group G` : a class indicating that a group has exactly two normal subgroups

## Implementation notes

Subgroup inclusion is denoted `≤` rather than `⊆`, although `∈` is defined as
membership of a subgroup's underlying set.

## Tags
subgroup, subgroups
-/

open_locale big_operators pointwise

variables {G : Type*} [group G]
variables {A : Type*} [add_group A]

section subgroup_class

/-- `inv_mem_class S G` states `S` is a type of subsets `s ⊆ G` closed under inverses. -/
class inv_mem_class (S G : Type*) [has_inv G] [set_like S G] :=
(inv_mem : ∀ {s : S} {x}, x ∈ s → x⁻¹ ∈ s)

export inv_mem_class (inv_mem)

/-- `neg_mem_class S G` states `S` is a type of subsets `s ⊆ G` closed under negation. -/
class neg_mem_class (S G : Type*) [has_neg G] [set_like S G] :=
(neg_mem : ∀ {s : S} {x}, x ∈ s → -x ∈ s)

export neg_mem_class (neg_mem)

/-- `subgroup_class S G` states `S` is a type of subsets `s ⊆ G` that are subgroups of `G`. -/
class subgroup_class (S G : Type*) [div_inv_monoid G] [set_like S G]
  extends submonoid_class S G :=
(inv_mem : ∀ {s : S} {x}, x ∈ s → x⁻¹ ∈ s)

/-- `add_subgroup_class S G` states `S` is a type of subsets `s ⊆ G` that are
additive subgroups of `G`. -/
class add_subgroup_class (S G : Type*) [sub_neg_monoid G] [set_like S G]
  extends add_submonoid_class S G :=
(neg_mem : ∀ {s : S} {x}, x ∈ s → -x ∈ s)

attribute [to_additive] inv_mem_class subgroup_class

variables (M S : Type*) [div_inv_monoid M] [set_like S M] [hSM : subgroup_class S M]
include hSM

@[to_additive, priority 100] -- See note [lower instance priority]
instance subgroup_class.to_inv_mem_class : inv_mem_class S M :=
{ .. hSM }

variables {S M} {H K : S}

/-- A subgroup is closed under division. -/
@[to_additive "An additive subgroup is closed under subtraction."]
theorem div_mem {x y : M} (hx : x ∈ H) (hy : y ∈ H) : x / y ∈ H :=
by rw [div_eq_mul_inv]; exact mul_mem hx (inv_mem hy)

@[to_additive]
lemma zpow_mem {x : M} (hx : x ∈ K) : ∀ n : ℤ, x ^ n ∈ K
| (n : ℕ) := by { rw [zpow_coe_nat], exact pow_mem hx n }
| -[1+ n] := by { rw [zpow_neg_succ_of_nat], exact inv_mem (pow_mem hx n.succ) }

omit hSM
variables [set_like S G] [hSG : subgroup_class S G]
include hSG

@[simp, to_additive] theorem inv_mem_iff {x : G} : x⁻¹ ∈ H ↔ x ∈ H :=
⟨λ h, inv_inv x ▸ inv_mem h, inv_mem⟩

@[to_additive] lemma div_mem_comm_iff {a b : G} : a / b ∈ H ↔ b / a ∈ H :=
by rw [← inv_mem_iff, div_eq_mul_inv, div_eq_mul_inv, mul_inv_rev, inv_inv]

@[simp, to_additive]
theorem inv_coe_set : (H : set G)⁻¹ = H :=
by { ext, simp }

@[simp, to_additive]
lemma exists_inv_mem_iff_exists_mem {P : G → Prop} :
  (∃ (x : G), x ∈ H ∧ P x⁻¹) ↔ ∃ x ∈ H, P x :=
by split; { rintros ⟨x, x_in, hx⟩, exact ⟨x⁻¹, inv_mem x_in, by simp [hx]⟩ }

@[to_additive]
lemma mul_mem_cancel_right {x y : G} (h : x ∈ H) : y * x ∈ H ↔ y ∈ H :=
⟨λ hba, by simpa using mul_mem hba (inv_mem h), λ hb, mul_mem hb h⟩

@[to_additive]
lemma mul_mem_cancel_left {x y : G} (h : x ∈ H) : x * y ∈ H ↔ y ∈ H :=
⟨λ hab, by simpa using mul_mem (inv_mem h) hab, mul_mem h⟩

namespace subgroup_class

omit hSG
include hSM

/-- A subgroup of a group inherits an inverse. -/
@[to_additive "An additive subgroup of a `add_group` inherits an inverse."]
instance has_inv : has_inv H := ⟨λ a, ⟨a⁻¹, inv_mem a.2⟩⟩

/-- A subgroup of a group inherits a division -/
@[to_additive "An additive subgroup of an `add_group` inherits a subtraction."]
instance has_div : has_div H := ⟨λ a b, ⟨a / b, div_mem a.2 b.2⟩⟩

omit hSM
/-- An additive subgroup of an `add_group` inherits an integer scaling. -/
instance _root_.add_subgroup_class.has_zsmul {M S} [sub_neg_monoid M] [set_like S M]
  [add_subgroup_class S M] {H : S} : has_smul ℤ H :=
⟨λ n a, ⟨n • a, zsmul_mem a.2 n⟩⟩
include hSM

/-- A subgroup of a group inherits an integer power. -/
@[to_additive]
instance has_zpow : has_pow H ℤ := ⟨λ a n, ⟨a ^ n, zpow_mem a.2 n⟩⟩
@[simp, norm_cast, to_additive] lemma coe_inv (x : H) : ↑(x⁻¹ : H) = (x⁻¹ : M) := rfl
@[simp, norm_cast, to_additive] lemma coe_div (x y : H) : (↑(x / y) : M) = ↑x / ↑y := rfl

omit hSM
variables (H)
include hSG

/-- A subgroup of a group inherits a group structure. -/
@[to_additive "An additive subgroup of an `add_group` inherits an `add_group` structure.",
priority 75] -- Prefer subclasses of `group` over subclasses of `subgroup_class`.
instance to_group : group H :=
subtype.coe_injective.group _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

omit hSG

/-- A subgroup of a `comm_group` is a `comm_group`. -/
@[to_additive "An additive subgroup of an `add_comm_group` is an `add_comm_group`.",
priority 75] -- Prefer subclasses of `comm_group` over subclasses of `subgroup_class`.
instance to_comm_group {G : Type*} [comm_group G] [set_like S G] [subgroup_class S G] :
  comm_group H :=
subtype.coe_injective.comm_group _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl)
  (λ _ _, rfl)

/-- A subgroup of an `ordered_comm_group` is an `ordered_comm_group`. -/
@[to_additive "An additive subgroup of an `add_ordered_comm_group` is an `add_ordered_comm_group`.",
priority 75] -- Prefer subclasses of `group` over subclasses of `subgroup_class`.
instance to_ordered_comm_group {G : Type*} [ordered_comm_group G] [set_like S G]
  [subgroup_class S G] : ordered_comm_group H :=
subtype.coe_injective.ordered_comm_group _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl)
  (λ _ _, rfl)

/-- A subgroup of a `linear_ordered_comm_group` is a `linear_ordered_comm_group`. -/
@[to_additive "An additive subgroup of a `linear_ordered_add_comm_group` is a
  `linear_ordered_add_comm_group`.",
  priority 75] -- Prefer subclasses of `group` over subclasses of `subgroup_class`.
instance to_linear_ordered_comm_group {G : Type*} [linear_ordered_comm_group G] [set_like S G]
  [subgroup_class S G] : linear_ordered_comm_group H :=
subtype.coe_injective.linear_ordered_comm_group _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl)
  (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

include hSG

/-- The natural group hom from a subgroup of group `G` to `G`. -/
@[to_additive "The natural group hom from an additive subgroup of `add_group` `G` to `G`."]
def subtype : H →* G := ⟨coe, rfl, λ _ _, rfl⟩

@[simp, to_additive] theorem coe_subtype : (subtype H : H → G) = coe := rfl

variables {H}

@[simp, norm_cast, to_additive coe_smul]
lemma coe_pow (x : H) (n : ℕ) : ((x ^ n : H) : G) = x ^ n :=
(subtype H : H →* G).map_pow _ _

@[simp, norm_cast, to_additive] lemma coe_zpow (x : H) (n : ℤ) : ((x ^ n : H) : G) = x ^ n :=
(subtype H : H →* G).map_zpow _ _

/-- The inclusion homomorphism from a subgroup `H` contained in `K` to `K`. -/
@[to_additive "The inclusion homomorphism from a additive subgroup `H` contained in `K` to `K`."]
def inclusion {H K : S} (h : H ≤ K) : H →* K :=
monoid_hom.mk' (λ x, ⟨x, h x.prop⟩) (λ ⟨a, ha⟩  ⟨b, hb⟩, rfl)

@[simp, to_additive] lemma inclusion_self (x : H) : inclusion le_rfl x = x := by { cases x, refl }
@[simp, to_additive] lemma inclusion_mk {h : H ≤ K} (x : G) (hx : x ∈ H) :
  inclusion h ⟨x, hx⟩ = ⟨x, h hx⟩ := rfl

@[to_additive]
lemma inclusion_right (h : H ≤ K) (x : K) (hx : (x : G) ∈ H) : inclusion h ⟨x, hx⟩ = x :=
by { cases x, refl }

@[simp] lemma inclusion_inclusion {L : S} (hHK : H ≤ K) (hKL : K ≤ L) (x : H) :
  inclusion hKL (inclusion hHK x) = inclusion (hHK.trans hKL) x :=
by { cases x, refl }

@[simp, to_additive]
lemma coe_inclusion {H K : S} {h : H ≤ K} (a : H) : (inclusion h a : G) = a :=
by { cases a, simp only [inclusion, set_like.coe_mk, monoid_hom.mk'_apply] }

@[simp, to_additive]
lemma subtype_comp_inclusion {H K : S} (hH : H ≤ K) :
  (subtype K).comp (inclusion hH) = subtype H :=
by { ext, simp only [monoid_hom.comp_apply, coe_subtype, coe_inclusion] }

end subgroup_class

end subgroup_class

set_option old_structure_cmd true

/-- A subgroup of a group `G` is a subset containing 1, closed under multiplication
and closed under multiplicative inverse. -/
structure subgroup (G : Type*) [group G] extends submonoid G :=
(inv_mem' {x} : x ∈ carrier → x⁻¹ ∈ carrier)

/-- An additive subgroup of an additive group `G` is a subset containing 0, closed
under addition and additive inverse. -/
structure add_subgroup (G : Type*) [add_group G] extends add_submonoid G:=
(neg_mem' {x} : x ∈ carrier → -x ∈ carrier)

attribute [to_additive] subgroup
attribute [to_additive add_subgroup.to_add_submonoid] subgroup.to_submonoid

/-- Reinterpret a `subgroup` as a `submonoid`. -/
add_decl_doc subgroup.to_submonoid

/-- Reinterpret an `add_subgroup` as an `add_submonoid`. -/
add_decl_doc add_subgroup.to_add_submonoid

namespace subgroup

@[to_additive]
instance : set_like (subgroup G) G :=
{ coe := subgroup.carrier,
  coe_injective' := λ p q h, by cases p; cases q; congr' }

@[to_additive]
instance : subgroup_class (subgroup G) G :=
{ mul_mem := subgroup.mul_mem',
  one_mem := subgroup.one_mem',
  inv_mem := subgroup.inv_mem' }

@[simp, to_additive]
lemma mem_carrier {s : subgroup G} {x : G} : x ∈ s.carrier ↔ x ∈ s := iff.rfl

@[simp, to_additive]
lemma mem_mk {s : set G} {x : G} (h_one) (h_mul) (h_inv) :
  x ∈ mk s h_one h_mul h_inv ↔ x ∈ s := iff.rfl

@[simp, to_additive]
lemma coe_set_mk {s : set G} (h_one) (h_mul) (h_inv) :
  (mk s h_one h_mul h_inv : set G) = s := rfl

@[simp, to_additive]
lemma mk_le_mk {s t : set G} (h_one) (h_mul) (h_inv) (h_one') (h_mul') (h_inv') :
  mk s h_one h_mul h_inv ≤ mk t h_one' h_mul' h_inv' ↔ s ⊆ t := iff.rfl

/-- See Note [custom simps projection] -/
@[to_additive "See Note [custom simps projection]"]
def simps.coe (S : subgroup G) : set G := S

initialize_simps_projections subgroup (carrier → coe)
initialize_simps_projections add_subgroup (carrier → coe)

@[simp, to_additive]
lemma coe_to_submonoid (K : subgroup G) : (K.to_submonoid : set G) = K := rfl

@[simp, to_additive]
lemma mem_to_submonoid (K : subgroup G) (x : G) : x ∈ K.to_submonoid ↔ x ∈ K := iff.rfl

@[to_additive]
instance (K : subgroup G) [d : decidable_pred (∈ K)] [fintype G] : fintype K :=
show fintype {g : G // g ∈ K}, from infer_instance

@[to_additive]
theorem to_submonoid_injective :
  function.injective (to_submonoid : subgroup G → submonoid G) :=
λ p q h, set_like.ext'_iff.2 (show _, from set_like.ext'_iff.1 h)

@[simp, to_additive]
theorem to_submonoid_eq {p q : subgroup G} : p.to_submonoid = q.to_submonoid ↔ p = q :=
to_submonoid_injective.eq_iff

@[to_additive, mono] lemma to_submonoid_strict_mono :
  strict_mono (to_submonoid : subgroup G → submonoid G) := λ _ _, id

attribute [mono] add_subgroup.to_add_submonoid_strict_mono

@[to_additive, mono]
lemma to_submonoid_mono : monotone (to_submonoid : subgroup G → submonoid G) :=
to_submonoid_strict_mono.monotone

attribute [mono] add_subgroup.to_add_submonoid_mono

@[simp, to_additive]
lemma to_submonoid_le {p q : subgroup G} : p.to_submonoid ≤ q.to_submonoid ↔ p ≤ q :=
iff.rfl

end subgroup

/-!
### Conversion to/from `additive`/`multiplicative`
-/
section mul_add

/-- Supgroups of a group `G` are isomorphic to additive subgroups of `additive G`. -/
@[simps]
def subgroup.to_add_subgroup : subgroup G ≃o add_subgroup (additive G) :=
{ to_fun := λ S,
  { neg_mem' := S.inv_mem',
    ..S.to_submonoid.to_add_submonoid },
  inv_fun := λ S,
  { inv_mem' := S.neg_mem',
    ..S.to_add_submonoid.to_submonoid' },
  left_inv := λ x, by cases x; refl,
  right_inv := λ x, by cases x; refl,
  map_rel_iff' := λ a b, iff.rfl, }

/-- Additive subgroup of an additive group `additive G` are isomorphic to subgroup of `G`. -/
abbreviation add_subgroup.to_subgroup' : add_subgroup (additive G) ≃o subgroup G :=
subgroup.to_add_subgroup.symm

/-- Additive supgroups of an additive group `A` are isomorphic to subgroups of `multiplicative A`.
-/
@[simps]
def add_subgroup.to_subgroup : add_subgroup A ≃o subgroup (multiplicative A) :=
{ to_fun := λ S,
  { inv_mem' := S.neg_mem',
    ..S.to_add_submonoid.to_submonoid },
  inv_fun := λ S,
  { neg_mem' := S.inv_mem',
    ..S.to_submonoid.to_add_submonoid' },
  left_inv := λ x, by cases x; refl,
  right_inv := λ x, by cases x; refl,
  map_rel_iff' := λ a b, iff.rfl, }

/-- Subgroups of an additive group `multiplicative A` are isomorphic to additive subgroups of `A`.
-/
abbreviation subgroup.to_add_subgroup' : subgroup (multiplicative A) ≃o add_subgroup A :=
add_subgroup.to_subgroup.symm

end mul_add

namespace subgroup

variables (H K : subgroup G)

/-- Copy of a subgroup with a new `carrier` equal to the old one. Useful to fix definitional
equalities.-/
@[to_additive "Copy of an additive subgroup with a new `carrier` equal to the old one.
Useful to fix definitional equalities"]
protected def copy (K : subgroup G) (s : set G) (hs : s = K) : subgroup G :=
{ carrier := s,
  one_mem' := hs.symm ▸ K.one_mem',
  mul_mem' := hs.symm ▸ K.mul_mem',
  inv_mem' := hs.symm ▸ K.inv_mem' }

@[simp, to_additive] lemma coe_copy (K : subgroup G) (s : set G) (hs : s = ↑K) :
  (K.copy s hs : set G) = s := rfl

@[to_additive]
lemma copy_eq (K : subgroup G) (s : set G) (hs : s = ↑K) : K.copy s hs = K :=
set_like.coe_injective hs

/-- Two subgroups are equal if they have the same elements. -/
@[ext, to_additive "Two `add_subgroup`s are equal if they have the same elements."]
theorem ext {H K : subgroup G} (h : ∀ x, x ∈ H ↔ x ∈ K) : H = K := set_like.ext h

/-- A subgroup contains the group's 1. -/
@[to_additive "An `add_subgroup` contains the group's 0."]
protected theorem one_mem : (1 : G) ∈ H := one_mem _

/-- A subgroup is closed under multiplication. -/
@[to_additive "An `add_subgroup` is closed under addition."]
protected theorem mul_mem {x y : G} : x ∈ H → y ∈ H → x * y ∈ H := mul_mem

/-- A subgroup is closed under inverse. -/
@[to_additive "An `add_subgroup` is closed under inverse."]
protected theorem inv_mem {x : G} : x ∈ H → x⁻¹ ∈ H := inv_mem

/-- A subgroup is closed under division. -/
@[to_additive "An `add_subgroup` is closed under subtraction."]
protected theorem div_mem {x y : G} (hx : x ∈ H) (hy : y ∈ H) : x / y ∈ H := div_mem hx hy

@[to_additive] protected theorem inv_mem_iff {x : G} : x⁻¹ ∈ H ↔ x ∈ H := inv_mem_iff

@[to_additive] protected lemma div_mem_comm_iff {a b : G} : a / b ∈ H ↔ b / a ∈ H :=
div_mem_comm_iff

@[to_additive] protected theorem inv_coe_set : (H : set G)⁻¹ = H := by { ext, simp }

@[to_additive] protected lemma exists_inv_mem_iff_exists_mem  (K : subgroup G) {P : G → Prop} :
  (∃ (x : G), x ∈ K ∧ P x⁻¹) ↔ ∃ x ∈ K, P x :=
exists_inv_mem_iff_exists_mem

@[to_additive] protected lemma mul_mem_cancel_right {x y : G} (h : x ∈ H) : y * x ∈ H ↔ y ∈ H :=
mul_mem_cancel_right h

@[to_additive] protected lemma mul_mem_cancel_left {x y : G} (h : x ∈ H) : x * y ∈ H ↔ y ∈ H :=
mul_mem_cancel_left h

/-- Product of a list of elements in a subgroup is in the subgroup. -/
@[to_additive "Sum of a list of elements in an `add_subgroup` is in the `add_subgroup`."]
protected lemma list_prod_mem {l : list G} : (∀ x ∈ l, x ∈ K) → l.prod ∈ K :=
list_prod_mem

/-- Product of a multiset of elements in a subgroup of a `comm_group` is in the subgroup. -/
@[to_additive "Sum of a multiset of elements in an `add_subgroup` of an `add_comm_group`
is in the `add_subgroup`."]
protected lemma multiset_prod_mem {G} [comm_group G] (K : subgroup G) (g : multiset G) :
  (∀ a ∈ g, a ∈ K) → g.prod ∈ K := multiset_prod_mem g

@[to_additive]
lemma multiset_noncomm_prod_mem (K : subgroup G) (g : multiset G)
  (comm : ∀ (x ∈ g) (y ∈ g), commute x y) :
  (∀ a ∈ g, a ∈ K) → g.noncomm_prod comm ∈ K :=
K.to_submonoid.multiset_noncomm_prod_mem g comm

/-- Product of elements of a subgroup of a `comm_group` indexed by a `finset` is in the
    subgroup. -/
@[to_additive "Sum of elements in an `add_subgroup` of an `add_comm_group` indexed by a `finset`
is in the `add_subgroup`."]
protected lemma prod_mem {G : Type*} [comm_group G] (K : subgroup G)
  {ι : Type*} {t : finset ι} {f : ι → G} (h : ∀ c ∈ t, f c ∈ K) :
  ∏ c in t, f c ∈ K :=
prod_mem h

@[to_additive]
lemma noncomm_prod_mem (K : subgroup G)
  {ι : Type*} {t : finset ι} {f : ι → G} (comm : ∀ (x ∈ t) (y ∈ t), commute (f x) (f y)) :
  (∀ c ∈ t, f c ∈ K) → t.noncomm_prod f comm ∈ K :=
K.to_submonoid.noncomm_prod_mem t f comm

@[to_additive add_subgroup.nsmul_mem]
protected lemma pow_mem {x : G} (hx : x ∈ K) : ∀ n : ℕ, x ^ n ∈ K := pow_mem hx

@[to_additive]
protected lemma zpow_mem {x : G} (hx : x ∈ K) : ∀ n : ℤ, x ^ n ∈ K := zpow_mem hx

/-- Construct a subgroup from a nonempty set that is closed under division. -/
@[to_additive "Construct a subgroup from a nonempty set that is closed under subtraction"]
def of_div (s : set G) (hsn : s.nonempty) (hs : ∀ x y ∈ s, x * y⁻¹ ∈ s) : subgroup G :=
have one_mem : (1 : G) ∈ s, from let ⟨x, hx⟩ := hsn in by simpa using hs x hx x hx,
have inv_mem : ∀ x, x ∈ s → x⁻¹ ∈ s, from λ x hx, by simpa using hs 1 one_mem x hx,
{ carrier := s,
  one_mem' := one_mem,
  inv_mem' := inv_mem,
  mul_mem' := λ x y hx hy, by simpa using hs x hx y⁻¹ (inv_mem y hy) }

/-- A subgroup of a group inherits a multiplication. -/
@[to_additive "An `add_subgroup` of an `add_group` inherits an addition."]
instance has_mul : has_mul H := H.to_submonoid.has_mul

/-- A subgroup of a group inherits a 1. -/
@[to_additive "An `add_subgroup` of an `add_group` inherits a zero."]
instance has_one : has_one H := H.to_submonoid.has_one

/-- A subgroup of a group inherits an inverse. -/
@[to_additive "A `add_subgroup` of a `add_group` inherits an inverse."]
instance has_inv : has_inv H := ⟨λ a, ⟨a⁻¹, H.inv_mem a.2⟩⟩

/-- A subgroup of a group inherits a division -/
@[to_additive "An `add_subgroup` of an `add_group` inherits a subtraction."]
instance has_div : has_div H := ⟨λ a b, ⟨a / b, H.div_mem a.2 b.2⟩⟩

/-- An `add_subgroup` of an `add_group` inherits a natural scaling. -/
instance _root_.add_subgroup.has_nsmul {G} [add_group G] {H : add_subgroup G} : has_smul ℕ H :=
⟨λ n a, ⟨n • a, H.nsmul_mem a.2 n⟩⟩

/-- A subgroup of a group inherits a natural power -/
@[to_additive]
instance has_npow : has_pow H ℕ := ⟨λ a n, ⟨a ^ n, H.pow_mem a.2 n⟩⟩

/-- An `add_subgroup` of an `add_group` inherits an integer scaling. -/
instance _root_.add_subgroup.has_zsmul {G} [add_group G] {H : add_subgroup G} : has_smul ℤ H :=
⟨λ n a, ⟨n • a, H.zsmul_mem a.2 n⟩⟩

/-- A subgroup of a group inherits an integer power -/
@[to_additive]
instance has_zpow : has_pow H ℤ := ⟨λ a n, ⟨a ^ n, H.zpow_mem a.2 n⟩⟩

@[simp, norm_cast, to_additive] lemma coe_mul (x y : H) : (↑(x * y) : G) = ↑x * ↑y := rfl
@[simp, norm_cast, to_additive] lemma coe_one : ((1 : H) : G) = 1 := rfl
@[simp, norm_cast, to_additive] lemma coe_inv (x : H) : ↑(x⁻¹ : H) = (x⁻¹ : G) := rfl
@[simp, norm_cast, to_additive] lemma coe_div (x y : H) : (↑(x / y) : G) = ↑x / ↑y := rfl
@[simp, norm_cast, to_additive] lemma coe_mk (x : G) (hx : x ∈ H) : ((⟨x, hx⟩ : H) : G) = x := rfl
@[simp, norm_cast, to_additive] lemma coe_pow (x : H) (n : ℕ) : ((x ^ n : H) : G) = x ^ n := rfl
@[simp, norm_cast, to_additive] lemma coe_zpow (x : H) (n : ℤ) : ((x ^ n : H) : G) = x ^ n := rfl

/-- A subgroup of a group inherits a group structure. -/
@[to_additive "An `add_subgroup` of an `add_group` inherits an `add_group` structure."]
instance to_group {G : Type*} [group G] (H : subgroup G) : group H :=
subtype.coe_injective.group _ rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

/-- A subgroup of a `comm_group` is a `comm_group`. -/
@[to_additive "An `add_subgroup` of an `add_comm_group` is an `add_comm_group`."]
instance to_comm_group {G : Type*} [comm_group G] (H : subgroup G) : comm_group H :=
subtype.coe_injective.comm_group _
  rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

/-- A subgroup of an `ordered_comm_group` is an `ordered_comm_group`. -/
@[to_additive "An `add_subgroup` of an `add_ordered_comm_group` is an `add_ordered_comm_group`."]
instance to_ordered_comm_group {G : Type*} [ordered_comm_group G] (H : subgroup G) :
  ordered_comm_group H :=
subtype.coe_injective.ordered_comm_group _
  rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

/-- A subgroup of a `linear_ordered_comm_group` is a `linear_ordered_comm_group`. -/
@[to_additive "An `add_subgroup` of a `linear_ordered_add_comm_group` is a
  `linear_ordered_add_comm_group`."]
instance to_linear_ordered_comm_group {G : Type*} [linear_ordered_comm_group G]
  (H : subgroup G) : linear_ordered_comm_group H :=
subtype.coe_injective.linear_ordered_comm_group _
  rfl (λ _ _, rfl) (λ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl) (λ _ _, rfl)

/-- The natural group hom from a subgroup of group `G` to `G`. -/
@[to_additive "The natural group hom from an `add_subgroup` of `add_group` `G` to `G`."]
def subtype : H →* G := ⟨coe, rfl, λ _ _, rfl⟩

@[simp, to_additive] theorem coe_subtype : ⇑H.subtype = coe := rfl

@[simp, norm_cast, to_additive] theorem coe_list_prod (l : list H) :
  (l.prod : G) = (l.map coe).prod :=
submonoid_class.coe_list_prod l

@[simp, norm_cast, to_additive] theorem coe_multiset_prod {G} [comm_group G] (H : subgroup G)
  (m : multiset H) : (m.prod : G) = (m.map coe).prod :=
submonoid_class.coe_multiset_prod m

@[simp, norm_cast, to_additive] theorem coe_finset_prod {ι G} [comm_group G] (H : subgroup G)
  (f : ι → H) (s : finset ι) :
  ↑(∏ i in s, f i) = (∏ i in s, f i : G) :=
submonoid_class.coe_finset_prod f s

/-- The inclusion homomorphism from a subgroup `H` contained in `K` to `K`. -/
@[to_additive "The inclusion homomorphism from a additive subgroup `H` contained in `K` to `K`."]
def inclusion {H K : subgroup G} (h : H ≤ K) : H →* K :=
monoid_hom.mk' (λ x, ⟨x, h x.prop⟩) (λ ⟨a, ha⟩  ⟨b, hb⟩, rfl)

@[simp, to_additive]
lemma coe_inclusion {H K : subgroup G} {h : H ≤ K} (a : H) : (inclusion h a : G) = a :=
by { cases a, simp only [inclusion, coe_mk, monoid_hom.mk'_apply] }

@[to_additive] lemma inclusion_injective {H K : subgroup G} (h : H ≤ K) :
  function.injective $ inclusion h :=
set.inclusion_injective h

@[simp, to_additive]
lemma subtype_comp_inclusion {H K : subgroup G} (hH : H ≤ K) :
  K.subtype.comp (inclusion hH) = H.subtype :=
by { ext, simp }

/-- The subgroup `G` of the group `G`. -/
@[to_additive "The `add_subgroup G` of the `add_group G`."]
instance : has_top (subgroup G) :=
⟨{ inv_mem' := λ _ _, set.mem_univ _ , .. (⊤ : submonoid G) }⟩

/-- The top subgroup is isomorphic to the group.

This is the group version of `submonoid.top_equiv`. -/
@[to_additive "The top additive subgroup is isomorphic to the additive group.

This is the additive group version of `add_submonoid.top_equiv`.", simps]
def top_equiv : (⊤ : subgroup G) ≃* G := submonoid.top_equiv

/-- The trivial subgroup `{1}` of an group `G`. -/
@[to_additive "The trivial `add_subgroup` `{0}` of an `add_group` `G`."]
instance : has_bot (subgroup G) :=
⟨{ inv_mem' := λ _, by simp *, .. (⊥ : submonoid G) }⟩

@[to_additive]
instance : inhabited (subgroup G) := ⟨⊥⟩

@[simp, to_additive] lemma mem_bot {x : G} : x ∈ (⊥ : subgroup G) ↔ x = 1 := iff.rfl

@[simp, to_additive] lemma mem_top (x : G) : x ∈ (⊤ : subgroup G) := set.mem_univ x

@[simp, to_additive] lemma coe_top : ((⊤ : subgroup G) : set G) = set.univ := rfl

@[simp, to_additive] lemma coe_bot : ((⊥ : subgroup G) : set G) = {1} := rfl

@[to_additive] instance : unique (⊥ : subgroup G) := ⟨⟨1⟩, λ g, subtype.ext g.2⟩

@[to_additive] lemma eq_bot_iff_forall : H = ⊥ ↔ ∀ x ∈ H, x = (1 : G) :=
begin
  rw set_like.ext'_iff,
  simp only [coe_bot, set.eq_singleton_iff_unique_mem, set_like.mem_coe, H.one_mem, true_and],
end

@[to_additive] lemma eq_bot_of_subsingleton [subsingleton H] : H = ⊥ :=
begin
  rw subgroup.eq_bot_iff_forall,
  intros y hy,
  rw [← subgroup.coe_mk H y hy, subsingleton.elim (⟨y, hy⟩ : H) 1, subgroup.coe_one],
end

@[to_additive] lemma coe_eq_univ {H : subgroup G} : (H : set G) = set.univ ↔ H = ⊤ :=
(set_like.ext'_iff.trans (by refl)).symm

@[to_additive] lemma coe_eq_singleton {H : subgroup G} : (∃ g : G, (H : set G) = {g}) ↔ H = ⊥ :=
⟨λ ⟨g, hg⟩, by { haveI : subsingleton (H : set G) := by { rw hg, apply_instance },
  exact H.eq_bot_of_subsingleton }, λ h, ⟨1, set_like.ext'_iff.mp h⟩⟩

@[to_additive] instance fintype_bot : fintype (⊥ : subgroup G) := ⟨{1},
by {rintro ⟨x, ⟨hx⟩⟩, exact finset.mem_singleton_self _}⟩

/- curly brackets `{}` are used here instead of instance brackets `[]` because
  the instance in a goal is often not the same as the one inferred by type class inference.  -/
@[simp, to_additive] lemma card_bot {_ : fintype ↥(⊥ : subgroup G)} :
  fintype.card (⊥ : subgroup G)  = 1 :=
fintype.card_eq_one_iff.2
  ⟨⟨(1 : G), set.mem_singleton 1⟩, λ ⟨y, hy⟩, subtype.eq $ subgroup.mem_bot.1 hy⟩

@[to_additive] lemma eq_top_of_card_eq [fintype H] [fintype G]
  (h : fintype.card H = fintype.card G) : H = ⊤ :=
begin
  haveI : fintype (H : set G) := ‹fintype H›,
  rw [set_like.ext'_iff, coe_top, ← finset.coe_univ, ← (H : set G).coe_to_finset, finset.coe_inj,
    ← finset.card_eq_iff_eq_univ, ← h, set.to_finset_card],
  congr
end

@[to_additive] lemma eq_top_of_le_card [fintype H] [fintype G]
  (h : fintype.card G ≤ fintype.card H) : H = ⊤ :=
eq_top_of_card_eq H (le_antisymm (fintype.card_le_of_injective coe subtype.coe_injective) h)

@[to_additive] lemma eq_bot_of_card_le [fintype H] (h : fintype.card H ≤ 1) : H = ⊥ :=
let _ := fintype.card_le_one_iff_subsingleton.mp h in by exactI eq_bot_of_subsingleton H

@[to_additive] lemma eq_bot_of_card_eq [fintype H] (h : fintype.card H = 1) : H = ⊥ :=
H.eq_bot_of_card_le (le_of_eq h)

@[to_additive] lemma nontrivial_iff_exists_ne_one (H : subgroup G) :
  nontrivial H ↔ ∃ x ∈ H, x ≠ (1:G) :=
subtype.nontrivial_iff_exists_ne (λ x, x ∈ H) (1 : H)

/-- A subgroup is either the trivial subgroup or nontrivial. -/
@[to_additive] lemma bot_or_nontrivial (H : subgroup G) : H = ⊥ ∨ nontrivial H :=
begin
  classical,
  by_cases h : ∀ x ∈ H, x = (1 : G),
  { left,
    exact H.eq_bot_iff_forall.mpr h },
  { right,
    simp only [not_forall] at h,
    simpa only [nontrivial_iff_exists_ne_one] }
end

/-- A subgroup is either the trivial subgroup or contains a nonzero element. -/
@[to_additive] lemma bot_or_exists_ne_one (H : subgroup G) : H = ⊥ ∨ ∃ x ∈ H, x ≠ (1:G) :=
begin
  convert H.bot_or_nontrivial,
  rw nontrivial_iff_exists_ne_one
end

@[to_additive] lemma card_le_one_iff_eq_bot [fintype H] : fintype.card H ≤ 1 ↔ H = ⊥ :=
⟨λ h, (eq_bot_iff_forall _).2
    (λ x hx, by simpa [subtype.ext_iff] using fintype.card_le_one_iff.1 h ⟨x, hx⟩ 1),
  λ h, by simp [h]⟩

@[to_additive] lemma one_lt_card_iff_ne_bot [fintype H] : 1 < fintype.card H ↔ H ≠ ⊥ :=
lt_iff_not_le.trans H.card_le_one_iff_eq_bot.not

/-- The inf of two subgroups is their intersection. -/
@[to_additive "The inf of two `add_subgroups`s is their intersection."]
instance : has_inf (subgroup G) :=
⟨λ H₁ H₂,
  { inv_mem' := λ _ ⟨hx, hx'⟩, ⟨H₁.inv_mem hx, H₂.inv_mem hx'⟩,
    .. H₁.to_submonoid ⊓ H₂.to_submonoid }⟩

@[simp, to_additive]
lemma coe_inf (p p' : subgroup G) : ((p ⊓ p' : subgroup G) : set G) = p ∩ p' := rfl

@[simp, to_additive]
lemma mem_inf {p p' : subgroup G} {x : G} : x ∈ p ⊓ p' ↔ x ∈ p ∧ x ∈ p' := iff.rfl

@[to_additive]
instance : has_Inf (subgroup G) :=
⟨λ s,
  { inv_mem' := λ x hx, set.mem_bInter $ λ i h, i.inv_mem (by apply set.mem_Inter₂.1 hx i h),
    .. (⨅ S ∈ s, subgroup.to_submonoid S).copy (⋂ S ∈ s, ↑S) (by simp) }⟩

@[simp, norm_cast, to_additive]
lemma coe_Inf (H : set (subgroup G)) : ((Inf H : subgroup G) : set G) = ⋂ s ∈ H, ↑s := rfl

@[simp, to_additive]
lemma mem_Inf {S : set (subgroup G)} {x : G} : x ∈ Inf S ↔ ∀ p ∈ S, x ∈ p := set.mem_Inter₂

@[to_additive]
lemma mem_infi {ι : Sort*} {S : ι → subgroup G} {x : G} : (x ∈ ⨅ i, S i) ↔ ∀ i, x ∈ S i :=
by simp only [infi, mem_Inf, set.forall_range_iff]

@[simp, norm_cast, to_additive]
lemma coe_infi {ι : Sort*} {S : ι → subgroup G} : (↑(⨅ i, S i) : set G) = ⋂ i, S i :=
by simp only [infi, coe_Inf, set.bInter_range]

/-- Subgroups of a group form a complete lattice. -/
@[to_additive "The `add_subgroup`s of an `add_group` form a complete lattice."]
instance : complete_lattice (subgroup G) :=
{ bot          := (⊥),
  bot_le       := λ S x hx, (mem_bot.1 hx).symm ▸ S.one_mem,
  top          := (⊤),
  le_top       := λ S x hx, mem_top x,
  inf          := (⊓),
  le_inf       := λ a b c ha hb x hx, ⟨ha hx, hb hx⟩,
  inf_le_left  := λ a b x, and.left,
  inf_le_right := λ a b x, and.right,
  .. complete_lattice_of_Inf (subgroup G) $ λ s, is_glb.of_image
    (λ H K, show (H : set G) ≤ K ↔ H ≤ K, from set_like.coe_subset_coe) is_glb_binfi }

@[to_additive]
lemma mem_sup_left {S T : subgroup G} : ∀ {x : G}, x ∈ S → x ∈ S ⊔ T :=
show S ≤ S ⊔ T, from le_sup_left

@[to_additive]
lemma mem_sup_right {S T : subgroup G} : ∀ {x : G}, x ∈ T → x ∈ S ⊔ T :=
show T ≤ S ⊔ T, from le_sup_right

@[to_additive]
lemma mul_mem_sup {S T : subgroup G} {x y : G} (hx : x ∈ S) (hy : y ∈ T) : x * y ∈ S ⊔ T :=
(S ⊔ T).mul_mem (mem_sup_left hx) (mem_sup_right hy)

@[to_additive]
lemma mem_supr_of_mem {ι : Sort*} {S : ι → subgroup G} (i : ι) :
  ∀ {x : G}, x ∈ S i → x ∈ supr S :=
show S i ≤ supr S, from le_supr _ _

@[to_additive]
lemma mem_Sup_of_mem {S : set (subgroup G)} {s : subgroup G}
  (hs : s ∈ S) : ∀ {x : G}, x ∈ s → x ∈ Sup S :=
show s ≤ Sup S, from le_Sup hs

@[simp, to_additive]
lemma subsingleton_iff : subsingleton (subgroup G) ↔ subsingleton G :=
⟨ λ h, by exactI ⟨λ x y,
    have ∀ i : G, i = 1 := λ i, mem_bot.mp $ subsingleton.elim (⊤ : subgroup G) ⊥ ▸ mem_top i,
    (this x).trans (this y).symm⟩,
  λ h, by exactI ⟨λ x y, subgroup.ext $ λ i, subsingleton.elim 1 i ▸ by simp [subgroup.one_mem]⟩⟩

@[simp, to_additive]
lemma nontrivial_iff : nontrivial (subgroup G) ↔ nontrivial G :=
not_iff_not.mp (
  (not_nontrivial_iff_subsingleton.trans subsingleton_iff).trans
  not_nontrivial_iff_subsingleton.symm)

@[to_additive]
instance [subsingleton G] : unique (subgroup G) :=
⟨⟨⊥⟩, λ a, @subsingleton.elim _ (subsingleton_iff.mpr ‹_›) a _⟩

@[to_additive]
instance [nontrivial G] : nontrivial (subgroup G) := nontrivial_iff.mpr ‹_›

@[to_additive] lemma eq_top_iff' : H = ⊤ ↔ ∀ x : G, x ∈ H :=
eq_top_iff.trans ⟨λ h m, h $ mem_top m, λ h m _, h m⟩

/-- The `subgroup` generated by a set. -/
@[to_additive "The `add_subgroup` generated by a set"]
def closure (k : set G) : subgroup G := Inf {K | k ⊆ K}

variable {k : set G}

@[to_additive]
lemma mem_closure {x : G} : x ∈ closure k ↔ ∀ K : subgroup G, k ⊆ K → x ∈ K :=
mem_Inf

/-- The subgroup generated by a set includes the set. -/
@[simp, to_additive "The `add_subgroup` generated by a set includes the set."]
lemma subset_closure : k ⊆ closure k := λ x hx, mem_closure.2 $ λ K hK, hK hx

@[to_additive]
lemma not_mem_of_not_mem_closure {P : G} (hP : P ∉ closure k) : P ∉ k := λ h, hP (subset_closure h)

open set

/-- A subgroup `K` includes `closure k` if and only if it includes `k`. -/
@[simp, to_additive "An additive subgroup `K` includes `closure k` if and only if it includes `k`"]
lemma closure_le : closure k ≤ K ↔ k ⊆ K :=
⟨subset.trans subset_closure, λ h, Inf_le h⟩

@[to_additive]
lemma closure_eq_of_le (h₁ : k ⊆ K) (h₂ : K ≤ closure k) : closure k = K :=
le_antisymm ((closure_le $ K).2 h₁) h₂

/-- An induction principle for closure membership. If `p` holds for `1` and all elements of `k`, and
is preserved under multiplication and inverse, then `p` holds for all elements of the closure
of `k`. -/
@[elab_as_eliminator, to_additive "An induction principle for additive closure membership. If `p`
holds for `0` and all elements of `k`, and is preserved under addition and inverses, then `p` holds
for all elements of the additive closure of `k`."]
lemma closure_induction {p : G → Prop} {x} (h : x ∈ closure k)
  (Hk : ∀ x ∈ k, p x) (H1 : p 1)
  (Hmul : ∀ x y, p x → p y → p (x * y))
  (Hinv : ∀ x, p x → p x⁻¹) : p x :=
(@closure_le _ _ ⟨p, Hmul, H1, Hinv⟩ _).2 Hk h

/-- A dependent version of `subgroup.closure_induction`.  -/
@[elab_as_eliminator, to_additive "A dependent version of `add_subgroup.closure_induction`. "]
lemma closure_induction' {p : Π x, x ∈ closure k → Prop}
  (Hs : ∀ x (h : x ∈ k), p x (subset_closure h))
  (H1 : p 1 (one_mem _))
  (Hmul : ∀ x hx y hy, p x hx → p y hy → p (x * y) (mul_mem hx hy))
  (Hinv : ∀ x hx, p x hx → p x⁻¹ (inv_mem hx))
  {x} (hx : x ∈ closure k) :
  p x hx :=
begin
  refine exists.elim _ (λ (hx : x ∈ closure k) (hc : p x hx), hc),
  exact closure_induction hx
    (λ x hx, ⟨_, Hs x hx⟩) ⟨_, H1⟩ (λ x y ⟨hx', hx⟩ ⟨hy', hy⟩, ⟨_, Hmul _ _ _ _ hx hy⟩)
    (λ x ⟨hx', hx⟩, ⟨_, Hinv _ _ hx⟩),
end

/-- An induction principle for closure membership for predicates with two arguments. -/
@[elab_as_eliminator, to_additive "An induction principle for additive closure membership, for
predicates with two arguments."]
lemma closure_induction₂ {p : G → G → Prop} {x} {y : G} (hx : x ∈ closure k) (hy : y ∈ closure k)
  (Hk : ∀ (x ∈ k) (y ∈ k), p x y)
  (H1_left : ∀ x, p 1 x)
  (H1_right : ∀ x, p x 1)
  (Hmul_left : ∀ x₁ x₂ y, p x₁ y → p x₂ y → p (x₁ * x₂) y)
  (Hmul_right : ∀ x y₁ y₂, p x y₁ → p x y₂ → p x (y₁ * y₂))
  (Hinv_left : ∀ x y, p x y → p x⁻¹ y)
  (Hinv_right : ∀ x y, p x y → p x y⁻¹) : p x y :=
closure_induction hx
  (λ x xk, closure_induction hy (Hk x xk) (H1_right x) (Hmul_right x) (Hinv_right x))
  (H1_left y) (λ z z', Hmul_left z z' y) (λ z, Hinv_left z y)

@[simp, to_additive]
lemma closure_closure_coe_preimage {k : set G} : closure ((coe : closure k → G) ⁻¹' k) = ⊤ :=
eq_top_iff.2 $ λ x, subtype.rec_on x $ λ x hx _, begin
  refine closure_induction' (λ g hg, _) _ (λ g₁ g₂ hg₁ hg₂, _) (λ g hg, _) hx,
  { exact subset_closure hg },
  { exact one_mem _ },
  { exact mul_mem },
  { exact inv_mem }
end

/-- If all the elements of a set `s` commute, then `closure s` is a commutative group. -/
@[to_additive "If all the elements of a set `s` commute, then `closure s` is an additive
commutative group."]
def closure_comm_group_of_comm {k : set G} (hcomm : ∀ (x ∈ k) (y ∈ k), x * y = y * x) :
  comm_group (closure k) :=
{ mul_comm := λ x y,
  begin
    ext,
    simp only [subgroup.coe_mul],
    refine closure_induction₂ x.prop y.prop hcomm
    (λ  x, by simp only [mul_one, one_mul])
    (λ  x, by simp only [mul_one, one_mul])
    (λ x y z h₁ h₂, by rw [mul_assoc, h₂, ←mul_assoc, h₁, mul_assoc])
    (λ x y z h₁ h₂, by rw [←mul_assoc, h₁, mul_assoc, h₂, ←mul_assoc])
    (λ x y h, by rw [inv_mul_eq_iff_eq_mul, ←mul_assoc, h, mul_assoc, mul_inv_self, mul_one])
    (λ x y h, by rw [mul_inv_eq_iff_eq_mul, mul_assoc, h, ←mul_assoc, inv_mul_self, one_mul])
  end,
  ..(closure k).to_group }

variable (G)

/-- `closure` forms a Galois insertion with the coercion to set. -/
@[to_additive "`closure` forms a Galois insertion with the coercion to set."]
protected def gi : galois_insertion (@closure G _) coe :=
{ choice := λ s _, closure s,
  gc := λ s t, @closure_le _ _ t s,
  le_l_u := λ s, subset_closure,
  choice_eq := λ s h, rfl }

variable {G}

/-- Subgroup closure of a set is monotone in its argument: if `h ⊆ k`,
then `closure h ≤ closure k`. -/
@[to_additive "Additive subgroup closure of a set is monotone in its argument: if `h ⊆ k`,
then `closure h ≤ closure k`"]
lemma closure_mono ⦃h k : set G⦄ (h' : h ⊆ k) : closure h ≤ closure k :=
(subgroup.gi G).gc.monotone_l h'

/-- Closure of a subgroup `K` equals `K`. -/
@[simp, to_additive "Additive closure of an additive subgroup `K` equals `K`"]
lemma closure_eq : closure (K : set G) = K := (subgroup.gi G).l_u_eq K

@[simp, to_additive] lemma closure_empty : closure (∅ : set G) = ⊥ :=
(subgroup.gi G).gc.l_bot

@[simp, to_additive] lemma closure_univ : closure (univ : set G) = ⊤ :=
@coe_top G _ ▸ closure_eq ⊤

@[to_additive]
lemma closure_union (s t : set G) : closure (s ∪ t) = closure s ⊔ closure t :=
(subgroup.gi G).gc.l_sup

@[to_additive]
lemma closure_Union {ι} (s : ι → set G) : closure (⋃ i, s i) = ⨆ i, closure (s i) :=
(subgroup.gi G).gc.l_supr

@[to_additive]
lemma closure_eq_bot_iff (G : Type*) [group G] (S : set G) :
  closure S = ⊥ ↔ S ⊆ {1} :=
by { rw [← le_bot_iff], exact closure_le _}

@[to_additive]
lemma supr_eq_closure {ι : Sort*} (p : ι → subgroup G) :
  (⨆ i, p i) = closure (⋃ i, (p i : set G)) :=
by simp_rw [closure_Union, closure_eq]

/-- The subgroup generated by an element of a group equals the set of integer number powers of
    the element. -/
@[to_additive /-"The `add_subgroup` generated by an element of an `add_group` equals the set of
natural number multiples of the element."-/]
lemma mem_closure_singleton {x y : G} : y ∈ closure ({x} : set G) ↔ ∃ n : ℤ, x ^ n = y :=
begin
  refine ⟨λ hy, closure_induction hy _ _ _ _,
    λ ⟨n, hn⟩, hn ▸ zpow_mem (subset_closure $ mem_singleton x) n⟩,
  { intros y hy,
    rw [eq_of_mem_singleton hy],
    exact ⟨1, zpow_one x⟩ },
  { exact ⟨0, zpow_zero x⟩ },
  { rintros _ _ ⟨n, rfl⟩ ⟨m, rfl⟩,
    exact ⟨n + m, zpow_add x n m⟩ },
    rintros _ ⟨n, rfl⟩,
    exact ⟨-n, zpow_neg x n⟩
end

@[to_additive]
lemma closure_singleton_one : closure ({1} : set G) = ⊥ :=
by simp [eq_bot_iff_forall, mem_closure_singleton]

@[simp, to_additive] lemma inv_subset_closure (S : set G) : S⁻¹ ⊆ closure S :=
begin
  intros s hs,
  rw [set_like.mem_coe, ←subgroup.inv_mem_iff],
  exact subset_closure (mem_inv.mp hs),
end

@[simp, to_additive] lemma closure_inv (S : set G) : closure S⁻¹ = closure S :=
begin
  refine le_antisymm ((subgroup.closure_le _).2 _) ((subgroup.closure_le _).2 _),
  { exact inv_subset_closure S },
  { simpa only [inv_inv] using inv_subset_closure S⁻¹ },
end

@[to_additive]
lemma closure_to_submonoid (S : set G) :
  (closure S).to_submonoid = submonoid.closure (S ∪ S⁻¹) :=
begin
  refine le_antisymm _ (submonoid.closure_le.2 _),
  { intros x hx,
    refine closure_induction hx (λ x hx, submonoid.closure_mono (subset_union_left S S⁻¹)
      (submonoid.subset_closure hx)) (submonoid.one_mem _) (λ x y hx hy, submonoid.mul_mem _ hx hy)
      (λ x hx, _),
    rwa [←submonoid.mem_closure_inv, set.union_inv, inv_inv, set.union_comm] },
  { simp only [true_and, coe_to_submonoid, union_subset_iff, subset_closure, inv_subset_closure] }
end

@[to_additive] lemma closure_induction_left {p : G → Prop} {x : G}
  (h : x ∈ closure k) (H1 : p 1) (Hmul : ∀ (x ∈ k) y, p y → p (x * y))
  (Hinv : ∀ (x ∈ k) y, p y → p (x⁻¹ * y)) : p x :=
let key := le_of_eq (closure_to_submonoid k) in submonoid.closure_induction_left (key h) H1
  (λ x hx, hx.elim (Hmul x) (λ hx y hy, (congr_arg _ (inv_inv x)).mp (Hinv x⁻¹ hx y hy)))

@[to_additive] lemma closure_induction_right {p : G → Prop} {x : G}
  (h : x ∈ closure k) (H1 : p 1) (Hmul : ∀ x (y ∈ k), p x → p (x * y))
  (Hinv : ∀ x (y ∈ k), p x → p (x * y⁻¹)) : p x :=
let key := le_of_eq (closure_to_submonoid k) in submonoid.closure_induction_right (key h) H1
  (λ x y hy, hy.elim (Hmul x y) (λ hy hx, (congr_arg _ (inv_inv y)).mp (Hinv x y⁻¹ hy hx)))

/-- An induction principle for closure membership. If `p` holds for `1` and all elements of
`k` and their inverse, and is preserved under multiplication, then `p` holds for all elements of
the closure of `k`. -/
@[to_additive "An induction principle for additive closure membership. If `p` holds for `0` and all
elements of `k` and their negation, and is preserved under addition, then `p` holds for all
elements of the additive closure of `k`."]
lemma closure_induction'' {p : G → Prop} {x} (h : x ∈ closure k)
  (Hk : ∀ x ∈ k, p x) (Hk_inv : ∀ x ∈ k, p x⁻¹) (H1 : p 1)
  (Hmul : ∀ x y, p x → p y → p (x * y)) : p x :=
closure_induction_left h H1 (λ x hx y hy, Hmul x y (Hk x hx) hy)
  (λ x hx y hy, Hmul x⁻¹ y (Hk_inv x hx) hy)

/-- An induction principle for elements of `⨆ i, S i`.
If `C` holds for `1` and all elements of `S i` for all `i`, and is preserved under multiplication,
then it holds for all elements of the supremum of `S`. -/
@[elab_as_eliminator, to_additive /-" An induction principle for elements of `⨆ i, S i`.
If `C` holds for `0` and all elements of `S i` for all `i`, and is preserved under addition,
then it holds for all elements of the supremum of `S`. "-/]
lemma supr_induction {ι : Sort*} (S : ι → subgroup G) {C : G → Prop} {x : G} (hx : x ∈ ⨆ i, S i)
  (hp : ∀ i (x ∈ S i), C x)
  (h1 : C 1)
  (hmul : ∀ x y, C x → C y → C (x * y)) : C x :=
begin
  rw supr_eq_closure at hx,
  refine closure_induction'' hx (λ x hx, _) (λ x hx, _) h1 hmul,
  { obtain ⟨i, hi⟩ := set.mem_Union.mp hx,
    exact hp _ _ hi, },
  { obtain ⟨i, hi⟩ := set.mem_Union.mp hx,
    exact hp _ _ (inv_mem hi), },
end

/-- A dependent version of `subgroup.supr_induction`. -/
@[elab_as_eliminator, to_additive /-"A dependent version of `add_subgroup.supr_induction`. "-/]
lemma supr_induction' {ι : Sort*} (S : ι → subgroup G) {C : Π x, (x ∈ ⨆ i, S i) → Prop}
  (hp : ∀ i (x ∈ S i), C x (mem_supr_of_mem i ‹_›))
  (h1 : C 1 (one_mem _))
  (hmul : ∀ x y hx hy, C x hx → C y hy → C (x * y) (mul_mem ‹_› ‹_›))
  {x : G} (hx : x ∈ ⨆ i, S i) : C x hx :=
begin
  refine exists.elim _ (λ (hx : x ∈ ⨆ i, S i) (hc : C x hx), hc),
  refine supr_induction S hx (λ i x hx, _) _ (λ x y, _),
  { exact ⟨_, hp _ _ hx⟩ },
  { exact ⟨_, h1⟩ },
  { rintro ⟨_, Cx⟩ ⟨_, Cy⟩,
    refine ⟨_, hmul _ _ _ _ Cx Cy⟩ },
end

@[to_additive]
lemma mem_supr_of_directed {ι} [hι : nonempty ι] {K : ι → subgroup G} (hK : directed (≤) K)
  {x : G} :
  x ∈ (supr K : subgroup G) ↔ ∃ i, x ∈ K i :=
begin
  refine ⟨_, λ ⟨i, hi⟩, (set_like.le_def.1 $ le_supr K i) hi⟩,
  suffices : x ∈ closure (⋃ i, (K i : set G)) → ∃ i, x ∈ K i,
    by simpa only [closure_Union, closure_eq (K _)] using this,
  refine (λ hx, closure_induction hx (λ _, mem_Union.1) _ _ _),
  { exact hι.elim (λ i, ⟨i, (K i).one_mem⟩) },
  { rintros x y ⟨i, hi⟩ ⟨j, hj⟩,
    rcases hK i j with ⟨k, hki, hkj⟩,
    exact ⟨k, mul_mem (hki hi) (hkj hj)⟩ },
    rintros _ ⟨i, hi⟩, exact ⟨i, inv_mem hi⟩
end

@[to_additive]
lemma coe_supr_of_directed {ι} [nonempty ι] {S : ι → subgroup G} (hS : directed (≤) S) :
  ((⨆ i, S i : subgroup G) : set G) = ⋃ i, ↑(S i) :=
set.ext $ λ x, by simp [mem_supr_of_directed hS]

@[to_additive]
lemma mem_Sup_of_directed_on {K : set (subgroup G)} (Kne : K.nonempty)
  (hK : directed_on (≤) K) {x : G} :
  x ∈ Sup K ↔ ∃ s ∈ K, x ∈ s :=
begin
  haveI : nonempty K := Kne.to_subtype,
  simp only [Sup_eq_supr', mem_supr_of_directed hK.directed_coe, set_coe.exists, subtype.coe_mk]
end

variables {N : Type*} [group N] {P : Type*} [group P]

/-- The preimage of a subgroup along a monoid homomorphism is a subgroup. -/
@[to_additive "The preimage of an `add_subgroup` along an `add_monoid` homomorphism
is an `add_subgroup`."]
def comap {N : Type*} [group N] (f : G →* N)
  (H : subgroup N) : subgroup G :=
{ carrier := (f ⁻¹' H),
  inv_mem' := λ a ha,
    show f a⁻¹ ∈ H, by rw f.map_inv; exact H.inv_mem ha,
  .. H.to_submonoid.comap f }

@[simp, to_additive]
lemma coe_comap (K : subgroup N) (f : G →* N) : (K.comap f : set G) = f ⁻¹' K := rfl

@[simp, to_additive]
lemma mem_comap {K : subgroup N} {f : G →* N} {x : G} : x ∈ K.comap f ↔ f x ∈ K := iff.rfl

@[to_additive]
lemma comap_mono {f : G →* N} {K K' : subgroup N} : K ≤ K' → comap f K ≤ comap f K' :=
preimage_mono

@[to_additive]
lemma comap_comap (K : subgroup P) (g : N →* P) (f : G →* N) :
  (K.comap g).comap f = K.comap (g.comp f) :=
rfl

/-- The image of a subgroup along a monoid homomorphism is a subgroup. -/
@[to_additive "The image of an `add_subgroup` along an `add_monoid` homomorphism
is an `add_subgroup`."]
def map (f : G →* N) (H : subgroup G) : subgroup N :=
{ carrier := (f '' H),
  inv_mem' := by { rintros _ ⟨x, hx, rfl⟩, exact ⟨x⁻¹, H.inv_mem hx, f.map_inv x⟩ },
  .. H.to_submonoid.map f }

@[simp, to_additive]
lemma coe_map (f : G →* N) (K : subgroup G) :
  (K.map f : set N) = f '' K := rfl

@[simp, to_additive]
lemma mem_map {f : G →* N} {K : subgroup G} {y : N} :
  y ∈ K.map f ↔ ∃ x ∈ K, f x = y :=
mem_image_iff_bex

@[to_additive]
lemma mem_map_of_mem (f : G →* N) {K : subgroup G} {x : G} (hx : x ∈ K) : f x ∈ K.map f :=
mem_image_of_mem f hx

@[to_additive]
lemma apply_coe_mem_map (f : G →* N) (K : subgroup G) (x : K) : f x ∈ K.map f :=
mem_map_of_mem f x.prop

@[to_additive]
lemma map_mono {f : G →* N} {K K' : subgroup G} : K ≤ K' → map f K ≤ map f K' :=
image_subset _

@[simp, to_additive]
lemma map_id : K.map (monoid_hom.id G) = K :=
set_like.coe_injective $ image_id _

@[to_additive]
lemma map_map (g : N →* P) (f : G →* N) : (K.map f).map g = K.map (g.comp f) :=
set_like.coe_injective $ image_image _ _ _

@[simp, to_additive]
lemma map_one_eq_bot : K.map (1 : G →* N) = ⊥ :=
eq_bot_iff.mpr $ by { rintros x ⟨y, _ , rfl⟩, simp }

@[to_additive]
lemma mem_map_equiv {f : G ≃* N} {K : subgroup G} {x : N} :
  x ∈ K.map f.to_monoid_hom ↔ f.symm x ∈ K :=
@set.mem_image_equiv _ _ ↑K f.to_equiv x

@[to_additive]
lemma mem_map_iff_mem {f : G →* N} (hf : function.injective f) {K : subgroup G} {x : G} :
  f x ∈ K.map f ↔ x ∈ K :=
hf.mem_set_image

@[to_additive]
lemma map_equiv_eq_comap_symm (f : G ≃* N) (K : subgroup G) :
  K.map f.to_monoid_hom = K.comap f.symm.to_monoid_hom :=
set_like.coe_injective (f.to_equiv.image_eq_preimage K)

@[to_additive]
lemma comap_equiv_eq_map_symm (f : N ≃* G) (K : subgroup G) :
  K.comap f.to_monoid_hom = K.map f.symm.to_monoid_hom :=
(map_equiv_eq_comap_symm f.symm K).symm

@[to_additive]
lemma map_le_iff_le_comap {f : G →* N} {K : subgroup G} {H : subgroup N} :
  K.map f ≤ H ↔ K ≤ H.comap f :=
image_subset_iff

@[to_additive]
lemma gc_map_comap (f : G →* N) : galois_connection (map f) (comap f) :=
λ _ _, map_le_iff_le_comap

@[to_additive]
lemma map_sup (H K : subgroup G) (f : G →* N) : (H ⊔ K).map f = H.map f ⊔ K.map f :=
(gc_map_comap f).l_sup

@[to_additive]
lemma map_supr {ι : Sort*} (f : G →* N) (s : ι → subgroup G) :
  (supr s).map f = ⨆ i, (s i).map f :=
(gc_map_comap f).l_supr

@[to_additive] lemma comap_sup_comap_le
  (H K : subgroup N) (f : G →* N) : comap f H ⊔ comap f K ≤ comap f (H ⊔ K) :=
monotone.le_map_sup (λ _ _, comap_mono) H K

@[to_additive] lemma supr_comap_le {ι : Sort*} (f : G →* N) (s : ι → subgroup N) :
  (⨆ i, (s i).comap f) ≤ (supr s).comap f :=
monotone.le_map_supr (λ _ _, comap_mono)

@[to_additive]
lemma comap_inf (H K : subgroup N) (f : G →* N) : (H ⊓ K).comap f = H.comap f ⊓ K.comap f :=
(gc_map_comap f).u_inf

@[to_additive]
lemma comap_infi {ι : Sort*} (f : G →* N) (s : ι → subgroup N) :
  (infi s).comap f = ⨅ i, (s i).comap f :=
(gc_map_comap f).u_infi

@[to_additive] lemma map_inf_le (H K : subgroup G) (f : G →* N) :
  map f (H ⊓ K) ≤ map f H ⊓ map f K :=
le_inf (map_mono inf_le_left) (map_mono inf_le_right)

@[to_additive] lemma map_inf_eq (H K : subgroup G) (f : G →* N) (hf : function.injective f) :
  map f (H ⊓ K) = map f H ⊓ map f K :=
begin
  rw ← set_like.coe_set_eq,
  simp [set.image_inter hf],
end

@[simp, to_additive] lemma map_bot (f : G →* N) : (⊥ : subgroup G).map f = ⊥ :=
(gc_map_comap f).l_bot

@[simp, to_additive] lemma map_top_of_surjective (f : G →* N) (h : function.surjective f) :
  subgroup.map f ⊤ = ⊤ :=
by {rw eq_top_iff, intros x hx, obtain ⟨y, hy⟩ := (h x), exact ⟨y, trivial, hy⟩ }

@[simp, to_additive] lemma comap_top (f : G →* N) : (⊤ : subgroup N).comap f = ⊤ :=
(gc_map_comap f).u_top

@[simp, to_additive] lemma comap_subtype_self_eq_top {G : Type*} [group G] {H : subgroup G} :
  comap H.subtype H = ⊤ := by { ext, simp }

@[simp, to_additive]
lemma comap_subtype_inf_left {H K : subgroup G} : comap H.subtype (H ⊓ K) = comap H.subtype K :=
ext $ λ x, and_iff_right_of_imp (λ _, x.prop)

@[simp, to_additive]
lemma comap_subtype_inf_right {H K : subgroup G} : comap K.subtype (H ⊓ K) = comap K.subtype H :=
ext $ λ x, and_iff_left_of_imp (λ _, x.prop)

/-- If `H ≤ K`, then `H` as a subgroup of `K` is isomorphic to `H`. -/
@[to_additive "If `H ≤ K`, then `H` as a subgroup of `K` is isomorphic to `H`.", simps]
def comap_subtype_equiv_of_le {G : Type*} [group G] {H K : subgroup G} (h : H ≤ K) :
  H.comap K.subtype ≃* H :=
{ to_fun := λ g, ⟨g.1, g.2⟩,
  inv_fun := λ g, ⟨⟨g.1, h g.2⟩, g.2⟩,
  left_inv := λ g, subtype.ext (subtype.ext rfl),
  right_inv := λ g, subtype.ext rfl,
  map_mul' := λ g h, rfl }

/-- For any subgroups `H` and `K`, view `H ⊓ K` as a subgroup of `K`. -/
@[to_additive "For any subgroups `H` and `K`, view `H ⊓ K` as a subgroup of `K`."]
def subgroup_of (H K : subgroup G) : subgroup K := H.comap K.subtype

@[to_additive] lemma coe_subgroup_of (H K : subgroup G) :
  (H.subgroup_of K : set K) = K.subtype ⁻¹' H := rfl

@[to_additive] lemma mem_subgroup_of {H K : subgroup G} {h : K} :
  h ∈ H.subgroup_of K ↔ (h : G) ∈ H :=
iff.rfl

@[to_additive] lemma subgroup_of_map_subtype (H K : subgroup G) :
  (H.subgroup_of K).map K.subtype = H ⊓ K := set_like.ext'
begin
  convert set.image_preimage_eq_inter_range,
  simp only [subtype.range_coe_subtype, coe_subtype, coe_inf],
  refl,
end

@[simp, to_additive] lemma bot_subgroup_of : (⊥ : subgroup G).subgroup_of H = ⊥ :=
eq.symm (subgroup.ext (λ g, subtype.ext_iff))

@[simp, to_additive] lemma top_subgroup_of : (⊤ : subgroup G).subgroup_of H = ⊤ :=
rfl

@[to_additive] lemma subgroup_of_bot_eq_bot : H.subgroup_of ⊥ = ⊥ :=
subsingleton.elim _ _

@[to_additive] lemma subgroup_of_bot_eq_top : H.subgroup_of ⊥ = ⊤ :=
subsingleton.elim _ _

@[simp, to_additive] lemma subgroup_of_self : H.subgroup_of H = ⊤ :=
top_le_iff.mp (λ g hg, g.2)

/-- Given `subgroup`s `H`, `K` of groups `G`, `N` respectively, `H × K` as a subgroup of `G × N`. -/
@[to_additive prod "Given `add_subgroup`s `H`, `K` of `add_group`s `A`, `B` respectively, `H × K`
as an `add_subgroup` of `A × B`."]
def prod (H : subgroup G) (K : subgroup N) : subgroup (G × N) :=
{ inv_mem' := λ _ hx, ⟨H.inv_mem' hx.1, K.inv_mem' hx.2⟩,
  .. submonoid.prod H.to_submonoid K.to_submonoid}

@[to_additive coe_prod]
lemma coe_prod (H : subgroup G) (K : subgroup N) : (H.prod K : set (G × N)) = H ×ˢ K := rfl

@[to_additive mem_prod]
lemma mem_prod {H : subgroup G} {K : subgroup N} {p : G × N} :
  p ∈ H.prod K ↔ p.1 ∈ H ∧ p.2 ∈ K := iff.rfl

@[to_additive prod_mono]
lemma prod_mono : ((≤) ⇒ (≤) ⇒ (≤)) (@prod G _ N _) (@prod G _ N _) :=
λ s s' hs t t' ht, set.prod_mono hs ht

@[to_additive prod_mono_right]
lemma prod_mono_right (K : subgroup G) : monotone (λ t : subgroup N, K.prod t) :=
prod_mono (le_refl K)

@[to_additive prod_mono_left]
lemma prod_mono_left (H : subgroup N) : monotone (λ K : subgroup G, K.prod H) :=
λ s₁ s₂ hs, prod_mono hs (le_refl H)

@[to_additive prod_top]
lemma prod_top (K : subgroup G) :
  K.prod (⊤ : subgroup N) = K.comap (monoid_hom.fst G N) :=
ext $ λ x, by simp [mem_prod, monoid_hom.coe_fst]

@[to_additive top_prod]
lemma top_prod (H : subgroup N) :
  (⊤ : subgroup G).prod H = H.comap (monoid_hom.snd G N) :=
ext $ λ x, by simp [mem_prod, monoid_hom.coe_snd]

@[simp, to_additive top_prod_top]
lemma top_prod_top : (⊤ : subgroup G).prod (⊤ : subgroup N) = ⊤ :=
(top_prod _).trans $ comap_top _

@[to_additive] lemma bot_prod_bot : (⊥ : subgroup G).prod (⊥ : subgroup N) = ⊥ :=
set_like.coe_injective $ by simp [coe_prod, prod.one_eq_mk]

@[to_additive le_prod_iff]
lemma le_prod_iff {H : subgroup G} {K : subgroup N} {J : subgroup (G × N)} :
  J ≤ H.prod K ↔ map (monoid_hom.fst G N) J ≤ H ∧ map (monoid_hom.snd G N) J ≤ K :=
by simpa only [← subgroup.to_submonoid_le] using submonoid.le_prod_iff

@[to_additive prod_le_iff]
lemma prod_le_iff {H : subgroup G} {K : subgroup N} {J : subgroup (G × N)} :
  H.prod K ≤ J ↔ map (monoid_hom.inl G N) H ≤ J ∧ map (monoid_hom.inr G N) K ≤ J :=
by simpa only [← subgroup.to_submonoid_le] using submonoid.prod_le_iff

@[simp, to_additive prod_eq_bot_iff]
lemma prod_eq_bot_iff {H : subgroup G} {K : subgroup N} :
  H.prod K = ⊥ ↔ H = ⊥ ∧ K = ⊥ :=
by simpa only [← subgroup.to_submonoid_eq] using submonoid.prod_eq_bot_iff

/-- Product of subgroups is isomorphic to their product as groups. -/
@[to_additive prod_equiv "Product of additive subgroups is isomorphic to their product
as additive groups"]
def prod_equiv (H : subgroup G) (K : subgroup N) : H.prod K ≃* H × K :=
{ map_mul' := λ x y, rfl, .. equiv.set.prod ↑H ↑K }

section pi

variables {η : Type*} {f : η → Type*}

-- defined here and not in group_theory.submonoid.operations to have access to algebra.group.pi

/-- A version of `set.pi` for submonoids. Given an index set `I` and a family of submodules
`s : Π i, submonoid f i`, `pi I s` is the submonoid of dependent functions `f : Π i, f i` such that
`f i` belongs to `pi I s` whenever `i ∈ I`. -/
@[to_additive " A version of `set.pi` for `add_submonoid`s. Given an index set `I` and a family
of submodules `s : Π i, add_submonoid f i`, `pi I s` is the `add_submonoid` of dependent functions
`f : Π i, f i` such that `f i` belongs to `pi I s` whenever `i ∈ I`. -/ "]
def _root_.submonoid.pi [∀ i, mul_one_class (f i)] (I : set η) (s : Π i, submonoid (f i)) :
  submonoid (Π i, f i) :=
{ carrier := I.pi (λ i, (s i).carrier),
  one_mem' := λ i _ , (s i).one_mem,
  mul_mem' := λ p q hp hq i hI, (s i).mul_mem (hp i hI) (hq i hI) }

variables [∀ i, group (f i)]

/-- A version of `set.pi` for subgroups. Given an index set `I` and a family of submodules
`s : Π i, subgroup f i`, `pi I s` is the subgroup of dependent functions `f : Π i, f i` such that
`f i` belongs to `pi I s` whenever `i ∈ I`. -/
@[to_additive " A version of `set.pi` for `add_subgroup`s. Given an index set `I` and a family
of submodules `s : Π i, add_subgroup f i`, `pi I s` is the `add_subgroup` of dependent functions
`f : Π i, f i` such that `f i` belongs to `pi I s` whenever `i ∈ I`. -/ "]
def pi (I : set η) (H : Π i, subgroup (f i)) : subgroup (Π i, f i) :=
{ submonoid.pi I (λ i, (H i).to_submonoid) with
  inv_mem' := λ p hp i hI, (H i).inv_mem (hp i hI) }

@[to_additive] lemma coe_pi (I : set η) (H : Π i, subgroup (f i)) :
  (pi I H : set (Π i, f i)) = set.pi I (λ i, (H i : set (f i))) := rfl

@[to_additive] lemma mem_pi (I : set η) {H : Π i, subgroup (f i)} {p : Π i, f i} :
  p ∈ pi I H ↔ (∀ i : η, i ∈ I → p i ∈ H i) := iff.rfl

@[to_additive] lemma pi_top (I : set η) : pi I (λ i, (⊤ : subgroup (f i))) = ⊤ :=
ext $ λ x, by simp [mem_pi]

@[to_additive] lemma pi_empty (H : Π i, subgroup (f i)): pi ∅ H = ⊤ :=
ext $ λ x, by simp [mem_pi]

@[to_additive] lemma pi_bot : pi set.univ (λ i, (⊥ : subgroup (f i))) = ⊥ :=
(eq_bot_iff_forall _).mpr $ λ p hp,
by { simp only [mem_pi, mem_bot] at *, ext j, exact hp j trivial, }

@[to_additive]
lemma le_pi_iff {I : set η} {H : Π i, subgroup (f i)} {J : subgroup (Π i, f i)} :
  J ≤ pi I H ↔ (∀ i : η , i ∈ I → map (pi.eval_monoid_hom f i) J ≤ H i) :=
begin
  split,
  { intros h i hi, rintros _ ⟨x, hx, rfl⟩, exact (h hx) _ hi, },
  { intros h x hx i hi, refine h i hi ⟨_, hx, rfl⟩, }
end

@[simp, to_additive]
lemma mul_single_mem_pi [decidable_eq η] {I : set η} {H : Π i, subgroup (f i)}
  (i : η) (x : f i) :
  pi.mul_single i x ∈ pi I H ↔ (i ∈ I → x ∈ H i) :=
begin
  split,
  { intros h hi, simpa using h i hi, },
  { intros h j hj,
    by_cases heq : j = i,
    { subst heq, simpa using h hj, },
    { simp [heq, one_mem], }, }
end

@[to_additive]
lemma pi_mem_of_mul_single_mem_aux [decidable_eq η] (I : finset η) {H : subgroup (Π i, f i) }
  (x : Π i, f i) (h1 : ∀ i, i ∉ I → x i = 1) (h2 : ∀ i, i ∈ I → pi.mul_single i (x i) ∈ H ) :
  x ∈ H :=
begin
  induction I using finset.induction_on with i I hnmem ih generalizing x,
  { convert one_mem H,
    ext i,
    exact (h1 i (not_mem_empty i)) },
  { have : x = function.update x i 1 * pi.mul_single i (x i),
    { ext j,
      by_cases heq : j = i,
      { subst heq, simp, },
      { simp [heq], }, },
    rw this, clear this,
    apply mul_mem,
    { apply ih; clear ih,
      { intros j hj,
        by_cases heq : j = i,
        { subst heq, simp, },
        { simp [heq], apply h1 j, simpa [heq] using hj, } },
      { intros j hj,
        have : j ≠ i, by { rintro rfl, contradiction },
        simp [this],
        exact h2 _ (finset.mem_insert_of_mem hj), }, },
    { apply h2, simp, } }
end

@[to_additive]
lemma pi_mem_of_mul_single_mem [fintype η] [decidable_eq η] {H : subgroup (Π i, f i)}
  (x : Π i, f i) (h : ∀ i, pi.mul_single i (x i) ∈ H) : x ∈ H :=
pi_mem_of_mul_single_mem_aux finset.univ x (by simp) (λ i _, h i)

/-- For finite index types, the `subgroup.pi` is generated by the embeddings of the groups.  -/
@[to_additive "For finite index types, the `subgroup.pi` is generated by the embeddings of the
additive groups."]
lemma pi_le_iff [decidable_eq η] [fintype η] {H : Π i, subgroup (f i)} {J : subgroup (Π i, f i)} :
  pi univ H ≤ J ↔ (∀ i : η, map (monoid_hom.single f i) (H i) ≤ J) :=
begin
  split,
  { rintros h i _ ⟨x, hx, rfl⟩, apply h, simpa using hx },
  { exact λ h x hx, pi_mem_of_mul_single_mem  x (λ i, h i (mem_map_of_mem _ (hx i trivial))), }
end

@[to_additive]
lemma pi_eq_bot_iff (H : Π i, subgroup (f i)) :
  pi set.univ H = ⊥ ↔ ∀ i, H i = ⊥ :=
begin
  classical,
  simp only [eq_bot_iff_forall],
  split,
  { intros h i x hx,
    have : monoid_hom.single f i x = 1 :=
    h (monoid_hom.single f i x) ((mul_single_mem_pi i x).mpr (λ _, hx)),
    simpa using congr_fun this i, },
  { exact λ h x hx, funext (λ i, h _ _ (hx i trivial)), },
end

end pi

/-- A subgroup is normal if whenever `n ∈ H`, then `g * n * g⁻¹ ∈ H` for every `g : G` -/
structure normal : Prop :=
(conj_mem : ∀ n, n ∈ H → ∀ g : G, g * n * g⁻¹ ∈ H)

attribute [class] normal

end subgroup

namespace add_subgroup

/-- An add_subgroup is normal if whenever `n ∈ H`, then `g + n - g ∈ H` for every `g : G` -/
structure normal (H : add_subgroup A) : Prop :=
(conj_mem [] : ∀ n, n ∈ H → ∀ g : A, g + n + -g ∈ H)

attribute [to_additive add_subgroup.normal] subgroup.normal
attribute [class] normal

end add_subgroup

namespace subgroup

variables {H K : subgroup G}
@[priority 100, to_additive]
instance normal_of_comm {G : Type*} [comm_group G] (H : subgroup G) : H.normal :=
⟨by simp [mul_comm, mul_left_comm]⟩

namespace normal

variable (nH : H.normal)

@[to_additive] lemma mem_comm {a b : G} (h : a * b ∈ H) : b * a ∈ H :=
have a⁻¹ * (a * b) * a⁻¹⁻¹ ∈ H, from nH.conj_mem (a * b) h a⁻¹, by simpa

@[to_additive] lemma mem_comm_iff {a b : G} : a * b ∈ H ↔ b * a ∈ H :=
⟨nH.mem_comm, nH.mem_comm⟩

end normal

variables (H)

/-- A subgroup is characteristic if it is fixed by all automorphisms.
  Several equivalent conditions are provided by lemmas of the form `characteristic.iff...` -/
structure characteristic : Prop :=
(fixed : ∀ ϕ : G ≃* G, H.comap ϕ.to_monoid_hom = H)

attribute [class] characteristic

@[priority 100] instance normal_of_characteristic [h : H.characteristic] : H.normal :=
⟨λ a ha b, (set_like.ext_iff.mp (h.fixed (mul_aut.conj b)) a).mpr ha⟩

end subgroup

namespace add_subgroup

variables (H : add_subgroup A)

/-- A add_subgroup is characteristic if it is fixed by all automorphisms.
  Several equivalent conditions are provided by lemmas of the form `characteristic.iff...` -/
structure characteristic  : Prop :=
(fixed : ∀ ϕ : A ≃+ A, H.comap ϕ.to_add_monoid_hom = H)

attribute [to_additive add_subgroup.characteristic] subgroup.characteristic
attribute [class] characteristic

@[priority 100] instance normal_of_characteristic [h : H.characteristic] : H.normal :=
⟨λ a ha b, (set_like.ext_iff.mp (h.fixed (add_aut.conj b)) a).mpr ha⟩

end add_subgroup

namespace subgroup

variables {H K : subgroup G}

@[to_additive] lemma characteristic_iff_comap_eq :
  H.characteristic ↔ ∀ ϕ : G ≃* G, H.comap ϕ.to_monoid_hom = H :=
⟨characteristic.fixed, characteristic.mk⟩

@[to_additive] lemma characteristic_iff_comap_le :
  H.characteristic ↔ ∀ ϕ : G ≃* G, H.comap ϕ.to_monoid_hom ≤ H :=
characteristic_iff_comap_eq.trans ⟨λ h ϕ, le_of_eq (h ϕ),
  λ h ϕ, le_antisymm (h ϕ) (λ g hg, h ϕ.symm ((congr_arg (∈ H) (ϕ.symm_apply_apply g)).mpr hg))⟩

@[to_additive] lemma characteristic_iff_le_comap :
  H.characteristic ↔ ∀ ϕ : G ≃* G, H ≤ H.comap ϕ.to_monoid_hom :=
characteristic_iff_comap_eq.trans ⟨λ h ϕ, ge_of_eq (h ϕ),
  λ h ϕ, le_antisymm (λ g hg, (congr_arg (∈ H) (ϕ.symm_apply_apply g)).mp (h ϕ.symm hg)) (h ϕ)⟩

@[to_additive] lemma characteristic_iff_map_eq :
  H.characteristic ↔ ∀ ϕ : G ≃* G, H.map ϕ.to_monoid_hom = H :=
begin
  simp_rw map_equiv_eq_comap_symm,
  exact characteristic_iff_comap_eq.trans ⟨λ h ϕ, h ϕ.symm, λ h ϕ, h ϕ.symm⟩,
end

@[to_additive] lemma characteristic_iff_map_le :
  H.characteristic ↔ ∀ ϕ : G ≃* G, H.map ϕ.to_monoid_hom ≤ H :=
begin
  simp_rw map_equiv_eq_comap_symm,
  exact characteristic_iff_comap_le.trans ⟨λ h ϕ, h ϕ.symm, λ h ϕ, h ϕ.symm⟩,
end

@[to_additive] lemma characteristic_iff_le_map :
  H.characteristic ↔ ∀ ϕ : G ≃* G, H ≤ H.map ϕ.to_monoid_hom :=
begin
  simp_rw map_equiv_eq_comap_symm,
  exact characteristic_iff_le_comap.trans ⟨λ h ϕ, h ϕ.symm, λ h ϕ, h ϕ.symm⟩,
end

@[to_additive] instance bot_characteristic : characteristic (⊥ : subgroup G) :=
characteristic_iff_le_map.mpr (λ ϕ, bot_le)

@[to_additive] instance top_characteristic : characteristic (⊤ : subgroup G) :=
characteristic_iff_map_le.mpr (λ ϕ, le_top)

variable (G)
/-- The center of a group `G` is the set of elements that commute with everything in `G` -/
@[to_additive "The center of an additive group `G` is the set of elements that commute with
everything in `G`"]
def center : subgroup G :=
{ carrier := set.center G,
  inv_mem' := λ a, set.inv_mem_center,
  .. submonoid.center G }

@[to_additive]
lemma coe_center : ↑(center G) = set.center G := rfl

@[simp, to_additive]
lemma center_to_submonoid : (center G).to_submonoid = submonoid.center G := rfl

variable {G}

@[to_additive] lemma mem_center_iff {z : G} : z ∈ center G ↔ ∀ g, g * z = z * g := iff.rfl

instance decidable_mem_center [decidable_eq G] [fintype G] : decidable_pred (∈ center G) :=
λ _, decidable_of_iff' _ mem_center_iff

@[to_additive] instance center_characteristic : (center G).characteristic :=
begin
  refine characteristic_iff_comap_le.mpr (λ ϕ g hg h, _),
  rw [←ϕ.injective.eq_iff, ϕ.map_mul, ϕ.map_mul],
  exact hg (ϕ h),
end

lemma _root_.comm_group.center_eq_top {G : Type*} [comm_group G] : center G = ⊤ :=
by { rw [eq_top_iff'], intros x y, exact mul_comm y x }

/-- A group is commutative if the center is the whole group -/
def _root_.group.comm_group_of_center_eq_top (h : center G = ⊤) : comm_group G :=
{ mul_comm := by { rw eq_top_iff' at h, intros x y, exact h y x },
  .. (_ : group G) }

variables {G} (H)

section normalizer

/-- The `normalizer` of `H` is the largest subgroup of `G` inside which `H` is normal. -/
@[to_additive "The `normalizer` of `H` is the largest subgroup of `G` inside which `H` is normal."]
def normalizer : subgroup G :=
{ carrier := {g : G | ∀ n, n ∈ H ↔ g * n * g⁻¹ ∈ H},
  one_mem' := by simp,
  mul_mem' := λ a b (ha : ∀ n, n ∈ H ↔ a * n * a⁻¹ ∈ H) (hb : ∀ n, n ∈ H ↔ b * n * b⁻¹ ∈ H) n,
    by { rw [hb, ha], simp [mul_assoc] },
  inv_mem' := λ a (ha : ∀ n, n ∈ H ↔ a * n * a⁻¹ ∈ H) n,
    by { rw [ha (a⁻¹ * n * a⁻¹⁻¹)], simp [mul_assoc] } }

-- variant for sets.
-- TODO should this replace `normalizer`?
/-- The `set_normalizer` of `S` is the subgroup of `G` whose elements satisfy `g*S*g⁻¹=S` -/
@[to_additive "The `set_normalizer` of `S` is the subgroup of `G` whose elements satisfy
`g+S-g=S`."]
def set_normalizer (S : set G) : subgroup G :=
{ carrier := {g : G | ∀ n, n ∈ S ↔ g * n * g⁻¹ ∈ S},
  one_mem' := by simp,
  mul_mem' := λ a b (ha : ∀ n, n ∈ S ↔ a * n * a⁻¹ ∈ S) (hb : ∀ n, n ∈ S ↔ b * n * b⁻¹ ∈ S) n,
    by { rw [hb, ha], simp [mul_assoc] },
  inv_mem' := λ a (ha : ∀ n, n ∈ S ↔ a * n * a⁻¹ ∈ S) n,
    by { rw [ha (a⁻¹ * n * a⁻¹⁻¹)], simp [mul_assoc] } }

lemma mem_normalizer_fintype {S : set G} [fintype S] {x : G}
  (h : ∀ n, n ∈ S → x * n * x⁻¹ ∈ S) : x ∈ subgroup.set_normalizer S :=
by haveI := classical.prop_decidable;
haveI := set.fintype_image S (λ n, x * n * x⁻¹); exact
λ n, ⟨h n, λ h₁,
have heq : (λ n, x * n * x⁻¹) '' S = S := set.eq_of_subset_of_card_le
  (λ n ⟨y, hy⟩, hy.2 ▸ h y hy.1) (by rw set.card_image_of_injective S conj_injective),
have x * n * x⁻¹ ∈ (λ n, x * n * x⁻¹) '' S := heq.symm ▸ h₁,
let ⟨y, hy⟩ := this in conj_injective hy.2 ▸ hy.1⟩

variable {H}
@[to_additive] lemma mem_normalizer_iff {g : G} :
  g ∈ H.normalizer ↔ ∀ h, h ∈ H ↔ g * h * g⁻¹ ∈ H :=
iff.rfl

@[to_additive] lemma mem_normalizer_iff'' {g : G} :
  g ∈ H.normalizer ↔ ∀ h : G, h ∈ H ↔ g⁻¹ * h * g ∈ H :=
by rw [←inv_mem_iff, mem_normalizer_iff, inv_inv]

@[to_additive] lemma mem_normalizer_iff' {g : G} : g ∈ H.normalizer ↔ ∀ n, n * g ∈ H ↔ g * n ∈ H :=
⟨λ h n, by rw [h, mul_assoc, mul_inv_cancel_right],
  λ h n, by rw [mul_assoc, ←h, inv_mul_cancel_right]⟩

@[to_additive] lemma le_normalizer : H ≤ normalizer H :=
λ x xH n, by rw [H.mul_mem_cancel_right (H.inv_mem xH), H.mul_mem_cancel_left xH]

@[priority 100, to_additive]
instance normal_in_normalizer : (H.comap H.normalizer.subtype).normal :=
⟨λ x xH g, by simpa using (g.2 x).1 xH⟩

@[to_additive] lemma normalizer_eq_top : H.normalizer = ⊤ ↔ H.normal :=
eq_top_iff.trans ⟨λ h, ⟨λ a ha b, (h (mem_top b) a).mp ha⟩, λ h a ha b,
  ⟨λ hb, h.conj_mem b hb a, λ hb, by rwa [h.mem_comm_iff, inv_mul_cancel_left] at hb⟩⟩

@[to_additive] lemma center_le_normalizer : center G ≤ H.normalizer :=
λ x hx y, by simp [← mem_center_iff.mp hx y, mul_assoc]

open_locale classical

@[to_additive]
lemma le_normalizer_of_normal [hK : (H.comap K.subtype).normal] (HK : H ≤ K) : K ≤ H.normalizer :=
λ x hx y, ⟨λ yH, hK.conj_mem ⟨y, HK yH⟩ yH ⟨x, hx⟩,
  λ yH, by simpa [mem_comap, mul_assoc] using
             hK.conj_mem ⟨x * y * x⁻¹, HK yH⟩ yH ⟨x⁻¹, K.inv_mem hx⟩⟩

variables {N : Type*} [group N]

/-- The preimage of the normalizer is contained in the normalizer of the preimage. -/
@[to_additive "The preimage of the normalizer is contained in the normalizer of the preimage."]
lemma le_normalizer_comap (f : N →* G) :
  H.normalizer.comap f ≤ (H.comap f).normalizer :=
λ x, begin
  simp only [mem_normalizer_iff, mem_comap],
  assume h n,
  simp [h (f n)]
end

/-- The image of the normalizer is contained in the normalizer of the image. -/
@[to_additive "The image of the normalizer is contained in the normalizer of the image."]
lemma le_normalizer_map (f : G →* N) :
  H.normalizer.map f ≤ (H.map f).normalizer :=
λ _, begin
  simp only [and_imp, exists_prop, mem_map, exists_imp_distrib, mem_normalizer_iff],
  rintros x hx rfl n,
  split,
  { rintros ⟨y, hy, rfl⟩,
    use [x * y * x⁻¹, (hx y).1 hy],
    simp },
  { rintros ⟨y, hyH, hy⟩,
    use [x⁻¹ * y * x],
    rw [hx],
    simp [hy, hyH, mul_assoc] }
end

variable (G)

/-- Every proper subgroup `H` of `G` is a proper normal subgroup of the normalizer of `H` in `G`. -/
def _root_.normalizer_condition := ∀ (H : subgroup G), H < ⊤ → H < normalizer H

variable {G}

/-- Alternative phrasing of the normalizer condition: Only the full group is self-normalizing.
This may be easier to work with, as it avoids inequalities and negations.  -/
lemma _root_.normalizer_condition_iff_only_full_group_self_normalizing :
  normalizer_condition G ↔ ∀ (H : subgroup G), H.normalizer = H → H = ⊤ :=
begin
  apply forall_congr, intro H,
  simp only [lt_iff_le_and_ne, le_normalizer, true_and, le_top, ne.def],
  tauto!,
end

variable (H)

/-- In a group that satisifes the normalizer condition, every maximal subgroup is normal -/
lemma normalizer_condition.normal_of_coatom
  (hnc : normalizer_condition G) (hmax : is_coatom H) : H.normal :=
normalizer_eq_top.mp (hmax.2 _ (hnc H (lt_top_iff_ne_top.mpr hmax.1)))

end normalizer

section centralizer

/-- The `centralizer` of `H` is the subgroup of `g : G` commuting with every `h : H`. -/
@[to_additive "The `centralizer` of `H` is the additive subgroup of `g : G` commuting with
every `h : H`."]
def centralizer : subgroup G :=
{ carrier := set.centralizer H,
  inv_mem' := λ g, set.inv_mem_centralizer,
  .. submonoid.centralizer ↑H }

@[to_additive] lemma mem_centralizer_iff {g : G} : g ∈ H.centralizer ↔ ∀ h ∈ H, h * g = g * h :=
iff.rfl

@[to_additive] lemma mem_centralizer_iff_commutator_eq_one {g : G} :
  g ∈ H.centralizer ↔ ∀ h ∈ H, h * g * h⁻¹ * g⁻¹ = 1 :=
by simp only [mem_centralizer_iff, mul_inv_eq_iff_eq_mul, one_mul]

@[to_additive] lemma centralizer_top : centralizer ⊤ = center G :=
set_like.ext' (set.centralizer_univ G)

@[to_additive] instance subgroup.centralizer.characteristic [hH : H.characteristic] :
  H.centralizer.characteristic :=
begin
  refine subgroup.characteristic_iff_comap_le.mpr (λ ϕ g hg h hh, ϕ.injective _),
  rw [map_mul, map_mul],
  exact hg (ϕ h) (subgroup.characteristic_iff_le_comap.mp hH ϕ hh),
end

end centralizer

/-- Commutivity of a subgroup -/
structure is_commutative : Prop :=
(is_comm : _root_.is_commutative H (*))

attribute [class] is_commutative

/-- Commutivity of an additive subgroup -/
structure _root_.add_subgroup.is_commutative (H : add_subgroup A) : Prop :=
(is_comm : _root_.is_commutative H (+))

attribute [to_additive add_subgroup.is_commutative] subgroup.is_commutative
attribute [class] add_subgroup.is_commutative

/-- A commutative subgroup is commutative -/
@[to_additive] instance is_commutative.comm_group [h : H.is_commutative] : comm_group H :=
{ mul_comm := h.is_comm.comm, .. H.to_group }

instance center.is_commutative : (center G).is_commutative :=
⟨⟨λ a b, subtype.ext (b.2 a)⟩⟩

end subgroup

namespace group
variables {s : set G}

/-- Given a set `s`, `conjugates_of_set s` is the set of all conjugates of
the elements of `s`. -/
def conjugates_of_set (s : set G) : set G := ⋃ a ∈ s, conjugates_of a

lemma mem_conjugates_of_set_iff {x : G} : x ∈ conjugates_of_set s ↔ ∃ a ∈ s, is_conj a x :=
set.mem_Union₂

theorem subset_conjugates_of_set : s ⊆ conjugates_of_set s :=
λ (x : G) (h : x ∈ s), mem_conjugates_of_set_iff.2 ⟨x, h, is_conj.refl _⟩

theorem conjugates_of_set_mono {s t : set G} (h : s ⊆ t) :
  conjugates_of_set s ⊆ conjugates_of_set t :=
set.bUnion_subset_bUnion_left h

lemma conjugates_subset_normal {N : subgroup G} [tn : N.normal] {a : G} (h : a ∈ N) :
  conjugates_of a ⊆ N :=
by { rintros a hc, obtain ⟨c, rfl⟩ := is_conj_iff.1 hc, exact tn.conj_mem a h c }

theorem conjugates_of_set_subset {s : set G} {N : subgroup G} [N.normal] (h : s ⊆ N) :
  conjugates_of_set s ⊆ N :=
set.Union₂_subset (λ x H, conjugates_subset_normal (h H))

/-- The set of conjugates of `s` is closed under conjugation. -/
lemma conj_mem_conjugates_of_set {x c : G} :
  x ∈ conjugates_of_set s → (c * x * c⁻¹ ∈ conjugates_of_set s) :=
λ H,
begin
  rcases (mem_conjugates_of_set_iff.1 H) with ⟨a,h₁,h₂⟩,
  exact mem_conjugates_of_set_iff.2 ⟨a, h₁, h₂.trans (is_conj_iff.2 ⟨c,rfl⟩)⟩,
end

end group

namespace subgroup
open group

variable {s : set G}

/-- The normal closure of a set `s` is the subgroup closure of all the conjugates of
elements of `s`. It is the smallest normal subgroup containing `s`. -/
def normal_closure (s : set G) : subgroup G := closure (conjugates_of_set s)

theorem conjugates_of_set_subset_normal_closure : conjugates_of_set s ⊆ normal_closure s :=
subset_closure

theorem subset_normal_closure : s ⊆ normal_closure s :=
set.subset.trans subset_conjugates_of_set conjugates_of_set_subset_normal_closure

theorem le_normal_closure {H : subgroup G} : H ≤ normal_closure ↑H :=
λ _ h, subset_normal_closure h

/-- The normal closure of `s` is a normal subgroup. -/
instance normal_closure_normal : (normal_closure s).normal :=
⟨λ n h g,
begin
  refine subgroup.closure_induction h (λ x hx, _) _ (λ x y ihx ihy, _) (λ x ihx, _),
  { exact (conjugates_of_set_subset_normal_closure (conj_mem_conjugates_of_set hx)) },
  { simpa using (normal_closure s).one_mem },
  { rw ← conj_mul,
    exact mul_mem ihx ihy },
  { rw ← conj_inv,
    exact inv_mem ihx }
end⟩

/-- The normal closure of `s` is the smallest normal subgroup containing `s`. -/
theorem normal_closure_le_normal {N : subgroup G} [N.normal]
  (h : s ⊆ N) : normal_closure s ≤ N :=
begin
  assume a w,
  refine closure_induction w (λ x hx, _) _  (λ x y ihx ihy, _) (λ x ihx, _),
  { exact (conjugates_of_set_subset h hx) },
  { exact one_mem _ },
  { exact mul_mem ihx ihy },
  { exact inv_mem ihx }
end

lemma normal_closure_subset_iff {N : subgroup G} [N.normal] : s ⊆ N ↔ normal_closure s ≤ N :=
⟨normal_closure_le_normal, set.subset.trans (subset_normal_closure)⟩

theorem normal_closure_mono {s t : set G} (h : s ⊆ t) : normal_closure s ≤ normal_closure t :=
normal_closure_le_normal (set.subset.trans h subset_normal_closure)

theorem normal_closure_eq_infi : normal_closure s =
  ⨅ (N : subgroup G) (_ : normal N) (hs : s ⊆ N), N :=
le_antisymm
  (le_infi (λ N, le_infi (λ hN, by exactI le_infi (normal_closure_le_normal))))
  (infi_le_of_le (normal_closure s) (infi_le_of_le (by apply_instance)
    (infi_le_of_le subset_normal_closure le_rfl)))

@[simp] theorem normal_closure_eq_self (H : subgroup G) [H.normal] : normal_closure ↑H = H :=
le_antisymm (normal_closure_le_normal rfl.subset) (le_normal_closure)

@[simp] theorem normal_closure_idempotent : normal_closure ↑(normal_closure s) = normal_closure s :=
normal_closure_eq_self _

theorem closure_le_normal_closure {s : set G} : closure s ≤ normal_closure s :=
by simp only [subset_normal_closure, closure_le]

@[simp] theorem normal_closure_closure_eq_normal_closure {s : set G} :
  normal_closure ↑(closure s) = normal_closure s :=
le_antisymm (normal_closure_le_normal closure_le_normal_closure)
  (normal_closure_mono subset_closure)

/-- The normal core of a subgroup `H` is the largest normal subgroup of `G` contained in `H`,
as shown by `subgroup.normal_core_eq_supr`. -/
def normal_core (H : subgroup G) : subgroup G :=
{ carrier := {a : G | ∀ b : G, b * a * b⁻¹ ∈ H},
  one_mem' := λ a, by rw [mul_one, mul_inv_self]; exact H.one_mem,
  inv_mem' := λ a h b, (congr_arg (∈ H) conj_inv).mp (H.inv_mem (h b)),
  mul_mem' := λ a b ha hb c, (congr_arg (∈ H) conj_mul).mp (H.mul_mem (ha c) (hb c)) }

lemma normal_core_le (H : subgroup G) : H.normal_core ≤ H :=
λ a h, by { rw [←mul_one a, ←inv_one, ←one_mul a], exact h 1 }

instance normal_core_normal (H : subgroup G) : H.normal_core.normal :=
⟨λ a h b c, by rw [mul_assoc, mul_assoc, ←mul_inv_rev, ←mul_assoc, ←mul_assoc]; exact h (c * b)⟩

lemma normal_le_normal_core {H : subgroup G} {N : subgroup G} [hN : N.normal] :
  N ≤ H.normal_core ↔ N ≤ H :=
⟨ge_trans H.normal_core_le, λ h_le n hn g, h_le (hN.conj_mem n hn g)⟩

lemma normal_core_mono {H K : subgroup G} (h : H ≤ K) : H.normal_core ≤ K.normal_core :=
normal_le_normal_core.mpr (H.normal_core_le.trans h)

lemma normal_core_eq_supr (H : subgroup G) :
  H.normal_core = ⨆ (N : subgroup G) (_ : normal N) (hs : N ≤ H), N :=
le_antisymm (le_supr_of_le H.normal_core
  (le_supr_of_le H.normal_core_normal (le_supr_of_le H.normal_core_le le_rfl)))
  (supr_le (λ N, supr_le (λ hN, supr_le (by exactI normal_le_normal_core.mpr))))

@[simp] lemma normal_core_eq_self (H : subgroup G) [H.normal] : H.normal_core = H :=
le_antisymm H.normal_core_le (normal_le_normal_core.mpr le_rfl)

@[simp] theorem normal_core_idempotent (H : subgroup G) :
  H.normal_core.normal_core = H.normal_core :=
H.normal_core.normal_core_eq_self

end subgroup

namespace monoid_hom

variables {N : Type*} {P : Type*} [group N] [group P] (K : subgroup G)

open subgroup

/-- The range of a monoid homomorphism from a group is a subgroup. -/
@[to_additive "The range of an `add_monoid_hom` from an `add_group` is an `add_subgroup`."]
def range (f : G →* N) : subgroup N :=
subgroup.copy ((⊤ : subgroup G).map f) (set.range f) (by simp [set.ext_iff])

@[to_additive]
instance decidable_mem_range (f : G →* N) [fintype G] [decidable_eq N] :
  decidable_pred (∈ f.range) :=
λ x, fintype.decidable_exists_fintype

@[simp, to_additive] lemma coe_range (f : G →* N) :
  (f.range : set N) = set.range f := rfl

@[simp, to_additive] lemma mem_range {f : G →* N} {y : N} :
  y ∈ f.range ↔ ∃ x, f x = y :=
iff.rfl

@[to_additive] lemma range_eq_map (f : G →* N) : f.range = (⊤ : subgroup G).map f :=
by ext; simp

/-- The canonical surjective group homomorphism `G →* f(G)` induced by a group
homomorphism `G →* N`. -/
@[to_additive "The canonical surjective `add_group` homomorphism `G →+ f(G)` induced by a group
homomorphism `G →+ N`."]
def range_restrict (f : G →* N) : G →* f.range :=
cod_restrict f _ $ λ x, ⟨x, rfl⟩

@[simp, to_additive]
lemma coe_range_restrict (f : G →* N) (g : G) : (f.range_restrict g : N) = f g := rfl

@[to_additive]
lemma range_restrict_surjective (f : G →* N) : function.surjective f.range_restrict :=
λ ⟨_, g, rfl⟩, ⟨g, rfl⟩

@[to_additive]
lemma map_range (g : N →* P) (f : G →* N) : f.range.map g = (g.comp f).range :=
by rw [range_eq_map, range_eq_map]; exact (⊤ : subgroup G).map_map g f

@[to_additive]
lemma range_top_iff_surjective {N} [group N] {f : G →* N} :
  f.range = (⊤ : subgroup N) ↔ function.surjective f :=
set_like.ext'_iff.trans $ iff.trans (by rw [coe_range, coe_top]) set.range_iff_surjective

/-- The range of a surjective monoid homomorphism is the whole of the codomain. -/
@[to_additive "The range of a surjective `add_monoid` homomorphism is the whole of the codomain."]
lemma range_top_of_surjective {N} [group N] (f : G →* N) (hf : function.surjective f) :
  f.range = (⊤ : subgroup N) :=
range_top_iff_surjective.2 hf

@[simp, to_additive]
lemma range_one : (1 : G →* N).range = ⊥ :=
set_like.ext $ λ x, by simpa using @comm _ (=) _ 1 x

@[simp, to_additive] lemma _root_.subgroup.subtype_range (H : subgroup G) : H.subtype.range = H :=
by { rw [range_eq_map, ← set_like.coe_set_eq, coe_map, subgroup.coe_subtype], ext, simp }

@[simp, to_additive] lemma _root_.subgroup.inclusion_range {H K : subgroup G} (h_le : H ≤ K) :
  (inclusion h_le).range = H.subgroup_of K :=
subgroup.ext (λ g, set.ext_iff.mp (set.range_inclusion h_le) g)

@[to_additive] lemma subgroup_of_range_eq_of_le {G₁ G₂ : Type*} [group G₁] [group G₂]
  {K : subgroup G₂} (f : G₁ →* G₂) (h : f.range ≤ K) :
  f.range.subgroup_of K = (f.cod_restrict K (λ x, h ⟨x, rfl⟩)).range :=
begin
  ext k,
  refine exists_congr _,
  simp [subtype.ext_iff],
end

/-- Computable alternative to `monoid_hom.of_injective`. -/
@[to_additive /-"Computable alternative to `add_monoid_hom.of_injective`."-/]
def of_left_inverse {f : G →* N} {g : N →* G} (h : function.left_inverse g f) : G ≃* f.range :=
{ to_fun := f.range_restrict,
  inv_fun := g ∘ f.range.subtype,
  left_inv := h,
  right_inv := by
  { rintros ⟨x, y, rfl⟩,
    apply subtype.ext,
    rw [coe_range_restrict, function.comp_apply, subgroup.coe_subtype, subtype.coe_mk, h] },
  .. f.range_restrict }

@[simp, to_additive] lemma of_left_inverse_apply {f : G →* N} {g : N →* G}
  (h : function.left_inverse g f) (x : G) :
  ↑(of_left_inverse h x) = f x := rfl

@[simp, to_additive] lemma of_left_inverse_symm_apply {f : G →* N} {g : N →* G}
  (h : function.left_inverse g f) (x : f.range) :
  (of_left_inverse h).symm x = g x := rfl

/-- The range of an injective group homomorphism is isomorphic to its domain. -/
@[to_additive /-"The range of an injective additive group homomorphism is isomorphic to its
domain."-/ ]
noncomputable def of_injective {f : G →* N} (hf : function.injective f) : G ≃* f.range :=
(mul_equiv.of_bijective (f.cod_restrict f.range (λ x, ⟨x, rfl⟩))
  ⟨λ x y h, hf (subtype.ext_iff.mp h), by { rintros ⟨x, y, rfl⟩, exact ⟨y, rfl⟩ }⟩)

@[to_additive]
lemma of_injective_apply {f : G →* N} (hf : function.injective f) {x : G} :
  ↑(of_injective hf x) = f x := rfl

section ker

variables {M : Type*} [mul_one_class M]

/-- The multiplicative kernel of a monoid homomorphism is the subgroup of elements `x : G` such that
`f x = 1` -/
@[to_additive "The additive kernel of an `add_monoid` homomorphism is the `add_subgroup` of elements
such that `f x = 0`"]
def ker (f : G →* M) : subgroup G :=
{ inv_mem' := λ x (hx : f x = 1),
    calc f x⁻¹ = f x * f x⁻¹ : by rw [hx, one_mul]
           ... = f (x * x⁻¹) : by rw [f.map_mul]
           ... = f 1 :         by rw [mul_right_inv]
           ... = 1 :           f.map_one,
  ..f.mker }

@[to_additive]
lemma mem_ker (f : G →* M) {x : G} : x ∈ f.ker ↔ f x = 1 := iff.rfl

@[to_additive]
lemma coe_ker (f : G →* M) : (f.ker : set G) = (f : G → M) ⁻¹' {1} := rfl

@[to_additive]
lemma eq_iff (f : G →* N) {x y : G} : f x = f y ↔ y⁻¹ * x ∈ f.ker :=
by rw [f.mem_ker, f.map_mul, f.map_inv, inv_mul_eq_one, eq_comm]

@[to_additive]
instance decidable_mem_ker [decidable_eq M] (f : G →* M) :
  decidable_pred (∈ f.ker) :=
λ x, decidable_of_iff (f x = 1) f.mem_ker

@[to_additive]
lemma comap_ker (g : N →* P) (f : G →* N) : g.ker.comap f = (g.comp f).ker := rfl

@[simp, to_additive] lemma comap_bot (f : G →* N) :
  (⊥ : subgroup N).comap f = f.ker := rfl

@[to_additive] lemma range_restrict_ker  (f : G →* N) : ker (range_restrict f) = ker f :=
begin
  ext,
  change (⟨f x, _⟩ : range f) = ⟨1, _⟩ ↔ f x = 1,
  simp only [],
end

@[simp, to_additive]
lemma ker_one : (1 : G →* M).ker = ⊤ :=
by { ext, simp [mem_ker] }

@[to_additive] lemma ker_eq_bot_iff (f : G →* N) : f.ker = ⊥ ↔ function.injective f :=
begin
  split,
  { intros h x y hxy,
    rwa [←mul_inv_eq_one, ←map_inv, ←map_mul, ←mem_ker, h, mem_bot, mul_inv_eq_one] at hxy },
  { exact λ h, le_bot_iff.mp (λ x hx, h (hx.trans f.map_one.symm)) },
end

@[simp, to_additive] lemma _root_.subgroup.ker_subtype (H : subgroup G) : H.subtype.ker = ⊥ :=
H.subtype.ker_eq_bot_iff.mpr subtype.coe_injective

@[simp, to_additive] lemma _root_.subgroup.ker_inclusion {H K : subgroup G} (h : H ≤ K) :
  (inclusion h).ker = ⊥ :=
(inclusion h).ker_eq_bot_iff.mpr (set.inclusion_injective h)

@[to_additive]
lemma prod_map_comap_prod {G' : Type*} {N' : Type*} [group G'] [group N']
  (f : G →* N) (g : G' →* N') (S : subgroup N) (S' : subgroup N') :
  (S.prod S').comap (prod_map f g) = (S.comap f).prod (S'.comap g) :=
set_like.coe_injective $ set.preimage_prod_map_prod f g _ _

@[to_additive]
lemma ker_prod_map {G' : Type*} {N' : Type*} [group G'] [group N'] (f : G →* N) (g : G' →* N') :
  (prod_map f g).ker = f.ker.prod g.ker :=
by rw [←comap_bot, ←comap_bot, ←comap_bot, ←prod_map_comap_prod, bot_prod_bot]

end ker

/-- The subgroup of elements `x : G` such that `f x = g x` -/
@[to_additive "The additive subgroup of elements `x : G` such that `f x = g x`"]
def eq_locus (f g : G →* N) : subgroup G :=
{ inv_mem' := λ x (hx : f x = g x), show f x⁻¹ = g x⁻¹, by rw [f.map_inv, g.map_inv, hx],
  .. eq_mlocus f g}

/-- If two monoid homomorphisms are equal on a set, then they are equal on its subgroup closure. -/
@[to_additive]
lemma eq_on_closure {f g : G →* N} {s : set G} (h : set.eq_on f g s) :
  set.eq_on f g (closure s) :=
show closure s ≤ f.eq_locus g, from (closure_le _).2 h

@[to_additive]
lemma eq_of_eq_on_top {f g : G →* N} (h : set.eq_on f g (⊤ : subgroup G)) :
  f = g :=
ext $ λ x, h trivial

@[to_additive]
lemma eq_of_eq_on_dense {s : set G} (hs : closure s = ⊤) {f g : G →* N} (h : s.eq_on f g) :
  f = g :=
eq_of_eq_on_top $ hs ▸ eq_on_closure h

@[to_additive]
lemma gclosure_preimage_le (f : G →* N) (s : set N) :
  closure (f ⁻¹' s) ≤ (closure s).comap f :=
(closure_le _).2 $ λ x hx, by rw [set_like.mem_coe, mem_comap]; exact subset_closure hx

/-- The image under a monoid homomorphism of the subgroup generated by a set equals the subgroup
generated by the image of the set. -/
@[to_additive "The image under an `add_monoid` hom of the `add_subgroup` generated by a set equals
the `add_subgroup` generated by the image of the set."]
lemma map_closure (f : G →* N) (s : set G) :
  (closure s).map f = closure (f '' s) :=
set.image_preimage.l_comm_of_u_comm
  (subgroup.gc_map_comap f) (subgroup.gi N).gc (subgroup.gi G).gc (λ t, rfl)

-- this instance can't go just after the definition of `mrange` because `fintype` is
-- not imported at that stage

/-- The range of a finite monoid under a monoid homomorphism is finite.
Note: this instance can form a diamond with `subtype.fintype` in the
presence of `fintype N`. -/
@[to_additive "The range of a finite additive monoid under an additive monoid homomorphism is
finite.

Note: this instance can form a diamond with `subtype.fintype` or `subgroup.fintype` in the
presence of `fintype N`."]
instance fintype_mrange {M N : Type*} [monoid M] [monoid N] [fintype M] [decidable_eq N]
  (f : M →* N) : fintype (mrange f) :=
set.fintype_range f

/-- The range of a finite group under a group homomorphism is finite.

Note: this instance can form a diamond with `subtype.fintype` or `subgroup.fintype` in the
presence of `fintype N`. -/
@[to_additive "The range of a finite additive group under an additive group homomorphism is finite.

Note: this instance can form a diamond with `subtype.fintype` or `subgroup.fintype` in the
presence of `fintype N`."]
instance fintype_range  [fintype G] [decidable_eq N] (f : G →* N) : fintype (range f) :=
set.fintype_range f

end monoid_hom

namespace subgroup

variables {N : Type*} [group N] (H : subgroup G)

@[to_additive] lemma map_eq_bot_iff {f : G →* N} : H.map f = ⊥ ↔ H ≤ f.ker :=
begin
  rw eq_bot_iff,
  split,
  { exact λ h x hx, h ⟨x, hx, rfl⟩ },
  { intros h x hx,
    obtain ⟨y, hy, rfl⟩ := hx,
    exact h hy },
end

@[to_additive]
lemma map_eq_bot_iff_of_injective {f : G →* N} (hf : function.injective f) : H.map f = ⊥ ↔ H = ⊥ :=
by rw [map_eq_bot_iff, f.ker_eq_bot_iff.mpr hf, le_bot_iff]

end subgroup

namespace subgroup

open monoid_hom

variables {N : Type*} [group N] (f : G →* N)

@[to_additive]
lemma map_le_range (H : subgroup G) : map f H ≤ f.range :=
(range_eq_map f).symm ▸ map_mono le_top

@[to_additive]
lemma map_subtype_le {H : subgroup G} (K : subgroup H) : K.map H.subtype ≤ H :=
(K.map_le_range H.subtype).trans (le_of_eq H.subtype_range)

@[to_additive]
lemma ker_le_comap (H : subgroup N) : f.ker ≤ comap f H :=
(comap_bot f) ▸ comap_mono bot_le

@[to_additive]
lemma map_comap_le (H : subgroup N) : map f (comap f H) ≤ H :=
(gc_map_comap f).l_u_le _

@[to_additive]
lemma le_comap_map (H : subgroup G) : H ≤ comap f (map f H) :=
(gc_map_comap f).le_u_l _

@[to_additive]
lemma map_comap_eq (H : subgroup N) :
  map f (comap f H) = f.range ⊓ H :=
set_like.ext' begin
  convert set.image_preimage_eq_inter_range,
  simp [set.inter_comm],
end

@[to_additive]
lemma comap_map_eq (H : subgroup G) : comap f (map f H) = H ⊔ f.ker :=
begin
  refine le_antisymm _ (sup_le (le_comap_map _ _) (ker_le_comap _ _)),
  intros x hx, simp only [exists_prop, mem_map, mem_comap] at hx,
  rcases hx with ⟨y, hy, hy'⟩,
  rw ← mul_inv_cancel_left y x,
  exact mul_mem_sup hy (by simp [mem_ker, hy']),
end

@[to_additive]
lemma map_comap_eq_self {f : G →* N} {H : subgroup N} (h : H ≤ f.range) :
  map f (comap f H) = H :=
by rwa [map_comap_eq, inf_eq_right]

@[to_additive]
lemma map_comap_eq_self_of_surjective {f : G →* N} (h : function.surjective f) (H : subgroup N) :
  map f (comap f H) = H :=
map_comap_eq_self ((range_top_of_surjective _ h).symm ▸ le_top)

@[to_additive]
lemma comap_le_comap_of_le_range {f : G →* N} {K L : subgroup N} (hf : K ≤ f.range) :
  K.comap f ≤ L.comap f ↔ K ≤ L :=
⟨(map_comap_eq_self hf).ge.trans ∘ map_le_iff_le_comap.mpr, comap_mono⟩

@[to_additive]
lemma comap_le_comap_of_surjective {f : G →* N} {K L : subgroup N} (hf : function.surjective f) :
  K.comap f ≤ L.comap f ↔ K ≤ L :=
comap_le_comap_of_le_range (le_top.trans (f.range_top_of_surjective hf).ge)

@[to_additive]
lemma comap_lt_comap_of_surjective {f : G →* N} {K L : subgroup N} (hf : function.surjective f) :
  K.comap f < L.comap f ↔ K < L :=
by simp_rw [lt_iff_le_not_le, comap_le_comap_of_surjective hf]

@[to_additive]
lemma comap_injective {f : G →* N} (h : function.surjective f) : function.injective (comap f) :=
λ K L, by simp only [le_antisymm_iff, comap_le_comap_of_surjective h, imp_self]

@[to_additive]
lemma comap_map_eq_self {f : G →* N} {H : subgroup G} (h : f.ker ≤ H) :
  comap f (map f H) = H :=
by rwa [comap_map_eq, sup_eq_left]

@[to_additive]
lemma comap_map_eq_self_of_injective {f : G →* N} (h : function.injective f) (H : subgroup G) :
  comap f (map f H) = H :=
comap_map_eq_self (((ker_eq_bot_iff _).mpr h).symm ▸ bot_le)

@[to_additive]
lemma map_le_map_iff_of_injective {f : G →* N} (hf : function.injective f) {H K : subgroup G} :
  H.map f ≤ K.map f ↔ H ≤ K :=
⟨(congr_arg2 (≤) (H.comap_map_eq_self_of_injective hf)
  (K.comap_map_eq_self_of_injective hf)).mp ∘ comap_mono, map_mono⟩

@[simp, to_additive]
lemma map_subtype_le_map_subtype {G' : subgroup G} {H K : subgroup G'} :
  H.map G'.subtype ≤ K.map G'.subtype ↔ H ≤ K :=
map_le_map_iff_of_injective subtype.coe_injective

@[to_additive]
lemma map_injective {f : G →* N} (h : function.injective f) : function.injective (map f) :=
λ K L hKL, by { apply_fun comap f at hKL, simpa [comap_map_eq_self_of_injective h] using hKL }

@[to_additive]
lemma map_eq_comap_of_inverse {f : G →* N} {g : N →* G} (hl : function.left_inverse g f)
  (hr : function.right_inverse g f) (H : subgroup G) : map f H = comap g H :=
set_like.ext' $ by rw [coe_map, coe_comap, set.image_eq_preimage_of_inverse hl hr]

/-- Given `f(A) = f(B)`, `ker f ≤ A`, and `ker f ≤ B`, deduce that `A = B`  -/
@[to_additive] lemma map_injective_of_ker_le
  {H K : subgroup G} (hH : f.ker ≤ H) (hK : f.ker ≤ K) (hf : map f H = map f K) :
  H = K :=
begin
  apply_fun comap f at hf,
  rwa [comap_map_eq, comap_map_eq, sup_of_le_left hH, sup_of_le_left hK] at hf,
end

@[to_additive] lemma comap_sup_eq_of_le_range
  {H K : subgroup N} (hH : H ≤ f.range) (hK : K ≤ f.range) :
  comap f H ⊔ comap f K = comap f (H ⊔ K) :=
map_injective_of_ker_le f ((ker_le_comap f H).trans le_sup_left) (ker_le_comap f (H ⊔ K))
  (by rw [map_comap_eq, map_sup, map_comap_eq, map_comap_eq, inf_eq_right.mpr hH,
    inf_eq_right.mpr hK, inf_eq_right.mpr (sup_le hH hK)])

@[to_additive] lemma comap_sup_eq (H K : subgroup N) (hf : function.surjective f) :
  comap f H ⊔ comap f K = comap f (H ⊔ K) :=
comap_sup_eq_of_le_range f (le_top.trans (ge_of_eq (f.range_top_of_surjective hf)))
  (le_top.trans (ge_of_eq (f.range_top_of_surjective hf)))

@[to_additive] lemma sup_subgroup_of_eq {H K L : subgroup G} (hH : H ≤ L) (hK : K ≤ L) :
  H.subgroup_of L ⊔ K.subgroup_of L = (H ⊔ K).subgroup_of L :=
comap_sup_eq_of_le_range L.subtype (hH.trans (ge_of_eq L.subtype_range))
  (hK.trans (ge_of_eq L.subtype_range))

/-- A subgroup is isomorphic to its image under an injective function -/
@[to_additive  "An additive subgroup is isomorphic to its image under an injective function"]
noncomputable def equiv_map_of_injective (H : subgroup G)
  (f : G →* N) (hf : function.injective f) : H ≃* H.map f :=
{ map_mul' := λ _ _, subtype.ext (f.map_mul _ _), ..equiv.set.image f H hf }

@[simp, to_additive] lemma coe_equiv_map_of_injective_apply (H : subgroup G)
  (f : G →* N) (hf : function.injective f) (h : H) :
  (equiv_map_of_injective H f hf h : N) = f h := rfl

/-- The preimage of the normalizer is equal to the normalizer of the preimage of a surjective
  function. -/
@[to_additive "The preimage of the normalizer is equal to the normalizer of the preimage of
a surjective function."]
lemma comap_normalizer_eq_of_surjective (H : subgroup G)
  {f : N →* G} (hf : function.surjective f) :
  H.normalizer.comap f = (H.comap f).normalizer :=
le_antisymm (le_normalizer_comap f)
  begin
    assume x hx,
    simp only [mem_comap, mem_normalizer_iff] at *,
    assume n,
    rcases hf n with ⟨y, rfl⟩,
    simp [hx y]
  end

@[to_additive]
lemma comap_normalizer_eq_of_injective_of_le_range {N : Type*} [group N] (H : subgroup G)
  {f : N →* G} (hf : function.injective f) (h : H.normalizer ≤ f.range) :
  comap f H.normalizer = (comap f H).normalizer :=
begin
  apply (subgroup.map_injective hf),
  rw map_comap_eq_self h,
  apply le_antisymm,
  { refine (le_trans (le_of_eq _) (map_mono (le_normalizer_comap _))),
    rewrite map_comap_eq_self h, },
  { refine (le_trans (le_normalizer_map f) (le_of_eq _)),
    rewrite map_comap_eq_self (le_trans le_normalizer h), }
end

@[to_additive]
lemma comap_subtype_normalizer_eq {H N : subgroup G} (h : H.normalizer ≤ N) :
  comap N.subtype H.normalizer = (comap N.subtype H).normalizer :=
begin
  apply comap_normalizer_eq_of_injective_of_le_range,
  exact subtype.coe_injective,
  simpa,
end

/-- The image of the normalizer is equal to the normalizer of the image of an isomorphism. -/
@[to_additive "The image of the normalizer is equal to the normalizer of the image of an
isomorphism."]
lemma map_equiv_normalizer_eq (H : subgroup G)
  (f : G ≃* N) : H.normalizer.map f.to_monoid_hom = (H.map f.to_monoid_hom).normalizer :=
begin
  ext x,
  simp only [mem_normalizer_iff, mem_map_equiv],
  rw [f.to_equiv.forall_congr],
  simp
end

/-- The image of the normalizer is equal to the normalizer of the image of a bijective
  function. -/
@[to_additive "The image of the normalizer is equal to the normalizer of the image of a bijective
  function."]
lemma map_normalizer_eq_of_bijective (H : subgroup G)
  {f : G →* N} (hf : function.bijective f) :
  H.normalizer.map f = (H.map f).normalizer :=
map_equiv_normalizer_eq H (mul_equiv.of_bijective f hf)

end subgroup

namespace monoid_hom

variables {G₁ G₂ G₃ : Type*} [group G₁] [group G₂] [group G₃]
variables (f : G₁ →* G₂) (f_inv : G₂ → G₁)

/-- Auxiliary definition used to define `lift_of_right_inverse` -/
@[to_additive "Auxiliary definition used to define `lift_of_right_inverse`"]
def lift_of_right_inverse_aux
  (hf : function.right_inverse f_inv f) (g : G₁ →* G₃) (hg : f.ker ≤ g.ker) :
  G₂ →* G₃ :=
{ to_fun := λ b, g (f_inv b),
  map_one' := hg (hf 1),
  map_mul' :=
  begin
    intros x y,
    rw [← g.map_mul, ← mul_inv_eq_one, ← g.map_inv, ← g.map_mul, ← g.mem_ker],
    apply hg,
    rw [f.mem_ker, f.map_mul, f.map_inv, mul_inv_eq_one, f.map_mul],
    simp only [hf _],
  end }

@[simp, to_additive]
lemma lift_of_right_inverse_aux_comp_apply
  (hf : function.right_inverse f_inv f) (g : G₁ →* G₃) (hg : f.ker ≤ g.ker) (x : G₁) :
  (f.lift_of_right_inverse_aux f_inv hf g hg) (f x) = g x :=
begin
  dsimp [lift_of_right_inverse_aux],
  rw [← mul_inv_eq_one, ← g.map_inv, ← g.map_mul, ← g.mem_ker],
  apply hg,
  rw [f.mem_ker, f.map_mul, f.map_inv, mul_inv_eq_one],
  simp only [hf _],
end

/-- `lift_of_right_inverse f hf g hg` is the unique group homomorphism `φ`

* such that `φ.comp f = g` (`monoid_hom.lift_of_right_inverse_comp`),
* where `f : G₁ →+* G₂` has a right_inverse `f_inv` (`hf`),
* and `g : G₂ →+* G₃` satisfies `hg : f.ker ≤ g.ker`.

See `monoid_hom.eq_lift_of_right_inverse` for the uniqueness lemma.

```
   G₁.
   |  \
 f |   \ g
   |    \
   v     \⌟
   G₂----> G₃
      ∃!φ
```
 -/
@[to_additive "`lift_of_right_inverse f f_inv hf g hg` is the unique additive group homomorphism `φ`

* such that `φ.comp f = g` (`add_monoid_hom.lift_of_right_inverse_comp`),
* where `f : G₁ →+ G₂` has a right_inverse `f_inv` (`hf`),
* and `g : G₂ →+ G₃` satisfies `hg : f.ker ≤ g.ker`.

See `add_monoid_hom.eq_lift_of_right_inverse` for the uniqueness lemma.

```
   G₁.
   |  \\
 f |   \\ g
   |    \\
   v     \\⌟
   G₂----> G₃
      ∃!φ
```"]
def lift_of_right_inverse
  (hf : function.right_inverse f_inv f) : {g : G₁ →* G₃ // f.ker ≤ g.ker} ≃ (G₂ →* G₃) :=
{ to_fun := λ g, f.lift_of_right_inverse_aux f_inv hf g.1 g.2,
  inv_fun := λ φ, ⟨φ.comp f, λ x hx, (mem_ker _).mpr $ by simp [(mem_ker _).mp hx]⟩,
  left_inv := λ g, by
  { ext,
    simp only [comp_apply, lift_of_right_inverse_aux_comp_apply, subtype.coe_mk,
      subtype.val_eq_coe], },
  right_inv := λ φ, by
  { ext b,
    simp [lift_of_right_inverse_aux, hf b], } }

/-- A non-computable version of `monoid_hom.lift_of_right_inverse` for when no computable right
inverse is available, that uses `function.surj_inv`. -/
@[simp, to_additive "A non-computable version of `add_monoid_hom.lift_of_right_inverse` for when no
computable right inverse is available."]
noncomputable abbreviation lift_of_surjective
  (hf : function.surjective f) : {g : G₁ →* G₃ // f.ker ≤ g.ker} ≃ (G₂ →* G₃) :=
f.lift_of_right_inverse (function.surj_inv hf) (function.right_inverse_surj_inv hf)

@[simp, to_additive]
lemma lift_of_right_inverse_comp_apply
  (hf : function.right_inverse f_inv f) (g : {g : G₁ →* G₃ // f.ker ≤ g.ker}) (x : G₁) :
  (f.lift_of_right_inverse f_inv hf g) (f x) = g x :=
f.lift_of_right_inverse_aux_comp_apply f_inv hf g.1 g.2 x

@[simp, to_additive]
lemma lift_of_right_inverse_comp (hf : function.right_inverse f_inv f)
  (g : {g : G₁ →* G₃ // f.ker ≤ g.ker}) :
  (f.lift_of_right_inverse f_inv hf g).comp f = g :=
monoid_hom.ext $ f.lift_of_right_inverse_comp_apply f_inv hf g

@[to_additive]
lemma eq_lift_of_right_inverse (hf : function.right_inverse f_inv f) (g : G₁ →* G₃)
  (hg : f.ker ≤ g.ker) (h : G₂ →* G₃) (hh : h.comp f = g) :
  h = (f.lift_of_right_inverse f_inv hf ⟨g, hg⟩) :=
begin
  simp_rw ←hh,
  exact ((f.lift_of_right_inverse f_inv hf).apply_symm_apply _).symm,
end

end monoid_hom

variables {N : Type*} [group N]

-- Here `H.normal` is an explicit argument so we can use dot notation with `comap`.
@[to_additive]
lemma subgroup.normal.comap {H : subgroup N} (hH : H.normal) (f : G →* N) :
  (H.comap f).normal :=
⟨λ _, by simp [subgroup.mem_comap, hH.conj_mem] {contextual := tt}⟩

@[priority 100, to_additive]
instance subgroup.normal_comap {H : subgroup N}
  [nH : H.normal] (f : G →* N) :  (H.comap f).normal := nH.comap _

@[priority 100, to_additive]
instance monoid_hom.normal_ker (f : G →* N) : f.ker.normal :=
by { rw [←f.comap_bot], apply_instance }

@[priority 100, to_additive]
instance subgroup.normal_inf (H N : subgroup G) [hN : N.normal] :
  ((H ⊓ N).comap H.subtype).normal :=
⟨λ x hx g, begin
  simp only [subgroup.mem_inf, coe_subtype, subgroup.mem_comap] at hx,
  simp only [subgroup.coe_mul, subgroup.mem_inf, coe_subtype, subgroup.coe_inv, subgroup.mem_comap],
  exact ⟨H.mul_mem (H.mul_mem g.2 hx.1) (H.inv_mem g.2), hN.1 x hx.2 g⟩,
end⟩

namespace subgroup

/-- The subgroup generated by an element. -/
def zpowers (g : G) : subgroup G :=
subgroup.copy (zpowers_hom G g).range (set.range ((^) g : ℤ → G)) rfl

@[simp] lemma mem_zpowers (g : G) : g ∈ zpowers g := ⟨1, zpow_one _⟩

lemma zpowers_eq_closure (g : G) : zpowers g = closure {g} :=
by { ext, exact mem_closure_singleton.symm }

@[simp] lemma range_zpowers_hom (g : G) : (zpowers_hom G g).range = zpowers g := rfl

lemma zpowers_subset {a : G} {K : subgroup G} (h : a ∈ K) : zpowers a ≤ K :=
λ x hx, match x, hx with _, ⟨i, rfl⟩ := K.zpow_mem h i end

lemma mem_zpowers_iff {g h : G} :
  h ∈ zpowers g ↔ ∃ (k : ℤ), g ^ k = h :=
iff.rfl

@[simp] lemma forall_zpowers {x : G} {p : zpowers x → Prop} :
  (∀ g, p g) ↔ ∀ m : ℤ, p ⟨x ^ m, m, rfl⟩ :=
set.forall_subtype_range_iff

@[simp] lemma exists_zpowers {x : G} {p : zpowers x → Prop} :
  (∃ g, p g) ↔ ∃ m : ℤ, p ⟨x ^ m, m, rfl⟩ :=
set.exists_subtype_range_iff

lemma forall_mem_zpowers {x : G} {p : G → Prop} :
  (∀ g ∈ zpowers x, p g) ↔ ∀ m : ℤ, p (x ^ m) :=
set.forall_range_iff

lemma exists_mem_zpowers {x : G} {p : G → Prop} :
  (∃ g ∈ zpowers x, p g) ↔ ∃ m : ℤ, p (x ^ m) :=
set.exists_range_iff

end subgroup

namespace add_subgroup

/-- The subgroup generated by an element. -/
def zmultiples (a : A) : add_subgroup A :=
add_subgroup.copy (zmultiples_hom A a).range (set.range ((• a) : ℤ → A)) rfl

@[simp] lemma range_zmultiples_hom (a : A) : (zmultiples_hom A a).range = zmultiples a := rfl

attribute [to_additive add_subgroup.zmultiples] subgroup.zpowers
attribute [to_additive add_subgroup.mem_zmultiples] subgroup.mem_zpowers
attribute [to_additive add_subgroup.zmultiples_eq_closure] subgroup.zpowers_eq_closure
attribute [to_additive add_subgroup.range_zmultiples_hom] subgroup.range_zpowers_hom
attribute [to_additive add_subgroup.zmultiples_subset] subgroup.zpowers_subset
attribute [to_additive add_subgroup.mem_zmultiples_iff] subgroup.mem_zpowers_iff
attribute [to_additive add_subgroup.forall_zmultiples] subgroup.forall_zpowers
attribute [to_additive add_subgroup.forall_mem_zmultiples] subgroup.forall_mem_zpowers
attribute [to_additive add_subgroup.exists_zmultiples] subgroup.exists_zpowers
attribute [to_additive add_subgroup.exists_mem_zmultiples] subgroup.exists_mem_zpowers

end add_subgroup

lemma int.mem_zmultiples_iff {a b : ℤ} :
  b ∈ add_subgroup.zmultiples a ↔ a ∣ b :=
exists_congr (λ k, by rw [mul_comm, eq_comm, ← smul_eq_mul])

lemma of_mul_image_zpowers_eq_zmultiples_of_mul { x : G } :
  additive.of_mul '' ((subgroup.zpowers x) : set G) = add_subgroup.zmultiples (additive.of_mul x) :=
begin
  ext y,
  split,
  { rintro ⟨z, ⟨m, hm⟩, hz2⟩,
    use m,
    simp only,
    rwa [← of_mul_zpow, hm] },
  { rintros ⟨n, hn⟩,
    refine ⟨x ^ n, ⟨n, rfl⟩, _⟩,
    rwa of_mul_zpow }
end

lemma of_add_image_zmultiples_eq_zpowers_of_add {x : A} :
  multiplicative.of_add '' ((add_subgroup.zmultiples x) : set A) =
  subgroup.zpowers (multiplicative.of_add x) :=
begin
  symmetry,
  rw equiv.eq_image_iff_symm_image_eq,
  exact of_mul_image_zpowers_eq_zmultiples_of_mul,
end

namespace subgroup

@[to_additive zmultiples_is_commutative]
instance zpowers_is_commutative (g : G) : (zpowers g).is_commutative :=
⟨⟨λ ⟨_, _, h₁⟩ ⟨_, _, h₂⟩, by rw [subtype.ext_iff, coe_mul, coe_mul,
  subtype.coe_mk, subtype.coe_mk, ←h₁, ←h₂, zpow_mul_comm]⟩⟩

@[simp, to_additive zmultiples_le]
lemma zpowers_le {g : G} {H : subgroup G} : zpowers g ≤ H ↔ g ∈ H :=
by rw [zpowers_eq_closure, closure_le, set.singleton_subset_iff, set_like.mem_coe]

@[simp, to_additive zmultiples_eq_bot] lemma zpowers_eq_bot {g : G} : zpowers g = ⊥ ↔ g = 1 :=
by rw [eq_bot_iff, zpowers_le, mem_bot]

end subgroup

namespace monoid_hom

variables {G' : Type*} [group G']

/-- The `monoid_hom` from the preimage of a subgroup to itself. -/
@[to_additive "the `add_monoid_hom` from the preimage of an additive subgroup to itself.", simps]
def subgroup_comap (f : G →* G') (H' : subgroup G') : H'.comap f →* H' :=
f.submonoid_comap H'.to_submonoid

/-- The `monoid_hom` from a subgroup to its image. -/
@[to_additive "the `add_monoid_hom` from an additive subgroup to its image", simps]
def subgroup_map (f : G →* G') (H : subgroup G) : H →* H.map f :=
f.submonoid_map H.to_submonoid

@[to_additive]
lemma subgroup_map_surjective (f : G →* G') (H : subgroup G) :
  function.surjective (f.subgroup_map H) :=
f.submonoid_map_surjective H.to_submonoid

end monoid_hom

namespace mul_equiv

variables {H K : subgroup G}

/-- Makes the identity isomorphism from a proof two subgroups of a multiplicative
    group are equal. -/
@[to_additive "Makes the identity additive isomorphism from a proof
two subgroups of an additive group are equal."]
def subgroup_congr (h : H = K) : H ≃* K :=
{ map_mul' :=  λ _ _, rfl, ..equiv.set_congr $ congr_arg _ h }

/-- A `mul_equiv` `φ` between two groups `G` and `G'` induces a `mul_equiv` between
a subgroup `H ≤ G` and the subgroup `φ(H) ≤ G'`. -/
@[to_additive "An `add_equiv` `φ` between two additive groups `G` and `G'` induces an `add_equiv`
between a subgroup `H ≤ G` and the subgroup `φ(H) ≤ G'`. "]
def subgroup_map {G'} [group G'] (e : G ≃* G') (H : subgroup G) :
  H ≃* H.map e.to_monoid_hom :=
e.submonoid_map H.to_submonoid

end mul_equiv

-- TODO : ↥(⊤ : subgroup H) ≃* H ?

namespace subgroup

variables {C : Type*} [comm_group C] {s t : subgroup C} {x : C}

@[to_additive]
lemma mem_sup : x ∈ s ⊔ t ↔ ∃ (y ∈ s) (z ∈ t), y * z = x :=
⟨λ h, begin
  rw [← closure_eq s, ← closure_eq t, ← closure_union] at h,
  apply closure_induction h,
  { rintro y (h | h),
    { exact ⟨y, h, 1, t.one_mem, by simp⟩ },
    { exact ⟨1, s.one_mem, y, h, by simp⟩ } },
  { exact ⟨1, s.one_mem, 1, ⟨t.one_mem, mul_one 1⟩⟩ },
  { rintro _ _ ⟨y₁, hy₁, z₁, hz₁, rfl⟩ ⟨y₂, hy₂, z₂, hz₂, rfl⟩,
    exact ⟨_, mul_mem hy₁ hy₂, _, mul_mem hz₁ hz₂, by simp [mul_assoc]; cc⟩ },
  { rintro _ ⟨y, hy, z, hz, rfl⟩,
    exact ⟨_, inv_mem hy, _, inv_mem hz, mul_comm z y ▸ (mul_inv_rev z y).symm⟩ }
end, by rintro ⟨y, hy, z, hz, rfl⟩; exact mul_mem_sup hy hz⟩

@[to_additive]
lemma mem_sup' : x ∈ s ⊔ t ↔ ∃ (y : s) (z : t), (y:C) * z = x :=
mem_sup.trans $ by simp only [set_like.exists, coe_mk]

@[to_additive]
lemma mem_closure_pair {x y z : C} : z ∈ closure ({x, y} : set C) ↔ ∃ m n : ℤ, x ^ m * y ^ n = z :=
begin
  rw [←set.singleton_union, subgroup.closure_union, mem_sup],
  simp_rw [exists_prop, mem_closure_singleton, exists_exists_eq_and],
end

@[to_additive]
instance : is_modular_lattice (subgroup C) :=
⟨λ x y z xz a ha, begin
  rw [mem_inf, mem_sup] at ha,
  rcases ha with ⟨⟨b, hb, c, hc, rfl⟩, haz⟩,
  rw mem_sup,
  refine ⟨b, hb, c, mem_inf.2 ⟨hc, _⟩, rfl⟩,
  rw ← inv_mul_cancel_left b c,
  apply z.mul_mem (z.inv_mem (xz hb)) haz,
end⟩

end subgroup

section
variables (G) (A)

/-- A `group` is simple when it has exactly two normal `subgroup`s. -/
class is_simple_group extends nontrivial G : Prop :=
(eq_bot_or_eq_top_of_normal : ∀ H : subgroup G, H.normal → H = ⊥ ∨ H = ⊤)

/-- An `add_group` is simple when it has exactly two normal `add_subgroup`s. -/
class is_simple_add_group extends nontrivial A : Prop :=
(eq_bot_or_eq_top_of_normal : ∀ H : add_subgroup A, H.normal → H = ⊥ ∨ H = ⊤)

attribute [to_additive] is_simple_group

variables {G} {A}

@[to_additive]
lemma subgroup.normal.eq_bot_or_eq_top [is_simple_group G] {H : subgroup G} (Hn : H.normal) :
  H = ⊥ ∨ H = ⊤ :=
is_simple_group.eq_bot_or_eq_top_of_normal H Hn

namespace is_simple_group

@[to_additive]
instance {C : Type*} [comm_group C] [is_simple_group C] :
  is_simple_order (subgroup C) :=
⟨λ H, H.normal_of_comm.eq_bot_or_eq_top⟩

open _root_.subgroup

@[to_additive]
lemma is_simple_group_of_surjective {H : Type*} [group H] [is_simple_group G]
  [nontrivial H] (f : G →* H) (hf : function.surjective f) :
  is_simple_group H :=
⟨nontrivial.exists_pair_ne, λ H iH, begin
  refine ((iH.comap f).eq_bot_or_eq_top).imp (λ h, _) (λ h, _),
  { rw [←map_bot f, ←h, map_comap_eq_self_of_surjective hf] },
  { rw [←comap_top f] at h, exact comap_injective hf h }
end⟩

end is_simple_group

end

namespace subgroup

section pointwise

@[to_additive]
lemma closure_mul_le (S T : set G) : closure (S * T) ≤ closure S ⊔ closure T :=
Inf_le $ λ x ⟨s, t, hs, ht, hx⟩, hx ▸ (closure S ⊔ closure T).mul_mem
    (set_like.le_def.mp le_sup_left $ subset_closure hs)
    (set_like.le_def.mp le_sup_right $ subset_closure ht)

@[to_additive]
lemma sup_eq_closure (H K : subgroup G) : H ⊔ K = closure (H * K) :=
le_antisymm
  (sup_le
    (λ h hh, subset_closure ⟨h, 1, hh, K.one_mem, mul_one h⟩)
    (λ k hk, subset_closure ⟨1, k, H.one_mem, hk, one_mul k⟩))
  (by conv_rhs { rw [← closure_eq H, ← closure_eq K] }; apply closure_mul_le)

@[to_additive]
private def mul_normal_aux (H N : subgroup G) [hN : N.normal] : subgroup G :=
{ carrier := (H : set G) * N,
  one_mem' := ⟨1, 1, H.one_mem, N.one_mem, by rw mul_one⟩,
  mul_mem' := λ a b ⟨h, n, hh, hn, ha⟩ ⟨h', n', hh', hn', hb⟩,
    ⟨h * h', h'⁻¹ * n * h' * n',
    H.mul_mem hh hh', N.mul_mem (by simpa using hN.conj_mem _ hn h'⁻¹) hn',
    by simp [← ha, ← hb, mul_assoc]⟩,
  inv_mem' := λ x ⟨h, n, hh, hn, hx⟩,
    ⟨h⁻¹, h * n⁻¹ * h⁻¹, H.inv_mem hh, hN.conj_mem _ (N.inv_mem hn) h,
    by rw [mul_assoc h, inv_mul_cancel_left, ← hx, mul_inv_rev]⟩ }

/-- The carrier of `H ⊔ N` is just `↑H * ↑N` (pointwise set product) when `N` is normal. -/
@[to_additive "The carrier of `H ⊔ N` is just `↑H + ↑N` (pointwise set addition)
when `N` is normal."]
lemma mul_normal (H N : subgroup G) [N.normal] : (↑(H ⊔ N) : set G) = H * N :=
set.subset.antisymm
  (show H ⊔ N ≤ mul_normal_aux H N,
    by { rw sup_eq_closure, apply Inf_le _, dsimp, refl })
  ((sup_eq_closure H N).symm ▸ subset_closure)

@[to_additive]
private def normal_mul_aux (N H : subgroup G) [hN : N.normal] : subgroup G :=
{ carrier := (N : set G) * H,
  one_mem' := ⟨1, 1, N.one_mem, H.one_mem, by rw mul_one⟩,
  mul_mem' := λ a b ⟨n, h, hn, hh, ha⟩ ⟨n', h', hn', hh', hb⟩,
    ⟨n * (h * n' * h⁻¹), h * h',
    N.mul_mem hn (hN.conj_mem _ hn' _), H.mul_mem hh hh',
    by simp [← ha, ← hb, mul_assoc]⟩,
  inv_mem' := λ x ⟨n, h, hn, hh, hx⟩,
    ⟨h⁻¹ * n⁻¹ * h, h⁻¹,
    by simpa using hN.conj_mem _ (N.inv_mem hn) h⁻¹, H.inv_mem hh,
    by rw [mul_inv_cancel_right, ← mul_inv_rev, hx]⟩ }

/-- The carrier of `N ⊔ H` is just `↑N * ↑H` (pointwise set product) when `N` is normal. -/
@[to_additive "The carrier of `N ⊔ H` is just `↑N + ↑H` (pointwise set addition)
when `N` is normal."]
lemma normal_mul (N H : subgroup G) [N.normal] : (↑(N ⊔ H) : set G) = N * H :=
set.subset.antisymm
  (show N ⊔ H ≤ normal_mul_aux N H,
    by { rw sup_eq_closure, apply Inf_le _, dsimp, refl })
  ((sup_eq_closure N H).symm ▸ subset_closure)

@[to_additive] lemma mul_inf_assoc (A B C : subgroup G) (h : A ≤ C) :
  (A : set G) * ↑(B ⊓ C) = (A * B) ⊓ C :=
begin
  ext,
  simp only [coe_inf, set.inf_eq_inter, set.mem_mul, set.mem_inter_iff],
  split,
  { rintros ⟨y, z, hy, ⟨hzB, hzC⟩, rfl⟩,
    refine ⟨_, mul_mem (h hy) hzC⟩,
    exact ⟨y, z, hy, hzB, rfl⟩ },
  rintros ⟨⟨y, z, hy, hz, rfl⟩, hyz⟩,
  refine ⟨y, z, hy, ⟨hz, _⟩, rfl⟩,
  suffices : y⁻¹ * (y * z) ∈ C, { simpa },
  exact mul_mem (inv_mem (h hy)) hyz
end

@[to_additive] lemma inf_mul_assoc (A B C : subgroup G) (h : C ≤ A) :
  ((A ⊓ B : subgroup G) : set G) * C = A ⊓ (B * C) :=
begin
  ext,
  simp only [coe_inf, set.inf_eq_inter, set.mem_mul, set.mem_inter_iff],
  split,
  { rintros ⟨y, z, ⟨hyA, hyB⟩, hz, rfl⟩,
    refine ⟨A.mul_mem hyA (h hz), _⟩,
    exact ⟨y, z, hyB, hz, rfl⟩ },
  rintros ⟨hyz, y, z, hy, hz, rfl⟩,
  refine ⟨y, z, ⟨_, hy⟩, hz, rfl⟩,
  suffices : (y * z) * z⁻¹ ∈ A, { simpa },
  exact mul_mem hyz (inv_mem (h hz))
end

end pointwise

section subgroup_normal

@[to_additive] lemma normal_subgroup_of_iff {H K : subgroup G} (hHK : H ≤ K) :
  (H.subgroup_of K).normal ↔ ∀ h k, h ∈ H → k ∈ K → k * h * k⁻¹ ∈ H :=
⟨λ hN h k hH hK, hN.conj_mem ⟨h, hHK hH⟩ hH ⟨k, hK⟩,
  λ hN, { conj_mem := λ h hm k, (hN h.1 k.1 hm k.2) }⟩

@[to_additive] instance prod_subgroup_of_prod_normal
  {H₁ K₁ : subgroup G} {H₂ K₂ : subgroup N}
  [h₁ : (H₁.subgroup_of K₁).normal] [h₂ : (H₂.subgroup_of K₂).normal] :
  ((H₁.prod H₂).subgroup_of (K₁.prod K₂)).normal :=
{ conj_mem := λ n hgHK g,
    ⟨h₁.conj_mem ⟨(n : G × N).fst, (mem_prod.mp n.2).1⟩
      hgHK.1 ⟨(g : G × N).fst, (mem_prod.mp g.2).1⟩,
    h₂.conj_mem ⟨(n : G × N).snd, (mem_prod.mp n.2).2⟩
      hgHK.2 ⟨(g : G × N).snd, (mem_prod.mp g.2).2⟩⟩ }

@[to_additive] instance prod_normal
  (H : subgroup G) (K : subgroup N) [hH : H.normal] [hK : K.normal] :
  (H.prod K).normal :=
{ conj_mem := λ n hg g,
    ⟨hH.conj_mem n.fst (subgroup.mem_prod.mp hg).1 g.fst,
     hK.conj_mem n.snd (subgroup.mem_prod.mp hg).2 g.snd⟩ }

@[to_additive] lemma inf_subgroup_of_inf_normal_of_right
  (A B' B : subgroup G) (hB : B' ≤ B) [hN : (B'.subgroup_of B).normal] :
  ((A ⊓ B').subgroup_of (A ⊓ B)).normal :=
{ conj_mem := λ n hn g,
    ⟨mul_mem (mul_mem (mem_inf.1 g.2).1 (mem_inf.1 n.2).1) (inv_mem (mem_inf.1 g.2).1),
    (normal_subgroup_of_iff hB).mp hN n g hn.2 (mem_inf.mp g.2).2⟩ }

@[to_additive] lemma inf_subgroup_of_inf_normal_of_left
  {A' A : subgroup G} (B : subgroup G) (hA : A' ≤ A) [hN : (A'.subgroup_of A).normal] :
  ((A' ⊓ B).subgroup_of (A ⊓ B)).normal :=
{ conj_mem := λ n hn g,
    ⟨(normal_subgroup_of_iff hA).mp hN n g hn.1  (mem_inf.mp g.2).1,
    mul_mem (mul_mem (mem_inf.1 g.2).2 (mem_inf.1 n.2).2) (inv_mem (mem_inf.1 g.2).2)⟩ }

instance sup_normal (H K : subgroup G) [hH : H.normal] [hK : K.normal] : (H ⊔ K).normal :=
{ conj_mem := λ n hmem g,
  begin
    change n ∈ ↑(H ⊔ K) at hmem,
    change g * n * g⁻¹ ∈ ↑(H ⊔ K),
    rw [normal_mul, set.mem_mul] at *,
    rcases hmem with ⟨h, k, hh, hk, rfl⟩,
    refine ⟨g * h * g⁻¹, g * k * g⁻¹, hH.conj_mem h hh g, hK.conj_mem k hk g, _⟩,
    simp
  end }

@[to_additive] instance normal_inf_normal (H K : subgroup G) [hH : H.normal] [hK : K.normal] :
  (H ⊓ K).normal :=
{ conj_mem := λ n hmem g,
  by { rw mem_inf at *, exact ⟨hH.conj_mem n hmem.1 g, hK.conj_mem n hmem.2 g⟩ } }

@[to_additive] lemma subgroup_of_sup (A A' B : subgroup G) (hA : A ≤ B) (hA' : A' ≤ B) :
  (A ⊔ A').subgroup_of B = A.subgroup_of B ⊔ A'.subgroup_of B :=
begin
  refine map_injective_of_ker_le B.subtype
    (ker_le_comap _ _) (le_trans (ker_le_comap B.subtype _) le_sup_left) _,
  { simp only [subgroup_of, map_comap_eq, map_sup, subtype_range],
    rw [inf_of_le_right (sup_le hA hA'), inf_of_le_right hA', inf_of_le_right hA] },
end

@[to_additive] lemma subgroup_normal.mem_comm {H K : subgroup G}
  (hK : H ≤ K) [hN : (H.subgroup_of K).normal] {a b : G} (hb : b ∈ K) (h : a * b ∈ H) :
  b * a ∈ H :=
begin
  have := (normal_subgroup_of_iff hK).mp hN (a * b) b h hb,
  rwa [mul_assoc, mul_assoc, mul_right_inv, mul_one] at this,
end

/-- Elements of disjoint, normal subgroups commute -/
@[to_additive] lemma commute_of_normal_of_disjoint
  (H₁ H₂ : subgroup G) (hH₁ : H₁.normal) (hH₂ : H₂.normal) (hdis : disjoint H₁ H₂)
  (x y : G) (hx : x ∈ H₁) (hy : y ∈ H₂) :
  commute x y :=
begin
  suffices : x * y * x⁻¹ * y⁻¹ = 1,
  { show x * y = y * x, by { rw [mul_assoc, mul_eq_one_iff_eq_inv] at this, simpa } },
  apply hdis, split,
  { suffices : x * (y * x⁻¹ * y⁻¹) ∈ H₁, by simpa [mul_assoc],
    exact H₁.mul_mem hx (hH₁.conj_mem _ (H₁.inv_mem hx) _) },
  { show x * y * x⁻¹ * y⁻¹ ∈ H₂,
    apply H₂.mul_mem _ (H₂.inv_mem hy),
    apply (hH₂.conj_mem _ hy), }
end

end subgroup_normal

@[to_additive]
lemma disjoint_def {H₁ H₂ : subgroup G} :
  disjoint H₁ H₂ ↔ ∀ {x : G}, x ∈ H₁ → x ∈ H₂ → x = 1 :=
show (∀ x, x ∈ H₁ ∧ x ∈ H₂ → x ∈ ({1} : set G)) ↔ _, by simp

@[to_additive]
lemma disjoint_def' {H₁ H₂ : subgroup G} :
  disjoint H₁ H₂ ↔ ∀ {x y : G}, x ∈ H₁ → y ∈ H₂ → x = y → x = 1 :=
disjoint_def.trans ⟨λ h x y hx hy hxy, h hx $ hxy.symm ▸ hy,
  λ h x hx hx', h hx hx' rfl⟩

@[to_additive]
lemma disjoint_iff_mul_eq_one {H₁ H₂ : subgroup G} :
  disjoint H₁ H₂ ↔ ∀ {x y : G}, x ∈ H₁ → y ∈ H₂ → x * y = 1 → x = 1 ∧ y = 1 :=
disjoint_def'.trans ⟨λ h x y hx hy hxy,
  let hx1 : x = 1 := h hx (H₂.inv_mem hy) (eq_inv_iff_mul_eq_one.mpr hxy) in
  ⟨hx1, by simpa [hx1] using hxy⟩,
  λ h x y hx hy hxy, (h hx (H₂.inv_mem hy) (mul_inv_eq_one.mpr hxy)).1 ⟩

/-- `finset.noncomm_prod` is “injective” in `f` if `f` maps into independent subgroups.  This
generalizes (one direction of) `subgroup.disjoint_iff_mul_eq_one`. -/
@[to_additive "`finset.noncomm_sum` is “injective” in `f` if `f` maps into independent subgroups.
This generalizes (one direction of) `add_subgroup.disjoint_iff_add_eq_zero`. "]
lemma eq_one_of_noncomm_prod_eq_one_of_independent {ι : Type*}
  (s : finset ι) (f : ι → G) (comm : ∀ (x ∈ s) (y ∈ s), commute (f x) (f y))
  (K : ι → subgroup G) (hind : complete_lattice.independent K) (hmem : ∀ (x ∈ s), f x ∈ K x)
  (heq1 : s.noncomm_prod f comm = 1) : ∀ (i ∈ s), f i = 1 :=
begin
  classical,
  revert heq1,
  induction s using finset.induction_on with i s hnmem ih,
  { simp, },
  { simp only [finset.forall_mem_insert] at comm hmem,
    specialize ih (λ x hx, (comm.2 x hx).2) hmem.2,
    have hmem_bsupr: s.noncomm_prod f (λ x hx, (comm.2 x hx).2) ∈ ⨆ (i ∈ (s : set ι)), K i,
    { refine subgroup.noncomm_prod_mem _ _ _,
      intros x hx,
      have : K x ≤ ⨆ (i ∈ (s : set ι)), K i := le_supr₂ x hx,
      exact this (hmem.2 x hx), },
    intro heq1,
    rw finset.noncomm_prod_insert_of_not_mem _ _ _ _ hnmem at heq1,
    have hnmem' : i ∉ (s : set ι), by simpa,
    obtain ⟨heq1i : f i = 1, heq1S : s.noncomm_prod f _ = 1⟩ :=
      subgroup.disjoint_iff_mul_eq_one.mp (hind.disjoint_bsupr hnmem') hmem.1 hmem_bsupr heq1,
    specialize ih heq1S,
    intros i h,
    simp only [finset.mem_insert] at h,
    rcases h with ⟨rfl | _⟩,
    { exact heq1i },
    { exact (ih _ h), } }
end

end subgroup

namespace is_conj
open subgroup

lemma normal_closure_eq_top_of {N : subgroup G} [hn : N.normal]
  {g g' : G} {hg : g ∈ N} {hg' : g' ∈ N} (hc : is_conj g g')
  (ht : normal_closure ({⟨g, hg⟩} : set N) = ⊤) :
  normal_closure ({⟨g', hg'⟩} : set N) = ⊤ :=
begin
  obtain ⟨c, rfl⟩ := is_conj_iff.1 hc,
  have h : ∀ x : N, (mul_aut.conj c) x ∈ N,
  { rintro ⟨x, hx⟩,
    exact hn.conj_mem _ hx c },
  have hs : function.surjective (((mul_aut.conj c).to_monoid_hom.restrict N).cod_restrict _ h),
  { rintro ⟨x, hx⟩,
    refine ⟨⟨c⁻¹ * x * c, _⟩, _⟩,
    { have h := hn.conj_mem _ hx c⁻¹,
      rwa [inv_inv] at h },
    simp only [monoid_hom.cod_restrict_apply, mul_equiv.coe_to_monoid_hom, mul_aut.conj_apply,
      coe_mk, monoid_hom.restrict_apply, subtype.mk_eq_mk, ← mul_assoc, mul_inv_self, one_mul],
    rw [mul_assoc, mul_inv_self, mul_one] },
  have ht' := map_mono (eq_top_iff.1 ht),
  rw [← monoid_hom.range_eq_map, monoid_hom.range_top_of_surjective _ hs] at ht',
  refine eq_top_iff.2 (le_trans ht' (map_le_iff_le_comap.2 (normal_closure_le_normal _))),
  rw [set.singleton_subset_iff, set_like.mem_coe],
  simp only [monoid_hom.cod_restrict_apply, mul_equiv.coe_to_monoid_hom, mul_aut.conj_apply, coe_mk,
    monoid_hom.restrict_apply, mem_comap],
  exact subset_normal_closure (set.mem_singleton _),
end

end is_conj

/-! ### Actions by `subgroup`s

These are just copies of the definitions about `submonoid` starting from `submonoid.mul_action`.
-/
section actions

namespace subgroup

variables {α β : Type*}

/-- The action by a subgroup is the action by the underlying group. -/
@[to_additive /-"The additive action by an add_subgroup is the action by the underlying
add_group. "-/]
instance [mul_action G α] (S : subgroup G) : mul_action S α :=
S.to_submonoid.mul_action

@[to_additive]
lemma smul_def [mul_action G α] {S : subgroup G} (g : S) (m : α) : g • m = (g : G) • m := rfl

@[to_additive]
instance smul_comm_class_left
  [mul_action G β] [has_smul α β] [smul_comm_class G α β] (S : subgroup G) :
  smul_comm_class S α β :=
S.to_submonoid.smul_comm_class_left

@[to_additive]
instance smul_comm_class_right
  [has_smul α β] [mul_action G β] [smul_comm_class α G β] (S : subgroup G) :
  smul_comm_class α S β :=
S.to_submonoid.smul_comm_class_right

/-- Note that this provides `is_scalar_tower S G G` which is needed by `smul_mul_assoc`. -/
instance
  [has_smul α β] [mul_action G α] [mul_action G β] [is_scalar_tower G α β] (S : subgroup G) :
  is_scalar_tower S α β :=
S.to_submonoid.is_scalar_tower

instance [mul_action G α] [has_faithful_smul G α] (S : subgroup G) :
  has_faithful_smul S α :=
S.to_submonoid.has_faithful_smul

/-- The action by a subgroup is the action by the underlying group. -/
instance [add_monoid α] [distrib_mul_action G α] (S : subgroup G) : distrib_mul_action S α :=
S.to_submonoid.distrib_mul_action

/-- The action by a subgroup is the action by the underlying group. -/
instance [monoid α] [mul_distrib_mul_action G α] (S : subgroup G) : mul_distrib_mul_action S α :=
S.to_submonoid.mul_distrib_mul_action

/-- The center of a group acts commutatively on that group. -/
instance center.smul_comm_class_left : smul_comm_class (center G) G G :=
submonoid.center.smul_comm_class_left

/-- The center of a group acts commutatively on that group. -/
instance center.smul_comm_class_right : smul_comm_class G (center G) G :=
submonoid.center.smul_comm_class_right

end subgroup

end actions

/-! ### Mul-opposite subgroups -/

section mul_opposite

namespace subgroup

/-- A subgroup `H` of `G` determines a subgroup `H.opposite` of the opposite group `Gᵐᵒᵖ`. -/
@[to_additive "An additive subgroup `H` of `G` determines an additive subgroup `H.opposite` of the
  opposite additive group `Gᵃᵒᵖ`."]
def opposite (H : subgroup G) : subgroup Gᵐᵒᵖ :=
{ carrier := mul_opposite.unop ⁻¹' (H : set G),
  one_mem' := H.one_mem,
  mul_mem' := λ a b ha hb, H.mul_mem hb ha,
  inv_mem' := λ a, H.inv_mem }

/-- Bijection between a subgroup `H` and its opposite. -/
@[to_additive "Bijection between an additive subgroup `H` and its opposite.", simps]
def opposite_equiv (H : subgroup G) : H ≃ H.opposite :=
mul_opposite.op_equiv.subtype_equiv $ λ _, iff.rfl

@[to_additive] instance (H : subgroup G) [encodable H] : encodable H.opposite :=
encodable.of_equiv H H.opposite_equiv.symm

@[to_additive] lemma smul_opposite_mul {H : subgroup G} (x g : G) (h : H.opposite) :
  h • (g * x) = g * (h • x) :=
begin
  cases h,
  simp [(•), mul_assoc],
end

@[to_additive] lemma smul_opposite_image_mul_preimage {H : subgroup G} (g : G) (h : H.opposite)
  (s : set G) : (λ y, h • y) '' (has_mul.mul g ⁻¹' s) = has_mul.mul g ⁻¹' ((λ y, h • y) '' s) :=
by { ext x, cases h, simp [(•), mul_assoc] }

end subgroup

end mul_opposite

/-! ### Saturated subgroups -/

section saturated

namespace subgroup

/-- A subgroup `H` of `G` is *saturated* if for all `n : ℕ` and `g : G` with `g^n ∈ H`
we have `n = 0` or `g ∈ H`. -/
@[to_additive "An additive subgroup `H` of `G` is *saturated* if
for all `n : ℕ` and `g : G` with `n•g ∈ H` we have `n = 0` or `g ∈ H`."]
def saturated (H : subgroup G) : Prop := ∀ ⦃n g⦄, g ^ n ∈ H → n = 0 ∨ g ∈ H

@[to_additive] lemma saturated_iff_npow {H : subgroup G} :
  saturated H ↔ (∀ (n : ℕ) (g : G), g ^ n ∈ H → n = 0 ∨ g ∈ H) := iff.rfl

@[to_additive] lemma saturated_iff_zpow {H : subgroup G} :
  saturated H ↔ (∀ (n : ℤ) (g : G), g ^ n ∈ H → n = 0 ∨ g ∈ H) :=
begin
  split,
  { rintros hH ⟨n⟩ g hgn,
    { simp only [int.coe_nat_eq_zero, int.of_nat_eq_coe, zpow_coe_nat] at hgn ⊢,
      exact hH hgn },
    { suffices : g ^ (n+1) ∈ H,
      { refine (hH this).imp _ id, simp only [is_empty.forall_iff, nat.succ_ne_zero], },
      simpa only [inv_mem_iff, zpow_neg_succ_of_nat] using hgn, } },
  { intros h n g hgn,
    specialize h n g,
    simp only [int.coe_nat_eq_zero, zpow_coe_nat] at h,
    apply h hgn }
end

end subgroup

namespace add_subgroup

lemma ker_saturated {A₁ A₂ : Type*} [add_comm_group A₁] [add_comm_group A₂]
  [no_zero_smul_divisors ℕ A₂] (f : A₁ →+ A₂) :
  (f.ker).saturated :=
begin
  intros n g hg,
  simpa only [f.mem_ker, nsmul_eq_smul, f.map_nsmul, smul_eq_zero] using hg
end

end add_subgroup

end saturated

subset_name: curated