l3lab/ntp-mathlib huggingface dataset:
/-- We show that the presheaf of functions to a type `T`
(no continuity assumptions, just plain functions)
form a sheaf.

In fact, the proof is identical when we do this for dependent functions to a type family `T`,
so we do the more general case.
-/
theorem toTypes_isSheaf (T : X → Type u) : (presheafToTypes X T).IsSheaf :=
  isSheaf_of_isSheafUniqueGluing_types.{u} _ fun ι U sf hsf => by
  -- We use the sheaf condition in terms of unique gluing
  -- U is a family of open sets, indexed by `ι` and `sf` is a compatible family of sections.
  -- In the informal comments below, I'll just write `U` to represent the union.
    -- Our first goal is to define a function "lifted" to all of `U`.
    -- We do this one point at a time. Using the axiom of choice, we can pick for each
    -- `x : ↑(iSup U)` an index `i : ι` such that `x` lies in `U i`
    choose index index_spec using fun x : ↑(iSup U) => Opens.mem_iSup.mp x.2
    -- Using this data, we can glue our functions together to a single section
    let s : ∀ x : ↑(iSup U), T x := fun x => sf (index x) ⟨x.1, index_spec x⟩
    refine' ⟨s, _, _⟩
    · 

Proof state:
case refine'_1
X : TopCat
T : ↑X → Type u
ι : Type u
U : ι → Opens ↑X
sf : (i : ι) → (forget (Type u)).obj ((presheafToTypes X T).obj (Opposite.op (U i)))
hsf : IsCompatible (presheafToTypes X T) U sf
index : ↥(iSup U) → ι
index_spec : ∀ (x : ↥(iSup U)), ↑x ∈ U (index x)
s : (x : ↥(iSup U)) → T ↑x := fun x => sf (index x) { val := ↑x, property := (_ : ↑x ∈ U (index x)) }
⊢ (fun s => IsGluing (presheafToTypes X T) U sf s) s

Next tactic:
intro i
/-- We show that the presheaf of functions to a type `T`
(no continuity assumptions, just plain functions)
form a sheaf.

In fact, the proof is identical when we do this for dependent functions to a type family `T`,
so we do the more general case.
-/
theorem toTypes_isSheaf (T : X → Type u) : (presheafToTypes X T).IsSheaf :=
  isSheaf_of_isSheafUniqueGluing_types.{u} _ fun ι U sf hsf => by
  -- We use the sheaf condition in terms of unique gluing
  -- U is a family of open sets, indexed by `ι` and `sf` is a compatible family of sections.
  -- In the informal comments below, I'll just write `U` to represent the union.
    -- Our first goal is to define a function "lifted" to all of `U`.
    -- We do this one point at a time. Using the axiom of choice, we can pick for each
    -- `x : ↑(iSup U)` an index `i : ι` such that `x` lies in `U i`
    choose index index_spec using fun x : ↑(iSup U) => Opens.mem_iSup.mp x.2
    -- Using this data, we can glue our functions together to a single section
    let s : ∀ x : ↑(iSup U), T x := fun x => sf (index x) ⟨x.1, index_spec x⟩
    refine' ⟨s, _, _⟩
    · intro i
      

Proof state:
case refine'_1
X : TopCat
T : ↑X → Type u
ι : Type u
U : ι → Opens ↑X
sf : (i : ι) → (forget (Type u)).obj ((presheafToTypes X T).obj (Opposite.op (U i)))
hsf : IsCompatible (presheafToTypes X T) U sf
index : ↥(iSup U) → ι
index_spec : ∀ (x : ↥(iSup U)), ↑x ∈ U (index x)
s : (x : ↥(iSup U)) → T ↑x := fun x => sf (index x) { val := ↑x, property := (_ : ↑x ∈ U (index x)) }
i : ι
⊢ ((presheafToTypes X T).map (leSupr U i).op) s = sf i

Next tactic:
funext x
/-- We show that the presheaf of functions to a type `T`
(no continuity assumptions, just plain functions)
form a sheaf.

