/-
Copyright (c) 2020 Kexing Ying. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kexing Ying

! This file was ported from Lean 3 source module group_theory.subgroup.finite
! leanprover-community/mathlib commit 13a5329a8625701af92e9a96ffc90fa787fff24d
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Set.Finite
import Mathbin.GroupTheory.Subgroup.Basic
import Mathbin.GroupTheory.Submonoid.Membership

/-!
# Subgroups

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

This file provides some result on multiplicative and additive subgroups in the finite context.

## Tags
subgroup, subgroups
-/

open BigOperators

variable {G : Type _} [Group G]

variable {A : Type _} [AddGroup A]

namespace Subgroup

@[to_additive]
instance (K : Subgroup G) [d : DecidablePred (· ∈ K)] [Fintype G] : Fintype K :=
  show Fintype { g : G // g ∈ K } from inferInstance

@[to_additive]
instance (K : Subgroup G) [Finite G] : Finite K :=
  Subtype.finite

end Subgroup

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

namespace Subgroup

variable (H K : Subgroup G)

/- warning: subgroup.list_prod_mem -> Subgroup.list_prod_mem is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (K : Subgroup.{u1} G _inst_1) {l : List.{u1} G}, (forall (x : G), (Membership.Mem.{u1, u1} G (List.{u1} G) (List.hasMem.{u1} G) x l) -> (Membership.Mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) x K)) -> (Membership.Mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) (List.prod.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (MulOneClass.toHasOne.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) l) K)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (K : Subgroup.{u1} G _inst_1) {l : List.{u1} G}, (forall (x : G), (Membership.mem.{u1, u1} G (List.{u1} G) (List.instMembershipList.{u1} G) x l) -> (Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x K)) -> (Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) (List.prod.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (InvOneClass.toOne.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G (Group.toDivisionMonoid.{u1} G _inst_1)))) l) K)
Case conversion may be inaccurate. Consider using '#align subgroup.list_prod_mem Subgroup.list_prod_memₓ'. -/
/-- Product of a list of elements in a subgroup is in the subgroup. -/
@[to_additive "Sum of a list of elements in an `add_subgroup` is in the `add_subgroup`."]
protected theorem list_prod_mem {l : List G} : (∀ x ∈ l, x ∈ K) → l.Prod ∈ K :=
  list_prod_mem
#align subgroup.list_prod_mem Subgroup.list_prod_mem
#align add_subgroup.list_sum_mem AddSubgroup.list_sum_mem

/- warning: subgroup.multiset_prod_mem -> Subgroup.multiset_prod_mem is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_3 : CommGroup.{u1} G] (K : Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (g : Multiset.{u1} G), (forall (a : G), (Membership.Mem.{u1, u1} G (Multiset.{u1} G) (Multiset.hasMem.{u1} G) a g) -> (Membership.Mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) a K)) -> (Membership.Mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) (Multiset.prod.{u1} G (CommGroup.toCommMonoid.{u1} G _inst_3) g) K)
but is expected to have type
  forall {G : Type.{u1}} [_inst_3 : CommGroup.{u1} G] (K : Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (g : Multiset.{u1} G), (forall (a : G), (Membership.mem.{u1, u1} G (Multiset.{u1} G) (Multiset.instMembershipMultiset.{u1} G) a g) -> (Membership.mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) a K)) -> (Membership.mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) (Multiset.prod.{u1} G (CommGroup.toCommMonoid.{u1} G _inst_3) g) K)
Case conversion may be inaccurate. Consider using '#align subgroup.multiset_prod_mem Subgroup.multiset_prod_memₓ'. -/
/-- Product of a multiset of elements in a subgroup of a `comm_group` is in the subgroup. -/
@[to_additive
      "Sum of a multiset of elements in an `add_subgroup` of an `add_comm_group`\nis in the `add_subgroup`."]
protected theorem multiset_prod_mem {G} [CommGroup G] (K : Subgroup G) (g : Multiset G) :
    (∀ a ∈ g, a ∈ K) → g.Prod ∈ K :=
  multiset_prod_mem g
#align subgroup.multiset_prod_mem Subgroup.multiset_prod_mem
#align add_subgroup.multiset_sum_mem AddSubgroup.multiset_sum_mem

/- warning: subgroup.multiset_noncomm_prod_mem -> Subgroup.multiset_noncommProd_mem is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (K : Subgroup.{u1} G _inst_1) (g : Multiset.{u1} G) (comm : Set.Pairwise.{u1} G (setOf.{u1} G (fun (x : G) => Membership.Mem.{u1, u1} G (Multiset.{u1} G) (Multiset.hasMem.{u1} G) x g)) (Commute.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))))), (forall (a : G), (Membership.Mem.{u1, u1} G (Multiset.{u1} G) (Multiset.hasMem.{u1} G) a g) -> (Membership.Mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) a K)) -> (Membership.Mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) (Multiset.noncommProd.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) g comm) K)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (K : Subgroup.{u1} G _inst_1) (g : Multiset.{u1} G) (comm : Set.Pairwise.{u1} G (setOf.{u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Multiset.{u1} G) (Multiset.instMembershipMultiset.{u1} G) x g)) (Commute.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))))), (forall (a : G), (Membership.mem.{u1, u1} G (Multiset.{u1} G) (Multiset.instMembershipMultiset.{u1} G) a g) -> (Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) a K)) -> (Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) (Multiset.noncommProd.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) g comm) K)
Case conversion may be inaccurate. Consider using '#align subgroup.multiset_noncomm_prod_mem Subgroup.multiset_noncommProd_memₓ'. -/
@[to_additive]
theorem multiset_noncommProd_mem (K : Subgroup G) (g : Multiset G) (comm) :
    (∀ a ∈ g, a ∈ K) → g.noncommProd comm ∈ K :=
  K.toSubmonoid.multiset_noncommProd_mem g comm
#align subgroup.multiset_noncomm_prod_mem Subgroup.multiset_noncommProd_mem
#align add_subgroup.multiset_noncomm_sum_mem AddSubgroup.multiset_noncommSum_mem

/- warning: subgroup.prod_mem -> Subgroup.prod_mem is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_3 : CommGroup.{u1} G] (K : Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) {ι : Type.{u2}} {t : Finset.{u2} ι} {f : ι -> G}, (forall (c : ι), (Membership.Mem.{u2, u2} ι (Finset.{u2} ι) (Finset.hasMem.{u2} ι) c t) -> (Membership.Mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) (f c) K)) -> (Membership.Mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) (Finset.prod.{u1, u2} G ι (CommGroup.toCommMonoid.{u1} G _inst_3) t (fun (c : ι) => f c)) K)
but is expected to have type
  forall {G : Type.{u2}} [_inst_3 : CommGroup.{u2} G] (K : Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) {ι : Type.{u1}} {t : Finset.{u1} ι} {f : ι -> G}, (forall (c : ι), (Membership.mem.{u1, u1} ι (Finset.{u1} ι) (Finset.instMembershipFinset.{u1} ι) c t) -> (Membership.mem.{u2, u2} G (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) (f c) K)) -> (Membership.mem.{u2, u2} G (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) (Finset.prod.{u2, u1} G ι (CommGroup.toCommMonoid.{u2} G _inst_3) t (fun (c : ι) => f c)) K)
Case conversion may be inaccurate. Consider using '#align subgroup.prod_mem Subgroup.prod_memₓ'. -/
/-- Product of elements of a subgroup of a `comm_group` indexed by a `finset` is in the
    subgroup. -/
@[to_additive
      "Sum of elements in an `add_subgroup` of an `add_comm_group` indexed by a `finset`\nis in the `add_subgroup`."]
protected theorem prod_mem {G : Type _} [CommGroup G] (K : Subgroup G) {ι : Type _} {t : Finset ι}
    {f : ι → G} (h : ∀ c ∈ t, f c ∈ K) : (∏ c in t, f c) ∈ K :=
  prod_mem h
#align subgroup.prod_mem Subgroup.prod_mem
#align add_subgroup.sum_mem AddSubgroup.sum_mem

/- warning: subgroup.noncomm_prod_mem -> Subgroup.noncommProd_mem is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (K : Subgroup.{u1} G _inst_1) {ι : Type.{u2}} {t : Finset.{u2} ι} {f : ι -> G} (comm : Set.Pairwise.{u2} ι ((fun (a : Type.{u2}) (b : Type.{u2}) [self : HasLiftT.{succ u2, succ u2} a b] => self.0) (Finset.{u2} ι) (Set.{u2} ι) (HasLiftT.mk.{succ u2, succ u2} (Finset.{u2} ι) (Set.{u2} ι) (CoeTCₓ.coe.{succ u2, succ u2} (Finset.{u2} ι) (Set.{u2} ι) (Finset.Set.hasCoeT.{u2} ι))) t) (fun (a : ι) (b : ι) => Commute.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (f a) (f b))), (forall (c : ι), (Membership.Mem.{u2, u2} ι (Finset.{u2} ι) (Finset.hasMem.{u2} ι) c t) -> (Membership.Mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) (f c) K)) -> (Membership.Mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) (Finset.noncommProd.{u2, u1} ι G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) t f comm) K)
but is expected to have type
  forall {G : Type.{u2}} [_inst_1 : Group.{u2} G] (K : Subgroup.{u2} G _inst_1) {ι : Type.{u1}} {t : Finset.{u1} ι} {f : ι -> G} (comm : Set.Pairwise.{u1} ι (Finset.toSet.{u1} ι t) (fun (a : ι) (b : ι) => Commute.{u2} G (MulOneClass.toMul.{u2} G (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)))) (f a) (f b))), (forall (c : ι), (Membership.mem.{u1, u1} ι (Finset.{u1} ι) (Finset.instMembershipFinset.{u1} ι) c t) -> (Membership.mem.{u2, u2} G (Subgroup.{u2} G _inst_1) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u2} G _inst_1)) (f c) K)) -> (Membership.mem.{u2, u2} G (Subgroup.{u2} G _inst_1) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u2} G _inst_1)) (Finset.noncommProd.{u1, u2} ι G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)) t f comm) K)
Case conversion may be inaccurate. Consider using '#align subgroup.noncomm_prod_mem Subgroup.noncommProd_memₓ'. -/
@[to_additive]
theorem noncommProd_mem (K : Subgroup G) {ι : Type _} {t : Finset ι} {f : ι → G} (comm) :
    (∀ c ∈ t, f c ∈ K) → t.noncommProd f comm ∈ K :=
  K.toSubmonoid.noncommProd_mem t f comm
#align subgroup.noncomm_prod_mem Subgroup.noncommProd_mem
#align add_subgroup.noncomm_sum_mem AddSubgroup.noncommSum_mem

/- warning: subgroup.coe_list_prod -> Subgroup.val_list_prod is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) (l : List.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H)), Eq.{succ u1} G ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) G (HasLiftT.mk.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) G (CoeTCₓ.coe.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) G (coeBase.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) G (coeSubtype.{succ u1} G (fun (x : G) => Membership.Mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) x H))))) (List.prod.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) (Subgroup.mul.{u1} G _inst_1 H) (Subgroup.one.{u1} G _inst_1 H) l)) (List.prod.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (MulOneClass.toHasOne.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (List.map.{u1, u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) G ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) G (HasLiftT.mk.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) G (CoeTCₓ.coe.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) G (coeBase.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) G (coeSubtype.{succ u1} G (fun (x : G) => Membership.Mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) x H)))))) l))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) (l : List.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H))), Eq.{succ u1} G (Subtype.val.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Set.{u1} G) (Set.instMembershipSet.{u1} G) x (SetLike.coe.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1) H)) (List.prod.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H)) (Subgroup.mul.{u1} G _inst_1 H) (Subgroup.one.{u1} G _inst_1 H) l)) (List.prod.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (InvOneClass.toOne.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G (Group.toDivisionMonoid.{u1} G _inst_1)))) (List.map.{u1, u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H)) G (Subtype.val.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H)) l))
Case conversion may be inaccurate. Consider using '#align subgroup.coe_list_prod Subgroup.val_list_prodₓ'. -/
@[simp, norm_cast, to_additive]
theorem val_list_prod (l : List H) : (l.Prod : G) = (l.map coe).Prod :=
  SubmonoidClass.coe_list_prod l
#align subgroup.coe_list_prod Subgroup.val_list_prod
#align add_subgroup.coe_list_sum AddSubgroup.val_list_sum

/- warning: subgroup.coe_multiset_prod -> Subgroup.val_multiset_prod is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_3 : CommGroup.{u1} G] (H : Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (m : Multiset.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H)), Eq.{succ u1} G ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H) G (HasLiftT.mk.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H) G (CoeTCₓ.coe.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H) G (coeBase.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H) G (coeSubtype.{succ u1} G (fun (x : G) => Membership.Mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) x H))))) (Multiset.prod.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H) (CommGroup.toCommMonoid.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H) (Subgroup.toCommGroup.{u1} G _inst_3 H)) m)) (Multiset.prod.{u1} G (CommGroup.toCommMonoid.{u1} G _inst_3) (Multiset.map.{u1, u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H) G ((fun (a : Type.{u1}) (b : Type.{u1}) [self : HasLiftT.{succ u1, succ u1} a b] => self.0) (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H) G (HasLiftT.mk.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H) G (CoeTCₓ.coe.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H) G (coeBase.{succ u1, succ u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) H) G (coeSubtype.{succ u1} G (fun (x : G) => Membership.Mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.setLike.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) x H)))))) m))
but is expected to have type
  forall {G : Type.{u1}} [_inst_3 : CommGroup.{u1} G] (H : Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (m : Multiset.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) x H))), Eq.{succ u1} G (Subtype.val.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Set.{u1} G) (Set.instMembershipSet.{u1} G) x (SetLike.coe.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) H)) (Multiset.prod.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) x H)) (Submonoid.toCommMonoid.{u1} G (CommGroup.toCommMonoid.{u1} G _inst_3) (Subgroup.toSubmonoid.{u1} G (CommGroup.toGroup.{u1} G _inst_3) H)) m)) (Multiset.prod.{u1} G (CommGroup.toCommMonoid.{u1} G _inst_3) (Multiset.map.{u1, u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) x H)) G (Subtype.val.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) x H)) m))
Case conversion may be inaccurate. Consider using '#align subgroup.coe_multiset_prod Subgroup.val_multiset_prodₓ'. -/
@[simp, norm_cast, to_additive]
theorem val_multiset_prod {G} [CommGroup G] (H : Subgroup G) (m : Multiset H) :
    (m.Prod : G) = (m.map coe).Prod :=
  SubmonoidClass.coe_multiset_prod m
#align subgroup.coe_multiset_prod Subgroup.val_multiset_prod
#align add_subgroup.coe_multiset_sum AddSubgroup.val_multiset_sum

/- warning: subgroup.coe_finset_prod -> Subgroup.val_finset_prod is a dubious translation:
lean 3 declaration is
  forall {ι : Type.{u1}} {G : Type.{u2}} [_inst_3 : CommGroup.{u2} G] (H : Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) (f : ι -> (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) H)) (s : Finset.{u1} ι), Eq.{succ u2} G ((fun (a : Type.{u2}) (b : Type.{u2}) [self : HasLiftT.{succ u2, succ u2} a b] => self.0) (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) H) G (HasLiftT.mk.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) H) G (CoeTCₓ.coe.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) H) G (coeBase.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) H) G (coeSubtype.{succ u2} G (fun (x : G) => Membership.Mem.{u2, u2} G (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) (SetLike.hasMem.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) x H))))) (Finset.prod.{u2, u1} (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) H) ι (CommGroup.toCommMonoid.{u2} (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) H) (Subgroup.toCommGroup.{u2} G _inst_3 H)) s (fun (i : ι) => f i))) (Finset.prod.{u2, u1} G ι (CommGroup.toCommMonoid.{u2} G _inst_3) s (fun (i : ι) => (fun (a : Type.{u2}) (b : Type.{u2}) [self : HasLiftT.{succ u2, succ u2} a b] => self.0) (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) H) G (HasLiftT.mk.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) H) G (CoeTCₓ.coe.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) H) G (coeBase.{succ u2, succ u2} (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) H) G (coeSubtype.{succ u2} G (fun (x : G) => Membership.Mem.{u2, u2} G (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) (SetLike.hasMem.{u2, u2} (Subgroup.{u2} G (CommGroup.toGroup.{u2} G _inst_3)) G (Subgroup.setLike.{u2} G (CommGroup.toGroup.{u2} G _inst_3))) x H))))) (f i)))
but is expected to have type
  forall {ι : Type.{u2}} {G : Type.{u1}} [_inst_3 : CommGroup.{u1} G] (H : Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (f : ι -> (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) x H))) (s : Finset.{u2} ι), Eq.{succ u1} G (Subtype.val.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Set.{u1} G) (Set.instMembershipSet.{u1} G) x (SetLike.coe.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) H)) (Finset.prod.{u1, u2} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3))) x H)) ι (Submonoid.toCommMonoid.{u1} G (CommGroup.toCommMonoid.{u1} G _inst_3) (Subgroup.toSubmonoid.{u1} G (CommGroup.toGroup.{u1} G _inst_3) H)) s (fun (i : ι) => f i))) (Finset.prod.{u1, u2} G ι (CommGroup.toCommMonoid.{u1} G _inst_3) s (fun (i : ι) => Subtype.val.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Set.{u1} G) (Set.instMembershipSet.{u1} G) x (SetLike.coe.{u1, u1} (Subgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) G (Subgroup.instSetLikeSubgroup.{u1} G (CommGroup.toGroup.{u1} G _inst_3)) H)) (f i)))
Case conversion may be inaccurate. Consider using '#align subgroup.coe_finset_prod Subgroup.val_finset_prodₓ'. -/
@[simp, norm_cast, to_additive]
theorem val_finset_prod {ι G} [CommGroup G] (H : Subgroup G) (f : ι → H) (s : Finset ι) :
    ↑(∏ i in s, f i) = (∏ i in s, f i : G) :=
  SubmonoidClass.coe_finset_prod f s
#align subgroup.coe_finset_prod Subgroup.val_finset_prod
#align add_subgroup.coe_finset_sum AddSubgroup.val_finset_sum

/- warning: subgroup.fintype_bot -> Subgroup.fintypeBot is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G], Fintype.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasBot.{u1} G _inst_1)))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G], Fintype.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instBotSubgroup.{u1} G _inst_1))))
Case conversion may be inaccurate. Consider using '#align subgroup.fintype_bot Subgroup.fintypeBotₓ'. -/
@[to_additive]
instance fintypeBot : Fintype (⊥ : Subgroup G) :=
  ⟨{1}, by
    rintro ⟨x, ⟨hx⟩⟩
    exact Finset.mem_singleton_self _⟩
#align subgroup.fintype_bot Subgroup.fintypeBot
#align add_subgroup.fintype_bot AddSubgroup.fintypeBot

/- warning: subgroup.card_bot -> Subgroup.card_bot is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {_x : Fintype.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasBot.{u1} G _inst_1)))}, Eq.{1} Nat (Fintype.card.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasBot.{u1} G _inst_1))) _x) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {_x : Fintype.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instBotSubgroup.{u1} G _inst_1))))}, Eq.{1} Nat (Fintype.card.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instBotSubgroup.{u1} G _inst_1)))) _x) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))
Case conversion may be inaccurate. Consider using '#align subgroup.card_bot Subgroup.card_botₓ'. -/
/- curly brackets `{}` are used here instead of instance brackets `[]` because
  the instance in a goal is often not the same as the one inferred by type class inference.  -/
@[simp, to_additive]
theorem card_bot {_ : Fintype ↥(⊥ : Subgroup G)} : Fintype.card (⊥ : Subgroup G) = 1 :=
  Fintype.card_eq_one_iff.2
    ⟨⟨(1 : G), Set.mem_singleton 1⟩, fun ⟨y, hy⟩ => Subtype.eq <| Subgroup.mem_bot.1 hy⟩
#align subgroup.card_bot Subgroup.card_bot
#align add_subgroup.card_bot AddSubgroup.card_bot

/- warning: subgroup.eq_top_of_card_eq -> Subgroup.eq_top_of_card_eq is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H)] [_inst_4 : Fintype.{u1} G], (Eq.{1} Nat (Fintype.card.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) _inst_3) (Fintype.card.{u1} G _inst_4)) -> (Eq.{succ u1} (Subgroup.{u1} G _inst_1) H (Top.top.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasTop.{u1} G _inst_1)))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H))] [_inst_4 : Fintype.{u1} G], (Eq.{1} Nat (Fintype.card.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H)) _inst_3) (Fintype.card.{u1} G _inst_4)) -> (Eq.{succ u1} (Subgroup.{u1} G _inst_1) H (Top.top.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instTopSubgroup.{u1} G _inst_1)))
Case conversion may be inaccurate. Consider using '#align subgroup.eq_top_of_card_eq Subgroup.eq_top_of_card_eqₓ'. -/
@[to_additive]
theorem eq_top_of_card_eq [Fintype H] [Fintype G] (h : Fintype.card H = Fintype.card G) : H = ⊤ :=
  by
  haveI : Fintype (H : Set G) := ‹Fintype H›
  rw [SetLike.ext'_iff, coe_top, ← Finset.coe_univ, ← (H : Set G).coe_toFinset, Finset.coe_inj, ←
    Finset.card_eq_iff_eq_univ, ← h, Set.toFinset_card]
  congr
