From 91a2abaceb9ccca5ff6ae8caf65a67c707a15fb4 Mon Sep 17 00:00:00 2001 From: Stefan Nilsson Date: Sun, 30 Apr 2017 08:12:14 +0200 Subject: [PATCH 1/7] Remove duplicate declaration of edge --- graph.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/graph.go b/graph.go index 29e12fb..e85045a 100644 --- a/graph.go +++ b/graph.go @@ -225,11 +225,6 @@ func Equal(g, h Iterator) bool { if g.Order() != h.Order() { return false } - type edge struct { - v int - w int - c int64 - } edges := make(map[edge]int) for v := 0; v < g.Order(); v++ { g.Visit(v, func(w int, c int64) (skip bool) { From 5f05f60cde219046814b654852726d7fadf961af Mon Sep 17 00:00:00 2001 From: korthaj Date: Sun, 30 Apr 2017 09:23:26 +0200 Subject: [PATCH 2/7] Cleanup --- bfs_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bfs_test.go b/bfs_test.go index 2c82b09..a7f5a45 100644 --- a/bfs_test.go +++ b/bfs_test.go @@ -6,7 +6,7 @@ import ( ) func TestBFS(t *testing.T) { - gm := New(10) + g := New(10) for _, e := range []struct { v, w int }{ @@ -15,12 +15,11 @@ func TestBFS(t *testing.T) { {2, 3}, {5, 6}, {3, 6}, {8, 9}, {4, 4}, } { - gm.AddBoth(e.v, e.w) + g.AddBoth(e.v, e.w) } - g := Sort(gm) exp := "0147925836" res := "0" - BFS(g, 0, func(v, w int, c int64) { + BFS(Sort(g), 0, func(v, w int, c int64) { res += strconv.Itoa(w) }) if mess, diff := diff(res, exp); diff { From b9ba69341de146eaac5f2947aa72ad4b0e78e706 Mon Sep 17 00:00:00 2001 From: korthaj Date: Sun, 30 Apr 2017 09:33:41 +0200 Subject: [PATCH 3/7] Cleanup --- euler_test.go | 66 +++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/euler_test.go b/euler_test.go index 31d8197..193bd1c 100644 --- a/euler_test.go +++ b/euler_test.go @@ -4,79 +4,79 @@ import ( "testing" ) -func TestEuler(t *testing.T) { +func TestEulerDirected(t *testing.T) { g := New(0) walk, ok := EulerDirected(g) if mess, diff := diff(walk, []int{}); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } if mess, diff := diff(ok, true); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } g = New(4) walk, ok = EulerDirected(g) if mess, diff := diff(walk, []int{}); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } if mess, diff := diff(ok, true); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } g.Add(0, 0) walk, ok = EulerDirected(g) if mess, diff := diff(walk, []int{0, 0}); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } if mess, diff := diff(ok, true); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } g.Add(0, 1) walk, ok = EulerDirected(g) if mess, diff := diff(walk, []int{0, 0, 1}); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } if mess, diff := diff(ok, true); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } g.Add(2, 3) walk, ok = EulerDirected(g) if mess, diff := diff(walk, []int{}); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } if mess, diff := diff(ok, false); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } g.Delete(2, 3) g.Add(2, 1) walk, ok = EulerDirected(g) if mess, diff := diff(walk, []int{}); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } if mess, diff := diff(ok, false); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } g.Delete(2, 1) g.Add(2, 2) walk, ok = EulerDirected(g) if mess, diff := diff(walk, []int{}); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } if mess, diff := diff(ok, false); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } g.Add(1, 2) walk, ok = EulerDirected(g) if mess, diff := diff(walk, []int{0, 0, 1, 2, 2}); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } if mess, diff := diff(ok, true); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerDirected: %s", mess) } } @@ -84,64 +84,64 @@ func TestEulerUndirected(t *testing.T) { g := New(0) walk, ok := EulerUndirected(g) if mess, diff := diff(walk, []int{}); diff { - t.Errorf("Euler undirected: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } if mess, diff := diff(ok, true); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } g = New(7) walk, ok = EulerUndirected(g) if mess, diff := diff(walk, []int{}); diff { - t.Errorf("Euler undirected: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } if mess, diff := diff(ok, true); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } g.AddBoth(0, 0) walk, ok = EulerUndirected(g) if mess, diff := diff(walk, []int{0, 0}); diff { - t.Errorf("Euler undirected: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } if mess, diff := diff(ok, true); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } g.AddBoth(0, 1) walk, ok = EulerUndirected(g) if mess, diff := diff(walk, []int{0, 0, 1}); diff { - t.Errorf("Euler undirected: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } if mess, diff := diff(ok, true); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } g.AddBoth(2, 3) walk, ok = EulerUndirected(g) if mess, diff := diff(walk, []int{}); diff { - t.Errorf("Euler undirected: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } if mess, diff := diff(ok, false); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } g.AddBoth(1, 2) walk, ok = EulerUndirected(g) if mess, diff := diff(walk, []int{0, 0, 1, 2, 3}); diff { - t.Errorf("Euler undirected: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } if mess, diff := diff(ok, true); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } g.AddBoth(2, 2) walk, ok = EulerUndirected(g) if mess, diff := diff(walk, []int{0, 0, 1, 2, 2, 3}); diff { - t.Errorf("Euler undirected: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } if mess, diff := diff(ok, true); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } g.AddBoth(4, 5) @@ -149,9 +149,9 @@ func TestEulerUndirected(t *testing.T) { g.AddBoth(4, 6) walk, ok = EulerUndirected(g) if mess, diff := diff(walk, []int{}); diff { - t.Errorf("Euler undirected: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } if mess, diff := diff(ok, false); diff { - t.Errorf("Euler: %s", mess) + t.Errorf("EulerUndirected: %s", mess) } } From d167321f6c28eeab9060d27db6aebea03ba670be Mon Sep 17 00:00:00 2001 From: korthaj Date: Mon, 1 May 2017 21:48:39 +0200 Subject: [PATCH 4/7] Add installation instructions --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 16ae2f1..16e2a4a 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,18 @@ they are instead computed as needed. New virtual graphs are constructed by composing and filtering a set of standard graphs, or by writing functions that describe the edges of a graph. +### Installation + +Once you have [installed Go][golang-install], run this command +to install the `graph` package: + + go get -u github.com/yourbasic/graph + +### Documentation + +There is an online reference for the package at +[godoc.org/github.com/yourbasic/graph][godoc-graph]. + ### Roadmap * The API of this library is frozen. @@ -57,6 +69,8 @@ in a computer science textbook. Stefan Nilsson – [korthaj](https://github.com/korthaj) +[godoc-graph]: https://godoc.org/github.com/yourbasic/graph +[golang-install]: http://golang.org/doc/install.html [cc010]: https://creativecommons.org/publicdomain/zero/1.0/deed.en [de]: https://commons.wikimedia.org/wiki/User:David_Eppstein [sv]: http://semver.org/ From 8834c931b5bc930154ca21050cd840f80fc96efa Mon Sep 17 00:00:00 2001 From: korthaj Date: Tue, 2 May 2017 22:59:43 +0200 Subject: [PATCH 5/7] Remove -u --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 16e2a4a..ef2d6c9 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ functions that describe the edges of a graph. Once you have [installed Go][golang-install], run this command to install the `graph` package: - go get -u github.com/yourbasic/graph + go get github.com/yourbasic/graph ### Documentation From ff853e7c8fe40ba0c9b6f4b9a4fa625184fc3c41 Mon Sep 17 00:00:00 2001 From: korthaj Date: Mon, 8 May 2017 23:25:31 +0200 Subject: [PATCH 6/7] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ef2d6c9..5c9b001 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ There is an online reference for the package at * The API of this library is frozen. * Bug fixes and performance enhancement can be expected. * New functionality might be included. -* The version numbers adhere to [semantic versioning][sv]. +* Version numbers adhere to [semantic versioning][sv]. The only accepted reason to modify the API of this package is to handle bug fixes that can't be resolved in any other reasonable way. From 65f457e4be0216c7879803edbcb0e9c89ec07443 Mon Sep 17 00:00:00 2001 From: korthaj Date: Wed, 10 May 2017 14:17:13 +0200 Subject: [PATCH 7/7] Minor refactoring --- build/build.go | 18 ++++++++---------- build/connect.go | 18 ++++++++++-------- build/edgeset.go | 30 ++++++++++++++++-------------- build/join.go | 35 ++++++++++++++++++++--------------- build/match.go | 29 +++++++++++++++-------------- build/subgraph.go | 9 +++++---- build/vertexset.go | 11 ++++++++--- 7 files changed, 82 insertions(+), 68 deletions(-) diff --git a/build/build.go b/build/build.go index 4fa9a2f..d5c8804 100644 --- a/build/build.go +++ b/build/build.go @@ -78,21 +78,19 @@ type CostFunc func(v, w int) int64 // Cost returns a CostFunc which always returns n. func Cost(n int64) CostFunc { - return func(_, _ int) int64 { return n } + return func(int, int) int64 { return n } } -func neverEdge(_, _ int) bool { return false } +func neverEdge(int, int) bool { return false } func alwaysEdge(v, w int) bool { return v != w } -func zero(_, _ int) int64 { return 0 } +func zero(int, int) int64 { return 0 } -func degreeZero(_ int) int { return 0 } -func degreeOne(_ int) int { return 1 } +func degreeZero(int) int { return 0 } +func degreeOne(int) int { return 1 } -func noNeighbors(_ int, _ int, _ func(w int, c int64) bool) bool { return false } +func noNeighbors(int, int, func(w int, c int64) bool) bool { return false } -const maxint = int(^uint(0) >> 1) -const minint = -maxint - 1 const bitsPerWord = 32 << uint(^uint(0)>>63) func min(m, n int) int { @@ -196,7 +194,7 @@ func generic(n int, cost CostFunc, edge func(v, w int) bool) *Virtual { cost: cost, } g.degree = func(v int) (deg int) { - g.visit(v, 0, func(_ int, _ int64) (skip bool) { + g.visit(v, 0, func(int, int64) (skip bool) { deg++ return }) @@ -229,7 +227,7 @@ func generic0(n int, edge func(v, w int) bool) *Virtual { cost: zero, } g.degree = func(v int) (deg int) { - g.visit(v, 0, func(_ int, _ int64) (skip bool) { + g.visit(v, 0, func(int, int64) (skip bool) { deg++ return }) diff --git a/build/connect.go b/build/connect.go index 81e31bc..f9daab1 100644 --- a/build/connect.go +++ b/build/connect.go @@ -25,8 +25,9 @@ func (g1 *Virtual) Connect(v1 int, g2 *Virtual) *Virtual { return g2.cost(0, w-t) case w == v1: return g2.cost(v-t, 0) + default: + return 0 } - return 0 } res := generic(n, newCost, func(v, w int) bool { @@ -39,8 +40,9 @@ func (g1 *Virtual) Connect(v1 int, g2 *Virtual) *Virtual { return g2.edge(0, w-t) case w == v1: return g2.edge(v-t, 0) + default: + return false } - return false }) res.degree = func(v int) (deg int) { @@ -55,23 +57,23 @@ func (g1 *Virtual) Connect(v1 int, g2 *Virtual) *Virtual { } res.visit = func(v int, a int, do func(w int, c int64) bool) (aborted bool) { - if v > t { + switch { + case v > t: return g2.visit(v-t, max(0, a-t), func(w int, c int64) (skip bool) { if w == 0 { return v1 >= a && do(v1, c) } return do(w+t, c) }) - } - if g1.visit(v, a, do) { + case g1.visit(v, a, do): return true - } - if v == v1 { + case v == v1: return g2.visit(0, max(0, a-t), func(w int, c int64) (skip bool) { return do(w+t, c) }) + default: + return } - return } return res } diff --git a/build/edgeset.go b/build/edgeset.go index afe313a..7342306 100644 --- a/build/edgeset.go +++ b/build/edgeset.go @@ -73,6 +73,7 @@ func newEdges(n int, e EdgeSet) *Virtual { case n == 1: return singleton() } + var noCost bool if e.Cost == nil { noCost = true @@ -83,12 +84,12 @@ func newEdges(n int, e EdgeSet) *Virtual { noFilter = true e.Keep = alwaysEdge } + from := e.From.And(Range(0, n)) to := e.To.And(Range(0, n)) if from.size() == 0 || to.size() == 0 { return Empty(n) } - res := generic(n, e.Cost, func(v, w int) (edge bool) { return e.Contains(v, w) }) @@ -104,13 +105,15 @@ func newEdges(n int, e EdgeSet) *Virtual { return to.size() case to.Contains(v): return from.size() + default: + return } - return } } visit := func(v int, a int, do func(w int, c int64) bool) (aborted bool) { - if intersect.Contains(v) { + switch { + case intersect.Contains(v): for _, in := range union.And(Range(a, n)).set { for w := in.a; w < in.b; w++ { if v != w && e.Keep(v, w) && do(w, e.Cost(v, w)) { @@ -119,8 +122,7 @@ func newEdges(n int, e EdgeSet) *Virtual { } } return - } - if from.Contains(v) { + case from.Contains(v): for _, in := range to.And(Range(a, n)).set { for w := in.a; w < in.b; w++ { if e.Keep(v, w) && do(w, e.Cost(v, w)) { @@ -129,8 +131,7 @@ func newEdges(n int, e EdgeSet) *Virtual { } } return - } - if to.Contains(v) { + case to.Contains(v): for _, in := range from.And(Range(a, n)).set { for w := in.a; w < in.b; w++ { if e.Keep(v, w) && do(w, e.Cost(v, w)) { @@ -139,12 +140,14 @@ func newEdges(n int, e EdgeSet) *Virtual { } } return + default: + return } - return } visit0 := func(v int, a int, do func(w int, c int64) bool) (aborted bool) { - if intersect.Contains(v) { + switch { + case intersect.Contains(v): for _, in := range union.And(Range(a, n)).set { for w := in.a; w < in.b; w++ { if v != w && e.Keep(v, w) && do(w, 0) { @@ -153,8 +156,7 @@ func newEdges(n int, e EdgeSet) *Virtual { } } return - } - if from.Contains(v) { + case from.Contains(v): for _, in := range to.And(Range(a, n)).set { for w := in.a; w < in.b; w++ { if e.Keep(v, w) && do(w, 0) { @@ -163,8 +165,7 @@ func newEdges(n int, e EdgeSet) *Virtual { } } return - } - if to.Contains(v) { + case to.Contains(v): for _, in := range from.And(Range(a, n)).set { for w := in.a; w < in.b; w++ { if e.Keep(v, w) && do(w, 0) { @@ -173,8 +174,9 @@ func newEdges(n int, e EdgeSet) *Virtual { } } return + default: + return } - return } if noCost { diff --git a/build/join.go b/build/join.go index e99b726..2c23d6f 100644 --- a/build/join.go +++ b/build/join.go @@ -20,11 +20,13 @@ func (g1 *Virtual) Join(g2 *Virtual, bridge EdgeSet) *Virtual { bridge.Cost = zero } joinCost := func(v, w int) int64 { - if v < t && w < t { + switch { + case v < t && w < t: return g1.cost(v, w) - } - if v >= t && w >= t { + case v >= t && w >= t: return g2.cost(v-t, w-t) + default: + return bridge.Cost(v, w) } return bridge.Cost(v, w) } @@ -38,32 +40,35 @@ func (g1 *Virtual) Join(g2 *Virtual, bridge EdgeSet) *Virtual { s2 := bridge.To.And(Range(t, g2.order+t)) res := generic(n, joinCost, func(v, w int) (edge bool) { - if v < t && w < t { + switch { + case v < t && w < t: return g1.edge(v, w) - } - if v >= t && w >= t { + case v >= t && w >= t: return g2.edge(v-t, w-t) - } - if !bridge.Keep(v, w) { + case !bridge.Keep(v, w): return false + default: + return s1.Contains(v) && s2.Contains(w) || + s1.Contains(w) && s2.Contains(v) } - return s1.Contains(v) && s2.Contains(w) || s1.Contains(w) && s2.Contains(v) }) if noFilter { res.degree = func(v int) (deg int) { - if v < t { + switch { + case v < t: deg = g1.degree(v) if s1.Contains(v) { deg += s2.size() } return + default: + deg = g2.degree(v - t) + if s2.Contains(v) { + deg += s1.size() + } + return } - deg = g2.degree(v - t) - if s2.Contains(v) { - deg += s1.size() - } - return } } diff --git a/build/match.go b/build/match.go index a5f397c..58ccdaa 100644 --- a/build/match.go +++ b/build/match.go @@ -21,13 +21,14 @@ func (g1 *Virtual) Match(g2 *Virtual, bridge EdgeSet) *Virtual { bridge.Cost = zero } matchCost := func(v, w int) int64 { - if v < t && w < t { + switch { + case v < t && w < t: return g1.cost(v, w) - } - if v >= t && w >= t { + case v >= t && w >= t: return g2.cost(v-t, w-t) + default: + return bridge.Cost(v, w) } - return bridge.Cost(v, w) } if bridge.Keep == nil { @@ -37,20 +38,20 @@ func (g1 *Virtual) Match(g2 *Virtual, bridge EdgeSet) *Virtual { s2 := bridge.To.And(Range(t, g2.order+t)) res := generic(n, matchCost, func(v, w int) (edge bool) { - if v < t && w < t { + switch { + case v < t && w < t: return g1.edge(v, w) - } - if v >= t && w >= t { + case v >= t && w >= t: return g2.edge(v-t, w-t) - } - if !bridge.Keep(v, w) { + case !bridge.Keep(v, w): return false + default: + s1v := s1.rank(v) + s1w := s1.rank(w) + s2v := s2.rank(v) + s2w := s2.rank(w) + return s1v != -1 && s1v == s2w || s1w != -1 && s1w == s2v } - s1v := s1.rank(v) - s1w := s1.rank(w) - s2v := s2.rank(v) - s2w := s2.rank(w) - return s1v != -1 && s1v == s2w || s1w != -1 && s1w == s2v }) res.visit = func(v int, a int, do func(w int, c int64) bool) (aborted bool) { diff --git a/build/subgraph.go b/build/subgraph.go index a48b77f..ade64ef 100644 --- a/build/subgraph.go +++ b/build/subgraph.go @@ -29,14 +29,15 @@ func (g *Virtual) Subgraph(s VertexSet) *Virtual { s0 := s.And(Range(s.get(a), n)) for _, in := range s0.set { if more := false; g.visit(v0, in.a, func(w0 int, c int64) (skip bool) { - if w0 >= in.b { + switch { + case w0 >= in.b: more, skip = true, true return - } - if do(s.rank(w0), c) { + case do(s.rank(w0), c): return true + default: + return } - return }) && !more { return true } diff --git a/build/vertexset.go b/build/vertexset.go index c05338b..72c58ec 100644 --- a/build/vertexset.go +++ b/build/vertexset.go @@ -11,6 +11,11 @@ type VertexSet struct { set []interval } +const ( + maxInt = int(^uint(0) >> 1) + minInt = -maxInt - 1 +) + // An interval represents the numbers [a, b). type interval struct { a, b int @@ -117,15 +122,15 @@ func (s VertexSet) complement() VertexSet { return VertexSet{} } t := empty() - prev := minint + prev := minInt for _, in := range s.set { if prev != in.a { t.set = append(t.set, interval{prev, in.a, 0}) } prev = in.b } - if prev < maxint { - t.set = append(t.set, interval{prev, maxint, 0}) + if prev < maxInt { + t.set = append(t.set, interval{prev, maxInt, 0}) } t.update() return t