github.com/charypar/monobuild@v0.0.0-20211122220434-fd884ed50212/graph/graph.go (about) 1 package graph 2 3 import ( 4 "sort" 5 6 "github.com/charypar/monobuild/set" 7 ) 8 9 // Graph is a DAG with string labeled vertices and int colored edges 10 type Graph struct { 11 edges map[string]Edges 12 } 13 14 // New creates a new Graph from a map of the shape 15 // string: Edge 16 // where Edge is a struct with a Label and a Colour 17 func New(graph map[string][]Edge) Graph { 18 g := make(map[string]Edges) 19 20 // Copy and normalise the graph - every node needs a key in the map 21 for v, es := range graph { 22 g[v] = make(Edges, 0, len(es)) 23 24 for _, e := range es { 25 g[v] = append(g[v], e) 26 27 _, ok := graph[e.Label] 28 if !ok { 29 g[e.Label] = Edges{} 30 } 31 } 32 } 33 34 return Graph{g} 35 } 36 37 // Vertices returns a full list of vertices in the graph 38 func (g Graph) Vertices() []string { 39 vs := make([]string, 0, len(g.edges)) 40 41 for v := range g.edges { 42 vs = append(vs, v) 43 } 44 45 sort.Strings(vs) 46 return vs 47 } 48 49 // Children returns the vertices that are connected to given vertices with an edge 50 func (g Graph) Children(vertices []string) []string { 51 all := set.New([]string{}) 52 53 for _, vertex := range vertices { 54 grandchildren, found := g.edges[vertex] 55 if !found { 56 continue 57 } 58 59 for _, gc := range grandchildren { 60 all.Add(gc.Label) 61 } 62 } 63 64 result := all.AsStrings() 65 sort.Strings(result) 66 67 return result 68 } 69 70 // Descendants returns all the vertices x for which a path to x exists from any of 71 // the vertices given 72 func (g Graph) Descendants(vertices []string) []string { 73 descendants := set.New(g.Children(vertices)) 74 discovered := descendants 75 76 for discovered.Size() > 0 { 77 grandchildren := set.New(g.Children(discovered.AsStrings())) 78 79 discovered = grandchildren.Without(descendants) 80 descendants = descendants.Union(discovered) 81 } 82 83 result := descendants.AsStrings() 84 sort.Strings(result) 85 86 return result 87 } 88 89 // Reverse returns a new graph with edges reversed 90 func (g Graph) Reverse() Graph { 91 edges := make(map[string]Edges) 92 93 // loop over the map keys deterministically 94 sorted := make([]string, 0, len(g.edges)) 95 for v := range g.edges { 96 sorted = append(sorted, v) 97 } 98 sort.Strings(sorted) 99 100 // here 101 for _, v := range sorted { 102 _, ok := edges[v] 103 if !ok { 104 edges[v] = Edges{} 105 } 106 107 for _, e := range g.edges[v] { 108 _, ok := edges[e.Label] 109 if !ok { 110 edges[e.Label] = Edges{} 111 } 112 113 edges[e.Label] = append(edges[e.Label], Edge{v, e.Colour}) 114 } 115 } 116 117 return Graph{edges} 118 } 119 120 // Subgraph filters the graph to only the nodes listed 121 func (g Graph) Subgraph(nodes []string) Graph { 122 filter := set.New(nodes) 123 filtered := make(map[string]Edges, len(g.edges)) 124 125 for v, es := range g.edges { 126 if !filter.Has(v) { 127 continue 128 } 129 130 filtered[v] = make(Edges, 0, len(es)) 131 for _, e := range es { 132 if !filter.Has(e.Label) { 133 continue 134 } 135 136 filtered[v] = append(filtered[v], e) 137 } 138 } 139 140 return Graph{filtered} 141 } 142 143 // FilterEdges returns a new graph with edges with a colour not present in 144 // colours removed 145 func (g Graph) FilterEdges(colours []int) Graph { 146 filter := make(map[int]bool, len(colours)) 147 for _, c := range colours { 148 filter[c] = true 149 } 150 151 filtered := make(map[string]Edges, len(g.edges)) 152 153 for v, es := range g.edges { 154 _, ok := filtered[v] 155 if !ok { 156 filtered[v] = make([]Edge, 0, len(es)) 157 } 158 159 for _, e := range es { 160 _, matches := filter[e.Colour] 161 if matches { 162 filtered[v] = append(filtered[v], e) 163 } 164 } 165 } 166 167 return Graph{filtered} 168 }