In fact, the proof is identical when we do this for dependent functions to a type family `T`,
so we do the more general case.
-/
theorem toTypes_isSheaf (T : X → Type u) : (presheafToTypes X T).IsSheaf :=
  isSheaf_of_isSheafUniqueGluing_types.{u} _ fun ι U sf hsf => by
  -- We use the sheaf condition in terms of unique gluing
  -- U is a family of open sets, indexed by `ι` and `sf` is a compatible family of sections.
  -- In the informal comments below, I'll just write `U` to represent the union.
    -- Our first goal is to define a function "lifted" to all of `U`.
    -- We do this one point at a time. Using the axiom of choice, we can pick for each
    -- `x : ↑(iSup U)` an index `i : ι` such that `x` lies in `U i`
    choose index index_spec using fun x : ↑(iSup U) => Opens.mem_iSup.mp x.2
    -- Using this data, we can glue our functions together to a single section
    let s : ∀ x : ↑(iSup U), T x := fun x => sf (index x) ⟨x.1, index_spec x⟩
    refine' ⟨s, _, _⟩
    · intro i
      funext x
      -- Now we need to verify that this lifted function restricts correctly to each set `U i`.
      -- Of course, the difficulty is that at any given point `x ∈ U i`,
      -- we may have used the axiom of choice to pick a different `j` with `x ∈ U j`
      -- when defining the function.
      -- Thus we'll need to use the fact that the restrictions are compatible.
      

Proof state:
case refine'_1.h
X : TopCat
T : ↑X → Type u
ι : Type u
U : ι → Opens ↑X
sf : (i : ι) → (forget (Type u)).obj ((presheafToTypes X T).obj (Opposite.op (U i)))
hsf : IsCompatible (presheafToTypes X T) U sf
index : ↥(iSup U) → ι
index_spec : ∀ (x : ↥(iSup U)), ↑x ∈ U (index x)
s : (x : ↥(iSup U)) → T ↑x := fun x => sf (index x) { val := ↑x, property := (_ : ↑x ∈ U (index x)) }
i : ι
x : ↥(Opposite.op (U i)).unop
⊢ ((presheafToTypes X T).map (leSupr U i).op) s x = sf i x

Next tactic:
exact congr_fun (hsf (index ⟨x, _⟩) i) ⟨x, ⟨index_spec ⟨x.1, _⟩, x.2⟩⟩
/-- We show that the presheaf of functions to a type `T`
(no continuity assumptions, just plain functions)
form a sheaf.

In fact, the proof is identical when we do this for dependent functions to a type family `T`,
so we do the more general case.
-/
theorem toTypes_isSheaf (T : X → Type u) : (presheafToTypes X T).IsSheaf :=
  isSheaf_of_isSheafUniqueGluing_types.{u} _ fun ι U sf hsf => by
  -- We use the sheaf condition in terms of unique gluing
  -- U is a family of open sets, indexed by `ι` and `sf` is a compatible family of sections.
  -- In the informal comments below, I'll just write `U` to represent the union.
    -- Our first goal is to define a function "lifted" to all of `U`.
    -- We do this one point at a time. Using the axiom of choice, we can pick for each
    -- `x : ↑(iSup U)` an index `i : ι` such that `x` lies in `U i`
    choose index index_spec using fun x : ↑(iSup U) => Opens.mem_iSup.mp x.2
    -- Using this data, we can glue our functions together to a single section
    let s : ∀ x : ↑(iSup U), T x := fun x => sf (index x) ⟨x.1, index_spec x⟩
    refine' ⟨s, _, _⟩
    · intro i
      funext x
      -- Now we need to verify that this lifted function restricts correctly to each set `U i`.
      -- Of course, the difficulty is that at any given point `x ∈ U i`,
      -- we may have used the axiom of choice to pick a different `j` with `x ∈ U j`
      -- when defining the function.
      -- Thus we'll need to use the fact that the restrictions are compatible.
      exact congr_fun (hsf (index ⟨x, _⟩) i) ⟨x, ⟨index_spec ⟨x.1, _⟩, x.2⟩⟩
    · -- Now we just need to check that the lift we picked was the only possible one.
      -- So we suppose we had some other gluing `t` of our sections
      

