|
| 1 | +/- |
| 2 | +Copyright (c) 2025 Matteo Cipollina. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Matteo Cipollina |
| 5 | +-/ |
| 6 | + |
| 7 | +import Mathlib.Algebra.Order.Group.Nat |
| 8 | +import Mathlib.Combinatorics.Quiver.Path |
| 9 | +import Mathlib.Data.Set.Insert |
| 10 | +import Mathlib.Data.List.Basic |
| 11 | + |
| 12 | +/-! |
| 13 | +# Path Vertices |
| 14 | +
|
| 15 | +This file provides lemmas for reasoning about the vertices of a path. |
| 16 | +-/ |
| 17 | + |
| 18 | +namespace Quiver.Path |
| 19 | + |
| 20 | +open List |
| 21 | + |
| 22 | +variable {V : Type*} [Quiver V] |
| 23 | + |
| 24 | +/-- The end vertex of a path. A path `p : Path a b` has `p.end = b`. -/ |
| 25 | +def «end» {a : V} : ∀ {b : V}, Path a b → V |
| 26 | + | b, _ => b |
| 27 | + |
| 28 | +@[simp] |
| 29 | +lemma end_cons {a b c : V} (p : Path a b) (e : b ⟶ c) : (p.cons e).end = c := rfl |
| 30 | + |
| 31 | +/-- The list of vertices in a path, including the start and end vertices. -/ |
| 32 | +def vertices {a : V} : ∀ {b : V}, Path a b → List V |
| 33 | + | _, nil => [a] |
| 34 | + | _, cons p e => (p.vertices).concat (p.cons e).end |
| 35 | + |
| 36 | +@[simp] |
| 37 | +lemma vertices_nil (a : V) : (nil : Path a a).vertices = [a] := rfl |
| 38 | + |
| 39 | +@[simp] |
| 40 | +lemma vertices_cons {a b c : V} (p : Path a b) (e : b ⟶ c) : |
| 41 | + (p.cons e).vertices = p.vertices.concat c := rfl |
| 42 | + |
| 43 | +/-- The vertex list of `cons` — convenient `simp` form. -/ |
| 44 | +lemma mem_vertices_cons {a b c : V} (p : Path a b) |
| 45 | + (e : b ⟶ c) {x : V} : |
| 46 | + x ∈ (p.cons e).vertices ↔ x ∈ p.vertices ∨ x = c := by |
| 47 | + simp only [vertices_cons] |
| 48 | + simp_all only [concat_eq_append, mem_append, mem_cons, not_mem_nil, or_false] |
| 49 | + |
| 50 | +lemma verticesSet_nil {a : V} : {v | v ∈ (nil : Path a a).vertices} = {a} := by |
| 51 | + simp only [vertices_nil, List.mem_singleton, Set.ext_iff, Set.mem_singleton_iff] |
| 52 | + exact fun x ↦ Set.mem_setOf |
| 53 | + |
| 54 | +/-- The length of vertices list equals path length plus one -/ |
| 55 | +@[simp] |
| 56 | +lemma vertices_length {V : Type*} [Quiver V] {a b : V} (p : Path a b) : |
| 57 | + p.vertices.length = p.length + 1 := by |
| 58 | + induction p with |
| 59 | + | nil => simp only [vertices_nil, List.length_cons, List.length_nil, zero_add, length_nil] |
| 60 | + | cons p' e ih => |
| 61 | + simp only [vertices_cons, length_cons, List.length_concat, ih] |
| 62 | + |
| 63 | +lemma length_vertices_pos {a b : V} (p : Path a b) : |
| 64 | + 0 < p.vertices.length := by simp |
| 65 | + |
| 66 | +lemma vertices_ne_nil {a : V} {b : V} (p : Path a b) : p.vertices ≠ [] := by |
| 67 | + simp [← List.length_pos_iff_ne_nil] |
| 68 | + |
| 69 | +/-- The head of the vertices list is the start vertex -/ |
| 70 | +@[simp] |
| 71 | +lemma vertices_head? {a b : V} (p : Path a b) : p.vertices.head? = some a := by |
| 72 | + induction p with |
| 73 | + | nil => simp only [vertices_nil, List.head?_cons] |
| 74 | + | cons p' e ih => simp [ih] |
| 75 | + |
| 76 | +@[simp] |
| 77 | +lemma getElem_vertices_zero {a b : V} (p : Path a b) : p.vertices[0] = a := by |
| 78 | + induction p with |
| 79 | + | nil => simp |
| 80 | + | cons p' e ih => simp [ih] |
| 81 | + |
| 82 | +@[simp] |
| 83 | +lemma vertices_getLast {a b : V} (p : Path a b) (h : p.vertices ≠ [] := p.vertices_ne_nil) : |
| 84 | + p.vertices.getLast h = b := by |
| 85 | + induction p with |
| 86 | + | nil => simp only [vertices_nil, List.getLast_singleton] |
| 87 | + | cons p' e ih => simp |
| 88 | + |
| 89 | +@[simp] |
| 90 | +lemma vertices_comp {a b c : V} (p : Path a b) (q : Path b c) : |
| 91 | + (p.comp q).vertices = p.vertices.dropLast ++ q.vertices := by |
| 92 | + induction q with |
| 93 | + | nil => simpa using (List.dropLast_append_getLast p.vertices_ne_nil).symm |
| 94 | + | cons q' e ih => simp [ih] |
| 95 | + |
| 96 | +lemma start_mem_vertices {a b : V} (p : Path a b) : a ∈ p.vertices := by |
| 97 | + induction p with |
| 98 | + | nil => simp |
| 99 | + | cons p' e ih => simp [ih] |
| 100 | + |
| 101 | +@[simp] lemma length_eq_zero_iff {a : V} (p : Path a a) : |
| 102 | + p.length = 0 ↔ p = Path.nil := by |
| 103 | + cases p <;> tauto |
| 104 | + |
| 105 | +/-- The head of the vertices list is the start vertex. -/ |
| 106 | +@[simp] |
| 107 | +lemma vertices_head_eq {a b : V} (p : Path a b) (h : p.vertices ≠ [] := p.vertices_ne_nil) : |
| 108 | + p.vertices.head h = a := by |
| 109 | + induction p with |
| 110 | + | nil => simp only [vertices_nil, List.head_cons] |
| 111 | + | cons p' _ ih => simp [List.head_append_of_ne_nil (vertices_ne_nil p'), ih] |
| 112 | + |
| 113 | +/-- The last element of the vertices list is the end vertex. -/ |
| 114 | +lemma vertices_getLast_eq {a b : V} (p : Path a b) (h : p.vertices ≠ [] := p.vertices_ne_nil) : |
| 115 | + p.vertices.getLast h = b := by |
| 116 | + exact vertices_getLast p (vertices_ne_nil p) |
| 117 | + |
| 118 | +lemma vertices_comp_get_length_eq {a b c : V} (p₁ : Path a c) (p₂ : Path c b) |
| 119 | + (h : p₁.length < (p₁.comp p₂).vertices.length := by simp) : |
| 120 | + (p₁.comp p₂).vertices.get ⟨p₁.length, h⟩ = c := by |
| 121 | + simp |
| 122 | + |
| 123 | +@[simp] |
| 124 | +lemma vertices_toPath {i j : V} (e : i ⟶ j) : |
| 125 | + (e.toPath).vertices = [i, j] := by |
| 126 | + change (Path.nil.cons e).vertices = [i, j] |
| 127 | + simp |
| 128 | + |
| 129 | +lemma vertices_toPath_tail {i j : V} (e : i ⟶ j) : |
| 130 | + (e.toPath).vertices.tail = [j] := by |
| 131 | + simp |
| 132 | + |
| 133 | +/-- If a composition is `nil`, the left component must be `nil` |
| 134 | + (proved via lengths, avoiding dependent pattern-matching). -/ |
| 135 | +lemma nil_of_comp_eq_nil_left {a b : V} {p : Path a b} {q : Path b a} |
| 136 | + (h : p.comp q = Path.nil) : p.length = 0 := by |
| 137 | + have hlen : (p.comp q).length = 0 := by |
| 138 | + simpa using congrArg Path.length h |
| 139 | + have : p.length + q.length = 0 := by |
| 140 | + simpa [length_comp] using hlen |
| 141 | + exact Nat.eq_zero_of_add_eq_zero_right this |
| 142 | + |
| 143 | +/-- If a composition is `nil`, the right component must be `nil` -/ |
| 144 | +lemma nil_of_comp_eq_nil_right {a b : V} {p : Path a b} {q : Path b a} |
| 145 | + (h : p.comp q = Path.nil) : q.length = 0 := by |
| 146 | + have hlen : (p.comp q).length = 0 := by |
| 147 | + simpa using congrArg Path.length h |
| 148 | + have : p.length + q.length = 0 := by |
| 149 | + simpa [length_comp] using hlen |
| 150 | + exact Nat.eq_zero_of_add_eq_zero_left this |
| 151 | + |
| 152 | +lemma comp_eq_nil_iff {a b : V} {p : Path a b} {q : Path b a} : |
| 153 | + p.comp q = Path.nil ↔ p.length = 0 ∧ q.length = 0 := by |
| 154 | + refine ⟨fun h ↦ ⟨nil_of_comp_eq_nil_left h, nil_of_comp_eq_nil_right h⟩, fun ⟨hp, hq⟩ ↦ ?_⟩ |
| 155 | + induction p with |
| 156 | + | nil => simpa using (length_eq_zero_iff q).mp hq |
| 157 | + | cons p' _ ihp => simp at hp |
| 158 | + |
| 159 | +@[simp] |
| 160 | +lemma end_mem_vertices {a b : V} (p : Path a b) : b ∈ p.vertices := by |
| 161 | + have h₁ : p.vertices.getLast (vertices_ne_nil p) = b := |
| 162 | + vertices_getLast p (vertices_ne_nil p) |
| 163 | + have h₂ := List.getLast_mem (l := p.vertices) (vertices_ne_nil p) |
| 164 | + simpa [h₁] using h₂ |
| 165 | + |
| 166 | +end Quiver.Path |
0 commit comments