#align subgroup.eq_top_of_card_eq Subgroup.eq_top_of_card_eq
#align add_subgroup.eq_top_of_card_eq AddSubgroup.eq_top_of_card_eq

/- warning: subgroup.eq_top_of_le_card -> Subgroup.eq_top_of_le_card is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H)] [_inst_4 : Fintype.{u1} G], (LE.le.{0} Nat Nat.hasLe (Fintype.card.{u1} G _inst_4) (Fintype.card.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) _inst_3)) -> (Eq.{succ u1} (Subgroup.{u1} G _inst_1) H (Top.top.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasTop.{u1} G _inst_1)))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H))] [_inst_4 : Fintype.{u1} G], (LE.le.{0} Nat instLENat (Fintype.card.{u1} G _inst_4) (Fintype.card.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H)) _inst_3)) -> (Eq.{succ u1} (Subgroup.{u1} G _inst_1) H (Top.top.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instTopSubgroup.{u1} G _inst_1)))
Case conversion may be inaccurate. Consider using '#align subgroup.eq_top_of_le_card Subgroup.eq_top_of_le_cardₓ'. -/
@[to_additive]
theorem eq_top_of_le_card [Fintype H] [Fintype G] (h : Fintype.card G ≤ Fintype.card H) : H = ⊤ :=
  eq_top_of_card_eq H (le_antisymm (Fintype.card_le_of_injective coe Subtype.coe_injective) h)
#align subgroup.eq_top_of_le_card Subgroup.eq_top_of_le_card
#align add_subgroup.eq_top_of_le_card AddSubgroup.eq_top_of_le_card

/- warning: subgroup.eq_bot_of_card_le -> Subgroup.eq_bot_of_card_le is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H)], (LE.le.{0} Nat Nat.hasLe (Fintype.card.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) _inst_3) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) -> (Eq.{succ u1} (Subgroup.{u1} G _inst_1) H (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasBot.{u1} G _inst_1)))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H))], (LE.le.{0} Nat instLENat (Fintype.card.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H)) _inst_3) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) -> (Eq.{succ u1} (Subgroup.{u1} G _inst_1) H (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instBotSubgroup.{u1} G _inst_1)))
Case conversion may be inaccurate. Consider using '#align subgroup.eq_bot_of_card_le Subgroup.eq_bot_of_card_leₓ'. -/
@[to_additive]
theorem eq_bot_of_card_le [Fintype H] (h : Fintype.card H ≤ 1) : H = ⊥ :=
  let _ := Fintype.card_le_one_iff_subsingleton.mp h
  eq_bot_of_subsingleton H
#align subgroup.eq_bot_of_card_le Subgroup.eq_bot_of_card_le
#align add_subgroup.eq_bot_of_card_le AddSubgroup.eq_bot_of_card_le

/- warning: subgroup.eq_bot_of_card_eq -> Subgroup.eq_bot_of_card_eq is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H)], (Eq.{1} Nat (Fintype.card.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) _inst_3) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) -> (Eq.{succ u1} (Subgroup.{u1} G _inst_1) H (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasBot.{u1} G _inst_1)))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H))], (Eq.{1} Nat (Fintype.card.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H)) _inst_3) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) -> (Eq.{succ u1} (Subgroup.{u1} G _inst_1) H (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instBotSubgroup.{u1} G _inst_1)))
Case conversion may be inaccurate. Consider using '#align subgroup.eq_bot_of_card_eq Subgroup.eq_bot_of_card_eqₓ'. -/
@[to_additive]
theorem eq_bot_of_card_eq [Fintype H] (h : Fintype.card H = 1) : H = ⊥ :=
  H.eq_bot_of_card_le (le_of_eq h)
#align subgroup.eq_bot_of_card_eq Subgroup.eq_bot_of_card_eq
#align add_subgroup.eq_bot_of_card_eq AddSubgroup.eq_bot_of_card_eq

/- warning: subgroup.card_le_one_iff_eq_bot -> Subgroup.card_le_one_iff_eq_bot is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H)], Iff (LE.le.{0} Nat Nat.hasLe (Fintype.card.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) _inst_3) (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne)))) (Eq.{succ u1} (Subgroup.{u1} G _inst_1) H (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasBot.{u1} G _inst_1)))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H))], Iff (LE.le.{0} Nat instLENat (Fintype.card.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H)) _inst_3) (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1))) (Eq.{succ u1} (Subgroup.{u1} G _inst_1) H (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instBotSubgroup.{u1} G _inst_1)))
Case conversion may be inaccurate. Consider using '#align subgroup.card_le_one_iff_eq_bot Subgroup.card_le_one_iff_eq_botₓ'. -/
@[to_additive]
theorem card_le_one_iff_eq_bot [Fintype H] : Fintype.card H ≤ 1 ↔ H = ⊥ :=
  ⟨fun h =>
    (eq_bot_iff_forall _).2 fun x hx => by
      simpa [Subtype.ext_iff] using Fintype.card_le_one_iff.1 h ⟨x, hx⟩ 1,
    fun h => by simp [h]⟩
#align subgroup.card_le_one_iff_eq_bot Subgroup.card_le_one_iff_eq_bot
#align add_subgroup.card_nonpos_iff_eq_bot AddSubgroup.card_le_one_iff_eq_bot

/- warning: subgroup.one_lt_card_iff_ne_bot -> Subgroup.one_lt_card_iff_ne_bot is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H)], Iff (LT.lt.{0} Nat Nat.hasLt (OfNat.ofNat.{0} Nat 1 (OfNat.mk.{0} Nat 1 (One.one.{0} Nat Nat.hasOne))) (Fintype.card.{u1} (coeSort.{succ u1, succ (succ u1)} (Subgroup.{u1} G _inst_1) Type.{u1} (SetLike.hasCoeToSort.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) H) _inst_3)) (Ne.{succ u1} (Subgroup.{u1} G _inst_1) H (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.hasBot.{u1} G _inst_1)))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] (H : Subgroup.{u1} G _inst_1) [_inst_3 : Fintype.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H))], Iff (LT.lt.{0} Nat instLTNat (OfNat.ofNat.{0} Nat 1 (instOfNatNat 1)) (Fintype.card.{u1} (Subtype.{succ u1} G (fun (x : G) => Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x H)) _inst_3)) (Ne.{succ u1} (Subgroup.{u1} G _inst_1) H (Bot.bot.{u1} (Subgroup.{u1} G _inst_1) (Subgroup.instBotSubgroup.{u1} G _inst_1)))
Case conversion may be inaccurate. Consider using '#align subgroup.one_lt_card_iff_ne_bot Subgroup.one_lt_card_iff_ne_botₓ'. -/
@[to_additive]
theorem one_lt_card_iff_ne_bot [Fintype H] : 1 < Fintype.card H ↔ H ≠ ⊥ :=
  lt_iff_not_le.trans H.card_le_one_iff_eq_bot.Not
#align subgroup.one_lt_card_iff_ne_bot Subgroup.one_lt_card_iff_ne_bot
#align add_subgroup.pos_card_iff_ne_bot AddSubgroup.one_lt_card_iff_ne_bot

end Subgroup

namespace Subgroup

section Pi

open Set

variable {η : Type _} {f : η → Type _} [∀ i, Group (f i)]

/- warning: subgroup.pi_mem_of_mul_single_mem_aux -> Subgroup.pi_mem_of_mulSingle_mem_aux is a dubious translation:
lean 3 declaration is
  forall {η : Type.{u1}} {f : η -> Type.{u2}} [_inst_3 : forall (i : η), Group.{u2} (f i)] [_inst_4 : DecidableEq.{succ u1} η] (I : Finset.{u1} η) {H : Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))} (x : forall (i : η), f i), (forall (i : η), (Not (Membership.Mem.{u1, u1} η (Finset.{u1} η) (Finset.hasMem.{u1} η) i I)) -> (Eq.{succ u2} (f i) (x i) (OfNat.ofNat.{u2} (f i) 1 (OfNat.mk.{u2} (f i) 1 (One.one.{u2} (f i) (MulOneClass.toHasOne.{u2} (f i) (Monoid.toMulOneClass.{u2} (f i) (DivInvMonoid.toMonoid.{u2} (f i) (Group.toDivInvMonoid.{u2} (f i) (_inst_3 i)))))))))) -> (forall (i : η), (Membership.Mem.{u1, u1} η (Finset.{u1} η) (Finset.hasMem.{u1} η) i I) -> (Membership.Mem.{max u1 u2, max u1 u2} (forall (i : η), f i) (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (SetLike.hasMem.{max u1 u2, max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (forall (i : η), f i) (Subgroup.setLike.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)))) (Pi.mulSingle.{u1, u2} η (fun (i : η) => f i) (fun (a : η) (b : η) => _inst_4 a b) (fun (i : η) => MulOneClass.toHasOne.{u2} (f i) (Monoid.toMulOneClass.{u2} (f i) (DivInvMonoid.toMonoid.{u2} (f i) (Group.toDivInvMonoid.{u2} (f i) (_inst_3 i))))) i (x i)) H)) -> (Membership.Mem.{max u1 u2, max u1 u2} (forall (i : η), f i) (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (SetLike.hasMem.{max u1 u2, max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (forall (i : η), f i) (Subgroup.setLike.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)))) x H)
