l3lab/ntp-mathlib huggingface dataset:
@[to_additive]
theorem length (h : Red L₁ L₂) : ∃ n, L₁.length = L₂.length + 2 * n := by
  induction' h with L₂ L₃ _h₁₂ h₂₃ ih
  · 

Proof state:
case refl
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
⊢ ∃ n, List.length L₁ = List.length L₁ + 2 * n

Next tactic:
exact ⟨0, rfl⟩
@[to_additive]
theorem length (h : Red L₁ L₂) : ∃ n, L₁.length = L₂.length + 2 * n := by
  induction' h with L₂ L₃ _h₁₂ h₂₃ ih
  · exact ⟨0, rfl⟩
  · 

Proof state:
case tail
α : Type u
L L₁ L₂✝ L₃✝ L₄ L₂ L₃ : List (α × Bool)
_h₁₂ : ReflTransGen Step L₁ L₂
h₂₃ : Step L₂ L₃
ih : ∃ n, List.length L₁ = List.length L₂ + 2 * n
⊢ ∃ n, List.length L₁ = List.length L₃ + 2 * n

Next tactic:
rcases ih with ⟨n, eq⟩
@[to_additive]
theorem length (h : Red L₁ L₂) : ∃ n, L₁.length = L₂.length + 2 * n := by
  induction' h with L₂ L₃ _h₁₂ h₂₃ ih
  · exact ⟨0, rfl⟩
  · rcases ih with ⟨n, eq⟩
    

Proof state:
case tail.intro
α : Type u
L L₁ L₂✝ L₃✝ L₄ L₂ L₃ : List (α × Bool)
_h₁₂ : ReflTransGen Step L₁ L₂
h₂₃ : Step L₂ L₃
n : ℕ
eq : List.length L₁ = List.length L₂ + 2 * n
⊢ ∃ n, List.length L₁ = List.length L₃ + 2 * n

Next tactic:
exists 1 + n
@[to_additive]
theorem length (h : Red L₁ L₂) : ∃ n, L₁.length = L₂.length + 2 * n := by
  induction' h with L₂ L₃ _h₁₂ h₂₃ ih
  · exact ⟨0, rfl⟩
  · rcases ih with ⟨n, eq⟩
    exists 1 + n
    

Proof state:
case tail.intro
α : Type u
L L₁ L₂✝ L₃✝ L₄ L₂ L₃ : List (α × Bool)
_h₁₂ : ReflTransGen Step L₁ L₂
h₂₃ : Step L₂ L₃
n : ℕ
eq : List.length L₁ = List.length L₂ + 2 * n
⊢ List.length L₁ = List.length L₃ + 2 * (1 + n)

Next tactic:
simp [mul_add, eq, (Step.length h₂₃).symm, add_assoc]
@[to_additive FreeAddGroup.equivalence_join_red]
theorem equivalence_join_red : Equivalence (Join (@Red α)) :=
  equivalence_join_reflTransGen fun a b c hab hac =>
    match b, c, Red.Step.diamond hab hac rfl with
    | b, _, Or.inl rfl => ⟨b, by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ a b✝ c b : List (α × Bool)
hab hac : Red.Step a b
⊢ ReflGen Red.Step b b

Next tactic:
rfl
@[to_additive FreeAddGroup.equivalence_join_red]
theorem equivalence_join_red : Equivalence (Join (@Red α)) :=
  equivalence_join_reflTransGen fun a b c hab hac =>
    match b, c, Red.Step.diamond hab hac rfl with
    | b, _, Or.inl rfl => ⟨b, by rfl, by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ a b✝ c b : List (α × Bool)
hab hac : Red.Step a b
⊢ ReflTransGen Red.Step b b

Next tactic:
rfl
@[to_additive]
instance [IsEmpty α] : Unique (FreeGroup α) := by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
inst✝ : IsEmpty α
⊢ Unique (FreeGroup α)

Next tactic:
unfold FreeGroup
@[to_additive]
instance [IsEmpty α] : Unique (FreeGroup α) := by unfold FreeGroup; 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
inst✝ : IsEmpty α
⊢ Unique (Quot Red.Step)

Next tactic:
infer_instance
@[to_additive (attr := simp)]
theorem invRev_length : (invRev L₁).length = L₁.length := by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
⊢ List.length (invRev L₁) = List.length L₁

Next tactic:
simp [invRev]
@[to_additive (attr := simp)]
theorem invRev_invRev : invRev (invRev L₁) = L₁ :=
  by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
