pgraph: Misc cleanups and additions that I had kicking around

These changes had been sitting in a feature branch for a while. Push
them up.
This commit is contained in:
James Shubin
2022-08-04 14:33:49 -04:00
parent 9d5cc07567
commit 57b37d9005

View File

@@ -28,9 +28,9 @@ import (
// Graph is the graph structure in this library. The graph abstract data type // Graph is the graph structure in this library. The graph abstract data type
// (ADT) is defined as follows: // (ADT) is defined as follows:
// * the directed graph arrows point from left to right ( -> ) // * The directed graph arrows point from left to right. ( -> )
// * the arrows point away from their dependencies (eg: arrows mean "before") // * The arrows point away from their dependencies. (eg: arrows mean "before")
// * IOW, you might see package -> file -> service (where package runs first) // * IOW, you might see package -> file -> service. (where package runs first)
// * This is also the direction that the notify should happen in... // * This is also the direction that the notify should happen in...
type Graph struct { type Graph struct {
Name string Name string
@@ -89,7 +89,10 @@ func (g *Graph) SetValue(key string, val interface{}) {
g.kv[key] = val g.kv[key] = val
} }
// Copy makes a copy of the graph struct. // Copy makes a copy of the graph struct. This doesn't copy the individual
// vertices or edges, those pointers remain untouched. This lets you modify the
// structure of the graph without changing the original. If you also want to
// copy the nodes, please use CopyWithFn instead.
func (g *Graph) Copy() *Graph { func (g *Graph) Copy() *Graph {
if g == nil { // allow nil graphs through if g == nil { // allow nil graphs through
return g return g
@@ -99,12 +102,80 @@ func (g *Graph) Copy() *Graph {
adjacency: make(map[Vertex]map[Vertex]Edge, len(g.adjacency)), adjacency: make(map[Vertex]map[Vertex]Edge, len(g.adjacency)),
kv: g.kv, kv: g.kv,
} }
for k, v := range g.adjacency { for v1, m := range g.adjacency {
newGraph.adjacency[k] = v // copy newGraph.adjacency[v1] = make(map[Vertex]Edge)
for v2, e := range m {
newGraph.adjacency[v1][v2] = e // copy
}
} }
return newGraph return newGraph
} }
// CopyWithFn makes a copy of the graph struct but lets you provide a function
// to copy the vertices.
// TODO: add tests
func (g *Graph) CopyWithFn(vertexCpFn func(Vertex) (Vertex, error)) (*Graph, error) {
if g == nil { // allow nil graphs through
return g, nil
}
if l := len(g.adjacency); vertexCpFn == nil && l > 0 {
return nil, fmt.Errorf("graph has %d vertices, but vertexCpFn is nil", l)
}
newGraph := &Graph{
Name: g.Name,
adjacency: make(map[Vertex]map[Vertex]Edge, len(g.adjacency)),
kv: g.kv,
}
vm := make(map[Vertex]Vertex) // copy mapping from old ptr to new ptr...
for v1, m := range g.adjacency {
// We copy each vertex, but then we need to do a lookup so that
// when (if) we see that old pointer again, we use the new one.
v, err := vertexCpFn(v1) // copy
if err != nil {
return nil, err
}
vm[v1] = v // mapping
newGraph.adjacency[v] = make(map[Vertex]Edge)
for v2, e := range m {
vx, exists := vm[v2] // copied equivalent of v2
if !exists {
// programming error or corrupt adjacency maps!
// anything in the second map, should be in the
// first one, or else it was added/modified oob
return nil, fmt.Errorf("corrupt datastructure")
}
// TODO: add edgeCpFn if it's deemed useful somehow...
//edge, err := edgeCpFn(e) // copy edge
//if err != nil {
// return nil, err
//}
//newGraph.adjacency[v][vx] = edge
newGraph.adjacency[v][vx] = e // store the edge
}
}
return newGraph, nil
}
// VertexSwap swaps vertices in a graph. It returns a new graph with the same
// structure but with replacements done according to the translation map passed
// in. If a vertex is not found in the graph, then it is not substituted.
// TODO: add tests
func (g *Graph) VertexSwap(vs map[Vertex]Vertex) (*Graph, error) {
vertexCpFn := func(v Vertex) (Vertex, error) {
if vs == nil { // pass through
return v, nil
}
vx, exists := vs[v]
if !exists {
return v, nil // pass through
}
return vx, nil // swap!
}
// We can implement the logic we want on top of CopyWithFn easily!
return g.CopyWithFn(vertexCpFn)
}
// GetName returns the name of the graph. // GetName returns the name of the graph.
func (g *Graph) GetName() string { func (g *Graph) GetName() string {
return g.Name return g.Name