but is expected to have type
  forall {η : Type.{u2}} {f : η -> Type.{u1}} [_inst_3 : forall (i : η), Group.{u1} (f i)] [_inst_4 : DecidableEq.{succ u2} η] (I : Finset.{u2} η) {H : Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))} (x : forall (i : η), f i), (forall (i : η), (Not (Membership.mem.{u2, u2} η (Finset.{u2} η) (Finset.instMembershipFinset.{u2} η) i I)) -> (Eq.{succ u1} (f i) (x i) (OfNat.ofNat.{u1} (f i) 1 (One.toOfNat1.{u1} (f i) (InvOneClass.toOne.{u1} (f i) (DivInvOneMonoid.toInvOneClass.{u1} (f i) (DivisionMonoid.toDivInvOneMonoid.{u1} (f i) (Group.toDivisionMonoid.{u1} (f i) (_inst_3 i))))))))) -> (forall (i : η), (Membership.mem.{u2, u2} η (Finset.{u2} η) (Finset.instMembershipFinset.{u2} η) i I) -> (Membership.mem.{max u1 u2, max u2 u1} (forall (i : η), f i) (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (SetLike.instMembership.{max u2 u1, max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (forall (i : η), f i) (Subgroup.instSetLikeSubgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)))) (Pi.mulSingle.{u2, u1} η f (fun (a : η) (b : η) => _inst_4 a b) (fun (i : η) => InvOneClass.toOne.{u1} (f i) (DivInvOneMonoid.toInvOneClass.{u1} (f i) (DivisionMonoid.toDivInvOneMonoid.{u1} (f i) (Group.toDivisionMonoid.{u1} (f i) (_inst_3 i))))) i (x i)) H)) -> (Membership.mem.{max u2 u1, max u2 u1} (forall (i : η), f i) (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (SetLike.instMembership.{max u2 u1, max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (forall (i : η), f i) (Subgroup.instSetLikeSubgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)))) x H)
Case conversion may be inaccurate. Consider using '#align subgroup.pi_mem_of_mul_single_mem_aux Subgroup.pi_mem_of_mulSingle_mem_auxₓ'. -/
@[to_additive]
theorem pi_mem_of_mulSingle_mem_aux [DecidableEq η] (I : Finset η) {H : Subgroup (∀ i, f i)}
    (x : ∀ i, f i) (h1 : ∀ i, i ∉ I → x i = 1) (h2 : ∀ i, i ∈ I → Pi.mulSingle i (x i) ∈ H) :
    x ∈ H := by
  induction' I using Finset.induction_on with i I hnmem ih generalizing x
  · convert one_mem H
    ext i
    exact h1 i (not_mem_empty i)
  · have : x = Function.update x i 1 * Pi.mulSingle i (x i) :=
      by
      ext j
      by_cases heq : j = i
      · subst HEq
        simp
      · simp [HEq]
    rw [this]
    clear this
    apply mul_mem
    · apply ih <;> clear ih
      · intro j hj
        by_cases heq : j = i
        · subst HEq
          simp
        · simp [HEq]
          apply h1 j
          simpa [HEq] using hj
      · intro j hj
        have : j ≠ i := by
          rintro rfl
          contradiction
        simp [this]
        exact h2 _ (Finset.mem_insert_of_mem hj)
    · apply h2
      simp
#align subgroup.pi_mem_of_mul_single_mem_aux Subgroup.pi_mem_of_mulSingle_mem_aux
#align add_subgroup.pi_mem_of_single_mem_aux AddSubgroup.pi_mem_of_single_mem_aux

/- warning: subgroup.pi_mem_of_mul_single_mem -> Subgroup.pi_mem_of_mulSingle_mem is a dubious translation:
lean 3 declaration is
  forall {η : Type.{u1}} {f : η -> Type.{u2}} [_inst_3 : forall (i : η), Group.{u2} (f i)] [_inst_4 : Finite.{succ u1} η] [_inst_5 : DecidableEq.{succ u1} η] {H : Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))} (x : forall (i : η), f i), (forall (i : η), Membership.Mem.{max u1 u2, max u1 u2} (forall (i : η), f i) (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (SetLike.hasMem.{max u1 u2, max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (forall (i : η), f i) (Subgroup.setLike.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)))) (Pi.mulSingle.{u1, u2} η (fun (i : η) => f i) (fun (a : η) (b : η) => _inst_5 a b) (fun (i : η) => MulOneClass.toHasOne.{u2} (f i) (Monoid.toMulOneClass.{u2} (f i) (DivInvMonoid.toMonoid.{u2} (f i) (Group.toDivInvMonoid.{u2} (f i) (_inst_3 i))))) i (x i)) H) -> (Membership.Mem.{max u1 u2, max u1 u2} (forall (i : η), f i) (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (SetLike.hasMem.{max u1 u2, max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (forall (i : η), f i) (Subgroup.setLike.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)))) x H)
but is expected to have type
  forall {η : Type.{u2}} {f : η -> Type.{u1}} [_inst_3 : forall (i : η), Group.{u1} (f i)] [_inst_4 : Finite.{succ u2} η] [_inst_5 : DecidableEq.{succ u2} η] {H : Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))} (x : forall (i : η), f i), (forall (i : η), Membership.mem.{max u1 u2, max u2 u1} (forall (i : η), f i) (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (SetLike.instMembership.{max u2 u1, max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (forall (i : η), f i) (Subgroup.instSetLikeSubgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)))) (Pi.mulSingle.{u2, u1} η f (fun (a : η) (b : η) => _inst_5 a b) (fun (i : η) => InvOneClass.toOne.{u1} (f i) (DivInvOneMonoid.toInvOneClass.{u1} (f i) (DivisionMonoid.toDivInvOneMonoid.{u1} (f i) (Group.toDivisionMonoid.{u1} (f i) (_inst_3 i))))) i (x i)) H) -> (Membership.mem.{max u2 u1, max u2 u1} (forall (i : η), f i) (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (SetLike.instMembership.{max u2 u1, max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (forall (i : η), f i) (Subgroup.instSetLikeSubgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)))) x H)
Case conversion may be inaccurate. Consider using '#align subgroup.pi_mem_of_mul_single_mem Subgroup.pi_mem_of_mulSingle_memₓ'. -/
@[to_additive]
theorem pi_mem_of_mulSingle_mem [Finite η] [DecidableEq η] {H : Subgroup (∀ i, f i)} (x : ∀ i, f i)
    (h : ∀ i, Pi.mulSingle i (x i) ∈ H) : x ∈ H :=
  by
  cases nonempty_fintype η
  exact pi_mem_of_mul_single_mem_aux Finset.univ x (by simp) fun i _ => h i
#align subgroup.pi_mem_of_mul_single_mem Subgroup.pi_mem_of_mulSingle_mem
#align add_subgroup.pi_mem_of_single_mem AddSubgroup.pi_mem_of_single_mem

/- warning: subgroup.pi_le_iff -> Subgroup.pi_le_iff is a dubious translation:
lean 3 declaration is
  forall {η : Type.{u1}} {f : η -> Type.{u2}} [_inst_3 : forall (i : η), Group.{u2} (f i)] [_inst_4 : DecidableEq.{succ u1} η] [_inst_5 : Finite.{succ u1} η] {H : forall (i : η), Subgroup.{u2} (f i) (_inst_3 i)} {J : Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))}, Iff (LE.le.{max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (Preorder.toLE.{max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (PartialOrder.toPreorder.{max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (SetLike.partialOrder.{max u1 u2, max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (forall (i : η), f i) (Subgroup.setLike.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)))))) (Subgroup.pi.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i) (Set.univ.{u1} η) H) J) (forall (i : η), LE.le.{max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (Preorder.toLE.{max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (PartialOrder.toPreorder.{max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (SetLike.partialOrder.{max u1 u2, max u1 u2} (Subgroup.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (forall (i : η), f i) (Subgroup.setLike.{max u1 u2} (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)))))) (Subgroup.map.{u2, max u1 u2} (f i) (_inst_3 i) (forall (i : η), f i) (Pi.group.{u1, u2} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)) (MonoidHom.single.{u1, u2} η f (fun (a : η) (b : η) => _inst_4 a b) (fun (i : η) => Monoid.toMulOneClass.{u2} (f i) (DivInvMonoid.toMonoid.{u2} (f i) (Group.toDivInvMonoid.{u2} (f i) (_inst_3 i)))) i) (H i)) J)
but is expected to have type
  forall {η : Type.{u2}} {f : η -> Type.{u1}} [_inst_3 : forall (i : η), Group.{u1} (f i)] [_inst_4 : DecidableEq.{succ u2} η] [_inst_5 : Finite.{succ u2} η] {H : forall (i : η), Subgroup.{u1} (f i) (_inst_3 i)} {J : Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))}, Iff (LE.le.{max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (Preorder.toLE.{max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (PartialOrder.toPreorder.{max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (CompleteSemilatticeInf.toPartialOrder.{max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (CompleteLattice.toCompleteSemilatticeInf.{max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (Subgroup.instCompleteLatticeSubgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))))))) (Subgroup.pi.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i) (Set.univ.{u2} η) H) J) (forall (i : η), LE.le.{max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (Preorder.toLE.{max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (PartialOrder.toPreorder.{max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (CompleteSemilatticeInf.toPartialOrder.{max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (CompleteLattice.toCompleteSemilatticeInf.{max u2 u1} (Subgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))) (Subgroup.instCompleteLatticeSubgroup.{max u2 u1} (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i))))))) (Subgroup.map.{u1, max u2 u1} (f i) (_inst_3 i) (forall (i : η), f i) (Pi.group.{u2, u1} η (fun (i : η) => f i) (fun (i : η) => _inst_3 i)) (MonoidHom.single.{u2, u1} η f (fun (a : η) (b : η) => _inst_4 a b) (fun (i : η) => Monoid.toMulOneClass.{u1} (f i) (DivInvMonoid.toMonoid.{u1} (f i) (Group.toDivInvMonoid.{u1} (f i) (_inst_3 i)))) i) (H i)) J)
Case conversion may be inaccurate. Consider using '#align subgroup.pi_le_iff Subgroup.pi_le_iffₓ'. -/
/-- For finite index types, the `subgroup.pi` is generated by the embeddings of the groups.  -/
@[to_additive
      "For finite index types, the `subgroup.pi` is generated by the embeddings of the\nadditive groups."]
theorem pi_le_iff [DecidableEq η] [Finite η] {H : ∀ i, Subgroup (f i)} {J : Subgroup (∀ i, f i)} :
    pi univ H ≤ J ↔ ∀ i : η, map (MonoidHom.single f i) (H i) ≤ J :=
  by
  constructor
  · rintro h i _ ⟨x, hx, rfl⟩
    apply h
    simpa using hx
  · exact fun h x hx => pi_mem_of_mul_single_mem x fun i => h i (mem_map_of_mem _ (hx i trivial))
#align subgroup.pi_le_iff Subgroup.pi_le_iff
#align add_subgroup.pi_le_iff AddSubgroup.pi_le_iff

end Pi

end Subgroup

namespace Subgroup

section Normalizer

/- warning: subgroup.mem_normalizer_fintype -> Subgroup.mem_normalizer_fintype is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {S : Set.{u1} G} [_inst_3 : Finite.{succ u1} (coeSort.{succ u1, succ (succ u1)} (Set.{u1} G) Type.{u1} (Set.hasCoeToSort.{u1} G) S)] {x : G}, (forall (n : G), (Membership.Mem.{u1, u1} G (Set.{u1} G) (Set.hasMem.{u1} G) n S) -> (Membership.Mem.{u1, u1} G (Set.{u1} G) (Set.hasMem.{u1} G) (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) x n) (Inv.inv.{u1} G (DivInvMonoid.toHasInv.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)) x)) S)) -> (Membership.Mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.hasMem.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.setLike.{u1} G _inst_1)) x (Subgroup.setNormalizer.{u1} G _inst_1 S))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {S : Set.{u1} G} [_inst_3 : Finite.{succ u1} (Set.Elem.{u1} G S)] {x : G}, (forall (n : G), (Membership.mem.{u1, u1} G (Set.{u1} G) (Set.instMembershipSet.{u1} G) n S) -> (Membership.mem.{u1, u1} G (Set.{u1} G) (Set.instMembershipSet.{u1} G) (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) (HMul.hMul.{u1, u1, u1} G G G (instHMul.{u1} G (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))))) x n) (Inv.inv.{u1} G (InvOneClass.toInv.{u1} G (DivInvOneMonoid.toInvOneClass.{u1} G (DivisionMonoid.toDivInvOneMonoid.{u1} G (Group.toDivisionMonoid.{u1} G _inst_1)))) x)) S)) -> (Membership.mem.{u1, u1} G (Subgroup.{u1} G _inst_1) (SetLike.instMembership.{u1, u1} (Subgroup.{u1} G _inst_1) G (Subgroup.instSetLikeSubgroup.{u1} G _inst_1)) x (Subgroup.setNormalizer.{u1} G _inst_1 S))
Case conversion may be inaccurate. Consider using '#align subgroup.mem_normalizer_fintype Subgroup.mem_normalizer_fintypeₓ'. -/
theorem mem_normalizer_fintype {S : Set G} [Finite S] {x : G} (h : ∀ n, n ∈ S → x * n * x⁻¹ ∈ S) :
    x ∈ Subgroup.setNormalizer S := by
  haveI := Classical.propDecidable <;> cases nonempty_fintype S <;>
      haveI := Set.fintypeImage S fun n => x * n * x⁻¹ <;>
    exact fun n =>
      ⟨h n, fun h₁ =>
        have heq : (fun n => x * n * x⁻¹) '' S = S :=
          Set.eq_of_subset_of_card_le (fun n ⟨y, hy⟩ => hy.2 ▸ h y hy.1)
            (by rw [Set.card_image_of_injective S conj_injective])
        have : x * n * x⁻¹ ∈ (fun n => x * n * x⁻¹) '' S := HEq.symm ▸ h₁
        let ⟨y, hy⟩ := this
        conj_injective hy.2 ▸ hy.1⟩
