/-
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
import algebra.group.conj
import algebra.pointwise
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

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

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

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

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

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

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

attribute [ext] add_subgroup.ext

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

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

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

lemma pow_mem {x : G} (hx : x ∈ K) : ∀ n : ℕ, x ^ n ∈ K := K.to_submonoid.pow_mem hx

lemma gpow_mem {x : G} (hx : x ∈ K) : ∀ n : ℤ, x ^ n ∈ K
| (n : ℕ) := by { rw [gpow_coe_nat], exact pow_mem _ hx n }
| -[1+ n] := by { rw [gpow_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_mk (x : G) (hx : x ∈ H) : ((⟨x, hx⟩ : H) : G) = x := rfl

attribute [norm_cast] add_subgroup.coe_add add_subgroup.coe_zero
  add_subgroup.coe_neg add_subgroup.coe_mk

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

/-- 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] 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] instance fintype_bot : fintype (⊥ : subgroup G) := ⟨{1},
by {rintro ⟨x, ⟨hx⟩⟩, exact finset.mem_singleton_self _}⟩

@[simp] lemma _root_.add_subgroup.card_bot : fintype.card (⊥ : add_subgroup A) = 1 :=
fintype.card_eq_one_iff.2
  ⟨⟨(0 : A), set.mem_singleton 0⟩, λ ⟨y, hy⟩, subtype.eq $ add_subgroup.mem_bot.1 hy⟩

-- `@[to_additive]` doesn't work, because it converts the `1 : ℕ` to `0`.
@[simp] lemma card_bot : 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 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,
    push_neg at h,
    simpa [nontrivial_iff_exists_ne_one] using h },
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

/-- 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, to_additive]
lemma coe_Inf (H : set (subgroup G)) : ((Inf H : subgroup G) : set G) = ⋂ s ∈ H, ↑s := rfl

attribute [norm_cast] coe_Inf add_subgroup.coe_Inf

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

attribute [norm_cast] coe_infi add_subgroup.coe_infi

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

@[to_additive]
lemma subsingleton_iff : subsingleton G ↔ subsingleton (subgroup G) :=
⟨ λ h, by exactI ⟨λ x y, subgroup.ext $ λ i, subsingleton.elim 1 i ▸ by simp [subgroup.one_mem]⟩,
  λ 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⟩⟩

@[to_additive]
lemma nontrivial_iff : nontrivial G ↔ nontrivial (subgroup 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.mp ‹_›) a _⟩

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

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

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`. -/
@[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

attribute [elab_as_eliminator] subgroup.closure_induction add_subgroup.closure_induction

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

attribute [elab_as_eliminator] subgroup.closure_induction' add_subgroup.closure_induction'

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. -/
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 ▸ gpow_mem _ (subset_closure $ mem_singleton x) n⟩,
  { intros y hy,
    rw [eq_of_mem_singleton hy],
    exact ⟨1, gpow_one x⟩ },
  { exact ⟨0, gpow_zero x⟩ },
  { rintros _ _ ⟨n, rfl⟩ ⟨m, rfl⟩,
    exact ⟨n + m, gpow_add x n m⟩ },
    rintros _ ⟨n, rfl⟩,
    exact ⟨-n, gpow_neg x n⟩
end

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 map_mono {f : G →* N} {K K' : subgroup G} : K ≤ K' → map f K ≤ map f K' :=
image_subset _

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

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

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

@[priority 100, to_additive]
instance bot_normal : normal (⊥ : subgroup G) := ⟨by simp⟩

@[priority 100, to_additive]
instance top_normal : normal (⊤ : subgroup G) := ⟨λ _ _, mem_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 a group `G` is the set of elements that commute with everything in
`G`"]
def center : subgroup G :=
{ carrier := {z | ∀ g, g * z = z * g},
  one_mem' := by simp,
  mul_mem' := λ a b (ha : ∀ g, g * a = a * g) (hb : ∀ g, g * b = b * g) g,
    by assoc_rw [ha, hb g],
  inv_mem' := λ a (ha : ∀ g, g * a = a * g) g,
    by rw [← inv_inj, mul_inv_rev, inv_inv, ← ha, mul_inv_rev, inv_inv] }

variable {G}

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

@[priority 100, to_additive]
instance center_normal : (center G).normal :=
⟨begin
  assume n hn g h,
  assoc_rw [hn (h * g), hn g],
  simp
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⟩

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

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₁, is_conj_trans h₂ (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)

end subgroup
namespace add_subgroup

open set

lemma gsmul_mem (H : add_subgroup A) {x : A} (hx : x ∈ H) :
  ∀ n : ℤ, n • x ∈ H
| (n : ℕ) := by { rw [gsmul_coe_nat], exact add_submonoid.nsmul_mem H.to_add_submonoid hx n }
| -[1+ n]        := begin
    rw gsmul_neg_succ_of_nat,
    apply H.neg_mem',
    exact add_submonoid.nsmul_mem H.to_add_submonoid hx n.succ
  end

/-- 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 : A} :
  y ∈ closure ({x} : set A) ↔ ∃ n : ℤ, n • x = y :=
begin
  refine ⟨λ hy, closure_induction hy _ _ _ _,
    λ ⟨n, hn⟩, hn ▸ gsmul_mem _ (subset_closure $ mem_singleton x) n⟩,
  { intros y hy,
    rw [eq_of_mem_singleton hy],
    exact ⟨1, one_gsmul x⟩ },
  { exact ⟨0, zero_gsmul x⟩ },
  { rintros _ _ ⟨n, rfl⟩ ⟨m, rfl⟩,
    exact ⟨n + m, add_gsmul x n m⟩ },
  { rintros _ ⟨n, rfl⟩,
    refine ⟨-n, neg_gsmul x n⟩ }
end

lemma closure_singleton_zero : closure ({0} : set A) = ⊥ :=
by simp [eq_bot_iff_forall, mem_closure_singleton]

variable (H : add_subgroup A)
@[simp] lemma coe_smul (x : H) (n : ℕ) : ((n • x : H) : A) = n • x :=
coe_subtype H ▸ add_monoid_hom.map_nsmul _ _ _
@[simp] lemma coe_gsmul (x : H) (n : ℤ) : ((n • x : H) : A) = n • x :=
coe_subtype H ▸ add_monoid_hom.map_gsmul _ _ _

attribute [to_additive add_subgroup.coe_smul] subgroup.coe_pow
attribute [to_additive add_subgroup.coe_gsmul] subgroup.coe_gpow

end add_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 (λ x, x ∈ 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 map_range (g : N →* P) (f : G →* N) : f.range.map g = (g.comp f).range :=
by rw [range_eq_map, range_eq_map]; exact (⊤ : subgroup G).map_map g f

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

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

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

/-- Computable alternative to `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] 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] 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. -/
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⟩ }⟩)

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

