/-
Copyright (c) 2020 Kexing Ying. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kexing Ying
-/
import Mathlib.PrePort
import Mathlib.Lean3Lib.init.default
import Mathlib.group_theory.submonoid.default
import Mathlib.algebra.group.conj
import Mathlib.order.modular_lattice
import Mathlib.PostPort

universes u_3 l u_1 u_2 u_4 u_5

namespace Mathlib

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

## Implementation notes

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

## Tags
subgroup, subgroups
-/

/-- A subgroup of a group `G` is a subset containing 1, closed under multiplication
and closed under multiplicative inverse. -/
structure subgroup (G : Type u_3) [group G]
extends submonoid G
where
  inv_mem' : ∀ {x : G}, 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 u_3) [add_group G]
extends add_submonoid G
where
  neg_mem' : ∀ {x : G}, x ∈ carrier → -x ∈ carrier

/-- Reinterpret a `subgroup` as a `submonoid`. -/
/-- Reinterpret an `add_subgroup` as an `add_submonoid`. -/
/-- Map from subgroups of group `G` to `add_subgroup`s of `additive G`. -/
def subgroup.to_add_subgroup {G : Type u_1} [group G] (H : subgroup G) : add_subgroup (additive G) :=
  add_subgroup.mk (add_submonoid.carrier (submonoid.to_add_submonoid (subgroup.to_submonoid H))) sorry sorry
    (subgroup.inv_mem' H)

/-- Map from `add_subgroup`s of `additive G` to subgroups of `G`. -/
def subgroup.of_add_subgroup {G : Type u_1} [group G] (H : add_subgroup (additive G)) : subgroup G :=
  subgroup.mk (submonoid.carrier (submonoid.of_add_submonoid (add_subgroup.to_add_submonoid H))) sorry sorry sorry

/-- Map from `add_subgroup`s of `add_group G` to subgroups of `multiplicative G`. -/
def add_subgroup.to_subgroup {G : Type u_1} [add_group G] (H : add_subgroup G) : subgroup (multiplicative G) :=
  subgroup.mk (submonoid.carrier (add_submonoid.to_submonoid (add_subgroup.to_add_submonoid H))) sorry sorry
    (add_subgroup.neg_mem' H)

/-- Map from subgroups of `multiplicative G` to `add_subgroup`s of `add_group G`. -/
def add_subgroup.of_subgroup {G : Type u_1} [add_group G] (H : subgroup (multiplicative G)) : add_subgroup G :=
  add_subgroup.mk (add_submonoid.carrier (add_submonoid.of_submonoid (subgroup.to_submonoid H))) sorry sorry sorry

/-- Subgroups of group `G` are isomorphic to additive subgroups of `additive G`. -/
def subgroup.add_subgroup_equiv (G : Type u_1) [group G] : subgroup G ≃ add_subgroup (additive G) :=
  equiv.mk subgroup.to_add_subgroup subgroup.of_add_subgroup sorry sorry

namespace subgroup

protected instance set.has_coe {G : Type u_1} [group G] : has_coe (subgroup G) (set G) :=
  has_coe.mk carrier

@[simp] theorem coe_to_submonoid {G : Type u_1} [group G] (K : subgroup G) : ↑(to_submonoid K) = ↑K :=
  rfl

protected instance has_mem {G : Type u_1} [group G] : has_mem G (subgroup G) :=
  has_mem.mk fun (m : G) (K : subgroup G) => m ∈ ↑K

protected instance Mathlib.add_subgroup.has_coe_to_sort {G : Type u_1} [add_group G] : has_coe_to_sort (add_subgroup G) :=
  has_coe_to_sort.mk (has_coe_to_sort.S (add_subgroup G)) fun (G_1 : add_subgroup G) => ↥G_1

@[simp] theorem Mathlib.add_subgroup.mem_coe {G : Type u_1} [add_group G] {K : add_subgroup G} {g : G} : g ∈ ↑K ↔ g ∈ K :=
  iff.rfl

@[simp] theorem Mathlib.add_subgroup.coe_coe {G : Type u_1} [add_group G] (K : add_subgroup G) : ↥↑K = ↥K :=
  rfl

-- note that `to_additive` transfers the `simp` attribute over but not the `norm_cast` attribute

protected instance Mathlib.add_subgroup.fintype {G : Type u_1} [add_group G] (K : add_subgroup G) [d : decidable_pred (add_subgroup.carrier K)] [fintype G] : fintype ↥K :=
  (fun (this : fintype (Subtype fun (g : G) => g ∈ add_subgroup.carrier K)) => this) infer_instance

end subgroup

protected theorem add_subgroup.exists {G : Type u_1} [add_group G] {K : add_subgroup G} {p : ↥K → Prop} : (∃ (x : ↥K), p x) ↔ ∃ (x : G), ∃ (H : x ∈ K), p { val := x, property := H } :=
  set_coe.exists

protected theorem subgroup.forall {G : Type u_1} [group G] {K : subgroup G} {p : ↥K → Prop} : (∀ (x : ↥K), p x) ↔ ∀ (x : G) (H : x ∈ K), p { val := x, property := H } :=
  set_coe.forall

namespace subgroup

/-- Copy of a subgroup with a new `carrier` equal to the old one. Useful to fix definitional
equalities.-/
protected def Mathlib.add_subgroup.copy {G : Type u_1} [add_group G] (K : add_subgroup G) (s : set G) (hs : s = ↑K) : add_subgroup G :=
  add_subgroup.mk s sorry sorry sorry

/- Two subgroups are equal if the underlying set are the same. -/

theorem Mathlib.add_subgroup.ext' {G : Type u_1} [add_group G] {H : add_subgroup G} {K : add_subgroup G} (h : ↑H = ↑K) : H = K := sorry

/- Two subgroups are equal if and only if the underlying subsets are equal. -/

protected theorem Mathlib.add_subgroup.ext'_iff {G : Type u_1} [add_group G] {H : add_subgroup G} {K : add_subgroup G} : H = K ↔ ↑H = ↑K :=
  { mp := fun (h : H = K) => h ▸ rfl, mpr := add_subgroup.ext' }

/-- Two subgroups are equal if they have the same elements. -/
theorem ext {G : Type u_1} [group G] {H : subgroup G} {K : subgroup G} (h : ∀ (x : G), x ∈ H ↔ x ∈ K) : H = K :=
  ext' (set.ext h)

/-- A subgroup contains the group's 1. -/
theorem one_mem {G : Type u_1} [group G] (H : subgroup G) : 1 ∈ H :=
  one_mem' H

/-- A subgroup is closed under multiplication. -/
theorem Mathlib.add_subgroup.add_mem {G : Type u_1} [add_group G] (H : add_subgroup G) {x : G} {y : G} : x ∈ H → y ∈ H → x + y ∈ H :=
  fun (hx : x ∈ H) (hy : y ∈ H) => add_subgroup.add_mem' H hx hy

/-- A subgroup is closed under inverse. -/
theorem inv_mem {G : Type u_1} [group G] (H : subgroup G) {x : G} : x ∈ H → x⁻¹ ∈ H :=
  fun (hx : x ∈ H) => inv_mem' H hx

/-- A subgroup is closed under division. -/
theorem Mathlib.add_subgroup.sub_mem {G : Type u_1} [add_group G] (H : add_subgroup G) {x : G} {y : G} (hx : x ∈ H) (hy : y ∈ H) : x - y ∈ H := sorry

@[simp] theorem Mathlib.add_subgroup.neg_mem_iff {G : Type u_1} [add_group G] (H : add_subgroup G) {x : G} : -x ∈ H ↔ x ∈ H :=
  { mp := fun (h : -x ∈ H) => neg_neg x ▸ add_subgroup.neg_mem H h, mpr := add_subgroup.neg_mem H }

theorem Mathlib.add_subgroup.add_mem_cancel_right {G : Type u_1} [add_group G] (H : add_subgroup G) {x : G} {y : G} (h : x ∈ H) : y + x ∈ H ↔ y ∈ H := sorry

theorem Mathlib.add_subgroup.add_mem_cancel_left {G : Type u_1} [add_group G] (H : add_subgroup G) {x : G} {y : G} (h : x ∈ H) : x + y ∈ H ↔ y ∈ H := sorry

/-- Product of a list of elements in a subgroup is in the subgroup. -/
theorem list_prod_mem {G : Type u_1} [group G] (K : subgroup G) {l : List G} : (∀ (x : G), x ∈ l → x ∈ K) → list.prod l ∈ K :=
  submonoid.list_prod_mem (to_submonoid K)

/-- Product of a multiset of elements in a subgroup of a `comm_group` is in the subgroup. -/
theorem multiset_prod_mem {G : Type u_1} [comm_group G] (K : subgroup G) (g : multiset G) : (∀ (a : G), a ∈ g → a ∈ K) → multiset.prod g ∈ K :=
  submonoid.multiset_prod_mem (to_submonoid K) g

/-- Product of elements of a subgroup of a `comm_group` indexed by a `finset` is in the
    subgroup. -/
theorem Mathlib.add_subgroup.sum_mem {G : Type u_1} [add_comm_group G] (K : add_subgroup G) {ι : Type u_2} {t : finset ι} {f : ι → G} (h : ∀ (c : ι), c ∈ t → f c ∈ K) : (finset.sum t fun (c : ι) => f c) ∈ K :=
  add_submonoid.sum_mem (add_subgroup.to_add_submonoid K) h

theorem pow_mem {G : Type u_1} [group G] (K : subgroup G) {x : G} (hx : x ∈ K) (n : ℕ) : x ^ n ∈ K :=
  submonoid.pow_mem (to_submonoid K) hx

theorem gpow_mem {G : Type u_1} [group G] (K : subgroup G) {x : G} (hx : x ∈ K) (n : ℤ) : x ^ n ∈ K :=
  int.cases_on n (fun (n : ℕ) => idRhs (x ^ n ∈ K) (pow_mem K hx n))
    fun (n : ℕ) => idRhs (x ^ Nat.succ n⁻¹ ∈ K) (inv_mem K (pow_mem K hx (Nat.succ n)))

/-- Construct a subgroup from a nonempty set that is closed under division. -/
def Mathlib.add_subgroup.of_sub {G : Type u_1} [add_group G] (s : set G) (hsn : set.nonempty s) (hs : ∀ (x y : G), x ∈ s → y ∈ s → x + -y ∈ s) : add_subgroup G :=
  (fun (one_mem : 0 ∈ s) => (fun (inv_mem : ∀ (x : G), x ∈ s → -x ∈ s) => add_subgroup.mk s one_mem sorry inv_mem) sorry)
    sorry

/-- A subgroup of a group inherits a multiplication. -/
protected instance Mathlib.add_subgroup.has_add {G : Type u_1} [add_group G] (H : add_subgroup G) : Add ↥H :=
  add_submonoid.has_add (add_subgroup.to_add_submonoid H)

/-- A subgroup of a group inherits a 1. -/
protected instance Mathlib.add_subgroup.has_zero {G : Type u_1} [add_group G] (H : add_subgroup G) : HasZero ↥H :=
  add_submonoid.has_zero (add_subgroup.to_add_submonoid H)

/-- A subgroup of a group inherits an inverse. -/
protected instance Mathlib.add_subgroup.has_neg {G : Type u_1} [add_group G] (H : add_subgroup G) : Neg ↥H :=
  { neg := fun (a : ↥H) => { val := -↑a, property := sorry } }

/-- A subgroup of a group inherits a division -/
protected instance has_div {G : Type u_1} [group G] (H : subgroup G) : Div ↥H :=
  { div := fun (a b : ↥H) => { val := ↑a / ↑b, property := sorry } }

@[simp] theorem Mathlib.add_subgroup.coe_add {G : Type u_1} [add_group G] (H : add_subgroup G) (x : ↥H) (y : ↥H) : ↑(x + y) = ↑x + ↑y :=
  rfl

@[simp] theorem Mathlib.add_subgroup.coe_zero {G : Type u_1} [add_group G] (H : add_subgroup G) : ↑0 = 0 :=
  rfl

@[simp] theorem coe_inv {G : Type u_1} [group G] (H : subgroup G) (x : ↥H) : ↑(x⁻¹) = (↑x⁻¹) :=
  rfl

@[simp] theorem Mathlib.add_subgroup.coe_mk {G : Type u_1} [add_group G] (H : add_subgroup G) (x : G) (hx : x ∈ H) : ↑{ val := x, property := hx } = x :=
  rfl

/-- A subgroup of a group inherits a group structure. -/
protected instance Mathlib.add_subgroup.to_add_group {G : Type u_1} [add_group G] (H : add_subgroup G) : add_group ↥H :=
  add_group.mk add_monoid.add sorry add_monoid.zero sorry sorry Neg.neg Sub.sub sorry

/-- A subgroup of a `comm_group` is a `comm_group`. -/
protected instance Mathlib.add_subgroup.to_add_comm_group {G : Type u_1} [add_comm_group G] (H : add_subgroup G) : add_comm_group ↥H :=
  add_comm_group.mk add_group.add sorry add_group.zero sorry sorry add_group.neg add_group.sub sorry sorry

/-- The natural group hom from a subgroup of group `G` to `G`. -/
def subtype {G : Type u_1} [group G] (H : subgroup G) : ↥H →* G :=
  monoid_hom.mk coe sorry sorry

@[simp] theorem coe_subtype {G : Type u_1} [group G] (H : subgroup G) : ⇑(subtype H) = coe :=
  rfl

@[simp] theorem coe_pow {G : Type u_1} [group G] (H : subgroup G) (x : ↥H) (n : ℕ) : ↑(x ^ n) = ↑x ^ n :=
  coe_subtype H ▸ monoid_hom.map_pow (subtype H) x n

@[simp] theorem coe_gpow {G : Type u_1} [group G] (H : subgroup G) (x : ↥H) (n : ℤ) : ↑(x ^ n) = ↑x ^ n :=
  coe_subtype H ▸ monoid_hom.map_gpow (subtype H) x n

protected instance Mathlib.add_subgroup.has_le {G : Type u_1} [add_group G] : HasLessEq (add_subgroup G) :=
  { LessEq := fun (H K : add_subgroup G) => ∀ {x : G}, x ∈ H → x ∈ K }

theorem Mathlib.add_subgroup.le_def {G : Type u_1} [add_group G] {H : add_subgroup G} {K : add_subgroup G} : H ≤ K ↔ ∀ {x : G}, x ∈ H → x ∈ K :=
  iff.rfl

@[simp] theorem Mathlib.add_subgroup.coe_subset_coe {G : Type u_1} [add_group G] {H : add_subgroup G} {K : add_subgroup G} : ↑H ⊆ ↑K ↔ H ≤ K :=
  iff.rfl

protected instance partial_order {G : Type u_1} [group G] : partial_order (subgroup G) :=
  partial_order.mk LessEq partial_order.lt sorry sorry sorry

/-- The subgroup `G` of the group `G`. -/
protected instance has_top {G : Type u_1} [group G] : has_top (subgroup G) :=
  has_top.mk (mk (submonoid.carrier ⊤) sorry sorry sorry)

/-- The trivial subgroup `{1}` of an group `G`. -/
protected instance Mathlib.add_subgroup.has_bot {G : Type u_1} [add_group G] : has_bot (add_subgroup G) :=
  has_bot.mk (add_subgroup.mk (add_submonoid.carrier ⊥) sorry sorry sorry)

protected instance Mathlib.add_subgroup.inhabited {G : Type u_1} [add_group G] : Inhabited (add_subgroup G) :=
  { default := ⊥ }

@[simp] theorem mem_bot {G : Type u_1} [group G] {x : G} : x ∈ ⊥ ↔ x = 1 :=
  iff.rfl

@[simp] theorem Mathlib.add_subgroup.mem_top {G : Type u_1} [add_group G] (x : G) : x ∈ ⊤ :=
  set.mem_univ x

@[simp] theorem Mathlib.add_subgroup.coe_top {G : Type u_1} [add_group G] : ↑⊤ = set.univ :=
  rfl

@[simp] theorem coe_bot {G : Type u_1} [group G] : ↑⊥ = singleton 1 :=
  rfl

theorem Mathlib.add_subgroup.eq_bot_iff_forall {G : Type u_1} [add_group G] (H : add_subgroup G) : H = ⊥ ↔ ∀ (x : G), x ∈ H → x = 0 := sorry

theorem Mathlib.add_subgroup.eq_top_of_card_eq {G : Type u_1} [add_group G] (H : add_subgroup G) [fintype ↥H] [fintype G] (h : fintype.card ↥H = fintype.card G) : H = ⊤ := sorry

theorem nontrivial_iff_exists_ne_one {G : Type u_1} [group G] (H : subgroup G) : nontrivial ↥H ↔ ∃ (x : G), ∃ (H : x ∈ H), x ≠ 1 := sorry

/-- A subgroup is either the trivial subgroup or nontrivial. -/
theorem Mathlib.add_subgroup.bot_or_nontrivial {G : Type u_1} [add_group G] (H : add_subgroup G) : H = ⊥ ∨ nontrivial ↥H := sorry

/-- A subgroup is either the trivial subgroup or contains a nonzero element. -/
theorem bot_or_exists_ne_one {G : Type u_1} [group G] (H : subgroup G) : H = ⊥ ∨ ∃ (x : G), ∃ (H : x ∈ H), x ≠ 1 := sorry

/-- The inf of two subgroups is their intersection. -/
protected instance has_inf {G : Type u_1} [group G] : has_inf (subgroup G) :=
  has_inf.mk fun (H₁ H₂ : subgroup G) => mk (submonoid.carrier (to_submonoid H₁ ⊓ to_submonoid H₂)) sorry sorry sorry

@[simp] theorem coe_inf {G : Type u_1} [group G] (p : subgroup G) (p' : subgroup G) : ↑(p ⊓ p') = ↑p ∩ ↑p' :=
  rfl

@[simp] theorem Mathlib.add_subgroup.mem_inf {G : Type u_1} [add_group G] {p : add_subgroup G} {p' : add_subgroup G} {x : G} : x ∈ p ⊓ p' ↔ x ∈ p ∧ x ∈ p' :=
  iff.rfl

protected instance Mathlib.add_subgroup.has_Inf {G : Type u_1} [add_group G] : has_Inf (add_subgroup G) :=
  has_Inf.mk
    fun (s : set (add_subgroup G)) =>
      add_subgroup.mk
        (add_submonoid.carrier
          (add_submonoid.copy (infi fun (S : add_subgroup G) => infi fun (H : S ∈ s) => add_subgroup.to_add_submonoid S)
            (set.Inter fun (S : add_subgroup G) => set.Inter fun (H : S ∈ s) => ↑S) sorry))
        sorry sorry sorry

@[simp] theorem Mathlib.add_subgroup.coe_Inf {G : Type u_1} [add_group G] (H : set (add_subgroup G)) : ↑(Inf H) = set.Inter fun (s : add_subgroup G) => set.Inter fun (H : s ∈ H) => ↑s :=
  rfl

@[simp] theorem mem_Inf {G : Type u_1} [group G] {S : set (subgroup G)} {x : G} : x ∈ Inf S ↔ ∀ (p : subgroup G), p ∈ S → x ∈ p :=
  set.mem_bInter_iff

theorem Mathlib.add_subgroup.mem_infi {G : Type u_1} [add_group G] {ι : Sort u_2} {S : ι → add_subgroup G} {x : G} : (x ∈ infi fun (i : ι) => S i) ↔ ∀ (i : ι), x ∈ S i := sorry

@[simp] theorem Mathlib.add_subgroup.coe_infi {G : Type u_1} [add_group G] {ι : Sort u_2} {S : ι → add_subgroup G} : ↑(infi fun (i : ι) => S i) = set.Inter fun (i : ι) => ↑(S i) := sorry

/-- Subgroups of a group form a complete lattice. -/
protected instance Mathlib.add_subgroup.complete_lattice {G : Type u_1} [add_group G] : complete_lattice (add_subgroup G) :=
  complete_lattice.mk complete_lattice.sup complete_lattice.le complete_lattice.lt sorry sorry sorry sorry sorry sorry
    has_inf.inf sorry sorry sorry ⊤ sorry ⊥ sorry complete_lattice.Sup complete_lattice.Inf sorry sorry sorry sorry

theorem Mathlib.add_subgroup.mem_sup_left {G : Type u_1} [add_group G] {S : add_subgroup G} {T : add_subgroup G} {x : G} : x ∈ S → x ∈ S ⊔ T :=
  (fun (this : S ≤ S ⊔ T) => this) le_sup_left

theorem Mathlib.add_subgroup.mem_sup_right {G : Type u_1} [add_group G] {S : add_subgroup G} {T : add_subgroup G} {x : G} : x ∈ T → x ∈ S ⊔ T :=
  (fun (this : T ≤ S ⊔ T) => this) le_sup_right

theorem Mathlib.add_subgroup.mem_supr_of_mem {G : Type u_1} [add_group G] {ι : Type u_2} {S : ι → add_subgroup G} (i : ι) {x : G} : x ∈ S i → x ∈ supr S :=
  (fun (this : S i ≤ supr S) => this) (le_supr S i)

theorem Mathlib.add_subgroup.mem_Sup_of_mem {G : Type u_1} [add_group G] {S : set (add_subgroup G)} {s : add_subgroup G} (hs : s ∈ S) {x : G} : x ∈ s → x ∈ Sup S :=
  (fun (this : s ≤ Sup S) => this) (le_Sup hs)

theorem subsingleton_iff {G : Type u_1} [group G] : subsingleton G ↔ subsingleton (subgroup G) := sorry

theorem nontrivial_iff {G : Type u_1} [group G] : nontrivial G ↔ nontrivial (subgroup G) :=
  iff.mp not_iff_not
    (iff.trans (iff.trans not_nontrivial_iff_subsingleton subsingleton_iff) (iff.symm not_nontrivial_iff_subsingleton))

protected instance subsingleton {G : Type u_1} [group G] [subsingleton G] : subsingleton (subgroup G) :=
  iff.mp subsingleton_iff _inst_3

protected instance Mathlib.add_subgroup.nontrivial {G : Type u_1} [add_group G] [nontrivial G] : nontrivial (add_subgroup G) :=
  iff.mp add_subgroup.nontrivial_iff _inst_3

/-- The `subgroup` generated by a set. -/
def Mathlib.add_subgroup.closure {G : Type u_1} [add_group G] (k : set G) : add_subgroup G :=
  Inf (set_of fun (K : add_subgroup G) => k ⊆ ↑K)

theorem Mathlib.add_subgroup.mem_closure {G : Type u_1} [add_group G] {k : set G} {x : G} : x ∈ add_subgroup.closure k ↔ ∀ (K : add_subgroup G), k ⊆ ↑K → x ∈ K :=
  add_subgroup.mem_Inf

/-- The subgroup generated by a set includes the set. -/
@[simp] theorem subset_closure {G : Type u_1} [group G] {k : set G} : k ⊆ ↑(closure k) :=
  fun (x : G) (hx : x ∈ k) => iff.mpr mem_closure fun (K : subgroup G) (hK : k ⊆ ↑K) => hK hx

/-- A subgroup `K` includes `closure k` if and only if it includes `k`. -/
@[simp] theorem closure_le {G : Type u_1} [group G] (K : subgroup G) {k : set G} : closure k ≤ K ↔ k ⊆ ↑K :=
  { mp := set.subset.trans subset_closure, mpr := fun (h : k ⊆ ↑K) => Inf_le h }

theorem Mathlib.add_subgroup.closure_eq_of_le {G : Type u_1} [add_group G] (K : add_subgroup G) {k : set G} (h₁ : k ⊆ ↑K) (h₂ : K ≤ add_subgroup.closure k) : add_subgroup.closure k = K :=
  le_antisymm (iff.mpr (add_subgroup.closure_le K) 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`. -/
theorem Mathlib.add_subgroup.closure_induction {G : Type u_1} [add_group G] {k : set G} {p : G → Prop} {x : G} (h : x ∈ add_subgroup.closure k) (Hk : ∀ (x : G), x ∈ k → p x) (H1 : p 0) (Hmul : ∀ (x y : G), p x → p y → p (x + y)) (Hinv : ∀ (x : G), p x → p (-x)) : p x :=
  iff.mpr (add_subgroup.closure_le (add_subgroup.mk p H1 Hmul Hinv)) Hk x h

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

The difference with `subgroup.closure_induction` is that this acts on the subtype.
-/
theorem closure_induction' {G : Type u_1} [group G] (k : set G) {p : ↥(closure k) → Prop} (Hk : ∀ (x : G) (h : x ∈ k), p { val := x, property := subset_closure h }) (H1 : p 1) (Hmul : ∀ (x y : ↥(closure k)), p x → p y → p (x * y)) (Hinv : ∀ (x : ↥(closure k)), p x → p (x⁻¹)) (x : ↥(closure k)) : p x := sorry

/-- `closure` forms a Galois insertion with the coercion to set. -/
protected def Mathlib.add_subgroup.gi (G : Type u_1) [add_group G] : galois_insertion add_subgroup.closure coe :=
  galois_insertion.mk (fun (s : set G) (_x : ↑(add_subgroup.closure s) ≤ s) => add_subgroup.closure s) sorry sorry sorry

/-- Subgroup closure of a set is monotone in its argument: if `h ⊆ k`,
then `closure h ≤ closure k`. -/
theorem closure_mono {G : Type u_1} [group G] {h : set G} {k : set G} (h' : h ⊆ k) : closure h ≤ closure k :=
  galois_connection.monotone_l (galois_insertion.gc (subgroup.gi G)) h'

/-- Closure of a subgroup `K` equals `K`. -/
@[simp] theorem closure_eq {G : Type u_1} [group G] (K : subgroup G) : closure ↑K = K :=
  galois_insertion.l_u_eq (subgroup.gi G) K

@[simp] theorem Mathlib.add_subgroup.closure_empty {G : Type u_1} [add_group G] : add_subgroup.closure ∅ = ⊥ :=
  galois_connection.l_bot (galois_insertion.gc (add_subgroup.gi G))

@[simp] theorem closure_univ {G : Type u_1} [group G] : closure set.univ = ⊤ :=
  coe_top ▸ closure_eq ⊤

theorem closure_union {G : Type u_1} [group G] (s : set G) (t : set G) : closure (s ∪ t) = closure s ⊔ closure t :=
  galois_connection.l_sup (galois_insertion.gc (subgroup.gi G))

theorem Mathlib.add_subgroup.closure_Union {G : Type u_1} [add_group G] {ι : Sort u_2} (s : ι → set G) : add_subgroup.closure (set.Union fun (i : ι) => s i) = supr fun (i : ι) => add_subgroup.closure (s i) :=
  galois_connection.l_supr (galois_insertion.gc (add_subgroup.gi G))

theorem closure_eq_bot_iff (G : Type u_1) [group G] (S : set G) : closure S = ⊥ ↔ S ⊆ singleton 1 :=
  eq.mpr (id (Eq._oldrec (Eq.refl (closure S = ⊥ ↔ S ⊆ singleton 1)) (Eq.symm (propext le_bot_iff)))) (closure_le ⊥)

/-- The subgroup generated by an element of a group equals the set of integer number powers of
    the element. -/
theorem mem_closure_singleton {G : Type u_1} [group G] {x : G} {y : G} : y ∈ closure (singleton x) ↔ ∃ (n : ℤ), x ^ n = y := sorry

theorem closure_singleton_one {G : Type u_1} [group G] : closure (singleton 1) = ⊥ := sorry

theorem Mathlib.add_subgroup.mem_supr_of_directed {G : Type u_1} [add_group G] {ι : Sort u_2} [hι : Nonempty ι] {K : ι → add_subgroup G} (hK : directed LessEq K) {x : G} : x ∈ supr K ↔ ∃ (i : ι), x ∈ K i := sorry

theorem Mathlib.add_subgroup.coe_supr_of_directed {G : Type u_1} [add_group G] {ι : Sort u_2} [Nonempty ι] {S : ι → add_subgroup G} (hS : directed LessEq S) : ↑(supr fun (i : ι) => S i) = set.Union fun (i : ι) => ↑(S i) := sorry

theorem Mathlib.add_subgroup.mem_Sup_of_directed_on {G : Type u_1} [add_group G] {K : set (add_subgroup G)} (Kne : set.nonempty K) (hK : directed_on LessEq K) {x : G} : x ∈ Sup K ↔ ∃ (s : add_subgroup G), ∃ (H : s ∈ K), x ∈ s := sorry

/-- The preimage of a subgroup along a monoid homomorphism is a subgroup. -/
def Mathlib.add_subgroup.comap {G : Type u_1} [add_group G] {N : Type u_2} [add_group N] (f : G →+ N) (H : add_subgroup N) : add_subgroup G :=
  add_subgroup.mk (⇑f ⁻¹' ↑H) sorry sorry sorry

@[simp] theorem coe_comap {G : Type u_1} [group G] {N : Type u_3} [group N] (K : subgroup N) (f : G →* N) : ↑(comap f K) = ⇑f ⁻¹' ↑K :=
  rfl

@[simp] theorem Mathlib.add_subgroup.mem_comap {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] {K : add_subgroup N} {f : G →+ N} {x : G} : x ∈ add_subgroup.comap f K ↔ coe_fn f x ∈ K :=
  iff.rfl

theorem comap_comap {G : Type u_1} [group G] {N : Type u_3} [group N] {P : Type u_4} [group P] (K : subgroup P) (g : N →* P) (f : G →* N) : comap f (comap g K) = comap (monoid_hom.comp g f) K :=
  rfl

/-- The image of a subgroup along a monoid homomorphism is a subgroup. -/
def map {G : Type u_1} [group G] {N : Type u_3} [group N] (f : G →* N) (H : subgroup G) : subgroup N :=
  mk (⇑f '' ↑H) sorry sorry sorry

@[simp] theorem Mathlib.add_subgroup.coe_map {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) (K : add_subgroup G) : ↑(add_subgroup.map f K) = ⇑f '' ↑K :=
  rfl

@[simp] theorem Mathlib.add_subgroup.mem_map {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] {f : G →+ N} {K : add_subgroup G} {y : N} : y ∈ add_subgroup.map f K ↔ ∃ (x : G), ∃ (H : x ∈ K), coe_fn f x = y :=
  set.mem_image_iff_bex

theorem map_map {G : Type u_1} [group G] (K : subgroup G) {N : Type u_3} [group N] {P : Type u_4} [group P] (g : N →* P) (f : G →* N) : map g (map f K) = map (monoid_hom.comp g f) K :=
  ext' (set.image_image (fun (a : N) => coe_fn g a) (fun (a : G) => coe_fn f a) ↑K)

theorem Mathlib.add_subgroup.map_le_iff_le_comap {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] {f : G →+ N} {K : add_subgroup G} {H : add_subgroup N} : add_subgroup.map f K ≤ H ↔ K ≤ add_subgroup.comap f H :=
  set.image_subset_iff

theorem Mathlib.add_subgroup.gc_map_comap {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) : galois_connection (add_subgroup.map f) (add_subgroup.comap f) :=
  fun (_x : add_subgroup G) (_x_1 : add_subgroup N) => add_subgroup.map_le_iff_le_comap

theorem map_sup {G : Type u_1} [group G] {N : Type u_3} [group N] (H : subgroup G) (K : subgroup G) (f : G →* N) : map f (H ⊔ K) = map f H ⊔ map f K :=
  galois_connection.l_sup (gc_map_comap f)

theorem Mathlib.add_subgroup.map_supr {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] {ι : Sort u_2} (f : G →+ N) (s : ι → add_subgroup G) : add_subgroup.map f (supr s) = supr fun (i : ι) => add_subgroup.map f (s i) :=
  galois_connection.l_supr (add_subgroup.gc_map_comap f)

theorem comap_inf {G : Type u_1} [group G] {N : Type u_3} [group N] (H : subgroup N) (K : subgroup N) (f : G →* N) : comap f (H ⊓ K) = comap f H ⊓ comap f K :=
  galois_connection.u_inf (gc_map_comap f)

theorem comap_infi {G : Type u_1} [group G] {N : Type u_3} [group N] {ι : Sort u_2} (f : G →* N) (s : ι → subgroup N) : comap f (infi s) = infi fun (i : ι) => comap f (s i) :=
  galois_connection.u_infi (gc_map_comap f)

@[simp] theorem map_bot {G : Type u_1} [group G] {N : Type u_3} [group N] (f : G →* N) : map f ⊥ = ⊥ :=
  galois_connection.l_bot (gc_map_comap f)

@[simp] theorem Mathlib.add_subgroup.comap_top {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) : add_subgroup.comap f ⊤ = ⊤ :=
  galois_connection.u_top (add_subgroup.gc_map_comap f)

theorem Mathlib.add_subgroup.map_eq_bot_iff {G : Type u_1} [add_group G] {G' : Type u_2} [add_group G'] {f : G →+ G'} (hf : function.injective ⇑f) (H : add_subgroup G) : add_subgroup.map f H = ⊥ ↔ H = ⊥ := sorry

/-- Given `subgroup`s `H`, `K` of groups `G`, `N` respectively, `H × K` as a subgroup of `G × N`. -/
def Mathlib.add_subgroup.prod {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (H : add_subgroup G) (K : add_subgroup N) : add_subgroup (G × N) :=
  add_subgroup.mk
    (add_submonoid.carrier (add_submonoid.prod (add_subgroup.to_add_submonoid H) (add_subgroup.to_add_submonoid K))) sorry
    sorry sorry

theorem Mathlib.add_subgroup.coe_prod {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (H : add_subgroup G) (K : add_subgroup N) : ↑(add_subgroup.prod H K) = set.prod ↑H ↑K :=
  rfl

theorem Mathlib.add_subgroup.mem_prod {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] {H : add_subgroup G} {K : add_subgroup N} {p : G × N} : p ∈ add_subgroup.prod H K ↔ prod.fst p ∈ H ∧ prod.snd p ∈ K :=
  iff.rfl

theorem prod_mono {G : Type u_1} [group G] {N : Type u_3} [group N] : relator.lift_fun LessEq (LessEq ⇒ LessEq) prod prod :=
  fun (s s' : subgroup G) (hs : s ≤ s') (t t' : subgroup N) (ht : t ≤ t') => set.prod_mono hs ht

theorem prod_mono_right {G : Type u_1} [group G] {N : Type u_3} [group N] (K : subgroup G) : monotone fun (t : subgroup N) => prod K t :=
  prod_mono (le_refl K)

theorem Mathlib.add_subgroup.prod_mono_left {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (H : add_subgroup N) : monotone fun (K : add_subgroup G) => add_subgroup.prod K H :=
  fun (s₁ s₂ : add_subgroup G) (hs : s₁ ≤ s₂) => add_subgroup.prod_mono hs (le_refl H)

theorem Mathlib.add_subgroup.prod_top {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (K : add_subgroup G) : add_subgroup.prod K ⊤ = add_subgroup.comap (add_monoid_hom.fst G N) K := sorry

theorem Mathlib.add_subgroup.top_prod {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (H : add_subgroup N) : add_subgroup.prod ⊤ H = add_subgroup.comap (add_monoid_hom.snd G N) H := sorry

@[simp] theorem top_prod_top {G : Type u_1} [group G] {N : Type u_3} [group N] : prod ⊤ ⊤ = ⊤ :=
  Eq.trans (top_prod ⊤) (comap_top (monoid_hom.snd G N))

theorem bot_prod_bot {G : Type u_1} [group G] {N : Type u_3} [group N] : prod ⊥ ⊥ = ⊥ := sorry

/-- Product of subgroups is isomorphic to their product as groups. -/
def prod_equiv {G : Type u_1} [group G] {N : Type u_3} [group N] (H : subgroup G) (K : subgroup N) : ↥(prod H K) ≃* ↥H × ↥K :=
  mul_equiv.mk (equiv.to_fun (equiv.set.prod ↑H ↑K)) (equiv.inv_fun (equiv.set.prod ↑H ↑K)) sorry sorry sorry

/-- A subgroup is normal if whenever `n ∈ H`, then `g * n * g⁻¹ ∈ H` for every `g : G` -/
class normal {G : Type u_1} [group G] (H : subgroup G)
where
  conj_mem : ∀ (n : G), n ∈ H → ∀ (g : G), g * n * (g⁻¹) ∈ H

end subgroup

namespace add_subgroup

/-- An add_subgroup is normal if whenever `n ∈ H`, then `g + n - g ∈ H` for every `g : G` -/
class normal {A : Type u_2} [add_group A] (H : add_subgroup A)
where
  conj_mem : ∀ (n : A), n ∈ H → ∀ (g : A), g + n + -g ∈ H

end add_subgroup

namespace subgroup

protected instance normal_of_comm {G : Type u_1} [comm_group G] (H : subgroup G) : normal H := sorry

namespace normal

theorem mem_comm {G : Type u_1} [group G] {H : subgroup G} (nH : normal H) {a : G} {b : G} (h : a * b ∈ H) : b * a ∈ H := sorry

theorem mem_comm_iff {G : Type u_1} [group G] {H : subgroup G} (nH : normal H) {a : G} {b : G} : a * b ∈ H ↔ b * a ∈ H :=
  { mp := mem_comm nH, mpr := mem_comm nH }

end normal

protected instance bot_normal {G : Type u_1} [group G] : normal ⊥ :=
  normal.mk
    (eq.mpr
      (id
        (Eq.trans
          (Eq.trans
            (Eq.trans
              (forall_congr_eq
                fun (n : G) => imp_congr_eq (propext mem_bot) (forall_congr_eq fun (g : G) => propext mem_bot))
              (propext forall_eq))
            (forall_congr_eq
              fun (g : G) =>
                Eq.trans
                  ((fun (a a_1 : G) (e_1 : a = a_1) (ᾰ ᾰ_1 : G) (e_2 : ᾰ = ᾰ_1) => congr (congr_arg Eq e_1) e_2)
                    (g * 1 * (g⁻¹)) 1
                    (Eq.trans
                      ((fun (ᾰ ᾰ_1 : G) (e_2 : ᾰ = ᾰ_1) (ᾰ_2 ᾰ_3 : G) (e_3 : ᾰ_2 = ᾰ_3) =>
                          congr (congr_arg Mul.mul e_2) e_3)
                        (g * 1) g (mul_one g) (g⁻¹) (g⁻¹) (Eq.refl (g⁻¹)))
                      (mul_right_inv g))
                    1 1 (Eq.refl 1))
                  (propext (eq_self_iff_true 1))))
          (propext (forall_const G))))
      trivial)

protected instance top_normal {G : Type u_1} [group G] : normal ⊤ :=
  normal.mk fun (_x : G) (_x : _x ∈ ⊤) => mem_top

/-- The center of a group `G` is the set of elements that commute with everything in `G` -/
def center (G : Type u_1) [group G] : subgroup G :=
  mk (set_of fun (z : G) => ∀ (g : G), g * z = z * g) sorry sorry sorry

theorem Mathlib.add_subgroup.mem_center_iff {G : Type u_1} [add_group G] {z : G} : z ∈ add_subgroup.center G ↔ ∀ (g : G), g + z = z + g :=
  iff.rfl

protected instance Mathlib.add_subgroup.center_normal {G : Type u_1} [add_group G] : add_subgroup.normal (add_subgroup.center G) := sorry

/-- The `normalizer` of `H` is the smallest subgroup of `G` inside which `H` is normal. -/
def normalizer {G : Type u_1} [group G] (H : subgroup G) : subgroup G :=
  mk (set_of fun (g : G) => ∀ (n : G), n ∈ H ↔ g * n * (g⁻¹) ∈ H) sorry sorry sorry

-- 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` -/
def set_normalizer {G : Type u_1} [group G] (S : set G) : subgroup G :=
  mk (set_of fun (g : G) => ∀ (n : G), n ∈ S ↔ g * n * (g⁻¹) ∈ S) sorry sorry sorry

theorem Mathlib.add_subgroup.mem_normalizer_iff {G : Type u_1} [add_group G] {H : add_subgroup G} {g : G} : g ∈ add_subgroup.normalizer H ↔ ∀ (n : G), n ∈ H ↔ g + n + -g ∈ H :=
  iff.rfl

theorem le_normalizer {G : Type u_1} [group G] {H : subgroup G} : H ≤ normalizer H :=
  fun (x : G) (xH : x ∈ H) (n : G) =>
    eq.mpr (id (Eq._oldrec (Eq.refl (n ∈ H ↔ x * n * (x⁻¹) ∈ H)) (propext (mul_mem_cancel_right H (inv_mem H xH)))))
      (eq.mpr (id (Eq._oldrec (Eq.refl (n ∈ H ↔ x * n ∈ H)) (propext (mul_mem_cancel_left H xH)))) (iff.refl (n ∈ H)))

protected instance normal_in_normalizer {G : Type u_1} [group G] {H : subgroup G} : normal (comap (subtype (normalizer H)) H) := sorry

theorem le_normalizer_of_normal {G : Type u_1} [group G] {H : subgroup G} {K : subgroup G} [hK : normal (comap (subtype K) H)] (HK : H ≤ K) : K ≤ normalizer H := sorry

end subgroup

namespace group

/-- Given an element `a`, `conjugates a` is the set of conjugates. -/
def conjugates {G : Type u_1} [group G] (a : G) : set G :=
  set_of fun (b : G) => is_conj a b

theorem mem_conjugates_self {G : Type u_1} [group G] {a : G} : a ∈ conjugates a :=
  is_conj_refl a

/-- Given a set `s`, `conjugates_of_set s` is the set of all conjugates of
the elements of `s`. -/
def conjugates_of_set {G : Type u_1} [group G] (s : set G) : set G :=
  set.Union fun (a : G) => set.Union fun (H : a ∈ s) => conjugates a

theorem mem_conjugates_of_set_iff {G : Type u_1} [group G] {s : set G} {x : G} : x ∈ conjugates_of_set s ↔ ∃ (a : G), ∃ (H : a ∈ s), is_conj a x :=
  set.mem_bUnion_iff

theorem subset_conjugates_of_set {G : Type u_1} [group G] {s : set G} : s ⊆ conjugates_of_set s :=
  fun (x : G) (h : x ∈ s) => iff.mpr mem_conjugates_of_set_iff (Exists.intro x (Exists.intro h (is_conj_refl x)))

theorem conjugates_of_set_mono {G : Type u_1} [group G] {s : set G} {t : set G} (h : s ⊆ t) : conjugates_of_set s ⊆ conjugates_of_set t :=
  set.bUnion_subset_bUnion_left h

theorem conjugates_subset_normal {G : Type u_1} [group G] {N : subgroup G} [tn : subgroup.normal N] {a : G} (h : a ∈ N) : conjugates a ⊆ ↑N :=
  id
    fun (a_1 : G) (ᾰ : a_1 ∈ conjugates a) =>
      Exists.dcases_on ᾰ fun (c : G) (ᾰ_h : c * a * (c⁻¹) = a_1) => Eq._oldrec (subgroup.normal.conj_mem tn a h c) ᾰ_h

theorem conjugates_of_set_subset {G : Type u_1} [group G] {s : set G} {N : subgroup G} [subgroup.normal N] (h : s ⊆ ↑N) : conjugates_of_set s ⊆ ↑N :=
  set.bUnion_subset fun (x : G) (H : x ∈ s) => conjugates_subset_normal (h H)

/-- The set of conjugates of `s` is closed under conjugation. -/
theorem conj_mem_conjugates_of_set {G : Type u_1} [group G] {s : set G} {x : G} {c : G} : x ∈ conjugates_of_set s → c * x * (c⁻¹) ∈ conjugates_of_set s := sorry

end group

namespace subgroup

/-- 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 {G : Type u_1} [group G] (s : set G) : subgroup G :=
  closure (group.conjugates_of_set s)

theorem conjugates_of_set_subset_normal_closure {G : Type u_1} [group G] {s : set G} : group.conjugates_of_set s ⊆ ↑(normal_closure s) :=
  subset_closure

theorem subset_normal_closure {G : Type u_1} [group G] {s : set G} : s ⊆ ↑(normal_closure s) :=
  set.subset.trans group.subset_conjugates_of_set conjugates_of_set_subset_normal_closure

theorem le_normal_closure {G : Type u_1} [group G] {H : subgroup G} : H ≤ normal_closure ↑H :=
  fun (_x : G) (h : _x ∈ H) => subset_normal_closure h

/-- The normal closure of `s` is a normal subgroup. -/
protected instance normal_closure_normal {G : Type u_1} [group G] {s : set G} : normal (normal_closure s) := sorry

/-- The normal closure of `s` is the smallest normal subgroup containing `s`. -/
theorem normal_closure_le_normal {G : Type u_1} [group G] {s : set G} {N : subgroup G} [normal N] (h : s ⊆ ↑N) : normal_closure s ≤ N := sorry

theorem normal_closure_subset_iff {G : Type u_1} [group G] {s : set G} {N : subgroup G} [normal N] : s ⊆ ↑N ↔ normal_closure s ≤ N :=
  { mp := normal_closure_le_normal, mpr := set.subset.trans subset_normal_closure }

theorem normal_closure_mono {G : Type u_1} [group G] {s : set G} {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 {G : Type u_1} [group G] {s : set G} : normal_closure s = infi fun (N : subgroup G) => infi (infi fun (hs : s ⊆ ↑N) => N) :=
  le_antisymm (le_infi fun (N : subgroup G) => le_infi fun (hN : normal N) => le_infi normal_closure_le_normal)
    (infi_le_of_le (normal_closure s)
      (infi_le_of_le subgroup.normal_closure_normal (infi_le_of_le subset_normal_closure (le_refl (normal_closure s)))))

@[simp] theorem normal_closure_eq_self {G : Type u_1} [group G] (H : subgroup G) [normal H] : normal_closure ↑H = H :=
  le_antisymm (normal_closure_le_normal (eq.subset rfl)) le_normal_closure

@[simp] theorem normal_closure_idempotent {G : Type u_1} [group G] {s : set G} : normal_closure ↑(normal_closure s) = normal_closure s :=
  normal_closure_eq_self (normal_closure s)

theorem closure_le_normal_closure {G : Type u_1} [group G] {s : set G} : closure s ≤ normal_closure s :=
  eq.mpr (id (Eq.trans (propext (closure_le (normal_closure s))) (propext (iff_true_intro subset_normal_closure))))
    trivial

@[simp] theorem normal_closure_closure_eq_normal_closure {G : Type u_1} [group G] {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

theorem gsmul_mem {A : Type u_2} [add_group A] (H : add_subgroup A) {x : A} (hx : x ∈ H) (n : ℤ) : n •ℤ x ∈ H := sorry

/-- The `add_subgroup` generated by an element of an `add_group` equals the set of
natural number multiples of the element. -/
theorem mem_closure_singleton {A : Type u_2} [add_group A] {x : A} {y : A} : y ∈ closure (singleton x) ↔ ∃ (n : ℤ), n •ℤ x = y := sorry

theorem closure_singleton_zero {A : Type u_2} [add_group A] : closure (singleton 0) = ⊥ := sorry

@[simp] theorem coe_smul {A : Type u_2} [add_group A] (H : add_subgroup A) (x : ↥H) (n : ℕ) : ↑(n •ℕ x) = n •ℕ ↑x :=
  coe_subtype H ▸ add_monoid_hom.map_nsmul (subtype H) x n

@[simp] theorem coe_gsmul {A : Type u_2} [add_group A] (H : add_subgroup A) (x : ↥H) (n : ℤ) : ↑(n •ℤ x) = n •ℤ ↑x :=
  coe_subtype H ▸ add_monoid_hom.map_gsmul (subtype H) x n

end add_subgroup

namespace monoid_hom

/-- The range of a monoid homomorphism from a group is a subgroup. -/
def Mathlib.add_monoid_hom.range {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) : add_subgroup N :=
  add_subgroup.copy (add_subgroup.map f ⊤) (set.range ⇑f) sorry

protected instance decidable_mem_range {G : Type u_1} [group G] {N : Type u_3} [group N] (f : G →* N) [fintype G] [DecidableEq N] : decidable_pred fun (x : N) => x ∈ range f :=
  fun (x : N) => fintype.decidable_exists_fintype

@[simp] theorem Mathlib.add_monoid_hom.coe_range {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) : ↑(add_monoid_hom.range f) = set.range ⇑f :=
  rfl

@[simp] theorem Mathlib.add_monoid_hom.mem_range {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] {f : G →+ N} {y : N} : y ∈ add_monoid_hom.range f ↔ ∃ (x : G), coe_fn f x = y :=
  iff.rfl

theorem Mathlib.add_monoid_hom.range_eq_map {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) : add_monoid_hom.range f = add_subgroup.map f ⊤ := sorry

/-- The canonical surjective group homomorphism `G →* f(G)` induced by a group
homomorphism `G →* N`. -/
def Mathlib.add_monoid_hom.to_range {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) : G →+ ↥(add_monoid_hom.range f) :=
  add_monoid_hom.mk' (fun (g : G) => { val := coe_fn f g, property := sorry }) sorry

theorem Mathlib.add_monoid_hom.map_range {G : Type u_1} [add_group G] {N : Type u_3} {P : Type u_4} [add_group N] [add_group P] (g : N →+ P) (f : G →+ N) : add_subgroup.map g (add_monoid_hom.range f) = add_monoid_hom.range (add_monoid_hom.comp g f) := sorry

theorem range_top_iff_surjective {G : Type u_1} [group G] {N : Type u_2} [group N] {f : G →* N} : range f = ⊤ ↔ function.surjective ⇑f := sorry

/-- The range of a surjective monoid homomorphism is the whole of the codomain. -/
theorem range_top_of_surjective {G : Type u_1} [group G] {N : Type u_2} [group N] (f : G →* N) (hf : function.surjective ⇑f) : range f = ⊤ :=
  iff.mpr range_top_iff_surjective hf

/-- Restriction of a group hom to a subgroup of the codomain. -/
def Mathlib.add_monoid_hom.cod_restrict {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) (S : add_subgroup N) (h : ∀ (x : G), coe_fn f x ∈ S) : G →+ ↥S :=
  add_monoid_hom.mk (fun (n : G) => { val := coe_fn f n, property := h n }) sorry sorry

/-- The multiplicative kernel of a monoid homomorphism is the subgroup of elements `x : G` such that
`f x = 1` -/
def ker {G : Type u_1} [group G] {N : Type u_3} [group N] (f : G →* N) : subgroup G :=
  subgroup.comap f ⊥

theorem Mathlib.add_monoid_hom.mem_ker {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) {x : G} : x ∈ add_monoid_hom.ker f ↔ coe_fn f x = 0 :=
  iff.rfl

theorem comap_ker {G : Type u_1} [group G] {N : Type u_3} {P : Type u_4} [group N] [group P] (g : N →* P) (f : G →* N) : subgroup.comap f (ker g) = ker (comp g f) :=
  rfl

theorem Mathlib.add_monoid_hom.to_range_ker {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) : add_monoid_hom.ker (add_monoid_hom.to_range f) = add_monoid_hom.ker f := sorry

/-- The subgroup of elements `x : G` such that `f x = g x` -/
def eq_locus {G : Type u_1} [group G] {N : Type u_3} [group N] (f : G →* N) (g : G →* N) : subgroup G :=
  subgroup.mk (submonoid.carrier (eq_mlocus f g)) sorry sorry sorry

/-- If two monoid homomorphisms are equal on a set, then they are equal on its subgroup closure. -/
theorem Mathlib.add_monoid_hom.eq_on_closure {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] {f : G →+ N} {g : G →+ N} {s : set G} (h : set.eq_on (⇑f) (⇑g) s) : set.eq_on ⇑f ⇑g ↑(add_subgroup.closure s) :=
  (fun (this : add_subgroup.closure s ≤ add_monoid_hom.eq_locus f g) => this)
    (iff.mpr (add_subgroup.closure_le (add_monoid_hom.eq_locus f g)) h)

theorem eq_of_eq_on_top {G : Type u_1} [group G] {N : Type u_3} [group N] {f : G →* N} {g : G →* N} (h : set.eq_on ⇑f ⇑g ↑⊤) : f = g :=
  ext fun (x : G) => h trivial

theorem Mathlib.add_monoid_hom.eq_of_eq_on_dense {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] {s : set G} (hs : add_subgroup.closure s = ⊤) {f : G →+ N} {g : G →+ N} (h : set.eq_on (⇑f) (⇑g) s) : f = g :=
  add_monoid_hom.eq_of_eq_on_top (hs ▸ add_monoid_hom.eq_on_closure h)

theorem Mathlib.add_monoid_hom.gclosure_preimage_le {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) (s : set N) : add_subgroup.closure (⇑f ⁻¹' s) ≤ add_subgroup.comap f (add_subgroup.closure s) := sorry

/-- The image under a monoid homomorphism of the subgroup generated by a set equals the subgroup
generated by the image of the set. -/
theorem Mathlib.add_monoid_hom.map_closure {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] (f : G →+ N) (s : set G) : add_subgroup.map f (add_subgroup.closure s) = add_subgroup.closure (⇑f '' s) := sorry

end monoid_hom

namespace monoid_hom

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

* such that `φ.comp f = g` (`lift_of_surjective_comp`),
* where `f : G₁ →+* G₂` is surjective (`hf`),
* and `g : G₂ →+* G₃` satisfies `hg : f.ker ≤ g.ker`.

See `lift_of_surjective_eq` for the uniqueness lemma.

```
   G₁.
   |  \
 f |   \ g
   |    \
   v     \⌟
   G₂----> G₃
      ∃!φ
```
 -/
def Mathlib.add_monoid_hom.lift_of_surjective {G₁ : Type u_3} {G₂ : Type u_4} {G₃ : Type u_5} [add_group G₁] [add_group G₂] [add_group G₃] (f : G₁ →+ G₂) (hf : function.surjective ⇑f) (g : G₁ →+ G₃) (hg : add_monoid_hom.ker f ≤ add_monoid_hom.ker g) : G₂ →+ G₃ :=
  add_monoid_hom.mk (fun (b : G₂) => coe_fn g (classical.some (hf b))) sorry sorry

@[simp] theorem lift_of_surjective_comp_apply {G₁ : Type u_3} {G₂ : Type u_4} {G₃ : Type u_5} [group G₁] [group G₂] [group G₃] (f : G₁ →* G₂) (hf : function.surjective ⇑f) (g : G₁ →* G₃) (hg : ker f ≤ ker g) (x : G₁) : coe_fn (lift_of_surjective f hf g hg) (coe_fn f x) = coe_fn g x := sorry

@[simp] theorem Mathlib.add_monoid_hom.lift_of_surjective_comp {G₁ : Type u_3} {G₂ : Type u_4} {G₃ : Type u_5} [add_group G₁] [add_group G₂] [add_group G₃] (f : G₁ →+ G₂) (hf : function.surjective ⇑f) (g : G₁ →+ G₃) (hg : add_monoid_hom.ker f ≤ add_monoid_hom.ker g) : add_monoid_hom.comp (add_monoid_hom.lift_of_surjective f hf g hg) f = g := sorry

theorem eq_lift_of_surjective {G₁ : Type u_3} {G₂ : Type u_4} {G₃ : Type u_5} [group G₁] [group G₂] [group G₃] (f : G₁ →* G₂) (hf : function.surjective ⇑f) (g : G₁ →* G₃) (hg : ker f ≤ ker g) (h : G₂ →* G₃) (hh : comp h f = g) : h = lift_of_surjective f hf g hg := sorry

end monoid_hom

-- Here `H.normal` is an explicit argument so we can use dot notation with `comap`.

theorem subgroup.normal.comap {G : Type u_1} [group G] {N : Type u_3} [group N] {H : subgroup N} (hH : subgroup.normal H) (f : G →* N) : subgroup.normal (subgroup.comap f H) := sorry

protected instance add_subgroup.normal_comap {G : Type u_1} [add_group G] {N : Type u_3} [add_group N] {H : add_subgroup N} [nH : add_subgroup.normal H] (f : G →+ N) : add_subgroup.normal (add_subgroup.comap f H) :=
  add_subgroup.normal.comap nH f

protected instance monoid_hom.normal_ker {G : Type u_1} [group G] {N : Type u_3} [group N] (f : G →* N) : subgroup.normal (monoid_hom.ker f) :=
  eq.mpr (id (Eq._oldrec (Eq.refl (subgroup.normal (monoid_hom.ker f))) (monoid_hom.ker.equations._eqn_1 f)))
    (subgroup.normal_comap f)

namespace subgroup

/-- The subgroup generated by an element. -/
def gpowers {G : Type u_1} [group G] (g : G) : subgroup G :=
  subgroup.copy (monoid_hom.range (coe_fn (gpowers_hom G) g)) (set.range (pow g)) sorry

@[simp] theorem mem_gpowers {G : Type u_1} [group G] (g : G) : g ∈ gpowers g :=
  Exists.intro 1 (gpow_one g)

theorem gpowers_eq_closure {G : Type u_1} [group G] (g : G) : gpowers g = closure (singleton g) :=
  ext fun (x : G) => iff.symm mem_closure_singleton

@[simp] theorem range_gpowers_hom {G : Type u_1} [group G] (g : G) : monoid_hom.range (coe_fn (gpowers_hom G) g) = gpowers g :=
  rfl

theorem gpowers_subset {G : Type u_1} [group G] {a : G} {K : subgroup G} (h : a ∈ K) : gpowers a ≤ K := sorry

end subgroup

namespace add_subgroup

/-- The subgroup generated by an element. -/
def gmultiples {A : Type u_2} [add_group A] (a : A) : add_subgroup A :=
  add_subgroup.copy (add_monoid_hom.range (coe_fn (gmultiples_hom A) a)) (set.range fun (_x : ℤ) => _x •ℤ a) sorry

@[simp] theorem mem_gmultiples {A : Type u_2} [add_group A] (a : A) : a ∈ gmultiples a :=
  Exists.intro 1 (one_gsmul a)

theorem gmultiples_eq_closure {A : Type u_2} [add_group A] (a : A) : gmultiples a = closure (singleton a) :=
  ext fun (x : A) => iff.symm mem_closure_singleton

@[simp] theorem range_gmultiples_hom {A : Type u_2} [add_group A] (a : A) : add_monoid_hom.range (coe_fn (gmultiples_hom A) a) = gmultiples a :=
  rfl

theorem gmultiples_subset {A : Type u_2} [add_group A] {a : A} {B : add_subgroup A} (h : a ∈ B) : gmultiples a ≤ B :=
  subgroup.gpowers_subset h

end add_subgroup

namespace mul_equiv

/-- Makes the identity isomorphism from a proof two subgroups of a multiplicative
    group are equal. -/
def subgroup_congr {G : Type u_1} [group G] {H : subgroup G} {K : subgroup G} (h : H = K) : ↥H ≃* ↥K :=
  mk (equiv.to_fun (equiv.set_congr sorry)) (equiv.inv_fun (equiv.set_congr sorry)) sorry sorry sorry

end mul_equiv

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

namespace subgroup

theorem Mathlib.add_subgroup.mem_sup {C : Type u_4} [add_comm_group C] {s : add_subgroup C} {t : add_subgroup C} {x : C} : x ∈ s ⊔ t ↔ ∃ (y : C), ∃ (H : y ∈ s), ∃ (z : C), ∃ (H : z ∈ t), y + z = x := sorry

theorem Mathlib.add_subgroup.mem_sup' {C : Type u_4} [add_comm_group C] {s : add_subgroup C} {t : add_subgroup C} {x : C} : x ∈ s ⊔ t ↔ ∃ (y : ↥s), ∃ (z : ↥t), ↑y + ↑z = x := sorry

protected instance Mathlib.add_subgroup.is_modular_lattice {C : Type u_4} [add_comm_group C] : is_modular_lattice (add_subgroup C) := sorry


inductive Le (m : Nat) : Nat → Prop
  | base : Le m m
  | succ : (n : Nat) → Le m n → Le m n.succ

theorem ex1 (m : Nat) : Le m 0 → m = 0 := by
  intro h
  cases h
  rfl

theorem ex2 (m n : Nat) : Le m n → Le m.succ n.succ := by
  intro h
  induction h with
  | base => apply Le.base
  | succ n m ih =>
    apply Le.succ
    apply ih

theorem ex3 (m : Nat) : Le 0 m := by
  induction m with
  | zero => apply Le.base
  | succ m ih =>
    apply Le.succ
    apply ih

theorem ex4 (m : Nat) : ¬ Le m.succ 0 := by
  intro h
  cases h

theorem ex5 {m n : Nat} : Le m n.succ → m = n.succ ∨ Le m n := by
  intro h
  cases h with
  | base => apply Or.inl; rfl
  | succ => apply Or.inr; assumption

theorem ex6 {m n : Nat} : Le m.succ n.succ → Le m n := by
  revert m
  induction n with
  | zero =>
    intros m h;
    cases h with
    | base => apply Le.base
    | succ n h => exact absurd h (ex4 _)
  | succ n ih =>
    intros m h
    have aux := ih (m := m)
    cases ex5 h with
    | inl h =>
      injection h with h
      subst h
      apply Le.base
    | inr h =>
      apply Le.succ
      exact ih h

theorem ex7 {m n o : Nat} : Le m n → Le n o → Le m o := by
  intro h
  induction h with
  | base => intros; assumption
  | succ n s ih =>
    intro h₂
    apply ih
    apply ex6
    apply Le.succ
    assumption

theorem ex8 {m n : Nat} : Le m.succ n → Le m n := by
  intro h
  apply ex6
  apply Le.succ
  assumption

theorem ex9 {m n : Nat} : Le m n → m = n ∨ Le m.succ n := by
  intro h
  cases h with
  | base => apply Or.inl; rfl
  | succ n s =>
    apply Or.inr
    apply ex2
    assumption

/-
theorem ex10 (n : Nat) : ¬ Le n.succ n := by
  intro h
  cases h -- TODO: improve cases tactic
  done
-/

theorem ex10 (n : Nat) : n.succ ≠ n := by
  induction n with
  | zero => intro h; injection h; done
  | succ n ih => intro h; injection h with h; apply ih h

theorem ex11 (n : Nat) : ¬ Le n.succ n := by
  induction n with
  | zero => intro h; cases h; done
  | succ n ih =>
    intro h
    have aux := ex6 h
    exact absurd aux ih
    done

theorem ex12 (m n : Nat) : Le m n → Le n m → m = n := by
  revert m
  induction n with
  | zero => intro m h1 h2; apply ex1; assumption; done
  | succ n ih =>
    intro m h1 h2
    have ih := ih m
    cases ex5 h1 with
    | inl h => assumption
    | inr h =>
      have ih := ih h
      have h3 := ex8 h2
      have ih := ih h3
      subst ih
      apply absurd h2 (ex11 _)
      done

inductive Foo : Nat → Prop where
  | foo : Foo 0
  | bar : Foo 0
  | baz : Foo 1

example (f : Foo 0) : True := by
  cases f with
  | _ => trivial

example (f : Foo n) (h : n = 0) : True := by
  induction f with simp at h
  | _ => trivial


import tactic
import data.real.basic
/-

Fibonacci. Harder than it looks.

-/

def fib : ℕ → ℕ
| 0 := 0
| 1 := 1
| (n + 2) := fib n + fib (n + 1) -- remark that brackets needed around n+2, a common newbie error

-- When making a definition like this, I find it clear
-- to instantly restart the equation lemmas.

lemma fib_0 : fib 0 = 0 := by refl -- true by definition
lemma fib_1 : fib 1 = 1 := rfl -- term mode variant
lemma fib_succ_succ (n : ℕ) : fib (n + 2) = fib n + fib (n + 1) := by refl

-- Sample easy-looking thing which is easy:

open finset

example (n : ℕ) : 1 + (range n).sum fib = fib (n + 1) :=
begin
  -- easy induction
  induction n with d hd,
  { refl},
  rw sum_range_succ,
  rw add_left_comm, -- cunning shortcut
  rw hd,
  symmetry,
  exact fib_succ_succ d
end

-- But anything with subtraction in it is going to be hard.
-- For example I wouldn't fancy fib(n+1)^2-fib(n)fib(n+2)=(-1)^n using this nat definition.

def fibZ : ℕ → ℤ
| 0 := 0
| 1 := 1
| (n + 2) := fibZ n + fibZ (n + 1)

@[simp] lemma fibZ_0 : fibZ 0 = 0 := rfl
@[simp] lemma fibZ_1 : fibZ 1 = 1 := rfl
@[simp] lemma fibZ_succ_succ (n : ℕ) : fibZ (n + 2) = fibZ n + fibZ (n + 1) := rfl

-- painless subtraction
example (n : ℕ) : fibZ (n+2) - fibZ(n+1) = fibZ(n) :=
begin
  rw fibZ_succ_succ,
  ring,
end

-- Binet's formula for Fibonacci numbers uses reals.

open real

noncomputable def a : ℝ := (1+sqrt(5))/2
noncomputable def b : ℝ := (1-sqrt(5))/2

-- Binet says (a^n-b^n)/sqrt(5) = fib n

-- Can't prove this by straight induction!
-- Need P(n) and P(n+1) implies P(n+2)
-- So let's make our own induction principle

lemma induction2 (P : ℕ → Prop) (h0 : P 0) (h1 : P 1)
  (hind : ∀ d : ℕ, P d → P(d+1) → P(d+2)) : ∀ n, P n :=
begin
  -- reminder of maths proof: if Q(n) := P(n) ∧ P(n+1) then Q(n) can be
  -- proved by normal induction
  set Q : ℕ → Prop := λ n, P n ∧ P(n+1) with Qdef, -- note use of `set`
  have hQ : ∀ n, Q n,
  { -- prove hQ by usual induction
    intro n,
    induction n with d hd,
    { -- base case
      rw Qdef,
      split,
      { exact h0},
      { exact h1}},
    { -- inductive step for hQ
      rw Qdef at ⊢ hd,
      cases hd with hPd hPd1,
      split, exact hPd1,
      apply hind,
      { assumption},
      { assumption}}},
  -- now we know Q n is true for all n, so deducing P n is easy
  intro n,
  have hQn := hQ n,
  rw Qdef at hQn,
  cc, -- because why not
end

-- baby application: fib and fibZ coincide.

lemma fibZ_eq_fib : ∀ n, fibZ n = fib n :=
begin
  apply induction2,
  { refl},
  { refl},
  intros h h1 h2,
  rw [fib_succ_succ, fibZ_succ_succ, h1, h2],
  norm_cast,
end

-- Recall we're going for Binet. We're also going to need a^{d+2}=a^{d+1}+a^d
-- so let's get this out of the way

lemma a_min_pol : a^2=a+1 :=
begin
  rw a, -- 2's in denominator now
  have h2 : (2 : ℝ) ≠ 0,
    norm_num,
  field_simp [h2],
  ring, -- fails; doesn't close goal.
  -- Is using it bad style?
  -- this now stinks
  rw [add_mul, mul_assoc],
  rw mul_self_sqrt,
  { ring},
  { norm_num}
end

lemma a_thing (d : ℕ) : a^(d+2) = a^(d+1) + a^d :=
begin
  rw [pow_add, a_min_pol],
  ring_exp,
end

lemma b_min_pol : b^2=b+1 :=
begin
  rw b,
  have h2 : (2 : ℝ) ≠ 0,
    norm_num,
  field_simp [h2],
  ring, -- fails
  rw [sub_mul, mul_assoc],
  rw mul_self_sqrt,
  { ring},
  { norm_num}
end

lemma b_thing (d : ℕ) : b^(d+2) = b^(d+1) + b^d :=
begin
  rw [pow_add, b_min_pol],
  ring_exp,
end

theorem binet : ∀ n, (a^n-b^n)/sqrt(5) = fib n :=
begin
  -- prove using this modified induction principle
  apply induction2,
  { -- case n=0
    rw fib_0,
    rw pow_zero,
    rw pow_zero,
    -- goal now trivial to mathematicians
    rw [sub_self, zero_div], norm_cast,
  },
  { rw fib_1,
    rw pow_one,
    rw pow_one,
    rw [a,b],
    -- goal now trivial to mathematicians.
    -- division is scary, like subtraction is.
    -- top tip: tell Lean the denominators are non-zero.
    have h2 : (2 : ℝ) ≠ 0, by norm_num,
    have hs5 : sqrt 5 ≠ 0, by norm_num,
    field_simp [h2, hs5],
    ring},
  { intros d h1 h2,
    rw [a_thing, b_thing],
    rw fib_succ_succ,
    push_cast,
    rw [←h1, ←h2],
    ring}
end

lemma sub_mul_self_eq {R : Type*} [comm_ring R] (a b : R) : (a-b)^2=a^2-2*a*b+b^2 := by ring

-- Can now use Binet to prove things
example (n : ℕ) : fib (2*n+1)=fib(n)^2+fib(n+1)^2 :=
begin
  suffices : (fib (2*n+1) : ℝ) = (fib(n) : ℝ)^2 + (fib(n+1) : ℝ)^2,
    norm_cast at this,
    assumption,
  rw [←binet, ←binet, ←binet],
  have h5 : sqrt 5 ≠ 0,
    norm_num,
  field_simp [h5],
  -- ⊢ (√5)^2(a^(2n+1)-b^(2n+1))=√5((a^n-b^n)^2+(a^(n+1)-b^(n+1))^2)
  -- cancel a √5
  rw [pow_two, ←mul_assoc], congr',
  -- I think I am going to have to solve this by hand
  -- first turn √5's into a's or b's as appropriate
  have ha : sqrt 5 = 2 * a - 1,
    rw a, field_simp, ring,
  have hb : sqrt 5 = 1 - 2 * b,
    rw b, field_simp, ring,
  rw [sub_mul, sub_mul_self_eq, sub_mul_self_eq],
  conv begin
    to_lhs, congr, rw ha, skip, rw hb,
  end,
  -- a^n*b^n=(-1)^n
  rw [mul_assoc (2:ℝ), ←mul_pow],
  rw [mul_assoc (2:ℝ), ←mul_pow],
  have hab : a*b=-1,
  { rw [a,b],
    field_simp,
    ring,
    rw sqr_sqrt, norm_num, norm_num
  },
  -- perhaps this is actually tricky in real maths?
  rw hab,
  ring_exp,
  rw a_min_pol,
  rw b_min_pol,
  ring_exp,
end

-- integer approach involves proving a more general thing first
lemma fib_m_n (m n : ℕ) : fib(m+n+1)=fib(m+1)*fib(n+1)+fib(m)*fib(n) :=
begin
  -- I really don't fancy this using ℕ
  suffices : fibZ(m+n+1)=fibZ(m+1)*fibZ(n+1)+fibZ(m)*fibZ(n),
  { simp only [fibZ_eq_fib] at this,
    norm_cast at this,
    assumption},
  refine induction2 _ _ _ _ n,
  { simp},
  { show fibZ (m + 2) = fibZ (m + 1) * 1 + fibZ m * 1 ,
    rw fibZ_succ_succ,
    simp [add_comm]},
  { -- inductive step
    intros d h1 h2,
    show fibZ (m + (d + 1) + 2) = fibZ (m + 1) * fibZ ((d + 1) + 2) + fibZ m * fibZ (d + 2),
    rw fibZ_succ_succ d,
    rw fibZ_succ_succ (d+1),
    rw fibZ_succ_succ (m+(d+1)),
    rw h2,
    rw [←add_assoc m],
    rw h1,
    ring,
  }
end

example (n : ℕ) : fib (2*n+1)=fib(n)^2+fib(n+1)^2 :=
begin
  convert fib_m_n n n using 1; ring
end


import mynat.definition
import mynat.add

namespace mynat

lemma succ_add (a b : mynat) : succ(a) + b = succ(a + b) :=
begin [nat_num_game]
    induction b with d hd,
    {
        rw add_zero,
        rw add_zero,
        refl,
    },
    {
        rw add_succ,
        rw add_succ,
        rw hd,
        refl,
    }
end

end mynat


import .love09_hoare_logic_demo

/- # LoVe Exercise 9: Hoare Logic -/

set_option pp.beta true
set_option pp.generalized_field_notation false

namespace LoVe

/- ## Question 1: Program Verification

The following WHILE program is intended to compute the Gaussian sum up to `n`,
leaving the result in `r`. -/

def GAUSS : stmt :=
stmt.assign "r" (λs, 0) ;;
stmt.while (λs, s "n" ≠ 0)
  (stmt.assign "r" (λs, s "r" + s "n") ;;
   stmt.assign "n" (λs, s "n" - 1))

/- The summation function: -/

def sum_upto : ℕ → ℕ
| 0       := 0
| (n + 1) := n + 1 + sum_upto n

/- 1.1. Prove the correctness of `GAUSS` using `vcg`. The main challenge is to
figure out which invariant to use for the while loop. The invariant should
capture both the work that has been done already (the intermediate result) and
the work that remains to be done. -/

lemma GAUSS_correct (n₀ : ℕ) :
  {* λs, s "n" = n₀ *} GAUSS {* λs, s "r" = sum_upto n₀ *} :=
show {* λs, s "n" = n₀ *}
     stmt.assign "r" (λs, 0) ;;
     stmt.while_inv (λs, s "r" + sum_upto (s "n") = sum_upto n₀) (λs, s "n" ≠ 0)
       (stmt.assign "r" (λs, s "r" + s "n") ;;
        stmt.assign "n" (λs, s "n" - 1))
     {* λs, s "r" = sum_upto n₀ *}, from
  begin
    vcg; simp [sum_upto] { contextual := tt },
    intro s,
    cases' s "n",
    { simp },
    { simp [nat.succ_eq_add_one, sum_upto],
      cc }
  end

/- 1.2. The following WHILE program is intended to compute the product of `n`
and `m`, leaving the result in `r`. Prove its correctness using `vcg`.

Hint: If a variable `x` does not change in a program, it might be useful to
record this in the invariant, by adding a conjunct `s "x" = x₀`. -/

def MUL : stmt :=
stmt.assign "r" (λs, 0) ;;
stmt.while (λs, s "n" ≠ 0)
  (stmt.assign "r" (λs, s "r" + s "m") ;;
   stmt.assign "n" (λs, s "n" - 1))

lemma MUL_correct (n₀ m₀ : ℕ) :
  {* λs, s "n" = n₀ ∧ s "m" = m₀ *} MUL {* λs, s "r" = n₀ * m₀ *} :=
show {* λs, s "n" = n₀ ∧ s "m" = m₀ *}
     stmt.assign "r" (λs, 0) ;;
     stmt.while_inv (λs, s "m" = m₀ ∧ s "r" + s "n" * s "m" = n₀ * s "m")
         (λs, s "n" ≠ 0)
       (stmt.assign "r" (λs, s "r" + s "m") ;;
        stmt.assign "n" (λs, s "n" - 1))
     {* λs, s "r" = n₀ * m₀ *}, from
  begin
    vcg; simp { contextual := tt },
    intro s,
    cases' s "n",
    { simp },
    { simp [nat.succ_eq_add_one, mul_add, add_mul],
      cc }
  end

/- ## Question 2: Hoare Triples for Total Correctness -/

def total_hoare (P : state → Prop) (S : stmt) (Q : state → Prop) : Prop :=
∀s, P s → ∃t, (S, s) ⟹ t ∧ Q t

notation `[* ` P : 1 ` *] ` S : 1 ` [* ` Q : 1 ` *]` :=
total_hoare P S Q

namespace total_hoare

/- 2.1. Prove the consequence rule. -/

lemma consequence {P P' Q Q' : state → Prop} {S}
    (hS : [* P *] S [* Q *]) (hP : ∀s, P' s → P s) (hQ : ∀s, Q s → Q' s) :
  [* P' *] S [* Q' *] :=
begin
  intros s hs,
  cases' hS s (hP s hs) with t ht,
  apply exists.intro t,
  apply and.intro,
  { exact and.elim_left ht },
  { exact hQ t (and.elim_right ht) }
end

/- 2.2. Prove the rule for `skip`. -/

lemma skip_intro {P} :
  [* P *] stmt.skip [* P *] :=
begin
  intros s hs,
  apply exists.intro s,
  exact and.intro big_step.skip hs
end

/- 2.3. Prove the rule for `assign`. -/

lemma assign_intro {P : state → Prop} {x} {a : state → ℕ} :
  [* λs, P (s{x ↦ a s}) *] stmt.assign x a [* P *] :=
begin
  intros s hs,
  apply exists.intro (s{x ↦ a s}),
  exact and.intro big_step.assign hs
end

/- 2.4. Prove the rule for `seq`. -/

lemma seq_intro {P Q R S T} (hS : [* P *] S [* Q *]) (hT : [* Q *] T [* R *]) :
  [* P *] S ;; T [* R *] :=
begin
  intros s hs,
  cases' hS s hs with t hS',
  cases' hT t (and.elim_right hS') with u hT',
  apply exists.intro u,
  apply and.intro,
  { exact big_step.seq (and.elim_left hS') (and.elim_left hT') },
  { exact and.elim_right hT' }
end

/- 2.5. Complete the proof of the rule for `ite`.

Hint: This requires a case distinction on the truth value of `b s`. -/

lemma ite_intro {b P Q : state → Prop} {S T}
    (hS : [* λs, P s ∧ b s *] S [* Q *])
    (hT : [* λs, P s ∧ ¬ b s *] T [* Q *]) :
  [* P *] stmt.ite b S T [* Q *] :=
begin
  intros s hs,
  cases' classical.em (b s),
  { cases' hS s (and.intro hs h) with t ht,
    apply exists.intro t,
    apply and.intro,
    { exact big_step.ite_true h (and.elim_left ht) },
    { exact and.elim_right ht } },
  { cases' hT s (and.intro hs h) with t ht,
    apply exists.intro t,
    apply and.intro,
    { exact big_step.ite_false h (and.elim_left ht) },
    { exact and.elim_right ht } }
end

/- 2.6 (**optional**). Try to prove the rule for `while`.

The rule is parameterized by a loop invariant `I` and by a variant `V` that
decreases with each iteration of the loop body.

Before we prove the desired lemma, we introduce an auxiliary lemma. Its proof
requires well-founded induction. When using `while_var_intro_aux` as induction
hypothesis we recommend to do it directly after proving that the argument is
less than `v₀`:

    have ih : ∃u, (stmt.while b S, t) ⟹ u ∧ I u ∧ ¬ b u :=
      have V t < v₀ :=
        …,
      while_var_intro_aux (V t) …,

Similarly to `ite`, the proof requires a case distinction on `b s ∨ ¬ b s`. -/

lemma while_var_intro_aux {b : state → Prop} (I : state → Prop) (V : state → ℕ)
  {S} (h_inv : ∀v₀, [* λs, I s ∧ b s ∧ V s = v₀ *] S [* λs, I s ∧ V s < v₀ *]) :
  ∀v₀ s, V s = v₀ → I s → ∃t, (stmt.while b S, s) ⟹ t ∧ I t ∧ ¬ b t
| v₀ s V_eq hs :=
  begin
    cases' classical.em (b s) with hcs hncs,
    { have h_inv : ∃t, (S, s) ⟹ t ∧ I t ∧ V t < v₀ :=
        h_inv v₀ s (and.intro hs (and.intro hcs V_eq)),
      cases' h_inv with t ht,
      have ih : ∃u, (stmt.while b S, t) ⟹ u ∧ I u ∧ ¬ b u :=
        have V t < v₀ :=
          and.elim_right (and.elim_right ht),
        while_var_intro_aux (V t) t (by refl)
          (and.elim_left (and.elim_right ht)),
      cases' ih with u hu,
      apply exists.intro u,
      apply and.intro,
      { exact big_step.while_true hcs (and.elim_left ht) (and.elim_left hu) },
      { exact and.elim_right hu } },
    { apply exists.intro s,
      apply and.intro,
      { exact big_step.while_false hncs },
      { exact and.intro hs hncs } }
  end

lemma while_var_intro {b : state → Prop} (I : state → Prop) (V : state → ℕ) {S}
  (hinv : ∀v₀, [* λs, I s ∧ b s ∧ V s = v₀ *] S [* λs, I s ∧ V s < v₀ *]) :
  [* I *] stmt.while b S [* λs, I s ∧ ¬ b s *] :=
begin
  intros s hs,
  exact while_var_intro_aux I V hinv (V s) s (by refl) hs
end

end total_hoare

end LoVe


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

Integer power operation on fields.
-/

import algebra.group_power tactic.wlog

universe u

section field_power
open int nat
variables {α : Type u} [division_ring α]

@[simp] lemma zero_gpow : ∀ z : ℕ, z ≠ 0 → (0 : α)^z = 0
| 0 h := absurd rfl h
| (k+1) h := zero_mul _

def fpow (a : α) : ℤ → α
| (of_nat n) := a ^ n
| -[1+n] := 1/(a ^ (n+1))

lemma unit_pow {a : α} (ha : a ≠ 0) : ∀ n : ℕ, a ^ n = ↑((units.mk0 a ha)^n)
| 0 := by simp; refl
| (k+1) := by simp [_root_.pow_add]; congr; apply unit_pow

lemma fpow_eq_gpow {a : α} (h : a ≠ 0) : ∀ (z : ℤ), fpow a z = ↑(gpow (units.mk0 a h) z)
| (of_nat k) := by simp only [fpow, gpow]; apply unit_pow
| -[1+k] := by simp [fpow, gpow]; congr; apply unit_pow

lemma fpow_inv (a : α) : fpow a (-1) = a⁻¹ :=
begin change fpow a -[1+0] = a⁻¹, simp [fpow] end

lemma fpow_ne_zero_of_ne_zero {a : α} (ha : a ≠ 0) : ∀ (z : ℤ), fpow a z ≠ 0
| (of_nat n) := pow_ne_zero _ ha
| -[1+n] := one_div_ne_zero $ pow_ne_zero _ ha

@[simp] lemma fpow_zero {a : α} : fpow a 0 = 1 :=
pow_zero _

lemma fpow_add {a : α} (ha : a ≠ 0) (z1 z2 : ℤ) : fpow a (z1 + z2) = fpow a z1 * fpow a z2 :=
begin simp only [fpow_eq_gpow ha], rw ←units.mul_coe, congr, apply gpow_add end

end field_power

section discrete_field_power
open int nat
variables {α : Type u} [discrete_field α]

lemma zero_fpow : ∀ z : ℤ, z ≠ 0 → fpow (0 : α) z = 0
| (of_nat n) h :=
  have h2 : n ≠ 0, from assume : n = 0, by simpa [this] using h,
  by simp [h, h2, fpow]
| -[1+n] h :=
  have h1 : (0 : α) ^ (n+1) = 0, from zero_mul _,
  by simp [fpow, h1]

end discrete_field_power

section ordered_field_power
open int

variables {α : Type u} [discrete_linear_ordered_field α]

lemma fpow_nonneg_of_nonneg {a : α} (ha : a ≥ 0) : ∀ (z : ℤ), fpow a z ≥ 0
| (of_nat n) := pow_nonneg ha _
| -[1+n] := div_nonneg' zero_le_one $ pow_nonneg ha _

lemma fpow_le_of_le {x : α} (hx : 1 ≤ x) {a b : ℤ} (h : a ≤ b) : fpow x a ≤ fpow x b :=
begin
  induction a with a a; induction b with b b,
  { simp only [fpow],
    apply pow_le_pow hx,
    apply le_of_coe_nat_le_coe_nat h },
  { apply absurd h,
    apply not_le_of_gt,
    exact lt_of_lt_of_le (neg_succ_lt_zero _) (of_nat_nonneg _) },
  { simp only [fpow, one_div_eq_inv],
    apply le_trans (inv_le_one _); apply one_le_pow_of_one_le hx },
  { simp only [fpow],
    apply (one_div_le_one_div _ _).2,
    { apply pow_le_pow hx,
      have : -(↑(a+1) : ℤ) ≤ -(↑(b+1) : ℤ), from h,
      have h' := le_of_neg_le_neg this,
      apply le_of_coe_nat_le_coe_nat h' },
    repeat { apply pow_pos (lt_of_lt_of_le zero_lt_one hx) } }
end

lemma pow_le_max_of_min_le {x : α} (hx : x ≥ 1) {a b c : ℤ} (h : min a b ≤ c) :
      fpow x (-c) ≤ max (fpow x (-a)) (fpow x (-b)) :=
begin
  wlog hle : a ≤ b,
  have hnle : -b ≤ -a, from neg_le_neg hle,
  have hfle : fpow x (-b) ≤ fpow x (-a), from fpow_le_of_le hx hnle,
  have : fpow x (-c) ≤ fpow x (-a),
  { apply fpow_le_of_le hx,
    simpa [hle] using h },
  simpa [hfle] using this
end

end ordered_field_power


import util.list.pairwise
import util.list.nil

def ord_insert
  {t: Type}
  [has_le t]
  [@decidable_rel t has_le.le]
  (x: t)
: list t -> list t
| [] := [x]
| (y :: ys) := if x ≤ y then x :: y :: ys else y :: ord_insert ys

def insertion_sort
  {t: Type}
  [has_le t]
  [@decidable_rel t has_le.le]
: list t -> list t
| [] := []
| (x :: xs) := ord_insert x (insertion_sort xs)

def sorted {t: Type} [has_le t]: list t -> Prop := pairwise (≤)

theorem ord_insert_mem {t: Type} {xs: list t} {x y: t}
  [has_le t] [@decidable_rel t has_le.le]:
  x ∈ ord_insert y xs ↔
  x = y ∨ x ∈ xs :=
begin
  split,
  intro in_ins,
  induction xs,
  simp [ord_insert] at in_ins,
  left,
  exact in_ins,
  simp [ord_insert] at in_ins,
  by_cases y ≤ xs_hd,
  simp [h] at in_ins,
  exact in_ins,
  simp [h] at in_ins,
  cases in_ins,
  right,
  simp,
  left,
  exact in_ins,
  cases xs_ih in_ins,
  left,
  exact h_1,
  right,
  simp,
  right,
  exact h_1,
  intro h,
  induction xs,
  simp [ord_insert],
  cases h,
  exact h,
  exfalso,
  exact list.not_mem_nil x h,
  simp [ord_insert],
  by_cases h_1: y ≤ xs_hd,
  simp [h_1],
  exact h,
  simp [h_1],
  cases h,
  right,
  exact xs_ih (or.inl h),
  cases h,
  left,
  exact h,
  right,
  exact xs_ih (or.inr h),
end

theorem ord_insert_spec
  {t: Type}
  [has_le t]
  [@decidable_rel t has_le.le]
  [is_trans t has_le.le]
  [is_total t has_le.le]
  (x: t) (xs: list t):
  sorted xs -> sorted (ord_insert x xs) :=
begin
  intro h,
  induction h,
  simp [ord_insert],
  exact pairwise.cons (list.ball_nil (fun y, x ≤ y)) (pairwise.nil (≤)),
  simp [ord_insert],
  by_cases x ≤ h_x,
  simp [h],
  let a: sorted (h_x :: h_xs) := pairwise.cons h_a h_a_1,
  let b: ∀ (a: t), a ∈ h_x :: h_xs -> x ≤ a := fun a, fun b,
    or.elim b
    (fun b, begin rw b, exact h end)
    (fun b, trans h (h_a a b)),
  exact pairwise.cons b a,
  simp [h],
  let b: ∀ (a: t), a ∈ ord_insert x h_xs -> h_x ≤ a := fun a, fun b,
  begin
    cases iff.elim_left ord_insert_mem b,
    rw h_1,
    cases is_total.total (≤) x h_x,
    exfalso,
    exact h h_2,
    exact h_2,
    exact h_a a h_1,
  end,
  exact pairwise.cons b h_ih,
end

theorem ord_insert_pred {t: Type} {p: t -> Prop} {xs: list t} {x: t}
  [has_le t] [@decidable_rel t has_le.le]:
  (∀ (y ∈ xs), p y) ->
  p x ->
  (∀ (y ∈ ord_insert x xs), p y) :=
begin
  intros in_xs r_xx y in_insert,
  induction xs,
  simp [ord_insert] at in_insert,
  rw in_insert,
  exact r_xx,
  simp [ord_insert] at in_insert,
  by_cases x ≤ xs_hd,
  simp [h] at in_insert,
  cases in_insert,
  rw in_insert,
  exact r_xx,
  cases in_insert,
  rw in_insert,
  exact in_xs xs_hd (or.inl rfl),
  exact in_xs y (or.inr in_insert),
  simp [h] at in_insert,
  cases in_insert,
  rw in_insert,
  exact in_xs xs_hd (or.inl rfl),
  let in_xs' := fun y, fun h, in_xs y (or.inr h),
  exact xs_ih in_xs' in_insert,
end

theorem ord_insert_pairwise {t: Type} {r: t -> t -> Prop} {xs: list t} {x: t}
  [is_symm t r] [has_le t] [@decidable_rel t has_le.le]:
  (∀ (y ∈ xs), r x y) ->
  pairwise r xs ->
  pairwise r (ord_insert x xs) :=
begin
  intros in_xs pw_xs,
  induction pw_xs,
  simp [ord_insert],
  exact pairwise.cons (list.ball_nil (fun y, r x y)) (pairwise.nil r),
  simp [ord_insert],
  by_cases x ≤ pw_xs_x,
  simp [h],
  let a := pairwise.cons pw_xs_a pw_xs_a_1,
  exact pairwise.cons in_xs a,
  simp [h],
  let in_xs' := fun y, fun h, in_xs y (or.inr h),
  let a := pw_xs_ih in_xs',
  let b := in_xs pw_xs_x (or.inl rfl),
  let c := ord_insert_pred pw_xs_a (symm b),
  exact pairwise.cons c a,
end

theorem insertion_sort_spec
  {t: Type}
  [has_le t]
  [@decidable_rel t has_le.le]
  [is_trans t has_le.le]
  [is_total t has_le.le]
  (xs: list t):
  sorted (insertion_sort xs) :=
begin
  induction xs,
  simp [insertion_sort],
  exact pairwise.nil (≤),
  simp [insertion_sort],
  exact ord_insert_spec xs_hd (insertion_sort xs_tl) xs_ih,
end

theorem insertion_sort_pred {t: Type} {p: t -> Prop} {xs: list t}
  [has_le t] [@decidable_rel t has_le.le]:
  (∀ (y ∈ xs), p y) ->
  (∀ (y ∈ insertion_sort xs), p y) :=
begin
  intros in_xs y in_sort,
  induction xs,
  simp [insertion_sort] at in_sort,
  exfalso,
  exact in_sort,
  simp [insertion_sort] at in_sort,
  cases iff.elim_left ord_insert_mem in_sort,
  rw h,
  exact in_xs xs_hd (or.inl rfl),
  let in_xs' := fun y, fun h, in_xs y (or.inr h),
  exact xs_ih in_xs' h,
end

theorem insertion_sort_pairwise {t: Type} {r: t -> t -> Prop} {xs: list t}
  [is_symm t r] [has_le t] [@decidable_rel t has_le.le]:
  pairwise r xs ->
  pairwise r (insertion_sort xs) :=
begin
  intro h,
  induction h,
  simp [insertion_sort],
  exact pairwise.nil r,
  simp [insertion_sort],
  let a := insertion_sort_pred h_a,
  simp at a,
  exact ord_insert_pairwise a h_ih,
end

theorem insertion_sort_mem {t: Type} {xs: list t} {x: t}
  [has_le t] [@decidable_rel t has_le.le]:
  x ∈ xs ↔ x ∈ insertion_sort xs :=
begin
  split,
  intro x_in,
  induction xs,
  exfalso,
  exact list.not_mem_nil x x_in,
  simp [insertion_sort],
  cases x_in,
  exact iff.elim_right ord_insert_mem (or.inl x_in),
  exact iff.elim_right ord_insert_mem (or.inr (xs_ih x_in)),
  intro x_in,
  induction xs,
  simp [insertion_sort] at x_in,
  exfalso,
  exact x_in,
  simp [insertion_sort] at x_in,
  cases iff.elim_left ord_insert_mem x_in,
  left,
  exact h,
  right,
  exact xs_ih h,
end

theorem sorted_ne_eq {t: Type} {xs ys: list t} [decidable_linear_order t]:
  (∀ (x: t), x ∈ xs ↔ x ∈ ys) ->
  pairwise ne xs ->
  pairwise ne ys ->
  sorted xs ->
  sorted ys ->
  xs = ys :=
begin
  intros x_in p_xs p_ys s_xs s_ys,
  induction xs generalizing ys,
  exact symm (iff.elim_left nothing_mem_nil (fun x xi,
    list.not_mem_nil x (iff.elim_right (x_in x) xi))),
  cases ys,
  exfalso,
  exact list.not_mem_nil xs_hd (iff.elim_left (x_in xs_hd) (or.inl rfl)),
  cases p_xs,
  cases p_ys,
  cases s_xs,
  cases s_ys,
  cases iff.elim_left (x_in xs_hd) (or.inl rfl),
  let x_in': ∀ (x: t), x ∈ xs_tl ↔ x ∈ ys_tl := begin
    intro x,
    split,
    intro xi,
    cases iff.elim_left (x_in x) (or.inr xi),
    rw symm h_1 at h,
    exfalso,
    exact p_xs_a x xi h,
    exact h_1,
    intro xi,
    cases iff.elim_right (x_in x) (or.inr xi),
    rw symm h_1 at h,
    exfalso,
    exact p_ys_a x xi (symm h),
    exact h_1,
  end,
  let a := xs_ih p_xs_a_1 s_xs_a_1 x_in' p_ys_a_1 s_ys_a_1,
  rw h,
  rw a,
  cases iff.elim_right (x_in ys_hd) (or.inl rfl),
  exfalso,
  exact p_ys_a xs_hd h h_1,
  let a := s_ys_a xs_hd h,
  let b := s_xs_a ys_hd h_1,
  exfalso,
  exact p_ys_a xs_hd h (le_antisymm a b),
end


/-
Copyright (c) 2021 Eric Rodriguez. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Eric Rodriguez

! This file was ported from Lean 3 source module algebra.ne_zero
! leanprover-community/mathlib commit f340f229b1f461aa1c8ee11e0a172d0a3b301a4a
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/

import Mathlib.Logic.Basic
import Mathlib.Init.ZeroOne
import Mathlib.Init.Algebra.Order

/-!
# `NeZero` typeclass

We create a typeclass `NeZero n` which carries around the fact that `(n : R) ≠ 0`.

## Main declarations

* `NeZero`: `n ≠ 0` as a typeclass.
-/

/-- A type-class version of `n ≠ 0`.  -/
class NeZero {R} [Zero R] (n : R) : Prop where
  /-- The proposition that `n` is not zero. -/
  out : n ≠ 0
#align ne_zero NeZero

theorem NeZero.ne {R} [Zero R] (n : R) [h : NeZero n] : n ≠ 0 :=
  h.out
#align ne_zero.ne NeZero.ne

theorem NeZero.ne' {R} [Zero R] (n : R) [h : NeZero n] : 0 ≠ n :=
  h.out.symm
#align ne_zero.ne' NeZero.ne'

theorem neZero_iff {R : Type _} [Zero R] {n : R} : NeZero n ↔ n ≠ 0 :=
  ⟨fun h ↦ h.out, NeZero.mk⟩
#align ne_zero_iff neZero_iff

theorem not_neZero {R : Type _} [Zero R] {n : R} : ¬NeZero n ↔ n = 0 := by simp [neZero_iff]
#align not_ne_zero not_neZero

theorem eq_zero_or_neZero {α} [Zero α] (a : α) : a = 0 ∨ NeZero a :=
  (eq_or_ne a 0).imp_right NeZero.mk
#align eq_zero_or_ne_zero eq_zero_or_neZero

section
variable {α : Type _} [Zero α]

@[simp] lemma zero_ne_one [One α] [NeZero (1 : α)] : (0 : α) ≠ 1 := NeZero.ne' (1 : α)
@[simp] lemma one_ne_zero [One α] [NeZero (1 : α)] : (1 : α) ≠ 0 := NeZero.ne (1 : α)
#align one_ne_zero one_ne_zero
#align zero_ne_one zero_ne_one

lemma ne_zero_of_eq_one [One α] [NeZero (1 : α)] {a : α} (h : a = 1) : a ≠ 0 := h ▸ one_ne_zero
#align ne_zero_of_eq_one ne_zero_of_eq_one

lemma two_ne_zero [OfNat α 2] [NeZero (2 : α)] : (2 : α) ≠ 0 := NeZero.ne (2 : α)
lemma three_ne_zero [OfNat α 3] [NeZero (3 : α)] : (3 : α) ≠ 0 := NeZero.ne (3 : α)
lemma four_ne_zero [OfNat α 4] [NeZero (4 : α)] : (4 : α) ≠ 0 := NeZero.ne (4 : α)
#align four_ne_zero four_ne_zero
#align three_ne_zero three_ne_zero
#align two_ne_zero two_ne_zero

variable (α)

lemma zero_ne_one' [One α] [NeZero (1 : α)] : (0 : α) ≠ 1 := zero_ne_one
lemma one_ne_zero' [One α] [NeZero (1 : α)] : (1 : α) ≠ 0 := one_ne_zero
lemma two_ne_zero' [OfNat α 2] [NeZero (2 : α)] : (2 : α) ≠ 0 := two_ne_zero
lemma three_ne_zero' [OfNat α 3] [NeZero (3 : α)] : (3 : α) ≠ 0 := three_ne_zero
lemma four_ne_zero' [OfNat α 4] [NeZero (4 : α)] : (4 : α) ≠ 0 := four_ne_zero
#align four_ne_zero' four_ne_zero'
#align three_ne_zero' three_ne_zero'
#align two_ne_zero' two_ne_zero'
#align one_ne_zero' one_ne_zero'
#align zero_ne_one' zero_ne_one'

end

namespace NeZero

variable {M : Type _} {x : M}

instance succ : NeZero (n + 1) := ⟨n.succ_ne_zero⟩

theorem of_pos [Preorder M] [Zero M] (h : 0 < x) : NeZero x := ⟨ne_of_gt h⟩
#align ne_zero.of_pos NeZero.of_pos

end NeZero