l3lab/ntp-mathlib huggingface dataset:
/-- Equivalence between `Fin m × Fin n` and `Fin (m * n)` -/
@[simps]
def finProdFinEquiv : Fin m × Fin n ≃ Fin (m * n)
    where
  toFun x :=
    ⟨x.2 + n * x.1,
      calc
        x.2.1 + n * x.1.1 + 1 = x.1.1 * n + x.2.1 + 1 := by ac_rfl
        _ ≤ x.1.1 * n + n := Nat.add_le_add_left x.2.2 _
        _ = (x.1.1 + 1) * n := Eq.symm <| Nat.succ_mul _ _
        _ ≤ m * n := Nat.mul_le_mul_right _ x.1.2
        ⟩
  invFun x := (x.divNat, x.modNat)
  left_inv := fun ⟨x, y⟩ =>
    have H : 0 < n := Nat.pos_of_ne_zero fun H => Nat.not_lt_zero y.1 <| H ▸ y.2
    Prod.ext
      (Fin.eq_of_veq <|
        calc
          (y.1 + n * x.1) / n = y.1 / n + x.1 := Nat.add_mul_div_left _ _ H
          _ = 0 + x.1 := by 

Proof state:
m n : ℕ
x✝ : Fin m × Fin n
x : Fin m
y : Fin n
H : 0 < n
⊢ ↑y / n + ↑x = 0 + ↑x

Next tactic:
rw [Nat.div_eq_of_lt y.2]
/-- The equivalence induced by `a ↦ (a / n, a % n)` for nonzero `n`.
This is like `finProdFinEquiv.symm` but with `m` infinite.
See `Nat.div_mod_unique` for a similar propositional statement. -/
@[simps]
def Nat.divModEquiv (n : ℕ) [NeZero n] : ℕ ≃ ℕ × Fin n where
  toFun a := (a / n, ↑a)
  invFun p := p.1 * n + ↑p.2
  -- TODO: is there a canonical order of `*` and `+` here?
  left_inv a := Nat.div_add_mod' _ _
  right_inv p := by
    

Proof state:
m n✝ n : ℕ
inst✝ : NeZero n
p : ℕ × Fin n
⊢ (fun a => (a / n, ↑a)) ((fun p => p.1 * n + ↑p.2) p) = p

Next tactic:
refine' Prod.ext _ (Fin.ext <| Nat.mul_add_mod_of_lt p.2.is_lt)
/-- The equivalence induced by `a ↦ (a / n, a % n)` for nonzero `n`.
This is like `finProdFinEquiv.symm` but with `m` infinite.
See `Nat.div_mod_unique` for a similar propositional statement. -/
@[simps]
def Nat.divModEquiv (n : ℕ) [NeZero n] : ℕ ≃ ℕ × Fin n where
  toFun a := (a / n, ↑a)
  invFun p := p.1 * n + ↑p.2
  -- TODO: is there a canonical order of `*` and `+` here?
  left_inv a := Nat.div_add_mod' _ _
  right_inv p := by
    refine' Prod.ext _ (Fin.ext <| Nat.mul_add_mod_of_lt p.2.is_lt)
    

Proof state:
m n✝ n : ℕ
inst✝ : NeZero n
p : ℕ × Fin n
⊢ ((fun a => (a / n, ↑a)) ((fun p => p.1 * n + ↑p.2) p)).1 = p.1

Next tactic:
dsimp only
/-- The equivalence induced by `a ↦ (a / n, a % n)` for nonzero `n`.
This is like `finProdFinEquiv.symm` but with `m` infinite.
See `Nat.div_mod_unique` for a similar propositional statement. -/
@[simps]
def Nat.divModEquiv (n : ℕ) [NeZero n] : ℕ ≃ ℕ × Fin n where
  toFun a := (a / n, ↑a)
  invFun p := p.1 * n + ↑p.2
  -- TODO: is there a canonical order of `*` and `+` here?
  left_inv a := Nat.div_add_mod' _ _
  right_inv p := by
    refine' Prod.ext _ (Fin.ext <| Nat.mul_add_mod_of_lt p.2.is_lt)
    dsimp only
    

Proof state:
m n✝ n : ℕ
inst✝ : NeZero n
p : ℕ × Fin n
⊢ (p.1 * n + ↑p.2) / n = p.1

Next tactic:
rw [add_comm, Nat.add_mul_div_right _ _ (NeZero.pos n), Nat.div_eq_of_lt p.2.is_lt, zero_add]
/-- The equivalence induced by `a ↦ (a / n, a % n)` for nonzero `n`.
See `Int.ediv_emod_unique` for a similar propositional statement. -/
@[simps]
def Int.divModEquiv (n : ℕ) [NeZero n] : ℤ ≃ ℤ × Fin n where
  -- TODO: could cast from int directly if we import `data.zmod.defs`, though there are few lemmas
  -- about that coercion.
  toFun a := (a / n, ↑(a.natMod n))
  invFun p := p.1 * n + ↑p.2
  left_inv a := by
    

Proof state:
m n✝ n : ℕ
inst✝ : NeZero n
a : ℤ
⊢ (fun p => p.1 * ↑n + ↑↑p.2) ((fun a => (a / ↑n, ↑(natMod a ↑n))) a) = a

Next tactic:
simp_rw [Fin.coe_ofNat_eq_mod, Int.coe_nat_mod, Int.natMod,
      Int.toNat_of_nonneg (Int.emod_nonneg _ <| NeZero.ne ↑n), Int.emod_emod,
      Int.ediv_add_emod']
/-- The equivalence induced by `a ↦ (a / n, a % n)` for nonzero `n`.
See `Int.ediv_emod_unique` for a similar propositional statement. -/
@[simps]
def Int.divModEquiv (n : ℕ) [NeZero n] : ℤ ≃ ℤ × Fin n where
  -- TODO: could cast from int directly if we import `data.zmod.defs`, though there are few lemmas
  -- about that coercion.
  toFun a := (a / n, ↑(a.natMod n))
  invFun p := p.1 * n + ↑p.2
  left_inv a := by
    simp_rw [Fin.coe_ofNat_eq_mod, Int.coe_nat_mod, Int.natMod,
      Int.toNat_of_nonneg (Int.emod_nonneg _ <| NeZero.ne ↑n), Int.emod_emod,
      Int.ediv_add_emod']
  right_inv := fun ⟨q, r, hrn⟩ => by
    

Proof state:
m n✝ n : ℕ
inst✝ : NeZero n
x✝ : ℤ × Fin n
q : ℤ
r : ℕ
hrn : r < n
⊢ (fun a => (a / ↑n, ↑(natMod a ↑n))) ((fun p => p.1 * ↑n + ↑↑p.2) (q, { val := r, isLt := hrn })) =
    (q, { val := r, isLt := hrn })

Next tactic:
simp only [Fin.val_mk, Prod.mk.inj_iff, Fin.ext_iff]
/-- The equivalence induced by `a ↦ (a / n, a % n)` for nonzero `n`.
See `Int.ediv_emod_unique` for a similar propositional statement. -/
@[simps]
def Int.divModEquiv (n : ℕ) [NeZero n] : ℤ ≃ ℤ × Fin n where
  -- TODO: could cast from int directly if we import `data.zmod.defs`, though there are few lemmas
  -- about that coercion.
  toFun a := (a / n, ↑(a.natMod n))
  invFun p := p.1 * n + ↑p.2
  left_inv a := by
    simp_rw [Fin.coe_ofNat_eq_mod, Int.coe_nat_mod, Int.natMod,
      Int.toNat_of_nonneg (Int.emod_nonneg _ <| NeZero.ne ↑n), Int.emod_emod,
      Int.ediv_add_emod']
  right_inv := fun ⟨q, r, hrn⟩ => by
    simp only [Fin.val_mk, Prod.mk.inj_iff, Fin.ext_iff]
    

Proof state:
m n✝ n : ℕ
inst✝ : NeZero n
x✝ : ℤ × Fin n
q : ℤ
r : ℕ
hrn : r < n
⊢ (q * ↑n + ↑r) / ↑n = q ∧ ↑↑(natMod (q * ↑n + ↑r) ↑n) = r

Next tactic:
obtain ⟨h1, h2⟩ := Int.coe_nat_nonneg r, Int.ofNat_lt.2 hrn
/-- The equivalence induced by `a ↦ (a / n, a % n)` for nonzero `n`.
See `Int.ediv_emod_unique` for a similar propositional statement. -/
@[simps]
def Int.divModEquiv (n : ℕ) [NeZero n] : ℤ ≃ ℤ × Fin n where
  -- TODO: could cast from int directly if we import `data.zmod.defs`, though there are few lemmas
  -- about that coercion.
  toFun a := (a / n, ↑(a.natMod n))
  invFun p := p.1 * n + ↑p.2
  left_inv a := by
    simp_rw [Fin.coe_ofNat_eq_mod, Int.coe_nat_mod, Int.natMod,
      Int.toNat_of_nonneg (Int.emod_nonneg _ <| NeZero.ne ↑n), Int.emod_emod,
      Int.ediv_add_emod']
  right_inv := fun ⟨q, r, hrn⟩ => by
    simp only [Fin.val_mk, Prod.mk.inj_iff, Fin.ext_iff]
    obtain ⟨h1, h2⟩ := Int.coe_nat_nonneg r, Int.ofNat_lt.2 hrn
    

Proof state:
m n✝ n : ℕ
inst✝ : NeZero n
x✝ : ℤ × Fin n
q : ℤ
r : ℕ
hrn : r < n
h1 : 0 ≤ ↑r
h2 : ↑r < ↑n
⊢ (q * ↑n + ↑r) / ↑n = q ∧ ↑↑(natMod (q * ↑n + ↑r) ↑n) = r

Next tactic:
rw [add_comm, Int.add_mul_ediv_right _ _ (NeZero.ne ↑n), Int.ediv_eq_zero_of_lt h1 h2,
      Int.natMod, Int.add_mul_emod_self, Int.emod_eq_of_lt h1 h2, Int.toNat_coe_nat]
/-- The equivalence induced by `a ↦ (a / n, a % n)` for nonzero `n`.
See `Int.ediv_emod_unique` for a similar propositional statement. -/
@[simps]
def Int.divModEquiv (n : ℕ) [NeZero n] : ℤ ≃ ℤ × Fin n where
  -- TODO: could cast from int directly if we import `data.zmod.defs`, though there are few lemmas
  -- about that coercion.
  toFun a := (a / n, ↑(a.natMod n))
  invFun p := p.1 * n + ↑p.2
  left_inv a := by
    simp_rw [Fin.coe_ofNat_eq_mod, Int.coe_nat_mod, Int.natMod,
      Int.toNat_of_nonneg (Int.emod_nonneg _ <| NeZero.ne ↑n), Int.emod_emod,
      Int.ediv_add_emod']
  right_inv := fun ⟨q, r, hrn⟩ => by
    simp only [Fin.val_mk, Prod.mk.inj_iff, Fin.ext_iff]
    obtain ⟨h1, h2⟩ := Int.coe_nat_nonneg r, Int.ofNat_lt.2 hrn
    rw [add_comm, Int.add_mul_ediv_right _ _ (NeZero.ne ↑n), Int.ediv_eq_zero_of_lt h1 h2,
      Int.natMod, Int.add_mul_emod_self, Int.emod_eq_of_lt h1 h2, Int.toNat_coe_nat]
    

Proof state:
m n✝ n : ℕ
inst✝ : NeZero n
x✝ : ℤ × Fin n
q : ℤ
r : ℕ
hrn : r < n
h1 : 0 ≤ ↑r
h2 : ↑r < ↑n
⊢ 0 + q = q ∧ ↑↑r = r

Next tactic:
exact ⟨zero_add q, Fin.val_cast_of_lt hrn⟩
/-- Promote a `Fin n` into a larger `Fin m`, as a subtype where the underlying
values are retained. This is the `OrderIso` version of `Fin.castLE`. -/
@[simps apply symm_apply]
def Fin.castLEOrderIso {n m : ℕ} (h : n ≤ m) : Fin n ≃o { i : Fin m // (i : ℕ) < n }
    where
  toFun i := ⟨Fin.castLE h i, by 

Proof state:
m✝ n✝ n m : ℕ
h : n ≤ m
i : Fin n
⊢ ↑(castLE h i) < n

Next tactic:
simp
/-- Promote a `Fin n` into a larger `Fin m`, as a subtype where the underlying
values are retained. This is the `OrderIso` version of `Fin.castLE`. -/
@[simps apply symm_apply]
def Fin.castLEOrderIso {n m : ℕ} (h : n ≤ m) : Fin n ≃o { i : Fin m // (i : ℕ) < n }
    where
  toFun i := ⟨Fin.castLE h i, by simp⟩
  invFun i := ⟨i, i.prop⟩
  left_inv _ := by 

Proof state:
m✝ n✝ n m : ℕ
h : n ≤ m
x✝ : Fin n
⊢ (fun i => { val := ↑↑i, isLt := (_ : ↑↑i < n) }) ((fun i => { val := castLE h i, property := (_ : ↑i < n) }) x✝) = x✝

Next tactic:
simp
/-- Promote a `Fin n` into a larger `Fin m`, as a subtype where the underlying
values are retained. This is the `OrderIso` version of `Fin.castLE`. -/
@[simps apply symm_apply]
def Fin.castLEOrderIso {n m : ℕ} (h : n ≤ m) : Fin n ≃o { i : Fin m // (i : ℕ) < n }
    where
  toFun i := ⟨Fin.castLE h i, by simp⟩
  invFun i := ⟨i, i.prop⟩
  left_inv _ := by simp
  right_inv _ := by 

Proof state:
m✝ n✝ n m : ℕ
h : n ≤ m
x✝ : { i // ↑i < n }
⊢ (fun i => { val := castLE h i, property := (_ : ↑i < n) }) ((fun i => { val := ↑↑i, isLt := (_ : ↑↑i < n) }) x✝) = x✝

Next tactic:
simp
/-- Promote a `Fin n` into a larger `Fin m`, as a subtype where the underlying
values are retained. This is the `OrderIso` version of `Fin.castLE`. -/
@[simps apply symm_apply]
def Fin.castLEOrderIso {n m : ℕ} (h : n ≤ m) : Fin n ≃o { i : Fin m // (i : ℕ) < n }
    where
  toFun i := ⟨Fin.castLE h i, by simp⟩
  invFun i := ⟨i, i.prop⟩
  left_inv _ := by simp
  right_inv _ := by simp
  map_rel_iff' := by 

Proof state:
m✝ n✝ n m : ℕ
h : n ≤ m
⊢ ∀ {a b : Fin n},
    { toFun := fun i => { val := castLE h i, property := (_ : ↑i < n) },
            invFun := fun i => { val := ↑↑i, isLt := (_ : ↑↑i < n) }, left_inv := (_ : ∀ (x : Fin n), x = x),
            right_inv := (_ : ∀ (x : { i // ↑i < n }), { val := ↑x, property := (_ : (fun i => ↑i < n) ↑x) } = x) }
          a ≤
        { toFun := fun i => { val := castLE h i, property := (_ : ↑i < n) },
            invFun := fun i => { val := ↑↑i, isLt := (_ : ↑↑i < n) }, left_inv := (_ : ∀ (x : Fin n), x = x),
            right_inv := (_ : ∀ (x : { i // ↑i < n }), { val := ↑x, property := (_ : (fun i => ↑i < n) ↑x) } = x) }
          b ↔
      a ≤ b

Next tactic:
simp [(strictMono_castLE h).le_iff_le]
@[to_additive]
theorem IsSubgroup.div_mem {s : Set G} (hs : IsSubgroup s) {x y : G} (hx : x ∈ s) (hy : y ∈ s) :
    x / y ∈ s := by 

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set G
hs : IsSubgroup s
x y : G
hx : x ∈ s
hy : y ∈ s
⊢ x / y ∈ s

Next tactic:
simpa only [div_eq_mul_inv] using hs.mul_mem hx (hs.inv_mem hy)
theorem Additive.isAddSubgroup_iff {s : Set G} : @IsAddSubgroup (Additive G) _ s ↔ IsSubgroup s :=
  ⟨by 

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set G
⊢ IsAddSubgroup s → IsSubgroup s

Next tactic:
rintro ⟨⟨h₁, h₂⟩, h₃⟩
theorem Additive.isAddSubgroup_iff {s : Set G} : @IsAddSubgroup (Additive G) _ s ↔ IsSubgroup s :=
  ⟨by rintro ⟨⟨h₁, h₂⟩, h₃⟩; 

Proof state:
case mk.mk
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set G
h₃ : ∀ {a : Additive G}, a ∈ s → -a ∈ s
h₁ : 0 ∈ s
h₂ : ∀ {a b : Additive G}, a ∈ s → b ∈ s → a + b ∈ s
⊢ IsSubgroup s

Next tactic:
exact @IsSubgroup.mk G _ _ ⟨h₁, @h₂⟩ @h₃
theorem Multiplicative.isSubgroup_iff {s : Set A} :
    @IsSubgroup (Multiplicative A) _ s ↔ IsAddSubgroup s :=
  ⟨by 

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set A
⊢ IsSubgroup s → IsAddSubgroup s

Next tactic:
rintro ⟨⟨h₁, h₂⟩, h₃⟩
theorem Multiplicative.isSubgroup_iff {s : Set A} :
    @IsSubgroup (Multiplicative A) _ s ↔ IsAddSubgroup s :=
  ⟨by rintro ⟨⟨h₁, h₂⟩, h₃⟩; 

Proof state:
case mk.mk
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set A
h₃ : ∀ {a : Multiplicative A}, a ∈ s → a⁻¹ ∈ s
h₁ : 1 ∈ s
h₂ : ∀ {a b : Multiplicative A}, a ∈ s → b ∈ s → a * b ∈ s
⊢ IsAddSubgroup s

Next tactic:
exact @IsAddSubgroup.mk A _ _ ⟨h₁, @h₂⟩ @h₃
@[to_additive of_add_neg]
theorem IsSubgroup.of_div (s : Set G) (one_mem : (1 : G) ∈ s)
    (div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s) : IsSubgroup s :=
  have inv_mem : ∀ a, a ∈ s → a⁻¹ ∈ s := fun a ha => by
    

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a✝ a₁ a₂ b c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set G
one_mem : 1 ∈ s
div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s
a : G
ha : a ∈ s
⊢ a⁻¹ ∈ s

Next tactic:
have : 1 * a⁻¹ ∈ s := div_mem one_mem ha
@[to_additive of_add_neg]
theorem IsSubgroup.of_div (s : Set G) (one_mem : (1 : G) ∈ s)
    (div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s) : IsSubgroup s :=
  have inv_mem : ∀ a, a ∈ s → a⁻¹ ∈ s := fun a ha => by
    have : 1 * a⁻¹ ∈ s := div_mem one_mem ha
    

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a✝ a₁ a₂ b c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set G
one_mem : 1 ∈ s
div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s
a : G
ha : a ∈ s
this : 1 * a⁻¹ ∈ s
⊢ a⁻¹ ∈ s

Next tactic:
convert this using 1
@[to_additive of_add_neg]
theorem IsSubgroup.of_div (s : Set G) (one_mem : (1 : G) ∈ s)
    (div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s) : IsSubgroup s :=
  have inv_mem : ∀ a, a ∈ s → a⁻¹ ∈ s := fun a ha => by
    have : 1 * a⁻¹ ∈ s := div_mem one_mem ha
    convert this using 1
    

Proof state:
case h.e'_4
G : Type u_1
H : Type u_2
A : Type u_3
a✝ a₁ a₂ b c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set G
one_mem : 1 ∈ s
div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s
a : G
ha : a ∈ s
this : 1 * a⁻¹ ∈ s
⊢ a⁻¹ = 1 * a⁻¹

Next tactic:
rw [one_mul]
@[to_additive of_add_neg]
theorem IsSubgroup.of_div (s : Set G) (one_mem : (1 : G) ∈ s)
    (div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s) : IsSubgroup s :=
  have inv_mem : ∀ a, a ∈ s → a⁻¹ ∈ s := fun a ha => by
    have : 1 * a⁻¹ ∈ s := div_mem one_mem ha
    convert this using 1
    rw [one_mul]
  { inv_mem := inv_mem _
    mul_mem := fun {a b} ha hb => by
      

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a✝ a₁ a₂ b✝ c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set G
one_mem : 1 ∈ s
div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s
inv_mem : ∀ a ∈ s, a⁻¹ ∈ s
a b : G
ha : a ∈ s
hb : b ∈ s
⊢ a * b ∈ s

Next tactic:
have : a * b⁻¹⁻¹ ∈ s := div_mem ha (inv_mem b hb)
@[to_additive of_add_neg]
theorem IsSubgroup.of_div (s : Set G) (one_mem : (1 : G) ∈ s)
    (div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s) : IsSubgroup s :=
  have inv_mem : ∀ a, a ∈ s → a⁻¹ ∈ s := fun a ha => by
    have : 1 * a⁻¹ ∈ s := div_mem one_mem ha
    convert this using 1
    rw [one_mul]
  { inv_mem := inv_mem _
    mul_mem := fun {a b} ha hb => by
      have : a * b⁻¹⁻¹ ∈ s := div_mem ha (inv_mem b hb)
      

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a✝ a₁ a₂ b✝ c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set G
one_mem : 1 ∈ s
div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s
inv_mem : ∀ a ∈ s, a⁻¹ ∈ s
a b : G
ha : a ∈ s
hb : b ∈ s
this : a * b⁻¹⁻¹ ∈ s
⊢ a * b ∈ s

Next tactic:
convert this
@[to_additive of_add_neg]
theorem IsSubgroup.of_div (s : Set G) (one_mem : (1 : G) ∈ s)
    (div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s) : IsSubgroup s :=
  have inv_mem : ∀ a, a ∈ s → a⁻¹ ∈ s := fun a ha => by
    have : 1 * a⁻¹ ∈ s := div_mem one_mem ha
    convert this using 1
    rw [one_mul]
  { inv_mem := inv_mem _
    mul_mem := fun {a b} ha hb => by
      have : a * b⁻¹⁻¹ ∈ s := div_mem ha (inv_mem b hb)
      convert this
      

Proof state:
case h.e'_4.h.e'_6
G : Type u_1
H : Type u_2
A : Type u_3
a✝ a₁ a₂ b✝ c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set G
one_mem : 1 ∈ s
div_mem : ∀ {a b : G}, a ∈ s → b ∈ s → a * b⁻¹ ∈ s
inv_mem : ∀ a ∈ s, a⁻¹ ∈ s
a b : G
ha : a ∈ s
hb : b ∈ s
this : a * b⁻¹⁻¹ ∈ s
⊢ b = b⁻¹⁻¹

Next tactic:
rw [inv_inv]
theorem IsAddSubgroup.of_sub (s : Set A) (zero_mem : (0 : A) ∈ s)
    (sub_mem : ∀ {a b : A}, a ∈ s → b ∈ s → a - b ∈ s) : IsAddSubgroup s :=
  IsAddSubgroup.of_add_neg s zero_mem fun {x y} hx hy => by
    

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝¹ : Group G
inst✝ : AddGroup A
s : Set A
zero_mem : 0 ∈ s
sub_mem : ∀ {a b : A}, a ∈ s → b ∈ s → a - b ∈ s
x y : A
hx : x ∈ s
hy : y ∈ s
⊢ x + -y ∈ s

Next tactic:
simpa only [sub_eq_add_neg] using sub_mem hx hy
@[to_additive]
theorem inv_mem_iff : a⁻¹ ∈ s ↔ a ∈ s :=
  ⟨fun h => by 

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
s : Set G
hs : IsSubgroup s
h : a⁻¹ ∈ s
⊢ a ∈ s

Next tactic:
simpa using hs.inv_mem h
@[to_additive]
theorem mul_mem_cancel_right (h : a ∈ s) : b * a ∈ s ↔ b ∈ s :=
  ⟨fun hba => by 

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
s : Set G
hs : IsSubgroup s
h : a ∈ s
hba : b * a ∈ s
⊢ b ∈ s

Next tactic:
simpa using hs.mul_mem hba (hs.inv_mem h)
@[to_additive]
theorem mul_mem_cancel_left (h : a ∈ s) : a * b ∈ s ↔ b ∈ s :=
  ⟨fun hab => by 

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
s : Set G
hs : IsSubgroup s
h : a ∈ s
hab : a * b ∈ s
⊢ b ∈ s

Next tactic:
simpa using hs.mul_mem (hs.inv_mem h) hab
@[to_additive]
theorem isNormalSubgroup_of_commGroup [CommGroup G] {s : Set G} (hs : IsSubgroup s) :
    IsNormalSubgroup s :=
  { hs with normal := fun n hn g => by 

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : CommGroup G
s : Set G
hs : IsSubgroup s
n : G
hn : n ∈ s
g : G
⊢ g * n * g⁻¹ ∈ s

Next tactic:
rwa [mul_right_comm, mul_right_inv, one_mul]
theorem Additive.isNormalAddSubgroup_iff [Group G] {s : Set G} :
    @IsNormalAddSubgroup (Additive G) _ s ↔ IsNormalSubgroup s :=
  ⟨by 

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
s : Set G
⊢ IsNormalAddSubgroup s → IsNormalSubgroup s

Next tactic:
rintro ⟨h₁, h₂⟩
theorem Additive.isNormalAddSubgroup_iff [Group G] {s : Set G} :
    @IsNormalAddSubgroup (Additive G) _ s ↔ IsNormalSubgroup s :=
  ⟨by rintro ⟨h₁, h₂⟩; 

Proof state:
case mk
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
s : Set G
h₁ : IsAddSubgroup s
h₂ : ∀ n ∈ s, ∀ (g : Additive G), g + n + -g ∈ s
⊢ IsNormalSubgroup s

Next tactic:
exact @IsNormalSubgroup.mk G _ _ (Additive.isAddSubgroup_iff.1 h₁) @h₂
theorem Multiplicative.isNormalSubgroup_iff [AddGroup A] {s : Set A} :
    @IsNormalSubgroup (Multiplicative A) _ s ↔ IsNormalAddSubgroup s :=
  ⟨by
    

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : AddGroup A
s : Set A
⊢ IsNormalSubgroup s → IsNormalAddSubgroup s

Next tactic:
rintro ⟨h₁, h₂⟩
theorem Multiplicative.isNormalSubgroup_iff [AddGroup A] {s : Set A} :
    @IsNormalSubgroup (Multiplicative A) _ s ↔ IsNormalAddSubgroup s :=
  ⟨by
    rintro ⟨h₁, h₂⟩;
      

Proof state:
case mk
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : AddGroup A
s : Set A
h₁ : IsSubgroup s
h₂ : ∀ n ∈ s, ∀ (g : Multiplicative A), g * n * g⁻¹ ∈ s
⊢ IsNormalAddSubgroup s

Next tactic:
exact @IsNormalAddSubgroup.mk A _ _ (Multiplicative.isSubgroup_iff.1 h₁) @h₂
@[to_additive]
theorem mem_norm_comm {s : Set G} (hs : IsNormalSubgroup s) {a b : G} (hab : a * b ∈ s) :
    b * a ∈ s := by
  

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a✝ a₁ a₂ b✝ c : G
inst✝ : Group G
s : Set G
hs : IsNormalSubgroup s
a b : G
hab : a * b ∈ s
⊢ b * a ∈ s

Next tactic:
have h : a⁻¹ * (a * b) * a⁻¹⁻¹ ∈ s := hs.normal (a * b) hab a⁻¹
@[to_additive]
theorem mem_norm_comm {s : Set G} (hs : IsNormalSubgroup s) {a b : G} (hab : a * b ∈ s) :
    b * a ∈ s := by
  have h : a⁻¹ * (a * b) * a⁻¹⁻¹ ∈ s := hs.normal (a * b) hab a⁻¹
  

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a✝ a₁ a₂ b✝ c : G
inst✝ : Group G
s : Set G
hs : IsNormalSubgroup s
a b : G
hab : a * b ∈ s
h : a⁻¹ * (a * b) * a⁻¹⁻¹ ∈ s
⊢ b * a ∈ s

Next tactic:
simp at h
@[to_additive]
theorem mem_norm_comm {s : Set G} (hs : IsNormalSubgroup s) {a b : G} (hab : a * b ∈ s) :
    b * a ∈ s := by
  have h : a⁻¹ * (a * b) * a⁻¹⁻¹ ∈ s := hs.normal (a * b) hab a⁻¹
  simp at h; 

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a✝ a₁ a₂ b✝ c : G
inst✝ : Group G
s : Set G
hs : IsNormalSubgroup s
a b : G
hab : a * b ∈ s
h : b * a ∈ s
⊢ b * a ∈ s

Next tactic:
exact h
@[to_additive]
theorem trivial_normal : IsNormalSubgroup (trivial G) := by
  

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
⊢ IsNormalSubgroup (trivial G)

Next tactic:
refine' { .. }
@[to_additive]
theorem trivial_normal : IsNormalSubgroup (trivial G) := by
  refine' { .. } <;> 

Proof state:
case refine'_1
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
⊢ 1 ∈ trivial G

Next tactic:
simp (config := { contextual := true }) [trivial]
@[to_additive]
theorem trivial_normal : IsNormalSubgroup (trivial G) := by
  refine' { .. } <;> 

Proof state:
case refine'_2
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
⊢ ∀ {a b : G}, a ∈ trivial G → b ∈ trivial G → a * b ∈ trivial G

Next tactic:
simp (config := { contextual := true }) [trivial]
@[to_additive]
theorem trivial_normal : IsNormalSubgroup (trivial G) := by
  refine' { .. } <;> 

Proof state:
case refine'_3
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
⊢ ∀ {a : G}, a ∈ trivial G → a⁻¹ ∈ trivial G

Next tactic:
simp (config := { contextual := true }) [trivial]
@[to_additive]
theorem trivial_normal : IsNormalSubgroup (trivial G) := by
  refine' { .. } <;> 

Proof state:
case refine'_4
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
⊢ ∀ n ∈ trivial G, ∀ (g : G), g * n * g⁻¹ ∈ trivial G

Next tactic:
simp (config := { contextual := true }) [trivial]
@[to_additive]
theorem eq_trivial_iff {s : Set G} (hs : IsSubgroup s) : s = trivial G ↔ ∀ x ∈ s, x = (1 : G) := by
  

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
s : Set G
hs : IsSubgroup s
⊢ s = trivial G ↔ ∀ x ∈ s, x = 1

Next tactic:
simp only [Set.ext_iff, IsSubgroup.mem_trivial]
@[to_additive]
theorem eq_trivial_iff {s : Set G} (hs : IsSubgroup s) : s = trivial G ↔ ∀ x ∈ s, x = (1 : G) := by
  simp only [Set.ext_iff, IsSubgroup.mem_trivial];
    

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
s : Set G
hs : IsSubgroup s
⊢ (∀ (x : G), x ∈ s ↔ x = 1) ↔ ∀ x ∈ s, x = 1

Next tactic:
exact ⟨fun h x => (h x).1, fun h x => ⟨h x, fun hx => hx.symm ▸ hs.toIsSubmonoid.one_mem⟩⟩
@[to_additive]
theorem univ_subgroup : IsNormalSubgroup (@univ G) := by 

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
⊢ IsNormalSubgroup univ

Next tactic:
refine' { .. }
@[to_additive]
theorem univ_subgroup : IsNormalSubgroup (@univ G) := by refine' { .. } <;> 

Proof state:
case refine'_1
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
⊢ 1 ∈ univ

Next tactic:
simp
@[to_additive]
theorem univ_subgroup : IsNormalSubgroup (@univ G) := by refine' { .. } <;> 

Proof state:
case refine'_2
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
⊢ ∀ {a b : G}, a ∈ univ → b ∈ univ → a * b ∈ univ

Next tactic:
simp
@[to_additive]
theorem univ_subgroup : IsNormalSubgroup (@univ G) := by refine' { .. } <;> 

Proof state:
case refine'_3
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
⊢ ∀ {a : G}, a ∈ univ → a⁻¹ ∈ univ

Next tactic:
simp
@[to_additive]
theorem univ_subgroup : IsNormalSubgroup (@univ G) := by refine' { .. } <;> 

Proof state:
case refine'_4
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
⊢ ∀ n ∈ univ, ∀ (g : G), g * n * g⁻¹ ∈ univ

Next tactic:
simp
@[to_additive add_center_normal]
theorem center_normal : IsNormalSubgroup (center G) :=
  { one_mem := by 

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
⊢ 1 ∈ center G

Next tactic:
simp [center]
@[to_additive add_center_normal]
theorem center_normal : IsNormalSubgroup (center G) :=
  { one_mem := by simp [center]
    mul_mem := fun ha hb g => by
      

Proof state:
G : Type u_1
H : Type u_2
A : Type u_3
a a₁ a₂ b c : G
inst✝ : Group G
a✝ b✝ : G
ha : a✝ ∈ center G
hb : b✝ ∈ center G
g : G
⊢ g * (a✝ * b✝) = a✝ * b✝ * g

Next tactic:
rw [← mul_assoc, mem_center.2 ha g, mul_assoc, mem_center.2 hb g, ← mul_assoc]