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/README.md b/README.md index f1be949..fcf646e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,27 @@ # cps222-2022-p5-opokunyarko-colbert -## Attempted: - * We attempted the following requirements: - * Reading and printing data - * Shortest paths - * The road upgrading goal - * Connected components analysis - * If bridges fail, isolated group is formed - -## Unattempted: - * The following requirements were unattempted: - * +### Attempted & Status + +- Requirement 1 : Read and print data + +Town and road data can be correctly read and outputed to the terminal. Passes all tests with no diff issues and correct styling. Peer programmed. + +- Requirement 2 : Shortest Paths + +findShortestPath() method implemented to calculate shortest traversals and output them to the terminal. Passes all tests with no diff issues and correct stylings. Completed by Elijah Opoku-Nyarko with later revisions by Jake Colbert. + +- Requirement 3 : Upgrading + +minSpan() method implemented to find ideal connections for faster traversals and output recomendations to the terminal. Passes all tests with no diff issues and correct stylings. Completed by Elijah Opoku-Nyarko with later revisions by Jake Colbert. + +- Requirement 4 : Worst-Case Analysis of Bridge Collapse + +removeBridges() method implemented to find towns that would form isolated groups on bridge collapse. Passes all tests with some diff issues regarding ordering and correct stylings. Completed by Elijah Opoku-Nyarko with output revisions by Jake Colbert. + +- Requirement 5 : Articulation Points + +articulationPoints() method implemented to find points of the graph at which their destruction would disconnect the province graph. Does not pass tests. isAP[] array does not get modified by the recursive method and prints all zeros as it was initialized. In-Progress by Jake Colbert + +### Makefile & Testing + +A `make test-all` target has been implemented to return "Passed!" if no errors are caught when running other tests. On run, "--- ---" will be displayed to indicate which test was running upon crash. Any test error will likely result from differences in the project5.out file and the respective test.out file. View test.diff to see their differences. 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 + ------------------------------------------------------------------ +