l3lab/ntp-mathlib huggingface dataset:
theorem transAt_distrib {i j : N} (h : i ≠ j) (a b c d : Ω^ N X x) :
    transAt i (transAt j a b) (transAt j c d) = transAt j (transAt i a c) (transAt i b d) := by
  ext; simp_rw [transAt, coe_copy, Function.update_apply, if_neg h, if_neg h.symm]
  split_ifs <;>
    · congr 1; ext1; simp only [Function.update, eq_rec_constant, dite_eq_ite]
      apply ite_ite_comm; rintro rfl; 

Proof state:
case pos.h.e_6.h.h.h
N : Type u_1
X : Type u_2
inst✝¹ : TopologicalSpace X
x : X
inst✝ : DecidableEq N
i : N
a b c d : ↑(Ω^ N X x)
y✝ : N → ↑I
h✝¹ : ¬↑(y✝ i) ≤ 1 / 2
x✝ : N
h : i ≠ x✝
h✝ : ↑(y✝ x✝) ≤ 1 / 2
⊢ ¬x✝ = i

Next tactic:
exact h.symm
theorem transAt_distrib {i j : N} (h : i ≠ j) (a b c d : Ω^ N X x) :
    transAt i (transAt j a b) (transAt j c d) = transAt j (transAt i a c) (transAt i b d) := by
  ext; simp_rw [transAt, coe_copy, Function.update_apply, if_neg h, if_neg h.symm]
  split_ifs <;>
    · 

Proof state:
case neg
N : Type u_1
X : Type u_2
inst✝¹ : TopologicalSpace X
x : X
inst✝ : DecidableEq N
i j : N
h : i ≠ j
a b c d : ↑(Ω^ N X x)
y✝ : N → ↑I
h✝¹ : ¬↑(y✝ i) ≤ 1 / 2
h✝ : ¬↑(y✝ j) ≤ 1 / 2
⊢ d
      (Function.update (Function.update y✝ i (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ i) - 1))) j
        (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ j) - 1))) =
    d
      (Function.update (Function.update y✝ j (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ j) - 1))) i
        (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ i) - 1)))

Next tactic:
congr 1
theorem transAt_distrib {i j : N} (h : i ≠ j) (a b c d : Ω^ N X x) :
    transAt i (transAt j a b) (transAt j c d) = transAt j (transAt i a c) (transAt i b d) := by
  ext; simp_rw [transAt, coe_copy, Function.update_apply, if_neg h, if_neg h.symm]
  split_ifs <;>
    · congr 1; 

Proof state:
case neg.h.e_6.h
N : Type u_1
X : Type u_2
inst✝¹ : TopologicalSpace X
x : X
inst✝ : DecidableEq N
i j : N
h : i ≠ j
a b c d : ↑(Ω^ N X x)
y✝ : N → ↑I
h✝¹ : ¬↑(y✝ i) ≤ 1 / 2
h✝ : ¬↑(y✝ j) ≤ 1 / 2
⊢ Function.update (Function.update y✝ i (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ i) - 1))) j
      (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ j) - 1)) =
    Function.update (Function.update y✝ j (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ j) - 1))) i
      (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ i) - 1))

Next tactic:
ext1
theorem transAt_distrib {i j : N} (h : i ≠ j) (a b c d : Ω^ N X x) :
    transAt i (transAt j a b) (transAt j c d) = transAt j (transAt i a c) (transAt i b d) := by
  ext; simp_rw [transAt, coe_copy, Function.update_apply, if_neg h, if_neg h.symm]
  split_ifs <;>
    · congr 1; ext1; 

Proof state:
case neg.h.e_6.h.h
N : Type u_1
X : Type u_2
inst✝¹ : TopologicalSpace X
x : X
inst✝ : DecidableEq N
i j : N
h : i ≠ j
a b c d : ↑(Ω^ N X x)
y✝ : N → ↑I
h✝¹ : ¬↑(y✝ i) ≤ 1 / 2
h✝ : ¬↑(y✝ j) ≤ 1 / 2
x✝ : N
⊢ Function.update (Function.update y✝ i (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ i) - 1))) j
      (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ j) - 1)) x✝ =
    Function.update (Function.update y✝ j (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ j) - 1))) i
      (Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ i) - 1)) x✝