Proof state:
case refine'_2
X : TopCat
T : ↑X → Type u
ι : Type u
U : ι → Opens ↑X
sf : (i : ι) → (forget (Type u)).obj ((presheafToTypes X T).obj (Opposite.op (U i)))
hsf : IsCompatible (presheafToTypes X T) U sf
index : ↥(iSup U) → ι
index_spec : ∀ (x : ↥(iSup U)), ↑x ∈ U (index x)
s : (x : ↥(iSup U)) → T ↑x := fun x => sf (index x) { val := ↑x, property := (_ : ↑x ∈ U (index x)) }
⊢ ∀ (y : (forget (Type u)).obj ((presheafToTypes X T).obj (Opposite.op (iSup U)))),
    (fun s => IsGluing (presheafToTypes X T) U sf s) y → y = s

Next tactic:
intro t ht
/-- We show that the presheaf of functions to a type `T`
(no continuity assumptions, just plain functions)
form a sheaf.

In fact, the proof is identical when we do this for dependent functions to a type family `T`,
so we do the more general case.
-/
theorem toTypes_isSheaf (T : X → Type u) : (presheafToTypes X T).IsSheaf :=
  isSheaf_of_isSheafUniqueGluing_types.{u} _ fun ι U sf hsf => by
  -- We use the sheaf condition in terms of unique gluing
  -- U is a family of open sets, indexed by `ι` and `sf` is a compatible family of sections.
  -- In the informal comments below, I'll just write `U` to represent the union.
    -- Our first goal is to define a function "lifted" to all of `U`.
    -- We do this one point at a time. Using the axiom of choice, we can pick for each
    -- `x : ↑(iSup U)` an index `i : ι` such that `x` lies in `U i`
    choose index index_spec using fun x : ↑(iSup U) => Opens.mem_iSup.mp x.2
    -- Using this data, we can glue our functions together to a single section
    let s : ∀ x : ↑(iSup U), T x := fun x => sf (index x) ⟨x.1, index_spec x⟩
    refine' ⟨s, _, _⟩
    · intro i
      funext x
      -- Now we need to verify that this lifted function restricts correctly to each set `U i`.
      -- Of course, the difficulty is that at any given point `x ∈ U i`,
      -- we may have used the axiom of choice to pick a different `j` with `x ∈ U j`
      -- when defining the function.
      -- Thus we'll need to use the fact that the restrictions are compatible.
      exact congr_fun (hsf (index ⟨x, _⟩) i) ⟨x, ⟨index_spec ⟨x.1, _⟩, x.2⟩⟩
    · -- Now we just need to check that the lift we picked was the only possible one.
      -- So we suppose we had some other gluing `t` of our sections
      intro t ht
      -- and observe that we need to check that it agrees with our choice
      -- for each `x ∈ ↑(iSup U)`.
      

Proof state:
case refine'_2
X : TopCat
T : ↑X → Type u
ι : Type u
U : ι → Opens ↑X
sf : (i : ι) → (forget (Type u)).obj ((presheafToTypes X T).obj (Opposite.op (U i)))
hsf : IsCompatible (presheafToTypes X T) U sf
index : ↥(iSup U) → ι
index_spec : ∀ (x : ↥(iSup U)), ↑x ∈ U (index x)
s : (x : ↥(iSup U)) → T ↑x := fun x => sf (index x) { val := ↑x, property := (_ : ↑x ∈ U (index x)) }
t : (forget (Type u)).obj ((presheafToTypes X T).obj (Opposite.op (iSup U)))
ht : IsGluing (presheafToTypes X T) U sf t
⊢ t = s

Next tactic:
funext x
/-- We show that the presheaf of functions to a type `T`
(no continuity assumptions, just plain functions)
form a sheaf.

