l3lab/ntp-mathlib huggingface dataset:
/-- Lifting the universe level of morphisms and objects preserves connectedness. -/
instance [hc : IsConnected J] : IsConnected (ULiftHom.{v₂} (ULift.{u₂} J)) := by
  have : Nonempty (ULiftHom.{v₂} (ULift.{u₂} J)) := by 

Proof state:
J : Type u₁
inst✝¹ : Category.{v₁, u₁} J
K : Type u₂
inst✝ : Category.{v₂, u₂} K
hc : IsConnected J
⊢ Nonempty (ULiftHom (ULift.{u₂, u₁} J))

Next tactic:
simp [ULiftHom, hc.is_nonempty]
/-- Lifting the universe level of morphisms and objects preserves connectedness. -/
instance [hc : IsConnected J] : IsConnected (ULiftHom.{v₂} (ULift.{u₂} J)) := by
  have : Nonempty (ULiftHom.{v₂} (ULift.{u₂} J)) := by simp [ULiftHom, hc.is_nonempty]
  

Proof state:
J : Type u₁
inst✝¹ : Category.{v₁, u₁} J
K : Type u₂
inst✝ : Category.{v₂, u₂} K
hc : IsConnected J
this : Nonempty (ULiftHom (ULift.{u₂, u₁} J))
⊢ IsConnected (ULiftHom (ULift.{u₂, u₁} J))

Next tactic:
apply IsConnected.of_induct
/-- Lifting the universe level of morphisms and objects preserves connectedness. -/
instance [hc : IsConnected J] : IsConnected (ULiftHom.{v₂} (ULift.{u₂} J)) := by
  have : Nonempty (ULiftHom.{v₂} (ULift.{u₂} J)) := by simp [ULiftHom, hc.is_nonempty]
  apply IsConnected.of_induct
  

Proof state:
case h
J : Type u₁
inst✝¹ : Category.{v₁, u₁} J
K : Type u₂
inst✝ : Category.{v₂, u₂} K
hc : IsConnected J
this : Nonempty (ULiftHom (ULift.{u₂, u₁} J))
⊢ ∀ (p : Set (ULiftHom (ULift.{u₂, u₁} J))),
    ?j₀ ∈ p →
      (∀ {j₁ j₂ : ULiftHom (ULift.{u₂, u₁} J)}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)) →
        ∀ (j : ULiftHom (ULift.{u₂, u₁} J)), j ∈ p
case j₀
J : Type u₁
inst✝¹ : Category.{v₁, u₁} J
K : Type u₂
inst✝ : Category.{v₂, u₂} K
hc : IsConnected J
this : Nonempty (ULiftHom (ULift.{u₂, u₁} J))
⊢ ULiftHom (ULift.{u₂, u₁} J)

Next tactic:
rintro p hj₀ h ⟨j⟩
/-- Lifting the universe level of morphisms and objects preserves connectedness. -/
instance [hc : IsConnected J] : IsConnected (ULiftHom.{v₂} (ULift.{u₂} J)) := by
  have : Nonempty (ULiftHom.{v₂} (ULift.{u₂} J)) := by simp [ULiftHom, hc.is_nonempty]
  apply IsConnected.of_induct
  rintro p hj₀ h ⟨j⟩
  

Proof state:
case h.up
J : Type u₁
inst✝¹ : Category.{v₁, u₁} J
K : Type u₂
inst✝ : Category.{v₂, u₂} K
hc : IsConnected J
this : Nonempty (ULiftHom (ULift.{u₂, u₁} J))
p : Set (ULiftHom (ULift.{u₂, u₁} J))
hj₀ : ?j₀ ∈ p
h : ∀ {j₁ j₂ : ULiftHom (ULift.{u₂, u₁} J)}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j : J
⊢ { down := j } ∈ p
case j₀
J : Type u₁
inst✝¹ : Category.{v₁, u₁} J
K : Type u₂
inst✝ : Category.{v₂, u₂} K
hc : IsConnected J
this : Nonempty (ULiftHom (ULift.{u₂, u₁} J))
⊢ ULiftHom (ULift.{u₂, u₁} J)

