graph/iterator: consolidate node iterators
MODIFY
graph/iterator/nodes.go
graph/iterator/nodes.go
package iterator
-import "gonum.org/v1/gonum/graph"
+import (
+ "reflect"
+
+ "gonum.org/v1/gonum/graph"
+)
// OrderedNodes implements the graph.Nodes and graph.NodeSlicer interfaces.
// The iteration order of OrderedNodes is the order of nodes passed to
@@ -123,3 +127,54 @@ func (n *ImplicitNodes) NodeSlice() []graph.Node {
}
return nodes
}
+
+// Nodes implements the graph.Nodes interfaces.
+// The iteration order of Nodes is randomized.
+type Nodes struct {
+ nodes reflect.Value
+ iter *reflect.MapIter
+ pos int
+ curr graph.Node
+}
+
+// NewNodes returns a Nodes initialized with the provided nodes, a
+// map of node IDs to graph.Nodes. No check is made that the keys
+// match the graph.Node IDs, and the map keys are not used.
+//
+// Behavior of the Nodes is unspecified if nodes is mutated after
+// the call the NewNodes.
+func NewNodes(nodes map[int64]graph.Node) *Nodes {
+ rv := reflect.ValueOf(nodes)
+ return &Nodes{nodes: rv, iter: rv.MapRange()}
+}
+
+// Len returns the remaining number of nodes to be iterated over.
+func (n *Nodes) Len() int {
+ return n.nodes.Len() - n.pos
+}
+
+// Next returns whether the next call of Node will return a valid node.
+func (n *Nodes) Next() bool {
+ if n.pos >= n.nodes.Len() {
+ return false
+ }
+ ok := n.iter.Next()
+ if ok {
+ n.pos++
+ n.curr = n.iter.Value().Interface().(graph.Node)
+ }
+ return ok
+}
+
+// Node returns the current node of the iterator. Next must have been
+// called prior to a call to Node.
+func (n *Nodes) Node() graph.Node {
+ return n.curr
+}
+
+// Reset returns the iterator to its initial state.
+func (n *Nodes) Reset() {
+ n.curr = nil
+ n.pos = 0
+ n.iter = n.nodes.MapRange()
+}

MODIFY
graph/iterator/nodes_test.go
graph/iterator/nodes_test.go
@@ -97,3 +97,49 @@ func TestImplicitNodesIterate(t *testing.T) {
}
}
}
+
+var nodesTests = []struct {
+ nodes map[int64]graph.Node
+}{
+ {nodes: nil},
+ {nodes: make(map[int64]graph.Node)},
+ {nodes: map[int64]graph.Node{1: simple.Node(1)}},
+ {nodes: map[int64]graph.Node{1: simple.Node(1), 2: simple.Node(2), 3: simple.Node(3), 5: simple.Node(5)}},
+ {nodes: map[int64]graph.Node{5: simple.Node(5), 3: simple.Node(3), 2: simple.Node(2), 1: simple.Node(1)}},
+}
+
+func TestNodesIterate(t *testing.T) {
+ for _, test := range nodesTests {
+ it := iterator.NewNodes(test.nodes)
+ for i := 0; i < 2; i++ {
+ if it.Len() != len(test.nodes) {
+ t.Errorf("unexpected iterator length for round %d: got:%d want:%d", i, it.Len(), len(test.nodes))
+ }
+ var got map[int64]graph.Node
+ if test.nodes != nil {
+ got = make(map[int64]graph.Node)
+ }
+ for it.Next() {
+ n := it.Node()
+ got[n.ID()] = n
+ if len(got)+it.Len() != len(test.nodes) {
+ t.Errorf("unexpected iterator length during iteration for round %d: got:%d want:%d", i, it.Len(), len(test.nodes))
+ }
+ }
+ want := test.nodes
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("unexpected iterator output for round %d: got:%#v want:%#v", i, got, want)
+ }
+ func() {
+ defer func() {
+ r := recover()
+ if r != nil {
+ t.Errorf("unexpected panic: %v", r)
+ }
+ }()
+ it.Next()
+ }()
+ it.Reset()
+ }
+ }
+}

graph/iterator: consolidate node iterators