-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmain.go
165 lines (137 loc) · 3.16 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package bfstree
import (
"fmt"
"strings"
)
var (
Debug = false // enable debug message printing
)
func dbg(format string, a ...interface{}) {
if Debug {
fmt.Printf(format, a...)
}
}
type Edge interface {
From() string
To() string
}
type BFSTree struct {
edges []Edge
}
func New(edges ...Edge) *BFSTree { return &BFSTree{edges} }
func (b *BFSTree) Len() int { return len(b.edges) }
func (b *BFSTree) Edges() []Edge { return b.edges }
func (b *BFSTree) AddEdge(e Edge) { b.edges = append(b.edges, e) }
// Return unique node names
func (b *BFSTree) Nodes() []string {
var names []string
for _, e := range b.edges {
names = append(names, e.To())
names = append(names, e.From())
}
return uniq(names)
}
// return edges from a given start point
func (b *BFSTree) fromNode(start string) (res []Edge) {
for _, e := range b.edges {
if e.From() == start {
res = append(res, e)
}
}
return res
}
func (b *BFSTree) FindPath(start string, end string) (path *Path, err error) {
var iter int
var paths []*Path
// Create start paths from origin node
for _, e := range b.fromNode(start) {
p := newPath(e)
if e.To() == end {
return p, nil
}
paths = append(paths, p)
}
for len(paths) > 0 {
var newPaths []*Path
dbg("iter %d\n", iter)
for _, p := range paths {
dbg(" %s\n", p)
children := b.fromNode(p.Last().To())
// maximum path depth reached, drop
if len(children) == 0 {
dbg(" dropped path (no children): %s\n", p)
continue
}
// branch path for each child node
for _, e := range children {
// drop circular paths
if p.IsCircular(e) {
dbg(" dropped circular child: %s->%s\n", e.From(), e.To())
continue
}
np := newPath(p.edges...)
np.AddEdge(e)
dbg(" new path branch: %s\n", np)
if e.To() == end {
return np, nil
}
newPaths = append(newPaths, np)
}
}
iter++
paths = newPaths
}
return path, fmt.Errorf("no path found")
}
type Path struct {
*BFSTree
}
func newPath(edges ...Edge) *Path {
np := &Path{&BFSTree{}}
for _, e := range edges {
np.AddEdge(e)
}
return np
}
func (p *Path) String() string { return strings.Join(p.Nodes(), "->") }
func (p *Path) Last() Edge { return p.edges[len(p.edges)-1] }
// Returns names for all path nodes in the order they are transversed
func (p *Path) Nodes() []string {
names := []string{p.edges[0].From()}
for _, e := range p.edges {
names = append(names, e.To())
}
return names
}
// Return whether a given edge, if added, would result in
// a circular or recursive path
func (p *Path) IsCircular(edge Edge) bool {
child := edge.To()
for _, e := range p.edges {
if e.From() == child || e.To() == child {
return true
}
}
return false
}
// Return whether this path transverses a given node name
func (p *Path) HasNode(s string) bool {
for _, e := range p.edges {
if e.From() == s || e.To() == s {
return true
}
}
return false
}
// uniq returns a unique subset of the string slice provided.
func uniq(a []string) []string {
u := make([]string, 0, len(a))
m := make(map[string]bool)
for _, val := range a {
if _, ok := m[val]; !ok {
m[val] = true
u = append(u, val)
}
}
return u
}