Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/yourbasic/graph
Browse files Browse the repository at this point in the history
  • Loading branch information
korthaj committed Jun 2, 2017
2 parents 4071e42 + 23ae8ac commit 4e527a4
Show file tree
Hide file tree
Showing 13 changed files with 94 additions and 109 deletions.
3 changes: 2 additions & 1 deletion bipart.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ func Bipartition(g Iterator) (part []int, ok bool) {
colors[v] = white
whiteCount++
for queue := []int{v}; len(queue) > 0; {
v, queue = queue[0], queue[1:]
v := queue[0]
queue = queue[1:]
if g.Visit(v, func(w int, _ int64) (skip bool) {
switch {
case colors[w] != none:
Expand Down
49 changes: 25 additions & 24 deletions build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
// to turn on caching for any component. This gives constant time
// performance for all basic operations on that component.
//
// Example usage
// Tutorial
//
// The package examples show how to build graphs from standard components
// using composition and filtering. They also demonstrate how to apply
// a cost function to a virtual graph.
// The Euclid and Maxflow examples show how to build graphs from
// standard components using composition and filtering. They also
// demonstrate how to apply a cost function to a virtual graph.
//
package build

Expand All @@ -41,21 +41,25 @@ import (
// by composing and filtering a set of standard graphs, or by writing
// functions that describe the edges of a graph.
type Virtual struct {
// The `order` field is, in fact, a constant function.
// It returns the number of vertices in the graph.
order int
// The edge and cost functions define a weighted graph without self-loops.

// The `edge` and `cost` functions define a weighted graph without self-loops.
//
// • edge(v, w) returns true whenever (v, w) belongs to the graph;
// the value is disregarded when v == w.
//
// • cost(v, w) returns the cost of (v, w); the value is disregarded
// when edge(v, w) is false.
// • cost(v, w) returns the cost of (v, w);
// the value is disregarded when edge(v, w) is false.
//
edge func(v, w int) bool
cost func(v, w int) int64

// These functions can be used to improve performance.
// They MUST BE CONSISTENT with edge and cost.
// The generic() factory method contains a basic implementation.
// The Consistent test function should be used to check compliance.
// The `degree` and `visit` functions can be used to improve performance.
// They MUST BE CONSISTENT with edge and cost. If not implemented,
// the `generic` or `generic0` implementation is used instead.
// The `Consistent` test function should be used to check compliance.
//
// • degree(v) returns the outdegree of vertex v.
//
Expand All @@ -64,12 +68,13 @@ type Virtual struct {
// If a call to do returns true, visit MUST ABORT the iteration
// and return true; if successful it should return false.
// Precondition: a ≥ 0.
//
degree func(v int) int
visit func(v int, a int, do func(w int, c int64) (skip bool)) (aborted bool)
}

// FilterFunc is a function that tells if there is a directed edge from v to w.
// The nil value represents an edge functions that always returns true.
// The nil value represents an edge function that always returns true.
type FilterFunc func(v, w int) bool

// CostFunc is a function that computes the cost of an edge from v to w.
Expand Down Expand Up @@ -107,7 +112,7 @@ func max(m, n int) int {
return m
}

// null is the null graph; a graph of order 0.
// null is the null graph; a graph with no vertices.
var null = new(Virtual)

// singleton returns a graph with one vertex.
Expand All @@ -126,7 +131,7 @@ func edge() *Virtual {
g := &Virtual{
order: 2,
cost: zero,
edge: func(v, w int) bool { return v != w },
edge: alwaysEdge,
degree: degreeOne,
}
g.visit = func(v int, a int, do func(w int, c int64) bool) (aborted bool) {
Expand Down Expand Up @@ -260,17 +265,16 @@ func Generic(n int, edge FilterFunc) *Virtual {
return generic0(n, edge)
}

// Specific returns a cached copy of g with constant time performance
// for all basic operations. It uses space proportional to
// the size of the graph.
// Specific returns a cached copy of g with constant time performance for
// all basic operations. It uses space proportional to the size of the graph.
//
// This function does not accept multigraphs and graphs with self-loops.
func Specific(g graph.Iterator) *Virtual {
stats := graph.Check(g)
h := graph.Sort(g)
stats := graph.Check(h)
if stats.Multi != 0 || stats.Loops != 0 {
panic("Virtual doesn't support multiple edges or self-loops")
}
h := graph.Sort(g)
res := &Virtual{
order: h.Order(),
edge: h.Edge,
Expand All @@ -282,7 +286,7 @@ func Specific(g graph.Iterator) *Virtual {
return res
}
res.cost = func(v, w int) (cost int64) {
if !res.edge(v, w) {
if !h.Edge(v, w) {
return 0
}
h.VisitFrom(v, w, func(w int, c int64) (skip bool) {
Expand Down Expand Up @@ -353,10 +357,7 @@ func (g *Virtual) Complement() *Virtual {
return singleton()
}
res := generic0(n, func(v, w int) (edge bool) {
if v != w {
return !g.edge(v, w)
}
return
return v != w && !g.edge(v, w)
})
res.degree = func(v int) int { return n - 1 - g.degree(v) }
res.visit = func(v int, a int, do func(w int, c int64) bool) (aborted bool) {
Expand Down
2 changes: 1 addition & 1 deletion build/edgeset.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (g *Virtual) Delete(e EdgeSet) *Virtual {
})
}

// NewEdges returns a virtual graph with n vertices and all edges
// newEdges returns a virtual graph with n vertices and all edges
// belonging to the edge set.
func newEdges(n int, e EdgeSet) *Virtual {
switch {
Expand Down
1 change: 0 additions & 1 deletion build/join.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ func (g1 *Virtual) Join(g2 *Virtual, bridge EdgeSet) *Virtual {
default:
return bridge.Cost(v, w)
}
return bridge.Cost(v, w)
}

var noFilter bool
Expand Down
4 changes: 2 additions & 2 deletions build/vertexset.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type interval struct {
index int // index of a in the whole set
}

// updates the index values.
// update updates the index values.
func (s VertexSet) update() {
prev := 0
for i, in := range s.set {
Expand All @@ -46,7 +46,7 @@ func Range(a, b int) VertexSet {

// Vertex returns a set containing the single vertex v.
func Vertex(v int) VertexSet {
return Range(v, v+1)
return VertexSet{[]interval{{v, v + 1, 0}}}
}

// size returns the number of elements in this set, or -1 for the universe.
Expand Down
88 changes: 40 additions & 48 deletions euler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ package graph
// If no such walk exists, it returns an empty walk and sets ok to false.
func EulerDirected(g Iterator) (walk []int, ok bool) {
n := g.Order()
// Compute outdegree - indegree for each vertex.
degree := make([]int, n)
degree := make([]int, n) // outdegree - indegree for each vertex
edgeCount := 0
for v := range degree {
g.Visit(v, func(w int, _ int64) (skip bool) {
edgeCount++
degree[v]++
degree[w]--
return
})
}
if edgeCount == 0 {
return []int{}, true
}

start, end := -1, -1
for v := range degree {
Expand All @@ -28,42 +32,38 @@ func EulerDirected(g Iterator) (walk []int, ok bool) {
}

// Make a copy of g
edgeCount := 0
h := make([][]int, n)
for v := range h {
g.Visit(v, func(w int, _ int64) (skip bool) {
h[v] = append(h[v], w)
edgeCount++
return
})
}
if edgeCount == 0 {
return []int{}, true
}

// Find a starting point with neighbors.
for v := 0; v < n && start == -1; v++ {
if len(h[v]) > 0 {
start = v
if start == -1 {
for v, neighbors := range h {
if len(neighbors) > 0 {
start = v
break
}
}
}

stack := []int{start}
for len(stack) > 0 {
v := stack[len(stack)-1]
stack = stack[:len(stack)-1]
for stack := []int{start}; len(stack) > 0; {
n := len(stack)
v := stack[n-1]
stack = stack[:n-1]
for len(h[v]) > 0 {
stack = append(stack, v)
v, h[v] = h[v][0], h[v][1:]
edgeCount--
}
walk = append(walk, v)
}

if edgeCount != 0 {
if edgeCount > 0 {
return []int{}, false
}

for i, j := 0, len(walk)-1; i < j; i, j = i+1, j-1 {
walk[i], walk[j] = walk[j], walk[i]
}
Expand All @@ -75,16 +75,20 @@ func EulerDirected(g Iterator) (walk []int, ok bool) {
// and sets ok to false.
func EulerUndirected(g Iterator) (walk []int, ok bool) {
n := g.Order()
// Compute outdegree for each vertex.
out := make([]int, n)
out := make([]int, n) // outdegree for each vertex
edgeCount := 0
for v := range out {
g.Visit(v, func(w int, _ int64) (skip bool) {
edgeCount++
if v != w {
out[v]++
}
return
})
}
if edgeCount == 0 {
return []int{}, true
}

start, oddDeg := -1, 0
for v := range out {
Expand All @@ -97,52 +101,40 @@ func EulerUndirected(g Iterator) (walk []int, ok bool) {
return []int{}, false
}

// Make a copy of g.
edgeCount := 0
h := New(n)
for v := 0; v < n; v++ {
g.Visit(v, func(w int, _ int64) (skip bool) {
h.Add(v, w)
edgeCount++
return
})
}
if edgeCount == 0 {
return []int{}, true
}

// Find a starting point with neighbors.
for v := 0; v < n && start == -1; v++ {
h.Visit(v, func(w int, _ int64) (skip bool) {
start = w
return true
})
if start == -1 {
for v := 0; v < n; v++ {
if g.Visit(v, func(w int, _ int64) (skip bool) {
start = w
return true
}) {
break
}
}
}

stack := []int{start}
for len(stack) > 0 {
v := stack[len(stack)-1]
stack = stack[:len(stack)-1]
h := Copy(g)
for stack := []int{start}; len(stack) > 0; {
n := len(stack)
v := stack[n-1]
stack = stack[:n-1]
for h.Degree(v) > 0 {
stack = append(stack, v)
var w int
h.Visit(v, func(u int, _ int64) (skip bool) {
w = u
return true
})
h.DeleteBoth(v, w)
edgeCount--
if v != w {
h.DeleteBoth(v, w)
edgeCount -= 2
} else {
h.Delete(v, v)
edgeCount--
}
v = w
}
walk = append(walk, v)
}

if edgeCount != 0 {
if edgeCount > 0 {
return []int{}, false
}
return walk, true
Expand Down
3 changes: 1 addition & 2 deletions example_dfs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ func (d *DFSData) dfsVisit(g graph.Iterator, v int) {
d.Finish[v] = d.Time
}

// An implementation of depth-first search
// demonstrating how to use this package.
// Show how to use this package by implementing a complete depth-first search.
func Example_dFS() {
// Build a small directed graph:
//
Expand Down
2 changes: 1 addition & 1 deletion examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"github.com/yourbasic/graph"
)

// How to iterate over the edges of a graph.
// Build a plain graph and visit all of its edges.
func Example_basics() {
// Build a graph with four vertices and four undirected edges.
// (Each of these edges are, in fact, represented by two directed
Expand Down
10 changes: 6 additions & 4 deletions graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@
// by composing and filtering a set of standard graphs, or by writing
// functions that describe the edges of a graph.
//
// Example usage
// Tutorial
//
// The package examples show how to build plain graphs and how to efficiently
// use the Visit iterator, the vital abstraction of this package.
// The Basics example shows how to build a plain graph and how to
// efficiently use the Visit iterator, the key abstraction of this package.
//
// The DFS example contains a full implementation of depth-first search.
//
package graph

Expand All @@ -63,7 +65,7 @@ type Iterator interface {
// • The calls to the do function may occur in any order,
// and the order may vary.
//
Visit(v int, Do func(w int, c int64) (skip bool)) (aborted bool)
Visit(v int, do func(w int, c int64) (skip bool)) (aborted bool)
}

// The maximum and minum value of an edge cost.
Expand Down
Loading

0 comments on commit 4e527a4

Please sign in to comment.