In fact, the proof is identical when we do this for dependent functions to a type family `T`,
so we do the more general case.
-/
theorem toTypes_isSheaf (T : X → Type u) : (presheafToTypes X T).IsSheaf :=
  isSheaf_of_isSheafUniqueGluing_types.{u} _ fun ι U sf hsf => by
  -- We use the sheaf condition in terms of unique gluing
  -- U is a family of open sets, indexed by `ι` and `sf` is a compatible family of sections.
  -- In the informal comments below, I'll just write `U` to represent the union.
    -- Our first goal is to define a function "lifted" to all of `U`.
    -- We do this one point at a time. Using the axiom of choice, we can pick for each
    -- `x : ↑(iSup U)` an index `i : ι` such that `x` lies in `U i`
    choose index index_spec using fun x : ↑(iSup U) => Opens.mem_iSup.mp x.2
    -- Using this data, we can glue our functions together to a single section
    let s : ∀ x : ↑(iSup U), T x := fun x => sf (index x) ⟨x.1, index_spec x⟩
    refine' ⟨s, _, _⟩
    · intro i
      funext x
      -- Now we need to verify that this lifted function restricts correctly to each set `U i`.
      -- Of course, the difficulty is that at any given point `x ∈ U i`,
      -- we may have used the axiom of choice to pick a different `j` with `x ∈ U j`
      -- when defining the function.
      -- Thus we'll need to use the fact that the restrictions are compatible.
      exact congr_fun (hsf (index ⟨x, _⟩) i) ⟨x, ⟨index_spec ⟨x.1, _⟩, x.2⟩⟩
    · -- Now we just need to check that the lift we picked was the only possible one.
      -- So we suppose we had some other gluing `t` of our sections
      intro t ht
      -- and observe that we need to check that it agrees with our choice
      -- for each `x ∈ ↑(iSup U)`.
      funext x
      

Proof state:
case refine'_2.h
X : TopCat
T : ↑X → Type u
ι : Type u
U : ι → Opens ↑X
sf : (i : ι) → (forget (Type u)).obj ((presheafToTypes X T).obj (Opposite.op (U i)))
hsf : IsCompatible (presheafToTypes X T) U sf
index : ↥(iSup U) → ι
index_spec : ∀ (x : ↥(iSup U)), ↑x ∈ U (index x)
s : (x : ↥(iSup U)) → T ↑x := fun x => sf (index x) { val := ↑x, property := (_ : ↑x ∈ U (index x)) }
t : (forget (Type u)).obj ((presheafToTypes X T).obj (Opposite.op (iSup U)))
ht : IsGluing (presheafToTypes X T) U sf t
x : ↥(Opposite.op (iSup U)).unop
⊢ t x = s x

Next tactic:
exact congr_fun (ht (index x)) ⟨x.1, index_spec x⟩
theorem isAcyclic_iff_forall_adj_isBridge :
    G.IsAcyclic ↔ ∀ ⦃v w : V⦄, G.Adj v w → G.IsBridge ⟦(v, w)⟧ := by
  

Proof state:
V : Type u
G : SimpleGraph V
⊢ IsAcyclic G ↔ ∀ ⦃v w : V⦄, Adj G v w → IsBridge G ⟦(v, w)⟧

Next tactic:
simp_rw [isBridge_iff_adj_and_forall_cycle_not_mem]
theorem isAcyclic_iff_forall_adj_isBridge :
    G.IsAcyclic ↔ ∀ ⦃v w : V⦄, G.Adj v w → G.IsBridge ⟦(v, w)⟧ := by
  simp_rw [isBridge_iff_adj_and_forall_cycle_not_mem]
  

Proof state:
V : Type u
G : SimpleGraph V
⊢ IsAcyclic G ↔ ∀ ⦃v w : V⦄, Adj G v w → Adj G v w ∧ ∀ ⦃u : V⦄ (p : Walk G u u), IsCycle p → ⟦(v, w)⟧ ∉ edges p

Next tactic:
constructor
theorem isAcyclic_iff_forall_adj_isBridge :
    G.IsAcyclic ↔ ∀ ⦃v w : V⦄, G.Adj v w → G.IsBridge ⟦(v, w)⟧ := by
  simp_rw [isBridge_iff_adj_and_forall_cycle_not_mem]
  constructor
  · 

Proof state:
case mp
V : Type u
G : SimpleGraph V
⊢ IsAcyclic G → ∀ ⦃v w : V⦄, Adj G v w → Adj G v w ∧ ∀ ⦃u : V⦄ (p : Walk G u u), IsCycle p → ⟦(v, w)⟧ ∉ edges p

