// -*- mode: C++; c-file-style: "stroustrup"; c-basic-offset: 4; -*-
////////////////////////////////////////////////////////////////////
//
// $Id: tester.cpp 844 2011-10-13 22:05:28Z Sebastian Kupferschmid $
//
////////////////////////////////////////////////////////////////////

#include "tester.h"
#include "graph.h"
#include "node.h"
#include "edge.h"

#include <queue>
#include <set>
#include <climits>
#include <iostream>
#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH

using namespace std;

void GraphTester::test(const Graph* graph) const {
    cout << "testing nodes ... ";
    cout.flush();
    if (testNodes(graph)) {
	cout << " passed" << endl;
    } else {
	cout << " failed" << endl;
    }

    cout << "testing connection ... ";
    cout.flush();
    if (testConnected(graph)) {
	cout << " passed" << endl;
    } else {
	cout << " failed" << endl;
    }
    
    cout << "testing distances ... ";
    cout.flush();
    if (testDistances(graph)) {
	cout << " passed" << endl;
    } else {
	cout << " failed" << endl;
    }    
}

bool GraphTester::testConnected(const Graph* graph) const {
    std::queue<const Node*> open;
    std::vector<bool> visited(graph->nodes.size(), false);
    
    open.push(graph->initial);    
    while (!open.empty()) {
	const Node* state = open.front();
	open.pop();

	if (visited[state->id]) {
	    continue;
	}
	visited[state->id] = true;	
	foreach (const Edge* edge, state->out) {
	    const Node* succ = edge->to;
	    if (!visited[succ->id]) {
		open.push(succ);
	    }
	}
    }
    for (int i = 0; i < visited.size(); i++) {
	if (!visited[i]) {
	    return false;
	}
    }
    return true;
}

class State {
public:
    const Node* node;
    const State* predecessor;

    State(const Node* node, const State* predecessor) : 
	node(node), predecessor(predecessor) {}
};

int GraphTester::bfs(const Graph* graph, const Node* initial) const {
    queue<State*> open;
    vector<State*> close(graph->nodes.size(), NULL);
    
    open.push(new State(initial, NULL));    
    while (!open.empty()) {
	State* state = open.front();
	open.pop();
	
	if (state->node->distance == 0) {
	    int length = 0;
	    const State* p = state->predecessor;
	    while (p) {
		p = p->predecessor;
		length++;
	    }
	    foreach (State* s, close) {
		delete s;
	    }
	    return length;
	}
	if (close[state->node->id]) {
	    continue;
	} else {
	    close[state->node->id] = state;
	}
	foreach (const Edge* edge, state->node->out) {
	    const Node* succ = edge->to;
	    if (close[succ->id] == NULL) {
		open.push(new State(succ, state));
	    }
	}
    }
    foreach (State* state, close) {
	delete state;
    }
    return INT_MAX;
}

bool GraphTester::testDistances(const Graph* graph) const {
    typedef std::pair<int, Node*> entry_t;
    std::set<int> errors;
    foreach (const Node* node, graph->targets) {
	errors.insert(node->id);
    }

    foreach (const entry_t& e, graph->nodes) {
	if (e.second->distance != bfs(graph, e.second)) {
	    return false;
	}
    }
    return true;
}

bool GraphTester::testNodes(const Graph* graph) const {
    typedef std::pair<int, Node*> entry_t;
    foreach (const entry_t& e, graph->nodes) {
	if (!testNode(e.second)) {
	    return false;
	}
    }
    return true;
}

bool GraphTester::testNode(const Node* node) const {
    if (node->distance == INT_MAX) {
	foreach (const Edge* edge, node->out) {
	    if (edge->to->distance != INT_MAX) {
		return false;
	    }
	}
	return true;
    } else if (node->distance != 0) {
	foreach (const Edge* edge, node->out) {
	    if (node->distance == edge->to->distance + 1) {
		return true;
	    }
	}
	return false;
    }    
    return true;
}




