/-
Copyright (c) 2021 David Wärn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: David Wärn, Eric Wieser, Joachim Breitner

! This file was ported from Lean 3 source module group_theory.is_free_group
! leanprover-community/mathlib commit fac369018417f980cec5fcdafc766a69f88d8cfe
! Please do not edit these lines, except to modify the commit id
! if you have ported upstream changes.
-/
import Mathbin.GroupTheory.FreeGroup

/-!
# Free groups structures on arbitrary types

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

This file defines a type class for type that are free groups, together with the usual operations.
The type class can be instantiated by providing an isomorphim to the canonical free group, or by
proving that the universal property holds.

For the explicit construction of free groups, see `group_theory/free_group`.

## Main definitions

* `is_free_group G` - a typeclass to indicate that `G` is free over some generators
* `is_free_group.of` - the canonical injection of `G`'s generators into `G`
* `is_free_group.lift` - the universal property of the free group

## Main results

* `is_free_group.to_free_group` - any free group with generators `A` is equivalent to
  `free_group A`.
* `is_free_group.unique_lift` - the universal property of a free group
* `is_free_group.of_unique_lift` - constructing `is_free_group` from the universal property

-/

universe u

#print IsFreeGroup /-
/- ./././Mathport/Syntax/Translate/Command.lean:388:30: infer kinds are unsupported in Lean 4: #[`MulEquiv] [] -/
/-- `is_free_group G` means that `G` isomorphic to a free group. -/
class IsFreeGroup (G : Type u) [Group G] where
  Generators : Type u
  MulEquiv : FreeGroup generators ≃* G
#align is_free_group IsFreeGroup
-/

instance (X : Type _) : IsFreeGroup (FreeGroup X)
    where
  Generators := X
  MulEquiv := MulEquiv.refl _

namespace IsFreeGroup

variable (G : Type _) [Group G] [IsFreeGroup G]

