diff --git a/Makefile b/Makefile index 936c3e0..cf4c998 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,16 @@ # Author: Elijah Opoku-Nyarko and Jake Colbert +# define colors +ifneq (,$(findstring xterm,${TERM})) + RED := $(shell tput -Txterm setaf 1) + GREEN := $(shell tput -Txterm setaf 2) + RESET := $(shell tput -Txterm sgr0) +else + RED := "" + GREEN := "" + RESET := "" +endif + # default target all: project5 @@ -7,9 +18,57 @@ all: project5 project5: project5.cc province.cc province.h g++ -std=c++11 -o project5 project5.cc province.cc -# test the code against an expected output file -# this test should grow into a spaceship and end with blinkers -test-requirement1: project5 +# test all the code +test-all: project5 test-nowhere test-one-road test-simple test-local test-combo + @echo "" + @echo "$(GREEN)Passed all tests!$(RESET)" + @echo "" + +test: project5 ./project5 < test-data/t0502-combo.in > test-data/project5.out diff test-data/project5.out test-data/shortest.out > test.diff - rm test-data/project5.out \ No newline at end of file + +# test the code against an expected output file +test-nowhere: project5 + @echo "" + @echo "$(RED)--- Nowhere Test ---$(RESET)" + @echo "" + ./project5 < test-data/t01-nowhere.in > test-data/project5.out + diff test-data/project5.out test-data/t01-nowhere.out > test.diff + @echo "$(GREEN)Passed!$(RESET)" + +# test the code against an expected output file +test-one-road: project5 + @echo "" + @echo "$(RED)--- One Road Test ---$(RESET)" + @echo "" + ./project5 < test-data/t02-one-road.in > test-data/project5.out + diff test-data/project5.out test-data/t02-one-road.out > test.diff + @echo "$(GREEN)Passed!$(RESET)" + +# test the code against an expected output file +test-simple: project5 + @echo "" + @echo "$(RED)--- Simple Test ---$(RESET)" + @echo "" + ./project5 < test-data/t03-simple.in > test-data/project5.out + diff test-data/project5.out test-data/t03-simple.out > test.diff + @echo "$(GREEN)Passed!$(RESET)" + +# test the code against an expected output file +test-local: project5 + @echo "" + @echo "$(RED)--- Local Test ---$(RESET)" + @echo "" + ./project5 < test-data/t08-local.in > test-data/project5.out + diff test-data/project5.out test-data/t08-local.out > test.diff + @echo "$(GREEN)Passed!$(RESET)" + +# test the code against an expected output file +test-combo: project5 + @echo "" + @echo "$(RED)--- Combo Test ---$(RESET)" + @echo "" + ./project5 < test-data/t0502-combo.in > test-data/project5.out + diff test-data/project5.out test-data/t0502-combo.out > test.diff + @echo "$(GREEN)Passed!$(RESET)" \ No newline at end of file diff --git a/project5 b/project5 index 68879e5..e81b6e3 100755 Binary files a/project5 and b/project5 differ diff --git a/project5.cc b/project5.cc index 286a86d..328077b 100644 --- a/project5.cc +++ b/project5.cc @@ -40,8 +40,10 @@ int main(int argc, char *argv[]) { theProvince.printAll(0, cout); theProvince.printShortestPath(std::cout); theProvince.minSpan(std::cout); + theProvince.removeBridges(cout); + theProvince.articulationPoints(cout); std::cout << std::endl; std::cout << "------------------------------------------------------------------"; - std::cout << endl; + cout << endl << endl; } } diff --git a/province.cc b/province.cc index 23f036d..35d4c33 100644 --- a/province.cc +++ b/province.cc @@ -12,7 +12,7 @@ using namespace std; -/*f +/* * Constructor * @param source File containing province: * 1. One line: number of towns (n), number of roads (p) @@ -32,48 +32,27 @@ Province::Province(std::istream & source) { // Read town names for (int i = 0; i < _numberOfTowns; i++) { - source >> _towns[i]._name; // This needs to be converted to vector, im not sure how exactly - //cout << "This is the name: " << &_towns[i]._name << endl; + source >> _towns[i]._name; townMap[_towns[i]._name] = i; } // Read roads for (int i = 0; i < _numberOfRoads; i++) { - std::string tail, head; - source >> tail >> head; - int tailIndex = townMap[tail]; // index of the first town - int headIndex = townMap[head]; // index of the second town - - // Get the type of road ("B" If Bridge, "N" if normal road) - char type; - source >> type; - bool isBridge = (type == 'B'); - // Not sure how to Get the type if it is a normal road (ie. Not a Bridge) - - // Length of road - double length; - source >> length; - - // Add a road to the road list - Road newRoad(headIndex, tailIndex, isBridge, length); - _roads.push_back(newRoad); - - // Add a road to two connecting towns - _towns[tailIndex]._roads.push_back(Road(headIndex, tailIndex, - isBridge, length)); - _towns[headIndex]._roads.push_back(Road(tailIndex, headIndex, - isBridge, length)); - - + string tail, head; + char bridgeFlag; + double length; + source >> tail >> head >> bridgeFlag >> length; + bool isBridge = (bridgeFlag == 'B'); + _roads.push_back(Road(townMap.at(head), townMap.at(tail), isBridge, length)); + _towns[townMap.at(tail)]._roads.push_back(Road(townMap.at(head), townMap.at(tail), isBridge, length)); + _towns[townMap.at(head)]._roads.push_back(Road(townMap.at(tail), townMap.at(head), isBridge, length)); } } void Province::printAll(int start, std::ostream & output) { // keep track of whether a town(vertex) has been scheduled to be visited bool scheduled[_numberOfTowns]; - for (int i = 0; i < _numberOfTowns; i++){ - scheduled[i] = false; - } + memset(scheduled, 0, sizeof scheduled); // initialize to 0/false // Keep track of which towns have been visited queue toVisit; // use queue to keep track of which town to visit next @@ -147,13 +126,6 @@ void Province::printShortestPath(std::ostream & output) const { output << "The shortest paths from " + _towns[0]._name; output << " are:" << std::endl << std::endl; - // keeps track of the index of the predecessor to each - // town(vertex) n on the shortest path to n - int prev[_numberOfTowns]; - - output << "The shortest routes from " + _towns[0]._name; - output << " are:" << std::endl << std::endl; - // keeps track of the index of the predecessor to each // town(vertex) n on the shortest path to n int prev[_numberOfTowns]; @@ -311,15 +283,18 @@ void Province::minSpan(std::ostream & output) const { } } - output << "The road upgrading goal can be achieved at minimal cost by upgrading:"; - output << std::endl; - - // Print names of towns in minimum spanning tree of province - for (int i = 0; i < minSpanTree.size(); i++) { - output << " "; - output << _towns[minSpanTree[i]._head]._name; - output << " to "; - output << _towns[minSpanTree[i]._tail]._name << std::endl; + output << "The road upgrading goal can be achieved at minimal cost by upgrading:"; + output << std::endl; + + // Print names of towns in minimum spanning tree of province + for (int i = 0; i < minSpanTree.size(); i++) { + output << " "; + output << _towns[minSpanTree[i]._head]._name; + output << " to "; + output << _towns[minSpanTree[i]._tail]._name << std::endl; + } + output << endl; +} std::vector Province::bfs(int start) const { // Initialize list of towns scheduled to visit @@ -374,26 +349,14 @@ void Province::removeBridges(ostream &output) const { break; } } - // If only one town - if (_numberOfTowns == 1) { - output << "There is only one town, so the province " - << "will not be affected by a major storm"; - return; - - // If province has no bridge - } else if (!hasBridge) { - output << "The province has no bridges, so it " - << "will not be affected by a major storm"; - return; - } // Mark all towns as unvisited list toVisit; for (int i = 0; i < _numberOfTowns; i++) { toVisit.push_back(i); } - output << "Connected components in event of a major storm are: "; - output << endl << endl; + output << "Connected components in event of a major storm are:"; + output << endl; while (!toVisit.empty()) { // Mark current town as visited @@ -408,16 +371,95 @@ void Province::removeBridges(ostream &output) const { toVisit.remove(bfsResult[i]); } - output << " "; + output << " "; output << "If all bridges fail, the following towns would form "; output << "an isolated group:" << endl; // Print names of all towns in connected component for (int i = 0; i < bfsResult.size(); i++) { - output << " "; + output << " "; output << _towns[bfsResult[i]]._name << endl; } + output << endl; } - } +void Province::APUtil(int u, bool visited[], + int disc[], int low[], int& time, int parent, + bool isAP[]) const { + // Count of children in DFS Tree + int children = 0; + + // Mark the current node as visited + visited[u] = true; + + // Initialize discovery time and low value + disc[u] = low[u] = ++time; + + // Go through all vertices adjacent to this + for (int v = 0; v < _numberOfTowns; v++) { + // If v is not visited yet, then make it a child of u + // in DFS tree and recur for it + if (!visited[v]) { + children++; + APUtil(v, visited, disc, low, time, u, isAP); + + // Check if the subtree rooted with v has + // a connection to one of the ancestors of u + low[u] = min(low[u], low[v]); + + // If u is not root and low value of one of + // its child is more than discovery value of u. + if (parent != -1 && low[v] >= disc[u]) { + isAP[u] = true; + } + } else if (v != parent) { + low[u] = min(low[u], disc[v]); + } + } + + // If u is root of DFS tree and has two or more children. + if (parent == -1 && children > 1) { + isAP[u] = true; + } +} + +void Province::articulationPoints(std::ostream & output) const +{ + int disc[_numberOfTowns]; + memset(disc, 0, sizeof disc); + int low[_numberOfTowns]; + bool visited[_numberOfTowns]; + memset(visited, false, sizeof visited); + bool isAP[_numberOfTowns]; + memset(isAP, false, sizeof isAP); + int time = 0, par = -1; + + // Adding this loop so that the + // code works even if we are given + // disconnected graph + for (int u = 0; u < _numberOfTowns; u++) { + if (!visited[u]) { + APUtil(u, visited, disc, low, + time, par, isAP); + } + } + output << "Destruction of any of the following would result in the province becoming" << endl << "disconnected:" << endl; + + int count = 0; + // Printing the APs + for (int u = 0; u < _numberOfTowns; u++) { + if (isAP[u] == true) { + output << " " << _towns[u]._name << endl; + count++; + } + } + if (count == 0) { + output << " (None)" << endl; + } + output << endl; + + for (bool i : isAP) { + cerr << i << endl; + } +} \ No newline at end of file diff --git a/province.h b/province.h index fcd773f..a936882 100644 --- a/province.h +++ b/province.h @@ -61,6 +61,10 @@ class Province * @return - List of indices of towns in order of traversal */ std::vector bfs(int start) const; + + void APUtil(int u, bool visited[], + int disc[], int low[], int& time, int parent, + bool isAP[]) const; void dfsAux(int current, std::vector & dfsTowns, bool visited []) const; diff --git a/test-data/project5.out b/test-data/project5.out index 92607ce..a0c981d 100644 --- a/test-data/project5.out +++ b/test-data/project5.out @@ -14,7 +14,18 @@ The shortest paths from Wenham are: The road upgrading goal can be achieved at minimal cost by upgrading: Hamilton to Wenham +Connected components in event of a major storm are: + If all bridges fail, the following towns would form an isolated group: + Hamilton + Wenham + +Destruction of any of the following would result in the province becoming +disconnected: + (None) + + ------------------------------------------------------------------ + The input data is: A @@ -39,4 +50,20 @@ The road upgrading goal can be achieved at minimal cost by upgrading: B to A C to B +Connected components in event of a major storm are: + If all bridges fail, the following towns would form an isolated group: + C + + If all bridges fail, the following towns would form an isolated group: + B + + If all bridges fail, the following towns would form an isolated group: + A + +Destruction of any of the following would result in the province becoming +disconnected: + (None) + + ------------------------------------------------------------------ + diff --git a/test-data/requirement1.out b/test-data/requirement1.out new file mode 100644 index 0000000..d5d431a --- /dev/null +++ b/test-data/requirement1.out @@ -0,0 +1,39 @@ + +------------------------------------------------ +---------------- New DataSet: ------------------ +------------------------------------------------ + +The input data is : + + Salem + Beverly 2.4 mi via bridge + Danvers 3.7 mi via bridge + Lynn 4.9 mi + Beverly + Danvers 2.9 mi + Salem 2.4 mi via bridge + Wenham 5.2 mi + Danvers + Beverly 2.9 mi + Wenham 4.2 mi + Salem 3.7 mi via bridge + Lynn + Salem 4.9 mi + Wenham + Beverly 5.2 mi + Danvers 4.2 mi + + + +------------------------------------------------ +---------------- New DataSet: ------------------ +------------------------------------------------ + +The input data is : + + Wenham + Hamilton 1 mi + Hamilton + Wenham 1 mi + + diff --git a/test-data/shortest.out b/test-data/shortest.out index d2366e4..dcfc038 100644 --- a/test-data/shortest.out +++ b/test-data/shortest.out @@ -14,6 +14,15 @@ The shortest paths from Wenham are: The road upgrading goal can be achieved at minimal cost by upgrading: Hamilton to Wenham +Connected components in event of a major storm are: + If all bridges fail, the following towns would form an isolated group: + Wenham + Hamilton + +Destruction of any of the following would result in the province becoming +disconnected: + (None) + ------------------------------------------------------------------ @@ -41,5 +50,16 @@ The road upgrading goal can be achieved at minimal cost by upgrading: B to A C to B +Connected components in event of a major storm are: + If all bridges fail, the following towns would form an isolated group: + A + + If all bridges fail, the following towns would form an isolated group: + B + + If all bridges fail, the following towns would form an isolated group: + C + ------------------------------------------------------------------ +