VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END Attribute VB_Name = "API_StrongComponents" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = False Attribute VB_Exposed = False ' ===== Graph strongly linked components ==== ' Shared module version: 20220408 ' Tested in: TestCommons ' Depends on: CDS_Graph ' Required reference: Option Explicit Private graph_ As CDS_Graph Private components_ As Collection Private current_ As Scripting.Dictionary Private visited_ As Scripting.Dictionary Private reversedOrder_ As Scripting.Dictionary Public Function GetComponents(iGraph As CDS_Graph) As Collection Set graph_ = iGraph Set components_ = New Collection Call CalculateOrder Call ScanComponents Set GetComponents = components_ End Function ' ============= Private Function CalculateOrder() Set visited_ = New Scripting.Dictionary Set reversedOrder_ = New Scripting.Dictionary Dim vNode As Variant For Each vNode In graph_.Nodes If Not visited_.Exists(vNode) Then Call VisitInversed(vNode) End If Next vNode End Function Private Function ScanComponents() Set visited_ = New Scripting.Dictionary Dim nItem& Dim vItems As Variant: vItems = reversedOrder_.Keys() For nItem = UBound(vItems, 1) To LBound(vItems, 1) Step -1 Dim nodeID As Variant: nodeID = vItems(nItem) If visited_.Exists(nodeID) Then _ GoTo NEXT_NODE Set current_ = New Scripting.Dictionary Call VisitNode(nodeID) If current_.Count = 1 Then If Not graph_.HasEdge(nodeID, nodeID) Then _ GoTo NEXT_NODE End If Call components_.Add(current_) NEXT_NODE: Next nItem End Function Private Function VisitInversed(nodeID As Variant) Call visited_.Add(nodeID, 0) Call VisitChildrenInversed(nodeID) Call reversedOrder_.Add(nodeID, 0) End Function Private Function VisitChildrenInversed(nodeID As Variant) If graph_.nodes_(nodeID).inputs_.Count = 0 Then _ Exit Function Dim parentID As Variant For Each parentID In graph_.nodes_(nodeID).inputs_ If Not visited_.Exists(parentID) Then _ Call VisitInversed(parentID) Next parentID End Function Private Function VisitNode(nodeID As Variant) Call visited_.Add(nodeID, 0) Call current_.Add(nodeID, 0) Call VisitChildren(nodeID) End Function Private Function VisitChildren(nodeID As Variant) If graph_.nodes_(nodeID).outputs_.Count = 0 Then _ Exit Function Dim childID As Variant For Each childID In graph_.nodes_(nodeID).outputs_ If Not visited_.Exists(childID) Then _ Call VisitNode(childID) Next childID End Function