/- warning: is_free_group.to_free_group -> IsFreeGroup.toFreeGroup is a dubious translation:
lean 3 declaration is
  forall (G : Type.{u1}) [_inst_1 : Group.{u1} G] [_inst_2 : IsFreeGroup.{u1} G _inst_1], MulEquiv.{u1, u1} G (FreeGroup.{u1} (IsFreeGroup.Generators.{u1} G _inst_1 _inst_2)) (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (FreeGroup.hasMul.{u1} (IsFreeGroup.Generators.{u1} G _inst_1 _inst_2))
but is expected to have type
  forall (G : Type.{u1}) [_inst_1 : Group.{u1} G] [_inst_2 : IsFreeGroup.{u1} G _inst_1], MulEquiv.{u1, u1} G (FreeGroup.{u1} (IsFreeGroup.Generators.{u1} G _inst_1 _inst_2)) (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (FreeGroup.instMulFreeGroup.{u1} (IsFreeGroup.Generators.{u1} G _inst_1 _inst_2))
Case conversion may be inaccurate. Consider using '#align is_free_group.to_free_group IsFreeGroup.toFreeGroupₓ'. -/
/-- Any free group is isomorphic to "the" free group. -/
@[simps]
def toFreeGroup : G ≃* FreeGroup (Generators G) :=
  (mulEquiv G).symm
#align is_free_group.to_free_group IsFreeGroup.toFreeGroup

variable {G}

#print IsFreeGroup.of /-
/-- The canonical injection of G's generators into G -/
def of : Generators G → G :=
  (mulEquiv G).toFun ∘ FreeGroup.of
#align is_free_group.of IsFreeGroup.of
-/

/- warning: is_free_group.of_eq_free_group_of -> IsFreeGroup.of_eq_freeGroup_of is a dubious translation:
lean 3 declaration is
  forall {A : Type.{u1}}, Eq.{succ u1} ((IsFreeGroup.Generators.{u1} (FreeGroup.{u1} A) (FreeGroup.group.{u1} A) (FreeGroup.isFreeGroup.{u1} A)) -> (FreeGroup.{u1} A)) (IsFreeGroup.of.{u1} (FreeGroup.{u1} A) (FreeGroup.group.{u1} A) (FreeGroup.isFreeGroup.{u1} A)) (FreeGroup.of.{u1} (IsFreeGroup.Generators.{u1} (FreeGroup.{u1} A) (FreeGroup.group.{u1} A) (FreeGroup.isFreeGroup.{u1} A)))
but is expected to have type
  forall {A : Type.{u1}}, Eq.{succ u1} ((IsFreeGroup.Generators.{u1} (FreeGroup.{u1} A) (FreeGroup.instGroupFreeGroup.{u1} A) (instIsFreeGroupFreeGroupInstGroupFreeGroup.{u1} A)) -> (FreeGroup.{u1} A)) (IsFreeGroup.of.{u1} (FreeGroup.{u1} A) (FreeGroup.instGroupFreeGroup.{u1} A) (instIsFreeGroupFreeGroupInstGroupFreeGroup.{u1} A)) (FreeGroup.of.{u1} (IsFreeGroup.Generators.{u1} (FreeGroup.{u1} A) (FreeGroup.instGroupFreeGroup.{u1} A) (instIsFreeGroupFreeGroupInstGroupFreeGroup.{u1} A)))
Case conversion may be inaccurate. Consider using '#align is_free_group.of_eq_free_group_of IsFreeGroup.of_eq_freeGroup_ofₓ'. -/
@[simp]
theorem of_eq_freeGroup_of {A : Type u} : @of (FreeGroup A) _ _ = FreeGroup.of :=
  rfl
#align is_free_group.of_eq_free_group_of IsFreeGroup.of_eq_freeGroup_of

variable {H : Type _} [Group H]

#print IsFreeGroup.lift /-
/-- The equivalence between functions on the generators and group homomorphisms from a free group
given by those generators. -/
def lift : (Generators G → H) ≃ (G →* H) :=
  FreeGroup.lift.trans
    { toFun := fun f => f.comp (mulEquiv G).symm.toMonoidHom
      invFun := fun f => f.comp (mulEquiv G).toMonoidHom
      left_inv := fun f => by
        ext
        simp
      right_inv := fun f => by
        ext
        simp }
#align is_free_group.lift IsFreeGroup.lift
-/

/- warning: is_free_group.lift'_eq_free_group_lift -> IsFreeGroup.lift'_eq_freeGroup_lift is a dubious translation:
lean 3 declaration is
  forall {H : Type.{u2}} [_inst_3 : Group.{u2} H] {A : Type.{u1}}, Eq.{max 1 (max (max (succ u1) (succ u2)) (succ u2) (succ u1)) (max (succ u2) (succ u1)) (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u2) (succ u1)} ((IsFreeGroup.Generators.{u1} (FreeGroup.{u1} A) (FreeGroup.group.{u1} A) (FreeGroup.isFreeGroup.{u1} A)) -> H) (MonoidHom.{u1, u2} (FreeGroup.{u1} A) H (Monoid.toMulOneClass.{u1} (FreeGroup.{u1} A) (DivInvMonoid.toMonoid.{u1} (FreeGroup.{u1} A) (Group.toDivInvMonoid.{u1} (FreeGroup.{u1} A) (FreeGroup.group.{u1} A)))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3))))) (IsFreeGroup.lift.{u1, u2} (FreeGroup.{u1} A) (FreeGroup.group.{u1} A) (FreeGroup.isFreeGroup.{u1} A) H _inst_3) (FreeGroup.lift.{u1, u2} (IsFreeGroup.Generators.{u1} (FreeGroup.{u1} A) (FreeGroup.group.{u1} A) (FreeGroup.isFreeGroup.{u1} A)) H _inst_3)
but is expected to have type
  forall {H : Type.{u1}} [_inst_3 : Group.{u1} H] {A : Type.{u2}}, Eq.{max (succ u2) (succ u1)} (Equiv.{max (succ u2) (succ u1), max (succ u1) (succ u2)} ((IsFreeGroup.Generators.{u2} (FreeGroup.{u2} A) (FreeGroup.instGroupFreeGroup.{u2} A) (instIsFreeGroupFreeGroupInstGroupFreeGroup.{u2} A)) -> H) (MonoidHom.{u2, u1} (FreeGroup.{u2} A) H (Monoid.toMulOneClass.{u2} (FreeGroup.{u2} A) (DivInvMonoid.toMonoid.{u2} (FreeGroup.{u2} A) (Group.toDivInvMonoid.{u2} (FreeGroup.{u2} A) (FreeGroup.instGroupFreeGroup.{u2} A)))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3))))) (IsFreeGroup.lift.{u2, u1} (FreeGroup.{u2} A) (FreeGroup.instGroupFreeGroup.{u2} A) (instIsFreeGroupFreeGroupInstGroupFreeGroup.{u2} A) H _inst_3) (FreeGroup.lift.{u2, u1} (IsFreeGroup.Generators.{u2} (FreeGroup.{u2} A) (FreeGroup.instGroupFreeGroup.{u2} A) (instIsFreeGroupFreeGroupInstGroupFreeGroup.{u2} A)) H _inst_3)
