Skip to content

Commit 01f52d4

Browse files
Merge pull request Workiva#178 from JulianGriggs/simple-graph
RM-23161 Add Simple Graph Implementation/tests
2 parents d3b8653 + 720d4b4 commit 01f52d4

File tree

3 files changed

+389
-0
lines changed

3 files changed

+389
-0
lines changed

README.md

+7
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,13 @@ structure which preserve and reuse previous versions. This uses a very
184184
functional, cons-style of list manipulation. Insert, get, remove, and size
185185
operations are O(n) as you would expect.
186186

187+
#### Simple Graph
188+
189+
A mutable, non-persistent undirected graph where parallel edges and self-loops are
190+
not permitted. Operations to add an edge as well as retrieve the total number of
191+
vertices/edges are O(1) while the operation to retrieve the vertices adjacent to a
192+
target is O(n). For more details see [wikipedia](https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)#Simple_graph)
193+
187194
### Installation
188195

189196
1. Install Go 1.3 or higher.

graph/simple.go

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
Copyright 2017 Julian Griggs
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
/*
18+
Package graph provides graph implementations. Currently, this includes an
19+
undirected simple graph.
20+
*/
21+
package graph
22+
23+
import (
24+
"errors"
25+
"sync"
26+
)
27+
28+
var (
29+
// ErrVertexNotFound is returned when an operation is requested on a
30+
// non-existent vertex.
31+
ErrVertexNotFound = errors.New("vertex not found")
32+
33+
// ErrSelfLoop is returned when an operation tries to create a disallowed
34+
// self loop.
35+
ErrSelfLoop = errors.New("self loops not permitted")
36+
37+
// ErrParallelEdge is returned when an operation tries to create a
38+
// disallowed parallel edge.
39+
ErrParallelEdge = errors.New("parallel edges are not permitted")
40+
)
41+
42+
// SimpleGraph is a mutable, non-persistent undirected graph.
43+
// Parallel edges and self-loops are not permitted.
44+
// Additional description: https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)#Simple_graph
45+
type SimpleGraph struct {
46+
mutex sync.RWMutex
47+
adjacencyList map[interface{}]map[interface{}]struct{}
48+
v, e int
49+
}
50+
51+
// V returns the number of vertices in the SimpleGraph
52+
func (g *SimpleGraph) V() int {
53+
g.mutex.RLock()
54+
defer g.mutex.RUnlock()
55+
56+
return g.v
57+
}
58+
59+
// E returns the number of edges in the SimpleGraph
60+
func (g *SimpleGraph) E() int {
61+
g.mutex.RLock()
62+
defer g.mutex.RUnlock()
63+
64+
return g.e
65+
}
66+
67+
// AddEdge will create an edge between vertices v and w
68+
func (g *SimpleGraph) AddEdge(v, w interface{}) error {
69+
g.mutex.Lock()
70+
defer g.mutex.Unlock()
71+
72+
if v == w {
73+
return ErrSelfLoop
74+
}
75+
76+
g.addVertex(v)
77+
g.addVertex(w)
78+
79+
if _, ok := g.adjacencyList[v][w]; ok {
80+
return ErrParallelEdge
81+
}
82+
83+
g.adjacencyList[v][w] = struct{}{}
84+
g.adjacencyList[w][v] = struct{}{}
85+
g.e++
86+
return nil
87+
}
88+
89+
// Adj returns the list of all vertices connected to v
90+
func (g *SimpleGraph) Adj(v interface{}) ([]interface{}, error) {
91+
g.mutex.RLock()
92+
defer g.mutex.RUnlock()
93+
94+
deg, err := g.Degree(v)
95+
if err != nil {
96+
return nil, ErrVertexNotFound
97+
}
98+
99+
adj := make([]interface{}, deg)
100+
i := 0
101+
for key := range g.adjacencyList[v] {
102+
adj[i] = key
103+
i++
104+
}
105+
return adj, nil
106+
}
107+
108+
// Degree returns the number of vertices connected to v
109+
func (g *SimpleGraph) Degree(v interface{}) (int, error) {
110+
g.mutex.RLock()
111+
defer g.mutex.RUnlock()
112+
113+
val, ok := g.adjacencyList[v]
114+
if !ok {
115+
return 0, ErrVertexNotFound
116+
}
117+
return len(val), nil
118+
}
119+
120+
func (g *SimpleGraph) addVertex(v interface{}) {
121+
mm, ok := g.adjacencyList[v]
122+
if !ok {
123+
mm = make(map[interface{}]struct{})
124+
g.adjacencyList[v] = mm
125+
g.v++
126+
}
127+
}
128+
129+
// NewSimpleGraph creates and returns a SimpleGraph
130+
func NewSimpleGraph() *SimpleGraph {
131+
return &SimpleGraph{
132+
adjacencyList: make(map[interface{}]map[interface{}]struct{}),
133+
v: 0,
134+
e: 0,
135+
}
136+
}

