Skip to content

Commit cb665b6

Browse files
author
JulianGriggs
committed
Add Simple Graph Implementation/tests
1 parent e740680 commit cb665b6

File tree

3 files changed

+383
-0
lines changed

3 files changed

+383
-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

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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+
func (g *SimpleGraph) V() int {
52+
g.mutex.RLock()
53+
defer g.mutex.RUnlock()
54+
55+
return g.v
56+
}
57+
58+
func (g *SimpleGraph) E() int {
59+
g.mutex.RLock()
60+
defer g.mutex.RUnlock()
61+
62+
return g.e
63+
}
64+
65+
func (g *SimpleGraph) AddEdge(v, w interface{}) error {
66+
g.mutex.Lock()
67+
defer g.mutex.Unlock()
68+
69+
if v == w {
70+
return ErrSelfLoop
71+
}
72+
73+
g.addVertex(v)
74+
g.addVertex(w)
75+
76+
if _, ok := g.adjacencyList[v][w]; ok {
77+
return ErrParallelEdge
78+
}
79+
80+
g.adjacencyList[v][w] = struct{}{}
81+
g.adjacencyList[w][v] = struct{}{}
82+
g.e++
83+
return nil
84+
}
85+
86+
func (g *SimpleGraph) Adj(v interface{}) ([]interface{}, error) {
87+
g.mutex.RLock()
88+
defer g.mutex.RUnlock()
89+
90+
deg, err := g.Degree(v)
91+
if err != nil {
92+
return nil, ErrVertexNotFound
93+
}
94+
95+
adj := make([]interface{}, deg)
96+
i := 0
97+
for key := range g.adjacencyList[v] {
98+
adj[i] = key
99+
i++
100+
}
101+
return adj, nil
102+
}
103+
104+
func (g *SimpleGraph) Degree(v interface{}) (int, error) {
105+
g.mutex.RLock()
106+
defer g.mutex.RUnlock()
107+
108+
val, ok := g.adjacencyList[v]
109+
if !ok {
110+
return 0, ErrVertexNotFound
111+
}
112+
return len(val), nil
113+
}
114+
115+
func (g *SimpleGraph) addVertex(v interface{}) {
116+
mm, ok := g.adjacencyList[v]
117+
if !ok {
118+
mm = make(map[interface{}]struct{})
119+
g.adjacencyList[v] = mm
120+
g.v++
121+
}
122+
}
123+
124+
func NewSimpleGraph() *SimpleGraph {
125+
return &SimpleGraph{
126+
adjacencyList: make(map[interface{}]map[interface{}]struct{}),
127+
v: 0,
128+
e: 0,
129+
}
130+
}

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)