#align subgroup.mem_normalizer_fintype Subgroup.mem_normalizer_fintype

end Normalizer

end Subgroup

namespace MonoidHom

variable {N : Type _} [Group N]

open Subgroup

/- warning: monoid_hom.decidable_mem_range -> MonoidHom.decidableMemRange is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {N : Type.{u2}} [_inst_3 : Group.{u2} N] (f : MonoidHom.{u1, u2} G N (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} N (DivInvMonoid.toMonoid.{u2} N (Group.toDivInvMonoid.{u2} N _inst_3)))) [_inst_4 : Fintype.{u1} G] [_inst_5 : DecidableEq.{succ u2} N], DecidablePred.{succ u2} N (fun (_x : N) => Membership.Mem.{u2, u2} N (Subgroup.{u2} N _inst_3) (SetLike.hasMem.{u2, u2} (Subgroup.{u2} N _inst_3) N (Subgroup.setLike.{u2} N _inst_3)) _x (MonoidHom.range.{u1, u2} G _inst_1 N _inst_3 f))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {N : Type.{u2}} [_inst_3 : Group.{u2} N] (f : MonoidHom.{u1, u2} G N (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} N (DivInvMonoid.toMonoid.{u2} N (Group.toDivInvMonoid.{u2} N _inst_3)))) [_inst_4 : Fintype.{u1} G] [_inst_5 : DecidableEq.{succ u2} N], DecidablePred.{succ u2} N (fun (_x : N) => Membership.mem.{u2, u2} N (Subgroup.{u2} N _inst_3) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} N _inst_3) N (Subgroup.instSetLikeSubgroup.{u2} N _inst_3)) _x (MonoidHom.range.{u1, u2} G _inst_1 N _inst_3 f))
Case conversion may be inaccurate. Consider using '#align monoid_hom.decidable_mem_range MonoidHom.decidableMemRangeₓ'. -/
@[to_additive]
instance decidableMemRange (f : G →* N) [Fintype G] [DecidableEq N] : DecidablePred (· ∈ f.range) :=
  fun x => Fintype.decidableExistsFintype
#align monoid_hom.decidable_mem_range MonoidHom.decidableMemRange
#align add_monoid_hom.decidable_mem_range AddMonoidHom.decidableMemRange

/- warning: monoid_hom.fintype_mrange -> MonoidHom.fintypeMrange is a dubious translation:
lean 3 declaration is
  forall {M : Type.{u1}} {N : Type.{u2}} [_inst_4 : Monoid.{u1} M] [_inst_5 : Monoid.{u2} N] [_inst_6 : Fintype.{u1} M] [_inst_7 : DecidableEq.{succ u2} N] (f : MonoidHom.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_4) (Monoid.toMulOneClass.{u2} N _inst_5)), Fintype.{u2} (coeSort.{succ u2, succ (succ u2)} (Submonoid.{u2} N (Monoid.toMulOneClass.{u2} N _inst_5)) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Submonoid.{u2} N (Monoid.toMulOneClass.{u2} N _inst_5)) N (Submonoid.setLike.{u2} N (Monoid.toMulOneClass.{u2} N _inst_5))) (MonoidHom.mrange.{u1, u2, max u2 u1} M N (Monoid.toMulOneClass.{u1} M _inst_4) (Monoid.toMulOneClass.{u2} N _inst_5) (MonoidHom.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_4) (Monoid.toMulOneClass.{u2} N _inst_5)) (MonoidHom.monoidHomClass.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_4) (Monoid.toMulOneClass.{u2} N _inst_5)) f))
but is expected to have type
  forall {M : Type.{u1}} {N : Type.{u2}} [_inst_4 : Monoid.{u1} M] [_inst_5 : Monoid.{u2} N] [_inst_6 : Fintype.{u1} M] [_inst_7 : DecidableEq.{succ u2} N] (f : MonoidHom.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_4) (Monoid.toMulOneClass.{u2} N _inst_5)), Fintype.{u2} (Subtype.{succ u2} N (fun (x : N) => Membership.mem.{u2, u2} N (Submonoid.{u2} N (Monoid.toMulOneClass.{u2} N _inst_5)) (SetLike.instMembership.{u2, u2} (Submonoid.{u2} N (Monoid.toMulOneClass.{u2} N _inst_5)) N (Submonoid.instSetLikeSubmonoid.{u2} N (Monoid.toMulOneClass.{u2} N _inst_5))) x (MonoidHom.mrange.{u1, u2, max u1 u2} M N (Monoid.toMulOneClass.{u1} M _inst_4) (Monoid.toMulOneClass.{u2} N _inst_5) (MonoidHom.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_4) (Monoid.toMulOneClass.{u2} N _inst_5)) (MonoidHom.monoidHomClass.{u1, u2} M N (Monoid.toMulOneClass.{u1} M _inst_4) (Monoid.toMulOneClass.{u2} N _inst_5)) f)))
Case conversion may be inaccurate. Consider using '#align monoid_hom.fintype_mrange MonoidHom.fintypeMrangeₓ'. -/
-- this instance can't go just after the definition of `mrange` because `fintype` is
-- not imported at that stage
/-- The range of a finite monoid under a monoid homomorphism is finite.
Note: this instance can form a diamond with `subtype.fintype` in the
presence of `fintype N`. -/
@[to_additive
      "The range of a finite additive monoid under an additive monoid homomorphism is\nfinite.\n\nNote: this instance can form a diamond with `subtype.fintype` or `subgroup.fintype` in the\npresence of `fintype N`."]
instance fintypeMrange {M N : Type _} [Monoid M] [Monoid N] [Fintype M] [DecidableEq N]
    (f : M →* N) : Fintype (mrange f) :=
  Set.fintypeRange f
#align monoid_hom.fintype_mrange MonoidHom.fintypeMrange
#align add_monoid_hom.fintype_mrange AddMonoidHom.fintypeMrange

/- warning: monoid_hom.fintype_range -> MonoidHom.fintypeRange is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {N : Type.{u2}} [_inst_3 : Group.{u2} N] [_inst_4 : Fintype.{u1} G] [_inst_5 : DecidableEq.{succ u2} N] (f : MonoidHom.{u1, u2} G N (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} N (DivInvMonoid.toMonoid.{u2} N (Group.toDivInvMonoid.{u2} N _inst_3)))), Fintype.{u2} (coeSort.{succ u2, succ (succ u2)} (Subgroup.{u2} N _inst_3) Type.{u2} (SetLike.hasCoeToSort.{u2, u2} (Subgroup.{u2} N _inst_3) N (Subgroup.setLike.{u2} N _inst_3)) (MonoidHom.range.{u1, u2} G _inst_1 N _inst_3 f))
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] {N : Type.{u2}} [_inst_3 : Group.{u2} N] [_inst_4 : Fintype.{u1} G] [_inst_5 : DecidableEq.{succ u2} N] (f : MonoidHom.{u1, u2} G N (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} N (DivInvMonoid.toMonoid.{u2} N (Group.toDivInvMonoid.{u2} N _inst_3)))), Fintype.{u2} (Subtype.{succ u2} N (fun (x : N) => Membership.mem.{u2, u2} N (Subgroup.{u2} N _inst_3) (SetLike.instMembership.{u2, u2} (Subgroup.{u2} N _inst_3) N (Subgroup.instSetLikeSubgroup.{u2} N _inst_3)) x (MonoidHom.range.{u1, u2} G _inst_1 N _inst_3 f)))
Case conversion may be inaccurate. Consider using '#align monoid_hom.fintype_range MonoidHom.fintypeRangeₓ'. -/
/-- The range of a finite group under a group homomorphism is finite.