Next tactic:
intro ha v w hvw
theorem isAcyclic_iff_forall_adj_isBridge :
    G.IsAcyclic ↔ ∀ ⦃v w : V⦄, G.Adj v w → G.IsBridge ⟦(v, w)⟧ := by
  simp_rw [isBridge_iff_adj_and_forall_cycle_not_mem]
  constructor
  · intro ha v w hvw
    

Proof state:
case mp
V : Type u
G : SimpleGraph V
ha : IsAcyclic G
v w : V
hvw : Adj G v w
⊢ Adj G v w ∧ ∀ ⦃u : V⦄ (p : Walk G u u), IsCycle p → ⟦(v, w)⟧ ∉ edges p

Next tactic:
apply And.intro hvw
theorem isAcyclic_iff_forall_adj_isBridge :
    G.IsAcyclic ↔ ∀ ⦃v w : V⦄, G.Adj v w → G.IsBridge ⟦(v, w)⟧ := by
  simp_rw [isBridge_iff_adj_and_forall_cycle_not_mem]
  constructor
  · intro ha v w hvw
    apply And.intro hvw
    

Proof state:
case mp
V : Type u
G : SimpleGraph V
ha : IsAcyclic G
v w : V
hvw : Adj G v w
⊢ ∀ ⦃u : V⦄ (p : Walk G u u), IsCycle p → ⟦(v, w)⟧ ∉ edges p

Next tactic:
intro u p hp
theorem isAcyclic_iff_forall_adj_isBridge :
    G.IsAcyclic ↔ ∀ ⦃v w : V⦄, G.Adj v w → G.IsBridge ⟦(v, w)⟧ := by
  simp_rw [isBridge_iff_adj_and_forall_cycle_not_mem]
  constructor
  · intro ha v w hvw
    apply And.intro hvw
    intro u p hp
    

Proof state:
case mp
V : Type u
G : SimpleGraph V
ha : IsAcyclic G
v w : V
hvw : Adj G v w
u : V
p : Walk G u u
hp : IsCycle p
⊢ ⟦(v, w)⟧ ∉ edges p

Next tactic:
cases ha p hp
theorem isAcyclic_iff_forall_adj_isBridge :
    G.IsAcyclic ↔ ∀ ⦃v w : V⦄, G.Adj v w → G.IsBridge ⟦(v, w)⟧ := by
  simp_rw [isBridge_iff_adj_and_forall_cycle_not_mem]
  constructor
  · intro ha v w hvw
    apply And.intro hvw
    intro u p hp
    cases ha p hp
  · 

Proof state:
case mpr
V : Type u
G : SimpleGraph V
⊢ (∀ ⦃v w : V⦄, Adj G v w → Adj G v w ∧ ∀ ⦃u : V⦄ (p : Walk G u u), IsCycle p → ⟦(v, w)⟧ ∉ edges p) → IsAcyclic G

Next tactic:
rintro hb v (_ | ⟨ha, p⟩) hp
theorem isAcyclic_iff_forall_adj_isBridge :
    G.IsAcyclic ↔ ∀ ⦃v w : V⦄, G.Adj v w → G.IsBridge ⟦(v, w)⟧ := by
  simp_rw [isBridge_iff_adj_and_forall_cycle_not_mem]
  constructor
  · intro ha v w hvw
    apply And.intro hvw
    intro u p hp
    cases ha p hp
  · rintro hb v (_ | ⟨ha, p⟩) hp
    · 

Proof state:
case mpr.nil
V : Type u
G : SimpleGraph V
hb : ∀ ⦃v w : V⦄, Adj G v w → Adj G v w ∧ ∀ ⦃u : V⦄ (p : Walk G u u), IsCycle p → ⟦(v, w)⟧ ∉ edges p
v : V
hp : IsCycle nil
⊢ False

Next tactic:
exact hp.not_of_nil
theorem isAcyclic_iff_forall_adj_isBridge :
    G.IsAcyclic ↔ ∀ ⦃v w : V⦄, G.Adj v w → G.IsBridge ⟦(v, w)⟧ := by
  simp_rw [isBridge_iff_adj_and_forall_cycle_not_mem]
  constructor
  · intro ha v w hvw
    apply And.intro hvw
    intro u p hp
    cases ha p hp
  · rintro hb v (_ | ⟨ha, p⟩) hp
    · exact hp.not_of_nil
    · 

