forked from open-policy-agent/opa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgraph.go
90 lines (80 loc) · 2.13 KB
/
graph.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
// Copyright 2016 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package util
// Traversal defines a basic interface to perform traversals.
type Traversal interface {
// Edges should return the neighbours of node "u".
Edges(u T) []T
// Visited should return true if node "u" has already been visited in this
// traversal. If the same traversal is used multiple times, the state that
// tracks visited nodes should be reset.
Visited(u T) bool
}
// Equals should return true if node "u" equals node "v".
type Equals func(u T, v T) bool
// Iter should return true to indicate stop.
type Iter func(u T) bool
// DFS performs a depth first traversal calling f for each node starting from u.
// If f returns true, traversal stops and DFS returns true.
func DFS(t Traversal, f Iter, u T) bool {
lifo := NewLIFO(u)
for lifo.Size() > 0 {
next, _ := lifo.Pop()
if t.Visited(next) {
continue
}
if f(next) {
return true
}
for _, v := range t.Edges(next) {
lifo.Push(v)
}
}
return false
}
// BFS performs a breadth first traversal calling f for each node starting from
// u. If f returns true, traversal stops and BFS returns true.
func BFS(t Traversal, f Iter, u T) bool {
fifo := NewFIFO(u)
for fifo.Size() > 0 {
next, _ := fifo.Pop()
if t.Visited(next) {
continue
}
if f(next) {
return true
}
for _, v := range t.Edges(next) {
fifo.Push(v)
}
}
return false
}
// DFSPath returns a path from node a to node z found by performing
// a depth first traversal. If no path is found, an empty slice is returned.
func DFSPath(t Traversal, eq Equals, a, z T) []T {
p := dfsRecursive(t, eq, a, z, []T{})
for i := len(p)/2 - 1; i >= 0; i-- {
o := len(p) - i - 1
p[i], p[o] = p[o], p[i]
}
return p
}
func dfsRecursive(t Traversal, eq Equals, u, z T, path []T) []T {
if t.Visited(u) {
return path
}
for _, v := range t.Edges(u) {
if eq(v, z) {
path = append(path, z)
path = append(path, u)
return path
}
if p := dfsRecursive(t, eq, v, z, path); len(p) > 0 {
path = append(p, u)
return path
}
}
return path
}