From b1ac3140465306f9a66b04700082400ce81adb3b Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Fri, 19 May 2017 00:06:33 -0400 Subject: [PATCH] bump(github.com/gonum/graph): 50b27dea7ebbfb052dfaf91681afc6fde28d8796 --- third_party/BUILD | 1 + third_party/forked/gonum/graph/BUILD | 32 +++ third_party/forked/gonum/graph/LICENSE | 23 ++ third_party/forked/gonum/graph/README.md | 1 + third_party/forked/gonum/graph/graph.go | 153 +++++++++++ .../forked/gonum/graph/internal/linear/BUILD | 28 ++ .../gonum/graph/internal/linear/linear.go | 74 ++++++ third_party/forked/gonum/graph/simple/BUILD | 43 ++++ .../forked/gonum/graph/simple/simple.go | 45 ++++ .../forked/gonum/graph/simple/undirected.go | 241 ++++++++++++++++++ .../gonum/graph/simple/undirected_test.go | 63 +++++ third_party/forked/gonum/graph/traverse/BUILD | 32 +++ .../forked/gonum/graph/traverse/traverse.go | 186 ++++++++++++++ 13 files changed, 922 insertions(+) create mode 100644 third_party/forked/gonum/graph/BUILD create mode 100644 third_party/forked/gonum/graph/LICENSE create mode 100644 third_party/forked/gonum/graph/README.md create mode 100644 third_party/forked/gonum/graph/graph.go create mode 100644 third_party/forked/gonum/graph/internal/linear/BUILD create mode 100644 third_party/forked/gonum/graph/internal/linear/linear.go create mode 100644 third_party/forked/gonum/graph/simple/BUILD create mode 100644 third_party/forked/gonum/graph/simple/simple.go create mode 100644 third_party/forked/gonum/graph/simple/undirected.go create mode 100644 third_party/forked/gonum/graph/simple/undirected_test.go create mode 100644 third_party/forked/gonum/graph/traverse/BUILD create mode 100644 third_party/forked/gonum/graph/traverse/traverse.go diff --git a/third_party/BUILD b/third_party/BUILD index 801e43d5501..a100a2ca1ae 100644 --- a/third_party/BUILD +++ b/third_party/BUILD @@ -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"], diff --git a/third_party/forked/gonum/graph/BUILD b/third_party/forked/gonum/graph/BUILD new file mode 100644 index 00000000000..5fef90777fb --- /dev/null +++ b/third_party/forked/gonum/graph/BUILD @@ -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"], +) diff --git a/third_party/forked/gonum/graph/LICENSE b/third_party/forked/gonum/graph/LICENSE new file mode 100644 index 00000000000..76edf5ef7e9 --- /dev/null +++ b/third_party/forked/gonum/graph/LICENSE @@ -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. \ No newline at end of file diff --git a/third_party/forked/gonum/graph/README.md b/third_party/forked/gonum/graph/README.md new file mode 100644 index 00000000000..3b51e664a47 --- /dev/null +++ b/third_party/forked/gonum/graph/README.md @@ -0,0 +1 @@ +Forked from gonum/graph@50b27dea7ebbfb052dfaf91681afc6fde28d8796 to support memory-use improvements to the simple graph diff --git a/third_party/forked/gonum/graph/graph.go b/third_party/forked/gonum/graph/graph.go new file mode 100644 index 00000000000..adade5d79bb --- /dev/null +++ b/third_party/forked/gonum/graph/graph.go @@ -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)) + } + } +} diff --git a/third_party/forked/gonum/graph/internal/linear/BUILD b/third_party/forked/gonum/graph/internal/linear/BUILD new file mode 100644 index 00000000000..95b086fd354 --- /dev/null +++ b/third_party/forked/gonum/graph/internal/linear/BUILD @@ -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"], +) diff --git a/third_party/forked/gonum/graph/internal/linear/linear.go b/third_party/forked/gonum/graph/internal/linear/linear.go new file mode 100644 index 00000000000..ce7c6cfffdf --- /dev/null +++ b/third_party/forked/gonum/graph/internal/linear/linear.go @@ -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] +} diff --git a/third_party/forked/gonum/graph/simple/BUILD b/third_party/forked/gonum/graph/simple/BUILD new file mode 100644 index 00000000000..a5c9b446a4b --- /dev/null +++ b/third_party/forked/gonum/graph/simple/BUILD @@ -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"], +) diff --git a/third_party/forked/gonum/graph/simple/simple.go b/third_party/forked/gonum/graph/simple/simple.go new file mode 100644 index 00000000000..9bc56b8be63 --- /dev/null +++ b/third_party/forked/gonum/graph/simple/simple.go @@ -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)) +} diff --git a/third_party/forked/gonum/graph/simple/undirected.go b/third_party/forked/gonum/graph/simple/undirected.go new file mode 100644 index 00000000000..1d08d0cc271 --- /dev/null +++ b/third_party/forked/gonum/graph/simple/undirected.go @@ -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()]) +} diff --git a/third_party/forked/gonum/graph/simple/undirected_test.go b/third_party/forked/gonum/graph/simple/undirected_test.go new file mode 100644 index 00000000000..42c1f606170 --- /dev/null +++ b/third_party/forked/gonum/graph/simple/undirected_test.go @@ -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) +} diff --git a/third_party/forked/gonum/graph/traverse/BUILD b/third_party/forked/gonum/graph/traverse/BUILD new file mode 100644 index 00000000000..ec40a55dd58 --- /dev/null +++ b/third_party/forked/gonum/graph/traverse/BUILD @@ -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"], +) diff --git a/third_party/forked/gonum/graph/traverse/traverse.go b/third_party/forked/gonum/graph/traverse/traverse.go new file mode 100644 index 00000000000..cc361c85fe3 --- /dev/null +++ b/third_party/forked/gonum/graph/traverse/traverse.go @@ -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() + } +}