From 3c172cfb60aa3367e1deba1f87a88b6af6f29a1e Mon Sep 17 00:00:00 2001 From: sbaldu Date: Fri, 28 Apr 2023 22:59:30 +0200 Subject: [PATCH 1/6] Implement method for writing graph to DOT file --- include/Graph/Graph.hpp | 61 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/include/Graph/Graph.hpp b/include/Graph/Graph.hpp index b0a0e980c..ad685b37f 100755 --- a/include/Graph/Graph.hpp +++ b/include/Graph/Graph.hpp @@ -89,10 +89,15 @@ class Graph { const std::string &OFileName, bool compress, bool writeNodeFeat, bool writeEdgeWeight, InputOutputFormat format) const; + int writeToDotFile(const std::string &workingDir, + const std::string &OFileName, + const std::string &graphName); int readFromStandardFile(const std::string &workingDir, const std::string &OFileName, bool compress, bool readNodeFeat, bool readEdgeWeight, InputOutputFormat format); + int readFromDotFile(const std::string &workingDir, + const std::string &fileName); void recreateGraphFromReadFiles( std::unordered_map> &edgeMap, @@ -742,14 +747,53 @@ int Graph::writeToStandardFile(const std::string &workingDir, return 0; } +template +int Graph::writeToDotFile(const std::string &workingDir, + const std::string &OFileName, + const std::string &graphName) { + const std::string linkSymbol = "--"; + const std::string directedLinkSymbol = "->"; + + const std::string completePathToFileGraph = workingDir + '/' + OFileName + ".dot"; + std::ofstream ofileGraph; + ofileGraph.open(completePathToFileGraph); + if (!ofileGraph.is_open()) { + // ERROR File Not Open + return -1; + } + + for(auto const& edgeIt : edgeSet) { + std::string edgeLine = ""; + if (edgeIt->isDirected()) { + edgeLine += "digraph " + graphName + " {"; + edgeLine += '\t' + std::to_string(edgeIt->getFrom().getUserId()) + ' '; + edgeLine += directedLinkSymbol + ' '; + edgeLine += std::to_string(edgeIt->getTo().getUserId()); + } else { + edgeLine += '\t' + std::to_string(edgeIt->getNode1().getUserId()) + ' '; + edgeLine += linkSymbol + ' '; + edgeLine += std::to_string(edgeIt->getNode2().getUserId()); + } + if (edgeIt->isWeighted()) { + // Weights in dot files must be integers + edgeLine += " [weight" + std::to_string(static_cast(edgeIt->getWeight())) + ']'; + } + edgeLine += ";\n"; + ofileGraph << edgeLine; + } + ofileGraph << '}'; + ofileGraph.close(); + + return 0; +} + // This ctype facet classifies ',' and '\t' as whitespace struct csv_whitespace : std::ctype { static const mask *make_table() { // make a copy of the "C" locale table static std::vector v(classic_table(), classic_table() + table_size); v[','] |= space; // comma will be classified as whitespace - v['\t'] |= space; - v[' '] &= ~space; // space will not be classified as whitespace + v['\t'] |= space; v[' '] &= ~space; // space will not be classified as whitespace return &v[0]; } csv_whitespace(std::size_t refs = 0) : ctype(make_table(), false, refs) {} @@ -846,6 +890,19 @@ int Graph::readFromStandardFile(const std::string &workingDir, return 0; } +template +int Graph::readFromDotFile(const std::string &workingDir, const std::string &fileName) { + std::string node1; + std::string node2; + std::string link; + + std::ifstream iFile(workingDir + fileName); + while (getline(iFile, node1, ' ')) { + getline(iFile, link, ' '); + getline(iFile, node2, ';'); + } +} + template void Graph::recreateGraphFromReadFiles( std::unordered_map> From 1bd4fa347fc0e91023dbca3f536a1ce35de0d289 Mon Sep 17 00:00:00 2001 From: sbaldu Date: Wed, 3 May 2023 18:09:17 +0200 Subject: [PATCH 2/6] Implement method for reading graphs from DOT files --- include/Graph/Graph.hpp | 3445 ++++++++++++++++++++------------------- 1 file changed, 1785 insertions(+), 1660 deletions(-) mode change 100755 => 100644 include/Graph/Graph.hpp diff --git a/include/Graph/Graph.hpp b/include/Graph/Graph.hpp old mode 100755 new mode 100644 index ad685b37f..e953062bc --- a/include/Graph/Graph.hpp +++ b/include/Graph/Graph.hpp @@ -751,40 +751,42 @@ template int Graph::writeToDotFile(const std::string &workingDir, const std::string &OFileName, const std::string &graphName) { - const std::string linkSymbol = "--"; - const std::string directedLinkSymbol = "->"; - - const std::string completePathToFileGraph = workingDir + '/' + OFileName + ".dot"; - std::ofstream ofileGraph; - ofileGraph.open(completePathToFileGraph); - if (!ofileGraph.is_open()) { - // ERROR File Not Open - return -1; - } + const std::string linkSymbol = "--"; + const std::string directedLinkSymbol = "->"; - for(auto const& edgeIt : edgeSet) { - std::string edgeLine = ""; - if (edgeIt->isDirected()) { - edgeLine += "digraph " + graphName + " {"; - edgeLine += '\t' + std::to_string(edgeIt->getFrom().getUserId()) + ' '; - edgeLine += directedLinkSymbol + ' '; - edgeLine += std::to_string(edgeIt->getTo().getUserId()); - } else { - edgeLine += '\t' + std::to_string(edgeIt->getNode1().getUserId()) + ' '; - edgeLine += linkSymbol + ' '; - edgeLine += std::to_string(edgeIt->getNode2().getUserId()); - } - if (edgeIt->isWeighted()) { - // Weights in dot files must be integers - edgeLine += " [weight" + std::to_string(static_cast(edgeIt->getWeight())) + ']'; - } - edgeLine += ";\n"; - ofileGraph << edgeLine; + const std::string completePathToFileGraph = + workingDir + '/' + OFileName + ".dot"; + std::ofstream ofileGraph; + ofileGraph.open(completePathToFileGraph); + if (!ofileGraph.is_open()) { + // ERROR File Not Open + return -1; + } + + for (auto const &edgeIt : edgeSet) { + std::string edgeLine = ""; + if (edgeIt->isDirected()) { + edgeLine += "digraph " + graphName + " {"; + edgeLine += '\t' + std::to_string(edgeIt->getFrom().getUserId()) + ' '; + edgeLine += directedLinkSymbol + ' '; + edgeLine += std::to_string(edgeIt->getTo().getUserId()); + } else { + edgeLine += '\t' + std::to_string(edgeIt->getNode1().getUserId()) + ' '; + edgeLine += linkSymbol + ' '; + edgeLine += std::to_string(edgeIt->getNode2().getUserId()); } - ofileGraph << '}'; - ofileGraph.close(); + if (edgeIt->isWeighted()) { + // Weights in dot files must be integers + edgeLine += " [weight" + + std::to_string(static_cast(edgeIt->getWeight())) + ']'; + } + edgeLine += ";\n"; + ofileGraph << edgeLine; + } + ofileGraph << '}'; + ofileGraph.close(); - return 0; + return 0; } // This ctype facet classifies ',' and '\t' as whitespace @@ -793,7 +795,8 @@ struct csv_whitespace : std::ctype { // make a copy of the "C" locale table static std::vector v(classic_table(), classic_table() + table_size); v[','] |= space; // comma will be classified as whitespace - v['\t'] |= space; v[' '] &= ~space; // space will not be classified as whitespace + v['\t'] |= space; + v[' '] &= ~space; // space will not be classified as whitespace return &v[0]; } csv_whitespace(std::size_t refs = 0) : ctype(make_table(), false, refs) {} @@ -891,15 +894,76 @@ int Graph::readFromStandardFile(const std::string &workingDir, } template -int Graph::readFromDotFile(const std::string &workingDir, const std::string &fileName) { - std::string node1; - std::string node2; - std::string link; - - std::ifstream iFile(workingDir + fileName); - while (getline(iFile, node1, ' ')) { - getline(iFile, link, ' '); - getline(iFile, node2, ';'); +int Graph::readFromDotFile(const std::string &workingDir, + const std::string &fileName) { + // Define the edge maps + std::unordered_map> + edgeMap; + std::unordered_map edgeDirectedMap; + std::unordered_map edgeWeightMap; + + // Define the node strings and the "garbage collector" temp string + std::string node1; + std::string node2; + std::string temp; + + // Get full path to the file and open it + std::string completePathToFileGraph = workingDir + '/' + fileName ".dot"; + std::ifstream iFile(completePathToFileGraph); + + // Check if the graph is directed + bool directed = false; + std::ifstream fileContentStream(completePathToFileGraph); + std::string fileContent(std::istreambuf_iterator{fileContentStream}, + {}); + const std::string directedSeparator = "->"; + if (content.find(directedSeparator) { + directed = true; + } + // Check if the graph is weighted + bool weighted = false; + if (content.find("weight")) { + directed = true; + } + + // Write the header of the DOT file in the temp string + getline(iFile, temp); + + unsigned long long edgeId = 0; + std::string fileRow; + while (getline(iFile, fileRow)) { + // If you've reached the end of the file, stop + if (fileRow == "}") { + break; + } + + // Remove the whitespaces before the definition of the edge + while (*fileRow.begin() == ' ') { + fileRow.erase(fileRow.begin()); + } + + std::stringstream row_stream(fileRow); + getline(row_stream, node1, ' '); + // Store the symbol representing the edge inside temp + getline(row_stream, temp, ' '); + if (weighted) { + getline(row_stream, node2, ' '); + + getline(row_stream, temp, '='); + std::string weight; + getline(row_stream, weight, ']'); + // Erase any whitespaces + weight.erase(std::remove(weight.begin(), weight.end(), ' '), + weight.end()); + edgeWeightMap[edgeId] = std::stod(weight); + } else { + getline(row_stream, node2, ';'); + } + + // Save the edge and increment the edge counter + edgeMap[edgeId] = std::pair(node1, node2); + edgeDirectedMap[edgeId] = directed; + ++edgeId; } } @@ -910,1694 +974,1745 @@ void Graph::recreateGraphFromReadFiles( std::unordered_map &edgeDirectedMap, std::unordered_map &nodeFeatMap, std::unordered_map &edgeWeightMap) { - std::unordered_map *> nodeMap; - for (const auto &edgeIt : edgeMap) { - Node *node1 = nullptr; - Node *node2 = nullptr; - if (nodeMap.find(edgeIt.second.first) == nodeMap.end()) { - // Create new Node - T feat; - if (nodeFeatMap.find(edgeIt.second.first) != nodeFeatMap.end()) { - feat = nodeFeatMap.at(edgeIt.second.first); - } - node1 = new Node(edgeIt.second.first, feat); - nodeMap[edgeIt.second.first] = node1; - } else { - node1 = nodeMap.at(edgeIt.second.first); - } - if (nodeMap.find(edgeIt.second.second) == nodeMap.end()) { - // Create new Node - T feat; - if (nodeFeatMap.find(edgeIt.second.second) != nodeFeatMap.end()) { - feat = nodeFeatMap.at(edgeIt.second.second); - } - node2 = new Node(edgeIt.second.second, feat); - nodeMap[edgeIt.second.second] = node2; - } else { - node2 = nodeMap.at(edgeIt.second.second); - } + std::unordered_map *> nodeMap; + for (const auto &edgeIt : edgeMap) { + Node *node1 = nullptr; + Node *node2 = nullptr; + if (nodeMap.find(edgeIt.second.first) == nodeMap.end()) { + // Create new Node + T feat; + if (nodeFeatMap.find(edgeIt.second.first) != nodeFeatMap.end()) { + feat = nodeFeatMap.at(edgeIt.second.first); + } + node1 = new Node(edgeIt.second.first, feat); + nodeMap[edgeIt.second.first] = node1; + } else { + node1 = nodeMap.at(edgeIt.second.first); + } + if (nodeMap.find(edgeIt.second.second) == nodeMap.end()) { + // Create new Node + T feat; + if (nodeFeatMap.find(edgeIt.second.second) != nodeFeatMap.end()) { + feat = nodeFeatMap.at(edgeIt.second.second); + } + node2 = new Node(edgeIt.second.second, feat); + nodeMap[edgeIt.second.second] = node2; + } else { + node2 = nodeMap.at(edgeIt.second.second); + } - if (edgeWeightMap.find(edgeIt.first) != edgeWeightMap.end()) { - if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && - edgeDirectedMap.at(edgeIt.first)) { - auto edge = new DirectedWeightedEdge(edgeIt.first, *node1, *node2, - edgeWeightMap.at(edgeIt.first)); - addEdge(edge); - } else { - auto edge = new UndirectedWeightedEdge( - edgeIt.first, *node1, *node2, edgeWeightMap.at(edgeIt.first)); - addEdge(edge); - } - } else { - if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && - edgeDirectedMap.at(edgeIt.first)) { - auto edge = new DirectedEdge(edgeIt.first, *node1, *node2); - addEdge(edge); - } else { - auto edge = new UndirectedEdge(edgeIt.first, *node1, *node2); - addEdge(edge); - } - } - } + if (edgeWeightMap.find(edgeIt.first) != edgeWeightMap.end()) { + if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && + edgeDirectedMap.at(edgeIt.first)) { + auto edge = new DirectedWeightedEdge( + edgeIt.first, *node1, *node2, edgeWeightMap.at(edgeIt.first)); + addEdge(edge); + } else { + auto edge = new UndirectedWeightedEdge( + edgeIt.first, *node1, *node2, edgeWeightMap.at(edgeIt.first)); + addEdge(edge); + } + } else { + if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && + edgeDirectedMap.at(edgeIt.first)) { + auto edge = new DirectedEdge(edgeIt.first, *node1, *node2); + addEdge(edge); + } else { + auto edge = new UndirectedEdge(edgeIt.first, *node1, *node2); + addEdge(edge); + } + } + } } template int Graph::compressFile(const std::string &inputFile, const std::string &outputFile) const { - std::ifstream ifs; - ifs.open(inputFile); - if (!ifs.is_open()) { - // ERROR File Not Open - return -1; - } - std::string content((std::istreambuf_iterator(ifs)), - (std::istreambuf_iterator())); - - const char *content_ptr = content.c_str(); - gzFile outFileZ = gzopen(outputFile.c_str(), "wb"); - if (outFileZ == NULL) { - // printf("Error: Failed to gzopen %s\n", outputFile.c_str()); - return -1; - } + std::ifstream ifs; + ifs.open(inputFile); + if (!ifs.is_open()) { + // ERROR File Not Open + return -1; + } + std::string content((std::istreambuf_iterator(ifs)), + (std::istreambuf_iterator())); + + const char *content_ptr = content.c_str(); + gzFile outFileZ = gzopen(outputFile.c_str(), "wb"); + if (outFileZ == NULL) { + // printf("Error: Failed to gzopen %s\n", outputFile.c_str()); + return -1; + } - unsigned int zippedBytes; - zippedBytes = gzwrite(outFileZ, content_ptr, strlen(content_ptr)); + unsigned int zippedBytes; + zippedBytes = gzwrite(outFileZ, content_ptr, strlen(content_ptr)); - ifs.close(); - gzclose(outFileZ); - return 0; + ifs.close(); + gzclose(outFileZ); + return 0; } template int Graph::decompressFile(const std::string &inputFile, const std::string &outputFile) const { - gzFile inFileZ = gzopen(inputFile.c_str(), "rb"); - if (inFileZ == NULL) { - // printf("Error: Failed to gzopen %s\n", inputFile.c_str()); - return -1; - } - unsigned char unzipBuffer[8192]; - unsigned int unzippedBytes; - std::vector unzippedData; - std::ofstream ofs; - ofs.open(outputFile); - if (!ofs.is_open()) { - // ERROR File Not Open - return -1; - } - while (true) { - unzippedBytes = gzread(inFileZ, unzipBuffer, 8192); - if (unzippedBytes > 0) { - unzippedData.insert(unzippedData.end(), unzipBuffer, - unzipBuffer + unzippedBytes); - } else { - break; - } - } - for (const auto &c : unzippedData) { - ofs << c; - } - ofs << std::endl; - ofs.close(); - gzclose(inFileZ); - return 0; + gzFile inFileZ = gzopen(inputFile.c_str(), "rb"); + if (inFileZ == NULL) { + // printf("Error: Failed to gzopen %s\n", inputFile.c_str()); + return -1; + } + unsigned char unzipBuffer[8192]; + unsigned int unzippedBytes; + std::vector unzippedData; + std::ofstream ofs; + ofs.open(outputFile); + if (!ofs.is_open()) { + // ERROR File Not Open + return -1; + } + while (true) { + unzippedBytes = gzread(inFileZ, unzipBuffer, 8192); + if (unzippedBytes > 0) { + unzippedData.insert(unzippedData.end(), unzipBuffer, + unzipBuffer + unzippedBytes); + } else { + break; + } + } + for (const auto &c : unzippedData) { + ofs << c; + } + ofs << std::endl; + ofs.close(); + gzclose(inFileZ); + return 0; } template unsigned long long Graph::setFind( std::unordered_map *subsets, const unsigned long long nodeId) const { - // find root and make root as parent of i - // (path compression) - if ((*subsets)[nodeId].parent != nodeId) { - (*subsets)[nodeId].parent = - Graph::setFind(subsets, (*subsets)[nodeId].parent); - } + // find root and make root as parent of i + // (path compression) + if ((*subsets)[nodeId].parent != nodeId) { + (*subsets)[nodeId].parent = + Graph::setFind(subsets, (*subsets)[nodeId].parent); + } - return (*subsets)[nodeId].parent; + return (*subsets)[nodeId].parent; } template void Graph::setUnion(std::unordered_map *subsets, const unsigned long long elem1, const unsigned long long elem2) const { - // if both sets have same parent - // then there's nothing to be done - if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; - auto elem1Parent = Graph::setFind(subsets, elem1); - auto elem2Parent = Graph::setFind(subsets, elem2); - if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) - (*subsets)[elem1].parent = elem2Parent; - else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) - (*subsets)[elem2].parent = elem1Parent; - else { - (*subsets)[elem2].parent = elem1Parent; - (*subsets)[elem1Parent].rank++; - } + // if both sets have same parent + // then there's nothing to be done + if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; + auto elem1Parent = Graph::setFind(subsets, elem1); + auto elem2Parent = Graph::setFind(subsets, elem2); + if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) + (*subsets)[elem1].parent = elem2Parent; + else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) + (*subsets)[elem2].parent = elem1Parent; + else { + (*subsets)[elem2].parent = elem1Parent; + (*subsets)[elem1Parent].rank++; + } } template std::shared_ptr>> Graph::eulerianPath() const { - const auto nodeSet = Graph::getNodeSet(); - const auto adj = Graph::getAdjMatrix(); - std::shared_ptr>> eulerPath = - std::make_shared>>(); - std::vector *> currentPath; - auto currentNode = *(nodeSet.begin()); - currentPath.push_back(currentNode); - while (currentPath.size() > 0) { - auto &edges = adj->at(currentNode); - // we keep removing the edges that - // have been traversed from the adjacency list - if (edges.size()) { - auto firstEdge = edges.back().second; - auto nextNodeId = firstEdge->getNodePair().second; - currentPath.push_back(nextNodeId); - currentNode = nextNodeId; - edges.pop_back(); - } else { - eulerPath->push_back(*currentNode); - currentNode = currentPath.back(); - currentPath.pop_back(); - } - } - return eulerPath; + const auto nodeSet = Graph::getNodeSet(); + const auto adj = Graph::getAdjMatrix(); + std::shared_ptr>> eulerPath = + std::make_shared>>(); + std::vector *> currentPath; + auto currentNode = *(nodeSet.begin()); + currentPath.push_back(currentNode); + while (currentPath.size() > 0) { + auto &edges = adj->at(currentNode); + // we keep removing the edges that + // have been traversed from the adjacency list + if (edges.size()) { + auto firstEdge = edges.back().second; + auto nextNodeId = firstEdge->getNodePair().second; + currentPath.push_back(nextNodeId); + currentNode = nextNodeId; + edges.pop_back(); + } else { + eulerPath->push_back(*currentNode); + currentNode = currentPath.back(); + currentPath.pop_back(); + } + } + return eulerPath; } template const std::shared_ptr> Graph::getAdjMatrix() const { - auto adj = std::make_shared>(); - auto addElementToAdjMatrix = [&adj](const Node *nodeFrom, - const Node *nodeTo, - const Edge *edge) { - std::pair *, const Edge *> elem = {nodeTo, edge}; - (*adj)[nodeFrom].push_back(std::move(elem)); - }; - for (const auto &edgeSetIt : edgeSet) { - if (edgeSetIt->isDirected().has_value() && - edgeSetIt->isDirected().value()) { - const DirectedEdge *d_edge = - dynamic_cast *>(edgeSetIt); - addElementToAdjMatrix(&(d_edge->getFrom()), &(d_edge->getTo()), d_edge); - } else if (edgeSetIt->isDirected().has_value() && - !edgeSetIt->isDirected().value()) { - const UndirectedEdge *ud_edge = - dynamic_cast *>(edgeSetIt); - ; - addElementToAdjMatrix(&(ud_edge->getNode1()), &(ud_edge->getNode2()), - ud_edge); - addElementToAdjMatrix(&(ud_edge->getNode2()), &(ud_edge->getNode1()), - ud_edge); - } else { // is a simple edge we cannot create adj matrix - return adj; - } - } - return adj; + auto adj = std::make_shared>(); + auto addElementToAdjMatrix = [&adj](const Node *nodeFrom, + const Node *nodeTo, + const Edge *edge) { + std::pair *, const Edge *> elem = {nodeTo, edge}; + (*adj)[nodeFrom].push_back(std::move(elem)); + }; + for (const auto &edgeSetIt : edgeSet) { + if (edgeSetIt->isDirected().has_value() && + edgeSetIt->isDirected().value()) { + const DirectedEdge *d_edge = + dynamic_cast *>(edgeSetIt); + addElementToAdjMatrix(&(d_edge->getFrom()), &(d_edge->getTo()), + d_edge); + } else if (edgeSetIt->isDirected().has_value() && + !edgeSetIt->isDirected().value()) { + const UndirectedEdge *ud_edge = + dynamic_cast *>(edgeSetIt); + ; + addElementToAdjMatrix(&(ud_edge->getNode1()), + &(ud_edge->getNode2()), ud_edge); + addElementToAdjMatrix(&(ud_edge->getNode2()), + &(ud_edge->getNode1()), ud_edge); + } else { // is a simple edge we cannot create adj matrix + return adj; + } + } + return adj; } template const DijkstraResult Graph::dijkstra(const Node &source, const Node &target) const { - DijkstraResult result; - auto nodeSet = Graph::getNodeSet(); - if (std::find(nodeSet.begin(), nodeSet.end(), &source) == nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - if (std::find(nodeSet.begin(), nodeSet.end(), &target) == nodeSet.end()) { - // check if target node exist in the graph - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } - const auto adj = getAdjMatrix(); - // n denotes the number of vertices in graph - int n = adj->size(); + DijkstraResult result; + auto nodeSet = Graph::getNodeSet(); + if (std::find(nodeSet.begin(), nodeSet.end(), &source) == + nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + if (std::find(nodeSet.begin(), nodeSet.end(), &target) == + nodeSet.end()) { + // check if target node exist in the graph + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + const auto adj = getAdjMatrix(); + // n denotes the number of vertices in graph + int n = adj->size(); - // setting all the distances initially to INF_DOUBLE - std::unordered_map *, double> dist; + // setting all the distances initially to INF_DOUBLE + std::unordered_map *, double> dist; - for (const auto &node : nodeSet) { - dist[node] = INF_DOUBLE; - } + for (const auto &node : nodeSet) { + dist[node] = INF_DOUBLE; + } - // creating a min heap using priority queue - // first element of pair contains the distance - // second element of pair contains the vertex - std::priority_queue *>, - std::vector *>>, - std::greater *>>> - pq; - - // pushing the source vertex 's' with 0 distance in min heap - pq.push(std::make_pair(0.0, &source)); - - // marking the distance of source as 0 - dist[&source] = 0; - - while (!pq.empty()) { - // second element of pair denotes the node / vertex - const Node *currentNode = pq.top().second; - - // first element of pair denotes the distance - double currentDist = pq.top().first; - - pq.pop(); - - // for all the reachable vertex from the currently exploring vertex - // we will try to minimize the distance - if (adj->find(currentNode) != adj->end()) { - for (const auto &elem : adj->at(currentNode)) { - // minimizing distances - if (elem.second->isWeighted().has_value() && - elem.second->isWeighted().value()) { - if (elem.second->isDirected().has_value() && - elem.second->isDirected().value()) { - const DirectedWeightedEdge *dw_edge = - dynamic_cast *>(elem.second); - if (dw_edge->getWeight() < 0) { - result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; - return result; - } else if (currentDist + dw_edge->getWeight() < dist[elem.first]) { - dist[elem.first] = currentDist + dw_edge->getWeight(); - pq.push(std::make_pair(dist[elem.first], elem.first)); - } - } else if (elem.second->isDirected().has_value() && - !elem.second->isDirected().value()) { - const UndirectedWeightedEdge *udw_edge = - dynamic_cast *>(elem.second); - if (udw_edge->getWeight() < 0) { - result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; - return result; - } else if (currentDist + udw_edge->getWeight() < dist[elem.first]) { - dist[elem.first] = currentDist + udw_edge->getWeight(); - pq.push(std::make_pair(dist[elem.first], elem.first)); + // creating a min heap using priority queue + // first element of pair contains the distance + // second element of pair contains the vertex + std::priority_queue *>, + std::vector *>>, + std::greater *>>> + pq; + + // pushing the source vertex 's' with 0 distance in min heap + pq.push(std::make_pair(0.0, &source)); + + // marking the distance of source as 0 + dist[&source] = 0; + + while (!pq.empty()) { + // second element of pair denotes the node / vertex + const Node *currentNode = pq.top().second; + + // first element of pair denotes the distance + double currentDist = pq.top().first; + + pq.pop(); + + // for all the reachable vertex from the currently exploring vertex + // we will try to minimize the distance + if (adj->find(currentNode) != adj->end()) { + for (const auto &elem : adj->at(currentNode)) { + // minimizing distances + if (elem.second->isWeighted().has_value() && + elem.second->isWeighted().value()) { + if (elem.second->isDirected().has_value() && + elem.second->isDirected().value()) { + const DirectedWeightedEdge *dw_edge = + dynamic_cast *>( + elem.second); + if (dw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } else if (currentDist + dw_edge->getWeight() < + dist[elem.first]) { + dist[elem.first] = currentDist + dw_edge->getWeight(); + pq.push(std::make_pair(dist[elem.first], elem.first)); + } + } else if (elem.second->isDirected().has_value() && + !elem.second->isDirected().value()) { + const UndirectedWeightedEdge *udw_edge = + dynamic_cast *>( + elem.second); + if (udw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } else if (currentDist + udw_edge->getWeight() < + dist[elem.first]) { + dist[elem.first] = currentDist + udw_edge->getWeight(); + pq.push(std::make_pair(dist[elem.first], elem.first)); + } + } else { + // ERROR it shouldn't never returned ( does not exist a Node + // Weighted and not Directed/Undirected) + result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; + return result; + } + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } } - } else { - // ERROR it shouldn't never returned ( does not exist a Node - // Weighted and not Directed/Undirected) - result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; - return result; } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; + } + if (dist[&target] != INF_DOUBLE) { + result.success = true; + result.errorMessage = ""; + result.result = dist[&target]; return result; } - } - } - } - if (dist[&target] != INF_DOUBLE) { - result.success = true; - result.errorMessage = ""; - result.result = dist[&target]; - return result; - } - result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; - return result; + result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; + return result; } template const BellmanFordResult Graph::bellmanford(const Node &source, const Node &target) const { - BellmanFordResult result; - result.success = false; - result.errorMessage = ""; - result.result = INF_DOUBLE; - auto nodeSet = Graph::getNodeSet(); - if (std::find(nodeSet.begin(), nodeSet.end(), &source) == nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - if (std::find(nodeSet.begin(), nodeSet.end(), &target) == nodeSet.end()) { - // check if target node exist in the graph - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } - // setting all the distances initially to INF_DOUBLE - std::unordered_map *, double> dist, currentDist; - // n denotes the number of vertices in graph - auto n = nodeSet.size(); - for (const auto &elem : nodeSet) { - dist[elem] = INF_DOUBLE; - currentDist[elem] = INF_DOUBLE; - } + BellmanFordResult result; + result.success = false; + result.errorMessage = ""; + result.result = INF_DOUBLE; + auto nodeSet = Graph::getNodeSet(); + if (std::find(nodeSet.begin(), nodeSet.end(), &source) == + nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + if (std::find(nodeSet.begin(), nodeSet.end(), &target) == + nodeSet.end()) { + // check if target node exist in the graph + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + // setting all the distances initially to INF_DOUBLE + std::unordered_map *, double> dist, currentDist; + // n denotes the number of vertices in graph + auto n = nodeSet.size(); + for (const auto &elem : nodeSet) { + dist[elem] = INF_DOUBLE; + currentDist[elem] = INF_DOUBLE; + } - // marking the distance of source as 0 - dist[&source] = 0; - // set if node distances in two consecutive - // iterations remain the same. - auto earlyStopping = false; - // outer loop for vertex relaxation - for (int i = 0; i < n - 1; ++i) { - auto edgeSet = Graph::getEdgeSet(); - // inner loop for distance updates of - // each relaxation - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto edge_weight = (dynamic_cast(edge))->getWeight(); - if (dist[elem.first] + edge_weight < dist[elem.second]) - dist[elem.second] = dist[elem.first] + edge_weight; - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - auto flag = true; - for (const auto &[key, value] : dist) { - if (currentDist[key] != value) { - flag = false; - break; - } - } - for (const auto &[key, value] : dist) { - currentDist[key] = value; // update the current distance - } - if (flag) { - earlyStopping = true; - break; - } - } + // marking the distance of source as 0 + dist[&source] = 0; + // set if node distances in two consecutive + // iterations remain the same. + auto earlyStopping = false; + // outer loop for vertex relaxation + for (int i = 0; i < n - 1; ++i) { + auto edgeSet = Graph::getEdgeSet(); + // inner loop for distance updates of + // each relaxation + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto edge_weight = + (dynamic_cast(edge))->getWeight(); + if (dist[elem.first] + edge_weight < dist[elem.second]) + dist[elem.second] = dist[elem.first] + edge_weight; + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + auto flag = true; + for (const auto &[key, value] : dist) { + if (currentDist[key] != value) { + flag = false; + break; + } + } + for (const auto &[key, value] : dist) { + currentDist[key] = value; // update the current distance + } + if (flag) { + earlyStopping = true; + break; + } + } - // check if there exists a negative cycle - if (!earlyStopping) { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - auto edge_weight = (dynamic_cast(edge))->getWeight(); - if (dist[elem.first] + edge_weight < dist[elem.second]) { - result.success = true; - result.negativeCycle = true; - result.errorMessage = ""; - return result; - } - } - } + // check if there exists a negative cycle + if (!earlyStopping) { + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + auto edge_weight = + (dynamic_cast(edge))->getWeight(); + if (dist[elem.first] + edge_weight < dist[elem.second]) { + result.success = true; + result.negativeCycle = true; + result.errorMessage = ""; + return result; + } + } + } - if (dist[&target] != INF_DOUBLE) { - result.success = true; - result.errorMessage = ""; - result.negativeCycle = false; - result.result = dist[&target]; - return result; - } - result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; - result.result = -1; - return result; + if (dist[&target] != INF_DOUBLE) { + result.success = true; + result.errorMessage = ""; + result.negativeCycle = false; + result.result = dist[&target]; + return result; + } + result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; + result.result = -1; + return result; } template const FWResult Graph::floydWarshall() const { - FWResult result; - result.success = false; - result.errorMessage = ""; - std::unordered_map, double, - CXXGRAPH::pair_hash> - pairwise_dist; - const auto &nodeSet = Graph::getNodeSet(); - // create a pairwise distance matrix with distance node distances - // set to inf. Distance of node to itself is set as 0. - for (const auto &elem1 : nodeSet) { - for (const auto &elem2 : nodeSet) { - auto key = std::make_pair(elem1->getUserId(), elem2->getUserId()); - if (elem1 != elem2) - pairwise_dist[key] = INF_DOUBLE; - else - pairwise_dist[key] = 0.0; - } - } + FWResult result; + result.success = false; + result.errorMessage = ""; + std::unordered_map, double, + CXXGRAPH::pair_hash> + pairwise_dist; + const auto &nodeSet = Graph::getNodeSet(); + // create a pairwise distance matrix with distance node distances + // set to inf. Distance of node to itself is set as 0. + for (const auto &elem1 : nodeSet) { + for (const auto &elem2 : nodeSet) { + auto key = std::make_pair(elem1->getUserId(), elem2->getUserId()); + if (elem1 != elem2) + pairwise_dist[key] = INF_DOUBLE; + else + pairwise_dist[key] = 0.0; + } + } - const auto &edgeSet = Graph::getEdgeSet(); - // update the weights of nodes - // connected by edges - for (const auto &edge : edgeSet) { - const auto &elem = edge->getNodePair(); - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto edgeWeight = (dynamic_cast(edge))->getWeight(); - auto key = - std::make_pair(elem.first->getUserId(), elem.second->getUserId()); - pairwise_dist[key] = edgeWeight; - } else { - // if an edge exists but has no weight associated - // with it, we return an error message - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } + const auto &edgeSet = Graph::getEdgeSet(); + // update the weights of nodes + // connected by edges + for (const auto &edge : edgeSet) { + const auto &elem = edge->getNodePair(); + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto edgeWeight = + (dynamic_cast(edge))->getWeight(); + auto key = std::make_pair(elem.first->getUserId(), + elem.second->getUserId()); + pairwise_dist[key] = edgeWeight; + } else { + // if an edge exists but has no weight associated + // with it, we return an error message + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } - for (const auto &k : nodeSet) { - // set all vertices as source one by one - for (const auto &src : nodeSet) { - // iterate through all vertices as destination for the - // current source - auto src_k = std::make_pair(src->getUserId(), k->getUserId()); - for (const auto &dst : nodeSet) { - // If vertex k provides a shorter path than - // src to dst, update the value of - // pairwise_dist[src_to_dst] - auto src_dst = std::make_pair(src->getUserId(), dst->getUserId()); - auto k_dst = std::make_pair(k->getUserId(), dst->getUserId()); - if (pairwise_dist[src_dst] > - (pairwise_dist[src_k] + pairwise_dist[k_dst]) && - (pairwise_dist[k_dst] != INF_DOUBLE && - pairwise_dist[src_k] != INF_DOUBLE)) - pairwise_dist[src_dst] = pairwise_dist[src_k] + pairwise_dist[k_dst]; - } - } - } + for (const auto &k : nodeSet) { + // set all vertices as source one by one + for (const auto &src : nodeSet) { + // iterate through all vertices as destination for the + // current source + auto src_k = std::make_pair(src->getUserId(), k->getUserId()); + for (const auto &dst : nodeSet) { + // If vertex k provides a shorter path than + // src to dst, update the value of + // pairwise_dist[src_to_dst] + auto src_dst = std::make_pair(src->getUserId(), dst->getUserId()); + auto k_dst = std::make_pair(k->getUserId(), dst->getUserId()); + if (pairwise_dist[src_dst] > + (pairwise_dist[src_k] + pairwise_dist[k_dst]) && + (pairwise_dist[k_dst] != INF_DOUBLE && + pairwise_dist[src_k] != INF_DOUBLE)) + pairwise_dist[src_dst] = + pairwise_dist[src_k] + pairwise_dist[k_dst]; + } + } + } - result.success = true; - // presense of negative number in the diagonal indicates - // that that the graph contains a negative cycle - for (const auto &node : nodeSet) { - auto diag = std::make_pair(node->getUserId(), node->getUserId()); - if (pairwise_dist[diag] < 0.) { - result.negativeCycle = true; - return result; - } - } - result.result = std::move(pairwise_dist); - return result; + result.success = true; + // presense of negative number in the diagonal indicates + // that that the graph contains a negative cycle + for (const auto &node : nodeSet) { + auto diag = std::make_pair(node->getUserId(), node->getUserId()); + if (pairwise_dist[diag] < 0.) { + result.negativeCycle = true; + return result; + } + } + result.result = std::move(pairwise_dist); + return result; } template const MstResult Graph::prim() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - if (!isConnectedGraph()) { - result.errorMessage = ERR_NOT_STRONG_CONNECTED; - return result; - } - auto nodeSet = Graph::getNodeSet(); - auto n = nodeSet.size(); - const auto adj = Graph::getAdjMatrix(); - - // setting all the distances initially to INF_DOUBLE - std::unordered_map *, double> dist; - for (const auto &elem : (*adj)) { - dist[elem.first] = INF_DOUBLE; - } + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + if (!isConnectedGraph()) { + result.errorMessage = ERR_NOT_STRONG_CONNECTED; + return result; + } + auto nodeSet = Graph::getNodeSet(); + auto n = nodeSet.size(); + const auto adj = Graph::getAdjMatrix(); + + // setting all the distances initially to INF_DOUBLE + std::unordered_map *, double> dist; + for (const auto &elem : (*adj)) { + dist[elem.first] = INF_DOUBLE; + } - // creating a min heap using priority queue - // first element of pair contains the distance - // second element of pair contains the vertex - std::priority_queue *>, - std::vector *>>, - std::greater *>>> - pq; - - // pushing the source vertex 's' with 0 distance in min heap - auto source = *(nodeSet.begin()); - pq.push(std::make_pair(0.0, source)); - result.mstCost = 0; - std::vector doneNode; - // mark source node as done - // otherwise we get (0, 0) also in mst - doneNode.push_back(source->getId()); - // stores the parent and corresponding child node - // of the edges that are part of MST - std::unordered_map parentNode; - while (!pq.empty()) { - // second element of pair denotes the node / vertex - const Node *currentNode = pq.top().second; - auto nodeId = currentNode->getId(); - if (std::find(doneNode.begin(), doneNode.end(), nodeId) == doneNode.end()) { - auto pair = std::make_pair(parentNode[nodeId], currentNode->getUserId()); - result.mst.push_back(pair); - result.mstCost += pq.top().first; - doneNode.push_back(nodeId); - } + // creating a min heap using priority queue + // first element of pair contains the distance + // second element of pair contains the vertex + std::priority_queue *>, + std::vector *>>, + std::greater *>>> + pq; + + // pushing the source vertex 's' with 0 distance in min heap + auto source = *(nodeSet.begin()); + pq.push(std::make_pair(0.0, source)); + result.mstCost = 0; + std::vector doneNode; + // mark source node as done + // otherwise we get (0, 0) also in mst + doneNode.push_back(source->getId()); + // stores the parent and corresponding child node + // of the edges that are part of MST + std::unordered_map parentNode; + while (!pq.empty()) { + // second element of pair denotes the node / vertex + const Node *currentNode = pq.top().second; + auto nodeId = currentNode->getId(); + if (std::find(doneNode.begin(), doneNode.end(), nodeId) == + doneNode.end()) { + auto pair = + std::make_pair(parentNode[nodeId], currentNode->getUserId()); + result.mst.push_back(pair); + result.mstCost += pq.top().first; + doneNode.push_back(nodeId); + } - pq.pop(); - // for all the reachable vertex from the currently exploring vertex - // we will try to minimize the distance - if (adj->find(currentNode) != adj->end()) { - for (const auto &elem : adj->at(currentNode)) { - // minimizing distances - if (elem.second->isWeighted().has_value() && - elem.second->isWeighted().value()) { - const UndirectedWeightedEdge *udw_edge = - dynamic_cast *>(elem.second); - if ((udw_edge->getWeight() < dist[elem.first]) && - (std::find(doneNode.begin(), doneNode.end(), - elem.first->getId()) == doneNode.end())) { - dist[elem.first] = udw_edge->getWeight(); - parentNode[elem.first->getId()] = currentNode->getUserId(); - pq.push(std::make_pair(dist[elem.first], elem.first)); + pq.pop(); + // for all the reachable vertex from the currently exploring vertex + // we will try to minimize the distance + if (adj->find(currentNode) != adj->end()) { + for (const auto &elem : adj->at(currentNode)) { + // minimizing distances + if (elem.second->isWeighted().has_value() && + elem.second->isWeighted().value()) { + const UndirectedWeightedEdge *udw_edge = + dynamic_cast *>( + elem.second); + if ((udw_edge->getWeight() < dist[elem.first]) && + (std::find(doneNode.begin(), doneNode.end(), + elem.first->getId()) == doneNode.end())) { + dist[elem.first] = udw_edge->getWeight(); + parentNode[elem.first->getId()] = currentNode->getUserId(); + pq.push(std::make_pair(dist[elem.first], elem.first)); + } + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; } - } - } - } - result.success = true; - return result; + result.success = true; + return result; } template const MstResult Graph::boruvka() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - const auto nodeSet = Graph::getNodeSet(); - const auto n = nodeSet.size(); - - // Use std map for storing n subsets. - std::unordered_map subsets; - - // Initially there are n different trees. - // Finally there will be one tree that will be MST - int numTrees = n; - - // check if all edges are weighted and store the weights - // in a map whose keys are the edge ids and values are the edge weights - const auto edgeSet = Graph::getEdgeSet(); - std::unordered_map edgeWeight; - for (const auto &edge : edgeSet) { - if (edge->isWeighted().has_value() && edge->isWeighted().value()) - edgeWeight[edge->getId()] = - (dynamic_cast(edge))->getWeight(); - else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - - for (const auto &node : nodeSet) { - Subset set{node->getId(), 0}; - subsets[node->getId()] = set; - } + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + const auto nodeSet = Graph::getNodeSet(); + const auto n = nodeSet.size(); + + // Use std map for storing n subsets. + std::unordered_map subsets; + + // Initially there are n different trees. + // Finally there will be one tree that will be MST + int numTrees = n; + + // check if all edges are weighted and store the weights + // in a map whose keys are the edge ids and values are the edge weights + const auto edgeSet = Graph::getEdgeSet(); + std::unordered_map edgeWeight; + for (const auto &edge : edgeSet) { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) + edgeWeight[edge->getId()] = + (dynamic_cast(edge))->getWeight(); + else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } - result.mstCost = 0; // we will store the cost here - // exit when only 1 tree i.e. mst - while (numTrees > 1) { - // Everytime initialize cheapest map - // It stores index of the cheapest edge of subset. - std::unordered_map cheapest; - for (const auto &node : nodeSet) cheapest[node->getId()] = INT_MAX; + for (const auto &node : nodeSet) { + Subset set{node->getId(), 0}; + subsets[node->getId()] = set; + } - // Traverse through all edges and update - // cheapest of every component - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - auto edgeId = edge->getId(); - // Find sets of two corners of current edge - auto set1 = Graph::setFind(&subsets, elem.first->getId()); - auto set2 = Graph::setFind(&subsets, elem.second->getId()); - - // If two corners of current edge belong to - // same set, ignore current edge - if (set1 == set2) continue; - - // Else check if current edge is closer to previous - // cheapest edges of set1 and set2 - if (cheapest[set1] == INT_MAX || - edgeWeight[cheapest[set1]] > edgeWeight[edgeId]) - cheapest[set1] = edgeId; - - if (cheapest[set2] == INT_MAX || - edgeWeight[cheapest[set2]] > edgeWeight[edgeId]) - cheapest[set2] = edgeId; - } + result.mstCost = 0; // we will store the cost here + // exit when only 1 tree i.e. mst + while (numTrees > 1) { + // Everytime initialize cheapest map + // It stores index of the cheapest edge of subset. + std::unordered_map cheapest; + for (const auto &node : nodeSet) cheapest[node->getId()] = INT_MAX; + + // Traverse through all edges and update + // cheapest of every component + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + auto edgeId = edge->getId(); + // Find sets of two corners of current edge + auto set1 = Graph::setFind(&subsets, elem.first->getId()); + auto set2 = Graph::setFind(&subsets, elem.second->getId()); + + // If two corners of current edge belong to + // same set, ignore current edge + if (set1 == set2) continue; + + // Else check if current edge is closer to previous + // cheapest edges of set1 and set2 + if (cheapest[set1] == INT_MAX || + edgeWeight[cheapest[set1]] > edgeWeight[edgeId]) + cheapest[set1] = edgeId; + + if (cheapest[set2] == INT_MAX || + edgeWeight[cheapest[set2]] > edgeWeight[edgeId]) + cheapest[set2] = edgeId; + } - // iterate over all the vertices and add picked - // cheapest edges to MST - for (const auto &[nodeId, edgeId] : cheapest) { - // Check if cheapest for current set exists - if (edgeId != INT_MAX) { - auto cheapestNode = Graph::getEdge(edgeId).value()->getNodePair(); - auto set1 = Graph::setFind(&subsets, cheapestNode.first->getId()); - auto set2 = Graph::setFind(&subsets, cheapestNode.second->getId()); - if (set1 == set2) continue; - result.mstCost += edgeWeight[edgeId]; - auto newEdgeMST = std::make_pair(cheapestNode.first->getUserId(), - cheapestNode.second->getUserId()); - result.mst.push_back(newEdgeMST); - // take union of set1 and set2 and decrease number of trees - Graph::setUnion(&subsets, set1, set2); - numTrees--; - } - } - } - result.success = true; - return result; + // iterate over all the vertices and add picked + // cheapest edges to MST + for (const auto &[nodeId, edgeId] : cheapest) { + // Check if cheapest for current set exists + if (edgeId != INT_MAX) { + auto cheapestNode = + Graph::getEdge(edgeId).value()->getNodePair(); + auto set1 = + Graph::setFind(&subsets, cheapestNode.first->getId()); + auto set2 = + Graph::setFind(&subsets, cheapestNode.second->getId()); + if (set1 == set2) continue; + result.mstCost += edgeWeight[edgeId]; + auto newEdgeMST = + std::make_pair(cheapestNode.first->getUserId(), + cheapestNode.second->getUserId()); + result.mst.push_back(newEdgeMST); + // take union of set1 and set2 and decrease number of trees + Graph::setUnion(&subsets, set1, set2); + numTrees--; + } + } + } + result.success = true; + return result; } template const MstResult Graph::kruskal() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - auto nodeSet = Graph::getNodeSet(); - auto n = nodeSet.size(); - - // check if all edges are weighted and store the weights - // in a map whose keys are the edge ids and values are the edge weights - auto edgeSet = Graph::getEdgeSet(); - std::priority_queue *>, - std::vector *>>, - std::greater *>>> - sortedEdges; - for (const auto &edge : edgeSet) { - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto weight = (dynamic_cast(edge))->getWeight(); - sortedEdges.push(std::make_pair(weight, edge)); - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + auto nodeSet = Graph::getNodeSet(); + auto n = nodeSet.size(); + + // check if all edges are weighted and store the weights + // in a map whose keys are the edge ids and values are the edge weights + auto edgeSet = Graph::getEdgeSet(); + std::priority_queue *>, + std::vector *>>, + std::greater *>>> + sortedEdges; + for (const auto &edge : edgeSet) { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto weight = (dynamic_cast(edge))->getWeight(); + sortedEdges.push(std::make_pair(weight, edge)); + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } - std::unordered_map subset; + std::unordered_map subset; - for (const auto &node : nodeSet) { - Subset set{node->getId(), 0}; - subset[node->getId()] = set; - } - result.mstCost = 0; - while ((!sortedEdges.empty()) && (result.mst.size() < n)) { - auto [edgeWeight, cheapestEdge] = sortedEdges.top(); - sortedEdges.pop(); - auto &[first, second] = cheapestEdge->getNodePair(); - auto set1 = Graph::setFind(&subset, first->getId()); - auto set2 = Graph::setFind(&subset, second->getId()); - if (set1 != set2) { - result.mst.push_back( - std::make_pair(first->getUserId(), second->getUserId())); - result.mstCost += edgeWeight; - } - Graph::setUnion(&subset, set1, set2); - } - result.success = true; - return result; -} + for (const auto &node : nodeSet) { + Subset set{node->getId(), 0}; + subset[node->getId()] = set; + } + result.mstCost = 0; + while ((!sortedEdges.empty()) && (result.mst.size() < n)) { + auto [edgeWeight, cheapestEdge] = sortedEdges.top(); + sortedEdges.pop(); + auto &[first, second] = cheapestEdge->getNodePair(); + auto set1 = Graph::setFind(&subset, first->getId()); + auto set2 = Graph::setFind(&subset, second->getId()); + if (set1 != set2) { + result.mst.push_back( + std::make_pair(first->getUserId(), second->getUserId())); + result.mstCost += edgeWeight; + } + Graph::setUnion(&subset, set1, set2); + } + result.success = true; + return result; +} template BestFirstSearchResult Graph::best_first_search( const Node &source, const Node &target) const { - BestFirstSearchResult result; - auto &nodeSet = Graph::getNodeSet(); - using pq_type = std::pair *>; + BestFirstSearchResult result; + auto &nodeSet = Graph::getNodeSet(); + using pq_type = std::pair *>; - if (std::find(nodeSet.begin(), nodeSet.end(), &source) == nodeSet.end()) { - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } + if (std::find(nodeSet.begin(), nodeSet.end(), &source) == + nodeSet.end()) { + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } - if (std::find(nodeSet.begin(), nodeSet.end(), &target) == nodeSet.end()) { - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } + if (std::find(nodeSet.begin(), nodeSet.end(), &target) == + nodeSet.end()) { + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } - auto adj = Graph::getAdjMatrix(); - std::priority_queue, std::greater> pq; + auto adj = Graph::getAdjMatrix(); + std::priority_queue, + std::greater> + pq; - std::vector> visited; - visited.push_back(source); - pq.push(std::make_pair(static_cast(0), &source)); + std::vector> visited; + visited.push_back(source); + pq.push(std::make_pair(static_cast(0), &source)); - while (!pq.empty()) { - const Node *currentNode = pq.top().second; - pq.pop(); - result.nodesInBestSearchOrder.push_back(*currentNode); + while (!pq.empty()) { + const Node *currentNode = pq.top().second; + pq.pop(); + result.nodesInBestSearchOrder.push_back(*currentNode); - if (*currentNode == target) { - break; - } - if (adj->find(currentNode) != adj->end()) { - for (const auto &elem : adj->at(currentNode)) { - if (elem.second->isWeighted().has_value()) { - if (elem.second->isDirected().has_value()) { - const DirectedWeightedEdge *dw_edge = - static_cast *>(elem.second); - if (std::find(visited.begin(), visited.end(), *(elem.first)) == - visited.end()) { - visited.push_back(*(elem.first)); - pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); - } - } else { - const UndirectedWeightedEdge *dw_edge = - static_cast *>(elem.second); - if (std::find(visited.begin(), visited.end(), *(elem.first)) == - visited.end()) { - visited.push_back(*(elem.first)); - pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); + if (*currentNode == target) { + break; + } + if (adj->find(currentNode) != adj->end()) { + for (const auto &elem : adj->at(currentNode)) { + if (elem.second->isWeighted().has_value()) { + if (elem.second->isDirected().has_value()) { + const DirectedWeightedEdge *dw_edge = + static_cast *>(elem.second); + if (std::find(visited.begin(), visited.end(), + *(elem.first)) == visited.end()) { + visited.push_back(*(elem.first)); + pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); + } + } else { + const UndirectedWeightedEdge *dw_edge = + static_cast *>( + elem.second); + if (std::find(visited.begin(), visited.end(), + *(elem.first)) == visited.end()) { + visited.push_back(*(elem.first)); + pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); + } + } + } else { + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + result.nodesInBestSearchOrder.clear(); + return result; + } } } - } else { - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - result.nodesInBestSearchOrder.clear(); - return result; } - } - } - } - result.success = true; - return result; + result.success = true; + return result; } template const std::vector> Graph::breadth_first_search( const Node &start) const { - // vector to keep track of visited nodes - std::vector> visited; - auto &nodeSet = Graph::getNodeSet(); - // check is exist node in the graph - if (std::find(nodeSet.begin(), nodeSet.end(), &start) == nodeSet.end()) { - return visited; - } - const auto adj = Graph::getAdjMatrix(); - // queue that stores vertices that need to be further explored - std::queue *> tracker; - - // mark the starting node as visited - visited.push_back(start); - tracker.push(&start); - while (!tracker.empty()) { - const Node *node = tracker.front(); - tracker.pop(); - if (adj->find(node) != adj->end()) { - for (const auto &elem : adj->at(node)) { - // if the node is not visited then mark it as visited - // and push it to the queue - if (std::find(visited.begin(), visited.end(), *(elem.first)) == - visited.end()) { - visited.push_back(*(elem.first)); - tracker.push(elem.first); + // vector to keep track of visited nodes + std::vector> visited; + auto &nodeSet = Graph::getNodeSet(); + // check is exist node in the graph + if (std::find(nodeSet.begin(), nodeSet.end(), &start) == + nodeSet.end()) { + return visited; + } + const auto adj = Graph::getAdjMatrix(); + // queue that stores vertices that need to be further explored + std::queue *> tracker; + + // mark the starting node as visited + visited.push_back(start); + tracker.push(&start); + while (!tracker.empty()) { + const Node *node = tracker.front(); + tracker.pop(); + if (adj->find(node) != adj->end()) { + for (const auto &elem : adj->at(node)) { + // if the node is not visited then mark it as visited + // and push it to the queue + if (std::find(visited.begin(), visited.end(), *(elem.first)) == + visited.end()) { + visited.push_back(*(elem.first)); + tracker.push(elem.first); + } + } + } } - } - } - } - return visited; + return visited; } template const std::vector> Graph::concurrency_breadth_first_search( const Node &start, size_t num_threads) const { - std::vector> bfs_result; - // check is exist node in the graph - auto &nodeSet = Graph::getNodeSet(); - if (std::find(nodeSet.begin(), nodeSet.end(), &start) == nodeSet.end()) { - return bfs_result; - } + std::vector> bfs_result; + // check is exist node in the graph + auto &nodeSet = Graph::getNodeSet(); + if (std::find(nodeSet.begin(), nodeSet.end(), &start) == + nodeSet.end()) { + return bfs_result; + } - std::unordered_map *, int> node_to_index; - for (const auto &node : nodeSet) { - node_to_index[node] = node_to_index.size(); - } - std::vector visited(nodeSet.size(), 0); + std::unordered_map *, int> node_to_index; + for (const auto &node : nodeSet) { + node_to_index[node] = node_to_index.size(); + } + std::vector visited(nodeSet.size(), 0); - // parameter limitations - if (num_threads <= 0) { - std::cout << "Error: number of threads should be greater than 0" - << std::endl; - num_threads = 2; - } + // parameter limitations + if (num_threads <= 0) { + std::cout << "Error: number of threads should be greater than 0" + << std::endl; + num_threads = 2; + } - const auto &adj = Graph::getAdjMatrix(); - // vector that stores vertices to be visit - std::vector *> level_tracker, next_level_tracker; - level_tracker.reserve(static_cast(1.0 * nodeSet.size())); - next_level_tracker.reserve(static_cast(1.0 * nodeSet.size())); - - // mark the starting node as visited - visited[node_to_index[&start]] = 1; - level_tracker.push_back(&start); - - // a worker is assigned a small part of tasks for each time - // assignments of tasks in current level and updates of tasks in next level - // are inclusive - std::mutex tracker_mutex; - std::mutex next_tracker_mutex; - std::atomic assigned_tasks = 0; - int num_tasks = 1; - // unit of task assignment, which mean assign block_size tasks to a worker - // each time - int block_size = 1; - int level = 1; - - auto extract_tasks = [&level_tracker, &tracker_mutex, &assigned_tasks, - &num_tasks, &block_size]() -> std::pair { - /* - std::lock_guard tracker_guard(tracker_mutex); - int task_block_size = std::min(num_tasks - assigned_tasks, block_size); - std::pair task_block{assigned_tasks, assigned_tasks + - task_block_size}; assigned_tasks += task_block_size; return task_block; - */ - int start = assigned_tasks.fetch_add(block_size); - int end = std::min(num_tasks, start + block_size); - return {start, end}; - }; - - auto submit_result = [&next_level_tracker, &next_tracker_mutex]( - std::vector *> &submission) -> void { - std::lock_guard tracker_guard(next_tracker_mutex); - next_level_tracker.insert(std::end(next_level_tracker), - std::begin(submission), std::end(submission)); - }; - - // worker thread sleep until it begin to search nodes of next level - std::mutex next_level_mutex; - std::condition_variable next_level_cond; - std::atomic waiting_workers = 0; - - auto bfs_worker = [&]() -> void { - // algorithm is not done - while (!level_tracker.empty()) { - // search for nodes in a level is not done - std::vector *> local_tracker; - while (1) { - auto [start_index, end_index] = extract_tasks(); - if (start_index >= end_index) { - break; - } - - for (int i = start_index; i < end_index; ++i) { - if (adj->count(level_tracker[i])) { - for (const auto &elem : adj->at(level_tracker[i])) { - int index = node_to_index[elem.first]; - if (visited[index] == 0) { - visited[index] = 1; - local_tracker.push_back(elem.first); + const auto &adj = Graph::getAdjMatrix(); + // vector that stores vertices to be visit + std::vector *> level_tracker, next_level_tracker; + level_tracker.reserve(static_cast(1.0 * nodeSet.size())); + next_level_tracker.reserve(static_cast(1.0 * nodeSet.size())); + + // mark the starting node as visited + visited[node_to_index[&start]] = 1; + level_tracker.push_back(&start); + + // a worker is assigned a small part of tasks for each time + // assignments of tasks in current level and updates of tasks in next + // level are inclusive + std::mutex tracker_mutex; + std::mutex next_tracker_mutex; + std::atomic assigned_tasks = 0; + int num_tasks = 1; + // unit of task assignment, which mean assign block_size tasks to a + // worker each time + int block_size = 1; + int level = 1; + + auto extract_tasks = [&level_tracker, &tracker_mutex, &assigned_tasks, + &num_tasks, + &block_size]() -> std::pair { + /* + std::lock_guard tracker_guard(tracker_mutex); + int task_block_size = std::min(num_tasks - assigned_tasks, + block_size); std::pair task_block{assigned_tasks, + assigned_tasks + task_block_size}; assigned_tasks += task_block_size; + return task_block; + */ + int start = assigned_tasks.fetch_add(block_size); + int end = std::min(num_tasks, start + block_size); + return {start, end}; + }; + + auto submit_result = + [&next_level_tracker, &next_tracker_mutex]( + std::vector *> &submission) -> void { + std::lock_guard tracker_guard(next_tracker_mutex); + next_level_tracker.insert(std::end(next_level_tracker), + std::begin(submission), + std::end(submission)); + }; + + // worker thread sleep until it begin to search nodes of next level + std::mutex next_level_mutex; + std::condition_variable next_level_cond; + std::atomic waiting_workers = 0; + + auto bfs_worker = [&]() -> void { + // algorithm is not done + while (!level_tracker.empty()) { + // search for nodes in a level is not done + std::vector *> local_tracker; + while (1) { + auto [start_index, end_index] = extract_tasks(); + if (start_index >= end_index) { + break; + } + + for (int i = start_index; i < end_index; ++i) { + if (adj->count(level_tracker[i])) { + for (const auto &elem : adj->at(level_tracker[i])) { + int index = node_to_index[elem.first]; + if (visited[index] == 0) { + visited[index] = 1; + local_tracker.push_back(elem.first); + } + } + } } } - } - } - } - // submit local result to global result - if (!local_tracker.empty()) { - submit_result(local_tracker); - } + // submit local result to global result + if (!local_tracker.empty()) { + submit_result(local_tracker); + } - // last worker need to do preparation for the next iteration - int cur_level = level; - if (num_threads == 1 + waiting_workers.fetch_add(1)) { - swap(level_tracker, next_level_tracker); - next_level_tracker.clear(); - - // adjust block_size according to number of nodes in next level - block_size = 4; - if (level_tracker.size() <= num_threads * 4) { - block_size = std::max( - 1, static_cast(std::ceil( - static_cast(level_tracker.size()) / num_threads))); - } else if (level_tracker.size() >= num_threads * 64) { - block_size = 16; - } - - num_tasks = level_tracker.size(); - waiting_workers = 0; - assigned_tasks = 0; - level = level + 1; - next_level_cond.notify_all(); - } else { - // not to wait if last worker reachs last statement before notify all or - // even further - std::unique_lock next_level_lock(next_level_mutex); - next_level_cond.wait(next_level_lock, [&level, cur_level]() { - return level != cur_level; - }); - } - } - }; + // last worker need to do preparation for the next iteration + int cur_level = level; + if (num_threads == 1 + waiting_workers.fetch_add(1)) { + swap(level_tracker, next_level_tracker); + next_level_tracker.clear(); + + // adjust block_size according to number of nodes in next level + block_size = 4; + if (level_tracker.size() <= num_threads * 4) { + block_size = std::max( + 1, static_cast( + std::ceil(static_cast(level_tracker.size()) / + num_threads))); + } else if (level_tracker.size() >= num_threads * 64) { + block_size = 16; + } - std::vector workers; - for (int i = 0; i < num_threads - 1; ++i) { - workers.emplace_back(std::thread(bfs_worker)); - } - bfs_worker(); + num_tasks = level_tracker.size(); + waiting_workers = 0; + assigned_tasks = 0; + level = level + 1; + next_level_cond.notify_all(); + } else { + // not to wait if last worker reachs last statement before notify + // all or even further + std::unique_lock next_level_lock(next_level_mutex); + next_level_cond.wait(next_level_lock, [&level, cur_level]() { + return level != cur_level; + }); + } + } + }; - for (auto &worker : workers) { - if (worker.joinable()) { - worker.join(); - } - } + std::vector workers; + for (int i = 0; i < num_threads - 1; ++i) { + workers.emplace_back(std::thread(bfs_worker)); + } + bfs_worker(); - for (const auto &visited_node : nodeSet) { - if (visited[node_to_index[visited_node]] != 0) { - bfs_result.push_back(*visited_node); - } - } + for (auto &worker : workers) { + if (worker.joinable()) { + worker.join(); + } + } + + for (const auto &visited_node : nodeSet) { + if (visited[node_to_index[visited_node]] != 0) { + bfs_result.push_back(*visited_node); + } + } - return bfs_result; + return bfs_result; } template const std::vector> Graph::depth_first_search( const Node &start) const { - // vector to keep track of visited nodes - std::vector> visited; - auto nodeSet = Graph::getNodeSet(); - // check is exist node in the graph - if (std::find(nodeSet.begin(), nodeSet.end(), &start) == nodeSet.end()) { - return visited; - } - const auto adj = Graph::getAdjMatrix(); - std::function>, const Node &, - std::vector> &)> - explore; - explore = [&explore](const std::shared_ptr> adj, - const Node &node, - std::vector> &visited) -> void { - visited.push_back(node); - if (adj->find(&node) != adj->end()) { - for (const auto &x : adj->at(&node)) { - if (std::find(visited.begin(), visited.end(), *(x.first)) == - visited.end()) { - explore(adj, *(x.first), visited); + // vector to keep track of visited nodes + std::vector> visited; + auto nodeSet = Graph::getNodeSet(); + // check is exist node in the graph + if (std::find(nodeSet.begin(), nodeSet.end(), &start) == + nodeSet.end()) { + return visited; } - } - } - }; - explore(adj, start, visited); + const auto adj = Graph::getAdjMatrix(); + std::function>, + const Node &, std::vector> &)> + explore; + explore = [&explore](const std::shared_ptr> adj, + const Node &node, + std::vector> &visited) -> void { + visited.push_back(node); + if (adj->find(&node) != adj->end()) { + for (const auto &x : adj->at(&node)) { + if (std::find(visited.begin(), visited.end(), *(x.first)) == + visited.end()) { + explore(adj, *(x.first), visited); + } + } + } + }; + explore(adj, start, visited); - return visited; + return visited; } template bool Graph::isCyclicDirectedGraphDFS() const { - if (!isDirectedGraph()) { - return false; - } - enum nodeStates : uint8_t { not_visited, in_stack, visited }; - auto nodeSet = Graph::getNodeSet(); - auto adjMatrix = Graph::getAdjMatrix(); - - /* State of the node. - * - * It is a vector of "nodeStates" which represents the state node is in. - * It can take only 3 values: "not_visited", "in_stack", and "visited". - * - * Initially, all nodes are in "not_visited" state. - */ - std::unordered_map state; - for (const auto &node : nodeSet) { - state[node->getId()] = not_visited; - } - int stateCounter = 0; - - // Start visiting each node. - for (const auto &node : nodeSet) { - // If a node is not visited, only then check for presence of cycle. - // There is no need to check for presence of cycle for a visited - // node as it has already been checked for presence of cycle. - if (state[node->getId()] == not_visited) { - // Check for cycle. - std::function>, - std::unordered_map &, - const Node *)> - isCyclicDFSHelper; - isCyclicDFSHelper = - [this, &isCyclicDFSHelper]( - const std::shared_ptr> adjMatrix, - std::unordered_map &states, - const Node *node) { - // Add node "in_stack" state. - states[node->getId()] = in_stack; - - // If the node has children, then recursively visit all children of - // the node. - auto const it = adjMatrix->find(node); - if (it != adjMatrix->end()) { - for (const auto &child : it->second) { - // If state of child node is "not_visited", evaluate that child - // for presence of cycle. - auto state_of_child = states.at((std::get<0>(child))->getId()); - if (state_of_child == not_visited) { - if (isCyclicDFSHelper(adjMatrix, states, - std::get<0>(child))) { - return true; + if (!isDirectedGraph()) { + return false; + } + enum nodeStates : uint8_t { not_visited, in_stack, visited }; + auto nodeSet = Graph::getNodeSet(); + auto adjMatrix = Graph::getAdjMatrix(); + + /* State of the node. + * + * It is a vector of "nodeStates" which represents the state node is in. + * It can take only 3 values: "not_visited", "in_stack", and "visited". + * + * Initially, all nodes are in "not_visited" state. + */ + std::unordered_map state; + for (const auto &node : nodeSet) { + state[node->getId()] = not_visited; + } + int stateCounter = 0; + + // Start visiting each node. + for (const auto &node : nodeSet) { + // If a node is not visited, only then check for presence of cycle. + // There is no need to check for presence of cycle for a visited + // node as it has already been checked for presence of cycle. + if (state[node->getId()] == not_visited) { + // Check for cycle. + std::function>, + std::unordered_map &, + const Node *)> + isCyclicDFSHelper; + isCyclicDFSHelper = + [this, &isCyclicDFSHelper]( + const std::shared_ptr> adjMatrix, + std::unordered_map &states, + const Node *node) { + // Add node "in_stack" state. + states[node->getId()] = in_stack; + + // If the node has children, then recursively visit all + // children of the node. + auto const it = adjMatrix->find(node); + if (it != adjMatrix->end()) { + for (const auto &child : it->second) { + // If state of child node is "not_visited", evaluate that + // child for presence of cycle. + auto state_of_child = + states.at((std::get<0>(child))->getId()); + if (state_of_child == not_visited) { + if (isCyclicDFSHelper(adjMatrix, states, + std::get<0>(child))) { + return true; + } + } else if (state_of_child == in_stack) { + // If child node was "in_stack", then that means that + // there is a cycle in the graph. Return true for + // presence of the cycle. + return true; + } + } } - } else if (state_of_child == in_stack) { - // If child node was "in_stack", then that means that there - // is a cycle in the graph. Return true for presence of the - // cycle. - return true; - } - } - } - // Current node has been evaluated for the presence of cycle and had - // no cycle. Mark current node as "visited". - states[node->getId()] = visited; - // Return that current node didn't result in any cycles. - return false; - }; - if (isCyclicDFSHelper(adjMatrix, state, node)) { - return true; - } - } - } + // Current node has been evaluated for the presence of cycle + // and had no cycle. Mark current node as "visited". + states[node->getId()] = visited; + // Return that current node didn't result in any cycles. + return false; + }; + if (isCyclicDFSHelper(adjMatrix, state, node)) { + return true; + } + } + } - // All nodes have been safely traversed, that means there is no cycle in - // the graph. Return false. - return false; + // All nodes have been safely traversed, that means there is no cycle in + // the graph. Return false. + return false; } template bool Graph::containsCycle(const T_EdgeSet *edgeSet) const { - std::unordered_map subset; - // initialize the subset parent and rank values - for (const auto &edge : *edgeSet) { - auto &[first, second] = edge->getNodePair(); - std::vector nodeId(2); - nodeId.push_back(first->getId()); - nodeId.push_back(second->getId()); - for (const auto &id : nodeId) { - auto nodeExists = [id](const auto &it) { - return (id == (it.second).parent); - }; - - if (std::find_if(subset.begin(), subset.end(), nodeExists) == - subset.end()) { - Subset set; - set.parent = id; - set.rank = 0; - subset[id] = set; - } - } - } - return Graph::containsCycle(edgeSet, &subset); + std::unordered_map subset; + // initialize the subset parent and rank values + for (const auto &edge : *edgeSet) { + auto &[first, second] = edge->getNodePair(); + std::vector nodeId(2); + nodeId.push_back(first->getId()); + nodeId.push_back(second->getId()); + for (const auto &id : nodeId) { + auto nodeExists = [id](const auto &it) { + return (id == (it.second).parent); + }; + + if (std::find_if(subset.begin(), subset.end(), nodeExists) == + subset.end()) { + Subset set; + set.parent = id; + set.rank = 0; + subset[id] = set; + } + } + } + return Graph::containsCycle(edgeSet, &subset); } template bool Graph::containsCycle( const T_EdgeSet *edgeSet, std::unordered_map *subset) const { - for (const auto &edge : *edgeSet) { - auto &[first, second] = edge->getNodePair(); - auto set1 = Graph::setFind(subset, first->getId()); - auto set2 = Graph::setFind(subset, second->getId()); - if (set1 == set2) return true; - Graph::setUnion(subset, set1, set2); - } - return false; + for (const auto &edge : *edgeSet) { + auto &[first, second] = edge->getNodePair(); + auto set1 = Graph::setFind(subset, first->getId()); + auto set2 = Graph::setFind(subset, second->getId()); + if (set1 == set2) return true; + Graph::setUnion(subset, set1, set2); + } + return false; } template bool Graph::isCyclicDirectedGraphBFS() const { - if (!isDirectedGraph()) { - return false; - } - const auto adjMatrix = Graph::getAdjMatrix(); - auto nodeSet = Graph::getNodeSet(); + if (!isDirectedGraph()) { + return false; + } + const auto adjMatrix = Graph::getAdjMatrix(); + auto nodeSet = Graph::getNodeSet(); - std::unordered_map indegree; - for (const auto &node : nodeSet) { - indegree[node->getId()] = 0; - } - // Calculate the indegree i.e. the number of incident edges to the node. - for (auto const &list : (*adjMatrix)) { - auto children = list.second; - for (auto const &child : children) { - indegree[std::get<0>(child)->getId()]++; - } - } + std::unordered_map indegree; + for (const auto &node : nodeSet) { + indegree[node->getId()] = 0; + } + // Calculate the indegree i.e. the number of incident edges to the node. + for (auto const &list : (*adjMatrix)) { + auto children = list.second; + for (auto const &child : children) { + indegree[std::get<0>(child)->getId()]++; + } + } - std::queue *> can_be_solved; - for (const auto &node : nodeSet) { - // If a node doesn't have any input edges, then that node will - // definately not result in a cycle and can be visited safely. - if (!indegree[node->getId()]) { - can_be_solved.emplace(&(*node)); - } - } + std::queue *> can_be_solved; + for (const auto &node : nodeSet) { + // If a node doesn't have any input edges, then that node will + // definately not result in a cycle and can be visited safely. + if (!indegree[node->getId()]) { + can_be_solved.emplace(&(*node)); + } + } - // Vertices that need to be traversed. - auto remain = Graph::getNodeSet().size(); - // While there are safe nodes that we can visit. - while (!can_be_solved.empty()) { - auto solved = can_be_solved.front(); - // Visit the node. - can_be_solved.pop(); - // Decrease number of nodes that need to be traversed. - remain--; - - // Visit all the children of the visited node. - auto it = adjMatrix->find(solved); - if (it != adjMatrix->end()) { - for (const auto &child : it->second) { - // Check if we can visited the node safely. - if (--indegree[std::get<0>(child)->getId()] == 0) { - // if node can be visited safely, then add that node to - // the visit queue. - can_be_solved.emplace(std::get<0>(child)); + // Vertices that need to be traversed. + auto remain = Graph::getNodeSet().size(); + // While there are safe nodes that we can visit. + while (!can_be_solved.empty()) { + auto solved = can_be_solved.front(); + // Visit the node. + can_be_solved.pop(); + // Decrease number of nodes that need to be traversed. + remain--; + + // Visit all the children of the visited node. + auto it = adjMatrix->find(solved); + if (it != adjMatrix->end()) { + for (const auto &child : it->second) { + // Check if we can visited the node safely. + if (--indegree[std::get<0>(child)->getId()] == 0) { + // if node can be visited safely, then add that node to + // the visit queue. + can_be_solved.emplace(std::get<0>(child)); + } + } + } } - } - } - } - // If there are still nodes that we can't visit, then it means that - // there is a cycle and return true, else return false. - return !(remain == 0); + // If there are still nodes that we can't visit, then it means that + // there is a cycle and return true, else return false. + return !(remain == 0); } template bool Graph::isDirectedGraph() const { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - if (!(edge->isDirected().has_value() && edge->isDirected().value())) { - // Found Undirected Edge - return false; - } - } - // No Undirected Edge - return true; + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + if (!(edge->isDirected().has_value() && edge->isDirected().value())) { + // Found Undirected Edge + return false; + } + } + // No Undirected Edge + return true; } template bool Graph::isUndirectedGraph() const { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - if ((edge->isDirected().has_value() && edge->isDirected().value())) { - // Found Directed Edge - return false; - } - } - // No Directed Edge - return true; + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + if ((edge->isDirected().has_value() && edge->isDirected().value())) { + // Found Directed Edge + return false; + } + } + // No Directed Edge + return true; } template bool Graph::isConnectedGraph() const { - if (!isUndirectedGraph()) { - return false; - } else { - auto nodeSet = getNodeSet(); - const auto adjMatrix = getAdjMatrix(); - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - std::function *)> dfs_helper = - [this, &adjMatrix, &visited, &dfs_helper](const Node *source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*adjMatrix)[source].size(); i++) { - const Node *neighbor = (*adjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); + if (!isUndirectedGraph()) { + return false; + } else { + auto nodeSet = getNodeSet(); + const auto adjMatrix = getAdjMatrix(); + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; + } + std::function *)> dfs_helper = + [this, &adjMatrix, &visited, &dfs_helper](const Node *source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*adjMatrix)[source].size(); i++) { + const Node *neighbor = (*adjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); + } + } + }; + // call dfs_helper for the first node + dfs_helper(*(nodeSet.begin())); + + // check if all the nodes are visited + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + return false; } } - }; - // call dfs_helper for the first node - dfs_helper(*(nodeSet.begin())); - - // check if all the nodes are visited - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - return false; - } - } - return true; - } + return true; + } } template bool Graph::isStronglyConnectedGraph() const { - if (!isDirectedGraph()) { - return false; - } else { - auto nodeSet = getNodeSet(); - const auto adjMatrix = getAdjMatrix(); - for (const auto &start_node : nodeSet) { - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - std::function *)> dfs_helper = - [this, &adjMatrix, &visited, &dfs_helper](const Node *source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*adjMatrix)[source].size(); i++) { - const Node *neighbor = (*adjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); + if (!isDirectedGraph()) { + return false; + } else { + auto nodeSet = getNodeSet(); + const auto adjMatrix = getAdjMatrix(); + for (const auto &start_node : nodeSet) { + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; + } + std::function *)> dfs_helper = + [this, &adjMatrix, &visited, + &dfs_helper](const Node *source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*adjMatrix)[source].size(); i++) { + const Node *neighbor = (*adjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); + } + } + }; + // call dfs_helper for the first node + dfs_helper(start_node); + + // check if all the nodes are visited + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + return false; } } - }; - // call dfs_helper for the first node - dfs_helper(start_node); - - // check if all the nodes are visited - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - return false; + } + return true; } - } - } - return true; - } } template TopoSortResult Graph::topologicalSort() const { - TopoSortResult result; - result.success = false; - - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else if (isCyclicDirectedGraphBFS()) { - result.errorMessage = ERR_CYCLIC_GRAPH; - return result; - } else { - const auto &adjMatrix = getAdjMatrix(); - const auto &nodeSet = getNodeSet(); - std::unordered_map *, bool> visited; - - std::function *)> postorder_helper = - [&postorder_helper, &adjMatrix, &visited, - &result](const Node *curNode) { - visited[curNode] = true; - - if (adjMatrix->find(curNode) != adjMatrix->end()) { - for (const auto &edge : adjMatrix->at(curNode)) { - const auto &nextNode = edge.first; - if (false == visited[nextNode]) { - postorder_helper(nextNode); - } - } - } + TopoSortResult result; + result.success = false; - result.nodesInTopoOrder.push_back(*curNode); - }; + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else if (isCyclicDirectedGraphBFS()) { + result.errorMessage = ERR_CYCLIC_GRAPH; + return result; + } else { + const auto &adjMatrix = getAdjMatrix(); + const auto &nodeSet = getNodeSet(); + std::unordered_map *, bool> visited; + + std::function *)> postorder_helper = + [&postorder_helper, &adjMatrix, &visited, + &result](const Node *curNode) { + visited[curNode] = true; + + if (adjMatrix->find(curNode) != adjMatrix->end()) { + for (const auto &edge : adjMatrix->at(curNode)) { + const auto &nextNode = edge.first; + if (false == visited[nextNode]) { + postorder_helper(nextNode); + } + } + } - int numNodes = adjMatrix->size(); - result.nodesInTopoOrder.reserve(numNodes); + result.nodesInTopoOrder.push_back(*curNode); + }; - for (const auto &node : nodeSet) { - if (false == visited[node]) { - postorder_helper(node); - } - } + int numNodes = adjMatrix->size(); + result.nodesInTopoOrder.reserve(numNodes); - result.success = true; - std::reverse(result.nodesInTopoOrder.begin(), - result.nodesInTopoOrder.end()); - return result; - } + for (const auto &node : nodeSet) { + if (false == visited[node]) { + postorder_helper(node); + } + } + + result.success = true; + std::reverse(result.nodesInTopoOrder.begin(), + result.nodesInTopoOrder.end()); + return result; + } } template TopoSortResult Graph::kahn() const { - TopoSortResult result; + TopoSortResult result; - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else { - const auto adjMatrix = Graph::getAdjMatrix(); - const auto nodeSet = Graph::getNodeSet(); - result.nodesInTopoOrder.reserve(adjMatrix->size()); + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else { + const auto adjMatrix = Graph::getAdjMatrix(); + const auto nodeSet = Graph::getNodeSet(); + result.nodesInTopoOrder.reserve(adjMatrix->size()); - std::unordered_map indegree; - for (const auto &node : nodeSet) { - indegree[node->getId()] = 0; - } - for (const auto &list : *adjMatrix) { - auto children = list.second; - for (const auto &child : children) { - indegree[std::get<0>(child)->getId()]++; - } - } + std::unordered_map indegree; + for (const auto &node : nodeSet) { + indegree[node->getId()] = 0; + } + for (const auto &list : *adjMatrix) { + auto children = list.second; + for (const auto &child : children) { + indegree[std::get<0>(child)->getId()]++; + } + } - std::queue *> topologicalOrder; + std::queue *> topologicalOrder; - for (const auto &node : nodeSet) { - if (!indegree[node->getId()]) { - topologicalOrder.emplace(node); - } - } + for (const auto &node : nodeSet) { + if (!indegree[node->getId()]) { + topologicalOrder.emplace(node); + } + } - size_t visited = 0; - while (!topologicalOrder.empty()) { - const Node *currentNode = topologicalOrder.front(); - topologicalOrder.pop(); - result.nodesInTopoOrder.push_back(*currentNode); + size_t visited = 0; + while (!topologicalOrder.empty()) { + const Node *currentNode = topologicalOrder.front(); + topologicalOrder.pop(); + result.nodesInTopoOrder.push_back(*currentNode); - if (adjMatrix->find(currentNode) != adjMatrix->end()) { - for (const auto &child : adjMatrix->at(currentNode)) { - if (--indegree[std::get<0>(child)->getId()] == 0) { - topologicalOrder.emplace(std::get<0>(child)); + if (adjMatrix->find(currentNode) != adjMatrix->end()) { + for (const auto &child : adjMatrix->at(currentNode)) { + if (--indegree[std::get<0>(child)->getId()] == 0) { + topologicalOrder.emplace(std::get<0>(child)); + } + } + } + visited++; } - } - } - visited++; - } - if (visited != nodeSet.size()) { - result.errorMessage = ERR_CYCLIC_GRAPH; - result.nodesInTopoOrder.clear(); - return result; - } + if (visited != nodeSet.size()) { + result.errorMessage = ERR_CYCLIC_GRAPH; + result.nodesInTopoOrder.clear(); + return result; + } - result.success = true; - return result; - } + result.success = true; + return result; + } } template SCCResult Graph::kosaraju() const { - SCCResult result; - result.success = false; + SCCResult result; + result.success = false; - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else { - auto nodeSet = getNodeSet(); - const auto adjMatrix = getAdjMatrix(); - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - - std::stack *> st; - std::function *)> dfs_helper = - [this, &adjMatrix, &visited, &dfs_helper, &st](const Node *source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*adjMatrix)[source].size(); i++) { - const Node *neighbor = (*adjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); - } + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else { + auto nodeSet = getNodeSet(); + const auto adjMatrix = getAdjMatrix(); + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; } - st.push(source); - }; - - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - dfs_helper(node); - } - } + std::stack *> st; + std::function *)> dfs_helper = + [this, &adjMatrix, &visited, &dfs_helper, + &st](const Node *source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*adjMatrix)[source].size(); i++) { + const Node *neighbor = (*adjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); + } + } - // construct the transpose of the given graph - AdjacencyMatrix rev; - auto addElementToAdjMatrix = [&rev](const Node *nodeFrom, - const Node *nodeTo, - const Edge *edge) { - std::pair *, const Edge *> elem = {nodeTo, edge}; - rev[nodeFrom].push_back(std::move(elem)); - }; - for (const auto &edgeSetIt : edgeSet) { - const DirectedEdge *d_edge = - dynamic_cast *>(edgeSetIt); - // Add the reverse edge to the reverse adjacency matrix - addElementToAdjMatrix(&(d_edge->getTo()), &(d_edge->getFrom()), d_edge); - } + st.push(source); + }; - visited.clear(); - - std::function *, std::vector> &)> dfs_helper1 = - [this, &rev, &visited, &dfs_helper1](const Node *source, - std::vector> &comp) { - // mark the vertex visited - visited[source->getId()] = true; - // Add the current vertex to the strongly connected component - comp.push_back(*source); - - // travel the neighbors - for (int i = 0; i < rev[source].size(); i++) { - const Node *neighbor = rev[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper1(neighbor, comp); + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + dfs_helper(node); } } - }; - while (st.size() != 0) { - auto rem = st.top(); - st.pop(); - if (visited[rem->getId()] == false) { - std::vector> comp; - dfs_helper1(rem, comp); - result.stronglyConnectedComps.push_back(comp); - } - } + // construct the transpose of the given graph + AdjacencyMatrix rev; + auto addElementToAdjMatrix = [&rev](const Node *nodeFrom, + const Node *nodeTo, + const Edge *edge) { + std::pair *, const Edge *> elem = {nodeTo, edge}; + rev[nodeFrom].push_back(std::move(elem)); + }; + for (const auto &edgeSetIt : edgeSet) { + const DirectedEdge *d_edge = + dynamic_cast *>(edgeSetIt); + // Add the reverse edge to the reverse adjacency matrix + addElementToAdjMatrix(&(d_edge->getTo()), &(d_edge->getFrom()), + d_edge); + } - result.success = true; - return result; - } + visited.clear(); + + std::function *, std::vector> &)> + dfs_helper1 = + [this, &rev, &visited, &dfs_helper1]( + const Node *source, std::vector> &comp) { + // mark the vertex visited + visited[source->getId()] = true; + // Add the current vertex to the strongly connected + // component + comp.push_back(*source); + + // travel the neighbors + for (int i = 0; i < rev[source].size(); i++) { + const Node *neighbor = rev[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper1(neighbor, comp); + } + } + }; + + while (st.size() != 0) { + auto rem = st.top(); + st.pop(); + if (visited[rem->getId()] == false) { + std::vector> comp; + dfs_helper1(rem, comp); + result.stronglyConnectedComps.push_back(comp); + } + } + + result.success = true; + return result; + } } template const DialResult Graph::dial(const Node &source, int maxWeight) const { - DialResult result; - result.success = false; + DialResult result; + result.success = false; - const auto adj = getAdjMatrix(); - auto nodeSet = getNodeSet(); + const auto adj = getAdjMatrix(); + auto nodeSet = getNodeSet(); - if (std::find(nodeSet.begin(), nodeSet.end(), &source) == nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - /* With each distance, iterator to that vertex in - its bucket is stored so that vertex can be deleted - in O(1) at time of updation. So - dist[i].first = distance of ith vertex from src vertex - dits[i].second = vertex i in bucket number */ - unsigned int V = nodeSet.size(); - std::unordered_map *, std::pair *>> dist; - - // Initialize all distances as infinite (INF) - for (const auto &node : nodeSet) { - dist[&(*node)].first = std::numeric_limits::max(); - } + if (std::find(nodeSet.begin(), nodeSet.end(), &source) == + nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + /* With each distance, iterator to that vertex in + its bucket is stored so that vertex can be deleted + in O(1) at time of updation. So + dist[i].first = distance of ith vertex from src vertex + dits[i].second = vertex i in bucket number */ + unsigned int V = nodeSet.size(); + std::unordered_map *, std::pair *>> + dist; + + // Initialize all distances as infinite (INF) + for (const auto &node : nodeSet) { + dist[&(*node)].first = std::numeric_limits::max(); + } - // Create buckets B[]. - // B[i] keep vertex of distance label i - std::deque *> B[maxWeight * V + 1]; + // Create buckets B[]. + // B[i] keep vertex of distance label i + std::deque *> B[maxWeight * V + 1]; - B[0].push_back(&source); - dist[&source].first = 0; + B[0].push_back(&source); + dist[&source].first = 0; - int idx = 0; - while (1) { - // Go sequentially through buckets till one non-empty - // bucket is found - while (B[idx].size() == 0 && idx < maxWeight * V) { - idx++; - } + int idx = 0; + while (1) { + // Go sequentially through buckets till one non-empty + // bucket is found + while (B[idx].size() == 0 && idx < maxWeight * V) { + idx++; + } - // If all buckets are empty, we are done. - if (idx == maxWeight * V) { - break; - } + // If all buckets are empty, we are done. + if (idx == maxWeight * V) { + break; + } - // Take top vertex from bucket and pop it - auto u = B[idx].front(); - B[idx].pop_front(); - - // Process all adjacents of extracted vertex 'u' and - // update their distanced if required. - for (const auto &i : (*adj)[u]) { - auto v = i.first; - int weight = 0; - if (i.second->isWeighted().has_value() && - i.second->isWeighted().value()) { - if (i.second->isDirected().has_value() && - i.second->isDirected().value()) { - const DirectedWeightedEdge *dw_edge = - dynamic_cast *>(i.second); - weight = dw_edge->getWeight(); - } else if (i.second->isDirected().has_value() && - !i.second->isDirected().value()) { - const UndirectedWeightedEdge *udw_edge = - dynamic_cast *>(i.second); - weight = udw_edge->getWeight(); - } else { - // ERROR it shouldn't never returned ( does not exist a Node Weighted - // and not Directed/Undirected) - result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; - return result; + // Take top vertex from bucket and pop it + auto u = B[idx].front(); + B[idx].pop_front(); + + // Process all adjacents of extracted vertex 'u' and + // update their distanced if required. + for (const auto &i : (*adj)[u]) { + auto v = i.first; + int weight = 0; + if (i.second->isWeighted().has_value() && + i.second->isWeighted().value()) { + if (i.second->isDirected().has_value() && + i.second->isDirected().value()) { + const DirectedWeightedEdge *dw_edge = + dynamic_cast *>(i.second); + weight = dw_edge->getWeight(); + } else if (i.second->isDirected().has_value() && + !i.second->isDirected().value()) { + const UndirectedWeightedEdge *udw_edge = + dynamic_cast *>(i.second); + weight = udw_edge->getWeight(); + } else { + // ERROR it shouldn't never returned ( does not exist a Node + // Weighted and not Directed/Undirected) + result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; + return result; + } + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + auto u_i = std::find_if( + dist.begin(), dist.end(), + [u](std::pair *, + std::pair *>> const &it) { + return (*u == *(it.first)); + }); + + auto v_i = std::find_if( + dist.begin(), dist.end(), + [v](std::pair *, + std::pair *>> const &it) { + return (*v == *(it.first)); + }); + long du = u_i->second.first; + long dv = v_i->second.first; + + // If there is shorted path to v through u. + if (dv > du + weight) { + // If dv is not INF then it must be in B[dv] + // bucket, so erase its entry using iterator + // in O(1) + if (dv != std::numeric_limits::max()) { + auto findIter = + std::find(B[dv].begin(), B[dv].end(), dist[v].second); + B[dv].erase(findIter); + } + + // updating the distance + dist[v].first = du + weight; + dv = dist[v].first; + + // pushing vertex v into updated distance's bucket + B[dv].push_front(v); + + // storing updated iterator in dist[v].second + dist[v].second = *(B[dv].begin()); + } + } } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - auto u_i = std::find_if( - dist.begin(), dist.end(), - [u](std::pair *, std::pair *>> const - &it) { return (*u == *(it.first)); }); - - auto v_i = std::find_if( - dist.begin(), dist.end(), - [v](std::pair *, std::pair *>> const - &it) { return (*v == *(it.first)); }); - long du = u_i->second.first; - long dv = v_i->second.first; - - // If there is shorted path to v through u. - if (dv > du + weight) { - // If dv is not INF then it must be in B[dv] - // bucket, so erase its entry using iterator - // in O(1) - if (dv != std::numeric_limits::max()) { - auto findIter = std::find(B[dv].begin(), B[dv].end(), dist[v].second); - B[dv].erase(findIter); - } - - // updating the distance - dist[v].first = du + weight; - dv = dist[v].first; - - // pushing vertex v into updated distance's bucket - B[dv].push_front(v); - - // storing updated iterator in dist[v].second - dist[v].second = *(B[dv].begin()); - } - } - } - for (const auto &dist_i : dist) { - result.minDistanceMap[dist_i.first->getId()] = dist_i.second.first; - } - result.success = true; + for (const auto &dist_i : dist) { + result.minDistanceMap[dist_i.first->getId()] = dist_i.second.first; + } + result.success = true; - return result; + return result; } template double Graph::fordFulkersonMaxFlow(const Node &source, const Node &target) const { - if (!isDirectedGraph()) { - return -1; - } - double maxFlow = 0; - std::unordered_map *, const Node *> parent; - std::map *, std::map *, double>> weightMap; - // build weight map - auto edgeSet = this->getEdgeSet(); - for (const auto &edge : edgeSet) { - // The Edge are all Directed at this point because is checked at the start - if (edge->isWeighted().value_or(false)) { - const DirectedWeightedEdge *dw_edge = - dynamic_cast *>(edge); - weightMap[edge->getNodePair().first][edge->getNodePair().second] = - dw_edge->getWeight(); - } else { - weightMap[edge->getNodePair().first][edge->getNodePair().second] = - 0; // No Weighted Edge are assumed to be 0 weigthed - } - } - - auto bfs_helper = [this, &source, &target, &parent, &weightMap]() -> bool { - std::unordered_map *, bool> visited; - std::queue *> queue; - queue.push(&source); - visited[&source] = true; - parent[&source] = nullptr; - while (!queue.empty()) { - auto u = queue.front(); - queue.pop(); - for (auto &v : weightMap[u]) { - if (!visited[v.first] && v.second > 0) { - queue.push(v.first); - visited[v.first] = true; - parent[v.first] = u; + if (!isDirectedGraph()) { + return -1; + } + double maxFlow = 0; + std::unordered_map *, const Node *> parent; + std::map *, std::map *, double>> weightMap; + // build weight map + auto edgeSet = this->getEdgeSet(); + for (const auto &edge : edgeSet) { + // The Edge are all Directed at this point because is checked at the + // start + if (edge->isWeighted().value_or(false)) { + const DirectedWeightedEdge *dw_edge = + dynamic_cast *>(edge); + weightMap[edge->getNodePair().first][edge->getNodePair().second] = + dw_edge->getWeight(); + } else { + weightMap[edge->getNodePair().first][edge->getNodePair().second] = + 0; // No Weighted Edge are assumed to be 0 weigthed + } } - } - } - return (visited[&target]); - }; - // Updating the residual values of edges - while (bfs_helper()) { - double pathFlow = std::numeric_limits::max(); - for (auto v = ⌖ v != &source; v = parent[v]) { - auto u = parent[v]; - pathFlow = std::min(pathFlow, weightMap[u][v]); - } - for (auto v = ⌖ v != &source; v = parent[v]) { - auto u = parent[v]; - weightMap[u][v] -= pathFlow; - weightMap[v][u] += pathFlow; - } - // Adding the path flows - maxFlow += pathFlow; - } + auto bfs_helper = [this, &source, &target, &parent, + &weightMap]() -> bool { + std::unordered_map *, bool> visited; + std::queue *> queue; + queue.push(&source); + visited[&source] = true; + parent[&source] = nullptr; + while (!queue.empty()) { + auto u = queue.front(); + queue.pop(); + for (auto &v : weightMap[u]) { + if (!visited[v.first] && v.second > 0) { + queue.push(v.first); + visited[v.first] = true; + parent[v.first] = u; + } + } + } - return maxFlow; + return (visited[&target]); + }; + // Updating the residual values of edges + while (bfs_helper()) { + double pathFlow = std::numeric_limits::max(); + for (auto v = ⌖ v != &source; v = parent[v]) { + auto u = parent[v]; + pathFlow = std::min(pathFlow, weightMap[u][v]); + } + for (auto v = ⌖ v != &source; v = parent[v]) { + auto u = parent[v]; + weightMap[u][v] -= pathFlow; + weightMap[v][u] += pathFlow; + } + // Adding the path flows + maxFlow += pathFlow; + } + + return maxFlow; } template const std::vector> Graph::graph_slicing(const Node &start) const { - std::vector> result; + std::vector> result; - auto nodeSet = Graph::getNodeSet(); - // check if start node in the graph - if (std::find(nodeSet.begin(), nodeSet.end(), &start) == nodeSet.end()) { - return result; - } - std::vector> C = Graph::depth_first_search(start); - std::deque *> C1; // complement of C i.e. nodeSet - C - for (auto const &node : nodeSet) { - // from the set of all nodes, remove nodes that exist in C - if (std::find_if(C.begin(), C.end(), [node](const Node nodeC) { - return (*node == nodeC); - }) == C.end()) - C1.push_back(node); - } + auto nodeSet = Graph::getNodeSet(); + // check if start node in the graph + if (std::find(nodeSet.begin(), nodeSet.end(), &start) == + nodeSet.end()) { + return result; + } + std::vector> C = Graph::depth_first_search(start); + std::deque *> C1; // complement of C i.e. nodeSet - C + for (auto const &node : nodeSet) { + // from the set of all nodes, remove nodes that exist in C + if (std::find_if(C.begin(), C.end(), [node](const Node nodeC) { + return (*node == nodeC); + }) == C.end()) + C1.push_back(node); + } - // For all nodes in C', apply DFS - // and get the list of reachable nodes and store in M - std::vector> M; - for (auto const &node : C1) { - std::vector> reachableNodes = Graph::depth_first_search(*node); - M.insert(M.end(), reachableNodes.begin(), reachableNodes.end()); - } - // removes nodes from C that are reachable from M. - for (const auto &nodeC : C) { - if (std::find_if(M.begin(), M.end(), [nodeC](const Node nodeM) { - return (nodeM == nodeC); - }) == M.end()) - result.push_back(nodeC); - } - return result; + // For all nodes in C', apply DFS + // and get the list of reachable nodes and store in M + std::vector> M; + for (auto const &node : C1) { + std::vector> reachableNodes = + Graph::depth_first_search(*node); + M.insert(M.end(), reachableNodes.begin(), reachableNodes.end()); + } + // removes nodes from C that are reachable from M. + for (const auto &nodeC : C) { + if (std::find_if(M.begin(), M.end(), [nodeC](const Node nodeM) { + return (nodeM == nodeC); + }) == M.end()) + result.push_back(nodeC); + } + return result; } template @@ -2605,59 +2720,61 @@ int Graph::writeToFile(InputOutputFormat format, const std::string &workingDir, const std::string &OFileName, bool compress, bool writeNodeFeat, bool writeEdgeWeight) const { - int result = 0; - result = writeToStandardFile(workingDir, OFileName, compress, writeNodeFeat, - writeEdgeWeight, format); - if (result == 0 && compress) { - auto _compress = [this, &workingDir, &OFileName, &writeNodeFeat, - &writeEdgeWeight](const std::string &extension) { - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - std::string completePathToFileGraphCompressed = - workingDir + "/" + OFileName + extension + ".gz"; - int _result = compressFile(completePathToFileGraph, - completePathToFileGraphCompressed); - if (_result == 0) { - _result = remove(completePathToFileGraph.c_str()); - } - if (_result == 0) { - if (writeNodeFeat) { - std::string completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - std::string completePathToFileNodeFeatCompressed = - workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; - _result = compressFile(completePathToFileNodeFeat, - completePathToFileNodeFeatCompressed); - if (_result == 0) { - _result = remove(completePathToFileNodeFeat.c_str()); - } - } - } - if (_result == 0) { - if (writeEdgeWeight) { - std::string completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - std::string completePathToFileEdgeWeightCompressed = - workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; - _result = compressFile(completePathToFileEdgeWeight, - completePathToFileEdgeWeightCompressed); - if (_result == 0) { - _result = remove(completePathToFileEdgeWeight.c_str()); + int result = 0; + result = writeToStandardFile(workingDir, OFileName, compress, + writeNodeFeat, writeEdgeWeight, format); + if (result == 0 && compress) { + auto _compress = [this, &workingDir, &OFileName, &writeNodeFeat, + &writeEdgeWeight](const std::string &extension) { + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + std::string completePathToFileGraphCompressed = + workingDir + "/" + OFileName + extension + ".gz"; + int _result = compressFile(completePathToFileGraph, + completePathToFileGraphCompressed); + if (_result == 0) { + _result = remove(completePathToFileGraph.c_str()); + } + if (_result == 0) { + if (writeNodeFeat) { + std::string completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + std::string completePathToFileNodeFeatCompressed = + workingDir + "/" + OFileName + "_NodeFeat" + extension + + ".gz"; + _result = compressFile(completePathToFileNodeFeat, + completePathToFileNodeFeatCompressed); + if (_result == 0) { + _result = remove(completePathToFileNodeFeat.c_str()); + } + } + } + if (_result == 0) { + if (writeEdgeWeight) { + std::string completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + std::string completePathToFileEdgeWeightCompressed = + workingDir + "/" + OFileName + "_EdgeWeight" + extension + + ".gz"; + _result = compressFile(completePathToFileEdgeWeight, + completePathToFileEdgeWeightCompressed); + if (_result == 0) { + _result = remove(completePathToFileEdgeWeight.c_str()); + } + } + } + return _result; + }; + if (format == InputOutputFormat::STANDARD_CSV) { + auto result = _compress(".csv"); + } else if (format == InputOutputFormat::STANDARD_TSV) { + auto result = _compress(".tsv"); + } else { + // OUTPUT FORMAT NOT RECOGNIZED + result = -1; } } - } - return _result; - }; - if (format == InputOutputFormat::STANDARD_CSV) { - auto result = _compress(".csv"); - } else if (format == InputOutputFormat::STANDARD_TSV) { - auto result = _compress(".tsv"); - } else { - // OUTPUT FORMAT NOT RECOGNIZED - result = -1; - } - } - return result; + return result; } template @@ -2665,52 +2782,54 @@ int Graph::readFromFile(InputOutputFormat format, const std::string &workingDir, const std::string &OFileName, bool compress, bool readNodeFeat, bool readEdgeWeight) { - int result = 0; - if (compress) { - auto decompress = [this, &workingDir, &OFileName, &readNodeFeat, - &readEdgeWeight](const std::string &extension) { - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - std::string completePathToFileGraphCompressed = - workingDir + "/" + OFileName + extension + ".gz"; - int _result = decompressFile(completePathToFileGraphCompressed, - completePathToFileGraph); - if (_result == 0) { - if (readNodeFeat) { - std::string completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - std::string completePathToFileNodeFeatCompressed = - workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; - _result = decompressFile(completePathToFileNodeFeatCompressed, - completePathToFileNodeFeat); + int result = 0; + if (compress) { + auto decompress = [this, &workingDir, &OFileName, &readNodeFeat, + &readEdgeWeight](const std::string &extension) { + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + std::string completePathToFileGraphCompressed = + workingDir + "/" + OFileName + extension + ".gz"; + int _result = decompressFile(completePathToFileGraphCompressed, + completePathToFileGraph); + if (_result == 0) { + if (readNodeFeat) { + std::string completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + std::string completePathToFileNodeFeatCompressed = + workingDir + "/" + OFileName + "_NodeFeat" + extension + + ".gz"; + _result = decompressFile(completePathToFileNodeFeatCompressed, + completePathToFileNodeFeat); + } + } + if (_result == 0) { + if (readEdgeWeight) { + std::string completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + std::string completePathToFileEdgeWeightCompressed = + workingDir + "/" + OFileName + "_EdgeWeight" + extension + + ".gz"; + _result = decompressFile(completePathToFileEdgeWeightCompressed, + completePathToFileEdgeWeight); + } + } + return _result; + }; + if (format == InputOutputFormat::STANDARD_CSV) { + result = decompress(".csv"); + } else if (format == InputOutputFormat::STANDARD_TSV) { + result = decompress(".tsv"); + } else { + // INPUT FORMAT NOT RECOGNIZED + result = -1; + } } - } - if (_result == 0) { - if (readEdgeWeight) { - std::string completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - std::string completePathToFileEdgeWeightCompressed = - workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; - _result = decompressFile(completePathToFileEdgeWeightCompressed, - completePathToFileEdgeWeight); + if (result == 0) { + result = readFromStandardFile(workingDir, OFileName, compress, + readNodeFeat, readEdgeWeight, format); } - } - return _result; - }; - if (format == InputOutputFormat::STANDARD_CSV) { - result = decompress(".csv"); - } else if (format == InputOutputFormat::STANDARD_TSV) { - result = decompress(".tsv"); - } else { - // INPUT FORMAT NOT RECOGNIZED - result = -1; - } - } - if (result == 0) { - result = readFromStandardFile(workingDir, OFileName, compress, readNodeFeat, - readEdgeWeight, format); - } - return result; + return result; } template @@ -2719,82 +2838,88 @@ PartitionMap Graph::partitionGraph( const unsigned int numberOfPartitions, const double param1, const double param2, const double param3, const unsigned int numberOfThreads) const { - PartitionMap partitionMap; - PARTITIONING::Globals globals(numberOfPartitions, algorithm, param1, param2, - param3, numberOfThreads); - const T_EdgeSet &edgeSet = getEdgeSet(); - globals.edgeCardinality = edgeSet.size(); - globals.vertexCardinality = this->getNodeSet().size(); - PARTITIONING::Partitioner partitioner(&edgeSet, globals); - PARTITIONING::CoordinatedPartitionState partitionState = - partitioner.performCoordinatedPartition(); - partitionMap = partitionState.getPartitionMap(); - return partitionMap; + PartitionMap partitionMap; + PARTITIONING::Globals globals(numberOfPartitions, algorithm, param1, + param2, param3, numberOfThreads); + const T_EdgeSet &edgeSet = getEdgeSet(); + globals.edgeCardinality = edgeSet.size(); + globals.vertexCardinality = this->getNodeSet().size(); + PARTITIONING::Partitioner partitioner(&edgeSet, globals); + PARTITIONING::CoordinatedPartitionState partitionState = + partitioner.performCoordinatedPartition(); + partitionMap = partitionState.getPartitionMap(); + return partitionMap; } template std::ostream &operator<<(std::ostream &os, const Graph &graph) { - os << "Graph:\n"; - auto edgeList = graph.getEdgeSet(); - auto it = edgeList.begin(); - for (it; it != edgeList.end(); ++it) { - if (!(*it)->isDirected().has_value() && !(*it)->isWeighted().has_value()) { - // Edge Case - os << **it << "\n"; - } else if (((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - ((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << dynamic_cast &>(*it) << "\n"; - } else if (((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - !((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << dynamic_cast &>(*it) << "\n"; - } else if (!(it->isDirected().has_value() && it->isDirected().value()) && - (it->isWeighted().has_value() && it->isWeighted().value())) { - os << dynamic_cast &>(*it) << "\n"; - } else if (!(it->isDirected().has_value() && it->isDirected().value()) && - !(it->isWeighted().has_value() && it->isWeighted().value())) { - os << dynamic_cast &>(*it) << "\n"; - } else { - os << *it << "\n"; - } - } - return os; + os << "Graph:\n"; + auto edgeList = graph.getEdgeSet(); + auto it = edgeList.begin(); + for (it; it != edgeList.end(); ++it) { + if (!(*it)->isDirected().has_value() && + !(*it)->isWeighted().has_value()) { + // Edge Case + os << **it << "\n"; + } else if (((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + ((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << dynamic_cast &>(*it) << "\n"; + } else if (((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + !((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << dynamic_cast &>(*it) << "\n"; + } else if (!(it->isDirected().has_value() && + it->isDirected().value()) && + (it->isWeighted().has_value() && + it->isWeighted().value())) { + os << dynamic_cast &>(*it) << "\n"; + } else if (!(it->isDirected().has_value() && + it->isDirected().value()) && + !(it->isWeighted().has_value() && + it->isWeighted().value())) { + os << dynamic_cast &>(*it) << "\n"; + } else { + os << *it << "\n"; + } + } + return os; } template std::ostream &operator<<(std::ostream &os, const AdjacencyMatrix &adj) { - os << "Adjacency Matrix:\n"; - unsigned long max_column = 0; - for (const auto &it : adj) { - if (it.second.size() > max_column) { - max_column = it.second.size(); - } - } - if (max_column == 0) { - os << "ERROR in Print\n"; - return os; - } else { - os << "|--|"; - for (int i = 0; i < max_column; ++i) { - os << "-----|"; - } - os << "\n"; - for (const auto &it : adj) { - os << "|N" << it.first->getId() << "|"; - for (const auto &it2 : it.second) { - os << "N" << it2.first->getId() << ",E" << it2.second->getId() << "|"; - } - os << "\n|--|"; - for (int i = 0; i < max_column; ++i) { - os << "-----|"; - } - os << "\n"; - } - } - return os; + os << "Adjacency Matrix:\n"; + unsigned long max_column = 0; + for (const auto &it : adj) { + if (it.second.size() > max_column) { + max_column = it.second.size(); + } + } + if (max_column == 0) { + os << "ERROR in Print\n"; + return os; + } else { + os << "|--|"; + for (int i = 0; i < max_column; ++i) { + os << "-----|"; + } + os << "\n"; + for (const auto &it : adj) { + os << "|N" << it.first->getId() << "|"; + for (const auto &it2 : it.second) { + os << "N" << it2.first->getId() << ",E" << it2.second->getId() + << "|"; + } + os << "\n|--|"; + for (int i = 0; i < max_column; ++i) { + os << "-----|"; + } + os << "\n"; + } + } + return os; } } // namespace CXXGRAPH From ab7cef6f7b6b6c18eeaa121c18bf3bc1f666a092 Mon Sep 17 00:00:00 2001 From: sbaldu Date: Tue, 9 May 2023 20:05:52 +0200 Subject: [PATCH 3/6] Debug writeToDOT function --- include/Graph/Graph.hpp | 3397 +++++++++++++++++++-------------------- 1 file changed, 1685 insertions(+), 1712 deletions(-) diff --git a/include/Graph/Graph.hpp b/include/Graph/Graph.hpp index e953062bc..44a17bc01 100644 --- a/include/Graph/Graph.hpp +++ b/include/Graph/Graph.hpp @@ -89,15 +89,13 @@ class Graph { const std::string &OFileName, bool compress, bool writeNodeFeat, bool writeEdgeWeight, InputOutputFormat format) const; - int writeToDotFile(const std::string &workingDir, - const std::string &OFileName, - const std::string &graphName); + int writeToDot(const std::string &workingDir, const std::string &OFileName, + const std::string &graphName) const; int readFromStandardFile(const std::string &workingDir, const std::string &OFileName, bool compress, bool readNodeFeat, bool readEdgeWeight, InputOutputFormat format); - int readFromDotFile(const std::string &workingDir, - const std::string &fileName); + int readFromDot(const std::string &workingDir, const std::string &fileName); void recreateGraphFromReadFiles( std::unordered_map> &edgeMap, @@ -520,6 +518,10 @@ class Graph { const std::string &OFileName = "graph", bool compress = false, bool writeNodeFeat = false, bool writeEdgeWeight = false) const; + virtual int writeToDotFile(const std::string &workingDir, + const std::string &OFileName, + const std::string &graphName) const; + /** * \brief * This function read the graph from an input file @@ -540,6 +542,9 @@ class Graph { const std::string &OFileName = "graph", bool compress = false, bool readNodeFeat = false, bool readEdgeWeight = false); + virtual int readFromDotFile(const std::string &workingDir, + const std::string &fileName); + /** * \brief * This function partition a graph in a set of partitions @@ -748,9 +753,9 @@ int Graph::writeToStandardFile(const std::string &workingDir, } template -int Graph::writeToDotFile(const std::string &workingDir, - const std::string &OFileName, - const std::string &graphName) { +int Graph::writeToDot(const std::string &workingDir, + const std::string &OFileName, + const std::string &graphName) const { const std::string linkSymbol = "--"; const std::string directedLinkSymbol = "->"; @@ -763,23 +768,35 @@ int Graph::writeToDotFile(const std::string &workingDir, return -1; } + // Write the header of the DOT file + std::string headerLine; + if (this->isDirectedGraph()) { + headerLine = "digraph " + graphName + " {\n"; + } else { + headerLine = "graph " + graphName + " {\n"; + } + ofileGraph << headerLine; + for (auto const &edgeIt : edgeSet) { std::string edgeLine = ""; - if (edgeIt->isDirected()) { - edgeLine += "digraph " + graphName + " {"; - edgeLine += '\t' + std::to_string(edgeIt->getFrom().getUserId()) + ' '; + if (edgeIt->isDirected().has_value() && edgeIt->isDirected().value()) { + auto directedIt = dynamic_cast *>(edgeIt); + edgeLine += '\t' + directedIt->getFrom().getUserId() + ' '; edgeLine += directedLinkSymbol + ' '; - edgeLine += std::to_string(edgeIt->getTo().getUserId()); + edgeLine += directedIt->getTo().getUserId(); } else { - edgeLine += '\t' + std::to_string(edgeIt->getNode1().getUserId()) + ' '; + edgeLine += '\t' + edgeIt->getNodePair().first->getUserId() + ' '; edgeLine += linkSymbol + ' '; - edgeLine += std::to_string(edgeIt->getNode2().getUserId()); + edgeLine += edgeIt->getNodePair().second->getUserId(); } - if (edgeIt->isWeighted()) { + if (edgeIt->isWeighted().has_value() && edgeIt->isWeighted().value()) { // Weights in dot files must be integers - edgeLine += " [weight" + - std::to_string(static_cast(edgeIt->getWeight())) + ']'; + edgeLine += " [weight=" + + std::to_string(static_cast( + dynamic_cast(edgeIt)->getWeight())) + + ']'; } + std::cout << __LINE__ << std::endl; edgeLine += ";\n"; ofileGraph << edgeLine; } @@ -894,8 +911,8 @@ int Graph::readFromStandardFile(const std::string &workingDir, } template -int Graph::readFromDotFile(const std::string &workingDir, - const std::string &fileName) { +int Graph::readFromDot(const std::string &workingDir, + const std::string &fileName) { // Define the edge maps std::unordered_map> edgeMap; @@ -908,7 +925,8 @@ int Graph::readFromDotFile(const std::string &workingDir, std::string temp; // Get full path to the file and open it - std::string completePathToFileGraph = workingDir + '/' + fileName ".dot"; + const std::string completePathToFileGraph = + workingDir + '/' + fileName + ".dot"; std::ifstream iFile(completePathToFileGraph); // Check if the graph is directed @@ -917,21 +935,21 @@ int Graph::readFromDotFile(const std::string &workingDir, std::string fileContent(std::istreambuf_iterator{fileContentStream}, {}); const std::string directedSeparator = "->"; - if (content.find(directedSeparator) { - directed = true; - } - // Check if the graph is weighted - bool weighted = false; - if (content.find("weight")) { + if (fileContent.find(directedSeparator)) { directed = true; - } + } + // Check if the graph is weighted + bool weighted = false; + if (fileContent.find("weight")) { + weighted = true; + } - // Write the header of the DOT file in the temp string - getline(iFile, temp); + // Write the header of the DOT file in the temp string + getline(iFile, temp); - unsigned long long edgeId = 0; - std::string fileRow; - while (getline(iFile, fileRow)) { + unsigned long long edgeId = 0; + std::string fileRow; + while (getline(iFile, fileRow)) { // If you've reached the end of the file, stop if (fileRow == "}") { break; @@ -964,7 +982,7 @@ int Graph::readFromDotFile(const std::string &workingDir, edgeMap[edgeId] = std::pair(node1, node2); edgeDirectedMap[edgeId] = directed; ++edgeId; - } + } } template @@ -974,1745 +992,1697 @@ void Graph::recreateGraphFromReadFiles( std::unordered_map &edgeDirectedMap, std::unordered_map &nodeFeatMap, std::unordered_map &edgeWeightMap) { - std::unordered_map *> nodeMap; - for (const auto &edgeIt : edgeMap) { - Node *node1 = nullptr; - Node *node2 = nullptr; - if (nodeMap.find(edgeIt.second.first) == nodeMap.end()) { - // Create new Node - T feat; - if (nodeFeatMap.find(edgeIt.second.first) != nodeFeatMap.end()) { - feat = nodeFeatMap.at(edgeIt.second.first); - } - node1 = new Node(edgeIt.second.first, feat); - nodeMap[edgeIt.second.first] = node1; - } else { - node1 = nodeMap.at(edgeIt.second.first); - } - if (nodeMap.find(edgeIt.second.second) == nodeMap.end()) { - // Create new Node - T feat; - if (nodeFeatMap.find(edgeIt.second.second) != nodeFeatMap.end()) { - feat = nodeFeatMap.at(edgeIt.second.second); - } - node2 = new Node(edgeIt.second.second, feat); - nodeMap[edgeIt.second.second] = node2; - } else { - node2 = nodeMap.at(edgeIt.second.second); - } + std::unordered_map *> nodeMap; + for (const auto &edgeIt : edgeMap) { + Node *node1 = nullptr; + Node *node2 = nullptr; + if (nodeMap.find(edgeIt.second.first) == nodeMap.end()) { + // Create new Node + T feat; + if (nodeFeatMap.find(edgeIt.second.first) != nodeFeatMap.end()) { + feat = nodeFeatMap.at(edgeIt.second.first); + } + node1 = new Node(edgeIt.second.first, feat); + nodeMap[edgeIt.second.first] = node1; + } else { + node1 = nodeMap.at(edgeIt.second.first); + } + if (nodeMap.find(edgeIt.second.second) == nodeMap.end()) { + // Create new Node + T feat; + if (nodeFeatMap.find(edgeIt.second.second) != nodeFeatMap.end()) { + feat = nodeFeatMap.at(edgeIt.second.second); + } + node2 = new Node(edgeIt.second.second, feat); + nodeMap[edgeIt.second.second] = node2; + } else { + node2 = nodeMap.at(edgeIt.second.second); + } - if (edgeWeightMap.find(edgeIt.first) != edgeWeightMap.end()) { - if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && - edgeDirectedMap.at(edgeIt.first)) { - auto edge = new DirectedWeightedEdge( - edgeIt.first, *node1, *node2, edgeWeightMap.at(edgeIt.first)); - addEdge(edge); - } else { - auto edge = new UndirectedWeightedEdge( - edgeIt.first, *node1, *node2, edgeWeightMap.at(edgeIt.first)); - addEdge(edge); - } - } else { - if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && - edgeDirectedMap.at(edgeIt.first)) { - auto edge = new DirectedEdge(edgeIt.first, *node1, *node2); - addEdge(edge); - } else { - auto edge = new UndirectedEdge(edgeIt.first, *node1, *node2); - addEdge(edge); - } - } - } + if (edgeWeightMap.find(edgeIt.first) != edgeWeightMap.end()) { + if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && + edgeDirectedMap.at(edgeIt.first)) { + auto edge = new DirectedWeightedEdge(edgeIt.first, *node1, *node2, + edgeWeightMap.at(edgeIt.first)); + addEdge(edge); + } else { + auto edge = new UndirectedWeightedEdge( + edgeIt.first, *node1, *node2, edgeWeightMap.at(edgeIt.first)); + addEdge(edge); + } + } else { + if (edgeDirectedMap.find(edgeIt.first) != edgeDirectedMap.end() && + edgeDirectedMap.at(edgeIt.first)) { + auto edge = new DirectedEdge(edgeIt.first, *node1, *node2); + addEdge(edge); + } else { + auto edge = new UndirectedEdge(edgeIt.first, *node1, *node2); + addEdge(edge); + } + } + } } template int Graph::compressFile(const std::string &inputFile, const std::string &outputFile) const { - std::ifstream ifs; - ifs.open(inputFile); - if (!ifs.is_open()) { - // ERROR File Not Open - return -1; - } - std::string content((std::istreambuf_iterator(ifs)), - (std::istreambuf_iterator())); - - const char *content_ptr = content.c_str(); - gzFile outFileZ = gzopen(outputFile.c_str(), "wb"); - if (outFileZ == NULL) { - // printf("Error: Failed to gzopen %s\n", outputFile.c_str()); - return -1; - } + std::ifstream ifs; + ifs.open(inputFile); + if (!ifs.is_open()) { + // ERROR File Not Open + return -1; + } + std::string content((std::istreambuf_iterator(ifs)), + (std::istreambuf_iterator())); + + const char *content_ptr = content.c_str(); + gzFile outFileZ = gzopen(outputFile.c_str(), "wb"); + if (outFileZ == NULL) { + // printf("Error: Failed to gzopen %s\n", outputFile.c_str()); + return -1; + } - unsigned int zippedBytes; - zippedBytes = gzwrite(outFileZ, content_ptr, strlen(content_ptr)); + unsigned int zippedBytes; + zippedBytes = gzwrite(outFileZ, content_ptr, strlen(content_ptr)); - ifs.close(); - gzclose(outFileZ); - return 0; + ifs.close(); + gzclose(outFileZ); + return 0; } template int Graph::decompressFile(const std::string &inputFile, const std::string &outputFile) const { - gzFile inFileZ = gzopen(inputFile.c_str(), "rb"); - if (inFileZ == NULL) { - // printf("Error: Failed to gzopen %s\n", inputFile.c_str()); - return -1; - } - unsigned char unzipBuffer[8192]; - unsigned int unzippedBytes; - std::vector unzippedData; - std::ofstream ofs; - ofs.open(outputFile); - if (!ofs.is_open()) { - // ERROR File Not Open - return -1; - } - while (true) { - unzippedBytes = gzread(inFileZ, unzipBuffer, 8192); - if (unzippedBytes > 0) { - unzippedData.insert(unzippedData.end(), unzipBuffer, - unzipBuffer + unzippedBytes); - } else { - break; - } - } - for (const auto &c : unzippedData) { - ofs << c; - } - ofs << std::endl; - ofs.close(); - gzclose(inFileZ); - return 0; + gzFile inFileZ = gzopen(inputFile.c_str(), "rb"); + if (inFileZ == NULL) { + // printf("Error: Failed to gzopen %s\n", inputFile.c_str()); + return -1; + } + unsigned char unzipBuffer[8192]; + unsigned int unzippedBytes; + std::vector unzippedData; + std::ofstream ofs; + ofs.open(outputFile); + if (!ofs.is_open()) { + // ERROR File Not Open + return -1; + } + while (true) { + unzippedBytes = gzread(inFileZ, unzipBuffer, 8192); + if (unzippedBytes > 0) { + unzippedData.insert(unzippedData.end(), unzipBuffer, + unzipBuffer + unzippedBytes); + } else { + break; + } + } + for (const auto &c : unzippedData) { + ofs << c; + } + ofs << std::endl; + ofs.close(); + gzclose(inFileZ); + return 0; } template unsigned long long Graph::setFind( std::unordered_map *subsets, const unsigned long long nodeId) const { - // find root and make root as parent of i - // (path compression) - if ((*subsets)[nodeId].parent != nodeId) { - (*subsets)[nodeId].parent = - Graph::setFind(subsets, (*subsets)[nodeId].parent); - } + // find root and make root as parent of i + // (path compression) + if ((*subsets)[nodeId].parent != nodeId) { + (*subsets)[nodeId].parent = + Graph::setFind(subsets, (*subsets)[nodeId].parent); + } - return (*subsets)[nodeId].parent; + return (*subsets)[nodeId].parent; } template void Graph::setUnion(std::unordered_map *subsets, const unsigned long long elem1, const unsigned long long elem2) const { - // if both sets have same parent - // then there's nothing to be done - if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; - auto elem1Parent = Graph::setFind(subsets, elem1); - auto elem2Parent = Graph::setFind(subsets, elem2); - if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) - (*subsets)[elem1].parent = elem2Parent; - else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) - (*subsets)[elem2].parent = elem1Parent; - else { - (*subsets)[elem2].parent = elem1Parent; - (*subsets)[elem1Parent].rank++; - } + // if both sets have same parent + // then there's nothing to be done + if ((*subsets)[elem1].parent == (*subsets)[elem2].parent) return; + auto elem1Parent = Graph::setFind(subsets, elem1); + auto elem2Parent = Graph::setFind(subsets, elem2); + if ((*subsets)[elem1Parent].rank < (*subsets)[elem2Parent].rank) + (*subsets)[elem1].parent = elem2Parent; + else if ((*subsets)[elem1Parent].rank > (*subsets)[elem2Parent].rank) + (*subsets)[elem2].parent = elem1Parent; + else { + (*subsets)[elem2].parent = elem1Parent; + (*subsets)[elem1Parent].rank++; + } } template std::shared_ptr>> Graph::eulerianPath() const { - const auto nodeSet = Graph::getNodeSet(); - const auto adj = Graph::getAdjMatrix(); - std::shared_ptr>> eulerPath = - std::make_shared>>(); - std::vector *> currentPath; - auto currentNode = *(nodeSet.begin()); - currentPath.push_back(currentNode); - while (currentPath.size() > 0) { - auto &edges = adj->at(currentNode); - // we keep removing the edges that - // have been traversed from the adjacency list - if (edges.size()) { - auto firstEdge = edges.back().second; - auto nextNodeId = firstEdge->getNodePair().second; - currentPath.push_back(nextNodeId); - currentNode = nextNodeId; - edges.pop_back(); - } else { - eulerPath->push_back(*currentNode); - currentNode = currentPath.back(); - currentPath.pop_back(); - } - } - return eulerPath; + const auto nodeSet = Graph::getNodeSet(); + const auto adj = Graph::getAdjMatrix(); + std::shared_ptr>> eulerPath = + std::make_shared>>(); + std::vector *> currentPath; + auto currentNode = *(nodeSet.begin()); + currentPath.push_back(currentNode); + while (currentPath.size() > 0) { + auto &edges = adj->at(currentNode); + // we keep removing the edges that + // have been traversed from the adjacency list + if (edges.size()) { + auto firstEdge = edges.back().second; + auto nextNodeId = firstEdge->getNodePair().second; + currentPath.push_back(nextNodeId); + currentNode = nextNodeId; + edges.pop_back(); + } else { + eulerPath->push_back(*currentNode); + currentNode = currentPath.back(); + currentPath.pop_back(); + } + } + return eulerPath; } template const std::shared_ptr> Graph::getAdjMatrix() const { - auto adj = std::make_shared>(); - auto addElementToAdjMatrix = [&adj](const Node *nodeFrom, - const Node *nodeTo, - const Edge *edge) { - std::pair *, const Edge *> elem = {nodeTo, edge}; - (*adj)[nodeFrom].push_back(std::move(elem)); - }; - for (const auto &edgeSetIt : edgeSet) { - if (edgeSetIt->isDirected().has_value() && - edgeSetIt->isDirected().value()) { - const DirectedEdge *d_edge = - dynamic_cast *>(edgeSetIt); - addElementToAdjMatrix(&(d_edge->getFrom()), &(d_edge->getTo()), - d_edge); - } else if (edgeSetIt->isDirected().has_value() && - !edgeSetIt->isDirected().value()) { - const UndirectedEdge *ud_edge = - dynamic_cast *>(edgeSetIt); - ; - addElementToAdjMatrix(&(ud_edge->getNode1()), - &(ud_edge->getNode2()), ud_edge); - addElementToAdjMatrix(&(ud_edge->getNode2()), - &(ud_edge->getNode1()), ud_edge); - } else { // is a simple edge we cannot create adj matrix - return adj; - } - } - return adj; + auto adj = std::make_shared>(); + auto addElementToAdjMatrix = [&adj](const Node *nodeFrom, + const Node *nodeTo, + const Edge *edge) { + std::pair *, const Edge *> elem = {nodeTo, edge}; + (*adj)[nodeFrom].push_back(std::move(elem)); + }; + for (const auto &edgeSetIt : edgeSet) { + if (edgeSetIt->isDirected().has_value() && + edgeSetIt->isDirected().value()) { + const DirectedEdge *d_edge = + dynamic_cast *>(edgeSetIt); + addElementToAdjMatrix(&(d_edge->getFrom()), &(d_edge->getTo()), d_edge); + } else if (edgeSetIt->isDirected().has_value() && + !edgeSetIt->isDirected().value()) { + const UndirectedEdge *ud_edge = + dynamic_cast *>(edgeSetIt); + ; + addElementToAdjMatrix(&(ud_edge->getNode1()), &(ud_edge->getNode2()), + ud_edge); + addElementToAdjMatrix(&(ud_edge->getNode2()), &(ud_edge->getNode1()), + ud_edge); + } else { // is a simple edge we cannot create adj matrix + return adj; + } + } + return adj; } template const DijkstraResult Graph::dijkstra(const Node &source, const Node &target) const { - DijkstraResult result; - auto nodeSet = Graph::getNodeSet(); - if (std::find(nodeSet.begin(), nodeSet.end(), &source) == - nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - if (std::find(nodeSet.begin(), nodeSet.end(), &target) == - nodeSet.end()) { - // check if target node exist in the graph - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } - const auto adj = getAdjMatrix(); - // n denotes the number of vertices in graph - int n = adj->size(); + DijkstraResult result; + auto nodeSet = Graph::getNodeSet(); + if (std::find(nodeSet.begin(), nodeSet.end(), &source) == nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + if (std::find(nodeSet.begin(), nodeSet.end(), &target) == nodeSet.end()) { + // check if target node exist in the graph + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + const auto adj = getAdjMatrix(); + // n denotes the number of vertices in graph + int n = adj->size(); - // setting all the distances initially to INF_DOUBLE - std::unordered_map *, double> dist; + // setting all the distances initially to INF_DOUBLE + std::unordered_map *, double> dist; - for (const auto &node : nodeSet) { - dist[node] = INF_DOUBLE; - } + for (const auto &node : nodeSet) { + dist[node] = INF_DOUBLE; + } - // creating a min heap using priority queue - // first element of pair contains the distance - // second element of pair contains the vertex - std::priority_queue *>, - std::vector *>>, - std::greater *>>> - pq; - - // pushing the source vertex 's' with 0 distance in min heap - pq.push(std::make_pair(0.0, &source)); - - // marking the distance of source as 0 - dist[&source] = 0; - - while (!pq.empty()) { - // second element of pair denotes the node / vertex - const Node *currentNode = pq.top().second; - - // first element of pair denotes the distance - double currentDist = pq.top().first; - - pq.pop(); - - // for all the reachable vertex from the currently exploring vertex - // we will try to minimize the distance - if (adj->find(currentNode) != adj->end()) { - for (const auto &elem : adj->at(currentNode)) { - // minimizing distances - if (elem.second->isWeighted().has_value() && - elem.second->isWeighted().value()) { - if (elem.second->isDirected().has_value() && - elem.second->isDirected().value()) { - const DirectedWeightedEdge *dw_edge = - dynamic_cast *>( - elem.second); - if (dw_edge->getWeight() < 0) { - result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; - return result; - } else if (currentDist + dw_edge->getWeight() < - dist[elem.first]) { - dist[elem.first] = currentDist + dw_edge->getWeight(); - pq.push(std::make_pair(dist[elem.first], elem.first)); - } - } else if (elem.second->isDirected().has_value() && - !elem.second->isDirected().value()) { - const UndirectedWeightedEdge *udw_edge = - dynamic_cast *>( - elem.second); - if (udw_edge->getWeight() < 0) { - result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; - return result; - } else if (currentDist + udw_edge->getWeight() < - dist[elem.first]) { - dist[elem.first] = currentDist + udw_edge->getWeight(); - pq.push(std::make_pair(dist[elem.first], elem.first)); - } - } else { - // ERROR it shouldn't never returned ( does not exist a Node - // Weighted and not Directed/Undirected) - result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; - return result; - } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } + // creating a min heap using priority queue + // first element of pair contains the distance + // second element of pair contains the vertex + std::priority_queue *>, + std::vector *>>, + std::greater *>>> + pq; + + // pushing the source vertex 's' with 0 distance in min heap + pq.push(std::make_pair(0.0, &source)); + + // marking the distance of source as 0 + dist[&source] = 0; + + while (!pq.empty()) { + // second element of pair denotes the node / vertex + const Node *currentNode = pq.top().second; + + // first element of pair denotes the distance + double currentDist = pq.top().first; + + pq.pop(); + + // for all the reachable vertex from the currently exploring vertex + // we will try to minimize the distance + if (adj->find(currentNode) != adj->end()) { + for (const auto &elem : adj->at(currentNode)) { + // minimizing distances + if (elem.second->isWeighted().has_value() && + elem.second->isWeighted().value()) { + if (elem.second->isDirected().has_value() && + elem.second->isDirected().value()) { + const DirectedWeightedEdge *dw_edge = + dynamic_cast *>(elem.second); + if (dw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } else if (currentDist + dw_edge->getWeight() < dist[elem.first]) { + dist[elem.first] = currentDist + dw_edge->getWeight(); + pq.push(std::make_pair(dist[elem.first], elem.first)); + } + } else if (elem.second->isDirected().has_value() && + !elem.second->isDirected().value()) { + const UndirectedWeightedEdge *udw_edge = + dynamic_cast *>(elem.second); + if (udw_edge->getWeight() < 0) { + result.errorMessage = ERR_NEGATIVE_WEIGHTED_EDGE; + return result; + } else if (currentDist + udw_edge->getWeight() < dist[elem.first]) { + dist[elem.first] = currentDist + udw_edge->getWeight(); + pq.push(std::make_pair(dist[elem.first], elem.first)); } + } else { + // ERROR it shouldn't never returned ( does not exist a Node + // Weighted and not Directed/Undirected) + result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; + return result; } - } - if (dist[&target] != INF_DOUBLE) { - result.success = true; - result.errorMessage = ""; - result.result = dist[&target]; + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; return result; } - result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; - return result; + } + } + } + if (dist[&target] != INF_DOUBLE) { + result.success = true; + result.errorMessage = ""; + result.result = dist[&target]; + return result; + } + result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; + return result; } template const BellmanFordResult Graph::bellmanford(const Node &source, const Node &target) const { - BellmanFordResult result; - result.success = false; - result.errorMessage = ""; - result.result = INF_DOUBLE; - auto nodeSet = Graph::getNodeSet(); - if (std::find(nodeSet.begin(), nodeSet.end(), &source) == - nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - if (std::find(nodeSet.begin(), nodeSet.end(), &target) == - nodeSet.end()) { - // check if target node exist in the graph - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } - // setting all the distances initially to INF_DOUBLE - std::unordered_map *, double> dist, currentDist; - // n denotes the number of vertices in graph - auto n = nodeSet.size(); - for (const auto &elem : nodeSet) { - dist[elem] = INF_DOUBLE; - currentDist[elem] = INF_DOUBLE; - } - - // marking the distance of source as 0 - dist[&source] = 0; - // set if node distances in two consecutive - // iterations remain the same. - auto earlyStopping = false; - // outer loop for vertex relaxation - for (int i = 0; i < n - 1; ++i) { - auto edgeSet = Graph::getEdgeSet(); - // inner loop for distance updates of - // each relaxation - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto edge_weight = - (dynamic_cast(edge))->getWeight(); - if (dist[elem.first] + edge_weight < dist[elem.second]) - dist[elem.second] = dist[elem.first] + edge_weight; - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } - auto flag = true; - for (const auto &[key, value] : dist) { - if (currentDist[key] != value) { - flag = false; - break; - } - } - for (const auto &[key, value] : dist) { - currentDist[key] = value; // update the current distance - } - if (flag) { - earlyStopping = true; - break; - } - } + BellmanFordResult result; + result.success = false; + result.errorMessage = ""; + result.result = INF_DOUBLE; + auto nodeSet = Graph::getNodeSet(); + if (std::find(nodeSet.begin(), nodeSet.end(), &source) == nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + if (std::find(nodeSet.begin(), nodeSet.end(), &target) == nodeSet.end()) { + // check if target node exist in the graph + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } + // setting all the distances initially to INF_DOUBLE + std::unordered_map *, double> dist, currentDist; + // n denotes the number of vertices in graph + auto n = nodeSet.size(); + for (const auto &elem : nodeSet) { + dist[elem] = INF_DOUBLE; + currentDist[elem] = INF_DOUBLE; + } - // check if there exists a negative cycle - if (!earlyStopping) { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - auto edge_weight = - (dynamic_cast(edge))->getWeight(); - if (dist[elem.first] + edge_weight < dist[elem.second]) { - result.success = true; - result.negativeCycle = true; - result.errorMessage = ""; - return result; - } - } - } + // marking the distance of source as 0 + dist[&source] = 0; + // set if node distances in two consecutive + // iterations remain the same. + auto earlyStopping = false; + // outer loop for vertex relaxation + for (int i = 0; i < n - 1; ++i) { + auto edgeSet = Graph::getEdgeSet(); + // inner loop for distance updates of + // each relaxation + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto edge_weight = (dynamic_cast(edge))->getWeight(); + if (dist[elem.first] + edge_weight < dist[elem.second]) + dist[elem.second] = dist[elem.first] + edge_weight; + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + auto flag = true; + for (const auto &[key, value] : dist) { + if (currentDist[key] != value) { + flag = false; + break; + } + } + for (const auto &[key, value] : dist) { + currentDist[key] = value; // update the current distance + } + if (flag) { + earlyStopping = true; + break; + } + } - if (dist[&target] != INF_DOUBLE) { - result.success = true; - result.errorMessage = ""; - result.negativeCycle = false; - result.result = dist[&target]; - return result; - } - result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; - result.result = -1; + // check if there exists a negative cycle + if (!earlyStopping) { + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + auto edge_weight = (dynamic_cast(edge))->getWeight(); + if (dist[elem.first] + edge_weight < dist[elem.second]) { + result.success = true; + result.negativeCycle = true; + result.errorMessage = ""; return result; + } + } + } + + if (dist[&target] != INF_DOUBLE) { + result.success = true; + result.errorMessage = ""; + result.negativeCycle = false; + result.result = dist[&target]; + return result; + } + result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; + result.result = -1; + return result; } template const FWResult Graph::floydWarshall() const { - FWResult result; - result.success = false; - result.errorMessage = ""; - std::unordered_map, double, - CXXGRAPH::pair_hash> - pairwise_dist; - const auto &nodeSet = Graph::getNodeSet(); - // create a pairwise distance matrix with distance node distances - // set to inf. Distance of node to itself is set as 0. - for (const auto &elem1 : nodeSet) { - for (const auto &elem2 : nodeSet) { - auto key = std::make_pair(elem1->getUserId(), elem2->getUserId()); - if (elem1 != elem2) - pairwise_dist[key] = INF_DOUBLE; - else - pairwise_dist[key] = 0.0; - } - } + FWResult result; + result.success = false; + result.errorMessage = ""; + std::unordered_map, double, + CXXGRAPH::pair_hash> + pairwise_dist; + const auto &nodeSet = Graph::getNodeSet(); + // create a pairwise distance matrix with distance node distances + // set to inf. Distance of node to itself is set as 0. + for (const auto &elem1 : nodeSet) { + for (const auto &elem2 : nodeSet) { + auto key = std::make_pair(elem1->getUserId(), elem2->getUserId()); + if (elem1 != elem2) + pairwise_dist[key] = INF_DOUBLE; + else + pairwise_dist[key] = 0.0; + } + } - const auto &edgeSet = Graph::getEdgeSet(); - // update the weights of nodes - // connected by edges - for (const auto &edge : edgeSet) { - const auto &elem = edge->getNodePair(); - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto edgeWeight = - (dynamic_cast(edge))->getWeight(); - auto key = std::make_pair(elem.first->getUserId(), - elem.second->getUserId()); - pairwise_dist[key] = edgeWeight; - } else { - // if an edge exists but has no weight associated - // with it, we return an error message - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } + const auto &edgeSet = Graph::getEdgeSet(); + // update the weights of nodes + // connected by edges + for (const auto &edge : edgeSet) { + const auto &elem = edge->getNodePair(); + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto edgeWeight = (dynamic_cast(edge))->getWeight(); + auto key = + std::make_pair(elem.first->getUserId(), elem.second->getUserId()); + pairwise_dist[key] = edgeWeight; + } else { + // if an edge exists but has no weight associated + // with it, we return an error message + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } - for (const auto &k : nodeSet) { - // set all vertices as source one by one - for (const auto &src : nodeSet) { - // iterate through all vertices as destination for the - // current source - auto src_k = std::make_pair(src->getUserId(), k->getUserId()); - for (const auto &dst : nodeSet) { - // If vertex k provides a shorter path than - // src to dst, update the value of - // pairwise_dist[src_to_dst] - auto src_dst = std::make_pair(src->getUserId(), dst->getUserId()); - auto k_dst = std::make_pair(k->getUserId(), dst->getUserId()); - if (pairwise_dist[src_dst] > - (pairwise_dist[src_k] + pairwise_dist[k_dst]) && - (pairwise_dist[k_dst] != INF_DOUBLE && - pairwise_dist[src_k] != INF_DOUBLE)) - pairwise_dist[src_dst] = - pairwise_dist[src_k] + pairwise_dist[k_dst]; - } - } - } + for (const auto &k : nodeSet) { + // set all vertices as source one by one + for (const auto &src : nodeSet) { + // iterate through all vertices as destination for the + // current source + auto src_k = std::make_pair(src->getUserId(), k->getUserId()); + for (const auto &dst : nodeSet) { + // If vertex k provides a shorter path than + // src to dst, update the value of + // pairwise_dist[src_to_dst] + auto src_dst = std::make_pair(src->getUserId(), dst->getUserId()); + auto k_dst = std::make_pair(k->getUserId(), dst->getUserId()); + if (pairwise_dist[src_dst] > + (pairwise_dist[src_k] + pairwise_dist[k_dst]) && + (pairwise_dist[k_dst] != INF_DOUBLE && + pairwise_dist[src_k] != INF_DOUBLE)) + pairwise_dist[src_dst] = pairwise_dist[src_k] + pairwise_dist[k_dst]; + } + } + } - result.success = true; - // presense of negative number in the diagonal indicates - // that that the graph contains a negative cycle - for (const auto &node : nodeSet) { - auto diag = std::make_pair(node->getUserId(), node->getUserId()); - if (pairwise_dist[diag] < 0.) { - result.negativeCycle = true; - return result; - } - } - result.result = std::move(pairwise_dist); - return result; + result.success = true; + // presense of negative number in the diagonal indicates + // that that the graph contains a negative cycle + for (const auto &node : nodeSet) { + auto diag = std::make_pair(node->getUserId(), node->getUserId()); + if (pairwise_dist[diag] < 0.) { + result.negativeCycle = true; + return result; + } + } + result.result = std::move(pairwise_dist); + return result; } template const MstResult Graph::prim() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - if (!isConnectedGraph()) { - result.errorMessage = ERR_NOT_STRONG_CONNECTED; - return result; - } - auto nodeSet = Graph::getNodeSet(); - auto n = nodeSet.size(); - const auto adj = Graph::getAdjMatrix(); - - // setting all the distances initially to INF_DOUBLE - std::unordered_map *, double> dist; - for (const auto &elem : (*adj)) { - dist[elem.first] = INF_DOUBLE; - } + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + if (!isConnectedGraph()) { + result.errorMessage = ERR_NOT_STRONG_CONNECTED; + return result; + } + auto nodeSet = Graph::getNodeSet(); + auto n = nodeSet.size(); + const auto adj = Graph::getAdjMatrix(); + + // setting all the distances initially to INF_DOUBLE + std::unordered_map *, double> dist; + for (const auto &elem : (*adj)) { + dist[elem.first] = INF_DOUBLE; + } - // creating a min heap using priority queue - // first element of pair contains the distance - // second element of pair contains the vertex - std::priority_queue *>, - std::vector *>>, - std::greater *>>> - pq; - - // pushing the source vertex 's' with 0 distance in min heap - auto source = *(nodeSet.begin()); - pq.push(std::make_pair(0.0, source)); - result.mstCost = 0; - std::vector doneNode; - // mark source node as done - // otherwise we get (0, 0) also in mst - doneNode.push_back(source->getId()); - // stores the parent and corresponding child node - // of the edges that are part of MST - std::unordered_map parentNode; - while (!pq.empty()) { - // second element of pair denotes the node / vertex - const Node *currentNode = pq.top().second; - auto nodeId = currentNode->getId(); - if (std::find(doneNode.begin(), doneNode.end(), nodeId) == - doneNode.end()) { - auto pair = - std::make_pair(parentNode[nodeId], currentNode->getUserId()); - result.mst.push_back(pair); - result.mstCost += pq.top().first; - doneNode.push_back(nodeId); - } + // creating a min heap using priority queue + // first element of pair contains the distance + // second element of pair contains the vertex + std::priority_queue *>, + std::vector *>>, + std::greater *>>> + pq; + + // pushing the source vertex 's' with 0 distance in min heap + auto source = *(nodeSet.begin()); + pq.push(std::make_pair(0.0, source)); + result.mstCost = 0; + std::vector doneNode; + // mark source node as done + // otherwise we get (0, 0) also in mst + doneNode.push_back(source->getId()); + // stores the parent and corresponding child node + // of the edges that are part of MST + std::unordered_map parentNode; + while (!pq.empty()) { + // second element of pair denotes the node / vertex + const Node *currentNode = pq.top().second; + auto nodeId = currentNode->getId(); + if (std::find(doneNode.begin(), doneNode.end(), nodeId) == doneNode.end()) { + auto pair = std::make_pair(parentNode[nodeId], currentNode->getUserId()); + result.mst.push_back(pair); + result.mstCost += pq.top().first; + doneNode.push_back(nodeId); + } - pq.pop(); - // for all the reachable vertex from the currently exploring vertex - // we will try to minimize the distance - if (adj->find(currentNode) != adj->end()) { - for (const auto &elem : adj->at(currentNode)) { - // minimizing distances - if (elem.second->isWeighted().has_value() && - elem.second->isWeighted().value()) { - const UndirectedWeightedEdge *udw_edge = - dynamic_cast *>( - elem.second); - if ((udw_edge->getWeight() < dist[elem.first]) && - (std::find(doneNode.begin(), doneNode.end(), - elem.first->getId()) == doneNode.end())) { - dist[elem.first] = udw_edge->getWeight(); - parentNode[elem.first->getId()] = currentNode->getUserId(); - pq.push(std::make_pair(dist[elem.first], elem.first)); - } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } + pq.pop(); + // for all the reachable vertex from the currently exploring vertex + // we will try to minimize the distance + if (adj->find(currentNode) != adj->end()) { + for (const auto &elem : adj->at(currentNode)) { + // minimizing distances + if (elem.second->isWeighted().has_value() && + elem.second->isWeighted().value()) { + const UndirectedWeightedEdge *udw_edge = + dynamic_cast *>(elem.second); + if ((udw_edge->getWeight() < dist[elem.first]) && + (std::find(doneNode.begin(), doneNode.end(), + elem.first->getId()) == doneNode.end())) { + dist[elem.first] = udw_edge->getWeight(); + parentNode[elem.first->getId()] = currentNode->getUserId(); + pq.push(std::make_pair(dist[elem.first], elem.first)); } + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; } - result.success = true; - return result; + } + } + } + result.success = true; + return result; } template const MstResult Graph::boruvka() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - const auto nodeSet = Graph::getNodeSet(); - const auto n = nodeSet.size(); - - // Use std map for storing n subsets. - std::unordered_map subsets; - - // Initially there are n different trees. - // Finally there will be one tree that will be MST - int numTrees = n; - - // check if all edges are weighted and store the weights - // in a map whose keys are the edge ids and values are the edge weights - const auto edgeSet = Graph::getEdgeSet(); - std::unordered_map edgeWeight; - for (const auto &edge : edgeSet) { - if (edge->isWeighted().has_value() && edge->isWeighted().value()) - edgeWeight[edge->getId()] = - (dynamic_cast(edge))->getWeight(); - else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + const auto nodeSet = Graph::getNodeSet(); + const auto n = nodeSet.size(); - for (const auto &node : nodeSet) { - Subset set{node->getId(), 0}; - subsets[node->getId()] = set; - } + // Use std map for storing n subsets. + std::unordered_map subsets; - result.mstCost = 0; // we will store the cost here - // exit when only 1 tree i.e. mst - while (numTrees > 1) { - // Everytime initialize cheapest map - // It stores index of the cheapest edge of subset. - std::unordered_map cheapest; - for (const auto &node : nodeSet) cheapest[node->getId()] = INT_MAX; - - // Traverse through all edges and update - // cheapest of every component - for (const auto &edge : edgeSet) { - auto elem = edge->getNodePair(); - auto edgeId = edge->getId(); - // Find sets of two corners of current edge - auto set1 = Graph::setFind(&subsets, elem.first->getId()); - auto set2 = Graph::setFind(&subsets, elem.second->getId()); - - // If two corners of current edge belong to - // same set, ignore current edge - if (set1 == set2) continue; - - // Else check if current edge is closer to previous - // cheapest edges of set1 and set2 - if (cheapest[set1] == INT_MAX || - edgeWeight[cheapest[set1]] > edgeWeight[edgeId]) - cheapest[set1] = edgeId; - - if (cheapest[set2] == INT_MAX || - edgeWeight[cheapest[set2]] > edgeWeight[edgeId]) - cheapest[set2] = edgeId; - } + // Initially there are n different trees. + // Finally there will be one tree that will be MST + int numTrees = n; - // iterate over all the vertices and add picked - // cheapest edges to MST - for (const auto &[nodeId, edgeId] : cheapest) { - // Check if cheapest for current set exists - if (edgeId != INT_MAX) { - auto cheapestNode = - Graph::getEdge(edgeId).value()->getNodePair(); - auto set1 = - Graph::setFind(&subsets, cheapestNode.first->getId()); - auto set2 = - Graph::setFind(&subsets, cheapestNode.second->getId()); - if (set1 == set2) continue; - result.mstCost += edgeWeight[edgeId]; - auto newEdgeMST = - std::make_pair(cheapestNode.first->getUserId(), - cheapestNode.second->getUserId()); - result.mst.push_back(newEdgeMST); - // take union of set1 and set2 and decrease number of trees - Graph::setUnion(&subsets, set1, set2); - numTrees--; - } - } - } - result.success = true; - return result; + // check if all edges are weighted and store the weights + // in a map whose keys are the edge ids and values are the edge weights + const auto edgeSet = Graph::getEdgeSet(); + std::unordered_map edgeWeight; + for (const auto &edge : edgeSet) { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) + edgeWeight[edge->getId()] = + (dynamic_cast(edge))->getWeight(); + else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } + + for (const auto &node : nodeSet) { + Subset set{node->getId(), 0}; + subsets[node->getId()] = set; + } + + result.mstCost = 0; // we will store the cost here + // exit when only 1 tree i.e. mst + while (numTrees > 1) { + // Everytime initialize cheapest map + // It stores index of the cheapest edge of subset. + std::unordered_map cheapest; + for (const auto &node : nodeSet) cheapest[node->getId()] = INT_MAX; + + // Traverse through all edges and update + // cheapest of every component + for (const auto &edge : edgeSet) { + auto elem = edge->getNodePair(); + auto edgeId = edge->getId(); + // Find sets of two corners of current edge + auto set1 = Graph::setFind(&subsets, elem.first->getId()); + auto set2 = Graph::setFind(&subsets, elem.second->getId()); + + // If two corners of current edge belong to + // same set, ignore current edge + if (set1 == set2) continue; + + // Else check if current edge is closer to previous + // cheapest edges of set1 and set2 + if (cheapest[set1] == INT_MAX || + edgeWeight[cheapest[set1]] > edgeWeight[edgeId]) + cheapest[set1] = edgeId; + + if (cheapest[set2] == INT_MAX || + edgeWeight[cheapest[set2]] > edgeWeight[edgeId]) + cheapest[set2] = edgeId; + } + + // iterate over all the vertices and add picked + // cheapest edges to MST + for (const auto &[nodeId, edgeId] : cheapest) { + // Check if cheapest for current set exists + if (edgeId != INT_MAX) { + auto cheapestNode = Graph::getEdge(edgeId).value()->getNodePair(); + auto set1 = Graph::setFind(&subsets, cheapestNode.first->getId()); + auto set2 = Graph::setFind(&subsets, cheapestNode.second->getId()); + if (set1 == set2) continue; + result.mstCost += edgeWeight[edgeId]; + auto newEdgeMST = std::make_pair(cheapestNode.first->getUserId(), + cheapestNode.second->getUserId()); + result.mst.push_back(newEdgeMST); + // take union of set1 and set2 and decrease number of trees + Graph::setUnion(&subsets, set1, set2); + numTrees--; + } + } + } + result.success = true; + return result; } template const MstResult Graph::kruskal() const { - MstResult result; - result.success = false; - result.errorMessage = ""; - result.mstCost = INF_DOUBLE; - if (!isUndirectedGraph()) { - result.errorMessage = ERR_DIR_GRAPH; - return result; - } - auto nodeSet = Graph::getNodeSet(); - auto n = nodeSet.size(); - - // check if all edges are weighted and store the weights - // in a map whose keys are the edge ids and values are the edge weights - auto edgeSet = Graph::getEdgeSet(); - std::priority_queue *>, - std::vector *>>, - std::greater *>>> - sortedEdges; - for (const auto &edge : edgeSet) { - if (edge->isWeighted().has_value() && edge->isWeighted().value()) { - auto weight = (dynamic_cast(edge))->getWeight(); - sortedEdges.push(std::make_pair(weight, edge)); - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - } + MstResult result; + result.success = false; + result.errorMessage = ""; + result.mstCost = INF_DOUBLE; + if (!isUndirectedGraph()) { + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + auto nodeSet = Graph::getNodeSet(); + auto n = nodeSet.size(); + + // check if all edges are weighted and store the weights + // in a map whose keys are the edge ids and values are the edge weights + auto edgeSet = Graph::getEdgeSet(); + std::priority_queue *>, + std::vector *>>, + std::greater *>>> + sortedEdges; + for (const auto &edge : edgeSet) { + if (edge->isWeighted().has_value() && edge->isWeighted().value()) { + auto weight = (dynamic_cast(edge))->getWeight(); + sortedEdges.push(std::make_pair(weight, edge)); + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + return result; + } + } - std::unordered_map subset; + std::unordered_map subset; - for (const auto &node : nodeSet) { - Subset set{node->getId(), 0}; - subset[node->getId()] = set; - } - result.mstCost = 0; - while ((!sortedEdges.empty()) && (result.mst.size() < n)) { - auto [edgeWeight, cheapestEdge] = sortedEdges.top(); - sortedEdges.pop(); - auto &[first, second] = cheapestEdge->getNodePair(); - auto set1 = Graph::setFind(&subset, first->getId()); - auto set2 = Graph::setFind(&subset, second->getId()); - if (set1 != set2) { - result.mst.push_back( - std::make_pair(first->getUserId(), second->getUserId())); - result.mstCost += edgeWeight; - } - Graph::setUnion(&subset, set1, set2); - } - result.success = true; - return result; + for (const auto &node : nodeSet) { + Subset set{node->getId(), 0}; + subset[node->getId()] = set; + } + result.mstCost = 0; + while ((!sortedEdges.empty()) && (result.mst.size() < n)) { + auto [edgeWeight, cheapestEdge] = sortedEdges.top(); + sortedEdges.pop(); + auto &[first, second] = cheapestEdge->getNodePair(); + auto set1 = Graph::setFind(&subset, first->getId()); + auto set2 = Graph::setFind(&subset, second->getId()); + if (set1 != set2) { + result.mst.push_back( + std::make_pair(first->getUserId(), second->getUserId())); + result.mstCost += edgeWeight; + } + Graph::setUnion(&subset, set1, set2); + } + result.success = true; + return result; } template BestFirstSearchResult Graph::best_first_search( const Node &source, const Node &target) const { - BestFirstSearchResult result; - auto &nodeSet = Graph::getNodeSet(); - using pq_type = std::pair *>; + BestFirstSearchResult result; + auto &nodeSet = Graph::getNodeSet(); + using pq_type = std::pair *>; - if (std::find(nodeSet.begin(), nodeSet.end(), &source) == - nodeSet.end()) { - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } + if (std::find(nodeSet.begin(), nodeSet.end(), &source) == nodeSet.end()) { + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } - if (std::find(nodeSet.begin(), nodeSet.end(), &target) == - nodeSet.end()) { - result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; - return result; - } + if (std::find(nodeSet.begin(), nodeSet.end(), &target) == nodeSet.end()) { + result.errorMessage = ERR_TARGET_NODE_NOT_IN_GRAPH; + return result; + } - auto adj = Graph::getAdjMatrix(); - std::priority_queue, - std::greater> - pq; + auto adj = Graph::getAdjMatrix(); + std::priority_queue, std::greater> pq; - std::vector> visited; - visited.push_back(source); - pq.push(std::make_pair(static_cast(0), &source)); + std::vector> visited; + visited.push_back(source); + pq.push(std::make_pair(static_cast(0), &source)); - while (!pq.empty()) { - const Node *currentNode = pq.top().second; - pq.pop(); - result.nodesInBestSearchOrder.push_back(*currentNode); + while (!pq.empty()) { + const Node *currentNode = pq.top().second; + pq.pop(); + result.nodesInBestSearchOrder.push_back(*currentNode); - if (*currentNode == target) { - break; - } - if (adj->find(currentNode) != adj->end()) { - for (const auto &elem : adj->at(currentNode)) { - if (elem.second->isWeighted().has_value()) { - if (elem.second->isDirected().has_value()) { - const DirectedWeightedEdge *dw_edge = - static_cast *>(elem.second); - if (std::find(visited.begin(), visited.end(), - *(elem.first)) == visited.end()) { - visited.push_back(*(elem.first)); - pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); - } - } else { - const UndirectedWeightedEdge *dw_edge = - static_cast *>( - elem.second); - if (std::find(visited.begin(), visited.end(), - *(elem.first)) == visited.end()) { - visited.push_back(*(elem.first)); - pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); - } - } - } else { - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - result.nodesInBestSearchOrder.clear(); - return result; - } + if (*currentNode == target) { + break; + } + if (adj->find(currentNode) != adj->end()) { + for (const auto &elem : adj->at(currentNode)) { + if (elem.second->isWeighted().has_value()) { + if (elem.second->isDirected().has_value()) { + const DirectedWeightedEdge *dw_edge = + static_cast *>(elem.second); + if (std::find(visited.begin(), visited.end(), *(elem.first)) == + visited.end()) { + visited.push_back(*(elem.first)); + pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); + } + } else { + const UndirectedWeightedEdge *dw_edge = + static_cast *>(elem.second); + if (std::find(visited.begin(), visited.end(), *(elem.first)) == + visited.end()) { + visited.push_back(*(elem.first)); + pq.push(std::make_pair(dw_edge->getWeight(), elem.first)); } } + } else { + result.errorMessage = ERR_NO_WEIGHTED_EDGE; + result.nodesInBestSearchOrder.clear(); + return result; } + } + } + } - result.success = true; - return result; + result.success = true; + return result; } template const std::vector> Graph::breadth_first_search( const Node &start) const { - // vector to keep track of visited nodes - std::vector> visited; - auto &nodeSet = Graph::getNodeSet(); - // check is exist node in the graph - if (std::find(nodeSet.begin(), nodeSet.end(), &start) == - nodeSet.end()) { - return visited; - } - const auto adj = Graph::getAdjMatrix(); - // queue that stores vertices that need to be further explored - std::queue *> tracker; - - // mark the starting node as visited - visited.push_back(start); - tracker.push(&start); - while (!tracker.empty()) { - const Node *node = tracker.front(); - tracker.pop(); - if (adj->find(node) != adj->end()) { - for (const auto &elem : adj->at(node)) { - // if the node is not visited then mark it as visited - // and push it to the queue - if (std::find(visited.begin(), visited.end(), *(elem.first)) == - visited.end()) { - visited.push_back(*(elem.first)); - tracker.push(elem.first); - } - } - } + // vector to keep track of visited nodes + std::vector> visited; + auto &nodeSet = Graph::getNodeSet(); + // check is exist node in the graph + if (std::find(nodeSet.begin(), nodeSet.end(), &start) == nodeSet.end()) { + return visited; + } + const auto adj = Graph::getAdjMatrix(); + // queue that stores vertices that need to be further explored + std::queue *> tracker; + + // mark the starting node as visited + visited.push_back(start); + tracker.push(&start); + while (!tracker.empty()) { + const Node *node = tracker.front(); + tracker.pop(); + if (adj->find(node) != adj->end()) { + for (const auto &elem : adj->at(node)) { + // if the node is not visited then mark it as visited + // and push it to the queue + if (std::find(visited.begin(), visited.end(), *(elem.first)) == + visited.end()) { + visited.push_back(*(elem.first)); + tracker.push(elem.first); } + } + } + } - return visited; + return visited; } template const std::vector> Graph::concurrency_breadth_first_search( const Node &start, size_t num_threads) const { - std::vector> bfs_result; - // check is exist node in the graph - auto &nodeSet = Graph::getNodeSet(); - if (std::find(nodeSet.begin(), nodeSet.end(), &start) == - nodeSet.end()) { - return bfs_result; - } - - std::unordered_map *, int> node_to_index; - for (const auto &node : nodeSet) { - node_to_index[node] = node_to_index.size(); - } - std::vector visited(nodeSet.size(), 0); - - // parameter limitations - if (num_threads <= 0) { - std::cout << "Error: number of threads should be greater than 0" - << std::endl; - num_threads = 2; - } - - const auto &adj = Graph::getAdjMatrix(); - // vector that stores vertices to be visit - std::vector *> level_tracker, next_level_tracker; - level_tracker.reserve(static_cast(1.0 * nodeSet.size())); - next_level_tracker.reserve(static_cast(1.0 * nodeSet.size())); - - // mark the starting node as visited - visited[node_to_index[&start]] = 1; - level_tracker.push_back(&start); - - // a worker is assigned a small part of tasks for each time - // assignments of tasks in current level and updates of tasks in next - // level are inclusive - std::mutex tracker_mutex; - std::mutex next_tracker_mutex; - std::atomic assigned_tasks = 0; - int num_tasks = 1; - // unit of task assignment, which mean assign block_size tasks to a - // worker each time - int block_size = 1; - int level = 1; - - auto extract_tasks = [&level_tracker, &tracker_mutex, &assigned_tasks, - &num_tasks, - &block_size]() -> std::pair { - /* - std::lock_guard tracker_guard(tracker_mutex); - int task_block_size = std::min(num_tasks - assigned_tasks, - block_size); std::pair task_block{assigned_tasks, - assigned_tasks + task_block_size}; assigned_tasks += task_block_size; - return task_block; - */ - int start = assigned_tasks.fetch_add(block_size); - int end = std::min(num_tasks, start + block_size); - return {start, end}; - }; + std::vector> bfs_result; + // check is exist node in the graph + auto &nodeSet = Graph::getNodeSet(); + if (std::find(nodeSet.begin(), nodeSet.end(), &start) == nodeSet.end()) { + return bfs_result; + } - auto submit_result = - [&next_level_tracker, &next_tracker_mutex]( - std::vector *> &submission) -> void { - std::lock_guard tracker_guard(next_tracker_mutex); - next_level_tracker.insert(std::end(next_level_tracker), - std::begin(submission), - std::end(submission)); - }; + std::unordered_map *, int> node_to_index; + for (const auto &node : nodeSet) { + node_to_index[node] = node_to_index.size(); + } + std::vector visited(nodeSet.size(), 0); - // worker thread sleep until it begin to search nodes of next level - std::mutex next_level_mutex; - std::condition_variable next_level_cond; - std::atomic waiting_workers = 0; - - auto bfs_worker = [&]() -> void { - // algorithm is not done - while (!level_tracker.empty()) { - // search for nodes in a level is not done - std::vector *> local_tracker; - while (1) { - auto [start_index, end_index] = extract_tasks(); - if (start_index >= end_index) { - break; - } + // parameter limitations + if (num_threads <= 0) { + std::cout << "Error: number of threads should be greater than 0" + << std::endl; + num_threads = 2; + } - for (int i = start_index; i < end_index; ++i) { - if (adj->count(level_tracker[i])) { - for (const auto &elem : adj->at(level_tracker[i])) { - int index = node_to_index[elem.first]; - if (visited[index] == 0) { - visited[index] = 1; - local_tracker.push_back(elem.first); - } - } - } + const auto &adj = Graph::getAdjMatrix(); + // vector that stores vertices to be visit + std::vector *> level_tracker, next_level_tracker; + level_tracker.reserve(static_cast(1.0 * nodeSet.size())); + next_level_tracker.reserve(static_cast(1.0 * nodeSet.size())); + + // mark the starting node as visited + visited[node_to_index[&start]] = 1; + level_tracker.push_back(&start); + + // a worker is assigned a small part of tasks for each time + // assignments of tasks in current level and updates of tasks in next + // level are inclusive + std::mutex tracker_mutex; + std::mutex next_tracker_mutex; + std::atomic assigned_tasks = 0; + int num_tasks = 1; + // unit of task assignment, which mean assign block_size tasks to a + // worker each time + int block_size = 1; + int level = 1; + + auto extract_tasks = [&level_tracker, &tracker_mutex, &assigned_tasks, + &num_tasks, &block_size]() -> std::pair { + /* + std::lock_guard tracker_guard(tracker_mutex); + int task_block_size = std::min(num_tasks - assigned_tasks, + block_size); std::pair task_block{assigned_tasks, + assigned_tasks + task_block_size}; assigned_tasks += task_block_size; + return task_block; + */ + int start = assigned_tasks.fetch_add(block_size); + int end = std::min(num_tasks, start + block_size); + return {start, end}; + }; + + auto submit_result = [&next_level_tracker, &next_tracker_mutex]( + std::vector *> &submission) -> void { + std::lock_guard tracker_guard(next_tracker_mutex); + next_level_tracker.insert(std::end(next_level_tracker), + std::begin(submission), std::end(submission)); + }; + + // worker thread sleep until it begin to search nodes of next level + std::mutex next_level_mutex; + std::condition_variable next_level_cond; + std::atomic waiting_workers = 0; + + auto bfs_worker = [&]() -> void { + // algorithm is not done + while (!level_tracker.empty()) { + // search for nodes in a level is not done + std::vector *> local_tracker; + while (1) { + auto [start_index, end_index] = extract_tasks(); + if (start_index >= end_index) { + break; + } + + for (int i = start_index; i < end_index; ++i) { + if (adj->count(level_tracker[i])) { + for (const auto &elem : adj->at(level_tracker[i])) { + int index = node_to_index[elem.first]; + if (visited[index] == 0) { + visited[index] = 1; + local_tracker.push_back(elem.first); } } + } + } + } - // submit local result to global result - if (!local_tracker.empty()) { - submit_result(local_tracker); - } - - // last worker need to do preparation for the next iteration - int cur_level = level; - if (num_threads == 1 + waiting_workers.fetch_add(1)) { - swap(level_tracker, next_level_tracker); - next_level_tracker.clear(); - - // adjust block_size according to number of nodes in next level - block_size = 4; - if (level_tracker.size() <= num_threads * 4) { - block_size = std::max( - 1, static_cast( - std::ceil(static_cast(level_tracker.size()) / - num_threads))); - } else if (level_tracker.size() >= num_threads * 64) { - block_size = 16; - } + // submit local result to global result + if (!local_tracker.empty()) { + submit_result(local_tracker); + } - num_tasks = level_tracker.size(); - waiting_workers = 0; - assigned_tasks = 0; - level = level + 1; - next_level_cond.notify_all(); - } else { - // not to wait if last worker reachs last statement before notify - // all or even further - std::unique_lock next_level_lock(next_level_mutex); - next_level_cond.wait(next_level_lock, [&level, cur_level]() { - return level != cur_level; - }); - } - } - }; + // last worker need to do preparation for the next iteration + int cur_level = level; + if (num_threads == 1 + waiting_workers.fetch_add(1)) { + swap(level_tracker, next_level_tracker); + next_level_tracker.clear(); + + // adjust block_size according to number of nodes in next level + block_size = 4; + if (level_tracker.size() <= num_threads * 4) { + block_size = std::max( + 1, static_cast(std::ceil( + static_cast(level_tracker.size()) / num_threads))); + } else if (level_tracker.size() >= num_threads * 64) { + block_size = 16; + } + + num_tasks = level_tracker.size(); + waiting_workers = 0; + assigned_tasks = 0; + level = level + 1; + next_level_cond.notify_all(); + } else { + // not to wait if last worker reachs last statement before notify + // all or even further + std::unique_lock next_level_lock(next_level_mutex); + next_level_cond.wait(next_level_lock, [&level, cur_level]() { + return level != cur_level; + }); + } + } + }; - std::vector workers; - for (int i = 0; i < num_threads - 1; ++i) { - workers.emplace_back(std::thread(bfs_worker)); - } - bfs_worker(); + std::vector workers; + for (int i = 0; i < num_threads - 1; ++i) { + workers.emplace_back(std::thread(bfs_worker)); + } + bfs_worker(); - for (auto &worker : workers) { - if (worker.joinable()) { - worker.join(); - } - } + for (auto &worker : workers) { + if (worker.joinable()) { + worker.join(); + } + } - for (const auto &visited_node : nodeSet) { - if (visited[node_to_index[visited_node]] != 0) { - bfs_result.push_back(*visited_node); - } - } + for (const auto &visited_node : nodeSet) { + if (visited[node_to_index[visited_node]] != 0) { + bfs_result.push_back(*visited_node); + } + } - return bfs_result; + return bfs_result; } template const std::vector> Graph::depth_first_search( const Node &start) const { - // vector to keep track of visited nodes - std::vector> visited; - auto nodeSet = Graph::getNodeSet(); - // check is exist node in the graph - if (std::find(nodeSet.begin(), nodeSet.end(), &start) == - nodeSet.end()) { - return visited; + // vector to keep track of visited nodes + std::vector> visited; + auto nodeSet = Graph::getNodeSet(); + // check is exist node in the graph + if (std::find(nodeSet.begin(), nodeSet.end(), &start) == nodeSet.end()) { + return visited; + } + const auto adj = Graph::getAdjMatrix(); + std::function>, const Node &, + std::vector> &)> + explore; + explore = [&explore](const std::shared_ptr> adj, + const Node &node, + std::vector> &visited) -> void { + visited.push_back(node); + if (adj->find(&node) != adj->end()) { + for (const auto &x : adj->at(&node)) { + if (std::find(visited.begin(), visited.end(), *(x.first)) == + visited.end()) { + explore(adj, *(x.first), visited); } - const auto adj = Graph::getAdjMatrix(); - std::function>, - const Node &, std::vector> &)> - explore; - explore = [&explore](const std::shared_ptr> adj, - const Node &node, - std::vector> &visited) -> void { - visited.push_back(node); - if (adj->find(&node) != adj->end()) { - for (const auto &x : adj->at(&node)) { - if (std::find(visited.begin(), visited.end(), *(x.first)) == - visited.end()) { - explore(adj, *(x.first), visited); - } - } - } - }; - explore(adj, start, visited); + } + } + }; + explore(adj, start, visited); - return visited; + return visited; } template bool Graph::isCyclicDirectedGraphDFS() const { - if (!isDirectedGraph()) { - return false; - } - enum nodeStates : uint8_t { not_visited, in_stack, visited }; - auto nodeSet = Graph::getNodeSet(); - auto adjMatrix = Graph::getAdjMatrix(); - - /* State of the node. - * - * It is a vector of "nodeStates" which represents the state node is in. - * It can take only 3 values: "not_visited", "in_stack", and "visited". - * - * Initially, all nodes are in "not_visited" state. - */ - std::unordered_map state; - for (const auto &node : nodeSet) { - state[node->getId()] = not_visited; - } - int stateCounter = 0; - - // Start visiting each node. - for (const auto &node : nodeSet) { - // If a node is not visited, only then check for presence of cycle. - // There is no need to check for presence of cycle for a visited - // node as it has already been checked for presence of cycle. - if (state[node->getId()] == not_visited) { - // Check for cycle. - std::function>, - std::unordered_map &, - const Node *)> - isCyclicDFSHelper; - isCyclicDFSHelper = - [this, &isCyclicDFSHelper]( - const std::shared_ptr> adjMatrix, - std::unordered_map &states, - const Node *node) { - // Add node "in_stack" state. - states[node->getId()] = in_stack; - - // If the node has children, then recursively visit all - // children of the node. - auto const it = adjMatrix->find(node); - if (it != adjMatrix->end()) { - for (const auto &child : it->second) { - // If state of child node is "not_visited", evaluate that - // child for presence of cycle. - auto state_of_child = - states.at((std::get<0>(child))->getId()); - if (state_of_child == not_visited) { - if (isCyclicDFSHelper(adjMatrix, states, - std::get<0>(child))) { - return true; - } - } else if (state_of_child == in_stack) { - // If child node was "in_stack", then that means that - // there is a cycle in the graph. Return true for - // presence of the cycle. - return true; - } - } - } + if (!isDirectedGraph()) { + return false; + } + enum nodeStates : uint8_t { not_visited, in_stack, visited }; + auto nodeSet = Graph::getNodeSet(); + auto adjMatrix = Graph::getAdjMatrix(); - // Current node has been evaluated for the presence of cycle - // and had no cycle. Mark current node as "visited". - states[node->getId()] = visited; - // Return that current node didn't result in any cycles. - return false; - }; - if (isCyclicDFSHelper(adjMatrix, state, node)) { - return true; + /* State of the node. + * + * It is a vector of "nodeStates" which represents the state node is in. + * It can take only 3 values: "not_visited", "in_stack", and "visited". + * + * Initially, all nodes are in "not_visited" state. + */ + std::unordered_map state; + for (const auto &node : nodeSet) { + state[node->getId()] = not_visited; + } + int stateCounter = 0; + + // Start visiting each node. + for (const auto &node : nodeSet) { + // If a node is not visited, only then check for presence of cycle. + // There is no need to check for presence of cycle for a visited + // node as it has already been checked for presence of cycle. + if (state[node->getId()] == not_visited) { + // Check for cycle. + std::function>, + std::unordered_map &, + const Node *)> + isCyclicDFSHelper; + isCyclicDFSHelper = + [this, &isCyclicDFSHelper]( + const std::shared_ptr> adjMatrix, + std::unordered_map &states, + const Node *node) { + // Add node "in_stack" state. + states[node->getId()] = in_stack; + + // If the node has children, then recursively visit all + // children of the node. + auto const it = adjMatrix->find(node); + if (it != adjMatrix->end()) { + for (const auto &child : it->second) { + // If state of child node is "not_visited", evaluate that + // child for presence of cycle. + auto state_of_child = states.at((std::get<0>(child))->getId()); + if (state_of_child == not_visited) { + if (isCyclicDFSHelper(adjMatrix, states, + std::get<0>(child))) { + return true; + } + } else if (state_of_child == in_stack) { + // If child node was "in_stack", then that means that + // there is a cycle in the graph. Return true for + // presence of the cycle. + return true; + } + } } - } - } - // All nodes have been safely traversed, that means there is no cycle in - // the graph. Return false. - return false; + // Current node has been evaluated for the presence of cycle + // and had no cycle. Mark current node as "visited". + states[node->getId()] = visited; + // Return that current node didn't result in any cycles. + return false; + }; + if (isCyclicDFSHelper(adjMatrix, state, node)) { + return true; + } + } + } + + // All nodes have been safely traversed, that means there is no cycle in + // the graph. Return false. + return false; } template bool Graph::containsCycle(const T_EdgeSet *edgeSet) const { - std::unordered_map subset; - // initialize the subset parent and rank values - for (const auto &edge : *edgeSet) { - auto &[first, second] = edge->getNodePair(); - std::vector nodeId(2); - nodeId.push_back(first->getId()); - nodeId.push_back(second->getId()); - for (const auto &id : nodeId) { - auto nodeExists = [id](const auto &it) { - return (id == (it.second).parent); - }; - - if (std::find_if(subset.begin(), subset.end(), nodeExists) == - subset.end()) { - Subset set; - set.parent = id; - set.rank = 0; - subset[id] = set; - } - } - } - return Graph::containsCycle(edgeSet, &subset); + std::unordered_map subset; + // initialize the subset parent and rank values + for (const auto &edge : *edgeSet) { + auto &[first, second] = edge->getNodePair(); + std::vector nodeId(2); + nodeId.push_back(first->getId()); + nodeId.push_back(second->getId()); + for (const auto &id : nodeId) { + auto nodeExists = [id](const auto &it) { + return (id == (it.second).parent); + }; + + if (std::find_if(subset.begin(), subset.end(), nodeExists) == + subset.end()) { + Subset set; + set.parent = id; + set.rank = 0; + subset[id] = set; + } + } + } + return Graph::containsCycle(edgeSet, &subset); } template bool Graph::containsCycle( const T_EdgeSet *edgeSet, std::unordered_map *subset) const { - for (const auto &edge : *edgeSet) { - auto &[first, second] = edge->getNodePair(); - auto set1 = Graph::setFind(subset, first->getId()); - auto set2 = Graph::setFind(subset, second->getId()); - if (set1 == set2) return true; - Graph::setUnion(subset, set1, set2); - } - return false; + for (const auto &edge : *edgeSet) { + auto &[first, second] = edge->getNodePair(); + auto set1 = Graph::setFind(subset, first->getId()); + auto set2 = Graph::setFind(subset, second->getId()); + if (set1 == set2) return true; + Graph::setUnion(subset, set1, set2); + } + return false; } template bool Graph::isCyclicDirectedGraphBFS() const { - if (!isDirectedGraph()) { - return false; - } - const auto adjMatrix = Graph::getAdjMatrix(); - auto nodeSet = Graph::getNodeSet(); + if (!isDirectedGraph()) { + return false; + } + const auto adjMatrix = Graph::getAdjMatrix(); + auto nodeSet = Graph::getNodeSet(); - std::unordered_map indegree; - for (const auto &node : nodeSet) { - indegree[node->getId()] = 0; - } - // Calculate the indegree i.e. the number of incident edges to the node. - for (auto const &list : (*adjMatrix)) { - auto children = list.second; - for (auto const &child : children) { - indegree[std::get<0>(child)->getId()]++; - } - } + std::unordered_map indegree; + for (const auto &node : nodeSet) { + indegree[node->getId()] = 0; + } + // Calculate the indegree i.e. the number of incident edges to the node. + for (auto const &list : (*adjMatrix)) { + auto children = list.second; + for (auto const &child : children) { + indegree[std::get<0>(child)->getId()]++; + } + } - std::queue *> can_be_solved; - for (const auto &node : nodeSet) { - // If a node doesn't have any input edges, then that node will - // definately not result in a cycle and can be visited safely. - if (!indegree[node->getId()]) { - can_be_solved.emplace(&(*node)); - } - } + std::queue *> can_be_solved; + for (const auto &node : nodeSet) { + // If a node doesn't have any input edges, then that node will + // definately not result in a cycle and can be visited safely. + if (!indegree[node->getId()]) { + can_be_solved.emplace(&(*node)); + } + } - // Vertices that need to be traversed. - auto remain = Graph::getNodeSet().size(); - // While there are safe nodes that we can visit. - while (!can_be_solved.empty()) { - auto solved = can_be_solved.front(); - // Visit the node. - can_be_solved.pop(); - // Decrease number of nodes that need to be traversed. - remain--; - - // Visit all the children of the visited node. - auto it = adjMatrix->find(solved); - if (it != adjMatrix->end()) { - for (const auto &child : it->second) { - // Check if we can visited the node safely. - if (--indegree[std::get<0>(child)->getId()] == 0) { - // if node can be visited safely, then add that node to - // the visit queue. - can_be_solved.emplace(std::get<0>(child)); - } - } - } + // Vertices that need to be traversed. + auto remain = Graph::getNodeSet().size(); + // While there are safe nodes that we can visit. + while (!can_be_solved.empty()) { + auto solved = can_be_solved.front(); + // Visit the node. + can_be_solved.pop(); + // Decrease number of nodes that need to be traversed. + remain--; + + // Visit all the children of the visited node. + auto it = adjMatrix->find(solved); + if (it != adjMatrix->end()) { + for (const auto &child : it->second) { + // Check if we can visited the node safely. + if (--indegree[std::get<0>(child)->getId()] == 0) { + // if node can be visited safely, then add that node to + // the visit queue. + can_be_solved.emplace(std::get<0>(child)); } + } + } + } - // If there are still nodes that we can't visit, then it means that - // there is a cycle and return true, else return false. - return !(remain == 0); + // If there are still nodes that we can't visit, then it means that + // there is a cycle and return true, else return false. + return !(remain == 0); } template bool Graph::isDirectedGraph() const { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - if (!(edge->isDirected().has_value() && edge->isDirected().value())) { - // Found Undirected Edge - return false; - } - } - // No Undirected Edge - return true; + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + if (!(edge->isDirected().has_value() && edge->isDirected().value())) { + // Found Undirected Edge + return false; + } + } + // No Undirected Edge + return true; } template bool Graph::isUndirectedGraph() const { - auto edgeSet = Graph::getEdgeSet(); - for (const auto &edge : edgeSet) { - if ((edge->isDirected().has_value() && edge->isDirected().value())) { - // Found Directed Edge - return false; - } - } - // No Directed Edge - return true; + auto edgeSet = Graph::getEdgeSet(); + for (const auto &edge : edgeSet) { + if ((edge->isDirected().has_value() && edge->isDirected().value())) { + // Found Directed Edge + return false; + } + } + // No Directed Edge + return true; } template bool Graph::isConnectedGraph() const { - if (!isUndirectedGraph()) { - return false; - } else { - auto nodeSet = getNodeSet(); - const auto adjMatrix = getAdjMatrix(); - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - std::function *)> dfs_helper = - [this, &adjMatrix, &visited, &dfs_helper](const Node *source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*adjMatrix)[source].size(); i++) { - const Node *neighbor = (*adjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); - } - } - }; - // call dfs_helper for the first node - dfs_helper(*(nodeSet.begin())); - - // check if all the nodes are visited - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - return false; + if (!isUndirectedGraph()) { + return false; + } else { + auto nodeSet = getNodeSet(); + const auto adjMatrix = getAdjMatrix(); + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; + } + std::function *)> dfs_helper = + [this, &adjMatrix, &visited, &dfs_helper](const Node *source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*adjMatrix)[source].size(); i++) { + const Node *neighbor = (*adjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); } } - return true; - } + }; + // call dfs_helper for the first node + dfs_helper(*(nodeSet.begin())); + + // check if all the nodes are visited + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + return false; + } + } + return true; + } } template bool Graph::isStronglyConnectedGraph() const { - if (!isDirectedGraph()) { - return false; - } else { - auto nodeSet = getNodeSet(); - const auto adjMatrix = getAdjMatrix(); - for (const auto &start_node : nodeSet) { - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - std::function *)> dfs_helper = - [this, &adjMatrix, &visited, - &dfs_helper](const Node *source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*adjMatrix)[source].size(); i++) { - const Node *neighbor = (*adjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); - } - } - }; - // call dfs_helper for the first node - dfs_helper(start_node); - - // check if all the nodes are visited - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - return false; + if (!isDirectedGraph()) { + return false; + } else { + auto nodeSet = getNodeSet(); + const auto adjMatrix = getAdjMatrix(); + for (const auto &start_node : nodeSet) { + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; + } + std::function *)> dfs_helper = + [this, &adjMatrix, &visited, &dfs_helper](const Node *source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*adjMatrix)[source].size(); i++) { + const Node *neighbor = (*adjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); } } - } - return true; + }; + // call dfs_helper for the first node + dfs_helper(start_node); + + // check if all the nodes are visited + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + return false; } + } + } + return true; + } } template TopoSortResult Graph::topologicalSort() const { - TopoSortResult result; - result.success = false; - - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else if (isCyclicDirectedGraphBFS()) { - result.errorMessage = ERR_CYCLIC_GRAPH; - return result; - } else { - const auto &adjMatrix = getAdjMatrix(); - const auto &nodeSet = getNodeSet(); - std::unordered_map *, bool> visited; - - std::function *)> postorder_helper = - [&postorder_helper, &adjMatrix, &visited, - &result](const Node *curNode) { - visited[curNode] = true; - - if (adjMatrix->find(curNode) != adjMatrix->end()) { - for (const auto &edge : adjMatrix->at(curNode)) { - const auto &nextNode = edge.first; - if (false == visited[nextNode]) { - postorder_helper(nextNode); - } - } - } + TopoSortResult result; + result.success = false; + + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else if (isCyclicDirectedGraphBFS()) { + result.errorMessage = ERR_CYCLIC_GRAPH; + return result; + } else { + const auto &adjMatrix = getAdjMatrix(); + const auto &nodeSet = getNodeSet(); + std::unordered_map *, bool> visited; + + std::function *)> postorder_helper = + [&postorder_helper, &adjMatrix, &visited, + &result](const Node *curNode) { + visited[curNode] = true; + + if (adjMatrix->find(curNode) != adjMatrix->end()) { + for (const auto &edge : adjMatrix->at(curNode)) { + const auto &nextNode = edge.first; + if (false == visited[nextNode]) { + postorder_helper(nextNode); + } + } + } - result.nodesInTopoOrder.push_back(*curNode); - }; + result.nodesInTopoOrder.push_back(*curNode); + }; - int numNodes = adjMatrix->size(); - result.nodesInTopoOrder.reserve(numNodes); + int numNodes = adjMatrix->size(); + result.nodesInTopoOrder.reserve(numNodes); - for (const auto &node : nodeSet) { - if (false == visited[node]) { - postorder_helper(node); - } - } + for (const auto &node : nodeSet) { + if (false == visited[node]) { + postorder_helper(node); + } + } - result.success = true; - std::reverse(result.nodesInTopoOrder.begin(), - result.nodesInTopoOrder.end()); - return result; - } + result.success = true; + std::reverse(result.nodesInTopoOrder.begin(), + result.nodesInTopoOrder.end()); + return result; + } } template TopoSortResult Graph::kahn() const { - TopoSortResult result; + TopoSortResult result; - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else { - const auto adjMatrix = Graph::getAdjMatrix(); - const auto nodeSet = Graph::getNodeSet(); - result.nodesInTopoOrder.reserve(adjMatrix->size()); + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else { + const auto adjMatrix = Graph::getAdjMatrix(); + const auto nodeSet = Graph::getNodeSet(); + result.nodesInTopoOrder.reserve(adjMatrix->size()); - std::unordered_map indegree; - for (const auto &node : nodeSet) { - indegree[node->getId()] = 0; - } - for (const auto &list : *adjMatrix) { - auto children = list.second; - for (const auto &child : children) { - indegree[std::get<0>(child)->getId()]++; - } - } + std::unordered_map indegree; + for (const auto &node : nodeSet) { + indegree[node->getId()] = 0; + } + for (const auto &list : *adjMatrix) { + auto children = list.second; + for (const auto &child : children) { + indegree[std::get<0>(child)->getId()]++; + } + } - std::queue *> topologicalOrder; + std::queue *> topologicalOrder; - for (const auto &node : nodeSet) { - if (!indegree[node->getId()]) { - topologicalOrder.emplace(node); - } - } + for (const auto &node : nodeSet) { + if (!indegree[node->getId()]) { + topologicalOrder.emplace(node); + } + } - size_t visited = 0; - while (!topologicalOrder.empty()) { - const Node *currentNode = topologicalOrder.front(); - topologicalOrder.pop(); - result.nodesInTopoOrder.push_back(*currentNode); + size_t visited = 0; + while (!topologicalOrder.empty()) { + const Node *currentNode = topologicalOrder.front(); + topologicalOrder.pop(); + result.nodesInTopoOrder.push_back(*currentNode); - if (adjMatrix->find(currentNode) != adjMatrix->end()) { - for (const auto &child : adjMatrix->at(currentNode)) { - if (--indegree[std::get<0>(child)->getId()] == 0) { - topologicalOrder.emplace(std::get<0>(child)); - } - } - } - visited++; + if (adjMatrix->find(currentNode) != adjMatrix->end()) { + for (const auto &child : adjMatrix->at(currentNode)) { + if (--indegree[std::get<0>(child)->getId()] == 0) { + topologicalOrder.emplace(std::get<0>(child)); } + } + } + visited++; + } - if (visited != nodeSet.size()) { - result.errorMessage = ERR_CYCLIC_GRAPH; - result.nodesInTopoOrder.clear(); - return result; - } + if (visited != nodeSet.size()) { + result.errorMessage = ERR_CYCLIC_GRAPH; + result.nodesInTopoOrder.clear(); + return result; + } - result.success = true; - return result; - } + result.success = true; + return result; + } } template SCCResult Graph::kosaraju() const { - SCCResult result; - result.success = false; + SCCResult result; + result.success = false; - if (!isDirectedGraph()) { - result.errorMessage = ERR_UNDIR_GRAPH; - return result; - } else { - auto nodeSet = getNodeSet(); - const auto adjMatrix = getAdjMatrix(); - // created visited map - std::unordered_map visited; - for (const auto &node : nodeSet) { - visited[node->getId()] = false; - } - - std::stack *> st; - std::function *)> dfs_helper = - [this, &adjMatrix, &visited, &dfs_helper, - &st](const Node *source) { - // mark the vertex visited - visited[source->getId()] = true; - - // travel the neighbors - for (int i = 0; i < (*adjMatrix)[source].size(); i++) { - const Node *neighbor = (*adjMatrix)[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper(neighbor); - } - } - - st.push(source); - }; + if (!isDirectedGraph()) { + result.errorMessage = ERR_UNDIR_GRAPH; + return result; + } else { + auto nodeSet = getNodeSet(); + const auto adjMatrix = getAdjMatrix(); + // created visited map + std::unordered_map visited; + for (const auto &node : nodeSet) { + visited[node->getId()] = false; + } - for (const auto &node : nodeSet) { - if (visited[node->getId()] == false) { - dfs_helper(node); + std::stack *> st; + std::function *)> dfs_helper = + [this, &adjMatrix, &visited, &dfs_helper, &st](const Node *source) { + // mark the vertex visited + visited[source->getId()] = true; + + // travel the neighbors + for (int i = 0; i < (*adjMatrix)[source].size(); i++) { + const Node *neighbor = (*adjMatrix)[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper(neighbor); } } - // construct the transpose of the given graph - AdjacencyMatrix rev; - auto addElementToAdjMatrix = [&rev](const Node *nodeFrom, - const Node *nodeTo, - const Edge *edge) { - std::pair *, const Edge *> elem = {nodeTo, edge}; - rev[nodeFrom].push_back(std::move(elem)); - }; - for (const auto &edgeSetIt : edgeSet) { - const DirectedEdge *d_edge = - dynamic_cast *>(edgeSetIt); - // Add the reverse edge to the reverse adjacency matrix - addElementToAdjMatrix(&(d_edge->getTo()), &(d_edge->getFrom()), - d_edge); - } + st.push(source); + }; - visited.clear(); - - std::function *, std::vector> &)> - dfs_helper1 = - [this, &rev, &visited, &dfs_helper1]( - const Node *source, std::vector> &comp) { - // mark the vertex visited - visited[source->getId()] = true; - // Add the current vertex to the strongly connected - // component - comp.push_back(*source); - - // travel the neighbors - for (int i = 0; i < rev[source].size(); i++) { - const Node *neighbor = rev[source].at(i).first; - if (visited[neighbor->getId()] == false) { - // make recursive call from neighbor - dfs_helper1(neighbor, comp); - } - } - }; - - while (st.size() != 0) { - auto rem = st.top(); - st.pop(); - if (visited[rem->getId()] == false) { - std::vector> comp; - dfs_helper1(rem, comp); - result.stronglyConnectedComps.push_back(comp); + for (const auto &node : nodeSet) { + if (visited[node->getId()] == false) { + dfs_helper(node); + } + } + + // construct the transpose of the given graph + AdjacencyMatrix rev; + auto addElementToAdjMatrix = [&rev](const Node *nodeFrom, + const Node *nodeTo, + const Edge *edge) { + std::pair *, const Edge *> elem = {nodeTo, edge}; + rev[nodeFrom].push_back(std::move(elem)); + }; + for (const auto &edgeSetIt : edgeSet) { + const DirectedEdge *d_edge = + dynamic_cast *>(edgeSetIt); + // Add the reverse edge to the reverse adjacency matrix + addElementToAdjMatrix(&(d_edge->getTo()), &(d_edge->getFrom()), d_edge); + } + + visited.clear(); + + std::function *, std::vector> &)> dfs_helper1 = + [this, &rev, &visited, &dfs_helper1](const Node *source, + std::vector> &comp) { + // mark the vertex visited + visited[source->getId()] = true; + // Add the current vertex to the strongly connected + // component + comp.push_back(*source); + + // travel the neighbors + for (int i = 0; i < rev[source].size(); i++) { + const Node *neighbor = rev[source].at(i).first; + if (visited[neighbor->getId()] == false) { + // make recursive call from neighbor + dfs_helper1(neighbor, comp); } } + }; - result.success = true; - return result; - } + while (st.size() != 0) { + auto rem = st.top(); + st.pop(); + if (visited[rem->getId()] == false) { + std::vector> comp; + dfs_helper1(rem, comp); + result.stronglyConnectedComps.push_back(comp); + } + } + + result.success = true; + return result; + } } template const DialResult Graph::dial(const Node &source, int maxWeight) const { - DialResult result; - result.success = false; - - const auto adj = getAdjMatrix(); - auto nodeSet = getNodeSet(); - - if (std::find(nodeSet.begin(), nodeSet.end(), &source) == - nodeSet.end()) { - // check if source node exist in the graph - result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; - return result; - } - /* With each distance, iterator to that vertex in - its bucket is stored so that vertex can be deleted - in O(1) at time of updation. So - dist[i].first = distance of ith vertex from src vertex - dits[i].second = vertex i in bucket number */ - unsigned int V = nodeSet.size(); - std::unordered_map *, std::pair *>> - dist; - - // Initialize all distances as infinite (INF) - for (const auto &node : nodeSet) { - dist[&(*node)].first = std::numeric_limits::max(); - } - - // Create buckets B[]. - // B[i] keep vertex of distance label i - std::deque *> B[maxWeight * V + 1]; + DialResult result; + result.success = false; - B[0].push_back(&source); - dist[&source].first = 0; + const auto adj = getAdjMatrix(); + auto nodeSet = getNodeSet(); - int idx = 0; - while (1) { - // Go sequentially through buckets till one non-empty - // bucket is found - while (B[idx].size() == 0 && idx < maxWeight * V) { - idx++; - } + if (std::find(nodeSet.begin(), nodeSet.end(), &source) == nodeSet.end()) { + // check if source node exist in the graph + result.errorMessage = ERR_SOURCE_NODE_NOT_IN_GRAPH; + return result; + } + /* With each distance, iterator to that vertex in + its bucket is stored so that vertex can be deleted + in O(1) at time of updation. So + dist[i].first = distance of ith vertex from src vertex + dits[i].second = vertex i in bucket number */ + unsigned int V = nodeSet.size(); + std::unordered_map *, std::pair *>> dist; + + // Initialize all distances as infinite (INF) + for (const auto &node : nodeSet) { + dist[&(*node)].first = std::numeric_limits::max(); + } - // If all buckets are empty, we are done. - if (idx == maxWeight * V) { - break; - } + // Create buckets B[]. + // B[i] keep vertex of distance label i + std::deque *> B[maxWeight * V + 1]; - // Take top vertex from bucket and pop it - auto u = B[idx].front(); - B[idx].pop_front(); - - // Process all adjacents of extracted vertex 'u' and - // update their distanced if required. - for (const auto &i : (*adj)[u]) { - auto v = i.first; - int weight = 0; - if (i.second->isWeighted().has_value() && - i.second->isWeighted().value()) { - if (i.second->isDirected().has_value() && - i.second->isDirected().value()) { - const DirectedWeightedEdge *dw_edge = - dynamic_cast *>(i.second); - weight = dw_edge->getWeight(); - } else if (i.second->isDirected().has_value() && - !i.second->isDirected().value()) { - const UndirectedWeightedEdge *udw_edge = - dynamic_cast *>(i.second); - weight = udw_edge->getWeight(); - } else { - // ERROR it shouldn't never returned ( does not exist a Node - // Weighted and not Directed/Undirected) - result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; - return result; - } - } else { - // No Weighted Edge - result.errorMessage = ERR_NO_WEIGHTED_EDGE; - return result; - } - auto u_i = std::find_if( - dist.begin(), dist.end(), - [u](std::pair *, - std::pair *>> const &it) { - return (*u == *(it.first)); - }); - - auto v_i = std::find_if( - dist.begin(), dist.end(), - [v](std::pair *, - std::pair *>> const &it) { - return (*v == *(it.first)); - }); - long du = u_i->second.first; - long dv = v_i->second.first; - - // If there is shorted path to v through u. - if (dv > du + weight) { - // If dv is not INF then it must be in B[dv] - // bucket, so erase its entry using iterator - // in O(1) - if (dv != std::numeric_limits::max()) { - auto findIter = - std::find(B[dv].begin(), B[dv].end(), dist[v].second); - B[dv].erase(findIter); - } + B[0].push_back(&source); + dist[&source].first = 0; - // updating the distance - dist[v].first = du + weight; - dv = dist[v].first; + int idx = 0; + while (1) { + // Go sequentially through buckets till one non-empty + // bucket is found + while (B[idx].size() == 0 && idx < maxWeight * V) { + idx++; + } - // pushing vertex v into updated distance's bucket - B[dv].push_front(v); + // If all buckets are empty, we are done. + if (idx == maxWeight * V) { + break; + } - // storing updated iterator in dist[v].second - dist[v].second = *(B[dv].begin()); - } - } - } - for (const auto &dist_i : dist) { - result.minDistanceMap[dist_i.first->getId()] = dist_i.second.first; + // Take top vertex from bucket and pop it + auto u = B[idx].front(); + B[idx].pop_front(); + + // Process all adjacents of extracted vertex 'u' and + // update their distanced if required. + for (const auto &i : (*adj)[u]) { + auto v = i.first; + int weight = 0; + if (i.second->isWeighted().has_value() && + i.second->isWeighted().value()) { + if (i.second->isDirected().has_value() && + i.second->isDirected().value()) { + const DirectedWeightedEdge *dw_edge = + dynamic_cast *>(i.second); + weight = dw_edge->getWeight(); + } else if (i.second->isDirected().has_value() && + !i.second->isDirected().value()) { + const UndirectedWeightedEdge *udw_edge = + dynamic_cast *>(i.second); + weight = udw_edge->getWeight(); + } else { + // ERROR it shouldn't never returned ( does not exist a Node + // Weighted and not Directed/Undirected) + result.errorMessage = ERR_NO_DIR_OR_UNDIR_EDGE; + return result; } - result.success = true; - + } else { + // No Weighted Edge + result.errorMessage = ERR_NO_WEIGHTED_EDGE; return result; + } + auto u_i = std::find_if( + dist.begin(), dist.end(), + [u](std::pair *, std::pair *>> const + &it) { return (*u == *(it.first)); }); + + auto v_i = std::find_if( + dist.begin(), dist.end(), + [v](std::pair *, std::pair *>> const + &it) { return (*v == *(it.first)); }); + long du = u_i->second.first; + long dv = v_i->second.first; + + // If there is shorted path to v through u. + if (dv > du + weight) { + // If dv is not INF then it must be in B[dv] + // bucket, so erase its entry using iterator + // in O(1) + if (dv != std::numeric_limits::max()) { + auto findIter = std::find(B[dv].begin(), B[dv].end(), dist[v].second); + B[dv].erase(findIter); + } + + // updating the distance + dist[v].first = du + weight; + dv = dist[v].first; + + // pushing vertex v into updated distance's bucket + B[dv].push_front(v); + + // storing updated iterator in dist[v].second + dist[v].second = *(B[dv].begin()); + } + } + } + for (const auto &dist_i : dist) { + result.minDistanceMap[dist_i.first->getId()] = dist_i.second.first; + } + result.success = true; + + return result; } template double Graph::fordFulkersonMaxFlow(const Node &source, const Node &target) const { - if (!isDirectedGraph()) { - return -1; - } - double maxFlow = 0; - std::unordered_map *, const Node *> parent; - std::map *, std::map *, double>> weightMap; - // build weight map - auto edgeSet = this->getEdgeSet(); - for (const auto &edge : edgeSet) { - // The Edge are all Directed at this point because is checked at the - // start - if (edge->isWeighted().value_or(false)) { - const DirectedWeightedEdge *dw_edge = - dynamic_cast *>(edge); - weightMap[edge->getNodePair().first][edge->getNodePair().second] = - dw_edge->getWeight(); - } else { - weightMap[edge->getNodePair().first][edge->getNodePair().second] = - 0; // No Weighted Edge are assumed to be 0 weigthed - } - } - - auto bfs_helper = [this, &source, &target, &parent, - &weightMap]() -> bool { - std::unordered_map *, bool> visited; - std::queue *> queue; - queue.push(&source); - visited[&source] = true; - parent[&source] = nullptr; - while (!queue.empty()) { - auto u = queue.front(); - queue.pop(); - for (auto &v : weightMap[u]) { - if (!visited[v.first] && v.second > 0) { - queue.push(v.first); - visited[v.first] = true; - parent[v.first] = u; - } - } - } + if (!isDirectedGraph()) { + return -1; + } + double maxFlow = 0; + std::unordered_map *, const Node *> parent; + std::map *, std::map *, double>> weightMap; + // build weight map + auto edgeSet = this->getEdgeSet(); + for (const auto &edge : edgeSet) { + // The Edge are all Directed at this point because is checked at the + // start + if (edge->isWeighted().value_or(false)) { + const DirectedWeightedEdge *dw_edge = + dynamic_cast *>(edge); + weightMap[edge->getNodePair().first][edge->getNodePair().second] = + dw_edge->getWeight(); + } else { + weightMap[edge->getNodePair().first][edge->getNodePair().second] = + 0; // No Weighted Edge are assumed to be 0 weigthed + } + } - return (visited[&target]); - }; - // Updating the residual values of edges - while (bfs_helper()) { - double pathFlow = std::numeric_limits::max(); - for (auto v = ⌖ v != &source; v = parent[v]) { - auto u = parent[v]; - pathFlow = std::min(pathFlow, weightMap[u][v]); - } - for (auto v = ⌖ v != &source; v = parent[v]) { - auto u = parent[v]; - weightMap[u][v] -= pathFlow; - weightMap[v][u] += pathFlow; - } - // Adding the path flows - maxFlow += pathFlow; + auto bfs_helper = [this, &source, &target, &parent, &weightMap]() -> bool { + std::unordered_map *, bool> visited; + std::queue *> queue; + queue.push(&source); + visited[&source] = true; + parent[&source] = nullptr; + while (!queue.empty()) { + auto u = queue.front(); + queue.pop(); + for (auto &v : weightMap[u]) { + if (!visited[v.first] && v.second > 0) { + queue.push(v.first); + visited[v.first] = true; + parent[v.first] = u; } + } + } - return maxFlow; + return (visited[&target]); + }; + // Updating the residual values of edges + while (bfs_helper()) { + double pathFlow = std::numeric_limits::max(); + for (auto v = ⌖ v != &source; v = parent[v]) { + auto u = parent[v]; + pathFlow = std::min(pathFlow, weightMap[u][v]); + } + for (auto v = ⌖ v != &source; v = parent[v]) { + auto u = parent[v]; + weightMap[u][v] -= pathFlow; + weightMap[v][u] += pathFlow; + } + // Adding the path flows + maxFlow += pathFlow; + } + + return maxFlow; } template const std::vector> Graph::graph_slicing(const Node &start) const { - std::vector> result; + std::vector> result; - auto nodeSet = Graph::getNodeSet(); - // check if start node in the graph - if (std::find(nodeSet.begin(), nodeSet.end(), &start) == - nodeSet.end()) { - return result; - } - std::vector> C = Graph::depth_first_search(start); - std::deque *> C1; // complement of C i.e. nodeSet - C - for (auto const &node : nodeSet) { - // from the set of all nodes, remove nodes that exist in C - if (std::find_if(C.begin(), C.end(), [node](const Node nodeC) { - return (*node == nodeC); - }) == C.end()) - C1.push_back(node); - } + auto nodeSet = Graph::getNodeSet(); + // check if start node in the graph + if (std::find(nodeSet.begin(), nodeSet.end(), &start) == nodeSet.end()) { + return result; + } + std::vector> C = Graph::depth_first_search(start); + std::deque *> C1; // complement of C i.e. nodeSet - C + for (auto const &node : nodeSet) { + // from the set of all nodes, remove nodes that exist in C + if (std::find_if(C.begin(), C.end(), [node](const Node nodeC) { + return (*node == nodeC); + }) == C.end()) + C1.push_back(node); + } - // For all nodes in C', apply DFS - // and get the list of reachable nodes and store in M - std::vector> M; - for (auto const &node : C1) { - std::vector> reachableNodes = - Graph::depth_first_search(*node); - M.insert(M.end(), reachableNodes.begin(), reachableNodes.end()); - } - // removes nodes from C that are reachable from M. - for (const auto &nodeC : C) { - if (std::find_if(M.begin(), M.end(), [nodeC](const Node nodeM) { - return (nodeM == nodeC); - }) == M.end()) - result.push_back(nodeC); - } - return result; + // For all nodes in C', apply DFS + // and get the list of reachable nodes and store in M + std::vector> M; + for (auto const &node : C1) { + std::vector> reachableNodes = Graph::depth_first_search(*node); + M.insert(M.end(), reachableNodes.begin(), reachableNodes.end()); + } + // removes nodes from C that are reachable from M. + for (const auto &nodeC : C) { + if (std::find_if(M.begin(), M.end(), [nodeC](const Node nodeM) { + return (nodeM == nodeC); + }) == M.end()) + result.push_back(nodeC); + } + return result; } template @@ -2720,61 +2690,59 @@ int Graph::writeToFile(InputOutputFormat format, const std::string &workingDir, const std::string &OFileName, bool compress, bool writeNodeFeat, bool writeEdgeWeight) const { - int result = 0; - result = writeToStandardFile(workingDir, OFileName, compress, - writeNodeFeat, writeEdgeWeight, format); - if (result == 0 && compress) { - auto _compress = [this, &workingDir, &OFileName, &writeNodeFeat, - &writeEdgeWeight](const std::string &extension) { - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - std::string completePathToFileGraphCompressed = - workingDir + "/" + OFileName + extension + ".gz"; - int _result = compressFile(completePathToFileGraph, - completePathToFileGraphCompressed); - if (_result == 0) { - _result = remove(completePathToFileGraph.c_str()); - } - if (_result == 0) { - if (writeNodeFeat) { - std::string completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - std::string completePathToFileNodeFeatCompressed = - workingDir + "/" + OFileName + "_NodeFeat" + extension + - ".gz"; - _result = compressFile(completePathToFileNodeFeat, - completePathToFileNodeFeatCompressed); - if (_result == 0) { - _result = remove(completePathToFileNodeFeat.c_str()); - } - } - } - if (_result == 0) { - if (writeEdgeWeight) { - std::string completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - std::string completePathToFileEdgeWeightCompressed = - workingDir + "/" + OFileName + "_EdgeWeight" + extension + - ".gz"; - _result = compressFile(completePathToFileEdgeWeight, - completePathToFileEdgeWeightCompressed); - if (_result == 0) { - _result = remove(completePathToFileEdgeWeight.c_str()); - } - } - } - return _result; - }; - if (format == InputOutputFormat::STANDARD_CSV) { - auto result = _compress(".csv"); - } else if (format == InputOutputFormat::STANDARD_TSV) { - auto result = _compress(".tsv"); - } else { - // OUTPUT FORMAT NOT RECOGNIZED - result = -1; + int result = 0; + result = writeToStandardFile(workingDir, OFileName, compress, writeNodeFeat, + writeEdgeWeight, format); + if (result == 0 && compress) { + auto _compress = [this, &workingDir, &OFileName, &writeNodeFeat, + &writeEdgeWeight](const std::string &extension) { + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + std::string completePathToFileGraphCompressed = + workingDir + "/" + OFileName + extension + ".gz"; + int _result = compressFile(completePathToFileGraph, + completePathToFileGraphCompressed); + if (_result == 0) { + _result = remove(completePathToFileGraph.c_str()); + } + if (_result == 0) { + if (writeNodeFeat) { + std::string completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + std::string completePathToFileNodeFeatCompressed = + workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; + _result = compressFile(completePathToFileNodeFeat, + completePathToFileNodeFeatCompressed); + if (_result == 0) { + _result = remove(completePathToFileNodeFeat.c_str()); } } - return result; + } + if (_result == 0) { + if (writeEdgeWeight) { + std::string completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + std::string completePathToFileEdgeWeightCompressed = + workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; + _result = compressFile(completePathToFileEdgeWeight, + completePathToFileEdgeWeightCompressed); + if (_result == 0) { + _result = remove(completePathToFileEdgeWeight.c_str()); + } + } + } + return _result; + }; + if (format == InputOutputFormat::STANDARD_CSV) { + auto result = _compress(".csv"); + } else if (format == InputOutputFormat::STANDARD_TSV) { + auto result = _compress(".tsv"); + } else { + // OUTPUT FORMAT NOT RECOGNIZED + result = -1; + } + } + return result; } template @@ -2782,54 +2750,65 @@ int Graph::readFromFile(InputOutputFormat format, const std::string &workingDir, const std::string &OFileName, bool compress, bool readNodeFeat, bool readEdgeWeight) { - int result = 0; - if (compress) { - auto decompress = [this, &workingDir, &OFileName, &readNodeFeat, - &readEdgeWeight](const std::string &extension) { - std::string completePathToFileGraph = - workingDir + "/" + OFileName + extension; - std::string completePathToFileGraphCompressed = - workingDir + "/" + OFileName + extension + ".gz"; - int _result = decompressFile(completePathToFileGraphCompressed, - completePathToFileGraph); - if (_result == 0) { - if (readNodeFeat) { - std::string completePathToFileNodeFeat = - workingDir + "/" + OFileName + "_NodeFeat" + extension; - std::string completePathToFileNodeFeatCompressed = - workingDir + "/" + OFileName + "_NodeFeat" + extension + - ".gz"; - _result = decompressFile(completePathToFileNodeFeatCompressed, - completePathToFileNodeFeat); - } - } - if (_result == 0) { - if (readEdgeWeight) { - std::string completePathToFileEdgeWeight = - workingDir + "/" + OFileName + "_EdgeWeight" + extension; - std::string completePathToFileEdgeWeightCompressed = - workingDir + "/" + OFileName + "_EdgeWeight" + extension + - ".gz"; - _result = decompressFile(completePathToFileEdgeWeightCompressed, - completePathToFileEdgeWeight); - } - } - return _result; - }; - if (format == InputOutputFormat::STANDARD_CSV) { - result = decompress(".csv"); - } else if (format == InputOutputFormat::STANDARD_TSV) { - result = decompress(".tsv"); - } else { - // INPUT FORMAT NOT RECOGNIZED - result = -1; - } + int result = 0; + if (compress) { + auto decompress = [this, &workingDir, &OFileName, &readNodeFeat, + &readEdgeWeight](const std::string &extension) { + std::string completePathToFileGraph = + workingDir + "/" + OFileName + extension; + std::string completePathToFileGraphCompressed = + workingDir + "/" + OFileName + extension + ".gz"; + int _result = decompressFile(completePathToFileGraphCompressed, + completePathToFileGraph); + if (_result == 0) { + if (readNodeFeat) { + std::string completePathToFileNodeFeat = + workingDir + "/" + OFileName + "_NodeFeat" + extension; + std::string completePathToFileNodeFeatCompressed = + workingDir + "/" + OFileName + "_NodeFeat" + extension + ".gz"; + _result = decompressFile(completePathToFileNodeFeatCompressed, + completePathToFileNodeFeat); } - if (result == 0) { - result = readFromStandardFile(workingDir, OFileName, compress, - readNodeFeat, readEdgeWeight, format); + } + if (_result == 0) { + if (readEdgeWeight) { + std::string completePathToFileEdgeWeight = + workingDir + "/" + OFileName + "_EdgeWeight" + extension; + std::string completePathToFileEdgeWeightCompressed = + workingDir + "/" + OFileName + "_EdgeWeight" + extension + ".gz"; + _result = decompressFile(completePathToFileEdgeWeightCompressed, + completePathToFileEdgeWeight); } - return result; + } + return _result; + }; + if (format == InputOutputFormat::STANDARD_CSV) { + result = decompress(".csv"); + } else if (format == InputOutputFormat::STANDARD_TSV) { + result = decompress(".tsv"); + } else { + // INPUT FORMAT NOT RECOGNIZED + result = -1; + } + } + if (result == 0) { + result = readFromStandardFile(workingDir, OFileName, compress, readNodeFeat, + readEdgeWeight, format); + } + return result; +} + +template +int Graph::writeToDotFile(const std::string &workingDir, + const std::string &OFileName, + const std::string &graphName) const { + return writeToDot(workingDir, OFileName, graphName); +} + +template +int Graph::readFromDotFile(const std::string &workingDir, + const std::string &fileName) { + return readFromDot(workingDir, fileName); } template @@ -2838,88 +2817,82 @@ PartitionMap Graph::partitionGraph( const unsigned int numberOfPartitions, const double param1, const double param2, const double param3, const unsigned int numberOfThreads) const { - PartitionMap partitionMap; - PARTITIONING::Globals globals(numberOfPartitions, algorithm, param1, - param2, param3, numberOfThreads); - const T_EdgeSet &edgeSet = getEdgeSet(); - globals.edgeCardinality = edgeSet.size(); - globals.vertexCardinality = this->getNodeSet().size(); - PARTITIONING::Partitioner partitioner(&edgeSet, globals); - PARTITIONING::CoordinatedPartitionState partitionState = - partitioner.performCoordinatedPartition(); - partitionMap = partitionState.getPartitionMap(); - return partitionMap; + PartitionMap partitionMap; + PARTITIONING::Globals globals(numberOfPartitions, algorithm, param1, param2, + param3, numberOfThreads); + const T_EdgeSet &edgeSet = getEdgeSet(); + globals.edgeCardinality = edgeSet.size(); + globals.vertexCardinality = this->getNodeSet().size(); + PARTITIONING::Partitioner partitioner(&edgeSet, globals); + PARTITIONING::CoordinatedPartitionState partitionState = + partitioner.performCoordinatedPartition(); + partitionMap = partitionState.getPartitionMap(); + return partitionMap; } template std::ostream &operator<<(std::ostream &os, const Graph &graph) { - os << "Graph:\n"; - auto edgeList = graph.getEdgeSet(); - auto it = edgeList.begin(); - for (it; it != edgeList.end(); ++it) { - if (!(*it)->isDirected().has_value() && - !(*it)->isWeighted().has_value()) { - // Edge Case - os << **it << "\n"; - } else if (((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - ((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << dynamic_cast &>(*it) << "\n"; - } else if (((*it)->isDirected().has_value() && - (*it)->isDirected().value()) && - !((*it)->isWeighted().has_value() && - (*it)->isWeighted().value())) { - os << dynamic_cast &>(*it) << "\n"; - } else if (!(it->isDirected().has_value() && - it->isDirected().value()) && - (it->isWeighted().has_value() && - it->isWeighted().value())) { - os << dynamic_cast &>(*it) << "\n"; - } else if (!(it->isDirected().has_value() && - it->isDirected().value()) && - !(it->isWeighted().has_value() && - it->isWeighted().value())) { - os << dynamic_cast &>(*it) << "\n"; - } else { - os << *it << "\n"; - } - } - return os; + os << "Graph:\n"; + auto edgeList = graph.getEdgeSet(); + auto it = edgeList.begin(); + for (it; it != edgeList.end(); ++it) { + if (!(*it)->isDirected().has_value() && !(*it)->isWeighted().has_value()) { + // Edge Case + os << **it << "\n"; + } else if (((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + ((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << dynamic_cast &>(*it) << "\n"; + } else if (((*it)->isDirected().has_value() && + (*it)->isDirected().value()) && + !((*it)->isWeighted().has_value() && + (*it)->isWeighted().value())) { + os << dynamic_cast &>(*it) << "\n"; + } else if (!(it->isDirected().has_value() && it->isDirected().value()) && + (it->isWeighted().has_value() && it->isWeighted().value())) { + os << dynamic_cast &>(*it) << "\n"; + } else if (!(it->isDirected().has_value() && it->isDirected().value()) && + !(it->isWeighted().has_value() && it->isWeighted().value())) { + os << dynamic_cast &>(*it) << "\n"; + } else { + os << *it << "\n"; + } + } + return os; } template std::ostream &operator<<(std::ostream &os, const AdjacencyMatrix &adj) { - os << "Adjacency Matrix:\n"; - unsigned long max_column = 0; - for (const auto &it : adj) { - if (it.second.size() > max_column) { - max_column = it.second.size(); - } - } - if (max_column == 0) { - os << "ERROR in Print\n"; - return os; - } else { - os << "|--|"; - for (int i = 0; i < max_column; ++i) { - os << "-----|"; - } - os << "\n"; - for (const auto &it : adj) { - os << "|N" << it.first->getId() << "|"; - for (const auto &it2 : it.second) { - os << "N" << it2.first->getId() << ",E" << it2.second->getId() - << "|"; - } - os << "\n|--|"; - for (int i = 0; i < max_column; ++i) { - os << "-----|"; - } - os << "\n"; - } - } - return os; + os << "Adjacency Matrix:\n"; + unsigned long max_column = 0; + for (const auto &it : adj) { + if (it.second.size() > max_column) { + max_column = it.second.size(); + } + } + if (max_column == 0) { + os << "ERROR in Print\n"; + return os; + } else { + os << "|--|"; + for (int i = 0; i < max_column; ++i) { + os << "-----|"; + } + os << "\n"; + for (const auto &it : adj) { + os << "|N" << it.first->getId() << "|"; + for (const auto &it2 : it.second) { + os << "N" << it2.first->getId() << ",E" << it2.second->getId() << "|"; + } + os << "\n|--|"; + for (int i = 0; i < max_column; ++i) { + os << "-----|"; + } + os << "\n"; + } + } + return os; } } // namespace CXXGRAPH From ef0503522bd6e10c4c599b8fb5f0e7a8a13d070c Mon Sep 17 00:00:00 2001 From: sbaldu Date: Wed, 10 May 2023 14:49:12 +0200 Subject: [PATCH 4/6] Debug readFromDotFile function --- include/Graph/Graph.hpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/include/Graph/Graph.hpp b/include/Graph/Graph.hpp index 44a17bc01..69ba2f037 100644 --- a/include/Graph/Graph.hpp +++ b/include/Graph/Graph.hpp @@ -796,7 +796,6 @@ int Graph::writeToDot(const std::string &workingDir, dynamic_cast(edgeIt)->getWeight())) + ']'; } - std::cout << __LINE__ << std::endl; edgeLine += ";\n"; ofileGraph << edgeLine; } @@ -916,6 +915,7 @@ int Graph::readFromDot(const std::string &workingDir, // Define the edge maps std::unordered_map> edgeMap; + std::unordered_map nodeFeatMap; std::unordered_map edgeDirectedMap; std::unordered_map edgeWeightMap; @@ -927,7 +927,6 @@ int Graph::readFromDot(const std::string &workingDir, // Get full path to the file and open it const std::string completePathToFileGraph = workingDir + '/' + fileName + ".dot"; - std::ifstream iFile(completePathToFileGraph); // Check if the graph is directed bool directed = false; @@ -935,15 +934,17 @@ int Graph::readFromDot(const std::string &workingDir, std::string fileContent(std::istreambuf_iterator{fileContentStream}, {}); const std::string directedSeparator = "->"; - if (fileContent.find(directedSeparator)) { + if (fileContent.find("->") != std::string::npos) { directed = true; } // Check if the graph is weighted bool weighted = false; - if (fileContent.find("weight")) { + if (fileContent.find("weight") != std::string::npos) { weighted = true; } + fileContentStream.close(); + std::ifstream iFile(completePathToFileGraph); // Write the header of the DOT file in the temp string getline(iFile, temp); @@ -956,7 +957,7 @@ int Graph::readFromDot(const std::string &workingDir, } // Remove the whitespaces before the definition of the edge - while (*fileRow.begin() == ' ') { + while (*fileRow.begin() == ' ' || *fileRow.begin() == '\t') { fileRow.erase(fileRow.begin()); } @@ -965,7 +966,10 @@ int Graph::readFromDot(const std::string &workingDir, // Store the symbol representing the edge inside temp getline(row_stream, temp, ' '); if (weighted) { - getline(row_stream, node2, ' '); + getline(row_stream, node2, '['); + // Remove any whitespaces or tabs from the node string + node2.erase(std::remove(node2.begin(), node2.end(), ' '), node2.end()); + node2.erase(std::remove(node2.begin(), node2.end(), '\t'), node2.end()); getline(row_stream, temp, '='); std::string weight; @@ -983,6 +987,11 @@ int Graph::readFromDot(const std::string &workingDir, edgeDirectedMap[edgeId] = directed; ++edgeId; } + iFile.close(); + + recreateGraphFromReadFiles(edgeMap, edgeDirectedMap, nodeFeatMap, + edgeWeightMap); + return 0; } template From 1c171c9b53be47210e5086ea73872f4e1e6d6e7d Mon Sep 17 00:00:00 2001 From: sbaldu Date: Wed, 10 May 2023 14:50:09 +0200 Subject: [PATCH 5/6] Write tests for imprort/export from DOT methods --- test/DOTTest.cpp | 158 ++++++++++++++ test/test_dot_directed.dot | 412 +++++++++++++++++++++++++++++++++++ test/test_dot_undirected.dot | 214 ++++++++++++++++++ test/test_dot_weighted.dot | 213 ++++++++++++++++++ 4 files changed, 997 insertions(+) create mode 100644 test/DOTTest.cpp create mode 100644 test/test_dot_directed.dot create mode 100644 test/test_dot_undirected.dot create mode 100644 test/test_dot_weighted.dot diff --git a/test/DOTTest.cpp b/test/DOTTest.cpp new file mode 100644 index 000000000..ec9ca15f4 --- /dev/null +++ b/test/DOTTest.cpp @@ -0,0 +1,158 @@ +#include +#include + +#include "CXXGraph.hpp" +#include "gtest/gtest.h" + +TEST(DOTTest, WriteToDotDirectedWeighted) { + // Generate a simple test graph with few nodes and edges + CXXGRAPH::Node node1("1", 1); + CXXGRAPH::Node node2("2", 2); + CXXGRAPH::Node node3("3", 3); + std::pair *, const CXXGRAPH::Node *> pairNode( + &node1, &node2); + CXXGRAPH::DirectedWeightedEdge edge1(1, pairNode, 5); + CXXGRAPH::DirectedWeightedEdge edge2(2, node2, node3, 3); + CXXGRAPH::DirectedWeightedEdge edge3(3, node3, node1, 7); + CXXGRAPH::T_EdgeSet edgeSet; + edgeSet.insert(&edge1); + edgeSet.insert(&edge2); + edgeSet.insert(&edge3); + CXXGRAPH::Graph graph(edgeSet); + + // Write the graph to a DOT file + graph.writeToDotFile("./", "example_graph_directed_weighted", + "example_graph"); +} + +TEST(DOTTest, WriteToDotUndirected) { + // Generate a simple test graph with few nodes and edges + CXXGRAPH::Node node1("1", 1); + CXXGRAPH::Node node2("2", 2); + CXXGRAPH::Node node3("3", 3); + std::pair *, const CXXGRAPH::Node *> pairNode( + &node1, &node2); + CXXGRAPH::UndirectedEdge edge1(1, pairNode); + CXXGRAPH::UndirectedEdge edge2(2, node2, node3); + CXXGRAPH::UndirectedEdge edge3(3, node3, node1); + CXXGRAPH::T_EdgeSet edgeSet; + edgeSet.insert(&edge1); + edgeSet.insert(&edge2); + edgeSet.insert(&edge3); + CXXGRAPH::Graph graph(edgeSet); + + // Write the graph to a DOT file + graph.writeToDotFile("./", "example_graph_undirected", "example_graph"); +} + +TEST(DOTTest, WriteToDotMixed) { + CXXGRAPH::Node node1("1", 1); + CXXGRAPH::Node node2("2", 2); + CXXGRAPH::Node node3("3", 3); + + CXXGRAPH::DirectedWeightedEdge edge1(1, node1, node2, 1); + CXXGRAPH::UndirectedWeightedEdge edge2(2, node2, node3, 1); + CXXGRAPH::DirectedEdge edge3(3, node1, node3); + + CXXGRAPH::T_EdgeSet edgeSet; + edgeSet.insert(&edge1); + edgeSet.insert(&edge2); + edgeSet.insert(&edge3); + + CXXGRAPH::Graph graph(edgeSet); + graph.writeToDotFile("./", "mixed", "example_graph"); +} + +TEST(DOTTest, ReadFromDotUndirected) { + CXXGRAPH::Graph graph; + graph.readFromDotFile("..", "test_dot_undirected"); + + // Check that the graph is undirected + ASSERT_EQ(graph.isDirectedGraph(), false); + // Check that the number of edges and nodes are correct + ASSERT_EQ(graph.getEdgeSet().size(), 212); + ASSERT_EQ(graph.getNodeSet().size(), 30); + // Check that the first edge is correct + ASSERT_EQ(graph.getEdge(0).value()->getNodePair().first->getUserId(), "0"); + ASSERT_EQ(graph.getEdge(0).value()->getNodePair().second->getUserId(), "4"); + ASSERT_EQ(graph.getEdge(0).value()->isDirected(), false); + ASSERT_EQ(graph.getEdge(0).value()->isWeighted(), false); + // Check that the last edge is correct + ASSERT_EQ(graph.getEdge(211).value()->getNodePair().first->getUserId(), "28"); + ASSERT_EQ(graph.getEdge(211).value()->getNodePair().second->getUserId(), + "29"); + ASSERT_EQ(graph.getEdge(211).value()->isDirected(), false); + ASSERT_EQ(graph.getEdge(211).value()->isWeighted(), false); +} + +TEST(DOTTest, ReadFromDotUndirectedWeighted) { + CXXGRAPH::Graph graph; + graph.readFromDotFile("..", "test_dot_weighted"); + + // Check that the graph is undirected + ASSERT_EQ(graph.isDirectedGraph(), false); + // Check that the number of edges and nodes are correct + ASSERT_EQ(graph.getEdgeSet().size(), 211); + ASSERT_EQ(graph.getNodeSet().size(), 30); + // Check that the first edge is correct + ASSERT_EQ(graph.getEdge(0).value()->getNodePair().first->getUserId(), "0"); + ASSERT_EQ(graph.getEdge(0).value()->getNodePair().second->getUserId(), "1"); + ASSERT_EQ(dynamic_cast *>( + graph.getEdge(0).value()) + ->getWeight(), + 6); + ASSERT_EQ(graph.getEdge(0).value()->isDirected(), false); + ASSERT_EQ(graph.getEdge(0).value()->isWeighted(), true); + // Check that the last edge is correct + ASSERT_EQ(graph.getEdge(210).value()->getNodePair().first->getUserId(), "27"); + ASSERT_EQ(graph.getEdge(210).value()->getNodePair().second->getUserId(), + "28"); + ASSERT_EQ(dynamic_cast *>( + graph.getEdge(210).value()) + ->getWeight(), + 2); + ASSERT_EQ(graph.getEdge(210).value()->isDirected(), false); + ASSERT_EQ(graph.getEdge(0).value()->isWeighted(), true); +} + +TEST(DOTTest, ReadFromDotDirected) { + CXXGRAPH::Graph graph; + graph.readFromDotFile("..", "test_dot_directed"); + + // Check that the graph is undirected + ASSERT_EQ(graph.isDirectedGraph(), true); + // Check that the number of edges and nodes are correct + ASSERT_EQ(graph.getEdgeSet().size(), 410); + ASSERT_EQ(graph.getNodeSet().size(), 30); + // Check that the first edge is correct + ASSERT_EQ(graph.getEdge(0).value()->getNodePair().first->getUserId(), "0"); + ASSERT_EQ(graph.getEdge(0).value()->getNodePair().second->getUserId(), "1"); + ASSERT_EQ(dynamic_cast *>( + graph.getEdge(0).value()) + ->getFrom() + .getUserId(), + "0"); + ASSERT_EQ(dynamic_cast *>( + graph.getEdge(0).value()) + ->getTo() + .getUserId(), + "1"); + ASSERT_EQ(graph.getEdge(0).value()->isDirected(), true); + ASSERT_EQ(graph.getEdge(0).value()->isWeighted(), false); + // Check that the last edge is correct + ASSERT_EQ(graph.getEdge(409).value()->getNodePair().first->getUserId(), "29"); + ASSERT_EQ(graph.getEdge(409).value()->getNodePair().second->getUserId(), + "27"); + ASSERT_EQ(dynamic_cast *>( + graph.getEdge(409).value()) + ->getFrom() + .getUserId(), + "29"); + ASSERT_EQ(dynamic_cast *>( + graph.getEdge(409).value()) + ->getTo() + .getUserId(), + "27"); + ASSERT_EQ(graph.getEdge(409).value()->isDirected(), true); + ASSERT_EQ(graph.getEdge(409).value()->isWeighted(), false); +} diff --git a/test/test_dot_directed.dot b/test/test_dot_directed.dot new file mode 100644 index 000000000..2417ac9c0 --- /dev/null +++ b/test/test_dot_directed.dot @@ -0,0 +1,412 @@ +strict digraph directed_graph { + 0 -> 1; + 0 -> 7; + 0 -> 10; + 0 -> 11; + 0 -> 12; + 0 -> 14; + 0 -> 15; + 0 -> 16; + 0 -> 19; + 0 -> 21; + 0 -> 29; + 1 -> 0; + 1 -> 8; + 1 -> 10; + 1 -> 11; + 1 -> 13; + 1 -> 22; + 1 -> 23; + 1 -> 25; + 1 -> 26; + 1 -> 29; + 2 -> 0; + 2 -> 4; + 2 -> 5; + 2 -> 9; + 2 -> 13; + 2 -> 20; + 2 -> 22; + 2 -> 24; + 2 -> 27; + 3 -> 0; + 3 -> 1; + 3 -> 5; + 3 -> 7; + 3 -> 8; + 3 -> 9; + 3 -> 12; + 3 -> 14; + 3 -> 15; + 3 -> 18; + 3 -> 20; + 3 -> 21; + 3 -> 23; + 3 -> 26; + 3 -> 28; + 3 -> 29; + 4 -> 1; + 4 -> 2; + 4 -> 3; + 4 -> 6; + 4 -> 7; + 4 -> 10; + 4 -> 11; + 4 -> 12; + 4 -> 13; + 4 -> 17; + 4 -> 20; + 4 -> 21; + 4 -> 22; + 4 -> 26; + 4 -> 29; + 5 -> 0; + 5 -> 2; + 5 -> 4; + 5 -> 7; + 5 -> 9; + 5 -> 11; + 5 -> 13; + 5 -> 17; + 5 -> 18; + 5 -> 19; + 5 -> 23; + 5 -> 26; + 5 -> 28; + 6 -> 1; + 6 -> 4; + 6 -> 10; + 6 -> 13; + 6 -> 17; + 6 -> 18; + 6 -> 19; + 6 -> 20; + 6 -> 21; + 6 -> 24; + 6 -> 25; + 6 -> 26; + 6 -> 27; + 6 -> 28; + 7 -> 3; + 7 -> 6; + 7 -> 12; + 7 -> 13; + 7 -> 14; + 7 -> 15; + 7 -> 16; + 7 -> 20; + 7 -> 21; + 7 -> 22; + 7 -> 24; + 7 -> 25; + 8 -> 0; + 8 -> 3; + 8 -> 4; + 8 -> 5; + 8 -> 6; + 8 -> 7; + 8 -> 10; + 8 -> 11; + 8 -> 12; + 8 -> 13; + 8 -> 16; + 8 -> 19; + 8 -> 20; + 8 -> 21; + 8 -> 23; + 8 -> 25; + 8 -> 27; + 8 -> 28; + 9 -> 1; + 9 -> 2; + 9 -> 4; + 9 -> 10; + 9 -> 11; + 9 -> 12; + 9 -> 15; + 9 -> 17; + 9 -> 18; + 9 -> 21; + 9 -> 23; + 9 -> 24; + 9 -> 26; + 9 -> 27; + 10 -> 0; + 10 -> 1; + 10 -> 2; + 10 -> 4; + 10 -> 11; + 10 -> 12; + 10 -> 14; + 10 -> 15; + 10 -> 17; + 10 -> 18; + 10 -> 19; + 10 -> 25; + 10 -> 27; + 10 -> 28; + 11 -> 1; + 11 -> 3; + 11 -> 4; + 11 -> 5; + 11 -> 8; + 11 -> 10; + 11 -> 13; + 11 -> 14; + 11 -> 22; + 11 -> 23; + 11 -> 28; + 12 -> 0; + 12 -> 1; + 12 -> 2; + 12 -> 4; + 12 -> 5; + 12 -> 6; + 12 -> 8; + 12 -> 9; + 12 -> 10; + 12 -> 11; + 12 -> 13; + 12 -> 15; + 12 -> 17; + 12 -> 18; + 12 -> 20; + 12 -> 24; + 12 -> 28; + 13 -> 0; + 13 -> 1; + 13 -> 2; + 13 -> 3; + 13 -> 5; + 13 -> 7; + 13 -> 8; + 13 -> 9; + 13 -> 11; + 13 -> 14; + 13 -> 15; + 13 -> 16; + 13 -> 18; + 13 -> 20; + 13 -> 21; + 13 -> 26; + 13 -> 28; + 14 -> 2; + 14 -> 3; + 14 -> 4; + 14 -> 6; + 14 -> 7; + 14 -> 8; + 14 -> 9; + 14 -> 11; + 14 -> 15; + 14 -> 16; + 14 -> 19; + 14 -> 20; + 14 -> 22; + 14 -> 26; + 15 -> 0; + 15 -> 2; + 15 -> 4; + 15 -> 6; + 15 -> 7; + 15 -> 8; + 15 -> 9; + 15 -> 11; + 15 -> 18; + 15 -> 20; + 15 -> 22; + 15 -> 23; + 15 -> 24; + 15 -> 26; + 15 -> 27; + 16 -> 1; + 16 -> 2; + 16 -> 4; + 16 -> 5; + 16 -> 6; + 16 -> 7; + 16 -> 8; + 16 -> 9; + 16 -> 12; + 16 -> 13; + 16 -> 14; + 16 -> 19; + 16 -> 22; + 16 -> 23; + 16 -> 28; + 16 -> 29; + 17 -> 0; + 17 -> 5; + 17 -> 7; + 17 -> 9; + 17 -> 11; + 17 -> 14; + 17 -> 15; + 17 -> 16; + 17 -> 18; + 17 -> 23; + 17 -> 25; + 18 -> 0; + 18 -> 3; + 18 -> 4; + 18 -> 5; + 18 -> 7; + 18 -> 9; + 18 -> 10; + 18 -> 12; + 18 -> 22; + 18 -> 23; + 18 -> 24; + 18 -> 25; + 18 -> 27; + 18 -> 29; + 19 -> 0; + 19 -> 2; + 19 -> 3; + 19 -> 7; + 19 -> 8; + 19 -> 9; + 19 -> 10; + 19 -> 11; + 19 -> 16; + 19 -> 21; + 19 -> 23; + 19 -> 24; + 19 -> 25; + 19 -> 28; + 20 -> 1; + 20 -> 4; + 20 -> 5; + 20 -> 7; + 20 -> 9; + 20 -> 10; + 20 -> 11; + 20 -> 12; + 20 -> 13; + 20 -> 14; + 20 -> 16; + 20 -> 17; + 20 -> 18; + 20 -> 21; + 21 -> 1; + 21 -> 2; + 21 -> 4; + 21 -> 7; + 21 -> 9; + 21 -> 10; + 21 -> 11; + 21 -> 13; + 21 -> 16; + 21 -> 17; + 21 -> 18; + 21 -> 19; + 21 -> 22; + 21 -> 23; + 22 -> 0; + 22 -> 2; + 22 -> 4; + 22 -> 7; + 22 -> 8; + 22 -> 11; + 22 -> 16; + 22 -> 18; + 22 -> 20; + 22 -> 23; + 22 -> 25; + 22 -> 26; + 23 -> 2; + 23 -> 10; + 23 -> 12; + 23 -> 18; + 23 -> 19; + 23 -> 21; + 23 -> 22; + 23 -> 24; + 23 -> 27; + 24 -> 4; + 24 -> 8; + 24 -> 10; + 24 -> 11; + 24 -> 12; + 24 -> 14; + 24 -> 15; + 24 -> 16; + 24 -> 17; + 24 -> 18; + 24 -> 20; + 24 -> 21; + 24 -> 23; + 24 -> 28; + 24 -> 29; + 25 -> 1; + 25 -> 4; + 25 -> 5; + 25 -> 7; + 25 -> 14; + 25 -> 15; + 25 -> 16; + 25 -> 18; + 25 -> 21; + 25 -> 23; + 25 -> 26; + 26 -> 0; + 26 -> 1; + 26 -> 6; + 26 -> 9; + 26 -> 11; + 26 -> 12; + 26 -> 13; + 26 -> 14; + 26 -> 15; + 26 -> 16; + 26 -> 18; + 26 -> 19; + 26 -> 20; + 26 -> 21; + 26 -> 22; + 26 -> 23; + 26 -> 28; + 27 -> 0; + 27 -> 1; + 27 -> 2; + 27 -> 3; + 27 -> 5; + 27 -> 9; + 27 -> 10; + 27 -> 11; + 27 -> 12; + 27 -> 15; + 27 -> 16; + 27 -> 20; + 27 -> 23; + 27 -> 26; + 27 -> 28; + 27 -> 29; + 28 -> 0; + 28 -> 4; + 28 -> 5; + 28 -> 6; + 28 -> 9; + 28 -> 10; + 28 -> 11; + 28 -> 12; + 28 -> 18; + 28 -> 21; + 28 -> 23; + 28 -> 24; + 28 -> 26; + 28 -> 27; + 29 -> 1; + 29 -> 7; + 29 -> 10; + 29 -> 11; + 29 -> 12; + 29 -> 13; + 29 -> 14; + 29 -> 17; + 29 -> 18; + 29 -> 19; + 29 -> 20; + 29 -> 24; + 29 -> 27; +} diff --git a/test/test_dot_undirected.dot b/test/test_dot_undirected.dot new file mode 100644 index 000000000..a014d1d3d --- /dev/null +++ b/test/test_dot_undirected.dot @@ -0,0 +1,214 @@ +graph example_graph { + 0 -- 4; + 0 -- 8; + 0 -- 14; + 0 -- 15; + 0 -- 16; + 0 -- 17; + 0 -- 18; + 0 -- 19; + 0 -- 20; + 0 -- 23; + 0 -- 24; + 0 -- 26; + 0 -- 27; + 0 -- 28; + 1 -- 4; + 1 -- 6; + 1 -- 7; + 1 -- 9; + 1 -- 11; + 1 -- 15; + 1 -- 18; + 1 -- 19; + 1 -- 20; + 1 -- 22; + 2 -- 6; + 2 -- 8; + 2 -- 9; + 2 -- 10; + 2 -- 12; + 2 -- 15; + 2 -- 16; + 2 -- 19; + 2 -- 20; + 2 -- 21; + 2 -- 22; + 2 -- 23; + 2 -- 24; + 2 -- 26; + 2 -- 27; + 2 -- 29; + 3 -- 4; + 3 -- 5; + 3 -- 7; + 3 -- 8; + 3 -- 9; + 3 -- 11; + 3 -- 14; + 3 -- 15; + 3 -- 16; + 3 -- 17; + 3 -- 18; + 3 -- 21; + 3 -- 24; + 3 -- 26; + 3 -- 28; + 4 -- 5; + 4 -- 7; + 4 -- 8; + 4 -- 9; + 4 -- 10; + 4 -- 11; + 4 -- 14; + 4 -- 15; + 4 -- 16; + 4 -- 18; + 4 -- 23; + 4 -- 24; + 4 -- 27; + 5 -- 6; + 5 -- 10; + 5 -- 11; + 5 -- 12; + 5 -- 13; + 5 -- 17; + 5 -- 18; + 5 -- 19; + 5 -- 24; + 5 -- 28; + 5 -- 29; + 6 -- 7; + 6 -- 8; + 6 -- 9; + 6 -- 10; + 6 -- 11; + 6 -- 12; + 6 -- 13; + 6 -- 15; + 6 -- 17; + 6 -- 18; + 6 -- 20; + 6 -- 22; + 6 -- 23; + 6 -- 29; + 7 -- 8; + 7 -- 9; + 7 -- 13; + 7 -- 14; + 7 -- 17; + 7 -- 18; + 7 -- 20; + 7 -- 22; + 7 -- 24; + 7 -- 26; + 7 -- 27; + 8 -- 9; + 8 -- 11; + 8 -- 13; + 8 -- 16; + 8 -- 20; + 8 -- 21; + 8 -- 23; + 8 -- 24; + 8 -- 26; + 8 -- 28; + 9 -- 10; + 9 -- 12; + 9 -- 13; + 9 -- 14; + 9 -- 18; + 9 -- 21; + 9 -- 22; + 9 -- 29; + 10 -- 11; + 10 -- 13; + 10 -- 14; + 10 -- 16; + 10 -- 19; + 10 -- 20; + 10 -- 21; + 10 -- 23; + 10 -- 24; + 10 -- 25; + 10 -- 26; + 10 -- 27; + 10 -- 28; + 11 -- 12; + 11 -- 14; + 11 -- 20; + 11 -- 21; + 11 -- 23; + 11 -- 25; + 12 -- 14; + 12 -- 15; + 12 -- 16; + 12 -- 17; + 12 -- 18; + 12 -- 20; + 12 -- 22; + 12 -- 26; + 12 -- 27; + 13 -- 16; + 13 -- 17; + 13 -- 21; + 13 -- 22; + 13 -- 23; + 13 -- 24; + 13 -- 25; + 14 -- 15; + 14 -- 20; + 14 -- 26; + 14 -- 28; + 15 -- 17; + 15 -- 18; + 15 -- 20; + 15 -- 21; + 15 -- 22; + 15 -- 25; + 15 -- 26; + 15 -- 27; + 15 -- 28; + 16 -- 18; + 16 -- 20; + 16 -- 21; + 16 -- 22; + 16 -- 23; + 16 -- 24; + 16 -- 25; + 16 -- 27; + 16 -- 28; + 17 -- 19; + 17 -- 20; + 17 -- 22; + 18 -- 19; + 18 -- 20; + 18 -- 21; + 18 -- 22; + 18 -- 24; + 18 -- 25; + 18 -- 27; + 19 -- 20; + 19 -- 21; + 19 -- 22; + 19 -- 23; + 19 -- 24; + 19 -- 28; + 20 -- 21; + 20 -- 23; + 20 -- 26; + 20 -- 28; + 22 -- 26; + 22 -- 28; + 23 -- 25; + 23 -- 26; + 24 -- 25; + 24 -- 26; + 24 -- 27; + 24 -- 29; + 25 -- 29; + 26 -- 27; + 26 -- 28; + 27 -- 29; + 28 -- 29; +} diff --git a/test/test_dot_weighted.dot b/test/test_dot_weighted.dot new file mode 100644 index 000000000..908ec038f --- /dev/null +++ b/test/test_dot_weighted.dot @@ -0,0 +1,213 @@ +strict graph undirected_weighted { + 0 -- 1 [weight=6]; + 0 -- 7 [weight=5]; + 0 -- 10 [weight=2]; + 0 -- 11 [weight=1]; + 0 -- 12 [weight=3]; + 0 -- 13 [weight=8]; + 0 -- 17 [weight=1]; + 0 -- 18 [weight=5]; + 0 -- 19 [weight=4]; + 0 -- 20 [weight=1]; + 0 -- 22 [weight=2]; + 0 -- 24 [weight=2]; + 0 -- 25 [weight=9]; + 0 -- 27 [weight=5]; + 0 -- 29 [weight=4]; + 1 -- 2 [weight=8]; + 1 -- 3 [weight=4]; + 1 -- 5 [weight=8]; + 1 -- 8 [weight=8]; + 1 -- 10 [weight=9]; + 1 -- 12 [weight=9]; + 1 -- 13 [weight=8]; + 1 -- 14 [weight=5]; + 1 -- 19 [weight=7]; + 1 -- 20 [weight=3]; + 1 -- 21 [weight=6]; + 1 -- 23 [weight=5]; + 1 -- 24 [weight=4]; + 1 -- 25 [weight=6]; + 1 -- 26 [weight=6]; + 1 -- 27 [weight=3]; + 1 -- 29 [weight=5]; + 2 -- 3 [weight=2]; + 2 -- 4 [weight=2]; + 2 -- 5 [weight=8]; + 2 -- 6 [weight=6]; + 2 -- 10 [weight=6]; + 2 -- 12 [weight=5]; + 2 -- 13 [weight=3]; + 2 -- 14 [weight=1]; + 2 -- 15 [weight=4]; + 2 -- 17 [weight=7]; + 2 -- 23 [weight=9]; + 2 -- 25 [weight=6]; + 2 -- 26 [weight=9]; + 2 -- 27 [weight=5]; + 2 -- 28 [weight=4]; + 3 -- 4 [weight=4]; + 3 -- 5 [weight=9]; + 3 -- 6 [weight=6]; + 3 -- 7 [weight=4]; + 3 -- 10 [weight=4]; + 3 -- 11 [weight=7]; + 3 -- 12 [weight=8]; + 3 -- 19 [weight=5]; + 3 -- 21 [weight=3]; + 3 -- 24 [weight=8]; + 3 -- 27 [weight=1]; + 3 -- 28 [weight=1]; + 4 -- 7 [weight=6]; + 4 -- 8 [weight=7]; + 4 -- 12 [weight=7]; + 4 -- 16 [weight=7]; + 4 -- 18 [weight=3]; + 4 -- 19 [weight=3]; + 4 -- 22 [weight=7]; + 4 -- 23 [weight=4]; + 4 -- 28 [weight=8]; + 4 -- 29 [weight=5]; + 5 -- 6 [weight=4]; + 5 -- 7 [weight=6]; + 5 -- 9 [weight=5]; + 5 -- 10 [weight=7]; + 5 -- 11 [weight=5]; + 5 -- 14 [weight=9]; + 5 -- 15 [weight=5]; + 5 -- 16 [weight=9]; + 5 -- 22 [weight=9]; + 5 -- 23 [weight=1]; + 5 -- 27 [weight=4]; + 6 -- 7 [weight=5]; + 6 -- 9 [weight=5]; + 6 -- 10 [weight=9]; + 6 -- 11 [weight=7]; + 6 -- 15 [weight=4]; + 6 -- 16 [weight=2]; + 6 -- 18 [weight=5]; + 6 -- 20 [weight=1]; + 6 -- 21 [weight=6]; + 6 -- 22 [weight=9]; + 6 -- 23 [weight=6]; + 6 -- 27 [weight=1]; + 7 -- 8 [weight=1]; + 7 -- 12 [weight=3]; + 7 -- 14 [weight=2]; + 7 -- 17 [weight=3]; + 7 -- 18 [weight=1]; + 7 -- 19 [weight=7]; + 7 -- 20 [weight=4]; + 7 -- 23 [weight=7]; + 7 -- 26 [weight=9]; + 7 -- 27 [weight=2]; + 7 -- 28 [weight=4]; + 7 -- 29 [weight=8]; + 8 -- 9 [weight=4]; + 8 -- 10 [weight=6]; + 8 -- 13 [weight=1]; + 8 -- 22 [weight=4]; + 8 -- 23 [weight=2]; + 8 -- 25 [weight=7]; + 8 -- 27 [weight=6]; + 8 -- 29 [weight=3]; + 9 -- 17 [weight=5]; + 9 -- 18 [weight=7]; + 9 -- 22 [weight=3]; + 9 -- 26 [weight=2]; + 9 -- 29 [weight=8]; + 10 -- 11 [weight=8]; + 10 -- 12 [weight=2]; + 10 -- 13 [weight=6]; + 10 -- 14 [weight=2]; + 10 -- 16 [weight=8]; + 10 -- 19 [weight=1]; + 10 -- 20 [weight=3]; + 10 -- 23 [weight=6]; + 10 -- 25 [weight=7]; + 10 -- 28 [weight=1]; + 11 -- 13 [weight=7]; + 11 -- 15 [weight=9]; + 11 -- 18 [weight=6]; + 11 -- 19 [weight=6]; + 11 -- 22 [weight=2]; + 11 -- 23 [weight=8]; + 11 -- 25 [weight=1]; + 11 -- 27 [weight=4]; + 11 -- 29 [weight=5]; + 12 -- 14 [weight=7]; + 12 -- 19 [weight=6]; + 12 -- 20 [weight=5]; + 12 -- 21 [weight=8]; + 12 -- 22 [weight=7]; + 12 -- 26 [weight=2]; + 12 -- 27 [weight=3]; + 12 -- 28 [weight=4]; + 13 -- 15 [weight=6]; + 13 -- 19 [weight=8]; + 13 -- 20 [weight=7]; + 13 -- 23 [weight=2]; + 13 -- 25 [weight=4]; + 13 -- 29 [weight=1]; + 14 -- 16 [weight=2]; + 14 -- 17 [weight=3]; + 14 -- 19 [weight=8]; + 14 -- 25 [weight=4]; + 14 -- 28 [weight=3]; + 14 -- 29 [weight=7]; + 15 -- 16 [weight=2]; + 15 -- 17 [weight=1]; + 15 -- 21 [weight=3]; + 15 -- 22 [weight=9]; + 15 -- 26 [weight=8]; + 15 -- 29 [weight=4]; + 16 -- 18 [weight=4]; + 16 -- 19 [weight=7]; + 16 -- 20 [weight=6]; + 16 -- 24 [weight=4]; + 16 -- 26 [weight=1]; + 16 -- 28 [weight=8]; + 17 -- 18 [weight=8]; + 17 -- 19 [weight=3]; + 17 -- 20 [weight=4]; + 17 -- 21 [weight=1]; + 17 -- 22 [weight=2]; + 17 -- 23 [weight=9]; + 17 -- 26 [weight=2]; + 17 -- 27 [weight=9]; + 18 -- 19 [weight=4]; + 18 -- 28 [weight=4]; + 19 -- 20 [weight=9]; + 19 -- 21 [weight=7]; + 19 -- 22 [weight=6]; + 19 -- 24 [weight=3]; + 19 -- 25 [weight=1]; + 19 -- 26 [weight=9]; + 19 -- 29 [weight=9]; + 20 -- 21 [weight=6]; + 20 -- 23 [weight=3]; + 20 -- 25 [weight=4]; + 20 -- 26 [weight=4]; + 20 -- 27 [weight=3]; + 21 -- 22 [weight=1]; + 21 -- 26 [weight=6]; + 21 -- 27 [weight=4]; + 21 -- 28 [weight=9]; + 21 -- 29 [weight=1]; + 22 -- 24 [weight=1]; + 22 -- 26 [weight=8]; + 23 -- 24 [weight=8]; + 23 -- 25 [weight=5]; + 23 -- 27 [weight=2]; + 23 -- 29 [weight=4]; + 24 -- 25 [weight=1]; + 24 -- 28 [weight=6]; + 24 -- 29 [weight=3]; + 25 -- 26 [weight=9]; + 25 -- 27 [weight=7]; + 25 -- 29 [weight=3]; + 26 -- 27 [weight=4]; + 26 -- 28 [weight=5]; + 26 -- 29 [weight=4]; + 27 -- 28 [weight=2]; +} From ca6542d304fee6f12a9977a2a2b359b04c0552ad Mon Sep 17 00:00:00 2001 From: sbaldu Date: Wed, 10 May 2023 14:57:00 +0200 Subject: [PATCH 6/6] Small fix in readFromDot --- include/Graph/Graph.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/Graph/Graph.hpp b/include/Graph/Graph.hpp index 69ba2f037..559939dbf 100644 --- a/include/Graph/Graph.hpp +++ b/include/Graph/Graph.hpp @@ -933,7 +933,6 @@ int Graph::readFromDot(const std::string &workingDir, std::ifstream fileContentStream(completePathToFileGraph); std::string fileContent(std::istreambuf_iterator{fileContentStream}, {}); - const std::string directedSeparator = "->"; if (fileContent.find("->") != std::string::npos) { directed = true; }