⊢ invRev (invRev L₁) = L₁

Next tactic:
simp [invRev, List.map_reverse, (· ∘ ·)]
@[to_additive]
instance : Inv (FreeGroup α) :=
  ⟨Quot.map invRev
      (by
        

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
⊢ (Red.Step ⇒ Red.Step) invRev invRev

Next tactic:
intro a b h
@[to_additive]
instance : Inv (FreeGroup α) :=
  ⟨Quot.map invRev
      (by
        intro a b h
        

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ a b : List (α × Bool)
h : Red.Step a b
⊢ Red.Step (invRev a) (invRev b)

Next tactic:
cases h
@[to_additive]
instance : Inv (FreeGroup α) :=
  ⟨Quot.map invRev
      (by
        intro a b h
        cases h
        

Proof state:
case not
α : Type u
L L₁ L₂ L₃ L₄ L₁✝ L₂✝ : List (α × Bool)
x✝ : α
b✝ : Bool
⊢ Red.Step (invRev (L₁✝ ++ (x✝, b✝) :: (x✝, !b✝) :: L₂✝)) (invRev (L₁✝ ++ L₂✝))

Next tactic:
simp [invRev]
@[to_additive]
theorem Red.Step.invRev {L₁ L₂ : List (α × Bool)} (h : Red.Step L₁ L₂) :
    Red.Step (FreeGroup.invRev L₁) (FreeGroup.invRev L₂) := by
  

Proof state:
α : Type u
L L₁✝ L₂✝ L₃ L₄ L₁ L₂ : List (α × Bool)
h : Step L₁ L₂
⊢ Step (FreeGroup.invRev L₁) (FreeGroup.invRev L₂)

Next tactic:
cases' h with a b x y
@[to_additive]
theorem Red.Step.invRev {L₁ L₂ : List (α × Bool)} (h : Red.Step L₁ L₂) :
    Red.Step (FreeGroup.invRev L₁) (FreeGroup.invRev L₂) := by
  cases' h with a b x y
  

Proof state:
case not
α : Type u
L L₁ L₂ L₃ L₄ a b : List (α × Bool)
x : α
y : Bool
⊢ Step (FreeGroup.invRev (a ++ (x, y) :: (x, !y) :: b)) (FreeGroup.invRev (a ++ b))

Next tactic:
simp [FreeGroup.invRev]
@[to_additive (attr := simp)]
theorem Red.step_invRev_iff :
    Red.Step (FreeGroup.invRev L₁) (FreeGroup.invRev L₂) ↔ Red.Step L₁ L₂ :=
  ⟨fun h => by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
h : Step (FreeGroup.invRev L₁) (FreeGroup.invRev L₂)
⊢ Step L₁ L₂

Next tactic:
simpa only [invRev_invRev] using h.invRev
@[to_additive (attr := simp)]
theorem red_invRev_iff : Red (invRev L₁) (invRev L₂) ↔ Red L₁ L₂ :=
  ⟨fun h => by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
h : Red (invRev L₁) (invRev L₂)
⊢ Red L₁ L₂

Next tactic:
simpa only [invRev_invRev] using h.invRev
@[to_additive]
instance : Group (FreeGroup α) where
  mul := (· * ·)
  one := 1
  inv := Inv.inv
  mul_assoc := by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
⊢ ∀ (a b c : FreeGroup α), a * b * c = a * (b * c)

Next tactic:
rintro ⟨L₁⟩ ⟨L₂⟩ ⟨L₃⟩
@[to_additive]
instance : Group (FreeGroup α) where
  mul := (· * ·)
  one := 1
  inv := Inv.inv
  mul_assoc := by rintro ⟨L₁⟩ ⟨L₂⟩ ⟨L₃⟩; 

Proof state:
case mk.mk.mk
α : Type u
L L₁✝ L₂✝ L₃✝ L₄ : List (α × Bool)
a✝ : FreeGroup α
L₁ : List (α × Bool)
b✝ : FreeGroup α
L₂ : List (α × Bool)
c✝ : FreeGroup α
L₃ : List (α × Bool)
⊢ Quot.mk Red.Step L₁ * Quot.mk Red.Step L₂ * Quot.mk Red.Step L₃ =
    Quot.mk Red.Step L₁ * (Quot.mk Red.Step L₂ * Quot.mk Red.Step L₃)

Next tactic:
simp
@[to_additive]
instance : Group (FreeGroup α) where
  mul := (· * ·)
  one := 1
  inv := Inv.inv
  mul_assoc := by rintro ⟨L₁⟩ ⟨L₂⟩ ⟨L₃⟩; simp
  one_mul := by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
⊢ ∀ (a : FreeGroup α), 1 * a = a

Next tactic:
rintro ⟨L⟩
@[to_additive]
instance : Group (FreeGroup α) where
  mul := (· * ·)
  one := 1
  inv := Inv.inv
  mul_assoc := by rintro ⟨L₁⟩ ⟨L₂⟩ ⟨L₃⟩; simp
  one_mul := by rintro ⟨L⟩; 

Proof state:
case mk
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
a✝ : FreeGroup α
L : List (α × Bool)
⊢ 1 * Quot.mk Red.Step L = Quot.mk Red.Step L

Next tactic:
rfl
@[to_additive]
instance : Group (FreeGroup α) where
  mul := (· * ·)
  one := 1
  inv := Inv.inv
  mul_assoc := by rintro ⟨L₁⟩ ⟨L₂⟩ ⟨L₃⟩; simp
  one_mul := by rintro ⟨L⟩; rfl
  mul_one := by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
⊢ ∀ (a : FreeGroup α), a * 1 = a

Next tactic:
rintro ⟨L⟩
@[to_additive]
instance : Group (FreeGroup α) where
  mul := (· * ·)
  one := 1
  inv := Inv.inv
  mul_assoc := by rintro ⟨L₁⟩ ⟨L₂⟩ ⟨L₃⟩; simp
  one_mul := by rintro ⟨L⟩; rfl
  mul_one := by rintro ⟨L⟩; 

Proof state:
case mk
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
a✝ : FreeGroup α
L : List (α × Bool)
⊢ Quot.mk Red.Step L * 1 = Quot.mk Red.Step L

Next tactic:
simp [one_eq_mk]
@[to_additive]
instance : Group (FreeGroup α) where
  mul := (· * ·)
  one := 1
  inv := Inv.inv
  mul_assoc := by rintro ⟨L₁⟩ ⟨L₂⟩ ⟨L₃⟩; simp
  one_mul := by rintro ⟨L⟩; rfl
  mul_one := by rintro ⟨L⟩; simp [one_eq_mk]
  mul_left_inv := by
    

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
⊢ ∀ (a : FreeGroup α), a⁻¹ * a = 1

Next tactic:
rintro ⟨L⟩
@[to_additive]
instance : Group (FreeGroup α) where
  mul := (· * ·)
  one := 1
  inv := Inv.inv
  mul_assoc := by rintro ⟨L₁⟩ ⟨L₂⟩ ⟨L₃⟩; simp
  one_mul := by rintro ⟨L⟩; rfl
  mul_one := by rintro ⟨L⟩; simp [one_eq_mk]
  mul_left_inv := by
    rintro ⟨L⟩
    

Proof state:
case mk
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
a✝ : FreeGroup α
L : List (α × Bool)
⊢ (Quot.mk Red.Step L)⁻¹ * Quot.mk Red.Step L = 1

Next tactic:
exact
      List.recOn L rfl fun ⟨x, b⟩ tl ih =>
          Eq.trans (Quot.sound <| by simp [invRev, one_eq_mk]) ih
@[to_additive]
instance : Group (FreeGroup α) where
  mul := (· * ·)
  one := 1
  inv := Inv.inv
  mul_assoc := by rintro ⟨L₁⟩ ⟨L₂⟩ ⟨L₃⟩; simp
  one_mul := by rintro ⟨L⟩; rfl
  mul_one := by rintro ⟨L⟩; simp [one_eq_mk]
  mul_left_inv := by
    rintro ⟨L⟩
    exact
      List.recOn L rfl fun ⟨x, b⟩ tl ih =>
          Eq.trans (Quot.sound <| by 

Proof state:
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
a✝ : FreeGroup α
L : List (α × Bool)
x✝ : α × Bool
tl : List (α × Bool)
ih : (Quot.mk Red.Step tl)⁻¹ * Quot.mk Red.Step tl = 1
x : α
b : Bool
⊢ Red.Step (invRev ((x, b) :: tl) ++ (x, b) :: tl) (invRev tl ++ tl)

Next tactic:
simp [invRev, one_eq_mk]
/-- The canonical map from the type to the free group is an injection. -/
@[to_additive "The canonical map from the type to the additive free group is an injection."]
theorem of_injective : Function.Injective (@of α) := fun _ _ H => by
  

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
x✝¹ x✝ : α
H : of x✝¹ = of x✝
⊢ x✝¹ = x✝

Next tactic:
let ⟨L₁, hx, hy⟩ := Red.exact.1 H
/-- The canonical map from the type to the free group is an injection. -/
@[to_additive "The canonical map from the type to the additive free group is an injection."]
theorem of_injective : Function.Injective (@of α) := fun _ _ H => by
  let ⟨L₁, hx, hy⟩ := Red.exact.1 H
  

Proof state:
α : Type u
L L₁✝ L₂ L₃ L₄ : List (α × Bool)
x✝¹ x✝ : α
H : of x✝¹ = of x✝
L₁ : List (α × Bool)
hx : Red [(x✝¹, true)] L₁
hy : Red [(x✝, true)] L₁
⊢ x✝¹ = x✝

Next tactic:
simp [Red.singleton_iff] at hx hy
/-- The canonical map from the type to the free group is an injection. -/
@[to_additive "The canonical map from the type to the additive free group is an injection."]
theorem of_injective : Function.Injective (@of α) := fun _ _ H => by
  let ⟨L₁, hx, hy⟩ := Red.exact.1 H
  simp [Red.singleton_iff] at hx hy; 

Proof state:
α : Type u
L L₁✝ L₂ L₃ L₄ : List (α × Bool)
x✝¹ x✝ : α
H : of x✝¹ = of x✝
L₁ : List (α × Bool)
hx : L₁ = [(x✝¹, true)]
hy : L₁ = [(x✝, true)]
⊢ x✝¹ = x✝

Next tactic:
aesop
@[to_additive]
theorem Red.Step.lift {f : α → β} (H : Red.Step L₁ L₂) : Lift.aux f L₁ = Lift.aux f L₂ := by
  

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f✝ : α → β
x y : FreeGroup α
f : α → β
H : Step L₁ L₂
⊢ Lift.aux f L₁ = Lift.aux f L₂

Next tactic:
cases' H with _ _ _ b
@[to_additive]
theorem Red.Step.lift {f : α → β} (H : Red.Step L₁ L₂) : Lift.aux f L₁ = Lift.aux f L₂ := by
  cases' H with _ _ _ b; 

Proof state:
case not
α : Type u
L L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f✝ : α → β
x y : FreeGroup α
f : α → β
L₁✝ L₂✝ : List (α × Bool)
x✝ : α
b : Bool
⊢ Lift.aux f (L₁✝ ++ (x✝, b) :: (x✝, !b) :: L₂✝) = Lift.aux f (L₁✝ ++ L₂✝)

Next tactic:
cases b
@[to_additive]
theorem Red.Step.lift {f : α → β} (H : Red.Step L₁ L₂) : Lift.aux f L₁ = Lift.aux f L₂ := by
  cases' H with _ _ _ b; cases b <;> 

Proof state:
case not.false
α : Type u
L L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f✝ : α → β
x y : FreeGroup α
f : α → β
L₁✝ L₂✝ : List (α × Bool)
x✝ : α
⊢ Lift.aux f (L₁✝ ++ (x✝, false) :: (x✝, !false) :: L₂✝) = Lift.aux f (L₁✝ ++ L₂✝)

Next tactic:
simp [Lift.aux]
@[to_additive]
theorem Red.Step.lift {f : α → β} (H : Red.Step L₁ L₂) : Lift.aux f L₁ = Lift.aux f L₂ := by
  cases' H with _ _ _ b; cases b <;> 

Proof state:
case not.true
α : Type u
L L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f✝ : α → β
x y : FreeGroup α
f : α → β
L₁✝ L₂✝ : List (α × Bool)
x✝ : α
⊢ Lift.aux f (L₁✝ ++ (x✝, true) :: (x✝, !true) :: L₂✝) = Lift.aux f (L₁✝ ++ L₂✝)

Next tactic:
simp [Lift.aux]
/-- If `β` is a group, then any function from `α` to `β` extends uniquely to a group homomorphism
from the free group over `α` to `β` -/
@[to_additive (attr := simps symm_apply)
  "If `β` is an additive group, then any function from `α` to `β` extends uniquely to an
  additive group homomorphism from the free additive group over `α` to `β`"]
def lift : (α → β) ≃ (FreeGroup α →* β) where
  toFun f :=
    MonoidHom.mk' (Quot.lift (Lift.aux f) fun L₁ L₂ => Red.Step.lift) <| by
      

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f✝ : α → β
x y : FreeGroup α
f : α → β
⊢ ∀ (a b : FreeGroup α),
    Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) (a * b) =
      Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) a *
        Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) b

Next tactic:
rintro ⟨L₁⟩ ⟨L₂⟩
/-- If `β` is a group, then any function from `α` to `β` extends uniquely to a group homomorphism
from the free group over `α` to `β` -/
@[to_additive (attr := simps symm_apply)
  "If `β` is an additive group, then any function from `α` to `β` extends uniquely to an
  additive group homomorphism from the free additive group over `α` to `β`"]
def lift : (α → β) ≃ (FreeGroup α →* β) where
  toFun f :=
    MonoidHom.mk' (Quot.lift (Lift.aux f) fun L₁ L₂ => Red.Step.lift) <| by
      rintro ⟨L₁⟩ ⟨L₂⟩; 

Proof state:
case mk.mk
α : Type u
L L₁✝ L₂✝ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f✝ : α → β
x y : FreeGroup α
f : α → β
a✝ : FreeGroup α
L₁ : List (α × Bool)
b✝ : FreeGroup α
L₂ : List (α × Bool)
⊢ Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂)
      (Quot.mk Red.Step L₁ * Quot.mk Red.Step L₂) =
    Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂)
        (Quot.mk Red.Step L₁) *
      Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂)
        (Quot.mk Red.Step L₂)