Next tactic:
let p' : Set J := {j : J | p ⟨j⟩}
/-- Lifting the universe level of morphisms and objects preserves connectedness. -/
instance [hc : IsConnected J] : IsConnected (ULiftHom.{v₂} (ULift.{u₂} J)) := by
  have : Nonempty (ULiftHom.{v₂} (ULift.{u₂} J)) := by simp [ULiftHom, hc.is_nonempty]
  apply IsConnected.of_induct
  rintro p hj₀ h ⟨j⟩
  let p' : Set J := {j : J | p ⟨j⟩}
  

Proof state:
case h.up
J : Type u₁
inst✝¹ : Category.{v₁, u₁} J
K : Type u₂
inst✝ : Category.{v₂, u₂} K
hc : IsConnected J
this : Nonempty (ULiftHom (ULift.{u₂, u₁} J))
p : Set (ULiftHom (ULift.{u₂, u₁} J))
hj₀ : ?j₀ ∈ p
h : ∀ {j₁ j₂ : ULiftHom (ULift.{u₂, u₁} J)}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j : J
p' : Set J := {j | p { down := j }}
⊢ { down := j } ∈ p
case j₀
J : Type u₁
inst✝¹ : Category.{v₁, u₁} J
K : Type u₂
inst✝ : Category.{v₂, u₂} K
hc : IsConnected J
this : Nonempty (ULiftHom (ULift.{u₂, u₁} J))
⊢ ULiftHom (ULift.{u₂, u₁} J)

