l3lab/ntp-mathlib huggingface dataset:
/-- 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))
        · show _ = g ((of x)⁻¹ * 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 ((of x)⁻¹ * mk t)

Next tactic:
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
        rintro ⟨x, _ | _⟩ t (ih : _ = g (mk t))
        · show _ = g ((of x)⁻¹ * mk t)
          simpa [Lift.aux] using ih
        · 

Proof state:
case mk.true
α : 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, true) :: t)) =
    g (Quot.mk Red.Step ((x, true) :: t))

Next tactic:
show _ = g (of x * 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))
        · show _ = g ((of x)⁻¹ * mk t)
          simpa [Lift.aux] using ih
        · show _ = g (of x * mk t)
          

Proof state:
case mk.true
α : 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, true) :: t)) =
    g (of x * mk t)

Next tactic:
simpa [Lift.aux] using ih
@[to_additive]
theorem lift.range_le {s : Subgroup β} (H : Set.range f ⊆ s) : (lift f).range ≤ s := by
  

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x y : FreeGroup α
s : Subgroup β
H : Set.range f ⊆ ↑s
⊢ MonoidHom.range (lift f) ≤ s

Next tactic:
rintro _ ⟨⟨L⟩, rfl⟩
@[to_additive]
theorem lift.range_le {s : Subgroup β} (H : Set.range f ⊆ s) : (lift f).range ≤ s := by
  rintro _ ⟨⟨L⟩, rfl⟩;
    

Proof state:
case intro.mk
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x y : FreeGroup α
s : Subgroup β
H : Set.range f ⊆ ↑s
w✝ : FreeGroup α
L : List (α × Bool)
⊢ (lift f) (Quot.mk Red.Step L) ∈ s

Next tactic:
exact
      List.recOn L s.one_mem fun ⟨x, b⟩ tl ih =>
        Bool.recOn b (by simp at ih ⊢; exact s.mul_mem (s.inv_mem <| H ⟨x, rfl⟩) ih)
          (by simp at ih ⊢; exact s.mul_mem (H ⟨x, rfl⟩) ih)
