2017-08-15 14:55:28 +00:00
|
|
|
package pov
|
|
|
|
|
|
|
|
const testVersion = 2
|
|
|
|
|
2017-08-24 17:10:01 +00:00
|
|
|
// A Graph just holds all nodes in the graph
|
2017-08-15 14:55:28 +00:00
|
|
|
type Graph struct {
|
2017-08-24 16:16:04 +00:00
|
|
|
nodes []Node
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func New() *Graph {
|
2017-08-17 18:12:16 +00:00
|
|
|
return new(Graph)
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 17:10:01 +00:00
|
|
|
// A Node is a label and a slice of references to other nodes
|
|
|
|
type Node struct {
|
|
|
|
label string
|
|
|
|
connections []Node
|
|
|
|
}
|
|
|
|
|
2017-08-17 18:12:16 +00:00
|
|
|
func (g *Graph) AddNode(lbl string) {
|
2017-08-24 16:16:04 +00:00
|
|
|
if g.getNode(lbl) == nil {
|
|
|
|
g.nodes = append(g.nodes, Node{label: lbl})
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-24 16:16:04 +00:00
|
|
|
func (g *Graph) getNode(lbl string) *Node {
|
|
|
|
for i := range g.nodes {
|
|
|
|
if g.nodes[i].label == lbl {
|
|
|
|
return &g.nodes[i]
|
|
|
|
}
|
2017-08-17 18:12:16 +00:00
|
|
|
}
|
2017-08-24 16:16:04 +00:00
|
|
|
return nil
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 16:16:04 +00:00
|
|
|
func (g *Graph) AddArc(fr, to string) {
|
|
|
|
frN := g.getNode(fr)
|
|
|
|
if frN == nil {
|
|
|
|
g.AddNode(fr)
|
|
|
|
frN = g.getNode(fr)
|
2017-08-17 18:12:16 +00:00
|
|
|
}
|
2017-08-24 16:16:04 +00:00
|
|
|
toN := g.getNode(to)
|
|
|
|
if g.getNode(to) == nil {
|
|
|
|
g.AddNode(to)
|
|
|
|
toN = g.getNode(to)
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
2017-08-24 16:16:04 +00:00
|
|
|
frN.connections = append(frN.connections, *toN)
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 16:16:04 +00:00
|
|
|
func (g *Graph) ArcList() []string {
|
|
|
|
var ret []string
|
|
|
|
for _, k := range g.nodes {
|
|
|
|
for _, l := range k.connections {
|
|
|
|
ret = append(ret, k.label+" -> "+l.label)
|
2017-08-17 18:12:16 +00:00
|
|
|
}
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
2017-08-24 16:16:04 +00:00
|
|
|
return ret
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 16:16:04 +00:00
|
|
|
// getPath returns the shortest path from 'fr' to 'to'
|
|
|
|
func (g *Graph) getPath(fr, to string) []string {
|
|
|
|
if fr == to {
|
|
|
|
return []string{fr}
|
2017-08-17 18:12:16 +00:00
|
|
|
}
|
2017-08-24 16:16:04 +00:00
|
|
|
st := g.getNode(fr)
|
|
|
|
if st == nil {
|
|
|
|
// Couldn't find the starting node
|
|
|
|
return []string{}
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
2017-08-24 16:16:04 +00:00
|
|
|
var valPaths [][]string
|
|
|
|
for i := range st.connections {
|
|
|
|
if st.connections[i].label == to {
|
|
|
|
return []string{fr, to}
|
|
|
|
}
|
|
|
|
tst := g.getPath(st.connections[i].label, to)
|
|
|
|
if len(tst) > 0 {
|
2017-08-24 17:10:01 +00:00
|
|
|
tst = append([]string{fr}, tst...)
|
2017-08-24 16:16:04 +00:00
|
|
|
valPaths = append(valPaths, tst)
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
|
|
|
}
|
2017-08-24 16:16:04 +00:00
|
|
|
if len(valPaths) == 0 {
|
|
|
|
// No valid paths found
|
|
|
|
return []string{}
|
|
|
|
}
|
2017-08-15 14:55:28 +00:00
|
|
|
var ret []string
|
2017-08-24 16:16:04 +00:00
|
|
|
for i := range valPaths {
|
|
|
|
if len(valPaths[i]) < len(ret) || len(ret) == 0 {
|
|
|
|
ret = valPaths[i]
|
|
|
|
}
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2017-08-24 16:16:04 +00:00
|
|
|
func (g *Graph) ChangeRoot(oldRoot, newRoot string) *Graph {
|
|
|
|
pth := g.getPath(oldRoot, newRoot)
|
|
|
|
if len(pth) == 0 {
|
|
|
|
// No valid path
|
|
|
|
return g
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
2017-08-24 16:16:04 +00:00
|
|
|
for oldRoot != newRoot {
|
|
|
|
// We just keep swapping the first two in pth until we're done
|
|
|
|
if len(pth) >= 2 {
|
|
|
|
n := g.getNode(pth[1])
|
|
|
|
if n == nil {
|
|
|
|
return g
|
|
|
|
}
|
|
|
|
p := g.getNode(pth[0])
|
|
|
|
if p == nil {
|
|
|
|
return g
|
|
|
|
}
|
|
|
|
p.removeConnection(n.label)
|
|
|
|
n.connections = append(n.connections, *p)
|
|
|
|
oldRoot = n.label
|
2017-08-24 17:10:01 +00:00
|
|
|
pth = g.getPath(oldRoot, newRoot)
|
2017-08-15 14:55:28 +00:00
|
|
|
}
|
|
|
|
}
|
2017-08-24 16:16:04 +00:00
|
|
|
return g
|
2017-08-17 18:12:16 +00:00
|
|
|
}
|
|
|
|
|
2017-08-24 16:16:04 +00:00
|
|
|
func (n *Node) removeConnection(to string) {
|
|
|
|
for i, k := range n.connections {
|
|
|
|
if k.label == to {
|
|
|
|
copy(n.connections[i:], n.connections[i+1:])
|
|
|
|
n.connections[len(n.connections)-1] = Node{}
|
|
|
|
n.connections = n.connections[:len(n.connections)-1]
|
2017-08-17 18:12:16 +00:00
|
|
|
}
|
|
|
|
}
|
2017-08-24 16:16:04 +00:00
|
|
|
}
|