Proof state:
case mpr.cons
V : Type u
G : SimpleGraph V
hb : ∀ ⦃v w : V⦄, Adj G v w → Adj G v w ∧ ∀ ⦃u : V⦄ (p : Walk G u u), IsCycle p → ⟦(v, w)⟧ ∉ edges p
v v✝ : V
ha : Adj G v v✝
p : Walk G v✝ v
hp : IsCycle (cons ha p)
⊢ False

Next tactic:
apply (hb ha).2 _ hp
theorem isAcyclic_iff_forall_adj_isBridge :
    G.IsAcyclic ↔ ∀ ⦃v w : V⦄, G.Adj v w → G.IsBridge ⟦(v, w)⟧ := by
  simp_rw [isBridge_iff_adj_and_forall_cycle_not_mem]
  constructor
  · intro ha v w hvw
    apply And.intro hvw
    intro u p hp
    cases ha p hp
  · rintro hb v (_ | ⟨ha, p⟩) hp
    · exact hp.not_of_nil
    · apply (hb ha).2 _ hp
      

Proof state:
case mpr.cons
V : Type u
G : SimpleGraph V
hb : ∀ ⦃v w : V⦄, Adj G v w → Adj G v w ∧ ∀ ⦃u : V⦄ (p : Walk G u u), IsCycle p → ⟦(v, w)⟧ ∉ edges p
v v✝ : V
ha : Adj G v v✝
p : Walk G v✝ v
hp : IsCycle (cons ha p)
⊢ ⟦(v, v✝)⟧ ∈ edges (cons ha p)

Next tactic:
rw [Walk.edges_cons]
theorem isAcyclic_iff_forall_adj_isBridge :
    G.IsAcyclic ↔ ∀ ⦃v w : V⦄, G.Adj v w → G.IsBridge ⟦(v, w)⟧ := by
  simp_rw [isBridge_iff_adj_and_forall_cycle_not_mem]
  constructor
  · intro ha v w hvw
    apply And.intro hvw
    intro u p hp
    cases ha p hp
  · rintro hb v (_ | ⟨ha, p⟩) hp
    · exact hp.not_of_nil
    · apply (hb ha).2 _ hp
      rw [Walk.edges_cons]
      

Proof state:
case mpr.cons
V : Type u
G : SimpleGraph V
hb : ∀ ⦃v w : V⦄, Adj G v w → Adj G v w ∧ ∀ ⦃u : V⦄ (p : Walk G u u), IsCycle p → ⟦(v, w)⟧ ∉ edges p
v v✝ : V
ha : Adj G v v✝
p : Walk G v✝ v
hp : IsCycle (cons ha p)
⊢ ⟦(v, v✝)⟧ ∈ ⟦(v, v✝)⟧ :: edges p

Next tactic:
apply List.mem_cons_self
theorem isAcyclic_iff_forall_edge_isBridge :
    G.IsAcyclic ↔ ∀ ⦃e⦄, e ∈ (G.edgeSet) → G.IsBridge e := by
  

Proof state:
V : Type u
G : SimpleGraph V
⊢ IsAcyclic G ↔ ∀ ⦃e : Sym2 V⦄, e ∈ edgeSet G → IsBridge G e

Next tactic:
simp [isAcyclic_iff_forall_adj_isBridge, Sym2.forall]
theorem IsAcyclic.path_unique {G : SimpleGraph V} (h : G.IsAcyclic) {v w : V} (p q : G.Path v w) :
    p = q := by
  

Proof state:
V : Type u
G✝ G : SimpleGraph V
h : IsAcyclic G
v w : V
p q : Path G v w
⊢ p = q

Next tactic:
obtain ⟨p, hp⟩ := p
theorem IsAcyclic.path_unique {G : SimpleGraph V} (h : G.IsAcyclic) {v w : V} (p q : G.Path v w) :
    p = q := by
  obtain ⟨p, hp⟩ := p
  

Proof state:
case mk
V : Type u
G✝ G : SimpleGraph V
h : IsAcyclic G
v w : V
q : Path G v w
p : Walk G v w
hp : IsPath p
⊢ { val := p, property := hp } = q