Next tactic:
simp only [Function.update, eq_rec_constant, dite_eq_ite]
theorem transAt_distrib {i j : N} (h : i ≠ j) (a b c d : Ω^ N X x) :
    transAt i (transAt j a b) (transAt j c d) = transAt j (transAt i a c) (transAt i b d) := by
  ext; simp_rw [transAt, coe_copy, Function.update_apply, if_neg h, if_neg h.symm]
  split_ifs <;>
    · congr 1; ext1; simp only [Function.update, eq_rec_constant, dite_eq_ite]
      

Proof state:
case neg.h.e_6.h.h
N : Type u_1
X : Type u_2
inst✝¹ : TopologicalSpace X
x : X
inst✝ : DecidableEq N
i j : N
h : i ≠ j
a b c d : ↑(Ω^ N X x)
y✝ : N → ↑I
h✝¹ : ¬↑(y✝ i) ≤ 1 / 2
h✝ : ¬↑(y✝ j) ≤ 1 / 2
x✝ : N
⊢ (if x✝ = j then Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ j) - 1)
    else if x✝ = i then Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ i) - 1) else y✝ x✝) =
    if x✝ = i then Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ i) - 1)
    else if x✝ = j then Set.projIcc 0 1 transAt.proof_2 (2 * ↑(y✝ j) - 1) else y✝ x✝

Next tactic:
apply ite_ite_comm
theorem transAt_distrib {i j : N} (h : i ≠ j) (a b c d : Ω^ N X x) :
    transAt i (transAt j a b) (transAt j c d) = transAt j (transAt i a c) (transAt i b d) := by
  ext; simp_rw [transAt, coe_copy, Function.update_apply, if_neg h, if_neg h.symm]
  split_ifs <;>
    · congr 1; ext1; simp only [Function.update, eq_rec_constant, dite_eq_ite]
      apply ite_ite_comm; 

Proof state:
case neg.h.e_6.h.h.h
N : Type u_1
X : Type u_2
inst✝¹ : TopologicalSpace X
x : X
inst✝ : DecidableEq N
i j : N
h : i ≠ j
a b c d : ↑(Ω^ N X x)
y✝ : N → ↑I
h✝¹ : ¬↑(y✝ i) ≤ 1 / 2
h✝ : ¬↑(y✝ j) ≤ 1 / 2
x✝ : N
⊢ x✝ = j → ¬x✝ = i

Next tactic:
rintro rfl
theorem transAt_distrib {i j : N} (h : i ≠ j) (a b c d : Ω^ N X x) :
    transAt i (transAt j a b) (transAt j c d) = transAt j (transAt i a c) (transAt i b d) := by
  ext; simp_rw [transAt, coe_copy, Function.update_apply, if_neg h, if_neg h.symm]
  split_ifs <;>
    · congr 1; ext1; simp only [Function.update, eq_rec_constant, dite_eq_ite]
      apply ite_ite_comm; rintro rfl; 

Proof state:
case neg.h.e_6.h.h.h
N : Type u_1
X : Type u_2
inst✝¹ : TopologicalSpace X
x : X
inst✝ : DecidableEq N
i : N
a b c d : ↑(Ω^ N X x)
y✝ : N → ↑I
h✝¹ : ¬↑(y✝ i) ≤ 1 / 2
x✝ : N
h : i ≠ x✝
h✝ : ¬↑(y✝ x✝) ≤ 1 / 2
⊢ ¬x✝ = i

Next tactic:
exact h.symm
/-- Equivalence between the homotopy group of X and the fundamental group of
  `Ω^{j // j ≠ i} x`. -/