Next tactic:
simp [Lift.aux]
/-- If `β` is a group, then any function from `α` to `β` extends uniquely to a group homomorphism
from the free group over `α` to `β` -/
@[to_additive (attr := simps symm_apply)
  "If `β` is an additive group, then any function from `α` to `β` extends uniquely to an
  additive group homomorphism from the free additive group over `α` to `β`"]
def lift : (α → β) ≃ (FreeGroup α →* β) where
  toFun f :=
    MonoidHom.mk' (Quot.lift (Lift.aux f) fun L₁ L₂ => Red.Step.lift) <| by
      rintro ⟨L₁⟩ ⟨L₂⟩; simp [Lift.aux]
  invFun g := g ∘ of
  left_inv f := one_mul _
  right_inv g :=
    MonoidHom.ext <| by
      

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x y : FreeGroup α
g : FreeGroup α →* β
⊢ ∀ (x : FreeGroup α),
    ((fun f =>
            MonoidHom.mk'
              (Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂))
              (_ :
                ∀ (a b : FreeGroup α),
                  Quot.lift (Lift.aux f)
                      (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) (a * b) =
                    Quot.lift (Lift.aux f)
                        (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) a *
                      Quot.lift (Lift.aux f)
                        (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) b))
          ((fun g => ⇑g ∘ of) g))
        x =
      g x

