l3lab/ntp-mathlib huggingface dataset:
/-- The main theorem: the class group of an integral closure `S` of `R` in a
finite extension `L` of `K = Frac(R)` is finite if there is an admissible
absolute value.

See also `ClassGroup.fintypeOfAdmissibleOfAlgebraic` where `L` is an
algebraic extension of `R`, that includes some extra assumptions.
-/
noncomputable def fintypeOfAdmissibleOfFinite : Fintype (ClassGroup S) := by
  letI := Classical.decEq L
  letI := IsIntegralClosure.isFractionRing_of_finite_extension R K L S
  letI := IsIntegralClosure.isDedekindDomain R K L S
  choose s b hb_int using FiniteDimensional.exists_is_basis_integral R K L
-- Porting note: `this` and `f` below where solved at the end rather than being defined at first.
  have : LinearIndependent R ((Algebra.traceForm K L).dualBasis
      (traceForm_nondegenerate K L) b) := by
    refine' (Basis.linearIndependent _).restrict_scalars _
    simp only [Algebra.smul_def, mul_one]
    apply IsFractionRing.injective
  obtain ⟨n, b⟩ :=
    Submodule.basisOfPidOfLESpan this (IsIntegralClosure.range_le_span_dualBasis S b hb_int)
  let f : (S ⧸ LinearMap.ker (LinearMap.restrictScalars R (Algebra.linearMap S L))) ≃ₗ[R] S := by
    rw [LinearMap.ker_eq_bot.mpr]
    · 