def homotopyGroupEquivFundamentalGroup (i : N) :
    HomotopyGroup N X x ≃ FundamentalGroup (Ω^ { j // j ≠ i } X x) const := by
  

Proof state:
N : Type u_1
X : Type u_2
inst✝¹ : TopologicalSpace X
x : X
inst✝ : DecidableEq N
i : N
⊢ HomotopyGroup N X x ≃ FundamentalGroup (↑(Ω^ { j // j ≠ i } X x)) const

Next tactic:
refine' Equiv.trans _ (CategoryTheory.Groupoid.isoEquivHom _ _).symm
/-- Equivalence between the homotopy group of X and the fundamental group of
  `Ω^{j // j ≠ i} x`. -/
def homotopyGroupEquivFundamentalGroup (i : N) :
    HomotopyGroup N X x ≃ FundamentalGroup (Ω^ { j // j ≠ i } X x) const := by
  refine' Equiv.trans _ (CategoryTheory.Groupoid.isoEquivHom _ _).symm
  

Proof state:
N : Type u_1
X : Type u_2
inst✝¹ : TopologicalSpace X
x : X
inst✝ : DecidableEq N
i : N
⊢ HomotopyGroup N X x ≃ ({ as := const } ⟶ { as := const })

Next tactic:
apply Quotient.congr (loopHomeo i).toEquiv
/-- Equivalence between the homotopy group of X and the fundamental group of
  `Ω^{j // j ≠ i} x`. -/
def homotopyGroupEquivFundamentalGroup (i : N) :
    HomotopyGroup N X x ≃ FundamentalGroup (Ω^ { j // j ≠ i } X x) const := by
  refine' Equiv.trans _ (CategoryTheory.Groupoid.isoEquivHom _ _).symm
  apply Quotient.congr (loopHomeo i).toEquiv
  

Proof state:
N : Type u_1
X : Type u_2
inst✝¹ : TopologicalSpace X
x : X
inst✝ : DecidableEq N
i : N
⊢ ∀ (a₁ a₂ : ↑(Ω^ N X x)), Setoid.r a₁ a₂ ↔ Setoid.r ((loopHomeo i).toEquiv a₁) ((loopHomeo i).toEquiv a₂)

Next tactic:
exact fun p q => ⟨homotopicTo i, homotopicFrom i⟩
/-- The 0-dimensional generalized loops based at `x` are in bijection with `X`. -/
def genLoopHomeoOfIsEmpty (N x) [IsEmpty N] : Ω^ N X x ≃ₜ X where
  toFun f := f 0
  invFun y := ⟨ContinuousMap.const _ y, fun _ ⟨i, _⟩ => isEmptyElim i⟩
  left_inv f := by 

Proof state:
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x✝ : X
inst✝¹ : DecidableEq N✝
N : Type ?u.560569
x : X
inst✝ : IsEmpty N
f : ↑(Ω^ N X x)
⊢ (fun y =>
        { val := ContinuousMap.const (N → ↑I) y,
          property := (_ : ∀ x_1 ∈ Cube.boundary N, (ContinuousMap.const (N → ↑I) y) x_1 = x) })
      ((fun f => f 0) f) =
    f

Next tactic:
ext
/-- The 0-dimensional generalized loops based at `x` are in bijection with `X`. -/
def genLoopHomeoOfIsEmpty (N x) [IsEmpty N] : Ω^ N X x ≃ₜ X where
  toFun f := f 0
  invFun y := ⟨ContinuousMap.const _ y, fun _ ⟨i, _⟩ => isEmptyElim i⟩
  left_inv f := by ext; 

Proof state:
case H
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x✝ : X
inst✝¹ : DecidableEq N✝
N : Type ?u.560569
x : X
inst✝ : IsEmpty N
f : ↑(Ω^ N X x)
y✝ : N → ↑I
⊢ ((fun y =>
          { val := ContinuousMap.const (N → ↑I) y,
            property := (_ : ∀ x_1 ∈ Cube.boundary N, (ContinuousMap.const (N → ↑I) y) x_1 = x) })
        ((fun f => f 0) f))
      y✝ =
    f y✝

Next tactic:
exact congr_arg f (Subsingleton.elim _ _)
/-- The homotopy "group" indexed by an empty type is in bijection with
  the path components of `X`, aka the `ZerothHomotopy`. -/
def homotopyGroupEquivZerothHomotopyOfIsEmpty (N x) [IsEmpty N] :
    HomotopyGroup N X x ≃ ZerothHomotopy X :=
  Quotient.congr (genLoopHomeoOfIsEmpty N x).toEquiv
    (by
      -- joined iff homotopic
      

Proof state:
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x✝ : X
inst✝¹ : DecidableEq N✝
N : Type ?u.562961
x : X
inst✝ : IsEmpty N
⊢ ∀ (a₁ a₂ : ↑(Ω^ N X x)),
    Setoid.r a₁ a₂ ↔ Setoid.r ((genLoopHomeoOfIsEmpty N x).toEquiv a₁) ((genLoopHomeoOfIsEmpty N x).toEquiv a₂)

Next tactic:
intros a₁ a₂
/-- The homotopy "group" indexed by an empty type is in bijection with
  the path components of `X`, aka the `ZerothHomotopy`. -/
def homotopyGroupEquivZerothHomotopyOfIsEmpty (N x) [IsEmpty N] :
    HomotopyGroup N X x ≃ ZerothHomotopy X :=
  Quotient.congr (genLoopHomeoOfIsEmpty N x).toEquiv
    (by
      -- joined iff homotopic
      intros a₁ a₂;
      

Proof state:
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x✝ : X
inst✝¹ : DecidableEq N✝
N : Type ?u.562961
x : X
inst✝ : IsEmpty N
a₁ a₂ : ↑(Ω^ N X x)
⊢ Setoid.r a₁ a₂ ↔ Setoid.r ((genLoopHomeoOfIsEmpty N x).toEquiv a₁) ((genLoopHomeoOfIsEmpty N x).toEquiv a₂)

Next tactic:
constructor
/-- The homotopy "group" indexed by an empty type is in bijection with
  the path components of `X`, aka the `ZerothHomotopy`. -/
def homotopyGroupEquivZerothHomotopyOfIsEmpty (N x) [IsEmpty N] :
    HomotopyGroup N X x ≃ ZerothHomotopy X :=
  Quotient.congr (genLoopHomeoOfIsEmpty N x).toEquiv
    (by
      -- joined iff homotopic
      intros a₁ a₂;
      constructor <;> 

Proof state:
case mp
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x✝ : X
inst✝¹ : DecidableEq N✝
N : Type ?u.562961
x : X
inst✝ : IsEmpty N
a₁ a₂ : ↑(Ω^ N X x)
⊢ Setoid.r a₁ a₂ → Setoid.r ((genLoopHomeoOfIsEmpty N x).toEquiv a₁) ((genLoopHomeoOfIsEmpty N x).toEquiv a₂)

Next tactic:
rintro ⟨H⟩
/-- The homotopy "group" indexed by an empty type is in bijection with
  the path components of `X`, aka the `ZerothHomotopy`. -/
def homotopyGroupEquivZerothHomotopyOfIsEmpty (N x) [IsEmpty N] :
    HomotopyGroup N X x ≃ ZerothHomotopy X :=
  Quotient.congr (genLoopHomeoOfIsEmpty N x).toEquiv
    (by
      -- joined iff homotopic
      intros a₁ a₂;
      constructor <;> 

Proof state:
case mpr
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x✝ : X
inst✝¹ : DecidableEq N✝
N : Type ?u.562961
x : X
inst✝ : IsEmpty N
a₁ a₂ : ↑(Ω^ N X x)
⊢ Setoid.r ((genLoopHomeoOfIsEmpty N x).toEquiv a₁) ((genLoopHomeoOfIsEmpty N x).toEquiv a₂) → Setoid.r a₁ a₂

Next tactic:
rintro ⟨H⟩
/-- The homotopy "group" indexed by an empty type is in bijection with
  the path components of `X`, aka the `ZerothHomotopy`. -/
def homotopyGroupEquivZerothHomotopyOfIsEmpty (N x) [IsEmpty N] :
    HomotopyGroup N X x ≃ ZerothHomotopy X :=
  Quotient.congr (genLoopHomeoOfIsEmpty N x).toEquiv
    (by
      -- joined iff homotopic
      intros a₁ a₂;
      constructor <;> rintro ⟨H⟩
      

Proof state:
case mp.intro
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x✝ : X
inst✝¹ : DecidableEq N✝
N : Type ?u.562961
x : X
inst✝ : IsEmpty N
a₁ a₂ : ↑(Ω^ N X x)
H : ContinuousMap.HomotopyRel (↑a₁) (↑a₂) (Cube.boundary N)
⊢ Setoid.r ((genLoopHomeoOfIsEmpty N x).toEquiv a₁) ((genLoopHomeoOfIsEmpty N x).toEquiv a₂)
case mpr.intro
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x✝ : X
inst✝¹ : DecidableEq N✝
N : Type ?u.562961
x : X
inst✝ : IsEmpty N
a₁ a₂ : ↑(Ω^ N X x)
H : Path ((genLoopHomeoOfIsEmpty N x).toEquiv a₁) ((genLoopHomeoOfIsEmpty N x).toEquiv a₂)
⊢ Setoid.r a₁ a₂

Next tactic:
exacts
        [⟨{ toFun := fun t => H ⟨t, isEmptyElim⟩
            source' := (H.apply_zero _).trans (congr_arg a₁ <| Subsingleton.elim _ _)
            target' := (H.apply_one _).trans (congr_arg a₂ <| Subsingleton.elim _ _) }⟩,
        ⟨{  toFun := fun t0 => H t0.fst
            map_zero_left := fun _ => H.source.trans (congr_arg a₁ <| Subsingleton.elim _ _)
            map_one_left := fun _ => H.target.trans (congr_arg a₂ <| Subsingleton.elim _ _)
            prop' := fun _ _ ⟨i, _⟩ => isEmptyElim i }⟩]
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by 

Proof state:
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : ↑(Ω^ N X x)
⊢ Continuous fun t => p fun x => t

Next tactic:
continuity
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by continuity⟩
      (GenLoop.boundary _ (fun _ => 0) ⟨default, Or.inl rfl⟩)
      (GenLoop.boundary _ (fun _ => 1) ⟨default, Or.inr rfl⟩)
  invFun p :=
    ⟨⟨fun c => p (c default), by 

Proof state:
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : Ω X x
⊢ Continuous fun c => p (c default)

Next tactic:
continuity
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by continuity⟩
      (GenLoop.boundary _ (fun _ => 0) ⟨default, Or.inl rfl⟩)
      (GenLoop.boundary _ (fun _ => 1) ⟨default, Or.inr rfl⟩)
  invFun p :=
    ⟨⟨fun c => p (c default), by continuity⟩,
      by
      

Proof state:
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : Ω X x
⊢ (ContinuousMap.mk fun c => p (c default)) ∈ Ω^ N X x

Next tactic:
rintro y ⟨i, iH | iH⟩
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by continuity⟩
      (GenLoop.boundary _ (fun _ => 0) ⟨default, Or.inl rfl⟩)
      (GenLoop.boundary _ (fun _ => 1) ⟨default, Or.inr rfl⟩)
  invFun p :=
    ⟨⟨fun c => p (c default), by continuity⟩,
      by
      rintro y ⟨i, iH | iH⟩ <;> 

Proof state:
case intro.inl
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : Ω X x
y : N → ↑I
i : N
iH : y i = 0
⊢ (ContinuousMap.mk fun c => p (c default)) y = x

Next tactic:
cases Unique.eq_default i
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by continuity⟩
      (GenLoop.boundary _ (fun _ => 0) ⟨default, Or.inl rfl⟩)
      (GenLoop.boundary _ (fun _ => 1) ⟨default, Or.inr rfl⟩)
  invFun p :=
    ⟨⟨fun c => p (c default), by continuity⟩,
      by
      rintro y ⟨i, iH | iH⟩ <;> 

Proof state:
case intro.inr
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : Ω X x
y : N → ↑I
i : N
iH : y i = 1
⊢ (ContinuousMap.mk fun c => p (c default)) y = x

Next tactic:
cases Unique.eq_default i
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by continuity⟩
      (GenLoop.boundary _ (fun _ => 0) ⟨default, Or.inl rfl⟩)
      (GenLoop.boundary _ (fun _ => 1) ⟨default, Or.inr rfl⟩)
  invFun p :=
    ⟨⟨fun c => p (c default), by continuity⟩,
      by
      rintro y ⟨i, iH | iH⟩ <;> cases Unique.eq_default i <;> 

Proof state:
case intro.inl.refl
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : Ω X x
y : N → ↑I
iH : y default = 0
⊢ (ContinuousMap.mk fun c => p (c default)) y = x

Next tactic:
apply (congr_arg p iH).trans
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by continuity⟩
      (GenLoop.boundary _ (fun _ => 0) ⟨default, Or.inl rfl⟩)
      (GenLoop.boundary _ (fun _ => 1) ⟨default, Or.inr rfl⟩)
  invFun p :=
    ⟨⟨fun c => p (c default), by continuity⟩,
      by
      rintro y ⟨i, iH | iH⟩ <;> cases Unique.eq_default i <;> 

Proof state:
case intro.inr.refl
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : Ω X x
y : N → ↑I
iH : y default = 1
⊢ (ContinuousMap.mk fun c => p (c default)) y = x

Next tactic:
apply (congr_arg p iH).trans
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by continuity⟩
      (GenLoop.boundary _ (fun _ => 0) ⟨default, Or.inl rfl⟩)
      (GenLoop.boundary _ (fun _ => 1) ⟨default, Or.inr rfl⟩)
  invFun p :=
    ⟨⟨fun c => p (c default), by continuity⟩,
      by
      rintro y ⟨i, iH | iH⟩ <;> cases Unique.eq_default i <;> apply (congr_arg p iH).trans
      

Proof state:
case intro.inl.refl
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : Ω X x
y : N → ↑I
iH : y default = 0
⊢ p 0 = x
case intro.inr.refl
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : Ω X x
y : N → ↑I
iH : y default = 1
⊢ p 1 = x

Next tactic:
exacts [p.source, p.target]
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by continuity⟩
      (GenLoop.boundary _ (fun _ => 0) ⟨default, Or.inl rfl⟩)
      (GenLoop.boundary _ (fun _ => 1) ⟨default, Or.inr rfl⟩)
  invFun p :=
    ⟨⟨fun c => p (c default), by continuity⟩,
      by
      rintro y ⟨i, iH | iH⟩ <;> cases Unique.eq_default i <;> apply (congr_arg p iH).trans
      exacts [p.source, p.target]⟩
  left_inv p := by 

Proof state:
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : ↑(Ω^ N X x)
⊢ (fun p =>
        { val := ContinuousMap.mk fun c => p (c default),
          property := (_ : ∀ y ∈ Cube.boundary N, (ContinuousMap.mk fun c => p (c default)) y = x) })
      ((fun p =>
          { toContinuousMap := ContinuousMap.mk fun t => p fun x => t, source' := (_ : (p fun x => 0) = x),
            target' := (_ : (p fun x => 1) = x) })
        p) =
    p

Next tactic:
ext y
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by continuity⟩
      (GenLoop.boundary _ (fun _ => 0) ⟨default, Or.inl rfl⟩)
      (GenLoop.boundary _ (fun _ => 1) ⟨default, Or.inr rfl⟩)
  invFun p :=
    ⟨⟨fun c => p (c default), by continuity⟩,
      by
      rintro y ⟨i, iH | iH⟩ <;> cases Unique.eq_default i <;> apply (congr_arg p iH).trans
      exacts [p.source, p.target]⟩
  left_inv p := by ext y; 

Proof state:
case H
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : ↑(Ω^ N X x)
y : N → ↑I
⊢ ((fun p =>
          { val := ContinuousMap.mk fun c => p (c default),
            property := (_ : ∀ y ∈ Cube.boundary N, (ContinuousMap.mk fun c => p (c default)) y = x) })
        ((fun p =>
            { toContinuousMap := ContinuousMap.mk fun t => p fun x => t, source' := (_ : (p fun x => 0) = x),
              target' := (_ : (p fun x => 1) = x) })
          p))
      y =
    p y

Next tactic:
exact congr_arg p (eq_const_of_unique y).symm
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by continuity⟩
      (GenLoop.boundary _ (fun _ => 0) ⟨default, Or.inl rfl⟩)
      (GenLoop.boundary _ (fun _ => 1) ⟨default, Or.inr rfl⟩)
  invFun p :=
    ⟨⟨fun c => p (c default), by continuity⟩,
      by
      rintro y ⟨i, iH | iH⟩ <;> cases Unique.eq_default i <;> apply (congr_arg p iH).trans
      exacts [p.source, p.target]⟩
  left_inv p := by ext y; exact congr_arg p (eq_const_of_unique y).symm
  right_inv p := by 

Proof state:
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : Ω X x
⊢ (fun p =>
        { toContinuousMap := ContinuousMap.mk fun t => p fun x => t, source' := (_ : (p fun x => 0) = x),
          target' := (_ : (p fun x => 1) = x) })
      ((fun p =>
          { val := ContinuousMap.mk fun c => p (c default),
            property := (_ : ∀ y ∈ Cube.boundary N, (ContinuousMap.mk fun c => p (c default)) y = x) })
        p) =
    p

Next tactic:
ext
/-- The 1-dimensional generalized loops based at `x` are in bijection with loops at `x`. -/
def genLoopEquivOfUnique (N) [Unique N] : Ω^ N X x ≃ Ω X x where
  toFun p :=
    Path.mk ⟨fun t => p fun _ => t, by continuity⟩
      (GenLoop.boundary _ (fun _ => 0) ⟨default, Or.inl rfl⟩)
      (GenLoop.boundary _ (fun _ => 1) ⟨default, Or.inr rfl⟩)
  invFun p :=
    ⟨⟨fun c => p (c default), by continuity⟩,
      by
      rintro y ⟨i, iH | iH⟩ <;> cases Unique.eq_default i <;> apply (congr_arg p iH).trans
      exacts [p.source, p.target]⟩
  left_inv p := by ext y; exact congr_arg p (eq_const_of_unique y).symm
  right_inv p := by ext; 

Proof state:
case a.h
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.686096
inst✝ : Unique N
p : Ω X x
x✝ : ↑I
⊢ ((fun p =>
          { toContinuousMap := ContinuousMap.mk fun t => p fun x => t, source' := (_ : (p fun x => 0) = x),
            target' := (_ : (p fun x => 1) = x) })
        ((fun p =>
            { val := ContinuousMap.mk fun c => p (c default),
              property := (_ : ∀ y ∈ Cube.boundary N, (ContinuousMap.mk fun c => p (c default)) y = x) })
          p))
      x✝ =
    p x✝

Next tactic:
rfl
/-- The homotopy group at `x` indexed by a singleton is in bijection with the fundamental group,
  i.e. the loops based at `x` up to homotopy. -/
def homotopyGroupEquivFundamentalGroupOfUnique (N) [Unique N] :
    HomotopyGroup N X x ≃ FundamentalGroup X x := by
  

Proof state:
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.764964
inst✝ : Unique N
⊢ HomotopyGroup N X x ≃ FundamentalGroup X x

Next tactic:
refine' Equiv.trans _ (CategoryTheory.Groupoid.isoEquivHom _ _).symm
/-- The homotopy group at `x` indexed by a singleton is in bijection with the fundamental group,
  i.e. the loops based at `x` up to homotopy. -/
def homotopyGroupEquivFundamentalGroupOfUnique (N) [Unique N] :
    HomotopyGroup N X x ≃ FundamentalGroup X x := by
  refine' Equiv.trans _ (CategoryTheory.Groupoid.isoEquivHom _ _).symm
  

Proof state:
N✝ : Type u_1
X : Type u_2
inst✝² : TopologicalSpace X
x : X
inst✝¹ : DecidableEq N✝
N : Type ?u.764964
inst✝ : Unique N
⊢ HomotopyGroup N X x ≃ ({ as := x } ⟶ { as := x })

Next tactic:
refine' Quotient.congr (genLoopEquivOfUnique N) _