Skip to content

Commit

Permalink
Merge pull request #178 from JulianGriggs/simple-graph
Browse files Browse the repository at this point in the history
RM-23161 Add Simple Graph Implementation/tests
  • Loading branch information
brianshannan-wf authored Apr 12, 2018
2 parents d3b8653 + 720d4b4 commit 01f52d4
Show file tree
Hide file tree
Showing 3 changed files with 389 additions and 0 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,13 @@ structure which preserve and reuse previous versions. This uses a very
functional, cons-style of list manipulation. Insert, get, remove, and size
operations are O(n) as you would expect.

#### Simple Graph

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

### Installation

1. Install Go 1.3 or higher.
Expand Down
136 changes: 136 additions & 0 deletions graph/simple.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
Copyright 2017 Julian Griggs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/*
Package graph provides graph implementations. Currently, this includes an
undirected simple graph.
*/
package graph

import (
"errors"
"sync"
)

var (
// ErrVertexNotFound is returned when an operation is requested on a
// non-existent vertex.
ErrVertexNotFound = errors.New("vertex not found")

// ErrSelfLoop is returned when an operation tries to create a disallowed
// self loop.
ErrSelfLoop = errors.New("self loops not permitted")

// ErrParallelEdge is returned when an operation tries to create a
// disallowed parallel edge.
ErrParallelEdge = errors.New("parallel edges are not permitted")
)

// SimpleGraph is a mutable, non-persistent undirected graph.
// Parallel edges and self-loops are not permitted.
// Additional description: https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)#Simple_graph
type SimpleGraph struct {
mutex sync.RWMutex
adjacencyList map[interface{}]map[interface{}]struct{}
v, e int
}

// V returns the number of vertices in the SimpleGraph
func (g *SimpleGraph) V() int {
g.mutex.RLock()
defer g.mutex.RUnlock()

return g.v
}

// E returns the number of edges in the SimpleGraph
func (g *SimpleGraph) E() int {
g.mutex.RLock()
defer g.mutex.RUnlock()

return g.e
}

// AddEdge will create an edge between vertices v and w
func (g *SimpleGraph) AddEdge(v, w interface{}) error {
g.mutex.Lock()
defer g.mutex.Unlock()

if v == w {
return ErrSelfLoop
}

g.addVertex(v)
g.addVertex(w)

if _, ok := g.adjacencyList[v][w]; ok {
return ErrParallelEdge
}

g.adjacencyList[v][w] = struct{}{}
g.adjacencyList[w][v] = struct{}{}
g.e++
return nil
}

// Adj returns the list of all vertices connected to v
func (g *SimpleGraph) Adj(v interface{}) ([]interface{}, error) {
g.mutex.RLock()
defer g.mutex.RUnlock()

deg, err := g.Degree(v)
if err != nil {
return nil, ErrVertexNotFound
}

adj := make([]interface{}, deg)
i := 0
for key := range g.adjacencyList[v] {
adj[i] = key
i++
}
return adj, nil
}

// Degree returns the number of vertices connected to v
func (g *SimpleGraph) Degree(v interface{}) (int, error) {
g.mutex.RLock()
defer g.mutex.RUnlock()

val, ok := g.adjacencyList[v]
if !ok {
return 0, ErrVertexNotFound
}
return len(val), nil
}

func (g *SimpleGraph) addVertex(v interface{}) {
mm, ok := g.adjacencyList[v]
if !ok {
mm = make(map[interface{}]struct{})
g.adjacencyList[v] = mm
g.v++
}
}