Note: this instance can form a diamond with `subtype.fintype` or `subgroup.fintype` in the
presence of `fintype N`. -/
@[to_additive
      "The range of a finite additive group under an additive group homomorphism is finite.\n\nNote: this instance can form a diamond with `subtype.fintype` or `subgroup.fintype` in the\npresence of `fintype N`."]
instance fintypeRange [Fintype G] [DecidableEq N] (f : G →* N) : Fintype (range f) :=
  Set.fintypeRange f
#align monoid_hom.fintype_range MonoidHom.fintypeRange
#align add_monoid_hom.fintype_range AddMonoidHom.fintypeRange

end MonoidHom


/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
import category_theory.subobject.well_powered
import category_theory.types

/-!
# `Type u` is well-powered

By building a categorical equivalence `mono_over α ≌ set α` for any `α : Type u`,
we deduce that `subobject α ≃o set α` and that `Type u` is well-powered.

One would hope that for a particular concrete category `C` (`AddCommGroup`, etc)
it's viable to prove `[well_powered C]` without explicitly aligning `subobject X`
with the "hand-rolled" definition of subobjects.

This may be possible using Lawvere theories,
but it remains to be seen whether this just pushes lumps around in the carpet.
-/

universes u

open category_theory
open category_theory.subobject

open_locale category_theory.Type

lemma subtype_val_mono {α : Type u} (s : set α) : mono ↾(subtype.val : s → α) :=
(mono_iff_injective _).mpr subtype.val_injective

local attribute [instance] subtype_val_mono

/--
The category of `mono_over α`, for `α : Type u`, is equivalent to the partial order `set α`.
-/
@[simps]
noncomputable
def types.mono_over_equivalence_set (α : Type u) : mono_over α ≌ set α :=
{ functor :=
  { obj := λ f, set.range f.1.hom,
    map := λ f g t, hom_of_le begin
      rintro a ⟨x, rfl⟩,
      exact ⟨t.1 x, congr_fun t.w x⟩,
    end, },
  inverse :=
  { obj := λ s, mono_over.mk' (subtype.val : s → α),
    map := λ s t b, mono_over.hom_mk (λ w, ⟨w.1, set.mem_of_mem_of_subset w.2 (le_of_hom b)⟩)
      (by { ext, simp, }), },
  unit_iso := nat_iso.of_components
    (λ f, mono_over.iso_mk
      (equiv.of_injective f.1.hom ((mono_iff_injective _).mp f.2)).to_iso (by tidy))
    (by tidy),
  counit_iso := nat_iso.of_components
    (λ s, eq_to_iso subtype.range_val)
    (by tidy), }

