11package algo
22
33import (
4+ "context"
45 "math"
56
67 "github.com/gammazero/deque"
@@ -10,7 +11,7 @@ import (
1011 "github.com/specterops/dawgs/util"
1112)
1213
13- func StronglyConnectedComponents (digraph container.DirectedGraph ) ([]cardinality.Duplex [uint64 ], map [uint64 ]uint64 ) {
14+ func StronglyConnectedComponents (ctx context. Context , digraph container.DirectedGraph ) ([]cardinality.Duplex [uint64 ], map [uint64 ]uint64 ) {
1415 defer util .SLogMeasure ("StronglyConnectedComponents" )()
1516
1617 type descentCursor struct {
@@ -34,86 +35,84 @@ func StronglyConnectedComponents(digraph container.DirectedGraph) ([]cardinality
3435 )
3536
3637 digraph .EachNode (func (node uint64 ) bool {
37- if _ , visited := visitedIndex [node ]; visited {
38- return true
39- }
40-
41- dfsDescentStack = append (dfsDescentStack , & descentCursor {
42- id : node ,
43- branches : digraph .AdjacentNodes (node , graph .DirectionOutbound ),
44- branchIdx : 0 ,
45- })
46-
47- for len (dfsDescentStack ) > 0 {
48- nextCursor := dfsDescentStack [len (dfsDescentStack )- 1 ]
38+ if _ , visited := visitedIndex [node ]; ! visited {
39+ dfsDescentStack = append (dfsDescentStack , & descentCursor {
40+ id : node ,
41+ branches : digraph .AdjacentNodes (node , graph .DirectionOutbound ),
42+ branchIdx : 0 ,
43+ })
4944
50- if nextCursor .branchIdx == 0 {
51- // First visit of this node
52- visitedIndex [nextCursor .id ] = index
53- lowLinks [nextCursor .id ] = index
54- index += 1
45+ for len (dfsDescentStack ) > 0 {
46+ nextCursor := dfsDescentStack [len (dfsDescentStack )- 1 ]
5547
56- stack = append (stack , nextCursor .id )
57- onStack .Add (nextCursor .id )
58- } else if lastSearchedNodeID != nextCursor .id {
59- // Revisiting this node from a descending DFS
60- lowLinks [nextCursor .id ] = min (lowLinks [nextCursor .id ], lowLinks [lastSearchedNodeID ])
61- }
48+ if nextCursor .branchIdx == 0 {
49+ // First visit of this node
50+ visitedIndex [nextCursor .id ] = index
51+ lowLinks [nextCursor .id ] = index
52+ index += 1
6253
63- // Set to the current cursor ID for ascent
64- lastSearchedNodeID = nextCursor .id
65-
66- if nextCursor .branchIdx < len (nextCursor .branches ) {
67- // Advance to the next branch
68- nextBranchID := nextCursor .branches [nextCursor .branchIdx ]
69- nextCursor .branchIdx += 1
70-
71- if _ , visited := visitedIndex [nextBranchID ]; ! visited {
72- // This node has not been visited yet, run a DFS for it
73- lastSearchedNodeID = nextBranchID
74-
75- dfsDescentStack = append (dfsDescentStack , & descentCursor {
76- id : nextBranchID ,
77- branches : digraph .AdjacentNodes (nextBranchID , graph .DirectionOutbound ),
78- branchIdx : 0 ,
79- })
80- } else if onStack .Contains (nextBranchID ) {
81- // Branch is on the traversal stack; hence it is also in the current SCC
82- lowLinks [nextCursor .id ] = min (lowLinks [nextCursor .id ], visitedIndex [nextBranchID ])
54+ stack = append (stack , nextCursor .id )
55+ onStack .Add (nextCursor .id )
56+ } else if lastSearchedNodeID != nextCursor .id {
57+ // Revisiting this node from a descending DFS
58+ lowLinks [nextCursor .id ] = min (lowLinks [nextCursor .id ], lowLinks [lastSearchedNodeID ])
8359 }
84- } else {
85- // Finished visiting branches; exiting node
86- dfsDescentStack = dfsDescentStack [:len (dfsDescentStack )- 1 ]
8760
88- if lowLinks [nextCursor .id ] == visitedIndex [nextCursor .id ] {
89- var (
90- scc = cardinality .NewBitmap64 ()
91- sccID = uint64 (len (stronglyConnectedComponents ))
92- )
61+ // Set to the current cursor ID for ascent
62+ lastSearchedNodeID = nextCursor .id
63+
64+ if nextCursor .branchIdx < len (nextCursor .branches ) {
65+ // Advance to the next branch
66+ nextBranchID := nextCursor .branches [nextCursor .branchIdx ]
67+ nextCursor .branchIdx += 1
68+
69+ if _ , visited := visitedIndex [nextBranchID ]; ! visited {
70+ // This node has not been visited yet, run a DFS for it
71+ lastSearchedNodeID = nextBranchID
72+
73+ dfsDescentStack = append (dfsDescentStack , & descentCursor {
74+ id : nextBranchID ,
75+ branches : digraph .AdjacentNodes (nextBranchID , graph .DirectionOutbound ),
76+ branchIdx : 0 ,
77+ })
78+ } else if onStack .Contains (nextBranchID ) {
79+ // Branch is on the traversal stack; hence it is also in the current SCC
80+ lowLinks [nextCursor .id ] = min (lowLinks [nextCursor .id ], visitedIndex [nextBranchID ])
81+ }
82+ } else {
83+ // Finished visiting branches; exiting node
84+ dfsDescentStack = dfsDescentStack [:len (dfsDescentStack )- 1 ]
85+
86+ if lowLinks [nextCursor .id ] == visitedIndex [nextCursor .id ] {
87+ var (
88+ scc = cardinality .NewBitmap64 ()
89+ sccID = uint64 (len (stronglyConnectedComponents ))
90+ )
9391
94- for {
95- // Unwind the stack to the root of the component
96- currentNode := stack [len (stack )- 1 ]
97- stack = stack [:len (stack )- 1 ]
92+ for {
93+ // Unwind the stack to the root of the component
94+ currentNode := stack [len (stack )- 1 ]
95+ stack = stack [:len (stack )- 1 ]
9896
99- onStack .Remove (currentNode )
97+ onStack .Remove (currentNode )
10098
101- scc .Add (currentNode )
99+ scc .Add (currentNode )
102100
103- // Reverse index origin node to SCC
104- nodeToSCCIndex [currentNode ] = sccID
101+ // Reverse index origin node to SCC
102+ nodeToSCCIndex [currentNode ] = sccID
105103
106- if currentNode == nextCursor .id {
107- break
104+ if currentNode == nextCursor .id {
105+ break
106+ }
108107 }
109- }
110108
111- stronglyConnectedComponents = append (stronglyConnectedComponents , scc )
109+ stronglyConnectedComponents = append (stronglyConnectedComponents , scc )
110+ }
112111 }
113112 }
114113 }
115114
116- return true
115+ return util . IsContextLive ( ctx )
117116 })
118117
119118 return stronglyConnectedComponents , nodeToSCCIndex
@@ -260,9 +259,9 @@ func (s ComponentGraph) OriginReachable(startID, endID uint64) bool {
260259 return s .ComponentReachable (startComponent , endComponent )
261260}
262261
263- func NewComponentGraph (originGraph container.DirectedGraph ) ComponentGraph {
262+ func NewComponentGraph (ctx context. Context , originGraph container.DirectedGraph ) ComponentGraph {
264263 var (
265- componentMembers , memberComponentLookup = StronglyConnectedComponents (originGraph )
264+ componentMembers , memberComponentLookup = StronglyConnectedComponents (ctx , originGraph )
266265 componentDigraph = container .NewAdjacencyMapGraph ()
267266 nextEdgeID = uint64 (1 )
268267 )
@@ -283,7 +282,7 @@ func NewComponentGraph(originGraph container.DirectedGraph) ComponentGraph {
283282 nextEdgeID += 1
284283 }
285284
286- return true
285+ return util . IsContextLive ( ctx )
287286 })
288287
289288 originGraph .EachAdjacentNode (node , graph .DirectionOutbound , func (adjacent uint64 ) bool {
@@ -292,10 +291,10 @@ func NewComponentGraph(originGraph container.DirectedGraph) ComponentGraph {
292291 nextEdgeID += 1
293292 }
294293
295- return true
294+ return util . IsContextLive ( ctx )
296295 })
297296
298- return true
297+ return util . IsContextLive ( ctx )
299298 })
300299
301300 return ComponentGraph {
0 commit comments