Next tactic:
obtain ⟨q, hq⟩ := q
theorem IsAcyclic.path_unique {G : SimpleGraph V} (h : G.IsAcyclic) {v w : V} (p q : G.Path v w) :
    p = q := by
  obtain ⟨p, hp⟩ := p
  obtain ⟨q, hq⟩ := q
  

Proof state:
case mk.mk
V : Type u
G✝ G : SimpleGraph V
h : IsAcyclic G
v w : V
p : Walk G v w
hp : IsPath p
q : Walk G v w
hq : IsPath q
⊢ { val := p, property := hp } = { val := q, property := hq }

Next tactic:
rw [Subtype.mk.injEq]
theorem IsAcyclic.path_unique {G : SimpleGraph V} (h : G.IsAcyclic) {v w : V} (p q : G.Path v w) :
    p = q := by
  obtain ⟨p, hp⟩ := p
  obtain ⟨q, hq⟩ := q
  rw [Subtype.mk.injEq]
  

Proof state:
case mk.mk
V : Type u
G✝ G : SimpleGraph V
h : IsAcyclic G
v w : V
p : Walk G v w
hp : IsPath p
q : Walk G v w
hq : IsPath q
⊢ p = q

Next tactic:
induction p with
  | nil =>
    cases (Walk.isPath_iff_eq_nil _).mp hq
    rfl
  | cons ph p ih =>
    rw [isAcyclic_iff_forall_adj_isBridge] at h
    specialize h ph
    rw [isBridge_iff_adj_and_forall_walk_mem_edges] at h
    replace h := h.2 (q.append p.reverse)
    simp only [Walk.edges_append, Walk.edges_reverse, List.mem_append, List.mem_reverse] at h
    cases' h with h h
    · cases q with
      | nil => simp [Walk.isPath_def] at hp
      | cons _ q =>
        rw [Walk.cons_isPath_iff] at hp hq
        simp only [Walk.edges_cons, List.mem_cons, Sym2.eq_iff, true_and] at h
        rcases h with (⟨h, rfl⟩ | ⟨rfl, rfl⟩) | h
        · cases ih hp.1 q hq.1
          rfl
        · simp at hq
        · exact absurd (Walk.fst_mem_support_of_mem_edges _ h) hq.2
    · rw [Walk.cons_isPath_iff] at hp
      exact absurd (Walk.fst_mem_support_of_mem_edges _ h) hp.2
theorem IsAcyclic.path_unique {G : SimpleGraph V} (h : G.IsAcyclic) {v w : V} (p q : G.Path v w) :
    p = q := by
  obtain ⟨p, hp⟩ := p
  obtain ⟨q, hq⟩ := q
  rw [Subtype.mk.injEq]
  induction p with
  

Proof state:
case mk.mk.nil
V : Type u
G✝ G : SimpleGraph V
h : IsAcyclic G
v w u✝ : V
hp : IsPath nil
q : Walk G u✝ u✝
hq : IsPath q
⊢ nil = q

Next tactic:
| nil =>
    cases (Walk.isPath_iff_eq_nil _).mp hq
    rfl
theorem IsAcyclic.path_unique {G : SimpleGraph V} (h : G.IsAcyclic) {v w : V} (p q : G.Path v w) :
    p = q := by
  obtain ⟨p, hp⟩ := p
  obtain ⟨q, hq⟩ := q
  rw [Subtype.mk.injEq]
  induction p with
  | nil =>
    

Proof state:
case mk.mk.nil
V : Type u
G✝ G : SimpleGraph V
h : IsAcyclic G
v w u✝ : V
hp : IsPath nil
q : Walk G u✝ u✝
hq : IsPath q
⊢ nil = q

Next tactic:
cases (Walk.isPath_iff_eq_nil _).mp hq
theorem IsAcyclic.path_unique {G : SimpleGraph V} (h : G.IsAcyclic) {v w : V} (p q : G.Path v w) :
    p = q := by
  obtain ⟨p, hp⟩ := p
  obtain ⟨q, hq⟩ := q
  rw [Subtype.mk.injEq]
  induction p with
  | nil =>
    cases (Walk.isPath_iff_eq_nil _).mp hq
    

