This repository has been archived by the owner on Nov 22, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
nodey.go
225 lines (184 loc) · 4.79 KB
/
nodey.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// Copyright © 2019 Developer Network, LLC
//
// This file is subject to the terms and conditions defined in
// file 'LICENSE', which is part of this source code package.
package graph
import (
"context"
"fmt"
"strings"
"sync"
"devnw.com/validator"
"github.com/pkg/errors"
)
// TODO: add the ability to use heuristics through function literals
// TODO: Add support for topographical order
// TODO: Add path loading for reachable nodes
// TODO: Add shortest path loading for reachable nodes
// Node is the interface representation of a node in the graph
// type Node interface {
// Edges(ctx context.Context) <-chan Edge
// AllReachable(ctx context.Context, alg int) <-chan Node
// Reachable(ctx context.Context, alg int, node Node) bool
// AddEdge(relation Node, edge Edge) error
// DirectMutual(node Node) bool
// Value() interface{}
// Cost() float64
// Parent() Node
// String(ctx context.Context) string
// }
// Node is the interface representation of a node in the graph
type Node struct {
Value interface{}
Parent *Node
Cost float64
edges sync.Map
}
// AllReachable returns all of the nodes that are accessible from this node
// the algorithm passed in is based off of the algorithms defined in the
// CONST files for search constants. Defaults to BFS.
func (n *Node) AllReachable(ctx context.Context, alg int) <-chan *Node {
var nodes = make(chan *Node)
go func(nodes chan<- *Node) {
defer close(nodes)
switch alg {
case DFS:
// TODO: Call out to depth first search for results
case BFS:
fallthrough
default:
// TODO: Call out to breadth first search
}
}(nodes)
return nodes
}
// TODO: Add path to the return from reachable. This will need to be implemented using the parallel DFS and BFS options
// Reachable determines if a node is reachable from this node
func (n *Node) Reachable(ctx context.Context, alg int, node *Node) (reachable bool) {
var nodes = n.AllReachable(ctx, alg)
for {
select {
case <-ctx.Done():
return reachable
case n, ok := <-nodes:
if ok {
if n == node {
reachable = true
return reachable
}
} else {
return reachable
}
}
}
}
// DirectMutual determines if a specific node is directly mutual to this node
func (n *Node) DirectMutual(node Node) (mutual bool) {
if validator.IsValid(node) {
if edge, ok := n.edges.Load(node); ok {
mutual = validator.IsValid(edge)
}
}
return mutual
}
// AddEdge adds an edge from this node to the related node
func (n *Node) AddEdge(relation *Node, edge Edge) (err error) {
if validator.IsValid(relation) {
if validator.IsValid(edge) {
// TODO: if the edge is weighted should the edge be placed differently in the map?
if _, loaded := n.edges.LoadOrStore(relation, edge); loaded {
err = errors.Errorf("edge already exists on node [%v]", n.Value)
}
} else {
err = errors.Errorf("invalid edge [%v]", edge)
}
} else {
err = errors.Errorf("invalid node [%v]", relation)
}
return err
}
// Edges returns a channel which streams the edges for this node
func (n *Node) Edges(ctx context.Context) <-chan Edge {
var edges = make(chan Edge)
go func(edges chan<- Edge) {
defer close(edges)
// Iterate over the edges of the node
n.edges.Range(func(key, value interface{}) bool {
select {
case <-ctx.Done():
return false
default:
if edge, ok := value.(Edge); ok {
edges <- edge
}
}
// Always loop to completion
return true
})
}(edges)
return edges
}
func (n *Node) String(ctx context.Context) string {
var output = "%v: %s"
var weighted = "(%v, %v)"
var strs []string
edges := n.Edges(ctx)
func() {
for {
select {
case <-ctx.Done():
return
case e, ok := <-edges:
if ok {
relation := e.Child()
if relation == n {
relation = e.Parent()
}
switch e.(type) {
case WeightedEdge:
if e, ok := e.(WeightedEdge); ok {
strs = append(strs, fmt.Sprintf(weighted, relation.Value, e.Weight()))
}
default:
strs = append(strs, fmt.Sprintf("%v", relation.Value))
}
} else {
return
}
}
}
}()
return fmt.Sprintf(output, n.Value, strings.Join(strs, ","))
}
func (n *Node) Export(ctx context.Context) string {
var output = "%v=%v"
var weighted = "%v=%v"
var strs []string
edges := n.Edges(ctx)
func() {
for {
select {
case <-ctx.Done():
return
case e, ok := <-edges:
if ok {
relation := e.Child()
if relation == n {
relation = e.Parent()
}
switch e.(type) {
case WeightedEdge:
if e, ok := e.(WeightedEdge); ok {
strs = append(strs, fmt.Sprintf(weighted, relation.Value, e.Weight()))
}
default:
strs = append(strs, fmt.Sprintf("%v", relation.Value))
}
} else {
return
}
}
}
}()
return fmt.Sprintf(output, n.Value, strings.Join(strs, "\n"))
}