Next tactic:
rintro ⟨L⟩
/-- If `β` is a group, then any function from `α` to `β` extends uniquely to a group homomorphism
from the free group over `α` to `β` -/
@[to_additive (attr := simps symm_apply)
  "If `β` is an additive group, then any function from `α` to `β` extends uniquely to an
  additive group homomorphism from the free additive group over `α` to `β`"]
def lift : (α → β) ≃ (FreeGroup α →* β) where
  toFun f :=
    MonoidHom.mk' (Quot.lift (Lift.aux f) fun L₁ L₂ => Red.Step.lift) <| by
      rintro ⟨L₁⟩ ⟨L₂⟩; simp [Lift.aux]
  invFun g := g ∘ of
  left_inv f := one_mul _
  right_inv g :=
    MonoidHom.ext <| by
      rintro ⟨L⟩
      

Proof state:
case mk
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x y : FreeGroup α
g : FreeGroup α →* β
x✝ : FreeGroup α
L : List (α × Bool)
⊢ ((fun f =>
          MonoidHom.mk'
            (Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂))
            (_ :
              ∀ (a b : FreeGroup α),
                Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂)
                    (a * b) =
                  Quot.lift (Lift.aux f)
                      (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) a *
                    Quot.lift (Lift.aux f)
                      (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) b))
        ((fun g => ⇑g ∘ of) g))
      (Quot.mk Red.Step L) =
    g (Quot.mk Red.Step L)