instance : well_powered (Type u) :=
well_powered_of_essentially_small_mono_over
  (λ α, essentially_small.mk' (types.mono_over_equivalence_set α))

/--
For `α : Type u`, `subobject α` is order isomorphic to `set α`.
-/
noncomputable def types.subobject_equiv_set (α : Type u) : subobject α ≃o set α :=
(types.mono_over_equivalence_set α).thin_skeleton_order_iso


structure Category :=
  (Obj : Type)
  (Hom : Obj → Obj → Type)

structure Functor (C : Category) (D : Category) :=
  (onObjects   : C^.Obj → D^.Obj)
  (onMorphisms : Π { X Y : C^.Obj },
                C^.Hom X Y → D^.Hom (onObjects X) (onObjects Y))

lemma Functors_pointwise_equal
  { C D : Category }
  { F G : Functor C D }
  ( objectWitness : ∀ X : C^.Obj, F^.onObjects X = G^.onObjects X )
  ( morphismWitness : ∀ X Y : C^.Obj, ∀ f : C^.Hom X Y, F^.onMorphisms f == G^.onMorphisms f ) : F = G :=
  begin

  end


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

! This file was ported from Lean 3 source module data.finite.set
! leanprover-community/mathlib commit 327c3c0d9232d80e250dc8f65e7835b82b266ea5
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.Data.Fintype.Card

/-!
# Lemmas about `finite` and `set`s

> THIS FILE IS SYNCHRONIZED WITH MATHLIB4.
> Any changes to this file require a corresponding PR to mathlib4.

In this file we prove two lemmas about `finite` and `set`s.

## Tags

finiteness, finite sets
-/

open Set

universe u v w x

variable {α : Type u} {β : Type v} {ι : Sort w}

#print Finite.Set.finite_of_finite_image /-
theorem Finite.Set.finite_of_finite_image (s : Set α) {f : α → β} (h : s.InjOn f)
    [Finite (f '' s)] : Finite s :=
  Finite.of_equiv _ (Equiv.ofBijective _ h.bijOn_image.Bijective).symm
#align finite.set.finite_of_finite_image Finite.Set.finite_of_finite_image
-/

#print Finite.of_injective_finite_range /-
theorem Finite.of_injective_finite_range {f : ι → α} (hf : Function.Injective f)
    [Finite (range f)] : Finite ι :=
  Finite.of_injective (Set.rangeFactorization f) (hf.codRestrict _)
#align finite.of_injective_finite_range Finite.of_injective_finite_range
-/


/-
Copyright (c) 2018 Johannes Hölzl. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Johannes Hölzl, Callum Sutton, Yury Kudryashov
-/
import group_theory.perm.basic

/-!
# Multiplicative and additive group automorphisms

This file defines the automorphism group structure on `add_aut R := add_equiv R R` and
`mul_aut R := mul_equiv R R`.

## Implementation notes

The definition of multiplication in the automorphism groups agrees with function composition,
multiplication in `equiv.perm`, and multiplication in `category_theory.End`, but not with
`category_theory.comp`.

This file is kept separate from `data/equiv/mul_add` so that `group_theory.perm` is free to use
equivalences (and other files that use them) before the group structure is defined.

## Tags

mul_aut, add_aut
-/
variables {A : Type*} {M : Type*} {G : Type*}

/-- The group of multiplicative automorphisms. -/
@[reducible, to_additive "The group of additive automorphisms."]
def mul_aut (M : Type*) [has_mul M] := M ≃* M

namespace mul_aut

variables (M) [has_mul M]

/--
The group operation on multiplicative automorphisms is defined by
`λ g h, mul_equiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance : group (mul_aut M) :=
by refine_struct
{ mul := λ g h, mul_equiv.trans h g,
  one := mul_equiv.refl M,
  inv := mul_equiv.symm,
  div := _,
  npow := @npow_rec _ ⟨mul_equiv.refl M⟩ ⟨λ g h, mul_equiv.trans h g⟩,
  zpow := @zpow_rec _ ⟨mul_equiv.refl M⟩ ⟨λ g h, mul_equiv.trans h g⟩ ⟨mul_equiv.symm⟩ };
intros; ext; try { refl }; apply equiv.left_inv

instance : inhabited (mul_aut M) := ⟨1⟩

@[simp] lemma coe_mul (e₁ e₂ : mul_aut M) : ⇑(e₁ * e₂) = e₁ ∘ e₂ := rfl
@[simp] lemma coe_one : ⇑(1 : mul_aut M) = id := rfl

lemma mul_def (e₁ e₂ : mul_aut M) : e₁ * e₂ = e₂.trans e₁ := rfl
lemma one_def : (1 : mul_aut M) = mul_equiv.refl _ := rfl
lemma inv_def (e₁ : mul_aut M) : e₁⁻¹ = e₁.symm := rfl
@[simp] lemma mul_apply (e₁ e₂ : mul_aut M) (m : M) : (e₁ * e₂) m = e₁ (e₂ m) := rfl
@[simp] lemma one_apply (m : M) : (1 : mul_aut M) m = m := rfl

@[simp] lemma apply_inv_self (e : mul_aut M) (m : M) : e (e⁻¹ m) = m :=
mul_equiv.apply_symm_apply _ _

@[simp] lemma inv_apply_self (e : mul_aut M) (m : M) : e⁻¹ (e m) = m :=
mul_equiv.apply_symm_apply _ _

/-- Monoid hom from the group of multiplicative automorphisms to the group of permutations. -/
def to_perm : mul_aut M →* equiv.perm M :=
by refine_struct { to_fun := mul_equiv.to_equiv }; intros; refl

/-- The tautological action by `mul_aut M` on `M`.

This generalizes `function.End.apply_mul_action`. -/
instance apply_mul_distrib_mul_action {M} [monoid M] : mul_distrib_mul_action (mul_aut M) M :=
{ smul := ($),
  one_smul := λ _, rfl,
  mul_smul := λ _ _ _, rfl,
  smul_one := mul_equiv.map_one,
  smul_mul := mul_equiv.map_mul }

@[simp] protected lemma smul_def {M} [monoid M] (f : mul_aut M) (a : M) : f • a = f a := rfl

/-- `mul_aut.apply_mul_action` is faithful. -/
instance apply_has_faithful_scalar {M} [monoid M] : has_faithful_scalar (mul_aut M) M :=
⟨λ _ _, mul_equiv.ext⟩

/-- Group conjugation, `mul_aut.conj g h = g * h * g⁻¹`, as a monoid homomorphism
mapping multiplication in `G` into multiplication in the automorphism group `mul_aut G`.
See also the type `conj_act G` for any group `G`, which has a `mul_action (conj_act G) G` instance
where `conj G` acts on `G` by conjugation. -/
def conj [group G] : G →* mul_aut G :=
{ to_fun := λ g,
  { to_fun := λ h, g * h * g⁻¹,
    inv_fun := λ h, g⁻¹ * h * g,
    left_inv := λ _, by simp [mul_assoc],
    right_inv := λ _, by simp [mul_assoc],
    map_mul' := by simp [mul_assoc] },
  map_mul' := λ _ _, by ext; simp [mul_assoc],
  map_one' := by ext; simp [mul_assoc] }

@[simp] lemma conj_apply [group G] (g h : G) : conj g h = g * h * g⁻¹ := rfl
@[simp] lemma conj_symm_apply [group G] (g h : G) : (conj g).symm h = g⁻¹ * h * g := rfl
@[simp] lemma conj_inv_apply [group G] (g h : G) : (conj g)⁻¹ h = g⁻¹ * h * g := rfl

end mul_aut

namespace add_aut

variables (A) [has_add A]

/--
The group operation on additive automorphisms is defined by
`λ g h, add_equiv.trans h g`.
This means that multiplication agrees with composition, `(g*h)(x) = g (h x)`.
-/
instance group : group (add_aut A) :=
by refine_struct
{ mul := λ g h, add_equiv.trans h g,
  one := add_equiv.refl A,
  inv := add_equiv.symm,
  div := _,
  npow := @npow_rec _ ⟨add_equiv.refl A⟩ ⟨λ g h, add_equiv.trans h g⟩,
  zpow := @zpow_rec _ ⟨add_equiv.refl A⟩ ⟨λ g h, add_equiv.trans h g⟩ ⟨add_equiv.symm⟩ };
intros; ext; try { refl }; apply equiv.left_inv

instance : inhabited (add_aut A) := ⟨1⟩

@[simp] lemma coe_mul (e₁ e₂ : add_aut A) : ⇑(e₁ * e₂) = e₁ ∘ e₂ := rfl
@[simp] lemma coe_one : ⇑(1 : add_aut A) = id := rfl

lemma mul_def (e₁ e₂ : add_aut A) : e₁ * e₂ = e₂.trans e₁ := rfl
lemma one_def : (1 : add_aut A) = add_equiv.refl _ := rfl
lemma inv_def (e₁ : add_aut A) : e₁⁻¹ = e₁.symm := rfl
@[simp] lemma mul_apply (e₁ e₂ : add_aut A) (a : A) : (e₁ * e₂) a = e₁ (e₂ a) := rfl
@[simp] lemma one_apply (a : A) : (1 : add_aut A) a = a := rfl

@[simp] lemma apply_inv_self (e : add_aut A) (a : A) : e⁻¹ (e a) = a :=
add_equiv.apply_symm_apply _ _

@[simp] lemma inv_apply_self (e : add_aut A) (a : A) : e (e⁻¹ a) = a :=
add_equiv.apply_symm_apply _ _

/-- Monoid hom from the group of multiplicative automorphisms to the group of permutations. -/
def to_perm : add_aut A →* equiv.perm A :=
by refine_struct { to_fun := add_equiv.to_equiv }; intros; refl

/-- The tautological action by `add_aut A` on `A`.

This generalizes `function.End.apply_mul_action`. -/
instance apply_distrib_mul_action {A} [add_monoid A] : distrib_mul_action (add_aut A) A :=
{ smul := ($),
  smul_zero := add_equiv.map_zero,
  smul_add := add_equiv.map_add,
  one_smul := λ _, rfl,
  mul_smul := λ _ _ _, rfl }

@[simp] protected lemma smul_def {A} [add_monoid A] (f : add_aut A) (a : A) : f • a = f a := rfl

/-- `add_aut.apply_distrib_mul_action` is faithful. -/
instance apply_has_faithful_scalar {A} [add_monoid A] : has_faithful_scalar (add_aut A) A :=
⟨λ _ _, add_equiv.ext⟩

/-- Additive group conjugation, `add_aut.conj g h = g + h - g`, as an additive monoid
homomorphism mapping addition in `G` into multiplication in the automorphism group `add_aut G`
(written additively in order to define the map). -/
def conj [add_group G] : G →+ additive (add_aut G) :=
{ to_fun := λ g, @additive.of_mul (add_aut G)
  { to_fun := λ h, g + h + -g, -- this definition is chosen to match `mul_aut.conj`
    inv_fun := λ h, -g + h + g,
    left_inv := λ _, by simp [add_assoc],
    right_inv := λ _, by simp [add_assoc],
    map_add' := by simp [add_assoc] },
  map_add' := λ _ _, by apply additive.to_mul.injective; ext; simp [add_assoc],
  map_zero' := by ext; simpa }

@[simp] lemma conj_apply [add_group G] (g h : G) : conj g h = g + h + -g := rfl
@[simp] lemma conj_symm_apply [add_group G] (g h : G) : (conj g).symm h = -g + h + g := rfl
@[simp] lemma conj_inv_apply [add_group G] (g h : G) : (-(conj g)) h = -g + h + g := rfl

end add_aut


constant α : Type
constant empty_set : set α
constant full_set : set α
axiom empty.branch {A : set α} : A = empty_set ∨ A ≠ empty_set

constant element : α → set α → Prop
local notation a ` ∈ ` b := element a b
reserve infix ` ∈ `: 50

-- Subset
constant subset : set α → set α → Prop
axiom subset.empty_some {A : set α} : subset empty_set A
lemma subset.not_some_empty {A : set α} (h : A ≠ empty_set) :
  ¬ subset A empty_set := sorry

lemma subset.empty_empty : subset empty_set empty_set := subset.empty_some

-- Inverse
constant inverse : set α → set α
axiom inverse.empty : inverse empty_set = full_set
lemma full_set.def : full_set = inverse empty_set := inverse.empty.symm

-- Intersection
constant intersect : set α → set α → set α
axiom intersect.empty {A : set α} : intersect A empty_set = empty_set
axiom intersect.full_set {A : set α} : intersect A full_set = A

axiom intersect.subset {A B : set α} (h : subset A B) : intersect A B = A

lemma intersect.empty_empty : intersect empty_set empty_set = empty_set :=
  @intersect.empty empty_set

lemma intersect.full_full : intersect full_set full_set = full_set :=
  @intersect.full_set full_set

lemma intersect.def {x : α} {A B : set α} : (x ∈ intersect A B) ↔ (x ∈ A ∧ x ∈ B) := sorry

-- Subtraction
constant subtract : set α → set α → set α
axiom subtract.def (A B : set α) : subtract A B = intersect A (inverse B)

-- Union
constant union : set α → set α → set α

axiom union.empty {A : set α} : union A empty_set = A
axiom union.step {A B C : set α} (h : subset C B) :
  union A B = union C (union A (subtract B C))

lemma union.def {x : α} {A B : set α} : (x ∈ union A B) ↔ (x ∈ A ∨ x ∈ B) := sorry

lemma union.comm {A B : set α} : union A B = union B A := sorry

#print union.def
#print union.empty

lemma empty_union (A : set α) : union empty_set A = A :=
begin
  rw union.comm,
  apply union.empty,
  -- give shorter proof:
  -- =>> λ (A : set α), (id ((eq.refl (union empty_set A = A)).rec
  --    (union.comm empty_set A))).mpr (union.empty A)
  -- than rewrite tactic:
  -- rw union.empty,
  -- =>> λ (A : set α),
  --   (id ((eq.refl (union empty_set A = A)).rec (union.comm empty_set A))).mpr
  --     ((id ((eq.refl (union A empty_set = A)).rec (union.empty A))).mpr (eq.refl A))
end

constant is_empty : set α → Prop
axiom is_empty.base : is_empty empty_set

constant singleton_set : α → set α

constant set.without : set α → α → set α
axiom set.without.base (x : α) : set.without empty_set x = empty_set
-- Singleton ≈ set that contains only one element
lemma set.without.singleton (x : α) (A : set α) :
  (singleton_set x).without x = empty_set := sorry

constant size_of_set : set α → ℕ
axiom size_of_set.base : size_of_set empty_set = 0
axiom size_of_set.step (x : α) (A : set α) :
  element x A → size_of_set A = size_of_set (A.without x)

-- Definition of branching by assumption
constant subset_from_prop : (α → Prop) → set α → set α

axiom subset_from_prop.def (property : α → Prop) (A : set α) :
  subset_from_prop (λ _, true) A =
  union
    (subset_from_prop             property    A)
    (subset_from_prop (λ x : α, ¬ property x) A)
  ∧ subset (subset_from_prop             property    A) A
  ∧ subset (subset_from_prop (λ x : α, ¬ property x) A) A

-- lemma not_empty_imp_has_element (A : set α) :
--   A

/--
  if A ≠ ∅ we have:
      ⊢ A ≠ ∅ → union A A = A
    => by: ∀ A : set, union A A = ∅ → A = ∅
    | union A A ≠ ∅
    => by: ∀ A : set α, A ≠ ∅ → ∃ (x : α), x ∈ A
    | (∃ x, x ∈ A)
    ...
    (x ∈ union A A) ↔ (x ∈ A ∨ x ∈ A)
    ...
  otherwise A = ∅:
      ⊢ union ∅ ∅ = ∅
    by: union.empty: ∀ A : set, union A ∅ = A
    | union ∅ ∅ = ∅
    Qed.

  -/
lemma union.self₁ (A : set α) : union A A = A :=
begin

  sorry,
end

lemma union.self₂ (x : α) (A : set α) : element x (union A A) = element x A :=
begin
  have h := @union.def x A A,
  have h2 := or_self (element x A),
  rw ←h2,
  exact h.to_eq,
end


/-
Copyright (c) 2018 Kenny Lau. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kenny Lau, Mario Carneiro, Johan Commelin, Amelia Livingston, Anne Baanen
-/
import ring_theory.ideal.local_ring
import ring_theory.localization.ideal

/-!
# Localizations of commutative rings at the complement of a prime ideal

## Main definitions

 * `is_localization.at_prime (I : ideal R) [is_prime I] (S : Type*)` expresses that `S` is a
   localization at (the complement of) a prime ideal `I`, as an abbreviation of
   `is_localization I.prime_compl S`

## Main results

 * `is_localization.at_prime.local_ring`: a theorem (not an instance) stating a localization at the
   complement of a prime ideal is a local ring

## Implementation notes

See `src/ring_theory/localization/basic.lean` for a design overview.

## Tags
localization, ring localization, commutative ring localization, characteristic predicate,
commutative ring, field of fractions
-/
variables {R : Type*} [comm_semiring R] (M : submonoid R) (S : Type*) [comm_semiring S]
variables [algebra R S] {P : Type*} [comm_semiring P]

section at_prime

variables (I : ideal R) [hp : I.is_prime]
include hp
namespace ideal

/-- The complement of a prime ideal `I ⊆ R` is a submonoid of `R`. -/
def prime_compl :
  submonoid R :=
{ carrier := (Iᶜ : set R),
  one_mem' := by convert I.ne_top_iff_one.1 hp.1; refl,
  mul_mem' := λ x y hnx hny hxy, or.cases_on (hp.mem_or_mem hxy) hnx hny }

lemma prime_compl_le_non_zero_divisors [no_zero_divisors R] : I.prime_compl ≤ non_zero_divisors R :=
le_non_zero_divisors_of_no_zero_divisors $ not_not_intro I.zero_mem

end ideal

variables (S)

/-- Given a prime ideal `P`, the typeclass `is_localization.at_prime S P` states that `S` is
isomorphic to the localization of `R` at the complement of `P`. -/
protected abbreviation is_localization.at_prime := is_localization I.prime_compl S

/-- Given a prime ideal `P`, `localization.at_prime S P` is a localization of
`R` at the complement of `P`, as a quotient type. -/
protected abbreviation localization.at_prime := localization I.prime_compl

namespace is_localization

lemma at_prime.nontrivial [is_localization.at_prime S I] : nontrivial S :=
nontrivial_of_ne (0 : S) 1 $ λ hze,
begin
  rw [←(algebra_map R S).map_one, ←(algebra_map R S).map_zero] at hze,
  obtain ⟨t, ht⟩ := (eq_iff_exists I.prime_compl S).1 hze,
  have htz : (t : R) = 0, by simpa using ht.symm,
  exact t.2 (htz.symm ▸ I.zero_mem : ↑t ∈ I)
end

local attribute [instance] at_prime.nontrivial

theorem at_prime.local_ring [is_localization.at_prime S I] : local_ring S :=
local_ring.of_nonunits_add
begin
  intros x y hx hy hu,
  cases is_unit_iff_exists_inv.1 hu with z hxyz,
  have : ∀ {r : R} {s : I.prime_compl}, mk' S r s ∈ nonunits S → r ∈ I, from
    λ (r : R) (s : I.prime_compl), not_imp_comm.1
      (λ nr, is_unit_iff_exists_inv.2 ⟨mk' S ↑s (⟨r, nr⟩ : I.prime_compl),
        mk'_mul_mk'_eq_one' _ _ nr⟩),
  rcases mk'_surjective I.prime_compl x with ⟨rx, sx, hrx⟩,
  rcases mk'_surjective I.prime_compl y with ⟨ry, sy, hry⟩,
  rcases mk'_surjective I.prime_compl z with ⟨rz, sz, hrz⟩,
  rw [←hrx, ←hry, ←hrz, ←mk'_add, ←mk'_mul,
      ←mk'_self S I.prime_compl.one_mem] at hxyz,
  rw ←hrx at hx, rw ←hry at hy,
  obtain ⟨t, ht⟩ := is_localization.eq.1 hxyz,
  simp only [mul_one, one_mul, submonoid.coe_mul, subtype.coe_mk] at ht,
  suffices : ↑t * (↑sx * ↑sy * ↑sz) ∈ I, from
    not_or (mt hp.mem_or_mem $ not_or sx.2 sy.2) sz.2
      (hp.mem_or_mem $ (hp.mem_or_mem this).resolve_left t.2),
  rw [←ht],
  exact I.mul_mem_left _ (I.mul_mem_right _ (I.add_mem (I.mul_mem_right _ $ this hx)
                                                       (I.mul_mem_right _ $ this hy))),
end

end is_localization

namespace localization

/-- The localization of `R` at the complement of a prime ideal is a local ring. -/
instance at_prime.local_ring : local_ring (localization I.prime_compl) :=
is_localization.at_prime.local_ring (localization I.prime_compl) I

end localization

end at_prime

namespace is_localization

variables {A : Type*} [comm_ring A] [is_domain A]

/--
The localization of an integral domain at the complement of a prime ideal is an integral domain.
-/
instance is_domain_of_local_at_prime {P : ideal A} (hp : P.is_prime) :
  is_domain (localization.at_prime P) :=
is_domain_localization P.prime_compl_le_non_zero_divisors

namespace at_prime

variables (I : ideal R) [hI : I.is_prime] [is_localization.at_prime S I]
include hI

lemma is_unit_to_map_iff (x : R) :
  is_unit ((algebra_map R S) x) ↔ x ∈ I.prime_compl :=
⟨λ h hx, (is_prime_of_is_prime_disjoint I.prime_compl S I hI disjoint_compl_left).ne_top $
  (ideal.map (algebra_map R S) I).eq_top_of_is_unit_mem (ideal.mem_map_of_mem _ hx) h,
λ h, map_units S ⟨x, h⟩⟩

-- Can't use typeclasses to infer the `local_ring` instance, so use an `opt_param` instead
-- (since `local_ring` is a `Prop`, there should be no unification issues.)
lemma to_map_mem_maximal_iff (x : R) (h : _root_.local_ring S := local_ring S I) :
  algebra_map R S x ∈ local_ring.maximal_ideal S ↔ x ∈ I :=
not_iff_not.mp $ by
simpa only [local_ring.mem_maximal_ideal, mem_nonunits_iff, not_not]
  using is_unit_to_map_iff S I x

lemma comap_maximal_ideal (h : _root_.local_ring S := local_ring S I) :
  (local_ring.maximal_ideal S).comap (algebra_map R S) = I :=
ideal.ext $ λ x, by simpa only [ideal.mem_comap] using to_map_mem_maximal_iff _ I x

lemma is_unit_mk'_iff (x : R) (y : I.prime_compl) :
  is_unit (mk' S x y) ↔ x ∈ I.prime_compl :=
⟨λ h hx, mk'_mem_iff.mpr ((to_map_mem_maximal_iff S I x).mpr hx) h,
λ h, is_unit_iff_exists_inv.mpr ⟨mk' S ↑y ⟨x, h⟩, mk'_mul_mk'_eq_one ⟨x, h⟩ y⟩⟩

lemma mk'_mem_maximal_iff (x : R) (y : I.prime_compl) (h : _root_.local_ring S := local_ring S I) :
  mk' S x y ∈ local_ring.maximal_ideal S ↔ x ∈ I :=
not_iff_not.mp $ by
simpa only [local_ring.mem_maximal_ideal, mem_nonunits_iff, not_not]
  using is_unit_mk'_iff S I x y

end at_prime

end is_localization

namespace localization

open is_localization

local attribute [instance] classical.prop_decidable

variables (I : ideal R) [hI : I.is_prime]
include hI

variables {I}
/-- The unique maximal ideal of the localization at `I.prime_compl` lies over the ideal `I`. -/
lemma at_prime.comap_maximal_ideal :
  ideal.comap (algebra_map R (localization.at_prime I))
    (local_ring.maximal_ideal (localization I.prime_compl)) = I :=
at_prime.comap_maximal_ideal _ _

/-- The image of `I` in the localization at `I.prime_compl` is a maximal ideal, and in particular
it is the unique maximal ideal given by the local ring structure `at_prime.local_ring` -/
lemma at_prime.map_eq_maximal_ideal :
  ideal.map (algebra_map R (localization.at_prime I)) I =
    (local_ring.maximal_ideal (localization I.prime_compl)) :=
begin
  convert congr_arg (ideal.map _) at_prime.comap_maximal_ideal.symm,
  rw map_comap I.prime_compl
end

lemma le_comap_prime_compl_iff {J : ideal P} [hJ : J.is_prime] {f : R →+* P} :
  I.prime_compl ≤ J.prime_compl.comap f ↔ J.comap f ≤ I :=
⟨λ h x hx, by { contrapose! hx, exact h hx },
 λ h x hx hfxJ, hx (h hfxJ)⟩

variables (I)

/--
For a ring hom `f : R →+* S` and a prime ideal `J` in `S`, the induced ring hom from the
localization of `R` at `J.comap f` to the localization of `S` at `J`.

To make this definition more flexible, we allow any ideal `I` of `R` as input, together with a proof
that `I = J.comap f`. This can be useful when `I` is not definitionally equal to `J.comap f`.
 -/
noncomputable def local_ring_hom (J : ideal P) [hJ : J.is_prime] (f : R →+* P)
  (hIJ : I = J.comap f) :
  localization.at_prime I →+* localization.at_prime J :=
is_localization.map (localization.at_prime J) f (le_comap_prime_compl_iff.mpr (ge_of_eq hIJ))

lemma local_ring_hom_to_map (J : ideal P) [hJ : J.is_prime] (f : R →+* P)
  (hIJ : I = J.comap f) (x : R) :
  local_ring_hom I J f hIJ (algebra_map _ _ x) = algebra_map _ _ (f x) :=
map_eq _ _

lemma local_ring_hom_mk' (J : ideal P) [hJ : J.is_prime] (f : R →+* P)
  (hIJ : I = J.comap f) (x : R) (y : I.prime_compl) :
  local_ring_hom I J f hIJ (is_localization.mk' _ x y) =
    is_localization.mk' (localization.at_prime J) (f x)
      (⟨f y, le_comap_prime_compl_iff.mpr (ge_of_eq hIJ) y.2⟩ : J.prime_compl) :=
map_mk' _ _ _

instance is_local_ring_hom_local_ring_hom (J : ideal P) [hJ : J.is_prime] (f : R →+* P)
  (hIJ : I = J.comap f) :
  is_local_ring_hom (local_ring_hom I J f hIJ) :=
is_local_ring_hom.mk $ λ x hx,
begin
  rcases is_localization.mk'_surjective I.prime_compl x with ⟨r, s, rfl⟩,
  rw local_ring_hom_mk' at hx,
  rw at_prime.is_unit_mk'_iff at hx ⊢,
  exact λ hr, hx ((set_like.ext_iff.mp hIJ r).mp hr),
end

lemma local_ring_hom_unique (J : ideal P) [hJ : J.is_prime] (f : R →+* P)
  (hIJ : I = J.comap f) {j : localization.at_prime I →+* localization.at_prime J}
  (hj : ∀ x : R, j (algebra_map _ _ x) = algebra_map _ _ (f x)) :
  local_ring_hom I J f hIJ = j :=
map_unique _ _ hj

@[simp] lemma local_ring_hom_id :
  local_ring_hom I I (ring_hom.id R) (ideal.comap_id I).symm = ring_hom.id _ :=
local_ring_hom_unique _ _ _ _ (λ x, rfl)

@[simp] lemma local_ring_hom_comp {S : Type*} [comm_semiring S]
  (J : ideal S) [hJ : J.is_prime] (K : ideal P) [hK : K.is_prime]
  (f : R →+* S) (hIJ : I = J.comap f) (g : S →+* P) (hJK : J = K.comap g) :
  local_ring_hom I K (g.comp f) (by rw [hIJ, hJK, ideal.comap_comap f g]) =
  (local_ring_hom J K g hJK).comp (local_ring_hom I J f hIJ) :=
local_ring_hom_unique _ _ _ _
  (λ r, by simp only [function.comp_app, ring_hom.coe_comp, local_ring_hom_to_map])

end localization