Case conversion may be inaccurate. Consider using '#align is_free_group.lift'_eq_free_group_lift IsFreeGroup.lift'_eq_freeGroup_liftₓ'. -/
@[simp]
theorem lift'_eq_freeGroup_lift {A : Type u} : @lift (FreeGroup A) _ _ H _ = FreeGroup.lift :=
  rfl
#align is_free_group.lift'_eq_free_group_lift IsFreeGroup.lift'_eq_freeGroup_lift

/- warning: is_free_group.lift_of -> IsFreeGroup.lift_of is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] [_inst_2 : IsFreeGroup.{u1} G _inst_1] {H : Type.{u2}} [_inst_3 : Group.{u2} H] (f : (IsFreeGroup.Generators.{u1} G _inst_1 _inst_2) -> H) (a : IsFreeGroup.Generators.{u1} G _inst_1 _inst_2), Eq.{succ u2} H (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) (fun (_x : MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) => G -> H) (MonoidHom.hasCoeToFun.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) (coeFn.{max 1 (max (max (succ u1) (succ u2)) (succ u2) (succ u1)) (max (succ u2) (succ u1)) (succ u1) (succ u2), max (max (succ u1) (succ u2)) (succ u2) (succ u1)} (Equiv.{max (succ u1) (succ u2), max (succ u2) (succ u1)} ((IsFreeGroup.Generators.{u1} G _inst_1 _inst_2) -> H) (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3))))) (fun (_x : Equiv.{max (succ u1) (succ u2), max (succ u2) (succ u1)} ((IsFreeGroup.Generators.{u1} G _inst_1 _inst_2) -> H) (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3))))) => ((IsFreeGroup.Generators.{u1} G _inst_1 _inst_2) -> H) -> (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3))))) (Equiv.hasCoeToFun.{max (succ u1) (succ u2), max (succ u2) (succ u1)} ((IsFreeGroup.Generators.{u1} G _inst_1 _inst_2) -> H) (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3))))) (IsFreeGroup.lift.{u1, u2} G _inst_1 _inst_2 H _inst_3) f) (IsFreeGroup.of.{u1} G _inst_1 _inst_2 a)) (f a)
but is expected to have type
  forall {G : Type.{u2}} [_inst_1 : Group.{u2} G] [_inst_2 : IsFreeGroup.{u2} G _inst_1] {H : Type.{u1}} [_inst_3 : Group.{u1} H] (f : (IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H) (a : IsFreeGroup.Generators.{u2} G _inst_1 _inst_2), Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) (IsFreeGroup.of.{u2} G _inst_1 _inst_2 a)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : (IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H) => MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) f) G (fun (_x : G) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : (IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H) => MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) f) G H (MulOneClass.toMul.{u2} G (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)))) (MulOneClass.toMul.{u1} H (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) (MonoidHomClass.toMulHomClass.{max u2 u1, u2, u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : (IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H) => MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) f) G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3))) (MonoidHom.monoidHomClass.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))))) (FunLike.coe.{max (succ u1) (succ u2), max (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u2) (succ u1), max (succ u1) (succ u2)} ((IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H) (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3))))) ((IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H) (fun (_x : (IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : (IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H) => MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) _x) (Equiv.instFunLikeEquiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} ((IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H) (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3))))) (IsFreeGroup.lift.{u2, u1} G _inst_1 _inst_2 H _inst_3) f) (IsFreeGroup.of.{u2} G _inst_1 _inst_2 a)) (f a)
Case conversion may be inaccurate. Consider using '#align is_free_group.lift_of IsFreeGroup.lift_ofₓ'. -/
@[simp]
theorem lift_of (f : Generators G → H) (a : Generators G) : lift f (of a) = f a :=
  congr_fun (lift.symm_apply_apply f) a
