Skip to content
Snippets Groups Projects
Commit 87d182d7 authored by Oliver Herms's avatar Oliver Herms
Browse files

Adding Dijkstra's algorithm

parent e9e5c326
No related branches found
No related tags found
No related merge requests found
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["dijkstra.go"],
importpath = "github.com/bio-routing/bio-rd/util/dijkstra",
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["dijkstra_test.go"],
embed = [":go_default_library"],
deps = ["//vendor/github.com/stretchr/testify/assert:go_default_library"],
)
package dijkstra
// Topology represents a network topology
type Topology struct {
nodes map[Node]int64
edges map[Node]map[Node]int64
}
// Node represents a node in a graph
type Node struct {
Name string
}
// Edge represents a directed edge in a graph
type Edge struct {
NodeA Node
NodeB Node
Distance int64
}
// SPT represents a shortest path tree
type SPT map[Node]Path
// Path represents a path through a graph
type Path struct {
Edges []Edge
Distance int64
}
// NewTopologay creates a new topology
func NewTopologay(nodes []Node, edges []Edge) *Topology {
t := &Topology{
nodes: make(map[Node]int64),
edges: make(map[Node]map[Node]int64),
}
for _, n := range nodes {
t.nodes[n] = -1
}
for _, e := range edges {
if _, ok := t.edges[e.NodeA]; !ok {
t.edges[e.NodeA] = make(map[Node]int64)
}
t.edges[e.NodeA][e.NodeB] = e.Distance
}
return t
}
func (t *Topology) newSPT() SPT {
spt := make(SPT)
for n := range t.nodes {
spt[n] = Path{
Edges: make([]Edge, 0),
Distance: -1,
}
}
return spt
}
// SPT calculates the shortest path tree
func (t *Topology) SPT(from Node) SPT {
spt := t.newSPT()
tmp := spt[from]
tmp.Distance = 0
spt[from] = tmp
unmarked := make(map[Node]struct{})
for n := range t.nodes {
if n == from {
continue
}
unmarked[n] = struct{}{}
}
for len(unmarked) > 0 {
for neighbor, distance := range t.edges[from] {
if spt[neighbor].Distance == -1 {
tmp := spt[neighbor]
tmp.Distance = spt[from].Distance + distance
tmp.Edges = make([]Edge, len(spt[from].Edges)+1)
copy(tmp.Edges, spt[from].Edges)
tmp.Edges[len(spt[from].Edges)] = Edge{
NodeA: from,
NodeB: neighbor,
Distance: distance,
}
spt[neighbor] = tmp
continue
}
if spt[from].Distance+distance < spt[neighbor].Distance {
tmp := spt[neighbor]
tmp.Distance = spt[from].Distance + distance
tmp.Edges = make([]Edge, len(spt[from].Edges)+1)
copy(tmp.Edges, spt[from].Edges)
tmp.Edges[len(spt[from].Edges)] = Edge{
NodeA: from,
NodeB: neighbor,
Distance: distance,
}
spt[neighbor] = tmp
continue
}
}
var next *Node
nextDistance := int64(0)
for candidate := range unmarked {
if spt[candidate].Distance == -1 {
continue
}
if next == nil {
tmp := candidate
next = &tmp
nextDistance = spt[candidate].Distance
continue
}
if spt[candidate].Distance < nextDistance {
tmp := candidate
next = &tmp
nextDistance = spt[candidate].Distance
continue
}
}
from = *next
delete(unmarked, from)
}
return spt
}
package dijkstra
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSPT(t *testing.T) {
tests := []struct {
name string
nodes []Node
edges []Edge
expected SPT
}{
{
name: "Test #1",
nodes: []Node{
{
Name: "A",
},
{
Name: "B",
},
{
Name: "C",
},
{
Name: "D",
},
},
edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "A"},
Distance: 1,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "C"},
Distance: 5,
},
{
NodeA: Node{Name: "C"},
NodeB: Node{Name: "B"},
Distance: 5,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "D"},
Distance: 1,
},
{
NodeA: Node{Name: "D"},
NodeB: Node{Name: "B"},
Distance: 1,
},
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "D"},
Distance: 5,
},
{
NodeA: Node{Name: "D"},
NodeB: Node{Name: "A"},
Distance: 5,
},
{
NodeA: Node{Name: "D"},
NodeB: Node{Name: "C"},
Distance: 1,
},
{
NodeA: Node{Name: "C"},
NodeB: Node{Name: "D"},
Distance: 1,
},
},
expected: SPT{
Node{Name: "A"}: Path{
Edges: []Edge{},
Distance: 0,
},
Node{Name: "B"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
},
Distance: 1,
},
Node{Name: "C"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "D"},
Distance: 1,
},
{
NodeA: Node{Name: "D"},
NodeB: Node{Name: "C"},
Distance: 1,
},
},
Distance: 3,
},
Node{Name: "D"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "D"},
Distance: 1,
},
},
Distance: 2,
},
},
},
{
name: "Test #2",
nodes: []Node{
{
Name: "A",
},
{
Name: "B",
},
{
Name: "C",
},
{
Name: "D",
},
{
Name: "E",
},
},
edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "D"},
Distance: 2,
},
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "E"},
Distance: 3,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "C"},
Distance: 10,
},
{
NodeA: Node{Name: "E"},
NodeB: Node{Name: "C"},
Distance: 5,
},
},
expected: SPT{
Node{Name: "A"}: Path{
Edges: []Edge{},
Distance: 0,
},
Node{Name: "B"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
},
Distance: 1,
},
Node{Name: "C"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "E"},
Distance: 3,
},
{
NodeA: Node{Name: "E"},
NodeB: Node{Name: "C"},
Distance: 5,
},
},
Distance: 8,
},
Node{Name: "D"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "D"},
Distance: 2,
},
},
Distance: 2,
},
Node{Name: "E"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "E"},
Distance: 3,
},
},
Distance: 3,
},
},
},
}
for _, test := range tests {
top := NewTopologay(test.nodes, test.edges)
spt := top.SPT(Node{Name: "A"})
assert.Equalf(t, test.expected, spt, "Test %q", test.name)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment