bump(github.com/gonum/graph): 50b27dea7ebbfb052dfaf91681afc6fde28d8796

This commit is contained in:
Jordan Liggitt 2017-05-19 00:06:33 -04:00
parent 986cdfa6ff
commit b1ac314046
No known key found for this signature in database
GPG Key ID: 24E7ADF9A3B42012
13 changed files with 922 additions and 0 deletions

1
third_party/BUILD vendored
View File

@ -20,6 +20,7 @@ filegroup(
"//third_party/forked/golang/expansion:all-srcs",
"//third_party/forked/golang/reflect:all-srcs",
"//third_party/forked/golang/template:all-srcs",
"//third_party/forked/gonum/graph:all-srcs",
"//third_party/htpasswd:all-srcs",
],
tags = ["automanaged"],

32
third_party/forked/gonum/graph/BUILD vendored Normal file
View File

@ -0,0 +1,32 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["graph.go"],
tags = ["automanaged"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//third_party/forked/gonum/graph/internal/linear:all-srcs",
"//third_party/forked/gonum/graph/simple:all-srcs",
"//third_party/forked/gonum/graph/traverse:all-srcs",
],
tags = ["automanaged"],
)

23
third_party/forked/gonum/graph/LICENSE vendored Normal file
View File

@ -0,0 +1,23 @@
Copyright ©2013 The gonum Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the gonum project nor the names of its authors and
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1 @@
Forked from gonum/graph@50b27dea7ebbfb052dfaf91681afc6fde28d8796 to support memory-use improvements to the simple graph

153
third_party/forked/gonum/graph/graph.go vendored Normal file
View File

@ -0,0 +1,153 @@
// Copyright ©2014 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package graph
// Node is a graph node. It returns a graph-unique integer ID.
type Node interface {
ID() int
}
// Edge is a graph edge. In directed graphs, the direction of the
// edge is given from -> to, otherwise the edge is semantically
// unordered.
type Edge interface {
From() Node
To() Node
Weight() float64
}
// Graph is a generalized graph.
type Graph interface {
// Has returns whether the node exists within the graph.
Has(Node) bool
// Nodes returns all the nodes in the graph.
Nodes() []Node
// From returns all nodes that can be reached directly
// from the given node.
From(Node) []Node
// HasEdgeBeteen returns whether an edge exists between
// nodes x and y without considering direction.
HasEdgeBetween(x, y Node) bool
// Edge returns the edge from u to v if such an edge
// exists and nil otherwise. The node v must be directly
// reachable from u as defined by the From method.
Edge(u, v Node) Edge
}
// Undirected is an undirected graph.
type Undirected interface {
Graph
// EdgeBetween returns the edge between nodes x and y.
EdgeBetween(x, y Node) Edge
}
// Directed is a directed graph.
type Directed interface {
Graph
// HasEdgeFromTo returns whether an edge exists
// in the graph from u to v.
HasEdgeFromTo(u, v Node) bool
// To returns all nodes that can reach directly
// to the given node.
To(Node) []Node
}
// Weighter defines graphs that can report edge weights.
type Weighter interface {
// Weight returns the weight for the edge between
// x and y if Edge(x, y) returns a non-nil Edge.
// If x and y are the same node or there is no
// joining edge between the two nodes the weight
// value returned is implementation dependent.
// Weight returns true if an edge exists between
// x and y or if x and y have the same ID, false
// otherwise.
Weight(x, y Node) (w float64, ok bool)
}
// NodeAdder is an interface for adding arbitrary nodes to a graph.
type NodeAdder interface {
// NewNodeID returns a new unique arbitrary ID.
NewNodeID() int
// Adds a node to the graph. AddNode panics if
// the added node ID matches an existing node ID.
AddNode(Node)
}
// NodeRemover is an interface for removing nodes from a graph.
type NodeRemover interface {
// RemoveNode removes a node from the graph, as
// well as any edges attached to it. If the node
// is not in the graph it is a no-op.
RemoveNode(Node)
}
// EdgeSetter is an interface for adding edges to a graph.
type EdgeSetter interface {
// SetEdge adds an edge from one node to another.
// If the graph supports node addition the nodes
// will be added if they do not exist, otherwise
// SetEdge will panic.
// If the IDs returned by e.From and e.To are
// equal, SetEdge will panic.
SetEdge(e Edge)
}
// EdgeRemover is an interface for removing nodes from a graph.
type EdgeRemover interface {
// RemoveEdge removes the given edge, leaving the
// terminal nodes. If the edge does not exist it
// is a no-op.
RemoveEdge(Edge)
}
// Builder is a graph that can have nodes and edges added.
type Builder interface {
NodeAdder
EdgeSetter
}
// UndirectedBuilder is an undirected graph builder.
type UndirectedBuilder interface {
Undirected
Builder
}
// DirectedBuilder is a directed graph builder.
type DirectedBuilder interface {
Directed
Builder
}
// Copy copies nodes and edges as undirected edges from the source to the destination
// without first clearing the destination. Copy will panic if a node ID in the source
// graph matches a node ID in the destination.
//
// If the source is undirected and the destination is directed both directions will
// be present in the destination after the copy is complete.
//
// If the source is a directed graph, the destination is undirected, and a fundamental
// cycle exists with two nodes where the edge weights differ, the resulting destination
// graph's edge weight between those nodes is undefined. If there is a defined function
// to resolve such conflicts, an Undirect may be used to do this.
func Copy(dst Builder, src Graph) {
nodes := src.Nodes()
for _, n := range nodes {
dst.AddNode(n)
}
for _, u := range nodes {
for _, v := range src.From(u) {
dst.SetEdge(src.Edge(u, v))
}
}
}

View File

@ -0,0 +1,28 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["linear.go"],
tags = ["automanaged"],
deps = ["//third_party/forked/gonum/graph:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,74 @@
// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package linear provides common linear data structures.
package linear
import (
"k8s.io/kubernetes/third_party/forked/gonum/graph"
)
// NodeStack implements a LIFO stack of graph.Node.
type NodeStack []graph.Node
// Len returns the number of graph.Nodes on the stack.
func (s *NodeStack) Len() int { return len(*s) }
// Pop returns the last graph.Node on the stack and removes it
// from the stack.
func (s *NodeStack) Pop() graph.Node {
v := *s
v, n := v[:len(v)-1], v[len(v)-1]
*s = v
return n
}
// Push adds the node n to the stack at the last position.
func (s *NodeStack) Push(n graph.Node) { *s = append(*s, n) }
// NodeQueue implements a FIFO queue.
type NodeQueue struct {
head int
data []graph.Node
}
// Len returns the number of graph.Nodes in the queue.
func (q *NodeQueue) Len() int { return len(q.data) - q.head }
// Enqueue adds the node n to the back of the queue.
func (q *NodeQueue) Enqueue(n graph.Node) {
if len(q.data) == cap(q.data) && q.head > 0 {
l := q.Len()
copy(q.data, q.data[q.head:])
q.head = 0
q.data = append(q.data[:l], n)
} else {
q.data = append(q.data, n)
}
}
// Dequeue returns the graph.Node at the front of the queue and
// removes it from the queue.
func (q *NodeQueue) Dequeue() graph.Node {
if q.Len() == 0 {
panic("queue: empty queue")
}
var n graph.Node
n, q.data[q.head] = q.data[q.head], nil
q.head++
if q.Len() == 0 {
q.head = 0
q.data = q.data[:0]
}
return n
}
// Reset clears the queue for reuse.
func (q *NodeQueue) Reset() {
q.head = 0
q.data = q.data[:0]
}

View File

@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = ["undirected_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = ["//third_party/forked/gonum/graph:go_default_library"],
)
go_library(
name = "go_default_library",
srcs = [
"simple.go",
"undirected.go",
],
tags = ["automanaged"],
deps = [
"//third_party/forked/gonum/graph:go_default_library",
"//vendor/golang.org/x/tools/container/intsets:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,45 @@
// Copyright ©2014 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package simple provides a suite of simple graph implementations satisfying
// the gonum/graph interfaces.
package simple
import (
"math"
"k8s.io/kubernetes/third_party/forked/gonum/graph"
)
// Node is a simple graph node.
type Node int
// ID returns the ID number of the node.
func (n Node) ID() int {
return int(n)
}
// Edge is a simple graph edge.
type Edge struct {
F, T graph.Node
W float64
}
// From returns the from-node of the edge.
func (e Edge) From() graph.Node { return e.F }
// To returns the to-node of the edge.
func (e Edge) To() graph.Node { return e.T }
// Weight returns the weight of the edge.
func (e Edge) Weight() float64 { return e.W }
// maxInt is the maximum value of the machine-dependent int type.
const maxInt int = int(^uint(0) >> 1)
// isSame returns whether two float64 values are the same where NaN values
// are equalable.
func isSame(a, b float64) bool {
return a == b || (math.IsNaN(a) && math.IsNaN(b))
}

View File

@ -0,0 +1,241 @@
// Copyright ©2014 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package simple
import (
"fmt"
"golang.org/x/tools/container/intsets"
"k8s.io/kubernetes/third_party/forked/gonum/graph"
)
// UndirectedGraph implements a generalized undirected graph.
type UndirectedGraph struct {
nodes map[int]graph.Node
edges map[int]map[int]graph.Edge
self, absent float64
freeIDs intsets.Sparse
usedIDs intsets.Sparse
}
// NewUndirectedGraph returns an UndirectedGraph with the specified self and absent
// edge weight values.
func NewUndirectedGraph(self, absent float64) *UndirectedGraph {
return &UndirectedGraph{
nodes: make(map[int]graph.Node),
edges: make(map[int]map[int]graph.Edge),
self: self,
absent: absent,
}
}
// NewNodeID returns a new unique ID for a node to be added to g. The returned ID does
// not become a valid ID in g until it is added to g.
func (g *UndirectedGraph) NewNodeID() int {
if len(g.nodes) == 0 {
return 0
}
if len(g.nodes) == maxInt {
panic(fmt.Sprintf("simple: cannot allocate node: no slot"))
}
var id int
if g.freeIDs.Len() != 0 && g.freeIDs.TakeMin(&id) {
return id
}
if id = g.usedIDs.Max(); id < maxInt {
return id + 1
}
for id = 0; id < maxInt; id++ {
if !g.usedIDs.Has(id) {
return id
}
}
panic("unreachable")
}
// AddNode adds n to the graph. It panics if the added node ID matches an existing node ID.
func (g *UndirectedGraph) AddNode(n graph.Node) {
if _, exists := g.nodes[n.ID()]; exists {
panic(fmt.Sprintf("simple: node ID collision: %d", n.ID()))
}
g.nodes[n.ID()] = n
g.edges[n.ID()] = make(map[int]graph.Edge)
g.freeIDs.Remove(n.ID())
g.usedIDs.Insert(n.ID())
}
// RemoveNode removes n from the graph, as well as any edges attached to it. If the node
// is not in the graph it is a no-op.
func (g *UndirectedGraph) RemoveNode(n graph.Node) {
if _, ok := g.nodes[n.ID()]; !ok {
return
}
delete(g.nodes, n.ID())
for from := range g.edges[n.ID()] {
delete(g.edges[from], n.ID())
}
delete(g.edges, n.ID())
g.freeIDs.Insert(n.ID())
g.usedIDs.Remove(n.ID())
}
// SetEdge adds e, an edge from one node to another. If the nodes do not exist, they are added.
// It will panic if the IDs of the e.From and e.To are equal.
func (g *UndirectedGraph) SetEdge(e graph.Edge) {
var (
from = e.From()
fid = from.ID()
to = e.To()
tid = to.ID()
)
if fid == tid {
panic("simple: adding self edge")
}
if !g.Has(from) {
g.AddNode(from)
}
if !g.Has(to) {
g.AddNode(to)
}
g.edges[fid][tid] = e
g.edges[tid][fid] = e
}
// RemoveEdge removes e from the graph, leaving the terminal nodes. If the edge does not exist
// it is a no-op.
func (g *UndirectedGraph) RemoveEdge(e graph.Edge) {
from, to := e.From(), e.To()
if _, ok := g.nodes[from.ID()]; !ok {
return
}
if _, ok := g.nodes[to.ID()]; !ok {
return
}
delete(g.edges[from.ID()], to.ID())
delete(g.edges[to.ID()], from.ID())
}
// Node returns the node in the graph with the given ID.
func (g *UndirectedGraph) Node(id int) graph.Node {
return g.nodes[id]
}
// Has returns whether the node exists within the graph.
func (g *UndirectedGraph) Has(n graph.Node) bool {
_, ok := g.nodes[n.ID()]
return ok
}
// Nodes returns all the nodes in the graph.
func (g *UndirectedGraph) Nodes() []graph.Node {
nodes := make([]graph.Node, len(g.nodes))
i := 0
for _, n := range g.nodes {
nodes[i] = n
i++
}
return nodes
}
// Edges returns all the edges in the graph.
func (g *UndirectedGraph) Edges() []graph.Edge {
var edges []graph.Edge
seen := make(map[[2]int]struct{})
for _, u := range g.edges {
for _, e := range u {
uid := e.From().ID()
vid := e.To().ID()
if _, ok := seen[[2]int{uid, vid}]; ok {
continue
}
seen[[2]int{uid, vid}] = struct{}{}
seen[[2]int{vid, uid}] = struct{}{}
edges = append(edges, e)
}
}
return edges
}
// From returns all nodes in g that can be reached directly from n.
func (g *UndirectedGraph) From(n graph.Node) []graph.Node {
if !g.Has(n) {
return nil
}
nodes := make([]graph.Node, len(g.edges[n.ID()]))
i := 0
for from := range g.edges[n.ID()] {
nodes[i] = g.nodes[from]
i++
}
return nodes
}
// HasEdgeBetween returns whether an edge exists between nodes x and y.
func (g *UndirectedGraph) HasEdgeBetween(x, y graph.Node) bool {
_, ok := g.edges[x.ID()][y.ID()]
return ok
}
// Edge returns the edge from u to v if such an edge exists and nil otherwise.
// The node v must be directly reachable from u as defined by the From method.
func (g *UndirectedGraph) Edge(u, v graph.Node) graph.Edge {
return g.EdgeBetween(u, v)
}
// EdgeBetween returns the edge between nodes x and y.
func (g *UndirectedGraph) EdgeBetween(x, y graph.Node) graph.Edge {
// We don't need to check if neigh exists because
// it's implicit in the edges access.
if !g.Has(x) {
return nil
}
return g.edges[x.ID()][y.ID()]
}
// Weight returns the weight for the edge between x and y if Edge(x, y) returns a non-nil Edge.
// If x and y are the same node or there is no joining edge between the two nodes the weight
// value returned is either the graph's absent or self value. Weight returns true if an edge
// exists between x and y or if x and y have the same ID, false otherwise.
func (g *UndirectedGraph) Weight(x, y graph.Node) (w float64, ok bool) {
xid := x.ID()
yid := y.ID()
if xid == yid {
return g.self, true
}
if n, ok := g.edges[xid]; ok {
if e, ok := n[yid]; ok {
return e.Weight(), true
}
}
return g.absent, false
}
// Degree returns the degree of n in g.
func (g *UndirectedGraph) Degree(n graph.Node) int {
if _, ok := g.nodes[n.ID()]; !ok {
return 0
}
return len(g.edges[n.ID()])
}

View File

@ -0,0 +1,63 @@
// Copyright ©2014 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package simple
import (
"math"
"testing"
"k8s.io/kubernetes/third_party/forked/gonum/graph"
)
var _ graph.Graph = (*UndirectedGraph)(nil)
func TestAssertMutableNotDirected(t *testing.T) {
var g graph.UndirectedBuilder = NewUndirectedGraph(0, math.Inf(1))
if _, ok := g.(graph.Directed); ok {
t.Fatal("Graph is directed, but a MutableGraph cannot safely be directed!")
}
}
func TestMaxID(t *testing.T) {
g := NewUndirectedGraph(0, math.Inf(1))
nodes := make(map[graph.Node]struct{})
for i := Node(0); i < 3; i++ {
g.AddNode(i)
nodes[i] = struct{}{}
}
g.RemoveNode(Node(0))
delete(nodes, Node(0))
g.RemoveNode(Node(2))
delete(nodes, Node(2))
n := Node(g.NewNodeID())
g.AddNode(n)
if !g.Has(n) {
t.Error("added node does not exist in graph")
}
if _, exists := nodes[n]; exists {
t.Errorf("Created already existing node id: %v", n.ID())
}
}
// Test for issue #123 https://github.com/gonum/graph/issues/123
func TestIssue123UndirectedGraph(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Errorf("unexpected panic: %v", r)
}
}()
g := NewUndirectedGraph(0, math.Inf(1))
n0 := Node(g.NewNodeID())
g.AddNode(n0)
n1 := Node(g.NewNodeID())
g.AddNode(n1)
g.RemoveNode(n0)
n2 := Node(g.NewNodeID())
g.AddNode(n2)
}

View File

@ -0,0 +1,32 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["traverse.go"],
tags = ["automanaged"],
deps = [
"//third_party/forked/gonum/graph:go_default_library",
"//third_party/forked/gonum/graph/internal/linear:go_default_library",
"//vendor/golang.org/x/tools/container/intsets:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,186 @@
// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package traverse provides basic graph traversal primitives.
package traverse
import (
"golang.org/x/tools/container/intsets"
"k8s.io/kubernetes/third_party/forked/gonum/graph"
"k8s.io/kubernetes/third_party/forked/gonum/graph/internal/linear"
)
// BreadthFirst implements stateful breadth-first graph traversal.
type BreadthFirst struct {
EdgeFilter func(graph.Edge) bool
Visit func(u, v graph.Node)
queue linear.NodeQueue
visited *intsets.Sparse
}
// Walk performs a breadth-first traversal of the graph g starting from the given node,
// depending on the the EdgeFilter field and the until parameter if they are non-nil. The
// traversal follows edges for which EdgeFilter(edge) is true and returns the first node
// for which until(node, depth) is true. During the traversal, if the Visit field is
// non-nil, it is called with the nodes joined by each followed edge.
func (b *BreadthFirst) Walk(g graph.Graph, from graph.Node, until func(n graph.Node, d int) bool) graph.Node {
if b.visited == nil {
b.visited = &intsets.Sparse{}
}
b.queue.Enqueue(from)
b.visited.Insert(from.ID())
var (
depth int
children int
untilNext = 1
)
for b.queue.Len() > 0 {
t := b.queue.Dequeue()
if until != nil && until(t, depth) {
return t
}
for _, n := range g.From(t) {
if b.EdgeFilter != nil && !b.EdgeFilter(g.Edge(t, n)) {
continue
}
if b.visited.Has(n.ID()) {
continue
}
if b.Visit != nil {
b.Visit(t, n)
}
b.visited.Insert(n.ID())
children++
b.queue.Enqueue(n)
}
if untilNext--; untilNext == 0 {
depth++
untilNext = children
children = 0
}
}
return nil
}
// WalkAll calls Walk for each unvisited node of the graph g using edges independent
// of their direction. The functions before and after are called prior to commencing
// and after completing each walk if they are non-nil respectively. The function
// during is called on each node as it is traversed.
func (b *BreadthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
b.Reset()
for _, from := range g.Nodes() {
if b.Visited(from) {
continue
}
if before != nil {
before()
}
b.Walk(g, from, func(n graph.Node, _ int) bool {
if during != nil {
during(n)
}
return false
})
if after != nil {
after()
}
}
}
// Visited returned whether the node n was visited during a traverse.
func (b *BreadthFirst) Visited(n graph.Node) bool {
return b.visited != nil && b.visited.Has(n.ID())
}
// Reset resets the state of the traverser for reuse.
func (b *BreadthFirst) Reset() {
b.queue.Reset()
if b.visited != nil {
b.visited.Clear()
}
}
// DepthFirst implements stateful depth-first graph traversal.
type DepthFirst struct {
EdgeFilter func(graph.Edge) bool
Visit func(u, v graph.Node)
stack linear.NodeStack
visited *intsets.Sparse
}
// Walk performs a depth-first traversal of the graph g starting from the given node,
// depending on the the EdgeFilter field and the until parameter if they are non-nil. The
// traversal follows edges for which EdgeFilter(edge) is true and returns the first node
// for which until(node) is true. During the traversal, if the Visit field is non-nil, it
// is called with the nodes joined by each followed edge.
func (d *DepthFirst) Walk(g graph.Graph, from graph.Node, until func(graph.Node) bool) graph.Node {
if d.visited == nil {
d.visited = &intsets.Sparse{}
}
d.stack.Push(from)
d.visited.Insert(from.ID())
for d.stack.Len() > 0 {
t := d.stack.Pop()
if until != nil && until(t) {
return t
}
for _, n := range g.From(t) {
if d.EdgeFilter != nil && !d.EdgeFilter(g.Edge(t, n)) {
continue
}
if d.visited.Has(n.ID()) {
continue
}
if d.Visit != nil {
d.Visit(t, n)
}
d.visited.Insert(n.ID())
d.stack.Push(n)
}
}
return nil
}
// WalkAll calls Walk for each unvisited node of the graph g using edges independent
// of their direction. The functions before and after are called prior to commencing
// and after completing each walk if they are non-nil respectively. The function
// during is called on each node as it is traversed.
func (d *DepthFirst) WalkAll(g graph.Undirected, before, after func(), during func(graph.Node)) {
d.Reset()
for _, from := range g.Nodes() {
if d.Visited(from) {
continue
}
if before != nil {
before()
}
d.Walk(g, from, func(n graph.Node) bool {
if during != nil {
during(n)
}
return false
})
if after != nil {
after()
}
}
}
// Visited returned whether the node n was visited during a traverse.
func (d *DepthFirst) Visited(n graph.Node) bool {
return d.visited != nil && d.visited.Has(n.ID())
}
// Reset resets the state of the traverser for reuse.
func (d *DepthFirst) Reset() {
d.stack = d.stack[:0]
if d.visited != nil {
d.visited.Clear()
}
}