/**
 * A graph where the nodes are integers, and an implementation of the BFS algorithm
 *
 * Author: Erel Segal
 * Date: 2010-10-17
 */

#include <iostream>
#include <map>
#include <set>
#include <string>
#include <queue>
using namespace std;

typedef unsigned int uint;
typedef set<uint> AdjacencyList;
const uint MAXINT = 999999999u;

template <class Iterator> ostream& print (Iterator iFrom, Iterator iTo) {
	for (; iFrom!=iTo; ++iFrom) cout << *iFrom << " ";
	return (cout << endl);
}

template <class T> ostream& operator<< (ostream& out, const vector<T>& theVector) {
	return print (theVector.begin(), theVector.end());
}

template <class T> ostream& operator<< (ostream& out, const set<T>& theVector) {
	return print (theVector.begin(), theVector.end());
}



/**
 * A graph where the vertices are integers; stores edges in adjacency list.
 */
class IntegerGraph {
	vector<AdjacencyList> myVertices;

public:
	IntegerGraph (uint nVertices): myVertices(nVertices) {}
	uint size() const { return myVertices.size(); }

	IntegerGraph& addEdge (uint iFrom, uint iTo) {
		if (iFrom >= size()) {
			throw string("trying to add an edge from a nonexisting vertex index");
		}
		if (iTo >= size()) {
			throw string("trying to add an edge to a nonexisting vertex index");
		}
		myVertices[iFrom].insert(iTo);
		return *this;
	}

	void createClique() {     // for testing
		for (uint iFrom=0; iFrom<size(); ++iFrom) {
			for (uint iTo=0; iTo<size(); ++iTo) {
				if (iFrom!=iTo)
					addEdge(iFrom,iTo);
			}
		}
	}

	void printOutgoingEdges(uint iFrom, ostream& out) const {
		out << iFrom << " -> ";
		const AdjacencyList& targets = myVertices[iFrom];
		AdjacencyList::const_iterator f;
		for (f=targets.begin(); f!=targets.end(); ++f) {
			out << " " << (*f);
		}
		out << endl;
	}

	void printIncomingEdges(uint iTo, ostream& out) {
		out << iTo << " <- ";
		for (uint iFrom=0; iFrom<myVertices.size(); ++iFrom) {
			const AdjacencyList& currentTargets = myVertices[iFrom];
			if (currentTargets.count(iTo)) {
				out << iFrom << " ";
			}
		}
		out << endl;
	}

	// Print the graph, using the vertex indices
	void printIndices(ostream& out) const {
		for (uint i=0; i<myVertices.size(); ++i)
			out << i << " -> " << myVertices[i];
	}

	friend ostream& operator<< (ostream& out, const IntegerGraph& g) {
		g.printIndices(out);
		return out;
	}
	

	/// @see http://w...content-available-to-author-only...t.org/doc/libs/1_43_0/libs/graph/doc/breadth_first_search.html
	void breadth_first_search(uint source, vector<uint>& distances, vector<uint>& previous) const {
		typedef unsigned char color;
		const color white=0, gray=1, black=2;

		vector<color> vertexColor(size());
		distances.resize(size());
		previous.resize(size());
		queue<uint> vertexQueue;

		fill(vertexColor.begin(), vertexColor.end(), white);
		fill(distances.begin(), distances.end(), MAXINT);
		fill(previous.begin(), previous.end(), MAXINT);

		// Initlaize source:
		vertexColor[source] = gray;
		distances[source] = 0;
		previous[source] = MAXINT;
		vertexQueue.push(source);

		// Loop over all vertices:
		while (!vertexQueue.empty()) {
			uint current = vertexQueue.front();     // finish a vertex
			vertexQueue.pop();
			uint current_distance = distances[current];

			// Loop over all outgoing edges of the current vertex:
			const AdjacencyList& nextTargets = myVertices[current];
			AdjacencyList::const_iterator iNeighbour;
			for (iNeighbour=nextTargets.begin(); iNeighbour!=nextTargets.end(); ++iNeighbour) {
				uint neighbour = *iNeighbour;
				if (vertexColor[neighbour]==white) {   // a tree edge
					vertexColor[neighbour] = gray;
					distances[neighbour] = current_distance+1;
					previous[neighbour] = current;
					vertexQueue.push(neighbour);
				}
			}
			vertexColor[current] = black;   // finish a vertex
		}
	}
	
	
	void print_bfs_from(uint iFrom) {
		vector<uint> distances, previous;
		breadth_first_search(iFrom, distances, previous);
		cout << "BFS from " << iFrom << ": " << endl << "distances: " << distances << "previouses: " << previous << endl;
	}
}; // end of class IntegerGraph


int main() {
	IntegerGraph g(5);
	g.addEdge(0,1).addEdge(1,2).addEdge(2,3).addEdge(3,0).addEdge(0,2);
	cout << g << endl;
	g.printOutgoingEdges(0,cout);
	g.printOutgoingEdges(1,cout);
	g.printOutgoingEdges(2,cout);
	g.printOutgoingEdges(3,cout);
	g.printOutgoingEdges(4,cout);
	cout << endl;
	g.printIncomingEdges(0,cout);
	g.printIncomingEdges(1,cout);
	g.printIncomingEdges(2,cout);
	g.printIncomingEdges(3,cout);
	g.printIncomingEdges(4,cout);
	cout << endl;

	g.print_bfs_from(0);
	g.print_bfs_from(1);
	g.print_bfs_from(2);
	g.print_bfs_from(3);
	g.print_bfs_from(4);
}
