summaryrefslogtreecommitdiffstats
path: root/Source/cmComputeComponentGraph.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmComputeComponentGraph.cxx')
-rw-r--r--Source/cmComputeComponentGraph.cxx142
1 files changed, 142 insertions, 0 deletions
diff --git a/Source/cmComputeComponentGraph.cxx b/Source/cmComputeComponentGraph.cxx
new file mode 100644
index 0000000..4f21c3a
--- /dev/null
+++ b/Source/cmComputeComponentGraph.cxx
@@ -0,0 +1,142 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+#include "cmComputeComponentGraph.h"
+
+#include <algorithm>
+
+#include <assert.h>
+
+cmComputeComponentGraph::cmComputeComponentGraph(Graph const& input)
+ : InputGraph(input)
+{
+ // Identify components.
+ this->Tarjan();
+
+ // Compute the component graph.
+ this->ComponentGraph.resize(0);
+ this->ComponentGraph.resize(this->Components.size());
+ this->TransferEdges();
+}
+
+cmComputeComponentGraph::~cmComputeComponentGraph()
+{
+}
+
+void cmComputeComponentGraph::Tarjan()
+{
+ int n = static_cast<int>(this->InputGraph.size());
+ TarjanEntry entry = { 0, 0 };
+ this->TarjanEntries.resize(0);
+ this->TarjanEntries.resize(n, entry);
+ this->TarjanComponents.resize(0);
+ this->TarjanComponents.resize(n, -1);
+ this->TarjanWalkId = 0;
+ this->TarjanVisited.resize(0);
+ this->TarjanVisited.resize(n, 0);
+ for (int i = 0; i < n; ++i) {
+ // Start a new DFS from this node if it has never been visited.
+ if (!this->TarjanVisited[i]) {
+ assert(this->TarjanStack.empty());
+ ++this->TarjanWalkId;
+ this->TarjanIndex = 0;
+ this->TarjanVisit(i);
+ }
+ }
+}
+
+void cmComputeComponentGraph::TarjanVisit(int i)
+{
+ // We are now visiting this node.
+ this->TarjanVisited[i] = this->TarjanWalkId;
+
+ // Initialize the entry.
+ this->TarjanEntries[i].Root = i;
+ this->TarjanComponents[i] = -1;
+ this->TarjanEntries[i].VisitIndex = ++this->TarjanIndex;
+ this->TarjanStack.push(i);
+
+ // Follow outgoing edges.
+ EdgeList const& nl = this->InputGraph[i];
+ for (EdgeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni) {
+ int j = *ni;
+
+ // Ignore edges to nodes that have been reached by a previous DFS
+ // walk. Since we did not reach the current node from that walk
+ // it must not belong to the same component and it has already
+ // been assigned to a component.
+ if (this->TarjanVisited[j] > 0 &&
+ this->TarjanVisited[j] < this->TarjanWalkId) {
+ continue;
+ }
+
+ // Visit the destination if it has not yet been visited.
+ if (!this->TarjanVisited[j]) {
+ this->TarjanVisit(j);
+ }
+
+ // If the destination has not yet been assigned to a component,
+ // check if it has a better root for the current object.
+ if (this->TarjanComponents[j] < 0) {
+ if (this->TarjanEntries[this->TarjanEntries[j].Root].VisitIndex <
+ this->TarjanEntries[this->TarjanEntries[i].Root].VisitIndex) {
+ this->TarjanEntries[i].Root = this->TarjanEntries[j].Root;
+ }
+ }
+ }
+
+ // Check if we have found a component.
+ if (this->TarjanEntries[i].Root == i) {
+ // Yes. Create it.
+ int c = static_cast<int>(this->Components.size());
+ this->Components.push_back(NodeList());
+ NodeList& component = this->Components[c];
+
+ // Populate the component list.
+ int j;
+ do {
+ // Get the next member of the component.
+ j = this->TarjanStack.top();
+ this->TarjanStack.pop();
+
+ // Assign the member to the component.
+ this->TarjanComponents[j] = c;
+ this->TarjanEntries[j].Root = i;
+
+ // Store the node in its component.
+ component.push_back(j);
+ } while (j != i);
+
+ // Sort the component members for clarity.
+ std::sort(component.begin(), component.end());
+ }
+}
+
+void cmComputeComponentGraph::TransferEdges()
+{
+ // Map inter-component edges in the original graph to edges in the
+ // component graph.
+ int n = static_cast<int>(this->InputGraph.size());
+ for (int i = 0; i < n; ++i) {
+ int i_component = this->TarjanComponents[i];
+ EdgeList const& nl = this->InputGraph[i];
+ for (EdgeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni) {
+ int j = *ni;
+ int j_component = this->TarjanComponents[j];
+ if (i_component != j_component) {
+ // We do not attempt to combine duplicate edges, but instead
+ // store the inter-component edges with suitable multiplicity.
+ this->ComponentGraph[i_component].push_back(
+ cmGraphEdge(j_component, ni->IsStrong()));
+ }
+ }
+ }
+}