Next tactic:
exact List.recOn L
        (g.map_one.symm)
        (by
        rintro ⟨x, _ | _⟩ t (ih : _ = g (mk t))
        · show _ = g ((of x)⁻¹ * mk t)
          simpa [Lift.aux] using ih
        · show _ = g (of x * mk t)
          simpa [Lift.aux] using ih)
/-- If `β` is a group, then any function from `α` to `β` extends uniquely to a group homomorphism
from the free group over `α` to `β` -/
@[to_additive (attr := simps symm_apply)
  "If `β` is an additive group, then any function from `α` to `β` extends uniquely to an
  additive group homomorphism from the free additive group over `α` to `β`"]
def lift : (α → β) ≃ (FreeGroup α →* β) where
  toFun f :=
    MonoidHom.mk' (Quot.lift (Lift.aux f) fun L₁ L₂ => Red.Step.lift) <| by
      rintro ⟨L₁⟩ ⟨L₂⟩; simp [Lift.aux]
  invFun g := g ∘ of
  left_inv f := one_mul _
  right_inv g :=
    MonoidHom.ext <| by
      rintro ⟨L⟩
      exact List.recOn L
        (g.map_one.symm)
        (by
        

Proof state:
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x y : FreeGroup α
g : FreeGroup α →* β
x✝ : FreeGroup α
L : List (α × Bool)
⊢ ∀ (head : α × Bool) (tail : List (α × Bool)),
    ((fun f =>
              MonoidHom.mk'
                (Quot.lift (Lift.aux f)
                  (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂))
                (_ :
                  ∀ (a b : FreeGroup α),
                    Quot.lift (Lift.aux f)
                        (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) (a * b) =
                      Quot.lift (Lift.aux f)
                          (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) a *
                        Quot.lift (Lift.aux f)
                          (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) b))
            ((fun g => ⇑g ∘ of) g))
          (Quot.mk Red.Step tail) =
        g (Quot.mk Red.Step tail) →
      ((fun f =>
              MonoidHom.mk'
                (Quot.lift (Lift.aux f)
                  (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂))
                (_ :
                  ∀ (a b : FreeGroup α),
                    Quot.lift (Lift.aux f)
                        (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) (a * b) =
                      Quot.lift (Lift.aux f)
                          (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) a *
                        Quot.lift (Lift.aux f)
                          (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) b))
            ((fun g => ⇑g ∘ of) g))
          (Quot.mk Red.Step (head :: tail)) =
        g (Quot.mk Red.Step (head :: tail))

Next tactic:
rintro ⟨x, _ | _⟩ t (ih : _ = g (mk t))
/-- If `β` is a group, then any function from `α` to `β` extends uniquely to a group homomorphism
from the free group over `α` to `β` -/
@[to_additive (attr := simps symm_apply)
  "If `β` is an additive group, then any function from `α` to `β` extends uniquely to an
  additive group homomorphism from the free additive group over `α` to `β`"]
def lift : (α → β) ≃ (FreeGroup α →* β) where
  toFun f :=
    MonoidHom.mk' (Quot.lift (Lift.aux f) fun L₁ L₂ => Red.Step.lift) <| by
      rintro ⟨L₁⟩ ⟨L₂⟩; simp [Lift.aux]
  invFun g := g ∘ of
  left_inv f := one_mul _
  right_inv g :=
    MonoidHom.ext <| by
      rintro ⟨L⟩
      exact List.recOn L
        (g.map_one.symm)
        (by
        rintro ⟨x, _ | _⟩ t (ih : _ = g (mk t))
        · 

Proof state:
case mk.false
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x✝¹ y : FreeGroup α
g : FreeGroup α →* β
x✝ : FreeGroup α
L : List (α × Bool)
x : α
t : List (α × Bool)
ih :
  ((fun f =>
          MonoidHom.mk'
            (Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂))
            (_ :
              ∀ (a b : FreeGroup α),
                Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂)
                    (a * b) =
                  Quot.lift (Lift.aux f)
                      (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) a *
                    Quot.lift (Lift.aux f)
                      (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) b))
        ((fun g => ⇑g ∘ of) g))
      (Quot.mk Red.Step t) =
    g (mk t)
⊢ ((fun f =>
          MonoidHom.mk'
            (Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂))
            (_ :
              ∀ (a b : FreeGroup α),
                Quot.lift (Lift.aux f) (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂)
                    (a * b) =
                  Quot.lift (Lift.aux f)
                      (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) a *
                    Quot.lift (Lift.aux f)
                      (_ : ∀ (L₁ L₂ : List (α × Bool)), Red.Step L₁ L₂ → Lift.aux f L₁ = Lift.aux f L₂) b))
        ((fun g => ⇑g ∘ of) g))
      (Quot.mk Red.Step ((x, false) :: t)) =
    g (Quot.mk Red.Step ((x, false) :: t))

Next tactic:
show _ = g ((of x)⁻¹ * mk t)