graph: refactor Graph into being an interface
This commit is contained in:
parent
16f2b1bbde
commit
ad1e99585b
275
graph/graph.go
275
graph/graph.go
@ -63,6 +63,25 @@ func (e edge) Head() Value {
|
|||||||
return e.head
|
return e.head
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e edge) String() string {
|
||||||
|
return edgeID(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE the Node type exists primarily for convenience. As far as Graph's
|
||||||
|
// internals are concerned it doesn't _really_ exist, and no Graph method should
|
||||||
|
// ever take Node as a parameter (except the callback functions like in
|
||||||
|
// Traverse, where it's not really being taken in).
|
||||||
|
|
||||||
|
// Node wraps a Value in a Graph to include that Node's input and output Edges
|
||||||
|
// in that Graph.
|
||||||
|
type Node struct {
|
||||||
|
Value
|
||||||
|
|
||||||
|
// All Edges in the Graph with this Node's Value as their Head and Tail,
|
||||||
|
// respectively. These should not be expected to be deterministic.
|
||||||
|
Ins, Outs []Edge
|
||||||
|
}
|
||||||
|
|
||||||
// an edgeIndex maps valueIDs to a set of edgeIDs. Graph keeps two edgeIndex's,
|
// an edgeIndex maps valueIDs to a set of edgeIDs. Graph keeps two edgeIndex's,
|
||||||
// one for input edges and one for output edges.
|
// one for input edges and one for output edges.
|
||||||
type edgeIndex map[string]map[string]struct{}
|
type edgeIndex map[string]map[string]struct{}
|
||||||
@ -111,7 +130,36 @@ func (ei edgeIndex) del(valID, edgeID string) {
|
|||||||
//
|
//
|
||||||
// The Graph does not keep track of Edge ordering. Assume that all slices of
|
// The Graph does not keep track of Edge ordering. Assume that all slices of
|
||||||
// Edges are in random order.
|
// Edges are in random order.
|
||||||
type Graph struct {
|
type Graph interface {
|
||||||
|
// Empty returns a graph with no edges which is of the same underlying type
|
||||||
|
// as this one.
|
||||||
|
Empty() Graph
|
||||||
|
|
||||||
|
// Add returns a new Graph instance with the given Edge added to it. If the
|
||||||
|
// original Graph already had that Edge this returns the original Graph.
|
||||||
|
Add(Edge) Graph
|
||||||
|
|
||||||
|
// Del returns a new Graph instance without the given Edge in it. If the
|
||||||
|
// original Graph didn't have that Edge this returns the original Graph.
|
||||||
|
Del(Edge) Graph
|
||||||
|
|
||||||
|
// Edges returns all Edges which are part of the Graph, mapped using a
|
||||||
|
// string ID which is unique within the Graph and between Graphs of the same
|
||||||
|
// underlying type.
|
||||||
|
Edges() map[string]Edge
|
||||||
|
|
||||||
|
// EdgesTo returns all Edges whose Head is the given Value.
|
||||||
|
EdgesTo(v Value) []Edge
|
||||||
|
|
||||||
|
// EdgesFrom returns all Edges whose Tail is the given Value.
|
||||||
|
EdgesFrom(v Value) []Edge
|
||||||
|
|
||||||
|
// Has returns true if the Graph contains at least one Edge with a Head or
|
||||||
|
// Tail of Value.
|
||||||
|
Has(v Value) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type graph struct {
|
||||||
m map[string]Edge
|
m map[string]Edge
|
||||||
|
|
||||||
// these are indices mapping Value IDs to all the in/out edges for that
|
// these are indices mapping Value IDs to all the in/out edges for that
|
||||||
@ -119,8 +167,15 @@ type Graph struct {
|
|||||||
vIns, vOuts edgeIndex
|
vIns, vOuts edgeIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Graph) cp() Graph {
|
// Null is the empty graph from which all other Graphs are built.
|
||||||
g2 := Graph{
|
var Null = (graph{}).Empty()
|
||||||
|
|
||||||
|
func (g graph) Empty() Graph {
|
||||||
|
return (graph{}).cp() // cp also initializes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g graph) cp() graph {
|
||||||
|
g2 := graph{
|
||||||
m: make(map[string]Edge, len(g.m)),
|
m: make(map[string]Edge, len(g.m)),
|
||||||
vIns: g.vIns.cp(),
|
vIns: g.vIns.cp(),
|
||||||
vOuts: g.vOuts.cp(),
|
vOuts: g.vOuts.cp(),
|
||||||
@ -131,18 +186,16 @@ func (g Graph) cp() Graph {
|
|||||||
return g2
|
return g2
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Graph) String() string {
|
func (g graph) String() string {
|
||||||
edgeIDs := make([]string, 0, len(g.m))
|
edgeStrs := make([]string, 0, len(g.m))
|
||||||
for edgeID := range g.m {
|
for _, edge := range g.m {
|
||||||
edgeIDs = append(edgeIDs, edgeID)
|
edgeStrs = append(edgeStrs, fmt.Sprint(edge))
|
||||||
}
|
}
|
||||||
sort.Strings(edgeIDs)
|
sort.Strings(edgeStrs)
|
||||||
return "Graph{" + strings.Join(edgeIDs, ",") + "}"
|
return "Graph{" + strings.Join(edgeStrs, ",") + "}"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add returns a new Graph instance with the given Edge added to it. If the
|
func (g graph) Add(e Edge) Graph {
|
||||||
// original Graph already had that Edge this returns the original Graph.
|
|
||||||
func (g Graph) Add(e Edge) Graph {
|
|
||||||
id := edgeID(e)
|
id := edgeID(e)
|
||||||
if _, ok := g.m[id]; ok {
|
if _, ok := g.m[id]; ok {
|
||||||
return g
|
return g
|
||||||
@ -153,24 +206,26 @@ func (g Graph) Add(e Edge) Graph {
|
|||||||
return g2
|
return g2
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Graph) addDirty(edgeID string, e Edge) {
|
func (g graph) addDirty(edgeID string, e Edge) {
|
||||||
g.m[edgeID] = e
|
g.m[edgeID] = e
|
||||||
g.vIns.add(e.Head().ID, edgeID)
|
g.vIns.add(e.Head().ID, edgeID)
|
||||||
g.vOuts.add(e.Tail().ID, edgeID)
|
g.vOuts.add(e.Tail().ID, edgeID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Graph) estSize() int {
|
// addDirty attempts to add the Edge to Graph using an addDirty method,
|
||||||
lvIns := len(g.vIns)
|
// otherwise it just uses Add like normal
|
||||||
lvOuts := len(g.vOuts)
|
func addDirty(g Graph, edgeID string, e Edge) Graph {
|
||||||
if lvIns > lvOuts {
|
gd, ok := g.(interface {
|
||||||
return lvIns
|
addDirty(string, Edge)
|
||||||
|
})
|
||||||
|
if !ok {
|
||||||
|
return g.Add(e)
|
||||||
}
|
}
|
||||||
return lvOuts
|
gd.addDirty(edgeID, e)
|
||||||
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
// Del returns a new Graph instance without the given Edge in it. If the
|
func (g graph) Del(e Edge) Graph {
|
||||||
// original Graph didn't have that Edge this returns the original Graph.
|
|
||||||
func (g Graph) Del(e Edge) Graph {
|
|
||||||
id := edgeID(e)
|
id := edgeID(e)
|
||||||
if _, ok := g.m[id]; !ok {
|
if _, ok := g.m[id]; !ok {
|
||||||
return g
|
return g
|
||||||
@ -183,15 +238,50 @@ func (g Graph) Del(e Edge) Graph {
|
|||||||
return g2
|
return g2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g graph) Edges() map[string]Edge {
|
||||||
|
return g.m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g graph) EdgesTo(v Value) []Edge {
|
||||||
|
vIns := g.vIns[v.ID]
|
||||||
|
ins := make([]Edge, 0, len(vIns))
|
||||||
|
for edgeID := range vIns {
|
||||||
|
ins = append(ins, g.m[edgeID])
|
||||||
|
}
|
||||||
|
return ins
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g graph) EdgesFrom(v Value) []Edge {
|
||||||
|
vOuts := g.vOuts[v.ID]
|
||||||
|
outs := make([]Edge, 0, len(vOuts))
|
||||||
|
for edgeID := range vOuts {
|
||||||
|
outs = append(outs, g.m[edgeID])
|
||||||
|
}
|
||||||
|
return outs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g graph) Has(v Value) bool {
|
||||||
|
if _, ok := g.vIns[v.ID]; ok {
|
||||||
|
return true
|
||||||
|
} else if _, ok := g.vOuts[v.ID]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Disjoin looks at the whole Graph and returns all sub-graphs of it which don't
|
// Disjoin looks at the whole Graph and returns all sub-graphs of it which don't
|
||||||
// share any Edges between each other.
|
// share any Edges between each other.
|
||||||
func (g Graph) Disjoin() []Graph {
|
func Disjoin(g Graph) []Graph {
|
||||||
valM := make(map[string]*Graph, len(g.vOuts))
|
empty := g.Empty()
|
||||||
|
edges := g.Edges()
|
||||||
|
valM := make(map[string]*Graph, len(edges))
|
||||||
graphForEdge := func(edge Edge) *Graph {
|
graphForEdge := func(edge Edge) *Graph {
|
||||||
headGraph := valM[edge.Head().ID]
|
headGraph := valM[edge.Head().ID]
|
||||||
tailGraph := valM[edge.Tail().ID]
|
tailGraph := valM[edge.Tail().ID]
|
||||||
if headGraph == nil && tailGraph == nil {
|
if headGraph == nil && tailGraph == nil {
|
||||||
newGraph := Graph{}.cp() // cp also initializes
|
newGraph := empty.Empty()
|
||||||
return &newGraph
|
return &newGraph
|
||||||
} else if headGraph == nil && tailGraph != nil {
|
} else if headGraph == nil && tailGraph != nil {
|
||||||
return tailGraph
|
return tailGraph
|
||||||
@ -204,11 +294,13 @@ func (g Graph) Disjoin() []Graph {
|
|||||||
// the two values are part of different graphs, join the smaller into
|
// the two values are part of different graphs, join the smaller into
|
||||||
// the larger and change all values which were pointing to it to point
|
// the larger and change all values which were pointing to it to point
|
||||||
// into the larger (which will then be the join of them)
|
// into the larger (which will then be the join of them)
|
||||||
if len(tailGraph.m) > len(headGraph.m) {
|
tailEdges := (*tailGraph).Edges()
|
||||||
tailGraph, headGraph = headGraph, tailGraph
|
if headEdges := (*headGraph).Edges(); len(headEdges) > len(tailEdges) {
|
||||||
|
headGraph, tailGraph = tailGraph, headGraph
|
||||||
|
tailEdges = headEdges
|
||||||
}
|
}
|
||||||
for edgeID, edge := range tailGraph.m {
|
for edgeID, edge := range tailEdges {
|
||||||
(*headGraph).addDirty(edgeID, edge)
|
*headGraph = addDirty(*headGraph, edgeID, edge)
|
||||||
}
|
}
|
||||||
for valID, valGraph := range valM {
|
for valID, valGraph := range valM {
|
||||||
if valGraph == tailGraph {
|
if valGraph == tailGraph {
|
||||||
@ -218,9 +310,9 @@ func (g Graph) Disjoin() []Graph {
|
|||||||
return headGraph
|
return headGraph
|
||||||
}
|
}
|
||||||
|
|
||||||
for edgeID, edge := range g.m {
|
for edgeID, edge := range edges {
|
||||||
graph := graphForEdge(edge)
|
graph := graphForEdge(edge)
|
||||||
(*graph).addDirty(edgeID, edge)
|
*graph = addDirty(*graph, edgeID, edge)
|
||||||
valM[edge.Head().ID] = graph
|
valM[edge.Head().ID] = graph
|
||||||
valM[edge.Tail().ID] = graph
|
valM[edge.Tail().ID] = graph
|
||||||
}
|
}
|
||||||
@ -237,60 +329,35 @@ func (g Graph) Disjoin() []Graph {
|
|||||||
return graphs
|
return graphs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join returns a new Graph which shares all Edges of this Graph and all given
|
// Join returns a new Graph which shares all Edges of all given Graphs. All
|
||||||
// Graphs.
|
// given Graphs must be of the same underlying type.
|
||||||
func (g Graph) Join(graphs ...Graph) Graph {
|
func Join(graphs ...Graph) Graph {
|
||||||
g2 := g.cp()
|
g2 := graphs[0].Empty()
|
||||||
for _, graph := range graphs {
|
for _, graph := range graphs {
|
||||||
for edgeID, edge := range graph.m {
|
for edgeID, edge := range graph.Edges() {
|
||||||
g2.addDirty(edgeID, edge)
|
g2 = addDirty(g2, edgeID, edge)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return g2
|
return g2
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edges returns all Edges which are part of the Graph
|
// GetNode returns the Node for the given Value, or false if the Graph doesn't
|
||||||
func (g Graph) Edges() []Edge {
|
|
||||||
edges := make([]Edge, 0, len(g.m))
|
|
||||||
for _, e := range g.m {
|
|
||||||
edges = append(edges, e)
|
|
||||||
}
|
|
||||||
return edges
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE the Node type exists primarily for convenience. As far as Graph's
|
|
||||||
// internals are concerned it doesn't _really_ exist, and no Graph method should
|
|
||||||
// ever take Node as a parameter (except the callback functions like in
|
|
||||||
// Traverse, where it's not really being taken in).
|
|
||||||
|
|
||||||
// Node wraps a Value in a Graph to include that Node's input and output Edges
|
|
||||||
// in that Graph.
|
|
||||||
type Node struct {
|
|
||||||
Value
|
|
||||||
|
|
||||||
// All Edges in the Graph with this Node's Value as their Head and Tail,
|
|
||||||
// respectively. These should not be expected to be deterministic.
|
|
||||||
Ins, Outs []Edge
|
|
||||||
}
|
|
||||||
|
|
||||||
// Node returns the Node for the given Value, or false if the Graph doesn't
|
|
||||||
// contain the Value.
|
// contain the Value.
|
||||||
func (g Graph) Node(v Value) (Node, bool) {
|
func GetNode(g Graph, v Value) (Node, bool) {
|
||||||
n := Node{Value: v}
|
n := Node{
|
||||||
for edgeID := range g.vIns[v.ID] {
|
Value: v,
|
||||||
n.Ins = append(n.Ins, g.m[edgeID])
|
Ins: g.EdgesTo(v),
|
||||||
}
|
Outs: g.EdgesFrom(v),
|
||||||
for edgeID := range g.vOuts[v.ID] {
|
|
||||||
n.Outs = append(n.Outs, g.m[edgeID])
|
|
||||||
}
|
}
|
||||||
return n, len(n.Ins) > 0 || len(n.Outs) > 0
|
return n, len(n.Ins) > 0 || len(n.Outs) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nodes returns a Node for each Value which has at least one Edge in the Graph,
|
// GetNodes returns a Node for each Value which has at least one Edge in the
|
||||||
// with the Nodes mapped by their Value's ID.
|
// Graph, with the Nodes mapped by their Value's ID.
|
||||||
func (g Graph) Nodes() map[string]Node {
|
func GetNodes(g Graph) map[string]Node {
|
||||||
nodesM := make(map[string]Node, len(g.m)*2)
|
edges := g.Edges()
|
||||||
for _, edge := range g.m {
|
nodesM := make(map[string]Node, len(edges)*2)
|
||||||
|
for _, edge := range edges {
|
||||||
// if head and tail are modified at the same time it messes up the case
|
// if head and tail are modified at the same time it messes up the case
|
||||||
// where they are the same node
|
// where they are the same node
|
||||||
{
|
{
|
||||||
@ -311,17 +378,6 @@ func (g Graph) Nodes() map[string]Node {
|
|||||||
return nodesM
|
return nodesM
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has returns true if the Graph contains at least one Edge with a Head or Tail
|
|
||||||
// of Value.
|
|
||||||
func (g Graph) Has(v Value) bool {
|
|
||||||
if _, ok := g.vIns[v.ID]; ok {
|
|
||||||
return true
|
|
||||||
} else if _, ok := g.vOuts[v.ID]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Traverse is used to traverse the Graph until a stopping point is reached.
|
// Traverse is used to traverse the Graph until a stopping point is reached.
|
||||||
// Traversal starts with the cursor at the given start Value. Each hop is
|
// Traversal starts with the cursor at the given start Value. Each hop is
|
||||||
// performed by passing the cursor Value's Node into the next function. The
|
// performed by passing the cursor Value's Node into the next function. The
|
||||||
@ -332,10 +388,10 @@ func (g Graph) Has(v Value) bool {
|
|||||||
//
|
//
|
||||||
// If start has no Edges in the Graph, or a Value returned from next doesn't,
|
// If start has no Edges in the Graph, or a Value returned from next doesn't,
|
||||||
// this will still call next, but the Node will be the zero value.
|
// this will still call next, but the Node will be the zero value.
|
||||||
func (g Graph) Traverse(start Value, next func(n Node) (Value, bool)) {
|
func Traverse(g Graph, start Value, next func(n Node) (Value, bool)) {
|
||||||
curr := start
|
curr := start
|
||||||
for {
|
for {
|
||||||
currNode, ok := g.Node(curr)
|
currNode, ok := GetNode(g, curr)
|
||||||
if ok {
|
if ok {
|
||||||
curr, ok = next(currNode)
|
curr, ok = next(currNode)
|
||||||
} else {
|
} else {
|
||||||
@ -355,9 +411,9 @@ func (g Graph) Traverse(start Value, next func(n Node) (Value, bool)) {
|
|||||||
// Value has no edges in the Graph, traversal stops and this method returns.
|
// Value has no edges in the Graph, traversal stops and this method returns.
|
||||||
//
|
//
|
||||||
// The exact order of Nodes visited is _not_ deterministic.
|
// The exact order of Nodes visited is _not_ deterministic.
|
||||||
func (g Graph) VisitBreadth(start Value, callback func(n Node) bool) {
|
func VisitBreadth(g Graph, start Value, callback func(n Node) bool) {
|
||||||
visited := map[string]bool{}
|
visited := map[string]bool{}
|
||||||
toVisit := make([]Value, 0, g.estSize())
|
toVisit := make([]Value, 0, 16)
|
||||||
toVisit = append(toVisit, start)
|
toVisit = append(toVisit, start)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -371,7 +427,7 @@ func (g Graph) VisitBreadth(start Value, callback func(n Node) bool) {
|
|||||||
if visited[val.ID] {
|
if visited[val.ID] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
node, ok := g.Node(val)
|
node, ok := GetNode(g, val)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
} else if !callback(node) {
|
} else if !callback(node) {
|
||||||
@ -396,11 +452,11 @@ func (g Graph) VisitBreadth(start Value, callback func(n Node) bool) {
|
|||||||
// Value has no edges in the Graph, traversal stops and this method returns.
|
// Value has no edges in the Graph, traversal stops and this method returns.
|
||||||
//
|
//
|
||||||
// The exact order of Nodes visited is _not_ deterministic.
|
// The exact order of Nodes visited is _not_ deterministic.
|
||||||
func (g Graph) VisitDepth(start Value, callback func(n Node) bool) {
|
func VisitDepth(g Graph, start Value, callback func(n Node) bool) {
|
||||||
// VisitDepth is actually the same as VisitBreadth, only you read off the
|
// VisitDepth is actually the same as VisitBreadth, only you read off the
|
||||||
// toVisit list from back-to-front
|
// toVisit list from back-to-front
|
||||||
visited := map[string]bool{}
|
visited := map[string]bool{}
|
||||||
toVisit := make([]Value, 0, g.estSize())
|
toVisit := make([]Value, 0, 16)
|
||||||
toVisit = append(toVisit, start)
|
toVisit = append(toVisit, start)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@ -413,7 +469,7 @@ func (g Graph) VisitDepth(start Value, callback func(n Node) bool) {
|
|||||||
if visited[val.ID] {
|
if visited[val.ID] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
node, ok := g.Node(val)
|
node, ok := GetNode(g, val)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
} else if !callback(node) {
|
} else if !callback(node) {
|
||||||
@ -429,31 +485,38 @@ func (g Graph) VisitDepth(start Value, callback func(n Node) bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g Graph) edgesShared(g2 Graph) bool {
|
func edgesShared(g, g2 Graph) bool {
|
||||||
for id := range g2.m {
|
gEdges := g.Edges()
|
||||||
if _, ok := g.m[id]; !ok {
|
for id := range g2.Edges() {
|
||||||
|
if _, ok := gEdges[id]; !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubGraph returns true if the given Graph shares all of its Edges with this
|
// SubGraph returns true if g2 is a sub-graph of g; i.e., all edges in g2 are
|
||||||
// Graph.
|
// also in g. Both Graphs should be of the same underlying type.
|
||||||
func (g Graph) SubGraph(g2 Graph) bool {
|
func SubGraph(g, g2 Graph) bool {
|
||||||
|
gEdges, g2Edges := g.Edges(), g2.Edges()
|
||||||
// as a quick check before iterating through the edges, if g has fewer edges
|
// as a quick check before iterating through the edges, if g has fewer edges
|
||||||
// than g2 then g2 can't possibly be a sub-graph of it
|
// than g2 then g2 can't possibly be a sub-graph of it
|
||||||
if len(g.m) < len(g2.m) {
|
if len(gEdges) < len(g2Edges) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return g.edgesShared(g2)
|
for id := range g2Edges {
|
||||||
|
if _, ok := gEdges[id]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal returns true if the given Graph and this Graph have exactly the same
|
// Equal returns true if g and g2 have exactly the same Edges. Both Graphs
|
||||||
// Edges.
|
// should be of the same underlying type.
|
||||||
func (g Graph) Equal(g2 Graph) bool {
|
func Equal(g, g2 Graph) bool {
|
||||||
if len(g.m) != len(g2.m) {
|
if len(g.Edges()) != len(g2.Edges()) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return g.edgesShared(g2)
|
return SubGraph(g, g2)
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,7 @@ func TestGraph(t *T) {
|
|||||||
chk := mchk.Checker{
|
chk := mchk.Checker{
|
||||||
Init: func() mchk.State {
|
Init: func() mchk.State {
|
||||||
return state{
|
return state{
|
||||||
|
Graph: Null,
|
||||||
m: map[string]Edge{},
|
m: map[string]Edge{},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -64,8 +65,8 @@ func TestGraph(t *T) {
|
|||||||
delete(s.m, edgeID(p.del))
|
delete(s.m, edgeID(p.del))
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // test Nodes and Edges methods
|
{ // test GetNodes and Edges methods
|
||||||
nodes := s.Graph.Nodes()
|
nodes := GetNodes(s.Graph)
|
||||||
edges := s.Graph.Edges()
|
edges := s.Graph.Edges()
|
||||||
var aa []massert.Assertion
|
var aa []massert.Assertion
|
||||||
vals := map[string]bool{}
|
vals := map[string]bool{}
|
||||||
@ -93,12 +94,12 @@ func TestGraph(t *T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{ // test Node and Has. Nodes has already been tested so we can use
|
{ // test GetNode and Has. GetNodes has already been tested so we
|
||||||
// its returned Nodes as the expected ones
|
// can use its returned Nodes as the expected ones
|
||||||
var aa []massert.Assertion
|
var aa []massert.Assertion
|
||||||
for _, expNode := range s.Graph.Nodes() {
|
for _, expNode := range GetNodes(s.Graph) {
|
||||||
var naa []massert.Assertion
|
var naa []massert.Assertion
|
||||||
node, ok := s.Graph.Node(expNode.Value)
|
node, ok := GetNode(s.Graph, expNode.Value)
|
||||||
naa = append(naa, massert.Equal(true, ok))
|
naa = append(naa, massert.Equal(true, ok))
|
||||||
naa = append(naa, massert.Equal(true, s.Graph.Has(expNode.Value)))
|
naa = append(naa, massert.Equal(true, s.Graph.Has(expNode.Value)))
|
||||||
naa = append(naa, massert.Subset(expNode.Ins, node.Ins))
|
naa = append(naa, massert.Subset(expNode.Ins, node.Ins))
|
||||||
@ -108,7 +109,7 @@ func TestGraph(t *T) {
|
|||||||
|
|
||||||
aa = append(aa, massert.Comment(massert.All(naa...), "v:%q", expNode.ID))
|
aa = append(aa, massert.Comment(massert.All(naa...), "v:%q", expNode.ID))
|
||||||
}
|
}
|
||||||
_, ok := s.Graph.Node(strV("zz"))
|
_, ok := GetNode(s.Graph, strV("zz"))
|
||||||
aa = append(aa, massert.Equal(false, ok))
|
aa = append(aa, massert.Equal(false, ok))
|
||||||
aa = append(aa, massert.Equal(false, s.Graph.Has(strV("zz"))))
|
aa = append(aa, massert.Equal(false, s.Graph.Has(strV("zz"))))
|
||||||
|
|
||||||
@ -140,7 +141,12 @@ func TestSubGraphAndEqual(t *T) {
|
|||||||
|
|
||||||
chk := mchk.Checker{
|
chk := mchk.Checker{
|
||||||
Init: func() mchk.State {
|
Init: func() mchk.State {
|
||||||
return state{expEqual: true, expSubGraph: true}
|
return state{
|
||||||
|
g1: Null,
|
||||||
|
g2: Null,
|
||||||
|
expEqual: true,
|
||||||
|
expSubGraph: true,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Next: func(ss mchk.State) mchk.Action {
|
Next: func(ss mchk.State) mchk.Action {
|
||||||
i := mrand.Intn(10)
|
i := mrand.Intn(10)
|
||||||
@ -162,11 +168,11 @@ func TestSubGraphAndEqual(t *T) {
|
|||||||
s.expSubGraph = s.expSubGraph && p.add1
|
s.expSubGraph = s.expSubGraph && p.add1
|
||||||
s.expEqual = s.expEqual && p.add1 && p.add2
|
s.expEqual = s.expEqual && p.add1 && p.add2
|
||||||
|
|
||||||
if s.g1.SubGraph(s.g2) != s.expSubGraph {
|
if SubGraph(s.g1, s.g2) != s.expSubGraph {
|
||||||
return nil, fmt.Errorf("SubGraph expected to return %v", s.expSubGraph)
|
return nil, fmt.Errorf("SubGraph expected to return %v", s.expSubGraph)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.g1.Equal(s.g2) != s.expEqual {
|
if Equal(s.g1, s.g2) != s.expEqual {
|
||||||
return nil, fmt.Errorf("Equal expected to return %v", s.expEqual)
|
return nil, fmt.Errorf("Equal expected to return %v", s.expEqual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,6 +203,7 @@ func TestDisjoinUnion(t *T) {
|
|||||||
chk := mchk.Checker{
|
chk := mchk.Checker{
|
||||||
Init: func() mchk.State {
|
Init: func() mchk.State {
|
||||||
return state{
|
return state{
|
||||||
|
g: Null,
|
||||||
valM: map[string][]Value{},
|
valM: map[string][]Value{},
|
||||||
disjM: map[string]Graph{},
|
disjM: map[string]Graph{},
|
||||||
}
|
}
|
||||||
@ -228,23 +235,26 @@ func TestDisjoinUnion(t *T) {
|
|||||||
s, p := ss.(state), a.Params.(params)
|
s, p := ss.(state), a.Params.(params)
|
||||||
s.g = s.g.Add(p.e)
|
s.g = s.g.Add(p.e)
|
||||||
s.valM[p.prefix] = append(s.valM[p.prefix], p.e.Head(), p.e.Tail())
|
s.valM[p.prefix] = append(s.valM[p.prefix], p.e.Head(), p.e.Tail())
|
||||||
|
if s.disjM[p.prefix] == nil {
|
||||||
|
s.disjM[p.prefix] = Null
|
||||||
|
}
|
||||||
s.disjM[p.prefix] = s.disjM[p.prefix].Add(p.e)
|
s.disjM[p.prefix] = s.disjM[p.prefix].Add(p.e)
|
||||||
|
|
||||||
var aa []massert.Assertion
|
var aa []massert.Assertion
|
||||||
|
|
||||||
// test Disjoin
|
// test Disjoin
|
||||||
disj := s.g.Disjoin()
|
disj := Disjoin(s.g)
|
||||||
for prefix, graph := range s.disjM {
|
for prefix, graph := range s.disjM {
|
||||||
aa = append(aa, massert.Comment(
|
aa = append(aa, massert.Comment(
|
||||||
massert.Equal(true, graph.Equal(s.disjM[prefix])),
|
massert.Equal(true, Equal(graph, s.disjM[prefix])),
|
||||||
"prefix:%q", prefix,
|
"prefix:%q", prefix,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
aa = append(aa, massert.Len(disj, len(s.disjM)))
|
aa = append(aa, massert.Len(disj, len(s.disjM)))
|
||||||
|
|
||||||
// now test Join
|
// now test Join
|
||||||
join := (Graph{}).Join(disj...)
|
join := Join(disj...)
|
||||||
aa = append(aa, massert.Equal(true, s.g.Equal(join)))
|
aa = append(aa, massert.Equal(true, Equal(s.g, join)))
|
||||||
|
|
||||||
return s, massert.All(aa...).Assert()
|
return s, massert.All(aa...).Assert()
|
||||||
},
|
},
|
||||||
@ -303,9 +313,10 @@ func TestVisitBreadth(t *T) {
|
|||||||
chk := mchk.Checker{
|
chk := mchk.Checker{
|
||||||
Init: func() mchk.State {
|
Init: func() mchk.State {
|
||||||
return state{
|
return state{
|
||||||
|
g: Null,
|
||||||
ranks: []map[string]bool{
|
ranks: []map[string]bool{
|
||||||
map[string]bool{"start": true},
|
{"start": true},
|
||||||
map[string]bool{},
|
{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -340,7 +351,7 @@ func TestVisitBreadth(t *T) {
|
|||||||
var err error
|
var err error
|
||||||
expRanks := s.ranks
|
expRanks := s.ranks
|
||||||
currRank := map[string]bool{}
|
currRank := map[string]bool{}
|
||||||
s.g.VisitBreadth(strV("start"), func(n Node) bool {
|
VisitBreadth(s.g, strV("start"), func(n Node) bool {
|
||||||
currRank[n.Value.ID] = true
|
currRank[n.Value.ID] = true
|
||||||
if len(currRank) != len(expRanks[0]) {
|
if len(currRank) != len(expRanks[0]) {
|
||||||
return true
|
return true
|
||||||
|
Loading…
Reference in New Issue
Block a user