Proof state:
R : Type u_1
S : Type u_2
K : Type u_3
L : Type u_4
inst✝¹⁶ : EuclideanDomain R
inst✝¹⁵ : CommRing S
inst✝¹⁴ : IsDomain S
inst✝¹³ : Field K
inst✝¹² : Field L
inst✝¹¹ : Algebra R K
inst✝¹⁰ : IsFractionRing R K
inst✝⁹ : Algebra K L
inst✝⁸ : FiniteDimensional K L
inst✝⁷ : IsSeparable K L
algRL : Algebra R L
inst✝⁶ : IsScalarTower R K L
inst✝⁵ : Algebra R S
inst✝⁴ : Algebra S L
ist : IsScalarTower R S L
iic : IsIntegralClosure S R L
abv : AbsoluteValue R ℤ
ι : Type u_5
inst✝³ : DecidableEq ι
inst✝² : Fintype ι
bS : Basis ι R S
adm : AbsoluteValue.IsAdmissible abv
inst✝¹ : Infinite R
inst✝ : DecidableEq R
this✝² : DecidableEq L := Classical.decEq L
this✝¹ : IsFractionRing S L := IsIntegralClosure.isFractionRing_of_finite_extension R K L S
this✝ : IsDedekindDomain S := IsIntegralClosure.isDedekindDomain R K L S
s : Finset L
b✝ : Basis { x // x ∈ s } K L
hb_int : ∀ (x : { x // x ∈ s }), IsIntegral R (b✝ x)
this :
  LinearIndependent R
    ⇑(BilinForm.dualBasis (Algebra.traceForm K L) (_ : BilinForm.Nondegenerate (Algebra.traceForm K L)) b✝)
n : ℕ
b : Basis (Fin n) R ↥(LinearMap.range (↑R (Algebra.linearMap S L)))
⊢ (S ⧸ ⊥) ≃ₗ[R] S

Next tactic:
exact Submodule.quotEquivOfEqBot _ rfl
/-- The main theorem: the class group of an integral closure `S` of `R` in a
finite extension `L` of `K = Frac(R)` is finite if there is an admissible
absolute value.

See also `ClassGroup.fintypeOfAdmissibleOfAlgebraic` where `L` is an
algebraic extension of `R`, that includes some extra assumptions.
-/
noncomputable def fintypeOfAdmissibleOfFinite : Fintype (ClassGroup S) := by
  letI := Classical.decEq L
  letI := IsIntegralClosure.isFractionRing_of_finite_extension R K L S
  letI := IsIntegralClosure.isDedekindDomain R K L S
  choose s b hb_int using FiniteDimensional.exists_is_basis_integral R K L
-- Porting note: `this` and `f` below where solved at the end rather than being defined at first.
  have : LinearIndependent R ((Algebra.traceForm K L).dualBasis
      (traceForm_nondegenerate K L) b) := by
    refine' (Basis.linearIndependent _).restrict_scalars _
    simp only [Algebra.smul_def, mul_one]
    apply IsFractionRing.injective
  obtain ⟨n, b⟩ :=
    Submodule.basisOfPidOfLESpan this (IsIntegralClosure.range_le_span_dualBasis S b hb_int)
  let f : (S ⧸ LinearMap.ker (LinearMap.restrictScalars R (Algebra.linearMap S L))) ≃ₗ[R] S := by
    rw [LinearMap.ker_eq_bot.mpr]
    · exact Submodule.quotEquivOfEqBot _ rfl
    · 

Proof state:
R : Type u_1
S : Type u_2
K : Type u_3
L : Type u_4
inst✝¹⁶ : EuclideanDomain R
inst✝¹⁵ : CommRing S
inst✝¹⁴ : IsDomain S
inst✝¹³ : Field K
inst✝¹² : Field L
inst✝¹¹ : Algebra R K
inst✝¹⁰ : IsFractionRing R K
inst✝⁹ : Algebra K L
inst✝⁸ : FiniteDimensional K L
inst✝⁷ : IsSeparable K L
algRL : Algebra R L
inst✝⁶ : IsScalarTower R K L
inst✝⁵ : Algebra R S
inst✝⁴ : Algebra S L
ist : IsScalarTower R S L
iic : IsIntegralClosure S R L
abv : AbsoluteValue R ℤ
ι : Type u_5
inst✝³ : DecidableEq ι
inst✝² : Fintype ι
bS : Basis ι R S
adm : AbsoluteValue.IsAdmissible abv
inst✝¹ : Infinite R
inst✝ : DecidableEq R
this✝² : DecidableEq L := Classical.decEq L
this✝¹ : IsFractionRing S L := IsIntegralClosure.isFractionRing_of_finite_extension R K L S
this✝ : IsDedekindDomain S := IsIntegralClosure.isDedekindDomain R K L S
s : Finset L
b✝ : Basis { x // x ∈ s } K L
hb_int : ∀ (x : { x // x ∈ s }), IsIntegral R (b✝ x)
this :
  LinearIndependent R
    ⇑(BilinForm.dualBasis (Algebra.traceForm K L) (_ : BilinForm.Nondegenerate (Algebra.traceForm K L)) b✝)
n : ℕ
b : Basis (Fin n) R ↥(LinearMap.range (↑R (Algebra.linearMap S L)))
⊢ Function.Injective ⇑(↑R (Algebra.linearMap S L))

Next tactic:
exact IsIntegralClosure.algebraMap_injective _ R _
/-- The main theorem: the class group of an integral closure `S` of `R` in a
finite extension `L` of `K = Frac(R)` is finite if there is an admissible
absolute value.

See also `ClassGroup.fintypeOfAdmissibleOfAlgebraic` where `L` is an
algebraic extension of `R`, that includes some extra assumptions.
-/
noncomputable def fintypeOfAdmissibleOfFinite : Fintype (ClassGroup S) := by
  letI := Classical.decEq L
  letI := IsIntegralClosure.isFractionRing_of_finite_extension R K L S
  letI := IsIntegralClosure.isDedekindDomain R K L S
  choose s b hb_int using FiniteDimensional.exists_is_basis_integral R K L
-- Porting note: `this` and `f` below where solved at the end rather than being defined at first.
  have : LinearIndependent R ((Algebra.traceForm K L).dualBasis
      (traceForm_nondegenerate K L) b) := by
    refine' (Basis.linearIndependent _).restrict_scalars _
    simp only [Algebra.smul_def, mul_one]
    apply IsFractionRing.injective
  obtain ⟨n, b⟩ :=
    Submodule.basisOfPidOfLESpan this (IsIntegralClosure.range_le_span_dualBasis S b hb_int)
  let f : (S ⧸ LinearMap.ker (LinearMap.restrictScalars R (Algebra.linearMap S L))) ≃ₗ[R] S := by
    rw [LinearMap.ker_eq_bot.mpr]
    · exact Submodule.quotEquivOfEqBot _ rfl
    · exact IsIntegralClosure.algebraMap_injective _ R _
  

Proof state:
case mk
R : Type u_1
S : Type u_2
K : Type u_3
L : Type u_4
inst✝¹⁶ : EuclideanDomain R
inst✝¹⁵ : CommRing S
inst✝¹⁴ : IsDomain S
inst✝¹³ : Field K
inst✝¹² : Field L
inst✝¹¹ : Algebra R K
inst✝¹⁰ : IsFractionRing R K
inst✝⁹ : Algebra K L
inst✝⁸ : FiniteDimensional K L
inst✝⁷ : IsSeparable K L
algRL : Algebra R L
inst✝⁶ : IsScalarTower R K L
inst✝⁵ : Algebra R S
inst✝⁴ : Algebra S L
ist : IsScalarTower R S L
iic : IsIntegralClosure S R L
abv : AbsoluteValue R ℤ
ι : Type u_5
inst✝³ : DecidableEq ι
inst✝² : Fintype ι
bS : Basis ι R S
adm : AbsoluteValue.IsAdmissible abv
inst✝¹ : Infinite R
inst✝ : DecidableEq R
this✝² : DecidableEq L := Classical.decEq L
this✝¹ : IsFractionRing S L := IsIntegralClosure.isFractionRing_of_finite_extension R K L S
this✝ : IsDedekindDomain S := IsIntegralClosure.isDedekindDomain R K L S
s : Finset L
b✝ : Basis { x // x ∈ s } K L
hb_int : ∀ (x : { x // x ∈ s }), IsIntegral R (b✝ x)
this :
  LinearIndependent R
    ⇑(BilinForm.dualBasis (Algebra.traceForm K L) (_ : BilinForm.Nondegenerate (Algebra.traceForm K L)) b✝)
n : ℕ
b : Basis (Fin n) R ↥(LinearMap.range (↑R (Algebra.linearMap S L)))
f : (S ⧸ LinearMap.ker (↑R (Algebra.linearMap S L))) ≃ₗ[R] S :=
  Eq.mpr (_ : ((S ⧸ LinearMap.ker (↑R (Algebra.linearMap S L))) ≃ₗ[R] S) = ((S ⧸ ⊥) ≃ₗ[R] S))
    (Submodule.quotEquivOfEqBot ⊥ (_ : ⊥ = ⊥))
⊢ Fintype (ClassGroup S)

Next tactic:
let bS := b.map ((LinearMap.quotKerEquivRange _).symm ≪≫ₗ f)
/-- The main theorem: the class group of an integral closure `S` of `R` in a
finite extension `L` of `K = Frac(R)` is finite if there is an admissible
absolute value.

See also `ClassGroup.fintypeOfAdmissibleOfAlgebraic` where `L` is an
algebraic extension of `R`, that includes some extra assumptions.
-/
noncomputable def fintypeOfAdmissibleOfFinite : Fintype (ClassGroup S) := by
  letI := Classical.decEq L
  letI := IsIntegralClosure.isFractionRing_of_finite_extension R K L S
  letI := IsIntegralClosure.isDedekindDomain R K L S
  choose s b hb_int using FiniteDimensional.exists_is_basis_integral R K L
-- Porting note: `this` and `f` below where solved at the end rather than being defined at first.
  have : LinearIndependent R ((Algebra.traceForm K L).dualBasis
      (traceForm_nondegenerate K L) b) := by
    refine' (Basis.linearIndependent _).restrict_scalars _
    simp only [Algebra.smul_def, mul_one]
    apply IsFractionRing.injective
  obtain ⟨n, b⟩ :=
    Submodule.basisOfPidOfLESpan this (IsIntegralClosure.range_le_span_dualBasis S b hb_int)
  let f : (S ⧸ LinearMap.ker (LinearMap.restrictScalars R (Algebra.linearMap S L))) ≃ₗ[R] S := by
    rw [LinearMap.ker_eq_bot.mpr]
    · exact Submodule.quotEquivOfEqBot _ rfl
    · exact IsIntegralClosure.algebraMap_injective _ R _
  let bS := b.map ((LinearMap.quotKerEquivRange _).symm ≪≫ₗ f)
  

Proof state:
case mk
R : Type u_1
S : Type u_2
K : Type u_3
L : Type u_4
inst✝¹⁶ : EuclideanDomain R
inst✝¹⁵ : CommRing S
inst✝¹⁴ : IsDomain S
inst✝¹³ : Field K
inst✝¹² : Field L
inst✝¹¹ : Algebra R K
inst✝¹⁰ : IsFractionRing R K
inst✝⁹ : Algebra K L
inst✝⁸ : FiniteDimensional K L
inst✝⁷ : IsSeparable K L
algRL : Algebra R L
inst✝⁶ : IsScalarTower R K L
inst✝⁵ : Algebra R S
inst✝⁴ : Algebra S L
ist : IsScalarTower R S L
iic : IsIntegralClosure S R L
abv : AbsoluteValue R ℤ
ι : Type u_5
inst✝³ : DecidableEq ι
inst✝² : Fintype ι
bS✝ : Basis ι R S
adm : AbsoluteValue.IsAdmissible abv
inst✝¹ : Infinite R
inst✝ : DecidableEq R
this✝² : DecidableEq L := Classical.decEq L
this✝¹ : IsFractionRing S L := IsIntegralClosure.isFractionRing_of_finite_extension R K L S
this✝ : IsDedekindDomain S := IsIntegralClosure.isDedekindDomain R K L S
s : Finset L
b✝ : Basis { x // x ∈ s } K L
hb_int : ∀ (x : { x // x ∈ s }), IsIntegral R (b✝ x)
this :
  LinearIndependent R
    ⇑(BilinForm.dualBasis (Algebra.traceForm K L) (_ : BilinForm.Nondegenerate (Algebra.traceForm K L)) b✝)
n : ℕ
b : Basis (Fin n) R ↥(LinearMap.range (↑R (Algebra.linearMap S L)))
f : (S ⧸ LinearMap.ker (↑R (Algebra.linearMap S L))) ≃ₗ[R] S :=
  Eq.mpr (_ : ((S ⧸ LinearMap.ker (↑R (Algebra.linearMap S L))) ≃ₗ[R] S) = ((S ⧸ ⊥) ≃ₗ[R] S))
    (Submodule.quotEquivOfEqBot ⊥ (_ : ⊥ = ⊥))
bS : Basis (Fin n) R S :=
  Basis.map b (LinearEquiv.symm (LinearMap.quotKerEquivRange (↑R (Algebra.linearMap S L))) ≪≫ₗ f)
⊢ Fintype (ClassGroup S)

Next tactic:
exact fintypeOfAdmissibleOfAlgebraic L bS adm (fun x =>
    (IsFractionRing.isAlgebraic_iff R K L).mpr (Algebra.IsAlgebraic.of_finite K _ x))
/-- A free group basis for `G` over `ι` is associated to a map `ι → G` recording the images of
the generators. -/
instance funLike : FunLike (FreeGroupBasis ι G) ι (fun _ ↦ G) where
  coe b := fun i ↦ b.repr.symm (FreeGroup.of i)
  coe_injective' := by
    

Proof state:
ι : Type u_1
ι' : Type u_2
G : Type u_3
H : Type u_4
inst✝¹ : Group G
inst✝ : Group H
⊢ Injective fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)

Next tactic:
rintro ⟨b⟩  ⟨b'⟩ hbb'
/-- A free group basis for `G` over `ι` is associated to a map `ι → G` recording the images of
the generators. -/
instance funLike : FunLike (FreeGroupBasis ι G) ι (fun _ ↦ G) where
  coe b := fun i ↦ b.repr.symm (FreeGroup.of i)
  coe_injective' := by
    rintro ⟨b⟩  ⟨b'⟩ hbb'
    

Proof state:
case ofRepr.ofRepr
ι : Type u_1
ι' : Type u_2
G : Type u_3
H : Type u_4
inst✝¹ : Group G
inst✝ : Group H
b b' : G ≃* FreeGroup ι
hbb' :
  (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b } =
    (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b' }
⊢ { repr := b } = { repr := b' }

Next tactic:
have H : (b.symm : FreeGroup ι →* G) = (b'.symm : FreeGroup ι →* G) := by
      ext i; exact congr_fun hbb' i
/-- A free group basis for `G` over `ι` is associated to a map `ι → G` recording the images of
the generators. -/
instance funLike : FunLike (FreeGroupBasis ι G) ι (fun _ ↦ G) where
  coe b := fun i ↦ b.repr.symm (FreeGroup.of i)
  coe_injective' := by
    rintro ⟨b⟩  ⟨b'⟩ hbb'
    have H : (b.symm : FreeGroup ι →* G) = (b'.symm : FreeGroup ι →* G) := by
      

Proof state:
ι : Type u_1
ι' : Type u_2
G : Type u_3
H : Type u_4
inst✝¹ : Group G
inst✝ : Group H
b b' : G ≃* FreeGroup ι
hbb' :
  (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b } =
    (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b' }
⊢ ↑(MulEquiv.symm b) = ↑(MulEquiv.symm b')

Next tactic:
ext i
/-- A free group basis for `G` over `ι` is associated to a map `ι → G` recording the images of
the generators. -/
instance funLike : FunLike (FreeGroupBasis ι G) ι (fun _ ↦ G) where
  coe b := fun i ↦ b.repr.symm (FreeGroup.of i)
  coe_injective' := by
    rintro ⟨b⟩  ⟨b'⟩ hbb'
    have H : (b.symm : FreeGroup ι →* G) = (b'.symm : FreeGroup ι →* G) := by
      ext i; 

Proof state:
case h
ι : Type u_1
ι' : Type u_2
G : Type u_3
H : Type u_4
inst✝¹ : Group G
inst✝ : Group H
b b' : G ≃* FreeGroup ι
hbb' :
  (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b } =
    (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b' }
i : ι
⊢ ↑(MulEquiv.symm b) (FreeGroup.of i) = ↑(MulEquiv.symm b') (FreeGroup.of i)

Next tactic:
exact congr_fun hbb' i
/-- A free group basis for `G` over `ι` is associated to a map `ι → G` recording the images of
the generators. -/
instance funLike : FunLike (FreeGroupBasis ι G) ι (fun _ ↦ G) where
  coe b := fun i ↦ b.repr.symm (FreeGroup.of i)
  coe_injective' := by
    rintro ⟨b⟩  ⟨b'⟩ hbb'
    have H : (b.symm : FreeGroup ι →* G) = (b'.symm : FreeGroup ι →* G) := by
      ext i; exact congr_fun hbb' i
    

Proof state:
case ofRepr.ofRepr
ι : Type u_1
ι' : Type u_2
G : Type u_3
H✝ : Type u_4
inst✝¹ : Group G
inst✝ : Group H✝
b b' : G ≃* FreeGroup ι
hbb' :
  (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b } =
    (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b' }
H : ↑(MulEquiv.symm b) = ↑(MulEquiv.symm b')
⊢ { repr := b } = { repr := b' }

Next tactic:
have : b.symm = b'.symm := by ext x; exact FunLike.congr_fun H x
/-- A free group basis for `G` over `ι` is associated to a map `ι → G` recording the images of
the generators. -/
instance funLike : FunLike (FreeGroupBasis ι G) ι (fun _ ↦ G) where
  coe b := fun i ↦ b.repr.symm (FreeGroup.of i)
  coe_injective' := by
    rintro ⟨b⟩  ⟨b'⟩ hbb'
    have H : (b.symm : FreeGroup ι →* G) = (b'.symm : FreeGroup ι →* G) := by
      ext i; exact congr_fun hbb' i
    have : b.symm = b'.symm := by 

Proof state:
ι : Type u_1
ι' : Type u_2
G : Type u_3
H✝ : Type u_4
inst✝¹ : Group G
inst✝ : Group H✝
b b' : G ≃* FreeGroup ι
hbb' :
  (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b } =
    (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b' }
H : ↑(MulEquiv.symm b) = ↑(MulEquiv.symm b')
⊢ MulEquiv.symm b = MulEquiv.symm b'

Next tactic:
ext x
/-- A free group basis for `G` over `ι` is associated to a map `ι → G` recording the images of
the generators. -/
instance funLike : FunLike (FreeGroupBasis ι G) ι (fun _ ↦ G) where
  coe b := fun i ↦ b.repr.symm (FreeGroup.of i)
  coe_injective' := by
    rintro ⟨b⟩  ⟨b'⟩ hbb'
    have H : (b.symm : FreeGroup ι →* G) = (b'.symm : FreeGroup ι →* G) := by
      ext i; exact congr_fun hbb' i
    have : b.symm = b'.symm := by ext x; 

Proof state:
case h
ι : Type u_1
ι' : Type u_2
G : Type u_3
H✝ : Type u_4
inst✝¹ : Group G
inst✝ : Group H✝
b b' : G ≃* FreeGroup ι
hbb' :
  (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b } =
    (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b' }
H : ↑(MulEquiv.symm b) = ↑(MulEquiv.symm b')
x : FreeGroup ι
⊢ (MulEquiv.symm b) x = (MulEquiv.symm b') x

Next tactic:
exact FunLike.congr_fun H x
/-- A free group basis for `G` over `ι` is associated to a map `ι → G` recording the images of
the generators. -/
instance funLike : FunLike (FreeGroupBasis ι G) ι (fun _ ↦ G) where
  coe b := fun i ↦ b.repr.symm (FreeGroup.of i)
  coe_injective' := by
    rintro ⟨b⟩  ⟨b'⟩ hbb'
    have H : (b.symm : FreeGroup ι →* G) = (b'.symm : FreeGroup ι →* G) := by
      ext i; exact congr_fun hbb' i
    have : b.symm = b'.symm := by ext x; exact FunLike.congr_fun H x
    

Proof state:
case ofRepr.ofRepr
ι : Type u_1
ι' : Type u_2
G : Type u_3
H✝ : Type u_4
inst✝¹ : Group G
inst✝ : Group H✝
b b' : G ≃* FreeGroup ι
hbb' :
  (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b } =
    (fun b i => (MulEquiv.symm b.repr) (FreeGroup.of i)) { repr := b' }
H : ↑(MulEquiv.symm b) = ↑(MulEquiv.symm b')
this : MulEquiv.symm b = MulEquiv.symm b'
⊢ { repr := b } = { repr := b' }

Next tactic:
rw [ofRepr.injEq, ← MulEquiv.symm_symm b, ← MulEquiv.symm_symm b', this]
@[simp] lemma repr_apply_coe (b : FreeGroupBasis ι G) (i : ι) : b.repr (b i) = FreeGroup.of i := by
  

Proof state:
ι : Type u_1
ι' : Type u_2
G : Type u_3
H : Type u_4
inst✝¹ : Group G
inst✝ : Group H
b : FreeGroupBasis ι G
i : ι
⊢ b.repr (b i) = FreeGroup.of i

Next tactic:
change b.repr (b.repr.symm (FreeGroup.of i)) = FreeGroup.of i
@[simp] lemma repr_apply_coe (b : FreeGroupBasis ι G) (i : ι) : b.repr (b i) = FreeGroup.of i := by
  change b.repr (b.repr.symm (FreeGroup.of i)) = FreeGroup.of i
  

Proof state:
ι : Type u_1
ι' : Type u_2
G : Type u_3
H : Type u_4
inst✝¹ : Group G
inst✝ : Group H
b : FreeGroupBasis ι G
i : ι
⊢ b.repr ((MulEquiv.symm b.repr) (FreeGroup.of i)) = FreeGroup.of i

Next tactic:
simp
/-- Given a free group basis of `G` over `ι`, there is a canonical bijection between maps from `ι`
to a group `H` and morphisms from `G` to `H`. -/
@[simps!]
def lift (b : FreeGroupBasis ι G) : (ι → H) ≃ (G →* H) :=
  FreeGroup.lift.trans
    { toFun := fun f => f.comp b.repr.toMonoidHom
      invFun := fun f => f.comp b.repr.symm.toMonoidHom
      left_inv := fun f => by
        

Proof state:
ι : Type u_1
ι' : Type u_2
G : Type u_3
H : Type u_4
inst✝¹ : Group G
inst✝ : Group H
b : FreeGroupBasis ι G
f : FreeGroup ι →* H
⊢ (fun f => MonoidHom.comp f (MulEquiv.toMonoidHom (MulEquiv.symm b.repr)))
      ((fun f => MonoidHom.comp f (MulEquiv.toMonoidHom b.repr)) f) =
    f

Next tactic:
ext
/-- Given a free group basis of `G` over `ι`, there is a canonical bijection between maps from `ι`
to a group `H` and morphisms from `G` to `H`. -/
@[simps!]
def lift (b : FreeGroupBasis ι G) : (ι → H) ≃ (G →* H) :=
  FreeGroup.lift.trans
    { toFun := fun f => f.comp b.repr.toMonoidHom
      invFun := fun f => f.comp b.repr.symm.toMonoidHom
      left_inv := fun f => by
        ext
        

Proof state:
case h
ι : Type u_1
ι' : Type u_2
G : Type u_3
H : Type u_4
inst✝¹ : Group G
inst✝ : Group H
b : FreeGroupBasis ι G
f : FreeGroup ι →* H
a✝ : ι
⊢ ((fun f => MonoidHom.comp f (MulEquiv.toMonoidHom (MulEquiv.symm b.repr)))
        ((fun f => MonoidHom.comp f (MulEquiv.toMonoidHom b.repr)) f))
      (FreeGroup.of a✝) =
    f (FreeGroup.of a✝)

Next tactic:
simp
/-- Given a free group basis of `G` over `ι`, there is a canonical bijection between maps from `ι`
to a group `H` and morphisms from `G` to `H`. -/
@[simps!]
def lift (b : FreeGroupBasis ι G) : (ι → H) ≃ (G →* H) :=
  FreeGroup.lift.trans
    { toFun := fun f => f.comp b.repr.toMonoidHom
      invFun := fun f => f.comp b.repr.symm.toMonoidHom
      left_inv := fun f => by
        ext
        simp
      right_inv := fun f => by
        

Proof state:
ι : Type u_1
ι' : Type u_2
G : Type u_3
H : Type u_4
inst✝¹ : Group G
inst✝ : Group H
b : FreeGroupBasis ι G
f : G →* H
⊢ (fun f => MonoidHom.comp f (MulEquiv.toMonoidHom b.repr))
      ((fun f => MonoidHom.comp f (MulEquiv.toMonoidHom (MulEquiv.symm b.repr))) f) =
    f

Next tactic:
ext
/-- Given a free group basis of `G` over `ι`, there is a canonical bijection between maps from `ι`
to a group `H` and morphisms from `G` to `H`. -/
@[simps!]
def lift (b : FreeGroupBasis ι G) : (ι → H) ≃ (G →* H) :=
  FreeGroup.lift.trans
    { toFun := fun f => f.comp b.repr.toMonoidHom
      invFun := fun f => f.comp b.repr.symm.toMonoidHom
      left_inv := fun f => by
        ext
        simp
      right_inv := fun f => by
        ext
        

Proof state:
case h
ι : Type u_1
ι' : Type u_2
G : Type u_3
H : Type u_4
inst✝¹ : Group G
inst✝ : Group H
b : FreeGroupBasis ι G
f : G →* H
x✝ : G
⊢ ((fun f => MonoidHom.comp f (MulEquiv.toMonoidHom b.repr))
        ((fun f => MonoidHom.comp f (MulEquiv.toMonoidHom (MulEquiv.symm b.repr))) f))
      x✝ =
    f x✝

Next tactic:
simp
/-- If a group satisfies the universal property of a free group with respect to a given type, then
it admits a free group basis based on this type. Here, the universal property is expressed as
in `IsFreeGroup.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) :
    FreeGroupBasis X G where
  repr := MulEquiv.symm <| MonoidHom.toMulEquiv (FreeGroup.lift of) (lift FreeGroup.of)
      (by
        

Proof state:
ι : Type u_1
ι' : Type u_2
G✝ : Type u_3
H : Type u_4
inst✝² : Group G✝
inst✝¹ : Group H
G : Type u
inst✝ : Group G
X : Type u
of : X → G
lift : {H : Type u} → [inst : Group H] → (X → H) ≃ (G →* H)
lift_of : ∀ {H : Type u} [inst : Group H] (f : X → H) (a : X), (lift f) (of a) = f a
⊢ MonoidHom.comp (lift FreeGroup.of) (FreeGroup.lift of) = MonoidHom.id (FreeGroup X)

Next tactic:
apply FreeGroup.ext_hom
/-- If a group satisfies the universal property of a free group with respect to a given type, then
it admits a free group basis based on this type. Here, the universal property is expressed as
in `IsFreeGroup.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) :
    FreeGroupBasis X G where
  repr := MulEquiv.symm <| MonoidHom.toMulEquiv (FreeGroup.lift of) (lift FreeGroup.of)
      (by
        apply FreeGroup.ext_hom; 

Proof state:
case h
ι : Type u_1
ι' : Type u_2
G✝ : Type u_3
H : Type u_4
inst✝² : Group G✝
inst✝¹ : Group H
G : Type u
inst✝ : Group G
X : Type u
of : X → G
lift : {H : Type u} → [inst : Group H] → (X → H) ≃ (G →* H)
lift_of : ∀ {H : Type u} [inst : Group H] (f : X → H) (a : X), (lift f) (of a) = f a
⊢ ∀ (a : X),
    (MonoidHom.comp (lift FreeGroup.of) (FreeGroup.lift of)) (FreeGroup.of a) =
      (MonoidHom.id (FreeGroup X)) (FreeGroup.of a)

Next tactic:
intro x
/-- If a group satisfies the universal property of a free group with respect to a given type, then
it admits a free group basis based on this type. Here, the universal property is expressed as
in `IsFreeGroup.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) :
    FreeGroupBasis X G where
  repr := MulEquiv.symm <| MonoidHom.toMulEquiv (FreeGroup.lift of) (lift FreeGroup.of)
      (by
        apply FreeGroup.ext_hom; intro x
        

Proof state:
case h
ι : Type u_1
ι' : Type u_2
G✝ : Type u_3
H : Type u_4
inst✝² : Group G✝
inst✝¹ : Group H
G : Type u
inst✝ : Group G
X : Type u
of : X → G
lift : {H : Type u} → [inst : Group H] → (X → H) ≃ (G →* H)
lift_of : ∀ {H : Type u} [inst : Group H] (f : X → H) (a : X), (lift f) (of a) = f a
x : X
⊢ (MonoidHom.comp (lift FreeGroup.of) (FreeGroup.lift of)) (FreeGroup.of x) =
    (MonoidHom.id (FreeGroup X)) (FreeGroup.of x)

Next tactic:
simp only [MonoidHom.coe_comp, Function.comp_apply, MonoidHom.id_apply, FreeGroup.lift.of,
          lift_of]