/*============================================================================
  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()));
        }
      }
    }
}