#align is_free_group.lift_of IsFreeGroup.lift_of

/- warning: is_free_group.lift_symm_apply -> IsFreeGroup.lift_symm_apply is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] [_inst_2 : IsFreeGroup.{u1} G _inst_1] {H : Type.{u2}} [_inst_3 : Group.{u2} H] (f : MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) (a : IsFreeGroup.Generators.{u1} G _inst_1 _inst_2), Eq.{succ u2} H (coeFn.{max 1 (max (max (succ u2) (succ u1)) (succ u1) (succ u2)) (max (succ u1) (succ u2)) (succ u2) (succ u1), max (max (succ u2) (succ u1)) (succ u1) (succ u2)} (Equiv.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) ((IsFreeGroup.Generators.{u1} G _inst_1 _inst_2) -> H)) (fun (_x : Equiv.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) ((IsFreeGroup.Generators.{u1} G _inst_1 _inst_2) -> H)) => (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) -> (IsFreeGroup.Generators.{u1} G _inst_1 _inst_2) -> H) (Equiv.hasCoeToFun.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) ((IsFreeGroup.Generators.{u1} G _inst_1 _inst_2) -> H)) (Equiv.symm.{max (succ u1) (succ u2), max (succ u2) (succ u1)} ((IsFreeGroup.Generators.{u1} G _inst_1 _inst_2) -> H) (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) (IsFreeGroup.lift.{u1, u2} G _inst_1 _inst_2 H _inst_3)) f a) (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) (fun (_x : MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) => G -> H) (MonoidHom.hasCoeToFun.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) f (IsFreeGroup.of.{u1} G _inst_1 _inst_2 a))
but is expected to have type
  forall {G : Type.{u2}} [_inst_1 : Group.{u2} G] [_inst_2 : IsFreeGroup.{u2} G _inst_1] {H : Type.{u1}} [_inst_3 : Group.{u1} H] (f : MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) (a : IsFreeGroup.Generators.{u2} G _inst_1 _inst_2), Eq.{succ u1} H (FunLike.coe.{max (succ u1) (succ u2), max (succ u1) (succ u2), max (succ u1) (succ u2)} (Equiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) ((IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H)) (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) (fun (_x : MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) => (IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H) _x) (Equiv.instFunLikeEquiv.{max (succ u1) (succ u2), max (succ u1) (succ u2)} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) ((IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H)) (Equiv.symm.{max (succ u1) (succ u2), max (succ u1) (succ u2)} ((IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H) (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) (IsFreeGroup.lift.{u2, u1} G _inst_1 _inst_2 H _inst_3)) f a) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G (fun (_x : G) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G H (MulOneClass.toMul.{u2} G (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)))) (MulOneClass.toMul.{u1} H (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) (MonoidHomClass.toMulHomClass.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3))) (MonoidHom.monoidHomClass.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))))) f (IsFreeGroup.of.{u2} G _inst_1 _inst_2 a))
Case conversion may be inaccurate. Consider using '#align is_free_group.lift_symm_apply IsFreeGroup.lift_symm_applyₓ'. -/
@[simp]
theorem lift_symm_apply (f : G →* H) (a : Generators G) : (lift.symm f) a = f (of a) :=
  rfl
#align is_free_group.lift_symm_apply IsFreeGroup.lift_symm_apply

/- warning: is_free_group.ext_hom -> IsFreeGroup.ext_hom is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] [_inst_2 : IsFreeGroup.{u1} G _inst_1] {H : Type.{u2}} [_inst_3 : Group.{u2} H] {{f : MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))}} {{g : MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))}}, (forall (a : IsFreeGroup.Generators.{u1} G _inst_1 _inst_2), Eq.{succ u2} H (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) (fun (_x : MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) => G -> H) (MonoidHom.hasCoeToFun.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) f (IsFreeGroup.of.{u1} G _inst_1 _inst_2 a)) (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) (fun (_x : MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) => G -> H) (MonoidHom.hasCoeToFun.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) g (IsFreeGroup.of.{u1} G _inst_1 _inst_2 a))) -> (Eq.{max (succ u2) (succ u1)} (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) f g)
but is expected to have type
  forall {G : Type.{u2}} [_inst_1 : Group.{u2} G] [_inst_2 : IsFreeGroup.{u2} G _inst_1] {H : Type.{u1}} [_inst_3 : Group.{u1} H] {{f : MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))}} {{g : MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))}}, (forall (a : IsFreeGroup.Generators.{u2} G _inst_1 _inst_2), Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) (IsFreeGroup.of.{u2} G _inst_1 _inst_2 a)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G (fun (_x : G) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G H (MulOneClass.toMul.{u2} G (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)))) (MulOneClass.toMul.{u1} H (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) (MonoidHomClass.toMulHomClass.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3))) (MonoidHom.monoidHomClass.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))))) f (IsFreeGroup.of.{u2} G _inst_1 _inst_2 a)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G (fun (_x : G) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G H (MulOneClass.toMul.{u2} G (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)))) (MulOneClass.toMul.{u1} H (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) (MonoidHomClass.toMulHomClass.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3))) (MonoidHom.monoidHomClass.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))))) g (IsFreeGroup.of.{u2} G _inst_1 _inst_2 a))) -> (Eq.{max (succ u2) (succ u1)} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) f g)