graph/simple_test.go

+246
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
/*
2+
Copyright 2017 Julian Griggs
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package graph
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
)
24+
25+
func TestV(t *testing.T) {
26+
assert := assert.New(t)
27+
sgraph := NewSimpleGraph()
28+
assert.Equal(0, sgraph.V())
29+
30+
sgraph.AddEdge("A", "B")
31+
assert.Equal(2, sgraph.V())
32+
33+
sgraph.AddEdge("B", "C")
34+
assert.Equal(3, sgraph.V())
35+
36+
sgraph.AddEdge("A", "C")
37+
assert.Equal(3, sgraph.V())
38+
39+
// Parallel edges not allowed
40+
sgraph.AddEdge("A", "C")
41+
assert.Equal(3, sgraph.V())
42+
sgraph.AddEdge("C", "A")
43+
assert.Equal(3, sgraph.V())
44+
45+
// Self loops not allowed
46+
sgraph.AddEdge("C", "C")
47+
assert.Equal(3, sgraph.V())
48+
sgraph.AddEdge("D", "D")
49+
assert.Equal(3, sgraph.V())
50+
}
51+
52+
func TestE(t *testing.T) {
53+
assert := assert.New(t)
54+
sgraph := NewSimpleGraph()
55+
56+
assert.Equal(0, sgraph.E())
57+
58+
sgraph.AddEdge("A", "B")
59+
assert.Equal(1, sgraph.E())
60+
61+
sgraph.AddEdge("B", "C")
62+
assert.Equal(2, sgraph.E())
63+
64+
sgraph.AddEdge("A", "C")
65+
assert.Equal(3, sgraph.E())
66+
67+
// Parallel edges not allowed
68+
sgraph.AddEdge("A", "C")
69+
assert.Equal(3, sgraph.E())
70+
sgraph.AddEdge("C", "A")
71+
assert.Equal(3, sgraph.E())
72+
73+
// Self loops not allowed so no edges added
74+
sgraph.AddEdge("C", "C")
75+
assert.Equal(3, sgraph.E())
76+
sgraph.AddEdge("D", "D")
77+
assert.Equal(3, sgraph.E())
78+
}
79+
80+
func TestDegree(t *testing.T) {
81+
assert := assert.New(t)
82+
sgraph := NewSimpleGraph()
83+
84+
// No edges added so degree is 0
85+
v, err := sgraph.Degree("A")
86+
assert.Zero(v)
87+
assert.Error(err)
88+
89+
// One edge added
90+
sgraph.AddEdge("A", "B")
91+
v, err = sgraph.Degree("A")
92+
assert.Equal(1, v)
93+
assert.Nil(err)
94+
95+
// Self loops are not allowed
96+
sgraph.AddEdge("A", "A")
97+
v, err = sgraph.Degree("A")
98+
assert.Equal(1, v)
99+
assert.Nil(err)
100+
101+
// Parallel edges are not allowed
102+
sgraph.AddEdge("A", "B")
103+
v, err = sgraph.Degree("A")
104+
assert.Equal(1, v)
105+
assert.Nil(err)
106+
sgraph.AddEdge("B", "A")
107+
v, err = sgraph.Degree("A")
108+
assert.Equal(1, v)
109+
assert.Nil(err)
110+
111+
v, err = sgraph.Degree("B")
112+
assert.Equal(1, v)
113+
assert.Nil(err)
114+
115+
sgraph.AddEdge("C", "D")
116+
sgraph.AddEdge("A", "C")
117+
sgraph.AddEdge("E", "F")
118+
sgraph.AddEdge("E", "G")
119+
sgraph.AddEdge("H", "G")
120+
121+
v, err = sgraph.Degree("A")
122+
assert.Equal(2, v)
123+
assert.Nil(err)
124+
125+
v, err = sgraph.Degree("B")
126+
assert.Equal(1, v)
127+
assert.Nil(err)
128+
129+
v, err = sgraph.Degree("C")
130+
assert.Equal(2, v)
131+
assert.Nil(err)
132+
133+
v, err = sgraph.Degree("D")
134+
assert.Equal(1, v)
135+
assert.Nil(err)
136+
137+
v, err = sgraph.Degree("E")
138+
assert.Equal(2, v)
139+
assert.Nil(err)
140+
141+
v, err = sgraph.Degree("G")
142+
assert.Equal(2, v)
143+
assert.Nil(err)
144+
}
145+
146+
func TestAddEdge(t *testing.T) {
147+
assert := assert.New(t)
148+
sgraph := NewSimpleGraph()
149+
150+
err := sgraph.AddEdge("A", "B")
151+
assert.Nil(err)
152+
153+
err = sgraph.AddEdge("A", "B")
154+
assert.Error(err)
155+
156+
err = sgraph.AddEdge("B", "A")
157+
assert.Error(err)
158+
159+
err = sgraph.AddEdge("A", "A")
160+
assert.Error(err)
161+
162+
err = sgraph.AddEdge("C", "C")
163+
assert.Error(err)
164+
165+
err = sgraph.AddEdge("B", "C")
166+
assert.Nil(err)
167+
168+
}
169+
170+
func TestAdj(t *testing.T) {
171+
assert := assert.New(t)
172+
sgraph := NewSimpleGraph()
173+
174+
v, err := sgraph.Adj("A")
175+
assert.Zero(v)
176+
assert.Error(err)
177+
178+
// Self loops not allowed
179+
sgraph.AddEdge("A", "A")
180+
v, err = sgraph.Adj("A")
181+
assert.Zero(v)
182+
assert.Error(err)
183+
184+
sgraph.AddEdge("A", "B")
185+
v, err = sgraph.Adj("A")
186+
assert.Equal(1, len(v))
187+
assert.Nil(err)
188+
assert.Equal("B", v[0])
189+
190+
v, err = sgraph.Adj("B")
191+
assert.Equal(1, len(v))
192+
assert.Nil(err)
193+
assert.Equal("A", v[0])
194+
195+
// Parallel Edges not allowed
196+
sgraph.AddEdge("A", "B")
197+
sgraph.AddEdge("B", "A")
198+
v, err = sgraph.Adj("B")
199+
assert.Equal(1, len(v))
200+
assert.Nil(err)
201+
assert.Equal("A", v[0])
202+
203+
sgraph.AddEdge("C", "D")
204+
sgraph.AddEdge("A", "C")
205+
sgraph.AddEdge("E", "F")
206+
sgraph.AddEdge("E", "G")
207+
sgraph.AddEdge("H", "G")
208+
209+
v, err = sgraph.Adj("A")
210+
assert.Equal(2, len(v))
211+
assert.Nil(err)
212+
assert.Contains(v, "B")
213+
assert.Contains(v, "C")
214+
assert.NotContains(v, "A")
215+
assert.NotContains(v, "D")
216+
217+
v, err = sgraph.Adj("B")
218+
assert.Equal(1, len(v))
219+
assert.Nil(err)
220+
assert.Contains(v, "A")
221+
assert.NotContains(v, "B")
222+
assert.NotContains(v, "C")
223+
assert.NotContains(v, "D")
224+
225+
v, err = sgraph.Adj("C")
226+
assert.Equal(2, len(v))
227+
assert.Nil(err)
228+
assert.Contains(v, "A")
229+
assert.Contains(v, "D")
230+
assert.NotContains(v, "B")
231+
assert.NotContains(v, "C")
232+
233+
v, err = sgraph.Adj("E")
234+
assert.Equal(2, len(v))
235+
assert.Nil(err)
236+
assert.Contains(v, "F")
237+
assert.Contains(v, "G")
238+
assert.NotContains(v, "A")
239+
240+
v, err = sgraph.Adj("G")
241+
assert.Equal(2, len(v))
242+
assert.Nil(err)
243+
assert.Contains(v, "E")
244+
assert.Contains(v, "H")
245+
assert.NotContains(v, "A")
246+
}

0 commit comments

Comments
 (0)