/* Cycle detector that returns a list of
* edges involved in cycles in a digraph.
*
* Kieran Simpson 2006
*/
#include <iostream>
#include <stack>
#include <vector>
#include <cassert>
#include <cycle_detector.h>
#define VISIT_DEBUG
#define RUN_DEBUG
using namespace std;
using namespace cola;
// a global var representing time
TimeStamp Time;
CycleDetector::CycleDetector(unsigned numVertices, Edges *edges) {
this->V = numVertices;
this->edges = edges;
nodes = NULL;
// make the adjacency matrix
this->make_matrix();
assert(nodes->size() == this->V);
}
CycleDetector::~CycleDetector() {
if (nodes != NULL) {
for (unsigned i = 0; i < nodes->size(); i++) { if ((*nodes)[i] != NULL) { delete (*nodes)[i]; } }
delete nodes;
}
}
void CycleDetector::make_matrix() {
Edges::iterator ei;
Edge anEdge;
Node *newNode;
if (nodes != NULL) {
for (unsigned i = 0; i < nodes->size(); i++) { if ((*nodes)[i] != NULL) { delete (*nodes)[i]; } }
delete nodes;
}
nodes = new vector<Node *>(this->V, NULL);
traverse.clear();
// we should not have an empty array
assert(!nodes->empty());
assert(traverse.empty());
// from the edges passed, fill the adjacency matrix
for (ei = edges->begin(); ei != edges->end(); ++ei) {
anEdge = *ei;
// the matrix is indexed by the first vertex of the edge
// the second vertex of the edge is pushed onto another
// vector of all vertices connected to the first vertex
// with a directed edge
#ifdef ADJMAKE_DEBUG
cout << "vertex1: " << anEdge.first << ", vertex2: " << anEdge.second << endl;
#endif
if (!find_node(nodes, anEdge.first)) {
#ifdef ADJMAKE_DEBUG
cout << "Making a new vector indexed at: " << anEdge.first << endl;
#endif
newNode = new Node(anEdge.first);
newNode->dests.push_back(anEdge.second);
(*nodes)[anEdge.first] = newNode;
}
else {
(*nodes)[anEdge.first]->dests.push_back(anEdge.second);
}
// check if the destination vertex exists in the nodes map
if (!find_node(nodes, anEdge.second)) {
#ifdef ADJMAKE_DEBUG
cerr << "Making a new vector indexed at: " << anEdge.second << endl;
#endif
newNode = new Node(anEdge.second);
(*nodes)[anEdge.second] = newNode;
}
}
assert(!nodes->empty());
// the following block is code to print out
// the adjacency matrix.
#ifdef ADJMAKE_DEBUG
for (unsigned i = 0; i < nodes->size(); i++) {
Node *node = (*nodes)[i];
cout << "nodes[" << node->id << "]: ";
if (isSink(node)) { cout << "SINK"; }
else {
for (unsigned j = 0; j < node->dests.size(); j++) { cout << node->dests[j] << " "; }
}
cout << endl;
}
#endif
}
vector<bool> *CycleDetector::detect_cycles() {
cyclicEdgesMapping = new vector<bool>(edges->size(), false);
assert(!nodes->empty());
assert(!edges->empty());
// make a copy of the graph to ensure that we have visited all
// vertices
traverse.clear(); assert(traverse.empty());
for (unsigned i = 0; i < V; i++) { traverse.push_back(i); }
#ifdef SETUP_DEBUG
for (unsigned i = 0; i < traverse.size(); i++) {
cout << "traverse{" << i << "}: " << traverse[i] << endl;
}
#endif
// find the cycles
assert(nodes->size() > 1);
// while we still have vertices to visit, visit.
while (!traverse.empty()) {
// mark any vertices seen in a previous run as closed
while (!seenInRun.empty()) {
unsigned v = seenInRun.top();
seenInRun.pop();
#ifdef RUN_DEBUG
cout << "Marking vertex(" << v << ") as CLOSED" << endl;
#endif
(*nodes)[v]->status = Node::DoneVisiting;
}
assert(seenInRun.empty());
#ifdef VISIT_DEBUG
cout << "begining search at vertex(" << traverse[0] << ")" << endl;
#endif
Time = 0;
// go go go
visit(traverse[0]);
}
// clean up
while (!seenInRun.empty()) { seenInRun.pop(); }
assert(seenInRun.empty());
assert(traverse.empty());
return cyclicEdgesMapping;
}
void CycleDetector::mod_graph(unsigned numVertices, Edges *edges) {
this->V = numVertices;
this->edges = edges;
// remake the adjaceny matrix
this->make_matrix();
assert(nodes->size() == this->V);
}
void CycleDetector::visit(unsigned k) {
unsigned cycleOpen;
Node *thisNode = (*nodes)[k];
// state that we have seen this vertex
pair< bool, vector<unsigned>::iterator > haveSeen = find_node(traverse, k);
if (haveSeen.first) {
#ifdef VISIT_DEBUG
cout << "Visiting vertex(" << k << ") for the first time" << endl;
#endif
traverse.erase(haveSeen.second);
}
seenInRun.push(k);
// set up this node as being visited.
thisNode->stamp = ++Time;
thisNode->status = Node::BeingVisited;
// traverse to all the vertices adjacent to this vertex.
for (unsigned n = 0; n < thisNode->dests.size(); n++) {
Node *otherNode = (*nodes)[thisNode->dests[n]];
if (otherNode->status != Node::DoneVisiting) {
if (otherNode->status == Node::NotVisited) {
// visit this node
#ifdef VISIT_DEBUG
cout << "traversing from vertex(" << k << ") to vertex(" << otherNode->id << ")" << endl;
#endif
visit(otherNode->id);
}
// if we are part of a cycle get the timestamp of the ancestor
if (otherNode->cyclicAncestor != NULL) { cycleOpen = otherNode->cyclicAncestor->stamp; }
// else just get the timestamp of the node we just visited
else { cycleOpen = otherNode->stamp; }
// compare the stamp of the traversal with our stamp
if (cycleOpen <= thisNode->stamp) {
if (otherNode->cyclicAncestor == NULL) { otherNode->cyclicAncestor = otherNode; }
// store the cycle
for (unsigned i = 0; i < edges->size(); i++) {
if ((*edges)[i] == Edge(k, otherNode->id)) { (*cyclicEdgesMapping)[i] = true; }
#ifdef OUTPUT_DEBUG
cout << "Setting cyclicEdgesMapping[" << i << "] to true" << endl;
#endif
}
// this node is part of a cycle
if (thisNode->cyclicAncestor == NULL) { thisNode->cyclicAncestor = otherNode->cyclicAncestor; }
// see if we are part of a cycle with a cyclicAncestor that possesses a lower timestamp
if (otherNode->cyclicAncestor->stamp < thisNode->cyclicAncestor->stamp) { thisNode->cyclicAncestor = otherNode->cyclicAncestor; }
}
}
}
}
// determines whether or not a vertex is a sink
bool CycleDetector::isSink(Node *node) {
// a vertex is a sink if it has no outgoing edges,
// or that the adj entry is empty
if (node->dests.empty()) { return true; }
else { return false; }
}
bool CycleDetector::find_node(std::vector<Node *> *& list, unsigned k) {
for (unsigned i = 0; i < this->V; i++) {
if ((*list)[i] != NULL) {
if ((*list)[i]->id == k) { return true; }
}
}
return false;
}
pair< bool, vector<unsigned>::iterator > CycleDetector::find_node(std::vector<unsigned>& list, unsigned k) {
for (vector<unsigned>::iterator ti = traverse.begin(); ti != traverse.end(); ++ti) {
if (*ti == k) { return pair< bool, vector<unsigned>::iterator >(true, ti); }
}
return pair< bool, vector<unsigned>::iterator >(false, traverse.end());
}