@@ -8,43 +8,43 @@ import (
8
8
"github.com/microsoft/typescript-go/internal/collections"
9
9
)
10
10
11
- type BreadthFirstSearchResult [N comparable ] struct {
11
+ type BreadthFirstSearchResult [N any ] struct {
12
12
Stopped bool
13
13
Path []N
14
14
}
15
15
16
- type breadthFirstSearchJob [N comparable ] struct {
16
+ type breadthFirstSearchJob [N any ] struct {
17
17
node N
18
18
parent * breadthFirstSearchJob [N ]
19
19
}
20
20
21
- type BreadthFirstSearchLevel [N comparable ] struct {
22
- jobs * collections.OrderedMap [N , * breadthFirstSearchJob [N ]]
21
+ type BreadthFirstSearchLevel [K comparable , N any ] struct {
22
+ jobs * collections.OrderedMap [K , * breadthFirstSearchJob [N ]]
23
23
}
24
24
25
- func (l * BreadthFirstSearchLevel [N ]) Has (node N ) bool {
26
- return l .jobs .Has (node )
25
+ func (l * BreadthFirstSearchLevel [K , N ]) Has (key K ) bool {
26
+ return l .jobs .Has (key )
27
27
}
28
28
29
- func (l * BreadthFirstSearchLevel [N ]) Delete (node N ) {
30
- l .jobs .Delete (node )
29
+ func (l * BreadthFirstSearchLevel [K , N ]) Delete (key K ) {
30
+ l .jobs .Delete (key )
31
31
}
32
32
33
- func (l * BreadthFirstSearchLevel [N ]) Range (f func (node N ) bool ) {
34
- for node := range l .jobs .Keys () {
35
- if ! f (node ) {
33
+ func (l * BreadthFirstSearchLevel [K , N ]) Range (f func (node N ) bool ) {
34
+ for job := range l .jobs .Values () {
35
+ if ! f (job . node ) {
36
36
return
37
37
}
38
38
}
39
39
}
40
40
41
- type BreadthFirstSearchOptions [N comparable ] struct {
41
+ type BreadthFirstSearchOptions [K comparable , N any ] struct {
42
42
// Visited is a set of nodes that have already been visited.
43
43
// If nil, a new set will be created.
44
- Visited * collections.SyncSet [N ]
44
+ Visited * collections.SyncSet [K ]
45
45
// PreprocessLevel is a function that, if provided, will be called
46
46
// before each level, giving the caller an opportunity to remove nodes.
47
- PreprocessLevel func (* BreadthFirstSearchLevel [N ])
47
+ PreprocessLevel func (* BreadthFirstSearchLevel [K , N ])
48
48
}
49
49
50
50
// BreadthFirstSearchParallel performs a breadth-first search on a graph
@@ -55,41 +55,42 @@ func BreadthFirstSearchParallel[N comparable](
55
55
neighbors func (N ) []N ,
56
56
visit func (node N ) (isResult bool , stop bool ),
57
57
) BreadthFirstSearchResult [N ] {
58
- return BreadthFirstSearchParallelEx (start , neighbors , visit , BreadthFirstSearchOptions [N ]{})
58
+ return BreadthFirstSearchParallelEx (start , neighbors , visit , BreadthFirstSearchOptions [N , N ]{}, Identity )
59
59
}
60
60
61
61
// BreadthFirstSearchParallelEx is an extension of BreadthFirstSearchParallel that allows
62
62
// the caller to pass a pre-seeded set of already-visited nodes and a preprocessing function
63
63
// that can be used to remove nodes from each level before parallel processing.
64
- func BreadthFirstSearchParallelEx [N comparable ](
64
+ func BreadthFirstSearchParallelEx [K comparable , N any ](
65
65
start N ,
66
66
neighbors func (N ) []N ,
67
67
visit func (node N ) (isResult bool , stop bool ),
68
- options BreadthFirstSearchOptions [N ],
68
+ options BreadthFirstSearchOptions [K , N ],
69
+ getKey func (N ) K ,
69
70
) BreadthFirstSearchResult [N ] {
70
71
visited := options .Visited
71
72
if visited == nil {
72
- visited = & collections.SyncSet [N ]{}
73
+ visited = & collections.SyncSet [K ]{}
73
74
}
74
75
75
76
type result struct {
76
77
stop bool
77
78
job * breadthFirstSearchJob [N ]
78
- next * collections.OrderedMap [N , * breadthFirstSearchJob [N ]]
79
+ next * collections.OrderedMap [K , * breadthFirstSearchJob [N ]]
79
80
}
80
81
81
82
var fallback * breadthFirstSearchJob [N ]
82
83
// processLevel processes each node at the current level in parallel.
83
84
// It produces either a list of jobs to be processed in the next level,
84
85
// or a result if the visit function returns true for any node.
85
- processLevel := func (index int , jobs * collections.OrderedMap [N , * breadthFirstSearchJob [N ]]) result {
86
+ processLevel := func (index int , jobs * collections.OrderedMap [K , * breadthFirstSearchJob [N ]]) result {
86
87
var lowestFallback atomic.Int64
87
88
var lowestGoal atomic.Int64
88
89
var nextJobCount atomic.Int64
89
90
lowestGoal .Store (math .MaxInt64 )
90
91
lowestFallback .Store (math .MaxInt64 )
91
92
if options .PreprocessLevel != nil {
92
- options .PreprocessLevel (& BreadthFirstSearchLevel [N ]{jobs : jobs })
93
+ options .PreprocessLevel (& BreadthFirstSearchLevel [K , N ]{jobs : jobs })
93
94
}
94
95
next := make ([][]* breadthFirstSearchJob [N ], jobs .Size ())
95
96
var wg sync.WaitGroup
@@ -103,7 +104,7 @@ func BreadthFirstSearchParallelEx[N comparable](
103
104
}
104
105
105
106
// If we have already visited this node, skip it.
106
- if ! visited .AddIfAbsent (j .node ) {
107
+ if ! visited .AddIfAbsent (getKey ( j .node ) ) {
107
108
// Note that if we are here, we already visited this node at a
108
109
// previous *level*, which means `visit` must have returned false,
109
110
// so we don't need to update our result indices. This holds true
@@ -152,13 +153,13 @@ func BreadthFirstSearchParallelEx[N comparable](
152
153
_ , fallback , _ = jobs .EntryAt (int (index ))
153
154
}
154
155
}
155
- nextJobs := collections.NewOrderedMapWithSizeHint [N , * breadthFirstSearchJob [N ]](int (nextJobCount .Load ()))
156
+ nextJobs := collections.NewOrderedMapWithSizeHint [K , * breadthFirstSearchJob [N ]](int (nextJobCount .Load ()))
156
157
for _ , jobs := range next {
157
158
for _ , j := range jobs {
158
- if ! nextJobs .Has (j .node ) {
159
+ if ! nextJobs .Has (getKey ( j .node ) ) {
159
160
// Deduplicate synchronously to avoid messy locks and spawning
160
161
// unnecessary goroutines.
161
- nextJobs .Set (j .node , j )
162
+ nextJobs .Set (getKey ( j .node ) , j )
162
163
}
163
164
}
164
165
}
@@ -175,8 +176,8 @@ func BreadthFirstSearchParallelEx[N comparable](
175
176
}
176
177
177
178
levelIndex := 0
178
- level := collections .NewOrderedMapFromList ([]collections.MapEntry [N , * breadthFirstSearchJob [N ]]{
179
- {Key : start , Value : & breadthFirstSearchJob [N ]{node : start }},
179
+ level := collections .NewOrderedMapFromList ([]collections.MapEntry [K , * breadthFirstSearchJob [N ]]{
180
+ {Key : getKey ( start ) , Value : & breadthFirstSearchJob [N ]{node : start }},
180
181
})
181
182
for level .Size () > 0 {
182
183
result := processLevel (levelIndex , level )
0 commit comments