/src/CMake/Source/cmComputeComponentGraph.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmComputeComponentGraph.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <cassert> |
7 | | #include <cstddef> |
8 | | #include <limits> |
9 | | |
10 | | size_t const cmComputeComponentGraph::INVALID_COMPONENT = |
11 | | std::numeric_limits<size_t>::max(); |
12 | | |
13 | | cmComputeComponentGraph::cmComputeComponentGraph(Graph const& input) |
14 | 0 | : InputGraph(input) |
15 | 0 | { |
16 | 0 | } |
17 | | |
18 | 0 | cmComputeComponentGraph::~cmComputeComponentGraph() = default; |
19 | | |
20 | | void cmComputeComponentGraph::Compute() |
21 | 0 | { |
22 | | // Identify components. |
23 | 0 | this->Tarjan(); |
24 | | |
25 | | // Compute the component graph. |
26 | 0 | this->ComponentGraph.resize(0); |
27 | 0 | this->ComponentGraph.resize(this->Components.size()); |
28 | 0 | this->TransferEdges(); |
29 | 0 | } |
30 | | |
31 | | void cmComputeComponentGraph::Tarjan() |
32 | 0 | { |
33 | 0 | size_t n = this->InputGraph.size(); |
34 | 0 | TarjanEntry entry = { 0, 0 }; |
35 | 0 | this->TarjanEntries.resize(0); |
36 | 0 | this->TarjanEntries.resize(n, entry); |
37 | 0 | this->TarjanComponents.resize(0); |
38 | 0 | this->TarjanComponents.resize(n, INVALID_COMPONENT); |
39 | 0 | this->TarjanWalkId = 0; |
40 | 0 | this->TarjanVisited.resize(0); |
41 | 0 | this->TarjanVisited.resize(n, 0); |
42 | 0 | for (size_t i = 0; i < n; ++i) { |
43 | | // Start a new DFS from this node if it has never been visited. |
44 | 0 | if (!this->TarjanVisited[i]) { |
45 | 0 | assert(this->TarjanStack.empty()); |
46 | 0 | ++this->TarjanWalkId; |
47 | 0 | this->TarjanIndex = 0; |
48 | 0 | this->TarjanVisit(i); |
49 | 0 | } |
50 | 0 | } |
51 | 0 | } |
52 | | |
53 | | void cmComputeComponentGraph::TarjanVisit(size_t i) |
54 | 0 | { |
55 | | // We are now visiting this node. |
56 | 0 | this->TarjanVisited[i] = this->TarjanWalkId; |
57 | | |
58 | | // Initialize the entry. |
59 | 0 | this->TarjanEntries[i].Root = i; |
60 | 0 | this->TarjanComponents[i] = INVALID_COMPONENT; |
61 | 0 | this->TarjanEntries[i].VisitIndex = ++this->TarjanIndex; |
62 | 0 | this->TarjanStack.push(i); |
63 | | |
64 | | // Follow outgoing edges. |
65 | 0 | EdgeList const& nl = this->InputGraph[i]; |
66 | 0 | for (cmGraphEdge const& ni : nl) { |
67 | 0 | size_t j = ni; |
68 | | |
69 | | // Ignore edges to nodes that have been reached by a previous DFS |
70 | | // walk. Since we did not reach the current node from that walk |
71 | | // it must not belong to the same component and it has already |
72 | | // been assigned to a component. |
73 | 0 | if (this->TarjanVisited[j] > 0 && |
74 | 0 | this->TarjanVisited[j] < this->TarjanWalkId) { |
75 | 0 | continue; |
76 | 0 | } |
77 | | |
78 | | // Visit the destination if it has not yet been visited. |
79 | 0 | if (!this->TarjanVisited[j]) { |
80 | 0 | this->TarjanVisit(j); |
81 | 0 | } |
82 | | |
83 | | // If the destination has not yet been assigned to a component, |
84 | | // check if it has a better root for the current object. |
85 | 0 | if (this->TarjanComponents[j] == INVALID_COMPONENT) { |
86 | 0 | if (this->TarjanEntries[this->TarjanEntries[j].Root].VisitIndex < |
87 | 0 | this->TarjanEntries[this->TarjanEntries[i].Root].VisitIndex) { |
88 | 0 | this->TarjanEntries[i].Root = this->TarjanEntries[j].Root; |
89 | 0 | } |
90 | 0 | } |
91 | 0 | } |
92 | | |
93 | | // Check if we have found a component. |
94 | 0 | if (this->TarjanEntries[i].Root == i) { |
95 | | // Yes. Create it. |
96 | 0 | size_t c = this->Components.size(); |
97 | 0 | this->Components.emplace_back(); |
98 | 0 | NodeList& component = this->Components[c]; |
99 | | |
100 | | // Populate the component list. |
101 | 0 | size_t j; |
102 | 0 | do { |
103 | | // Get the next member of the component. |
104 | 0 | j = this->TarjanStack.top(); |
105 | 0 | this->TarjanStack.pop(); |
106 | | |
107 | | // Assign the member to the component. |
108 | 0 | this->TarjanComponents[j] = c; |
109 | 0 | this->TarjanEntries[j].Root = i; |
110 | | |
111 | | // Store the node in its component. |
112 | 0 | component.push_back(j); |
113 | 0 | } while (j != i); |
114 | | |
115 | | // Sort the component members for clarity. |
116 | 0 | std::sort(component.begin(), component.end()); |
117 | 0 | } |
118 | 0 | } |
119 | | |
120 | | void cmComputeComponentGraph::TransferEdges() |
121 | 0 | { |
122 | | // Map inter-component edges in the original graph to edges in the |
123 | | // component graph. |
124 | 0 | size_t n = this->InputGraph.size(); |
125 | 0 | for (size_t i = 0; i < n; ++i) { |
126 | 0 | size_t i_component = this->TarjanComponents[i]; |
127 | 0 | EdgeList const& nl = this->InputGraph[i]; |
128 | 0 | for (cmGraphEdge const& ni : nl) { |
129 | 0 | size_t j = ni; |
130 | 0 | size_t j_component = this->TarjanComponents[j]; |
131 | 0 | if (i_component != j_component) { |
132 | | // We do not attempt to combine duplicate edges, but instead |
133 | | // store the inter-component edges with suitable multiplicity. |
134 | 0 | this->ComponentGraph[i_component].emplace_back( |
135 | 0 | j_component, ni.IsStrong(), ni.IsCross(), ni.GetBacktrace()); |
136 | 0 | } |
137 | 0 | } |
138 | 0 | } |
139 | 0 | } |