diff --git a/topo/topo.go b/topo/topo.go index 0696a83f..23568bb7 100644 --- a/topo/topo.go +++ b/topo/topo.go @@ -38,6 +38,16 @@ func IsPathIn(g graph.Graph, path []graph.Node) bool { } } +// PathExistsIn returns whether there is a path in g starting at from extending +// to to. +// +// PathExistsIn exists as a helper function. If many tests for path existence +// are being performed, other approaches will be more efficient. +func PathExistsIn(g graph.Graph, from, to graph.Node) bool { + var t traverse.BreadthFirst + return t.Walk(g, from, func(n graph.Node, _ int) bool { return n.ID() == to.ID() }) != nil +} + // ConnectedComponents returns the connected components of the undirected graph g. func ConnectedComponents(g graph.Undirected) [][]graph.Node { var ( diff --git a/topo/topo_test.go b/topo/topo_test.go index 74c603dc..1cd086a4 100644 --- a/topo/topo_test.go +++ b/topo/topo_test.go @@ -54,6 +54,81 @@ func TestIsPath(t *testing.T) { } } +var pathExistsInUndirectedTests = []struct { + g []intset + from, to int + want bool +}{ + {g: batageljZaversnikGraph, from: 0, to: 0, want: true}, + {g: batageljZaversnikGraph, from: 0, to: 1, want: false}, + {g: batageljZaversnikGraph, from: 1, to: 2, want: true}, + {g: batageljZaversnikGraph, from: 2, to: 1, want: true}, + {g: batageljZaversnikGraph, from: 2, to: 12, want: false}, + {g: batageljZaversnikGraph, from: 20, to: 6, want: true}, +} + +func TestPathExistsInUndirected(t *testing.T) { + for i, test := range pathExistsInUndirectedTests { + g := simple.NewUndirectedGraph(0, math.Inf(1)) + + for u, e := range test.g { + if !g.Has(simple.Node(u)) { + g.AddNode(simple.Node(u)) + } + for v := range e { + if !g.Has(simple.Node(v)) { + g.AddNode(simple.Node(v)) + } + g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) + } + } + + got := PathExistsIn(g, simple.Node(test.from), simple.Node(test.to)) + if got != test.want { + t.Errorf("unexpected result for path existance in test %d: got:%t want %t", i, got, test.want) + } + } +} + +var pathExistsInDirectedTests = []struct { + g []intset + from, to int + want bool +}{ + // The graph definition is such that from node IDs are + // less than to node IDs. + {g: batageljZaversnikGraph, from: 0, to: 0, want: true}, + {g: batageljZaversnikGraph, from: 0, to: 1, want: false}, + {g: batageljZaversnikGraph, from: 1, to: 2, want: true}, + {g: batageljZaversnikGraph, from: 2, to: 1, want: false}, + {g: batageljZaversnikGraph, from: 2, to: 12, want: false}, + {g: batageljZaversnikGraph, from: 20, to: 6, want: false}, + {g: batageljZaversnikGraph, from: 6, to: 20, want: true}, +} + +func TestPathExistsInDirected(t *testing.T) { + for i, test := range pathExistsInDirectedTests { + g := simple.NewDirectedGraph(0, math.Inf(1)) + + for u, e := range test.g { + if !g.Has(simple.Node(u)) { + g.AddNode(simple.Node(u)) + } + for v := range e { + if !g.Has(simple.Node(v)) { + g.AddNode(simple.Node(v)) + } + g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) + } + } + + got := PathExistsIn(g, simple.Node(test.from), simple.Node(test.to)) + if got != test.want { + t.Errorf("unexpected result for path existance in test %d: got:%t want %t", i, got, test.want) + } + } +} + var connectedComponentTests = []struct { g []intset want [][]int