Case conversion may be inaccurate. Consider using '#align is_free_group.ext_hom IsFreeGroup.ext_homₓ'. -/
@[ext]
theorem ext_hom ⦃f g : G →* H⦄ (h : ∀ a : Generators G, f (of a) = g (of a)) : f = g :=
  lift.symm.Injective (funext h)
#align is_free_group.ext_hom IsFreeGroup.ext_hom

/- warning: is_free_group.unique_lift -> IsFreeGroup.unique_lift is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] [_inst_2 : IsFreeGroup.{u1} G _inst_1] {H : Type.{u2}} [_inst_3 : Group.{u2} H] (f : (IsFreeGroup.Generators.{u1} G _inst_1 _inst_2) -> H), ExistsUnique.{max (succ u2) (succ u1)} (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) (fun (F : MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) => forall (a : IsFreeGroup.Generators.{u1} G _inst_1 _inst_2), Eq.{succ u2} H (coeFn.{max (succ u2) (succ u1), max (succ u1) (succ u2)} (MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) (fun (_x : MonoidHom.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) => G -> H) (MonoidHom.hasCoeToFun.{u1, u2} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1))) (Monoid.toMulOneClass.{u2} H (DivInvMonoid.toMonoid.{u2} H (Group.toDivInvMonoid.{u2} H _inst_3)))) F (IsFreeGroup.of.{u1} G _inst_1 _inst_2 a)) (f a))
but is expected to have type
  forall {G : Type.{u2}} [_inst_1 : Group.{u2} G] [_inst_2 : IsFreeGroup.{u2} G _inst_1] {H : Type.{u1}} [_inst_3 : Group.{u1} H] (f : (IsFreeGroup.Generators.{u2} G _inst_1 _inst_2) -> H), ExistsUnique.{max (succ u2) (succ u1)} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) (fun (F : MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) => forall (a : IsFreeGroup.Generators.{u2} G _inst_1 _inst_2), Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) (IsFreeGroup.of.{u2} G _inst_1 _inst_2 a)) (FunLike.coe.{max (succ u2) (succ u1), succ u2, succ u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G (fun (_x : G) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) _x) (MulHomClass.toFunLike.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G H (MulOneClass.toMul.{u2} G (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1)))) (MulOneClass.toMul.{u1} H (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) (MonoidHomClass.toMulHomClass.{max u2 u1, u2, u1} (MonoidHom.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))) G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3))) (MonoidHom.monoidHomClass.{u2, u1} G H (Monoid.toMulOneClass.{u2} G (DivInvMonoid.toMonoid.{u2} G (Group.toDivInvMonoid.{u2} G _inst_1))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_3)))))) F (IsFreeGroup.of.{u2} G _inst_1 _inst_2 a)) (f a))
Case conversion may be inaccurate. Consider using '#align is_free_group.unique_lift IsFreeGroup.unique_liftₓ'. -/
/-- The universal property of a free group: A functions from the generators of `G` to another
group extends in a unique way to a homomorphism from `G`.

Note that since `is_free_group.lift` is expressed as a bijection, it already
expresses the universal property.  -/
theorem unique_lift (f : Generators G → H) : ∃! F : G →* H, ∀ a, F (of a) = f a := by
  simpa only [Function.funext_iff] using lift.symm.bijective.exists_unique f
#align is_free_group.unique_lift IsFreeGroup.unique_lift