Proof state:
case mk.mk.nil.refl
V : Type u
G✝ G : SimpleGraph V
h : IsAcyclic G
v w u✝ : V
hp hq : IsPath nil
⊢ nil = nil

Next tactic:
rfl
theorem IsAcyclic.path_unique {G : SimpleGraph V} (h : G.IsAcyclic) {v w : V} (p q : G.Path v w) :
    p = q := by
  obtain ⟨p, hp⟩ := p
  obtain ⟨q, hq⟩ := q
  rw [Subtype.mk.injEq]
  induction p with
  | nil =>
    cases (Walk.isPath_iff_eq_nil _).mp hq
    rfl
  

Proof state:
case mk.mk.cons
V : Type u
G✝ G : SimpleGraph V
h : IsAcyclic G
v w u✝ v✝ w✝ : V
ph : Adj G u✝ v✝
p : Walk G v✝ w✝
ih : IsPath p → ∀ (q : Walk G v✝ w✝), IsPath q → p = q
hp : IsPath (cons ph p)
q : Walk G u✝ w✝
hq : IsPath q
⊢ cons ph p = q

Next tactic:
| cons ph p ih =>
    rw [isAcyclic_iff_forall_adj_isBridge] at h
    specialize h ph
    rw [isBridge_iff_adj_and_forall_walk_mem_edges] at h
    replace h := h.2 (q.append p.reverse)
    simp only [Walk.edges_append, Walk.edges_reverse, List.mem_append, List.mem_reverse] at h
    cases' h with h h
    · cases q with
      | nil => simp [Walk.isPath_def] at hp
      | cons _ q =>
        rw [Walk.cons_isPath_iff] at hp hq
        simp only [Walk.edges_cons, List.mem_cons, Sym2.eq_iff, true_and] at h
        rcases h with (⟨h, rfl⟩ | ⟨rfl, rfl⟩) | h
        · cases ih hp.1 q hq.1
          rfl
        · simp at hq
        · exact absurd (Walk.fst_mem_support_of_mem_edges _ h) hq.2
    · rw [Walk.cons_isPath_iff] at hp
      exact absurd (Walk.fst_mem_support_of_mem_edges _ h) hp.2
theorem IsAcyclic.path_unique {G : SimpleGraph V} (h : G.IsAcyclic) {v w : V} (p q : G.Path v w) :
    p = q := by
  obtain ⟨p, hp⟩ := p
  obtain ⟨q, hq⟩ := q
  rw [Subtype.mk.injEq]
  induction p with
  | nil =>
    cases (Walk.isPath_iff_eq_nil _).mp hq
    rfl
  | cons ph p ih =>
    

Proof state:
case mk.mk.cons
V : Type u
G✝ G : SimpleGraph V
h : IsAcyclic G
v w u✝ v✝ w✝ : V
ph : Adj G u✝ v✝
p : Walk G v✝ w✝
ih : IsPath p → ∀ (q : Walk G v✝ w✝), IsPath q → p = q
hp : IsPath (cons ph p)
q : Walk G u✝ w✝
hq : IsPath q
⊢ cons ph p = q

Next tactic:
rw [isAcyclic_iff_forall_adj_isBridge] at h
theorem IsAcyclic.path_unique {G : SimpleGraph V} (h : G.IsAcyclic) {v w : V} (p q : G.Path v w) :
    p = q := by
  obtain ⟨p, hp⟩ := p
  obtain ⟨q, hq⟩ := q
  rw [Subtype.mk.injEq]
  induction p with
  | nil =>
    cases (Walk.isPath_iff_eq_nil _).mp hq
    rfl
  | cons ph p ih =>
    rw [isAcyclic_iff_forall_adj_isBridge] at h
    

Proof state:
case mk.mk.cons
V : Type u
G✝ G : SimpleGraph V
h : ∀ ⦃v w : V⦄, Adj G v w → IsBridge G ⟦(v, w)⟧
v w u✝ v✝ w✝ : V
ph : Adj G u✝ v✝
p : Walk G v✝ w✝
ih : IsPath p → ∀ (q : Walk G v✝ w✝), IsPath q → p = q
hp : IsPath (cons ph p)
q : Walk G u✝ w✝
hq : IsPath q
⊢ cons ph p = q

Next tactic:
specialize h ph