/-
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.center
import algebra.group.conj
import algebra.module.basic
import order.atoms

/-!
# 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]

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 :=
⟨subgroup.carrier, λ p q h, by cases p; cases q; congr'⟩

@[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."]
theorem one_mem : (1 : G) ∈ H := H.one_mem'

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

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

/-- A subgroup is closed under division. -/
@[to_additive "An `add_subgroup` is closed under subtraction."]
theorem div_mem {x y : G} (hx : x ∈ H) (hy : y ∈ H) : x / y ∈ H :=
by simpa only [div_eq_mul_inv] using H.mul_mem' hx (H.inv_mem' hy)

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

@[to_additive] lemma div_mem_comm_iff {a b : G} : a / b ∈ H ↔ b / a ∈ H :=
by rw [← H.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  (K : subgroup G) {P : G → Prop} :
  (∃ (x : G), x ∈ K ∧ P x⁻¹) ↔ ∃ x ∈ K, P x :=
by split; { rintros ⟨x, x_in, hx⟩, exact ⟨x⁻¹, inv_mem K 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 H.mul_mem hba (H.inv_mem h), λ hb, H.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 H.mul_mem (H.inv_mem h) hab, H.mul_mem 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`."]
lemma list_prod_mem {l : list G} : (∀ x ∈ l, x ∈ K) → l.prod ∈ K :=
K.to_submonoid.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`."]
lemma multiset_prod_mem {G} [comm_group G] (K : subgroup G) (g : multiset G) :
  (∀ a ∈ g, a ∈ K) → g.prod ∈ K := K.to_submonoid.multiset_prod_mem g

/-- 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`."]
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 :=
K.to_submonoid.prod_mem h

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

@[to_additive]
lemma zpow_mem {x : G} (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  K.inv_mem (K.pow_mem hx n.succ) }

/-- 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 x hx hx,
have inv_mem : ∀ x, x ∈ s → x⁻¹ ∈ s, from λ x hx, by simpa using hs 1 x one_mem hx,
{ carrier := s,
  one_mem' := one_mem,
  inv_mem' := inv_mem,
  mul_mem' := λ x y hx hy, by simpa using hs x y⁻¹ hx (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⟩⟩

@[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

/-- 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)

/-- 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)

/-- 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)

/-- 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)

/-- 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 coe_smul]
lemma coe_pow (x : H) (n : ℕ) : ((x ^ n : H) : G) = x ^ n :=
coe_subtype H ▸ monoid_hom.map_pow _ _ _
@[simp, norm_cast, to_additive] lemma coe_zpow (x : H) (n : ℤ) : ((x ^ n : H) : G) = x ^ n :=
coe_subtype H ▸ monoid_hom.map_zpow _ _ _

@[simp, norm_cast, to_additive] theorem coe_list_prod (l : list H) :
  (l.prod : G) = (l.map coe).prod :=
H.to_submonoid.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 :=
H.to_submonoid.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) :=
H.to_submonoid.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] }

@[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 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_ge'.trans (not_iff_not.mpr H.card_le_one_iff_eq_bot)

/-- 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_bInter_iff.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_bInter_iff

@[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 {ι : Type*} {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, H1, Hmul, Hinv⟩ _).2 Hk h

/-- An induction principle on elements of the subtype `subgroup.closure`.
If `p` holds for `1` and all elements of `k`, and is preserved under multiplication and inverse,
then `p` holds for all elements `x : closure k`.

The difference with `subgroup.closure_induction` is that this acts on the subtype.
-/
@[elab_as_eliminator, to_additive "An induction principle on elements of the subtype
`add_subgroup.closure`.  If `p` holds for `0` and all elements of `k`, and is preserved under
addition and negation, then `p` holds for all elements `x : closure k`.

The difference with `add_subgroup.closure_induction` is that this acts on the subtype."]
lemma closure_induction' (k : set G) {p : closure k → Prop}
  (Hk : ∀ x (h : x ∈ k), p ⟨x, subset_closure h⟩)
  (H1 : p 1)
  (Hmul : ∀ x y, p x → p y → p (x * y))
  (Hinv : ∀ x, p x → p x⁻¹)
  (x : closure k) :
  p x :=
subtype.rec_on x $ λ x hx, begin
  refine exists.elim _ (λ (hx : x ∈ closure k) (hc : p ⟨x, hx⟩), hc),
  exact closure_induction hx
    (λ x hx, ⟨subset_closure hx, Hk x hx⟩)
    ⟨one_mem _, H1⟩
    (λ x y hx hy, exists.elim hx $ λ hx' hx, exists.elim hy $ λ hy' hy,
      ⟨mul_mem _ hx' hy', Hmul _ _ hx hy⟩)
    (λ x hx, exists.elim hx $ λ hx' hx, ⟨inv_mem _ hx', Hinv _ hx⟩),
end

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

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 _}

/-- 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 [set.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, set.inv_inv, set.union_comm] },
  { simp only [true_and, coe_to_submonoid, union_subset_iff, subset_closure, inv_subset_closure] }
end

/-- 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 :=
begin
  rw [← mem_to_submonoid, closure_to_submonoid k] at h,
  refine submonoid.closure_induction h (λ x hx, _) H1 (λ x y hx hy, Hmul x y hx hy),
  { rw [mem_union, mem_inv] at hx,
    cases hx with mem invmem,
    { exact Hk x mem },
    { rw [← inv_inv x],
      exact Hk_inv _ invmem } },
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, (K k).mul_mem (hki hi) (hkj hj)⟩ },
    rintros _ ⟨i, hi⟩, exact ⟨i, inv_mem (K i) 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 _ _ _

@[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 comap_top (f : G →* N) : (⊤ : subgroup N).comap f = ⊤ :=
(gc_map_comap f).u_top

@[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 : set G).prod (K : set N) := 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]

/-- 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 }

/-- 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

variables {G} (H)
/-- 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 ∈ normalizer H ↔ ∀ n, n ∈ H ↔ g * n * g⁻¹ ∈ H := iff.rfl

@[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⟩⟩

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 (H)

/-- 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_bUnion_iff

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.bUnion_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 subgroup.one_mem _ },
  { exact subgroup.mul_mem _ ihx ihy },
  { exact subgroup.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_refl _))))

@[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, ←one_inv, ←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 :=
monoid_hom.mk' (λ g, ⟨f g, ⟨g, rfl⟩⟩) $ λ a b, by {ext, exact f.map_mul' _ _}

@[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."]

@[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)

/-- Restriction of a group hom to a subgroup of the domain. -/
@[to_additive "Restriction of an `add_group` hom to an `add_subgroup` of the domain."]
def restrict (f : G →* N) (H : subgroup G) : H →* N :=
f.comp H.subtype

@[simp, to_additive]
lemma restrict_apply {H : subgroup G} (f : G →* N) (x : H) :
  f.restrict H x = f (x : G) := rfl

/-- Restriction of a group hom to a subgroup of the codomain. -/
@[to_additive "Restriction of an `add_group` hom to an `add_subgroup` of the codomain."]
def cod_restrict (f : G →* N) (S : subgroup N) (h : ∀ x, f x ∈ S) : G →* S :=
{ to_fun := λ n, ⟨f n, h n⟩,
  map_one' := subtype.eq f.map_one,
  map_mul' := λ x y, subtype.eq (f.map_mul x y) }

@[simp, to_additive]
lemma cod_restrict_apply {G : Type*} [group G] {N : Type*} [group N] (f : G →* N)
  (S : subgroup N) (h : ∀ (x : G), f x ∈ S) {x : G} :
    f.cod_restrict S h x = ⟨f x, h x⟩ := rfl

@[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) :=
le_antisymm
  (map_le_iff_le_comap.2 $ le_trans (closure_mono $ set.subset_preimage_image f s)
    (gclosure_preimage_le _ _))
  ((closure_le _).2 $ set.image_subset _ subset_closure)

-- 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_injective {f : G →* N} (h : function.surjective f) : function.injective (comap f) :=
λ K L hKL, by { apply_fun map f at hKL, simpa [map_comap_eq_self_of_surjective h] using hKL }

@[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_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

/-- 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 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_equiv_map {G'} [group G'] (e : G ≃* G') (H : subgroup G) :
  H ≃* H.map e.to_monoid_hom :=
e.submonoid_equiv_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]
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 C (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 C (inv_mem C (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 ⟨mul_mem A 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 A hyz (inv_mem A (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 A (mul_mem A (mem_inf.1 g.2).1 (mem_inf.1 n.2).1) (inv_mem A (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 B (mul_mem B (mem_inf.1 g.2).2 (mem_inf.1 n.2).2) (inv_mem B (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

end subgroup_normal

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_scalar α β] [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_scalar α β] [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_scalar α β] [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_scalar G α] (S : subgroup G) :
  has_faithful_scalar S α :=
S.to_submonoid.has_faithful_scalar

/-- 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

end subgroup

end actions

/-! ### 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 [forall_false_left, 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