/-- 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 →* N) := (⊥ : subgroup N).comap f

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

@[to_additive]
instance decidable_mem_ker [decidable_eq N] (f : G →* N) :
  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

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

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

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

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 {H : Type*} [group H]

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

@[to_additive]
lemma ker_le_comap (f : G →* H) (K : subgroup H) : f.ker ≤ comap f K :=
comap_mono bot_le

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

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

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

@[to_additive]
lemma comap_map_eq (f : G →* H) (K : subgroup G) :
  comap f (map f K) = K ⊔ 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'⟩,
  have : y⁻¹ * x ∈ f.ker, { rw mem_ker, simp [hy'] },
  convert mul_mem _ (mem_sup_left hy) (mem_sup_right this),
  simp,
end

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

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

@[to_additive]
lemma comap_injective {f : G →* H} (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 →* H} {K : subgroup G} (h : f.ker ≤ K) :
  comap f (map f K) = K :=
by rwa [comap_map_eq, sup_eq_left]

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

@[to_additive]
lemma map_injective {f : G →* H} (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 →* H} {g : H →* G} (hl : function.left_inverse g f)
  (hr : function.right_inverse g f) (K : subgroup G) : map f K = comap g K :=
set_like.ext' $ by rw [coe_map, coe_comap, set.image_eq_preimage_of_inverse hl hr]

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 [monoid_hom.ker]; 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 gpowers (g : G) : subgroup G :=
subgroup.copy (gpowers_hom G g).range (set.range ((^) g : ℤ → G)) rfl

@[simp] lemma mem_gpowers (g : G) : g ∈ gpowers g := ⟨1, gpow_one _⟩

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

@[simp] lemma range_gpowers_hom (g : G) : (gpowers_hom G g).range = gpowers g := rfl

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

end subgroup

namespace add_subgroup

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

@[simp] lemma mem_gmultiples (a : A) : a ∈ gmultiples a := ⟨1, one_gsmul _⟩

lemma gmultiples_eq_closure (a : A) : gmultiples a = closure {a} :=
by { ext, exact mem_closure_singleton.symm }

@[simp] lemma range_gmultiples_hom (a : A) : (gmultiples_hom A a).range = gmultiples a := rfl

lemma gmultiples_subset {a : A} {B : add_subgroup A} (h : a ∈ B) : gmultiples a ≤ B :=
@subgroup.gpowers_subset (multiplicative A) _ _ (B.to_subgroup) h

attribute [to_additive add_subgroup.gmultiples] subgroup.gpowers
attribute [to_additive add_subgroup.mem_gmultiples] subgroup.mem_gpowers
attribute [to_additive add_subgroup.gmultiples_eq_closure] subgroup.gpowers_eq_closure
attribute [to_additive add_subgroup.range_gmultiples_hom] subgroup.range_gpowers_hom
attribute [to_additive add_subgroup.gmultiples_subset] subgroup.gpowers_subset

end add_subgroup

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

lemma of_add_image_gmultiples_eq_gpowers_of_add {x : A} :
  multiplicative.of_add '' ((add_subgroup.gmultiples x) : set A) =
  subgroup.gpowers (multiplicative.of_add x) :=
begin
  symmetry,
  rw equiv.eq_image_iff_symm_image_eq,
  exact of_mul_image_gpowers_eq_gmultiples_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 }

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 _
  ((le_sup_left : s ≤ s ⊔ t) hy)
  ((le_sup_right : t ≤ s ⊔ t) 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_lattice (subgroup C) :=
⟨λ H, H.normal_of_comm.eq_bot_or_eq_top⟩

open 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

section pointwise

namespace subgroup

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

end subgroup

end pointwise


theorem ex1 (a b c : Nat) (h₁ : a = b) (h₂ : b = c) : a + b = 0 + c + b :=
  calc  a + b  = b + b     := by rw [h₁]
        _      = 0 + c + b := rfl

theorem ex2 (a b c : Nat) (h₁ : a = b) (h₂ : b = c) : a + b = 0 + c + b :=
  calc  a + b     = b + b     := by rw [h₁]
        0 + c + b = 0 + c + b := rfl

theorem ex3 (a b c : Nat) (h₁ : a = b) (h₂ : b = c) : a + b = 0 + c + b :=
  calc  a + b  = b + b     := by rw [h₁]
        _      = 0 + b + b := by rw [Nat.zero_add]
        _      = 0 + c + b := by rw [h₂]

theorem ex4 (p : Nat → Prop) (a b : Nat) (h₁ : p a) (h₂ : p b) : p c :=
  calc  p a := h₁
        _   := h₂

theorem ex5 (p : Nat → Nat → Prop) (a b : Nat) (h₁ : p a b) (h₂ : p b c) : p a c :=
  calc  p a b := h₁
        p _ c := h₂
infix:50 " ≅ "  => HEq
instance {α β γ} : Trans (· ≅ · : α → β → Prop) (· ≅ · : β → γ → Prop) (· ≅ · : α → γ → Prop) where
  trans h₁ h₂ := HEq.trans h₁ h₂

theorem ex6 {a : α} {b : β} {c : γ} (h₁ : HEq a b) (h₂ : b ≅ c) : a ≅ c :=
  calc a ≅ b := h₁
       _ ≅ c := h₂  -- Error because the last two arguments of HEq are not explicit

abbrev HEqRel {α β} (a : α) (b : β) := HEq a b

infix:50 "===" => HEqRel

instance {α β γ} : Trans (HEqRel : α → β → Prop) (HEqRel : β → γ → Prop) (HEqRel : α → γ → Prop) where
  trans h₁ h₂ := HEq.trans h₁ h₂

theorem ex7 {a : α} {b : β} {c : γ} (h₁ : a ≅ b) (h₂ : b ≅ c) : a === c :=
  calc a === b := h₁
       _ === c := h₂


import tactic.find -- super useful!

section ex1
  variables {p q r s: Prop}

  -- commutativity of ∧ and ∨
  example : p ∧ q ↔ q ∧ p :=
    iff.intro
      (assume h: p ∧ q, and.intro h.right h.left)
      (assume h: q ∧ p, and.intro h.right h.left)

  -- in one direction
  theorem or_comm_1: p ∨ q → q ∨ p :=
    (assume h: p ∨ q,
      or.elim h
        (assume hp: p, show q ∨ p, from or.inr hp)
        (assume hq: q, show q ∨ p, from or.inl hq))
  example : p ∨ q ↔ q ∨ p :=
    iff.intro
      (@or_comm_1 p q)
      (@or_comm_1 q p)

  -- associativity of ∧ and ∨
  example : (p ∧ q) ∧ r ↔ p ∧ (q ∧ r) :=
    iff.intro
      (assume h: (p ∧ q) ∧ r, ⟨h.left.left, h.left.right, h.right⟩)
      (assume h: p ∧ (q ∧ r), ⟨⟨h.left, h.right.left⟩, h.right.right⟩)

  example : (p ∨ q) ∨ r ↔ p ∨ (q ∨ r) :=
    iff.intro
      -- ORs are so much worse to reason about :\
      (assume h: (p ∨ q) ∨ r, or.elim h
        (assume hpq: p ∨ q, or.elim hpq
          (assume hp: p, or.inl hp)
          (assume hq: q, or.inr (or.inl hq)))
        (assume hr: r, or.inr (or.inr hr)))
      (assume h: p ∨ (q ∨ r), or.elim h
        (assume hp: p, or.inl (or.inl hp))
        (assume hqr: q ∨ r, or.elim hqr
          (assume hq: q, or.inl (or.inr hq))
          (assume hr: r, or.inr hr)))

  -- distributivity
  example : p ∧ (q ∨ r) ↔ (p ∧ q) ∨ (p ∧ r) :=
    -- shamelessly pasted from the book
    iff.intro
      (assume h : p ∧ (q ∨ r),
        have hp : p, from h.left,
        or.elim (h.right)
          (assume hq : q,
            show (p ∧ q) ∨ (p ∧ r), from or.inl ⟨hp, hq⟩)
          (assume hr : r,
            show (p ∧ q) ∨ (p ∧ r), from or.inr ⟨hp, hr⟩))
      (assume h : (p ∧ q) ∨ (p ∧ r),
        or.elim h
          (assume hpq : p ∧ q,
            have hp : p, from hpq.left,
            have hq : q, from hpq.right,
            show p ∧ (q ∨ r), from ⟨hp, or.inl hq⟩)
          (assume hpr : p ∧ r,
            have hp : p, from hpr.left,
            have hr : r, from hpr.right,
            show p ∧ (q ∨ r), from ⟨hp, or.inr hr⟩))

  example : p ∨ (q ∧ r) ↔ (p ∨ q) ∧ (p ∨ r) :=
    iff.intro
      (assume h : p ∨ (q ∧ r), or.elim h
        (assume hp: p, and.intro (or.inl hp) (or.inl hp))
        (assume hqr: q ∧ r, and.intro (or.inr hqr.left) (or.inr hqr.right)))
      (assume h : (p ∨ q) ∧ (p ∨ r),
        have hpq: p ∨ q, from h.left,
        have hpr: p ∨ r, from h.right,
        or.elim hpq
          (assume hp: p, or.inl hp)
          (assume hq: q, or.elim hpr
            (assume hp: p, or.inl hp)
            (assume hr: r, or.inr (and.intro hq hr))))

  -- other properties
  example : (p → (q → r)) ↔ (p ∧ q → r) :=
    iff.intro
      (assume h: p → (q → r),
      assume hpq: p ∧ q,
      h hpq.left hpq.right)
      (assume h: (p ∧ q → r),
      assume hp: p,
      assume hq: q,
      h (and.intro hp hq))

  example : ((p ∨ q) → r) ↔ (p → r) ∧ (q → r) :=
    iff.intro
      (assume h: (p ∨ q) → r, and.intro
        (assume hp: p, h (or.inl hp))
        (assume hq: q, h (or.inr hq)))
      (assume h: (p → r) ∧ (q → r),
      assume hpq: p ∨ q, or.elim hpq
        (assume hp: p, h.left hp)
        (assume hq: q, h.right hq))

  theorem de_morgan_2 : ¬(p ∨ q) ↔ ¬p ∧ ¬q :=
    iff.intro
      (assume h: ¬(p ∨ q), and.intro
        (assume hp: p, absurd (or.inl hp) h)
        (assume hq: q, absurd (or.inr hq) h))
      (assume h: ¬p ∧ ¬q,
      assume hpq: p ∨ q,
      or.elim hpq
        (assume hp: p, absurd hp h.left)
        (assume hq: q, absurd hq h.right))

  example : ¬p ∨ ¬q → ¬(p ∧ q) :=
    assume h: ¬p ∨ ¬q,
    assume hpq: p ∧ q,
    or.elim h
      (assume hnp: ¬p, absurd hpq.left hnp)
      (assume hnq: ¬q, absurd hpq.right hnq)

  theorem non_contradiction : ¬(p ∧ ¬p) :=
    assume h: p ∧ ¬p,
    absurd h.left h.right

  example : p ∧ ¬q → ¬(p → q) :=
    assume h: p ∧ ¬q,
    assume hpq: p → q,
    absurd (hpq h.left) h.right

  example : ¬p → (p → q) :=
    assume hnp: ¬p,
    assume hp: p,
    absurd hp hnp

  theorem or_not_imp: (¬p ∨ q) → (p → q) :=
    assume h: ¬p ∨ q,
    assume hp: p,
    or.elim h
      (assume hnp: ¬p, absurd hp hnp)
      (assume hq: q, hq)

  example : p ∨ false ↔ p :=
    iff.intro
      (assume h: p ∨ false, or.elim h
        (assume hp: p, hp)
        (assume hp: false, false.elim hp))
      (assume h: p, or.inl h)

  example : p ∧ false ↔ false :=
    iff.intro
      (assume h: p ∧ false, false.elim h.right)
      (assume h: false, false.elim h)

  example : (p → q) → (¬q → ¬p) :=
    assume h: p → q,
    assume hnq: ¬q,
    assume hp: p,
    absurd (h hp) hnq

  -- these require classical reasoning
  theorem dne (h: ¬¬p): p :=
    classical.by_cases
      (assume hp: p, hp)
      (assume hnp: ¬p, absurd hnp h)

  -- finds by_contradiction
  #find (¬ _ → false) → _

  -- long proof in classical logic
  theorem de_morgan_1_b: ¬(p ∧ q) → ¬p ∨ ¬q :=
    assume hnpq: ¬(p ∧ q),
    classical.by_contradiction
      (assume not_conclusion: ¬(¬p ∨ ¬q),
        have hpq: p ∧ q, from and.intro
          (show p, from classical.by_contradiction
            (assume hnp: ¬p,
              not_conclusion (show ¬p ∨ ¬q, from or.intro_left (¬q) hnp)))
          (show q, from classical.by_contradiction
            (assume hnq: ¬q,
              not_conclusion (show ¬p ∨ ¬q, from or.intro_right (¬p) hnq))),
        show false, from hnpq hpq)

  -- code-golfy proof using classical logic
  theorem de_morgan_1_b' (hnpq: ¬(p ∧ q)) : ¬p ∨ ¬q :=
    or.elim (classical.em p) (λ hp, or.inr $ λ hq, hnpq ⟨hp, hq⟩) or.inl

  -- proof using decidability assumption on p
  theorem de_morgan_1_b'' (hnpq: ¬(p ∧ q)) [decidable p] : ¬p ∨ ¬q :=
    if hp : p then or.inr (λ hq, hnpq ⟨hp, hq⟩) else or.inl hp

  theorem not_imp : ¬(p → q) → p ∧ ¬q :=
    assume h: ¬(p → q),
    classical.by_contradiction
      (assume hno: ¬(p ∧ ¬q),
      have hnpnnq: ¬p ∨ ¬¬q, from de_morgan_1_b hno,
      have hnpq: ¬p ∨ q, from (or.elim hnpnnq
        (assume hp: ¬p, or.inl hp)
        (assume hq: ¬¬q, or.inr (dne hq))),
      have hno': p → q, from or_not_imp hnpq,
      absurd hno' h)

  example : (p → r ∨ s) → ((p → r) ∨ (p → s)) :=
    assume h: p → r ∨ s,
    -- absolutely horrific proof, must be some better way
    classical.by_contradiction
      (assume hno: ¬((p → r) ∨ (p → s)),
      have hno': _, from de_morgan_2.mp hno,
      have hnpr: ¬(p → r), from hno'.left,
      have hnps: ¬(p → s), from hno'.right,
      have hpnr: p ∧ ¬r, from not_imp hnpr,
      have hpns: p ∧ ¬s, from not_imp hnps,
      or.elim (h hpnr.left)
        (assume hr: r, absurd hr hpnr.right)
        (assume hs: s, absurd hs hpns.right))

  example : (p → q) → (¬p ∨ q) :=
    assume h: p → q,
    classical.by_contradiction
      (assume hfalse: ¬(¬p ∨ q),
      have hfalse': ¬¬p ∧ ¬q, from de_morgan_2.mp hfalse,
      absurd (h (dne hfalse'.left)) hfalse'.right)

  example : (¬q → ¬p) → (p → q) :=
    assume h: ¬q → ¬p,
    classical.by_contradiction
      (assume hno: ¬(p → q),
      have hpnq: p ∧ ¬q, from not_imp hno,
      absurd hpnq.left (h hpnq.right))

  example : p ∨ ¬p := classical.em p

  example : ((p → q) → p) → p :=
    assume h: ((p → q) → p),
    classical.by_cases
      (assume hpq: (p → q), h hpq)
      (assume hnpq: ¬(p → q),
        have hpnq: p ∧ ¬q, from not_imp hnpq,
        hpnq.left)
end ex1

section ex2
  variable p: Prop

  -- prove ¬(p ↔ ¬p) without using classical logic
  example : ¬(p ↔ ¬p) :=
    assume h: p ↔ ¬p,
    have hnp: ¬p, from
      (assume hp: p,
      have hnp: ¬p, from h.mp hp,
      show false, from hnp hp),
    have hp: p, from h.mpr hnp,
    show false, from hnp hp
end ex2

namespace dne_test
  variable {p: Prop}

  axiom dne (h: ¬¬p): p

  -- derive by_contradiction from dne
  theorem contradict (h: ¬p → false): p :=
    have hnn: ¬¬p, from not.intro h,
    show p, from dne hnn

  -- this seems to say it's unprovable: https://math.stackexchange.com/a/913019
  -- in spite of stack exchange, derive LEM from DNE
  theorem lem: p ∨ ¬p :=
    suffices hp: ¬(p ∨ ¬p) → false, from contradict hp,
    assume hp: ¬(p ∨ ¬p),
    have hnpnnp: ¬p ∧ ¬¬p, from de_morgan_2.mp hp,
    have hp: p, from dne hnpnnp.right,
    have hnp: ¬p, from hnpnnp.left,
    show false, from hnp hp
end dne_test

section de_morgan
  variables {p q: Prop}

  theorem de_morgan_1_a (hnpnq: ¬p ∨ ¬q): ¬(p ∧ q) :=
    assume (hpq: p ∧ q),
    or.elim hnpnq
      (assume hnp: ¬p,
        show false, from hnp hpq.left)
      (assume hnq: ¬q,
        show false, from hnq hpq.right)

  -- mathlib's proof, using equation compiler
  theorem de_morgan_1_a' (hnpnq: ¬p ∨ ¬q): ¬(p ∧ q)
    | ⟨hp, hq⟩ := or.elim hnpnq (absurd hp) (absurd hq)

  theorem de_morgan_1: ¬(p ∧ q) ↔ ¬p ∨ ¬q :=
    -- no longer annpoying, using implicit variables
    iff.intro de_morgan_1_b de_morgan_1_a
  #check de_morgan_1
end de_morgan

-- why is the type Prop "below" normal types? that is, we have
#check ℕ -- Type
#check 1 -- ℕ

#check Prop -- Type
constant p: Prop
#check p -- Prop
constant h: p
#check h -- p <- it's as if "1" was a type
-- kinda answered in section 4.1 of TPIL


/-
Copyright (c) 2021 Yury Kudryashov. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Yury Kudryashov
-/
import topology.urysohns_lemma
import topology.continuous_function.bounded

/-!
# Metrizability of a normal topological space with second countable topology

In this file we show that a normal topological space with second countable topology `X` is
metrizable: there exists a metric space structure that generates the same topology.

First we prove that `X` can be embedded into `l^∞`, then use this embedding to pull back the metric
space structure.
-/

open set filter metric
open_locale bounded_continuous_function filter topological_space

namespace topological_space

variables (X : Type*) [topological_space X] [normal_space X] [second_countable_topology X]

/-- A normal topological space with second countable topology can be embedded into `l^∞ = ℕ →ᵇ ℝ`.
-/
lemma exists_embedding_l_infty : ∃ f : X → (ℕ →ᵇ ℝ), embedding f :=
begin
  -- Choose a countable basis, and consider the set `s` of pairs of set `(U, V)` such that `U ∈ B`,
  -- `V ∈ B`, and `closure U ⊆ V`.
  rcases exists_countable_basis X with ⟨B, hBc, -, hB⟩,
  set s : set (set X × set X) := {UV ∈ B ×ˢ B| closure UV.1 ⊆ UV.2},
  -- `s` is a countable set.
  haveI : encodable s := ((hBc.prod hBc).mono (inter_subset_left _ _)).to_encodable,
  -- We don't have the space of bounded (possibly discontinuous) functions, so we equip `s`
  -- with the discrete topology and deal with `s →ᵇ ℝ` instead.
  letI : topological_space s := ⊥, haveI : discrete_topology s := ⟨rfl⟩,
  suffices : ∃ f : X → (s →ᵇ ℝ), embedding f,
  { rcases this with ⟨f, hf⟩,
    exact ⟨λ x, (f x).extend (encodable.encode' s) 0, (bounded_continuous_function.isometry_extend
      (encodable.encode' s) (0 : ℕ →ᵇ ℝ)).embedding.comp hf⟩ },
  have hd : ∀ UV : s, disjoint (closure UV.1.1) (UV.1.2ᶜ) :=
    λ UV, disjoint_compl_right.mono_right (compl_subset_compl.2 UV.2.2),
  -- Choose a sequence of `εₙ > 0`, `n : s`, that is bounded above by `1` and tends to zero
  -- along the `cofinite` filter.
  obtain ⟨ε, ε01, hε⟩ : ∃ ε : s → ℝ, (∀ UV, ε UV ∈ Ioc (0 : ℝ) 1) ∧ tendsto ε cofinite (𝓝 0),
  { rcases pos_sum_of_encodable zero_lt_one s with ⟨ε, ε0, c, hεc, hc1⟩,
    refine ⟨ε, λ UV, ⟨ε0 UV, _⟩, hεc.summable.tendsto_cofinite_zero⟩,
    exact (le_has_sum hεc UV $ λ _ _, (ε0 _).le).trans hc1 },
  /- For each `UV = (U, V) ∈ s` we use Urysohn's lemma to choose a function `f UV` that is equal to
  zero on `U` and is equal to `ε UV` on the complement to `V`. -/
  have : ∀ UV : s, ∃ f : C(X, ℝ), eq_on f 0 UV.1.1 ∧ eq_on f (λ _, ε UV) UV.1.2ᶜ ∧
    ∀ x, f x ∈ Icc 0 (ε UV),
  { intro UV,
    rcases exists_continuous_zero_one_of_closed is_closed_closure
      (hB.is_open UV.2.1.2).is_closed_compl (hd UV) with ⟨f, hf₀, hf₁, hf01⟩,
    exact ⟨ε UV • f, λ x hx, by simp [hf₀ (subset_closure hx)], λ x hx, by simp [hf₁ hx],
      λ x, ⟨mul_nonneg (ε01 _).1.le (hf01 _).1, mul_le_of_le_one_right (ε01 _).1.le (hf01 _).2⟩⟩ },
  choose f hf0 hfε hf0ε,
  have hf01 : ∀ UV x, f UV x ∈ Icc (0 : ℝ) 1,
    from λ UV x, Icc_subset_Icc_right (ε01 _).2 (hf0ε _ _),
  /- The embedding is given by `F x UV = f UV x`. -/
  set F : X → s →ᵇ ℝ := λ x, ⟨⟨λ UV, f UV x, continuous_of_discrete_topology⟩, 1, λ UV₁ UV₂,
    real.dist_le_of_mem_Icc_01 (hf01 _ _) (hf01 _ _)⟩,
  have hF : ∀ x UV, F x UV = f UV x := λ _ _, rfl,
  refine ⟨F, embedding.mk' _ (λ x y hxy, _) (λ x, le_antisymm _ _)⟩,
  { /- First we prove that `F` is injective. Indeed, if `F x = F y` and `x ≠ y`, then we can find
    `(U, V) ∈ s` such that `x ∈ U` and `y ∉ V`, hence `F x UV = 0 ≠ ε UV = F y UV`. -/
    refine not_not.1 (λ Hne, _), -- `by_contra Hne` timeouts
    rcases hB.mem_nhds_iff.1 (is_open_ne.mem_nhds Hne) with ⟨V, hVB, hxV, hVy⟩,
    rcases hB.exists_closure_subset (hB.mem_nhds hVB hxV) with ⟨U, hUB, hxU, hUV⟩,
    set UV : ↥s := ⟨(U, V), ⟨hUB, hVB⟩, hUV⟩,
    apply (ε01 UV).1.ne,
    calc (0 : ℝ) = F x UV : (hf0 UV hxU).symm
             ... = F y UV : by rw hxy
             ... = ε UV   : hfε UV (λ h : y ∈ V, hVy h rfl) },
  { /- Now we prove that each neighborhood `V` of `x : X` include a preimage of a neighborhood of
    `F x` under `F`. Without loss of generality, `V` belongs to `B`. Choose `U ∈ B` such that
    `x ∈ V` and `closure V ⊆ U`. Then the preimage of the `(ε (U, V))`-neighborhood of `F x`
    is included by `V`. -/
    refine ((nhds_basis_ball.comap _).le_basis_iff hB.nhds_has_basis).2 _,
    rintro V ⟨hVB, hxV⟩,
    rcases hB.exists_closure_subset (hB.mem_nhds hVB hxV) with ⟨U, hUB, hxU, hUV⟩,
    set UV : ↥s := ⟨(U, V), ⟨hUB, hVB⟩, hUV⟩,
    refine ⟨ε UV, (ε01 UV).1, λ y (hy : dist (F y) (F x) < ε UV), _⟩,
    replace hy : dist (F y UV) (F x UV) < ε UV,
      from (bounded_continuous_function.dist_coe_le_dist _).trans_lt hy,
    contrapose! hy,
    rw [hF, hF, hfε UV hy, hf0 UV hxU, pi.zero_apply, dist_zero_right],
    exact le_abs_self _ },
  { /- Finally, we prove that `F` is continuous. Given `δ > 0`, consider the set `T` of `(U, V) ∈ s`
    such that `ε (U, V) ≥ δ`. Since `ε` tends to zero, `T` is finite. Since each `f` is continuous,
    we can choose a neighborhood such that `dist (F y (U, V)) (F x (U, V)) ≤ δ` for any
    `(U, V) ∈ T`. For `(U, V) ∉ T`, the same inequality is true because both `F y (U, V)` and
    `F x (U, V)` belong to the interval `[0, ε (U, V)]`. -/
    refine (nhds_basis_closed_ball.comap _).ge_iff.2 (λ δ δ0, _),
    have h_fin : finite {UV : s | δ ≤ ε UV}, by simpa only [← not_lt] using hε (gt_mem_nhds δ0),
    have : ∀ᶠ y in 𝓝 x, ∀ UV, δ ≤ ε UV → dist (F y UV) (F x UV) ≤ δ,
    { refine (eventually_all_finite h_fin).2 (λ UV hUV, _),
      exact (f UV).continuous.tendsto x (closed_ball_mem_nhds _ δ0) },
    refine this.mono (λ y hy, (bounded_continuous_function.dist_le δ0.le).2 $ λ UV, _),
    cases le_total δ (ε UV) with hle hle,
    exacts [hy _ hle, (real.dist_le_of_mem_Icc (hf0ε _ _) (hf0ε _ _)).trans (by rwa sub_zero)] }
end

/-- A normal topological space with second countable topology `X` is metrizable: there exists a
metric space structure that generates the same topology. This definition provides a `metric_space`
instance such that the corresponding `topological_space X` instance is definitionally equal
to the original one. -/
@[reducible] noncomputable def to_metric_space : metric_space X :=
@metric_space.replace_uniformity X
  ((uniform_space.comap (exists_embedding_l_infty X).some infer_instance).replace_topology
    (exists_embedding_l_infty X).some_spec.induced)
  (metric_space.induced (exists_embedding_l_infty X).some
    (exists_embedding_l_infty X).some_spec.inj infer_instance)
  rfl

end topological_space


/-!

# Hilbert basis theorem

All rings are commutative and have a 1; all morphisms of rings send 1 to 1.

A commutative ring is said to be *Noetherian* if all ideals
are finitely generated. This turns out to be an extremely
important finiteness condition for rings. It is named after
the German mathematician Emmy Noether.

The Hilbert Basis Theorem says that if `R` is
a Noetherian ring, then so is the ring `R[X]` of polynomials
in one variable over `R`. Here is how to state the Hilbert
basis theorem in Lean:

```lean
theorem Hilbert_Basis_Theorem
  (R : Type) [comm_ring R] (hR : is_noetherian_ring R) :
  is_noetherian_ring (polynomial R) := ...
```

Here's a paper proof.

We start with a definition. For R a ring and n a natural number, let Mₙ denote the polynomials in R[X] of
degree at most n. It's clearly an R-submodule, and if j is a natural number then projection prⱼ onto the coefficient
of Xᶨ is an R-module homomorphism R[X] → R and hence Mₙ → R.

 Assume R is a Noetherian ring and let I be an ideal of R[X]. We want
 to prove that I is finitely-generated.

  Define J_n to be the ideal prₙ (Mₙ ∩ I)
  Clearly the J_n are an increasing sequence of ideals (use multiplication by X).
  Let J be their union.

    J is a finitely generated R-mod, generated by {j₁, j₂,…jₙ}
    choose hᵢ ∈ I representing jᵢ
    Let N be max of the degrees of the hᵢ, so J=J_N.
    Now here is a finite set S of generators for I.
    It's the obviously finite union of the following things
    * h's corresponding to generators of
    all the J_n for n ≤ N.
    * The hᵢ from above
    Proof:
    Say f is in J
    By strong Induction on deg(f) it suffices to prove that for
    every non-zero poly f in I, there exists g in the ideal (S)
    such that f-g has smaller degree than f
    So say f is non-zero. Two cases.
    1) deg(f) = d ≤ N
    Then the h's corresponding to J_d will do the job.
    2) deg(f) =d > N
    Then the leading coefficient of f is in J=J_N
    and we can use X^{d-N}
-/


import data.set
import data.int.basic

def odd : ℤ → Prop := λ n, ∃ m, n = 2 * m + 1
def even : ℤ → Prop := λ n, ∃ m, n = 2 * m

theorem even_or_odd : ∀ n, (even n ∨ odd n) :=
begin
    assume n,
    apply @int.induction_on (λ n, even n ∨ odd n),
        left, exact ⟨0, rfl⟩,
        assume k,
        assume h,
        cases h,
            right,
            apply exists.elim h,
                assume m,
                assume f,
                rw f,
                exact ⟨m, rfl⟩,

            left,
            apply exists.elim h,
                assume m,
                assume f,
                rw f,
                apply exists.intro (m + 1),
                    calc
                        2 * m + 1 + 1 = 2 * m + (1 + 1) : by simp
                        ... = 2 * m + 2 * 1 : by trivial
                        ... = 2 * (m + 1) : by rw mul_add,

        assume k h,
        cases h,
            right,
            apply exists.elim h,
                assume m,
                assume f,
                rw f,
                apply exists.intro (m - 1),
                    apply eq.symm,
                    calc
                        2 * (m - 1) + 1 = 2 * m - 2 * 1 + 1 : by rw mul_sub
                        ... = 2 * m - (2 * 1 - 1) : by simp,
            left,
            apply exists.elim h,
                assume m,
                assume f,
                rw f,
                apply exists.intro m,
                    calc
                        2 * m + 1 - 1 = 2 * m + (1 - 1) : by rw add_sub_assoc
                        ... = 2 * m : by simp,
end

example : ∀ n, ¬ (even n ∧ odd n) :=
begin
    assume n,
    apply @int.induction_on (λ n, ¬ (even n ∧ odd n)),
        assume h,
        cases h.2 with _ pf,
        cases w,
            repeat {cases pf},

        assume k h,
        assume h1,
        cases h1 with evenk oddk,
        have : even k ∧ odd k,
        split,
            apply exists.elim oddk,
                assume m eq1,
                let := congr_arg (λ n: ℤ, n - 1) eq1,
                simp at this,
                apply exists.intro m,
                    rw this,
                    simp [add_comm (2*m) (-1), add_assoc],

            apply exists.elim evenk,
                assume m,
                assume eq1,
                apply exists.intro (m - 1),
                    let := congr_arg (λ n: ℤ, n - 1) eq1,
                    simp at this,
                    rw this,
                    apply eq.symm,
                    calc
                        2 * (m - 1) + 1 = 2 * m - 2 * 1 + 1 : by rw mul_sub
                        ... = -(2-1) + 2 * m : by simp,
        contradiction,

        assume k h,
        assume h1,
        cases h1,
        have : even k ∧ odd k,
            split,
                apply exists.elim h1_right,
                    assume m eq1,
                    let := congr_arg (λ n: ℤ, n + 1) eq1,
                    simp at this,
                    apply exists.intro (m + 1),
                        rw this,
                        calc
                            1 + (1 + 2 * m) = 1 + 1 + 2 * m : by rw add_assoc
                            ... = 2 + 2 * m : by trivial
                            ... = 2 * m + 2 * 1 : by simp
                            ... = 2 * (m + 1) : by rw mul_add,

                apply exists.elim h1_left,
                    assume m eq1,
                    let := congr_arg (λ n: ℤ, n + 1) eq1,
                    simp at this,
                    have eq2 : 1 + 2 * m = 2 * m + 1 := by simp,
                    exact ⟨m, eq.trans this eq2⟩,
        contradiction,
end


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

! This file was ported from Lean 3 source module ring_theory.witt_vector.isocrystal
! leanprover-community/mathlib commit 6d584f1709bedbed9175bd9350df46599bdd7213
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.RingTheory.WittVector.FrobeniusFractionField

/-!

## F-isocrystals over a perfect field

When `k` is an integral domain, so is `𝕎 k`, and we can consider its field of fractions `K(p, k)`.
The endomorphism `witt_vector.frobenius` lifts to `φ : K(p, k) → K(p, k)`; if `k` is perfect, `φ` is
an automorphism.

Let `k` be a perfect integral domain. Let `V` be a vector space over `K(p,k)`.
An *isocrystal* is a bijective map `V → V` that is `φ`-semilinear.
A theorem of Dieudonné and Manin classifies the finite-dimensional isocrystals over algebraically
closed fields. In the one-dimensional case, this classification states that the isocrystal
structures are parametrized by their "slope" `m : ℤ`.
Any one-dimensional isocrystal is isomorphic to `φ(p^m • x) : K(p,k) → K(p,k)` for some `m`.

This file proves this one-dimensional case of the classification theorem.
The construction is described in Dupuis, Lewis, and Macbeth,
[Formalized functional analysis via semilinear maps][dupuis-lewis-macbeth2022].

## Main declarations

* `witt_vector.isocrystal`: a vector space over the field `K(p, k)` additionally equipped with a
  Frobenius-linear automorphism.
* `witt_vector.isocrystal_classification`: a one-dimensional isocrystal admits an isomorphism to one
  of the standard one-dimensional isocrystals.

## Notation

This file introduces notation in the locale `isocrystal`.
* `K(p, k)`: `fraction_ring (witt_vector p k)`
* `φ(p, k)`: `witt_vector.fraction_ring.frobenius_ring_hom p k`
* `M →ᶠˡ[p, k] M₂`: `linear_map (witt_vector.fraction_ring.frobenius_ring_hom p k) M M₂`
* `M ≃ᶠˡ[p, k] M₂`: `linear_equiv (witt_vector.fraction_ring.frobenius_ring_hom p k) M M₂`
* `Φ(p, k)`: `witt_vector.isocrystal.frobenius p k`
* `M →ᶠⁱ[p, k] M₂`: `witt_vector.isocrystal_hom p k M M₂`
* `M ≃ᶠⁱ[p, k] M₂`: `witt_vector.isocrystal_equiv p k M M₂`

## References

* [Formalized functional analysis via semilinear maps][dupuis-lewis-macbeth2022]
* [Theory of commutative formal groups over fields of finite characteristic][manin1963]
* <https://www.math.ias.edu/~lurie/205notes/Lecture26-Isocrystals.pdf>

-/

noncomputable section

open FiniteDimensional

namespace WittVector

variable (p : ℕ) [Fact p.Prime]

variable (k : Type _) [CommRing k]

-- mathport name: witt_vector.fraction_ring
scoped[Isocrystal] notation "K(" p ", " k ")" => FractionRing (WittVector p k)

section PerfectRing

variable [IsDomain k] [CharP k p] [PerfectRing k p]

/-! ### Frobenius-linear maps -/

/-- The Frobenius automorphism of `k` induces an automorphism of `K`. -/
def FractionRing.frobenius : K(p, k) ≃+* K(p, k) :=
  IsFractionRing.fieldEquivOfRingEquiv (frobeniusEquiv p k)
#align witt_vector.fraction_ring.frobenius WittVector.FractionRing.frobenius

/-- The Frobenius automorphism of `k` induces an endomorphism of `K`. For notation purposes. -/
def FractionRing.frobeniusRingHom : K(p, k) →+* K(p, k) :=
  FractionRing.frobenius p k
#align witt_vector.fraction_ring.frobenius_ring_hom WittVector.FractionRing.frobeniusRingHom

-- mathport name: witt_vector.frobenius_ring_hom
scoped[Isocrystal] notation "φ(" p ", " k ")" => WittVector.FractionRing.frobeniusRingHom p k

instance inv_pair₁ : RingHomInvPair φ(p, k) _ :=
  RingHomInvPair.of_ringEquiv (FractionRing.frobenius p k)
#align witt_vector.inv_pair₁ WittVector.inv_pair₁

instance inv_pair₂ : RingHomInvPair ((FractionRing.frobenius p k).symm : K(p, k) →+* K(p, k)) _ :=
  RingHomInvPair.of_ringEquiv (FractionRing.frobenius p k).symm
#align witt_vector.inv_pair₂ WittVector.inv_pair₂

-- mathport name: frobenius_ring_hom.linear_map
scoped[Isocrystal]
  notation:50 M " →ᶠˡ[" p ", " k "] " M₂ =>
    LinearMap (WittVector.FractionRing.frobeniusRingHom p k) M M₂

-- mathport name: frobenius_ring_hom.linear_equiv
scoped[Isocrystal]
  notation:50 M " ≃ᶠˡ[" p ", " k "] " M₂ =>
    LinearEquiv (WittVector.FractionRing.frobeniusRingHom p k) M M₂

/-! ### Isocrystals -/

/-- An isocrystal is a vector space over the field `K(p, k)` additionally equipped with a
Frobenius-linear automorphism.
-/
class Isocrystal (V : Type _) [AddCommGroup V] extends Module K(p, k) V where
  frob : V ≃ᶠˡ[p, k] V
#align witt_vector.isocrystal WittVector.Isocrystal

variable (V : Type _) [AddCommGroup V] [Isocrystal p k V]

variable (V₂ : Type _) [AddCommGroup V₂] [Isocrystal p k V₂]

variable {V}

/--
Project the Frobenius automorphism from an isocrystal. Denoted by `Φ(p, k)` when V can be inferred.
-/
def Isocrystal.frobenius : V ≃ᶠˡ[p, k] V :=
  @Isocrystal.frob p _ k _ _ _ _ _ _ _
#align witt_vector.isocrystal.frobenius WittVector.Isocrystal.frobenius

variable (V)

-- mathport name: «exprΦ( , )»
scoped[Isocrystal] notation "Φ(" p ", " k ")" => WittVector.Isocrystal.frobenius p k

/-- A homomorphism between isocrystals respects the Frobenius map. -/
@[nolint has_nonempty_instance]
structure IsocrystalHom extends V →ₗ[K(p, k)] V₂ where
  frob_equivariant : ∀ x : V, Φ(p, k) (to_linear_map x) = to_linear_map (Φ(p, k) x)
#align witt_vector.isocrystal_hom WittVector.IsocrystalHom

/-- An isomorphism between isocrystals respects the Frobenius map. -/
@[nolint has_nonempty_instance]
structure IsocrystalEquiv extends V ≃ₗ[K(p, k)] V₂ where
  frob_equivariant : ∀ x : V, Φ(p, k) (to_linear_equiv x) = to_linear_equiv (Φ(p, k) x)
#align witt_vector.isocrystal_equiv WittVector.IsocrystalEquiv

-- mathport name: isocrystal_hom
scoped[Isocrystal] notation:50 M " →ᶠⁱ[" p ", " k "] " M₂ => WittVector.IsocrystalHom p k M M₂

-- mathport name: isocrystal_equiv
scoped[Isocrystal] notation:50 M " ≃ᶠⁱ[" p ", " k "] " M₂ => WittVector.IsocrystalEquiv p k M M₂

end PerfectRing

open Isocrystal

/-! ### Classification of isocrystals in dimension 1 -/

/-- A helper instance for type class inference. -/
@[local instance]
def FractionRing.module : Module K(p, k) K(p, k) :=
  Semiring.toModule
#align witt_vector.fraction_ring.module WittVector.FractionRing.module

/- ./././Mathport/Syntax/Translate/Command.lean:42:9: unsupported derive handler module[module] witt_vector.fraction_ring(p, k) -/
/-- Type synonym for `K(p, k)` to carry the standard 1-dimensional isocrystal structure
of slope `m : ℤ`.
-/
@[nolint unused_arguments has_nonempty_instance]
def StandardOneDimIsocrystal (m : ℤ) : Type _ :=
  K(p, k)deriving AddCommGroup,
  «./././Mathport/Syntax/Translate/Command.lean:42:9: unsupported derive handler module[module] witt_vector.fraction_ring(p, k)»
#align witt_vector.standard_one_dim_isocrystal WittVector.StandardOneDimIsocrystal

section PerfectRing

variable [IsDomain k] [CharP k p] [PerfectRing k p]

/-- The standard one-dimensional isocrystal of slope `m : ℤ` is an isocrystal. -/
instance (m : ℤ) : Isocrystal p k (StandardOneDimIsocrystal p k m)
    where frob :=
    (FractionRing.frobenius p k).toSemilinearEquiv.trans
      (LinearEquiv.smulOfNeZero _ _ _ (zpow_ne_zero m (WittVector.FractionRing.p_nonzero p k)))

@[simp]
theorem StandardOneDimIsocrystal.frobenius_apply (m : ℤ) (x : StandardOneDimIsocrystal p k m) :
    Φ(p, k) x = (p : K(p, k)) ^ m • φ(p, k) x :=
  rfl
#align witt_vector.standard_one_dim_isocrystal.frobenius_apply WittVector.StandardOneDimIsocrystal.frobenius_apply

end PerfectRing

/-- A one-dimensional isocrystal over an algebraically closed field
admits an isomorphism to one of the standard (indexed by `m : ℤ`) one-dimensional isocrystals. -/
theorem isocrystal_classification (k : Type _) [Field k] [IsAlgClosed k] [CharP k p] (V : Type _)
    [AddCommGroup V] [Isocrystal p k V] (h_dim : finrank K(p, k) V = 1) :
    ∃ m : ℤ, Nonempty (StandardOneDimIsocrystal p k m ≃ᶠⁱ[p, k] V) :=
  by
  haveI : Nontrivial V := FiniteDimensional.nontrivial_of_finrank_eq_succ h_dim
  obtain ⟨x, hx⟩ : ∃ x : V, x ≠ 0 := exists_ne 0
  have : Φ(p, k) x ≠ 0 := by simpa only [map_zero] using Φ(p, k).Injective.Ne hx
  obtain ⟨a, ha, hax⟩ : ∃ a : K(p, k), a ≠ 0 ∧ Φ(p, k) x = a • x :=
    by
    rw [finrank_eq_one_iff_of_nonzero' x hx] at h_dim
    obtain ⟨a, ha⟩ := h_dim (Φ(p, k) x)
    refine' ⟨a, _, ha.symm⟩
    intro ha'
    apply this
    simp only [← ha, ha', zero_smul]
  obtain ⟨b, hb, m, hmb⟩ := WittVector.exists_frobenius_solution_fractionRing p ha
  replace hmb : φ(p, k) b * a = p ^ m * b := by convert hmb
  use m
  let F₀ : standard_one_dim_isocrystal p k m →ₗ[K(p, k)] V := LinearMap.toSpanSingleton K(p, k) V x
  let F : standard_one_dim_isocrystal p k m ≃ₗ[K(p, k)] V :=
    by
    refine' LinearEquiv.ofBijective F₀ ⟨_, _⟩
    · rw [← LinearMap.ker_eq_bot]
      exact LinearMap.ker_toSpanSingleton K(p, k) V hx
    · rw [← LinearMap.range_eq_top]
      rw [← (finrank_eq_one_iff_of_nonzero x hx).mp h_dim]
      rw [LinearMap.span_singleton_eq_range]
  refine' ⟨⟨(LinearEquiv.smulOfNeZero K(p, k) _ _ hb).trans F, _⟩⟩
  intro c
  rw [LinearEquiv.trans_apply, LinearEquiv.trans_apply, LinearEquiv.smulOfNeZero_apply,
    LinearEquiv.smulOfNeZero_apply, LinearEquiv.map_smul, LinearEquiv.map_smul]
  simp only [hax, LinearEquiv.ofBijective_apply, LinearMap.toSpanSingleton_apply,
    LinearEquiv.map_smulₛₗ, standard_one_dim_isocrystal.frobenius_apply, Algebra.id.smul_eq_mul]
  simp only [← mul_smul]
  congr 1
  linear_combination φ(p, k) c * hmb
#align witt_vector.isocrystal_classification WittVector.isocrystal_classification

end WittVector


import analysis.special_functions.exp_log analysis.special_functions.pow

open_locale big_operators nat
noncomputable theory
open complex

def poch (a : ℂ) (n : ℕ) := ∏ i in finset.range n, a + n

def rΓ_series (z : ℂ) (n : ℕ) := poch z (n + 1) / (n! * exp (z * log n))

def Γ_series (z : ℂ) (n : ℕ) := (n! : ℂ) * exp(z * log n) / poch z (n + 1)
def Γ_series' (z : ℂ) (n : ℕ) := ((n - 1)! : ℂ) * exp (z * log n) / poch z n

lemma rΓ_eq_inv_Γ (z : ℂ) (n : ℕ) : Γ_series z n = (rΓ_series z n)⁻¹ := by field_simp [rΓ_series, Γ_series]

def ln_Gamma_series (z : ℂ) (n : ℕ) := z * log (n) - log z - ∑ k in finset.range n, log(z / (k+1) + 1)

lemma lnG_def (z : ℂ) (n : ℕ) : ln_Gamma_series z n = z * log (n) - log z - ∑ k in finset.range n, log(z / (k+1) + 1) := rfl

def ln_Gamma (x : ℝ) := x

-- let us asssume convergence, because I don't know how to prove it.
lemma ln_Γ_complex_converges (z : ℂ) (n : ℕ) : filter.tendsto (ln_Gamma_series z) (filter.at_top) (nhds z) :=
begin

  sorry
end


theorem succ_ne_zero (a : ℕ) : nat.succ a ≠ 0 :=
begin
    symmetry,
    exact nat.zero_ne_succ a,
end