@[to_additive]
theorem lift.range_le {s : Subgroup β} (H : Set.range f ⊆ s) : (lift f).range ≤ s := by
  rintro _ ⟨⟨L⟩, rfl⟩;
    exact
      List.recOn L s.one_mem fun ⟨x, b⟩ tl ih =>
        Bool.recOn b (by 

Proof state:
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x✝¹ y : FreeGroup α
s : Subgroup β
H : Set.range f ⊆ ↑s
w✝ : FreeGroup α
L : List (α × Bool)
x✝ : α × Bool
tl : List (α × Bool)
ih : (lift f) (Quot.mk Red.Step tl) ∈ s
x : α
b : Bool
⊢ (lift f) (Quot.mk Red.Step ((x, false) :: tl)) ∈ s

Next tactic:
simp at ih ⊢
@[to_additive]
theorem lift.range_le {s : Subgroup β} (H : Set.range f ⊆ s) : (lift f).range ≤ s := by
  rintro _ ⟨⟨L⟩, rfl⟩;
    exact
      List.recOn L s.one_mem fun ⟨x, b⟩ tl ih =>
        Bool.recOn b (by simp at ih ⊢; 

Proof state:
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x✝¹ y : FreeGroup α
s : Subgroup β
H : Set.range f ⊆ ↑s
w✝ : FreeGroup α
L : List (α × Bool)
x✝ : α × Bool
tl : List (α × Bool)
ih : List.prod (List.map (fun x => bif x.2 then f x.1 else (f x.1)⁻¹) tl) ∈ s
x : α
b : Bool
⊢ (f x)⁻¹ * List.prod (List.map (fun x => bif x.2 then f x.1 else (f x.1)⁻¹) tl) ∈ s

Next tactic:
exact s.mul_mem (s.inv_mem <| H ⟨x, rfl⟩) ih
@[to_additive]
theorem lift.range_le {s : Subgroup β} (H : Set.range f ⊆ s) : (lift f).range ≤ s := by
  rintro _ ⟨⟨L⟩, rfl⟩;
    exact
      List.recOn L s.one_mem fun ⟨x, b⟩ tl ih =>
        Bool.recOn b (by simp at ih ⊢; exact s.mul_mem (s.inv_mem <| H ⟨x, rfl⟩) ih)
          (by 

Proof state:
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x✝¹ y : FreeGroup α
s : Subgroup β
H : Set.range f ⊆ ↑s
w✝ : FreeGroup α
L : List (α × Bool)
x✝ : α × Bool
tl : List (α × Bool)
ih : (lift f) (Quot.mk Red.Step tl) ∈ s
x : α
b : Bool
⊢ (lift f) (Quot.mk Red.Step ((x, true) :: tl)) ∈ s

Next tactic:
simp at ih ⊢
@[to_additive]
theorem lift.range_le {s : Subgroup β} (H : Set.range f ⊆ s) : (lift f).range ≤ s := by
  rintro _ ⟨⟨L⟩, rfl⟩;
    exact
      List.recOn L s.one_mem fun ⟨x, b⟩ tl ih =>
        Bool.recOn b (by simp at ih ⊢; exact s.mul_mem (s.inv_mem <| H ⟨x, rfl⟩) ih)
          (by simp at ih ⊢; 

Proof state:
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x✝¹ y : FreeGroup α
s : Subgroup β
H : Set.range f ⊆ ↑s
w✝ : FreeGroup α
L : List (α × Bool)
x✝ : α × Bool
tl : List (α × Bool)
ih : List.prod (List.map (fun x => bif x.2 then f x.1 else (f x.1)⁻¹) tl) ∈ s
x : α
b : Bool
⊢ f x * List.prod (List.map (fun x => bif x.2 then f x.1 else (f x.1)⁻¹) tl) ∈ s

Next tactic:
exact s.mul_mem (H ⟨x, rfl⟩) ih
@[to_additive]
theorem lift.range_eq_closure : (lift f).range = Subgroup.closure (Set.range f) := by
  

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x y : FreeGroup α
⊢ MonoidHom.range (lift f) = Subgroup.closure (Set.range f)

Next tactic:
apply le_antisymm (lift.range_le Subgroup.subset_closure)
@[to_additive]
theorem lift.range_eq_closure : (lift f).range = Subgroup.closure (Set.range f) := by
  apply le_antisymm (lift.range_le Subgroup.subset_closure)
  

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x y : FreeGroup α
⊢ Subgroup.closure (Set.range f) ≤ MonoidHom.range (lift f)

Next tactic:
rw [Subgroup.closure_le]
@[to_additive]
theorem lift.range_eq_closure : (lift f).range = Subgroup.closure (Set.range f) := by
  apply le_antisymm (lift.range_le Subgroup.subset_closure)
  rw [Subgroup.closure_le]
  

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x y : FreeGroup α
⊢ Set.range f ⊆ ↑(MonoidHom.range (lift f))

Next tactic:
rintro _ ⟨a, rfl⟩
@[to_additive]
theorem lift.range_eq_closure : (lift f).range = Subgroup.closure (Set.range f) := by
  apply le_antisymm (lift.range_le Subgroup.subset_closure)
  rw [Subgroup.closure_le]
  rintro _ ⟨a, rfl⟩
  

Proof state:
case intro
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x y : FreeGroup α
a : α
⊢ f a ∈ ↑(MonoidHom.range (lift f))

Next tactic:
exact ⟨FreeGroup.of a, by simp only [lift.of]⟩
@[to_additive]
theorem lift.range_eq_closure : (lift f).range = Subgroup.closure (Set.range f) := by
  apply le_antisymm (lift.range_le Subgroup.subset_closure)
  rw [Subgroup.closure_le]
  rintro _ ⟨a, rfl⟩
  exact ⟨FreeGroup.of a, by 

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

Next tactic:
simp only [lift.of]
/-- Any function from `α` to `β` extends uniquely to a group homomorphism from the free group over
  `α` to the free group over `β`. -/
@[to_additive "Any function from `α` to `β` extends uniquely to an additive group homomorphism from
  the additive free group over `α` to the additive free group over `β`."]
def map : FreeGroup α →* FreeGroup β :=
  MonoidHom.mk'
    (Quot.map (List.map fun x => (f x.1, x.2)) fun L₁ L₂ H => by 

Proof state:
α : Type u
L L₁✝ L₂✝ L₃ L₄ : List (α × Bool)
β : Type v
f : α → β
x y : FreeGroup α
L₁ L₂ : List (α × Bool)
H : Red.Step L₁ L₂
⊢ Red.Step (List.map (fun x => (f x.1, x.2)) L₁) (List.map (fun x => (f x.1, x.2)) L₂)

Next tactic:
cases H
/-- Any function from `α` to `β` extends uniquely to a group homomorphism from the free group over
  `α` to the free group over `β`. -/
@[to_additive "Any function from `α` to `β` extends uniquely to an additive group homomorphism from
  the additive free group over `α` to the additive free group over `β`."]
def map : FreeGroup α →* FreeGroup β :=
  MonoidHom.mk'
    (Quot.map (List.map fun x => (f x.1, x.2)) fun L₁ L₂ H => by cases H; 

Proof state:
case not
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
f : α → β
x y : FreeGroup α
L₁✝ L₂✝ : List (α × Bool)
x✝ : α
b✝ : Bool
⊢ Red.Step (List.map (fun x => (f x.1, x.2)) (L₁✝ ++ (x✝, b✝) :: (x✝, !b✝) :: L₂✝))
    (List.map (fun x => (f x.1, x.2)) (L₁✝ ++ L₂✝))

Next tactic:
simp
/-- Any function from `α` to `β` extends uniquely to a group homomorphism from the free group over
  `α` to the free group over `β`. -/
@[to_additive "Any function from `α` to `β` extends uniquely to an additive group homomorphism from
  the additive free group over `α` to the additive free group over `β`."]
def map : FreeGroup α →* FreeGroup β :=
  MonoidHom.mk'
    (Quot.map (List.map fun x => (f x.1, x.2)) fun L₁ L₂ H => by cases H; simp)
    (by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
f : α → β
x y : FreeGroup α
⊢ ∀ (a b : FreeGroup α),
    Quot.map (List.map fun x => (f x.1, x.2))
        (_ :
          ∀ (L₁ L₂ : List (α × Bool)),
            Red.Step L₁ L₂ → Red.Step (List.map (fun x => (f x.1, x.2)) L₁) (List.map (fun x => (f x.1, x.2)) L₂))
        (a * b) =
      Quot.map (List.map fun x => (f x.1, x.2))
          (_ :
            ∀ (L₁ L₂ : List (α × Bool)),
              Red.Step L₁ L₂ → Red.Step (List.map (fun x => (f x.1, x.2)) L₁) (List.map (fun x => (f x.1, x.2)) L₂))
          a *
        Quot.map (List.map fun x => (f x.1, x.2))
          (_ :
            ∀ (L₁ L₂ : List (α × Bool)),
              Red.Step L₁ L₂ → Red.Step (List.map (fun x => (f x.1, x.2)) L₁) (List.map (fun x => (f x.1, x.2)) L₂))
          b

Next tactic:
rintro ⟨L₁⟩ ⟨L₂⟩
/-- Any function from `α` to `β` extends uniquely to a group homomorphism from the free group over
  `α` to the free group over `β`. -/
@[to_additive "Any function from `α` to `β` extends uniquely to an additive group homomorphism from
  the additive free group over `α` to the additive free group over `β`."]
def map : FreeGroup α →* FreeGroup β :=
  MonoidHom.mk'
    (Quot.map (List.map fun x => (f x.1, x.2)) fun L₁ L₂ H => by cases H; simp)
    (by rintro ⟨L₁⟩ ⟨L₂⟩; 

Proof state:
case mk.mk
α : Type u
L L₁✝ L₂✝ L₃ L₄ : List (α × Bool)
β : Type v
f : α → β
x y a✝ : FreeGroup α
L₁ : List (α × Bool)
b✝ : FreeGroup α
L₂ : List (α × Bool)
⊢ Quot.map (List.map fun x => (f x.1, x.2))
      (_ :
        ∀ (L₁ L₂ : List (α × Bool)),
          Red.Step L₁ L₂ → Red.Step (List.map (fun x => (f x.1, x.2)) L₁) (List.map (fun x => (f x.1, x.2)) L₂))
      (Quot.mk Red.Step L₁ * Quot.mk Red.Step L₂) =
    Quot.map (List.map fun x => (f x.1, x.2))
        (_ :
          ∀ (L₁ L₂ : List (α × Bool)),
            Red.Step L₁ L₂ → Red.Step (List.map (fun x => (f x.1, x.2)) L₁) (List.map (fun x => (f x.1, x.2)) L₂))
        (Quot.mk Red.Step L₁) *
      Quot.map (List.map fun x => (f x.1, x.2))
        (_ :
          ∀ (L₁ L₂ : List (α × Bool)),
            Red.Step L₁ L₂ → Red.Step (List.map (fun x => (f x.1, x.2)) L₁) (List.map (fun x => (f x.1, x.2)) L₂))
        (Quot.mk Red.Step L₂)

Next tactic:
simp
@[to_additive (attr := simp)]
theorem map.id (x : FreeGroup α) : map id x = x := by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
f : α → β
x✝ y x : FreeGroup α
⊢ (map _root_.id) x = x

Next tactic:
rcases x with ⟨L⟩
@[to_additive (attr := simp)]
theorem map.id (x : FreeGroup α) : map id x = x := by rcases x with ⟨L⟩; 

Proof state:
case mk
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
f : α → β
x✝ y x : FreeGroup α
L : List (α × Bool)
⊢ (map _root_.id) (Quot.mk Red.Step L) = Quot.mk Red.Step L

Next tactic:
simp [List.map_id']
@[to_additive]
theorem map.comp {γ : Type w} (f : α → β) (g : β → γ) (x) :
    map g (map f x) = map (g ∘ f) x := by
  

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
f✝ : α → β
x✝ y : FreeGroup α
γ : Type w
f : α → β
g : β → γ
x : FreeGroup α
⊢ (map g) ((map f) x) = (map (g ∘ f)) x

Next tactic:
rcases x with ⟨L⟩
@[to_additive]
theorem map.comp {γ : Type w} (f : α → β) (g : β → γ) (x) :
    map g (map f x) = map (g ∘ f) x := by
  rcases x with ⟨L⟩; 

Proof state:
case mk
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
f✝ : α → β
x✝ y : FreeGroup α
γ : Type w
f : α → β
g : β → γ
x : FreeGroup α
L : List (α × Bool)
⊢ (map g) ((map f) (Quot.mk Red.Step L)) = (map (g ∘ f)) (Quot.mk Red.Step L)

Next tactic:
simp [(· ∘ ·)]
@[to_additive]
theorem map.unique (g : FreeGroup α →* FreeGroup β)
    (hg : ∀ x, g (FreeGroup.of x) = FreeGroup.of (f x)) :
    ∀ {x}, g x = map f x := by
  

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
f : α → β
x y : FreeGroup α
g : FreeGroup α →* FreeGroup β
hg : ∀ (x : α), g (FreeGroup.of x) = FreeGroup.of (f x)
⊢ ∀ {x : FreeGroup α}, g x = (map f) x

Next tactic:
rintro ⟨L⟩
@[to_additive]
theorem map.unique (g : FreeGroup α →* FreeGroup β)
    (hg : ∀ x, g (FreeGroup.of x) = FreeGroup.of (f x)) :
    ∀ {x}, g x = map f x := by
  rintro ⟨L⟩
  

Proof state:
case mk
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
f : α → β
x y : FreeGroup α
g : FreeGroup α →* FreeGroup β
hg : ∀ (x : α), g (FreeGroup.of x) = FreeGroup.of (f x)
x✝ : FreeGroup α
L : List (α × Bool)
⊢ g (Quot.mk Red.Step L) = (map f) (Quot.mk Red.Step L)

Next tactic:
exact List.recOn L g.map_one fun ⟨x, b⟩ t (ih : g (FreeGroup.mk t) = map f (FreeGroup.mk t)) =>
    Bool.recOn b
      (show g ((FreeGroup.of x)⁻¹ * FreeGroup.mk t) =
          FreeGroup.map f ((FreeGroup.of x)⁻¹ * FreeGroup.mk t) by
        simp [g.map_mul, g.map_inv, hg, ih])
      (show g (FreeGroup.of x * FreeGroup.mk t) =
          FreeGroup.map f (FreeGroup.of x * FreeGroup.mk t) by simp [g.map_mul, hg, ih])
@[to_additive]
theorem map.unique (g : FreeGroup α →* FreeGroup β)
    (hg : ∀ x, g (FreeGroup.of x) = FreeGroup.of (f x)) :
    ∀ {x}, g x = map f x := by
  rintro ⟨L⟩
  exact List.recOn L g.map_one fun ⟨x, b⟩ t (ih : g (FreeGroup.mk t) = map f (FreeGroup.mk t)) =>
    Bool.recOn b
      (show g ((FreeGroup.of x)⁻¹ * FreeGroup.mk t) =
          FreeGroup.map f ((FreeGroup.of x)⁻¹ * FreeGroup.mk t) by
        

Proof state:
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
f : α → β
x✝² y : FreeGroup α
g : FreeGroup α →* FreeGroup β
hg : ∀ (x : α), g (FreeGroup.of x) = FreeGroup.of (f x)
x✝¹ : FreeGroup α
L : List (α × Bool)
x✝ : α × Bool
t : List (α × Bool)
ih : g (FreeGroup.mk t) = (map f) (FreeGroup.mk t)
x : α
b : Bool
⊢ g ((FreeGroup.of x)⁻¹ * FreeGroup.mk t) = (map f) ((FreeGroup.of x)⁻¹ * FreeGroup.mk t)

Next tactic:
simp [g.map_mul, g.map_inv, hg, ih]
@[to_additive]
theorem map.unique (g : FreeGroup α →* FreeGroup β)
    (hg : ∀ x, g (FreeGroup.of x) = FreeGroup.of (f x)) :
    ∀ {x}, g x = map f x := by
  rintro ⟨L⟩
  exact List.recOn L g.map_one fun ⟨x, b⟩ t (ih : g (FreeGroup.mk t) = map f (FreeGroup.mk t)) =>
    Bool.recOn b
      (show g ((FreeGroup.of x)⁻¹ * FreeGroup.mk t) =
          FreeGroup.map f ((FreeGroup.of x)⁻¹ * FreeGroup.mk t) by
        simp [g.map_mul, g.map_inv, hg, ih])
      (show g (FreeGroup.of x * FreeGroup.mk t) =
          FreeGroup.map f (FreeGroup.of x * FreeGroup.mk t) by 

Proof state:
α : Type u
L✝ L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
f : α → β
x✝² y : FreeGroup α
g : FreeGroup α →* FreeGroup β
hg : ∀ (x : α), g (FreeGroup.of x) = FreeGroup.of (f x)
x✝¹ : FreeGroup α
L : List (α × Bool)
x✝ : α × Bool
t : List (α × Bool)
ih : g (FreeGroup.mk t) = (map f) (FreeGroup.mk t)
x : α
b : Bool
⊢ g (FreeGroup.of x * FreeGroup.mk t) = (map f) (FreeGroup.of x * FreeGroup.mk t)

Next tactic:
simp [g.map_mul, hg, ih]
@[to_additive]
theorem map_eq_lift : map f x = lift (of ∘ f) x :=
  Eq.symm <| map.unique _ fun x => by 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
f : α → β
x✝ y : FreeGroup α
x : α
⊢ (lift (of ∘ f)) (of x) = of (f x)

Next tactic:
simp
/-- Equivalent types give rise to multiplicatively equivalent free groups.

The converse can be found in `GroupTheory.FreeAbelianGroupFinsupp`,
as `Equiv.of_freeGroupEquiv`
 -/
@[to_additive (attr := simps apply)
  "Equivalent types give rise to additively equivalent additive free groups."]
def freeGroupCongr {α β} (e : α ≃ β) : FreeGroup α ≃* FreeGroup β where
  toFun := map e
  invFun := map e.symm
  left_inv x := by 

Proof state:
α✝ : Type u
L L₁ L₂ L₃ L₄ : List (α✝ × Bool)
β✝ : Type v
f : α✝ → β✝
x✝ y : FreeGroup α✝
α : Type ?u.101461
β : Type ?u.101462
e : α ≃ β
x : FreeGroup α
⊢ (map ⇑e.symm) ((map ⇑e) x) = x

Next tactic:
simp [Function.comp, map.comp]
/-- Equivalent types give rise to multiplicatively equivalent free groups.

The converse can be found in `GroupTheory.FreeAbelianGroupFinsupp`,
as `Equiv.of_freeGroupEquiv`
 -/
@[to_additive (attr := simps apply)
  "Equivalent types give rise to additively equivalent additive free groups."]
def freeGroupCongr {α β} (e : α ≃ β) : FreeGroup α ≃* FreeGroup β where
  toFun := map e
  invFun := map e.symm
  left_inv x := by simp [Function.comp, map.comp]
  right_inv x := by 

Proof state:
α✝ : Type u
L L₁ L₂ L₃ L₄ : List (α✝ × Bool)
β✝ : Type v
f : α✝ → β✝
x✝ y : FreeGroup α✝
α : Type ?u.101461
β : Type ?u.101462
e : α ≃ β
x : FreeGroup β
⊢ (map ⇑e) ((map ⇑e.symm) x) = x

Next tactic:
simp [Function.comp, map.comp]
@[to_additive]
theorem lift_eq_prod_map {β : Type v} [Group β] {f : α → β} {x} : lift f x = prod (map f x) := by
  

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x : FreeGroup α
⊢ (lift f) x = prod ((map f) x)

Next tactic:
rw [← lift.unique (prod.comp (map f))]
@[to_additive]
theorem lift_eq_prod_map {β : Type v} [Group β] {f : α → β} {x} : lift f x = prod (map f x) := by
  rw [← lift.unique (prod.comp (map f))]
  · 

Proof state:
α : Type u
L L₁ L₂ L₃ L₄ : List (α × Bool)
β : Type v
inst✝ : Group β
f : α → β
x : FreeGroup α
⊢ (MonoidHom.comp prod (map f)) x = prod ((map f) x)

Next tactic:
rfl