/- warning: is_free_group.of_lift -> IsFreeGroup.ofLift is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_4 : Group.{u1} G] (X : Type.{u1}) (of : X -> G) (lift : forall {H : Type.{u1}} [_inst_5 : Group.{u1} H], Equiv.{succ u1, succ u1} (X -> H) (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5))))), (forall {H : Type.{u1}} [_inst_6 : Group.{u1} H] (f : X -> H) (a : X), Eq.{succ u1} H (coeFn.{succ u1, succ u1} (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6)))) (fun (_x : MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6)))) => G -> H) (MonoidHom.hasCoeToFun.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6)))) (coeFn.{succ u1, succ u1} (Equiv.{succ u1, succ u1} (X -> H) (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6))))) (fun (_x : Equiv.{succ u1, succ u1} (X -> H) (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6))))) => (X -> H) -> (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6))))) (Equiv.hasCoeToFun.{succ u1, succ u1} (X -> H) (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6))))) (lift H _inst_6) f) (of a)) (f a)) -> (IsFreeGroup.{u1} G _inst_4)
but is expected to have type
  forall {G : Type.{u1}} [_inst_4 : Group.{u1} G] (X : Type.{u1}) (of : X -> G) (lift : forall {H : Type.{u1}} [_inst_5 : Group.{u1} H], Equiv.{succ u1, succ u1} (X -> H) (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5))))), (forall {H : Type.{u1}} [_inst_6 : Group.{u1} H] (f : X -> H) (a : X), Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) (of a)) (FunLike.coe.{succ u1, succ u1, succ u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : X -> H) => MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6)))) f) G (fun (_x : G) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) _x) (MulHomClass.toFunLike.{u1, u1, u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : X -> H) => MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6)))) f) G H (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4)))) (MulOneClass.toMul.{u1} H (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6)))) (MonoidHomClass.toMulHomClass.{u1, u1, u1} ((fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : X -> H) => MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6)))) f) G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6))) (MonoidHom.monoidHomClass.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6)))))) (FunLike.coe.{succ u1, succ u1, succ u1} (Equiv.{succ u1, succ u1} (X -> H) (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6))))) (X -> H) (fun (_x : X -> H) => (fun (x._@.Mathlib.Logic.Equiv.Defs._hyg.808 : X -> H) => MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6)))) _x) (Equiv.instFunLikeEquiv.{succ u1, succ u1} (X -> H) (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_6))))) (lift H _inst_6) f) (of a)) (f a)) -> (IsFreeGroup.{u1} G _inst_4)
Case conversion may be inaccurate. Consider using '#align is_free_group.of_lift IsFreeGroup.ofLiftₓ'. -/
/-- If a group satisfies the universal property of a free group, then it is a free group, where
the universal property is expressed as in `is_free_group.lift` and its properties. -/
def ofLift {G : Type u} [Group G] (X : Type u) (of : X → G)
    (lift : ∀ {H : Type u} [Group H], (X → H) ≃ (G →* H))
    (lift_of : ∀ {H : Type u} [Group H], ∀ (f : X → H) (a), lift f (of a) = f a) : IsFreeGroup G
    where
  Generators := X
  MulEquiv :=
    MonoidHom.toMulEquiv (FreeGroup.lift of) (lift FreeGroup.of)
      (by
        apply FreeGroup.ext_hom; intro x
        simp only [MonoidHom.coe_comp, Function.comp_apply, MonoidHom.id_apply, FreeGroup.lift.of,
          lift_of])
      (by
        let lift_symm_of : ∀ {H : Type u} [Group H], ∀ (f : G →* H) (a), lift.symm f a = f (of a) :=
          by intro H _ f a <;> simp [← lift_of (lift.symm f)]
        apply lift.symm.injective; ext x
        simp only [MonoidHom.coe_comp, Function.comp_apply, MonoidHom.id_apply, FreeGroup.lift.of,
          lift_of, lift_symm_of])
#align is_free_group.of_lift IsFreeGroup.ofLift