Next tactic:
have hj₀' : Classical.choice hc.is_nonempty ∈ p' := by
    simp only [(eq_self p')]
    exact hj₀
/-- Lifting the universe level of morphisms and objects preserves connectedness. -/
instance [hc : IsConnected J] : IsConnected (ULiftHom.{v₂} (ULift.{u₂} J)) := by
  have : Nonempty (ULiftHom.{v₂} (ULift.{u₂} J)) := by simp [ULiftHom, hc.is_nonempty]
  apply IsConnected.of_induct
  rintro p hj₀ h ⟨j⟩
  let p' : Set J := {j : J | p ⟨j⟩}
  have hj₀' : Classical.choice hc.is_nonempty ∈ p' := by
    

Proof state:
J : Type u₁
inst✝¹ : Category.{v₁, u₁} J
K : Type u₂
inst✝ : Category.{v₂, u₂} K
hc : IsConnected J
this : Nonempty (ULiftHom (ULift.{u₂, u₁} J))
p : Set (ULiftHom (ULift.{u₂, u₁} J))
hj₀ : ?j₀ ∈ p
h : ∀ {j₁ j₂ : ULiftHom (ULift.{u₂, u₁} J)}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j : J
p' : Set J := {j | p { down := j }}
⊢ Classical.choice (_ : Nonempty J) ∈ p'

Next tactic:
simp only [(eq_self p')]
/-- Lifting the universe level of morphisms and objects preserves connectedness. -/
instance [hc : IsConnected J] : IsConnected (ULiftHom.{v₂} (ULift.{u₂} J)) := by
  have : Nonempty (ULiftHom.{v₂} (ULift.{u₂} J)) := by simp [ULiftHom, hc.is_nonempty]
  apply IsConnected.of_induct
  rintro p hj₀ h ⟨j⟩
  let p' : Set J := {j : J | p ⟨j⟩}
  have hj₀' : Classical.choice hc.is_nonempty ∈ p' := by
    simp only [(eq_self p')]
    

Proof state:
J : Type u₁
inst✝¹ : Category.{v₁, u₁} J
K : Type u₂
inst✝ : Category.{v₂, u₂} K
hc : IsConnected J
this : Nonempty (ULiftHom (ULift.{u₂, u₁} J))
p : Set (ULiftHom (ULift.{u₂, u₁} J))
hj₀ : ?j₀ ∈ p
h : ∀ {j₁ j₂ : ULiftHom (ULift.{u₂, u₁} J)}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j : J
p' : Set J := {j | p { down := j }}
⊢ Classical.choice (_ : Nonempty J) ∈ {j | p { down := j }}

Next tactic:
exact hj₀
/-- Lifting the universe level of morphisms and objects preserves connectedness. -/
instance [hc : IsConnected J] : IsConnected (ULiftHom.{v₂} (ULift.{u₂} J)) := by
  have : Nonempty (ULiftHom.{v₂} (ULift.{u₂} J)) := by simp [ULiftHom, hc.is_nonempty]
  apply IsConnected.of_induct
  rintro p hj₀ h ⟨j⟩
  let p' : Set J := {j : J | p ⟨j⟩}
  have hj₀' : Classical.choice hc.is_nonempty ∈ p' := by
    simp only [(eq_self p')]
    exact hj₀
  

Proof state:
case h.up
J : Type u₁
inst✝¹ : Category.{v₁, u₁} J
K : Type u₂
inst✝ : Category.{v₂, u₂} K
hc : IsConnected J
this : Nonempty (ULiftHom (ULift.{u₂, u₁} J))
p : Set (ULiftHom (ULift.{u₂, u₁} J))
hj₀ : { down := Classical.choice (_ : Nonempty J) } ∈ p
h : ∀ {j₁ j₂ : ULiftHom (ULift.{u₂, u₁} J)}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j : J
p' : Set J := {j | p { down := j }}
hj₀' : Classical.choice (_ : Nonempty J) ∈ p'
⊢ { down := j } ∈ p

Next tactic:
apply induct_on_objects p' hj₀' @fun _ _ f =>
    h ((ULiftHomULiftCategory.equiv J).functor.map f)
/-- Another induction principle for `IsPreconnected J`:
given a type family `Z : J → Sort*` and
a rule for transporting in *both* directions along a morphism in `J`,
we can transport an `x : Z j₀` to a point in `Z j` for any `j`.
-/
theorem isPreconnected_induction [IsPreconnected J] (Z : J → Sort*)
    (h₁ : ∀ {j₁ j₂ : J} (_ : j₁ ⟶ j₂), Z j₁ → Z j₂) (h₂ : ∀ {j₁ j₂ : J} (_ : j₁ ⟶ j₂), Z j₂ → Z j₁)
    {j₀ : J} (x : Z j₀) (j : J) : Nonempty (Z j) :=
  (induct_on_objects { j | Nonempty (Z j) } ⟨x⟩
      (fun f =>
        ⟨by
          

Proof state:
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : IsPreconnected J
Z : J → Sort u_1
h₁ : {j₁ j₂ : J} → (j₁ ⟶ j₂) → Z j₁ → Z j₂
h₂ : {j₁ j₂ : J} → (j₁ ⟶ j₂) → Z j₂ → Z j₁
j₀ : J
x : Z j₀
j j₁✝ j₂✝ : J
f : j₁✝ ⟶ j₂✝
⊢ j₁✝ ∈ {j | Nonempty (Z j)} → j₂✝ ∈ {j | Nonempty (Z j)}

Next tactic:
rintro ⟨y⟩
/-- Another induction principle for `IsPreconnected J`:
given a type family `Z : J → Sort*` and
a rule for transporting in *both* directions along a morphism in `J`,
we can transport an `x : Z j₀` to a point in `Z j` for any `j`.
-/
theorem isPreconnected_induction [IsPreconnected J] (Z : J → Sort*)
    (h₁ : ∀ {j₁ j₂ : J} (_ : j₁ ⟶ j₂), Z j₁ → Z j₂) (h₂ : ∀ {j₁ j₂ : J} (_ : j₁ ⟶ j₂), Z j₂ → Z j₁)
    {j₀ : J} (x : Z j₀) (j : J) : Nonempty (Z j) :=
  (induct_on_objects { j | Nonempty (Z j) } ⟨x⟩
      (fun f =>
        ⟨by
          rintro ⟨y⟩
          

Proof state:
case intro
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : IsPreconnected J
Z : J → Sort u_1
h₁ : {j₁ j₂ : J} → (j₁ ⟶ j₂) → Z j₁ → Z j₂
h₂ : {j₁ j₂ : J} → (j₁ ⟶ j₂) → Z j₂ → Z j₁
j₀ : J
x : Z j₀
j j₁✝ j₂✝ : J
f : j₁✝ ⟶ j₂✝
y : Z j₁✝
⊢ j₂✝ ∈ {j | Nonempty (Z j)}

Next tactic:
exact ⟨h₁ f y⟩
/-- Another induction principle for `IsPreconnected J`:
given a type family `Z : J → Sort*` and
a rule for transporting in *both* directions along a morphism in `J`,
we can transport an `x : Z j₀` to a point in `Z j` for any `j`.
-/
theorem isPreconnected_induction [IsPreconnected J] (Z : J → Sort*)
    (h₁ : ∀ {j₁ j₂ : J} (_ : j₁ ⟶ j₂), Z j₁ → Z j₂) (h₂ : ∀ {j₁ j₂ : J} (_ : j₁ ⟶ j₂), Z j₂ → Z j₁)
    {j₀ : J} (x : Z j₀) (j : J) : Nonempty (Z j) :=
  (induct_on_objects { j | Nonempty (Z j) } ⟨x⟩
      (fun f =>
        ⟨by
          rintro ⟨y⟩
          exact ⟨h₁ f y⟩, by
          

Proof state:
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : IsPreconnected J
Z : J → Sort u_1
h₁ : {j₁ j₂ : J} → (j₁ ⟶ j₂) → Z j₁ → Z j₂
h₂ : {j₁ j₂ : J} → (j₁ ⟶ j₂) → Z j₂ → Z j₁
j₀ : J
x : Z j₀
j j₁✝ j₂✝ : J
f : j₁✝ ⟶ j₂✝
⊢ j₂✝ ∈ {j | Nonempty (Z j)} → j₁✝ ∈ {j | Nonempty (Z j)}

Next tactic:
rintro ⟨y⟩
/-- Another induction principle for `IsPreconnected J`:
given a type family `Z : J → Sort*` and
a rule for transporting in *both* directions along a morphism in `J`,
we can transport an `x : Z j₀` to a point in `Z j` for any `j`.
-/
theorem isPreconnected_induction [IsPreconnected J] (Z : J → Sort*)
    (h₁ : ∀ {j₁ j₂ : J} (_ : j₁ ⟶ j₂), Z j₁ → Z j₂) (h₂ : ∀ {j₁ j₂ : J} (_ : j₁ ⟶ j₂), Z j₂ → Z j₁)
    {j₀ : J} (x : Z j₀) (j : J) : Nonempty (Z j) :=
  (induct_on_objects { j | Nonempty (Z j) } ⟨x⟩
      (fun f =>
        ⟨by
          rintro ⟨y⟩
          exact ⟨h₁ f y⟩, by
          rintro ⟨y⟩
          

Proof state:
case intro
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : IsPreconnected J
Z : J → Sort u_1
h₁ : {j₁ j₂ : J} → (j₁ ⟶ j₂) → Z j₁ → Z j₂
h₂ : {j₁ j₂ : J} → (j₁ ⟶ j₂) → Z j₂ → Z j₁
j₀ : J
x : Z j₀
j j₁✝ j₂✝ : J
f : j₁✝ ⟶ j₂✝
y : Z j₂✝
⊢ j₁✝ ∈ {j | Nonempty (Z j)}

Next tactic:
exact ⟨h₂ f y⟩
/-- If `J` and `K` are equivalent, then if `J` is connected then `K` is as well. -/
theorem isConnected_of_equivalent {K : Type u₂} [Category.{v₂} K] (e : J ≌ K) [IsConnected J] :
    IsConnected K :=
  { is_nonempty := Nonempty.map e.functor.obj (by 

Proof state:
J : Type u₁
inst✝³ : Category.{v₁, u₁} J
K✝ : Type u₂
inst✝² : Category.{v₂, u₂} K✝
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
e : J ≌ K
inst✝ : IsConnected J
⊢ Nonempty J

Next tactic:
infer_instance
/-- Any equivalence relation containing (⟶) holds for all pairs of a connected category. -/
theorem equiv_relation [IsConnected J] (r : J → J → Prop) (hr : _root_.Equivalence r)
    (h : ∀ {j₁ j₂ : J} (_ : j₁ ⟶ j₂), r j₁ j₂) : ∀ j₁ j₂ : J, r j₁ j₂ := by
  

Proof state:
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : IsConnected J
r : J → J → Prop
hr : _root_.Equivalence r
h : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → r j₁ j₂
⊢ ∀ (j₁ j₂ : J), r j₁ j₂

Next tactic:
have z : ∀ j : J, r (Classical.arbitrary J) j :=
    induct_on_objects {k | r (Classical.arbitrary J) k} (hr.1 (Classical.arbitrary J))
      fun f => ⟨fun t => hr.3 t (h f), fun t => hr.3 t (hr.2 (h f))⟩
/-- Any equivalence relation containing (⟶) holds for all pairs of a connected category. -/
theorem equiv_relation [IsConnected J] (r : J → J → Prop) (hr : _root_.Equivalence r)
    (h : ∀ {j₁ j₂ : J} (_ : j₁ ⟶ j₂), r j₁ j₂) : ∀ j₁ j₂ : J, r j₁ j₂ := by
  have z : ∀ j : J, r (Classical.arbitrary J) j :=
    induct_on_objects {k | r (Classical.arbitrary J) k} (hr.1 (Classical.arbitrary J))
      fun f => ⟨fun t => hr.3 t (h f), fun t => hr.3 t (hr.2 (h f))⟩
  

Proof state:
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : IsConnected J
r : J → J → Prop
hr : _root_.Equivalence r
h : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → r j₁ j₂
z : ∀ (j : J), r (Classical.arbitrary J) j
⊢ ∀ (j₁ j₂ : J), r j₁ j₂

Next tactic:
intros
/-- Any equivalence relation containing (⟶) holds for all pairs of a connected category. -/
theorem equiv_relation [IsConnected J] (r : J → J → Prop) (hr : _root_.Equivalence r)
    (h : ∀ {j₁ j₂ : J} (_ : j₁ ⟶ j₂), r j₁ j₂) : ∀ j₁ j₂ : J, r j₁ j₂ := by
  have z : ∀ j : J, r (Classical.arbitrary J) j :=
    induct_on_objects {k | r (Classical.arbitrary J) k} (hr.1 (Classical.arbitrary J))
      fun f => ⟨fun t => hr.3 t (h f), fun t => hr.3 t (hr.2 (h f))⟩
  intros
  

Proof state:
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : IsConnected J
r : J → J → Prop
hr : _root_.Equivalence r
h : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → r j₁ j₂
z : ∀ (j : J), r (Classical.arbitrary J) j
j₁✝ j₂✝ : J
⊢ r j₁✝ j₂✝

Next tactic:
apply hr.3 (hr.2 (z _)) (z _)
/-- If any two objects in a nonempty category are related by `Zigzag`, the category is connected.
-/
theorem zigzag_isConnected [Nonempty J] (h : ∀ j₁ j₂ : J, Zigzag j₁ j₂) : IsConnected J := by
  

Proof state:
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
⊢ IsConnected J

Next tactic:
apply IsConnected.of_induct
/-- If any two objects in a nonempty category are related by `Zigzag`, the category is connected.
-/
theorem zigzag_isConnected [Nonempty J] (h : ∀ j₁ j₂ : J, Zigzag j₁ j₂) : IsConnected J := by
  apply IsConnected.of_induct
  

Proof state:
case h
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
⊢ ∀ (p : Set J), ?j₀ ∈ p → (∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)) → ∀ (j : J), j ∈ p
case j₀
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
⊢ J

Next tactic:
intro p hp hjp j
/-- If any two objects in a nonempty category are related by `Zigzag`, the category is connected.
-/
theorem zigzag_isConnected [Nonempty J] (h : ∀ j₁ j₂ : J, Zigzag j₁ j₂) : IsConnected J := by
  apply IsConnected.of_induct
  intro p hp hjp j
  

Proof state:
case h
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
p : Set J
hp : ?j₀ ∈ p
hjp : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j : J
⊢ j ∈ p
case j₀
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
⊢ J

Next tactic:
have : ∀ j₁ j₂ : J, Zigzag j₁ j₂ → (j₁ ∈ p ↔ j₂ ∈ p) := by
    introv k
    induction' k with _ _ _ zag k_ih
    · rfl
    · rw [k_ih]
      cases' zag with zag zag
      apply hjp (Nonempty.some zag)
      exact (hjp (Nonempty.some zag)).symm
/-- If any two objects in a nonempty category are related by `Zigzag`, the category is connected.
-/
theorem zigzag_isConnected [Nonempty J] (h : ∀ j₁ j₂ : J, Zigzag j₁ j₂) : IsConnected J := by
  apply IsConnected.of_induct
  intro p hp hjp j
  have : ∀ j₁ j₂ : J, Zigzag j₁ j₂ → (j₁ ∈ p ↔ j₂ ∈ p) := by
    

Proof state:
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
p : Set J
hp : ?j₀ ∈ p
hjp : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j : J
⊢ ∀ (j₁ j₂ : J), Zigzag j₁ j₂ → (j₁ ∈ p ↔ j₂ ∈ p)

Next tactic:
introv k
/-- If any two objects in a nonempty category are related by `Zigzag`, the category is connected.
-/
theorem zigzag_isConnected [Nonempty J] (h : ∀ j₁ j₂ : J, Zigzag j₁ j₂) : IsConnected J := by
  apply IsConnected.of_induct
  intro p hp hjp j
  have : ∀ j₁ j₂ : J, Zigzag j₁ j₂ → (j₁ ∈ p ↔ j₂ ∈ p) := by
    introv k
    

Proof state:
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
p : Set J
hp : ?j₀ ∈ p
hjp : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j j₁ j₂ : J
k : Zigzag j₁ j₂
⊢ j₁ ∈ p ↔ j₂ ∈ p

Next tactic:
induction' k with _ _ _ zag k_ih
/-- If any two objects in a nonempty category are related by `Zigzag`, the category is connected.
-/
theorem zigzag_isConnected [Nonempty J] (h : ∀ j₁ j₂ : J, Zigzag j₁ j₂) : IsConnected J := by
  apply IsConnected.of_induct
  intro p hp hjp j
  have : ∀ j₁ j₂ : J, Zigzag j₁ j₂ → (j₁ ∈ p ↔ j₂ ∈ p) := by
    introv k
    induction' k with _ _ _ zag k_ih
    · 

Proof state:
case refl
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
p : Set J
hp : ?j₀ ∈ p
hjp : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j j₁ j₂ : J
⊢ j₁ ∈ p ↔ j₁ ∈ p

Next tactic:
rfl
/-- If any two objects in a nonempty category are related by `Zigzag`, the category is connected.
-/
theorem zigzag_isConnected [Nonempty J] (h : ∀ j₁ j₂ : J, Zigzag j₁ j₂) : IsConnected J := by
  apply IsConnected.of_induct
  intro p hp hjp j
  have : ∀ j₁ j₂ : J, Zigzag j₁ j₂ → (j₁ ∈ p ↔ j₂ ∈ p) := by
    introv k
    induction' k with _ _ _ zag k_ih
    · rfl
    · 

Proof state:
case tail
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
p : Set J
hp : ?j₀ ∈ p
hjp : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j j₁ j₂ b✝ c✝ : J
a✝ : Relation.ReflTransGen Zag j₁ b✝
zag : Zag b✝ c✝
k_ih : j₁ ∈ p ↔ b✝ ∈ p
⊢ j₁ ∈ p ↔ c✝ ∈ p

Next tactic:
rw [k_ih]
/-- If any two objects in a nonempty category are related by `Zigzag`, the category is connected.
-/
theorem zigzag_isConnected [Nonempty J] (h : ∀ j₁ j₂ : J, Zigzag j₁ j₂) : IsConnected J := by
  apply IsConnected.of_induct
  intro p hp hjp j
  have : ∀ j₁ j₂ : J, Zigzag j₁ j₂ → (j₁ ∈ p ↔ j₂ ∈ p) := by
    introv k
    induction' k with _ _ _ zag k_ih
    · rfl
    · rw [k_ih]
      

Proof state:
case tail
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
p : Set J
hp : ?j₀ ∈ p
hjp : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j j₁ j₂ b✝ c✝ : J
a✝ : Relation.ReflTransGen Zag j₁ b✝
zag : Zag b✝ c✝
k_ih : j₁ ∈ p ↔ b✝ ∈ p
⊢ b✝ ∈ p ↔ c✝ ∈ p

Next tactic:
cases' zag with zag zag
/-- If any two objects in a nonempty category are related by `Zigzag`, the category is connected.
-/
theorem zigzag_isConnected [Nonempty J] (h : ∀ j₁ j₂ : J, Zigzag j₁ j₂) : IsConnected J := by
  apply IsConnected.of_induct
  intro p hp hjp j
  have : ∀ j₁ j₂ : J, Zigzag j₁ j₂ → (j₁ ∈ p ↔ j₂ ∈ p) := by
    introv k
    induction' k with _ _ _ zag k_ih
    · rfl
    · rw [k_ih]
      cases' zag with zag zag
      

Proof state:
case tail.inl
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
p : Set J
hp : ?j₀ ∈ p
hjp : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j j₁ j₂ b✝ c✝ : J
a✝ : Relation.ReflTransGen Zag j₁ b✝
k_ih : j₁ ∈ p ↔ b✝ ∈ p
zag : Nonempty (b✝ ⟶ c✝)
⊢ b✝ ∈ p ↔ c✝ ∈ p
case tail.inr
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
p : Set J
hp : ?j₀ ∈ p
hjp : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j j₁ j₂ b✝ c✝ : J
a✝ : Relation.ReflTransGen Zag j₁ b✝
k_ih : j₁ ∈ p ↔ b✝ ∈ p
zag : Nonempty (c✝ ⟶ b✝)
⊢ b✝ ∈ p ↔ c✝ ∈ p

Next tactic:
apply hjp (Nonempty.some zag)
/-- If any two objects in a nonempty category are related by `Zigzag`, the category is connected.
-/
theorem zigzag_isConnected [Nonempty J] (h : ∀ j₁ j₂ : J, Zigzag j₁ j₂) : IsConnected J := by
  apply IsConnected.of_induct
  intro p hp hjp j
  have : ∀ j₁ j₂ : J, Zigzag j₁ j₂ → (j₁ ∈ p ↔ j₂ ∈ p) := by
    introv k
    induction' k with _ _ _ zag k_ih
    · rfl
    · rw [k_ih]
      cases' zag with zag zag
      apply hjp (Nonempty.some zag)
      

Proof state:
case tail.inr
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
p : Set J
hp : ?j₀ ∈ p
hjp : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j j₁ j₂ b✝ c✝ : J
a✝ : Relation.ReflTransGen Zag j₁ b✝
k_ih : j₁ ∈ p ↔ b✝ ∈ p
zag : Nonempty (c✝ ⟶ b✝)
⊢ b✝ ∈ p ↔ c✝ ∈ p

Next tactic:
exact (hjp (Nonempty.some zag)).symm
/-- If any two objects in a nonempty category are related by `Zigzag`, the category is connected.
-/
theorem zigzag_isConnected [Nonempty J] (h : ∀ j₁ j₂ : J, Zigzag j₁ j₂) : IsConnected J := by
  apply IsConnected.of_induct
  intro p hp hjp j
  have : ∀ j₁ j₂ : J, Zigzag j₁ j₂ → (j₁ ∈ p ↔ j₂ ∈ p) := by
    introv k
    induction' k with _ _ _ zag k_ih
    · rfl
    · rw [k_ih]
      cases' zag with zag zag
      apply hjp (Nonempty.some zag)
      exact (hjp (Nonempty.some zag)).symm
  

Proof state:
case h
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
p : Set J
hp : ?j₀ ∈ p
hjp : ∀ {j₁ j₂ : J}, (j₁ ⟶ j₂) → (j₁ ∈ p ↔ j₂ ∈ p)
j : J
this : ∀ (j₁ j₂ : J), Zigzag j₁ j₂ → (j₁ ∈ p ↔ j₂ ∈ p)
⊢ j ∈ p
case j₀
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), Zigzag j₁ j₂
⊢ J

Next tactic:
rwa [this j (Classical.arbitrary J) (h _ _)]
/-- If any two objects in a nonempty category are linked by a sequence of (potentially reversed)
morphisms, then J is connected.

The converse of `exists_zigzag'`.
-/
theorem isConnected_of_zigzag [Nonempty J] (h : ∀ j₁ j₂ : J, ∃ l,
    List.Chain Zag j₁ l ∧ List.getLast (j₁ :: l) (List.cons_ne_nil _ _) = j₂) :
    IsConnected J := by
  

Proof state:
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), ∃ l, List.Chain Zag j₁ l ∧ List.getLast (j₁ :: l) (_ : j₁ :: l ≠ []) = j₂
⊢ IsConnected J

Next tactic:
apply zigzag_isConnected
/-- If any two objects in a nonempty category are linked by a sequence of (potentially reversed)
morphisms, then J is connected.

The converse of `exists_zigzag'`.
-/
theorem isConnected_of_zigzag [Nonempty J] (h : ∀ j₁ j₂ : J, ∃ l,
    List.Chain Zag j₁ l ∧ List.getLast (j₁ :: l) (List.cons_ne_nil _ _) = j₂) :
    IsConnected J := by
  apply zigzag_isConnected
  

Proof state:
case h
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), ∃ l, List.Chain Zag j₁ l ∧ List.getLast (j₁ :: l) (_ : j₁ :: l ≠ []) = j₂
⊢ ∀ (j₁ j₂ : J), Zigzag j₁ j₂

Next tactic:
intro j₁ j₂
/-- If any two objects in a nonempty category are linked by a sequence of (potentially reversed)
morphisms, then J is connected.

The converse of `exists_zigzag'`.
-/
theorem isConnected_of_zigzag [Nonempty J] (h : ∀ j₁ j₂ : J, ∃ l,
    List.Chain Zag j₁ l ∧ List.getLast (j₁ :: l) (List.cons_ne_nil _ _) = j₂) :
    IsConnected J := by
  apply zigzag_isConnected
  intro j₁ j₂
  

Proof state:
case h
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), ∃ l, List.Chain Zag j₁ l ∧ List.getLast (j₁ :: l) (_ : j₁ :: l ≠ []) = j₂
j₁ j₂ : J
⊢ Zigzag j₁ j₂

Next tactic:
rcases h j₁ j₂ with ⟨l, hl₁, hl₂⟩
/-- If any two objects in a nonempty category are linked by a sequence of (potentially reversed)
morphisms, then J is connected.

The converse of `exists_zigzag'`.
-/
theorem isConnected_of_zigzag [Nonempty J] (h : ∀ j₁ j₂ : J, ∃ l,
    List.Chain Zag j₁ l ∧ List.getLast (j₁ :: l) (List.cons_ne_nil _ _) = j₂) :
    IsConnected J := by
  apply zigzag_isConnected
  intro j₁ j₂
  rcases h j₁ j₂ with ⟨l, hl₁, hl₂⟩
  

Proof state:
case h.intro.intro
J : Type u₁
inst✝² : Category.{v₁, u₁} J
K : Type u₂
inst✝¹ : Category.{v₂, u₂} K
inst✝ : Nonempty J
h : ∀ (j₁ j₂ : J), ∃ l, List.Chain Zag j₁ l ∧ List.getLast (j₁ :: l) (_ : j₁ :: l ≠ []) = j₂
j₁ j₂ : J
l : List J
hl₁ : List.Chain Zag j₁ l
hl₂ : List.getLast (j₁ :: l) (_ : j₁ :: l ≠ []) = j₂
⊢ Zigzag j₁ j₂

Next tactic:
apply List.relationReflTransGen_of_exists_chain l hl₁ hl₂