// NewSimpleGraph creates and returns a SimpleGraph
func NewSimpleGraph() *SimpleGraph {
return &SimpleGraph{
adjacencyList: make(map[interface{}]map[interface{}]struct{}),
v: 0,
e: 0,
}
}
246 changes: 246 additions & 0 deletions graph/simple_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
/*
Copyright 2017 Julian Griggs
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package graph

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestV(t *testing.T) {
assert := assert.New(t)
sgraph := NewSimpleGraph()
assert.Equal(0, sgraph.V())

sgraph.AddEdge("A", "B")
assert.Equal(2, sgraph.V())

sgraph.AddEdge("B", "C")
assert.Equal(3, sgraph.V())

sgraph.AddEdge("A", "C")
assert.Equal(3, sgraph.V())

// Parallel edges not allowed
sgraph.AddEdge("A", "C")
assert.Equal(3, sgraph.V())
sgraph.AddEdge("C", "A")
assert.Equal(3, sgraph.V())

// Self loops not allowed
sgraph.AddEdge("C", "C")
assert.Equal(3, sgraph.V())
sgraph.AddEdge("D", "D")
assert.Equal(3, sgraph.V())
}

func TestE(t *testing.T) {
assert := assert.New(t)
sgraph := NewSimpleGraph()

assert.Equal(0, sgraph.E())

sgraph.AddEdge("A", "B")
assert.Equal(1, sgraph.E())

sgraph.AddEdge("B", "C")
assert.Equal(2, sgraph.E())

sgraph.AddEdge("A", "C")
assert.Equal(3, sgraph.E())

// Parallel edges not allowed
sgraph.AddEdge("A", "C")
assert.Equal(3, sgraph.E())
sgraph.AddEdge("C", "A")
assert.Equal(3, sgraph.E())

// Self loops not allowed so no edges added
sgraph.AddEdge("C", "C")
assert.Equal(3, sgraph.E())
sgraph.AddEdge("D", "D")
assert.Equal(3, sgraph.E())
}

func TestDegree(t *testing.T) {
assert := assert.New(t)
sgraph := NewSimpleGraph()

// No edges added so degree is 0
v, err := sgraph.Degree("A")
assert.Zero(v)
assert.Error(err)

// One edge added
sgraph.AddEdge("A", "B")
v, err = sgraph.Degree("A")
assert.Equal(1, v)
assert.Nil(err)

// Self loops are not allowed
sgraph.AddEdge("A", "A")
v, err = sgraph.Degree("A")
assert.Equal(1, v)
assert.Nil(err)

// Parallel edges are not allowed
sgraph.AddEdge("A", "B")
v, err = sgraph.Degree("A")
assert.Equal(1, v)
assert.Nil(err)
sgraph.AddEdge("B", "A")
v, err = sgraph.Degree("A")
assert.Equal(1, v)
assert.Nil(err)

v, err = sgraph.Degree("B")
assert.Equal(1, v)
assert.Nil(err)

sgraph.AddEdge("C", "D")
sgraph.AddEdge("A", "C")
sgraph.AddEdge("E", "F")
sgraph.AddEdge("E", "G")
sgraph.AddEdge("H", "G")

v, err = sgraph.Degree("A")
assert.Equal(2, v)
assert.Nil(err)

v, err = sgraph.Degree("B")
assert.Equal(1, v)
assert.Nil(err)

v, err = sgraph.Degree("C")
assert.Equal(2, v)
assert.Nil(err)

v, err = sgraph.Degree("D")
assert.Equal(1, v)
assert.Nil(err)

v, err = sgraph.Degree("E")
assert.Equal(2, v)
assert.Nil(err)

v, err = sgraph.Degree("G")
assert.Equal(2, v)
assert.Nil(err)
}

func TestAddEdge(t *testing.T) {
assert := assert.New(t)
sgraph := NewSimpleGraph()

err := sgraph.AddEdge("A", "B")
assert.Nil(err)

err = sgraph.AddEdge("A", "B")
assert.Error(err)

err = sgraph.AddEdge("B", "A")
assert.Error(err)

err = sgraph.AddEdge("A", "A")
assert.Error(err)

err = sgraph.AddEdge("C", "C")
assert.Error(err)

err = sgraph.AddEdge("B", "C")
assert.Nil(err)

}

func TestAdj(t *testing.T) {
assert := assert.New(t)
sgraph := NewSimpleGraph()

v, err := sgraph.Adj("A")
assert.Zero(v)
assert.Error(err)

// Self loops not allowed
sgraph.AddEdge("A", "A")
v, err = sgraph.Adj("A")
assert.Zero(v)
assert.Error(err)

sgraph.AddEdge("A", "B")
v, err = sgraph.Adj("A")
assert.Equal(1, len(v))
assert.Nil(err)
assert.Equal("B", v[0])

v, err = sgraph.Adj("B")
assert.Equal(1, len(v))
assert.Nil(err)
assert.Equal("A", v[0])

// Parallel Edges not allowed
sgraph.AddEdge("A", "B")
sgraph.AddEdge("B", "A")
v, err = sgraph.Adj("B")
assert.Equal(1, len(v))
assert.Nil(err)
assert.Equal("A", v[0])

sgraph.AddEdge("C", "D")
sgraph.AddEdge("A", "C")
sgraph.AddEdge("E", "F")
sgraph.AddEdge("E", "G")
sgraph.AddEdge("H", "G")

v, err = sgraph.Adj("A")
assert.Equal(2, len(v))
assert.Nil(err)
assert.Contains(v, "B")
assert.Contains(v, "C")
assert.NotContains(v, "A")
assert.NotContains(v, "D")

v, err = sgraph.Adj("B")
assert.Equal(1, len(v))
assert.Nil(err)
assert.Contains(v, "A")
assert.NotContains(v, "B")
assert.NotContains(v, "C")
assert.NotContains(v, "D")

v, err = sgraph.Adj("C")
assert.Equal(2, len(v))
assert.Nil(err)
assert.Contains(v, "A")
assert.Contains(v, "D")
assert.NotContains(v, "B")
assert.NotContains(v, "C")

v, err = sgraph.Adj("E")
assert.Equal(2, len(v))
assert.Nil(err)
assert.Contains(v, "F")
assert.Contains(v, "G")
assert.NotContains(v, "A")

v, err = sgraph.Adj("G")
assert.Equal(2, len(v))
assert.Nil(err)
assert.Contains(v, "E")
assert.Contains(v, "H")
assert.NotContains(v, "A")
}

0 comments on commit 01f52d4

Please sign in to comment.