/- warning: is_free_group.of_unique_lift -> IsFreeGroup.ofUniqueLift is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_4 : Group.{u1} G] (X : Type.{u1}) (of : X -> G), (forall {H : Type.{u1}} [_inst_5 : Group.{u1} H] (f : X -> H), ExistsUnique.{succ u1} (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))) (fun (F : MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))) => forall (a : X), Eq.{succ u1} H (coeFn.{succ u1, succ u1} (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))) (fun (_x : MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))) => G -> H) (MonoidHom.hasCoeToFun.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))) F (of a)) (f a))) -> (IsFreeGroup.{u1} G _inst_4)
but is expected to have type
  forall {G : Type.{u1}} [_inst_4 : Group.{u1} G] (X : Type.{u1}) (of : X -> G), (forall {H : Type.{u1}} [_inst_5 : Group.{u1} H] (f : X -> H), ExistsUnique.{succ u1} (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))) (fun (F : MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))) => forall (a : X), Eq.{succ u1} ((fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) (of a)) (FunLike.coe.{succ u1, succ u1, succ u1} (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))) G (fun (_x : G) => (fun (x._@.Mathlib.Algebra.Hom.Group._hyg.2391 : G) => H) _x) (MulHomClass.toFunLike.{u1, u1, u1} (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))) G H (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4)))) (MulOneClass.toMul.{u1} H (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))) (MonoidHomClass.toMulHomClass.{u1, u1, u1} (MonoidHom.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))) G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5))) (MonoidHom.monoidHomClass.{u1, u1} G H (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_4))) (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_5)))))) F (of a)) (f a))) -> (IsFreeGroup.{u1} G _inst_4)
Case conversion may be inaccurate. Consider using '#align is_free_group.of_unique_lift IsFreeGroup.ofUniqueLiftₓ'. -/
/-- If a group satisfies the universal property of a free group, then it is a free group, where
the universal property is expressed as in `is_free_group.unique_lift`.  -/
noncomputable def ofUniqueLift {G : Type u} [Group G] (X : Type u) (of : X → G)
    (h : ∀ {H : Type u} [Group H] (f : X → H), ∃! F : G →* H, ∀ a, F (of a) = f a) :
    IsFreeGroup G :=
  let lift {H : Type u} [Group H] : (X → H) ≃ (G →* H) :=
    { toFun := fun f => Classical.choose (h f)
      invFun := fun F => F ∘ of
      left_inv := fun f => funext (Classical.choose_spec (h f)).left
      right_inv := fun F => ((Classical.choose_spec (h (F ∘ of))).right F fun _ => rfl).symm }
  let lift_of {H : Type u} [Group H] (f : X → H) (a : X) : lift f (of a) = f a :=
    congr_fun (lift.symm_apply_apply f) a
  ofLift X of @lift @lift_of
#align is_free_group.of_unique_lift IsFreeGroup.ofUniqueLift

/- warning: is_free_group.of_mul_equiv -> IsFreeGroup.ofMulEquiv is a dubious translation:
lean 3 declaration is
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] [_inst_2 : IsFreeGroup.{u1} G _inst_1] {H : Type.{u1}} [_inst_4 : Group.{u1} H], (MulEquiv.{u1, u1} G H (MulOneClass.toHasMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (MulOneClass.toHasMul.{u1} H (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_4))))) -> (IsFreeGroup.{u1} H _inst_4)
but is expected to have type
  forall {G : Type.{u1}} [_inst_1 : Group.{u1} G] [_inst_2 : IsFreeGroup.{u1} G _inst_1] {H : Type.{u1}} [_inst_4 : Group.{u1} H], (MulEquiv.{u1, u1} G H (MulOneClass.toMul.{u1} G (Monoid.toMulOneClass.{u1} G (DivInvMonoid.toMonoid.{u1} G (Group.toDivInvMonoid.{u1} G _inst_1)))) (MulOneClass.toMul.{u1} H (Monoid.toMulOneClass.{u1} H (DivInvMonoid.toMonoid.{u1} H (Group.toDivInvMonoid.{u1} H _inst_4))))) -> (IsFreeGroup.{u1} H _inst_4)
Case conversion may be inaccurate. Consider using '#align is_free_group.of_mul_equiv IsFreeGroup.ofMulEquivₓ'. -/
/-- Being a free group transports across group isomorphisms. -/
def ofMulEquiv {H : Type _} [Group H] (h : G ≃* H) : IsFreeGroup H
    where
  Generators := Generators G
  MulEquiv := (mulEquiv G).trans h
#align is_free_group.of_mul_equiv IsFreeGroup.ofMulEquiv

end IsFreeGroup