From a8c26b3fb2aed72d09069eae825be2a8b4f5a5cf Mon Sep 17 00:00:00 2001 From: Simone Balducci <93096843+sbaldu@users.noreply.github.com> Date: Wed, 27 Sep 2023 00:02:16 +0200 Subject: [PATCH] Feature node storage for Graph class (#347) * Add nodeSet member for Graph class * Use cached nodeSet in all algorithms * Add test for graphs with isolated nodes * Fix typo in overload of addEdge * Formatting * Fix type in graphTest * Delete Edges linked to removed Node in removeNode * Add more tests for addNode and removeNode methods * Add test for getNode method * Restore original use of nodeSet and cache only isolated nodes * Add checks on size of isolatedNodesSet * Add test of set data for isolated node * Small fix --- include/CXXGraph/Graph/Graph.hpp | 268 ++++++++++++++++++++++--------- test/GraphTest.cpp | 192 +++++++++++++++++++++- 2 files changed, 386 insertions(+), 74 deletions(-) diff --git a/include/CXXGraph/Graph/Graph.hpp b/include/CXXGraph/Graph/Graph.hpp index 23dba8f92..6dc5bdad3 100644 --- a/include/CXXGraph/Graph/Graph.hpp +++ b/include/CXXGraph/Graph/Graph.hpp @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -42,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -85,6 +85,9 @@ using std::make_unique; template using T_EdgeSet = std::unordered_set>, edgeHash>; +template +using T_NodeSet = std::unordered_set>, nodeHash>; + namespace Partitioning { template class Partition; @@ -100,6 +103,7 @@ template class Graph { private: T_EdgeSet edgeSet = {}; + T_NodeSet isolatedNodesSet = {}; shared> cachedAdjMatrix; @@ -118,8 +122,8 @@ class Graph { const std::string &graphName) const; int readFromDot(const std::string &workingDir, const std::string &fileName); void recreateGraph( - std::unordered_map> &edgeMap, + std::unordered_map> + &edgeMap, std::unordered_map &edgeDirectedMap, std::unordered_map &nodeFeatMap, std::unordered_map &edgeWeightMap); @@ -175,6 +179,24 @@ class Graph { * */ virtual void addEdge(shared> edge); + /** + * \brief + * Function to add a Node to the Graph Node Set + * Note: No Thread Safe + * + * @param pointer to the node + * + */ + virtual void addNode(const Node *node); + /** + * \brief + * Function to add a Node to the Graph Node Set + * Note: No Thread Safe + * + * @param shared pointer to the node + * + */ + virtual void addNode(shared> node); /** * \brief * Function remove an Edge from the Graph Edge Set @@ -184,6 +206,15 @@ class Graph { * */ virtual void removeEdge(const CXXGraph::id_t edgeId); + /** + * \brief + * Function to remove a Node from the Graph Node Set + * Note: No Thread Safe + * + * @param edgeId The Edge Id to remove + * + */ + virtual void removeNode(const std::string &nodeUserId); /** * \brief * Finds the given edge defined by v1 and v2 within the graph. @@ -214,8 +245,17 @@ class Graph { * @returns a list of Nodes of the graph * */ - virtual const std::unordered_set>, nodeHash> - getNodeSet() const; + virtual const T_NodeSet getNodeSet() const; + /** + * \brief + * Function that return the Set of isolated nodes + * in the Graph + * Note: No Thread Safe + * + * @returns a list of Nodes of the graph + * + */ + virtual const T_NodeSet getIsolatedNodeSet() const; /** * \brief * Function that sets the data contained in a node @@ -244,6 +284,17 @@ class Graph { */ virtual const std::optional>> getEdge( const CXXGraph::id_t edgeId) const; + /** + * \brief + * Function that return a Node with specific ID if Exist in the Graph + * Note: No Thread Safe + * + * @param nodeId The Node Id to return + * @returns the Node if exist + * + */ + virtual const std::optional>> getNode( + const std::string &nodeUserId) const; /** * @brief This function generate a list of adjacency matrix with every element * of the matrix contain the node where is directed the link and the Edge @@ -300,9 +351,8 @@ class Graph { * @return parent node of elem * Note: No Thread Safe */ - virtual CXXGraph::id_t setFind( - std::unordered_map *, - const CXXGraph::id_t elem) const; + virtual CXXGraph::id_t setFind(std::unordered_map *, + const CXXGraph::id_t elem) const; /** * @brief This function finds the subset of given a nodeId * Subset is stored in a map where keys are the hash-id of the node & values @@ -773,29 +823,45 @@ void Graph::addEdge(const Edge *edge) { if (edge->isWeighted().has_value() && edge->isWeighted().value()) { auto edge_shared = make_shared>(*edge); this->edgeSet.insert(edge_shared); - std::pair>, shared>> elem = {edge_shared->getNodePair().second, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back(std::move(elem)); + + std::pair>, shared>> elem = { + edge_shared->getNodePair().second, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( + std::move(elem)); } else { auto edge_shared = make_shared>(*edge); this->edgeSet.insert(edge_shared); - std::pair>, shared>> elem = {edge_shared->getNodePair().second, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back(std::move(elem)); + + std::pair>, shared>> elem = { + edge_shared->getNodePair().second, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( + std::move(elem)); } } else { if (edge->isWeighted().has_value() && edge->isWeighted().value()) { auto edge_shared = make_shared>(*edge); this->edgeSet.insert(edge_shared); - std::pair>, shared>> elem = {edge_shared->getNodePair().second, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back(std::move(elem)); - std::pair>, shared>> elem1 = {edge_shared->getNodePair().first, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back(std::move(elem1)); + + std::pair>, shared>> elem = { + edge_shared->getNodePair().second, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( + std::move(elem)); + std::pair>, shared>> elem1 = { + edge_shared->getNodePair().first, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back( + std::move(elem1)); } else { auto edge_shared = make_shared>(*edge); this->edgeSet.insert(edge_shared); - std::pair>, shared>> elem = {edge_shared->getNodePair().second, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back(std::move(elem)); - std::pair>, shared>> elem1 = {edge_shared->getNodePair().first, edge_shared}; - (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back(std::move(elem1)); + + std::pair>, shared>> elem = { + edge_shared->getNodePair().second, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().first].push_back( + std::move(elem)); + std::pair>, shared>> elem1 = { + edge_shared->getNodePair().first, edge_shared}; + (*cachedAdjMatrix)[edge_shared->getNodePair().second].push_back( + std::move(elem1)); } } } @@ -803,19 +869,37 @@ void Graph::addEdge(const Edge *edge) { template void Graph::addEdge(shared> edge) { this->edgeSet.insert(edge); + /* Adding new edge in cached adjacency matrix */ - if(edge.get()->isDirected().has_value() && edge.get()->isDirected().value()){ - std::pair>, shared>> elem = {edge.get()->getNodePair().second, edge}; - (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back(std::move(elem)); - } - else{ - std::pair>, shared>> elem = {edge.get()->getNodePair().second, edge}; - (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back(std::move(elem)); - std::pair>, shared>> elem1 = {edge.get()->getNodePair().first, edge}; - (*cachedAdjMatrix)[edge.get()->getNodePair().second].push_back(std::move(elem1)); + if (edge.get()->isDirected().has_value() && + edge.get()->isDirected().value()) { + std::pair>, shared>> elem = { + edge.get()->getNodePair().second, edge}; + (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back( + std::move(elem)); + } else { + std::pair>, shared>> elem = { + edge.get()->getNodePair().second, edge}; + (*cachedAdjMatrix)[edge.get()->getNodePair().first].push_back( + std::move(elem)); + std::pair>, shared>> elem1 = { + edge.get()->getNodePair().first, edge}; + (*cachedAdjMatrix)[edge.get()->getNodePair().second].push_back( + std::move(elem1)); } } +template +void Graph::addNode(const Node *node) { + auto node_shared = make_shared>(*node); + this->isolatedNodesSet.insert(node_shared); +} + +template +void Graph::addNode(shared> node) { + this->isolatedNodesSet.insert(node); +} + template void Graph::removeEdge(const CXXGraph::id_t edgeId) { auto edgeOpt = Graph::getEdge(edgeId); @@ -828,28 +912,57 @@ void Graph::removeEdge(const CXXGraph::id_t edgeId) { int delIndex = -1; int i = 0; /* Removing the edge from the cached adjacency matrix */ - for(auto elem : (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first]){ - if(elem.second.get()->getId() == edgeId){ + for (auto elem : + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first]) { + if (elem.second.get()->getId() == edgeId) { delIndex = i; break; } i++; } - if(delIndex != -1){ - (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first].erase((*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first].begin()+delIndex); + if (delIndex != -1) { + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first].erase( + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().first] + .begin() + + delIndex); } - + delIndex = -1; i = 0; - for(auto elem : (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second]){ - if(elem.second.get()->getId() == edgeId){ + for (auto elem : + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second]) { + if (elem.second.get()->getId() == edgeId) { delIndex = i; break; } i++; } - if(delIndex != -1){ - (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second].erase((*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second].begin()+delIndex); + if (delIndex != -1) { + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second].erase( + (*cachedAdjMatrix)[edgeOpt.value().get()->getNodePair().second] + .begin() + + delIndex); + } + } +} + +template +void Graph::removeNode(const std::string &nodeUserId) { + auto nodeOpt = getNode(nodeUserId); + auto isolatedNodeIt = isolatedNodesSet.find(nodeOpt.value()); + + if (nodeOpt.has_value() && isolatedNodeIt != isolatedNodesSet.end()) { + // The node is isolated + isolatedNodesSet.erase(isolatedNodeIt); + } else if (nodeOpt.has_value()) { + // The node is not isolated + // Remove the edges containing the node + auto edgeset = edgeSet; + for (auto edgeIt : edgeset) { + if (edgeIt->getNodePair().first->getUserId() == nodeUserId || + edgeIt->getNodePair().second->getUserId() == nodeUserId) { + this->removeEdge(edgeIt->getId()); + } } } } @@ -868,19 +981,18 @@ bool Graph::findEdge(shared> v1, shared> v2, CXXGraph::id_t &id) const { // This could be made faster by looking for the edge hash, assuming we hash // based on node data, instead of a unique integer - if(cachedAdjMatrix.get() != NULL && cachedAdjMatrix->size() != 0){ + if (cachedAdjMatrix.get() != NULL && cachedAdjMatrix->size() != 0) { /* Searching for the edge using cached adjacency matrix */ - for(auto elem : (*cachedAdjMatrix)[v1]){ - if(elem.first == v2){ + for (auto elem : (*cachedAdjMatrix)[v1]) { + if (elem.first == v2) { id = elem.second.get()->getId(); return true; } } - } - else{ + } else { /* Searching for the edge using edgeset */ - + for (auto e : this->edgeSet) { if ((e->getNodePair().first == v1) && (e->getNodePair().second == v2)) { id = e->getId(); @@ -910,39 +1022,31 @@ bool Graph::findEdge(shared> v1, shared> v2, } template -const std::unordered_set>, nodeHash> -Graph::getNodeSet() const { - std::unordered_set>, nodeHash> nodeSet; +const T_NodeSet Graph::getNodeSet() const { + T_NodeSet nodeSet; + for (const auto &edgeSetIt : edgeSet) { nodeSet.insert(edgeSetIt->getNodePair().first); nodeSet.insert(edgeSetIt->getNodePair().second); } - /* - std::deque *> nodeSet; - for (const auto &edge : edgeSet) - { - if (std::find_if(nodeSet.begin(), nodeSet.end(), [edge](const Node - *node) { return (*edge->getNodePair().first == *node); }) == nodeSet.end()) - { - nodeSet.push_back(edge->getNodePair().first); - } - if (std::find_if(nodeSet.begin(), nodeSet.end(), [edge](const Node - *node) { return (*edge->getNodePair().second == *node); }) == nodeSet.end()) - { - nodeSet.push_back(edge->getNodePair().second); - } - } - */ + // Merge with the isolated nodes + nodeSet.insert(this->isolatedNodesSet.begin(), this->isolatedNodesSet.end()); + return nodeSet; } +template +const T_NodeSet Graph::getIsolatedNodeSet() const { + return isolatedNodesSet; +} + template void Graph::setNodeData(const std::string &nodeUserId, T data) { - auto nodeset = this->nodeSet(); + auto nodeSet = this->nodeSet(); auto nodeIt = std::find_if( - nodeset.begin(), nodeset.end(), + nodeSet.begin(), nodeSet.end(), [&nodeUserId](auto node) { return node->getUserId() == nodeUserId; }); - (*nodeIt)->setData(std::move(data)); + std::const_pointer_cast>(*nodeIt)->setData(std::move(data)); } template @@ -965,6 +1069,18 @@ const std::optional>> Graph::getEdge( return std::nullopt; } +template +const std::optional>> Graph::getNode( + const std::string &nodeUserId) const { + for (const auto &it : getNodeSet()) { + if (it->getUserId() == nodeUserId) { + return it; + } + } + + return std::nullopt; +} + template std::unordered_set>, nodeHash> Graph::nodeSet() { std::unordered_set>, nodeHash> nodeSet; @@ -974,6 +1090,9 @@ std::unordered_set>, nodeHash> Graph::nodeSet() { nodeSet.insert( std::const_pointer_cast>(edgeSetIt->getNodePair().second)); } + for (auto &isNodeIt : isolatedNodesSet) { + nodeSet.insert(std::const_pointer_cast>(isNodeIt)); + } return nodeSet; } @@ -1300,7 +1419,8 @@ int Graph::compressFile(const std::string &inputFile, } unsigned int zippedBytes; - zippedBytes = gzwrite(outFileZ, content_ptr, (unsigned int)strlen(content_ptr)); + zippedBytes = + gzwrite(outFileZ, content_ptr, (unsigned int)strlen(content_ptr)); ifs.close(); gzclose(outFileZ); @@ -1637,7 +1757,8 @@ const DijkstraResult Graph::dijkstra(const Node &source, } 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)); - parent[elem.first.get()->getUserId()] = currentNode.get()->getUserId(); + parent[elem.first.get()->getUserId()] = + currentNode.get()->getUserId(); } } else if (elem.second->isDirected().has_value() && !elem.second->isDirected().value()) { @@ -1650,7 +1771,8 @@ const DijkstraResult Graph::dijkstra(const Node &source, } 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)); - parent[elem.first.get()->getUserId()] = currentNode.get()->getUserId(); + parent[elem.first.get()->getUserId()] = + currentNode.get()->getUserId(); } } else { // ERROR it shouldn't never returned ( does not exist a Node @@ -1671,12 +1793,12 @@ const DijkstraResult Graph::dijkstra(const Node &source, result.errorMessage = ""; result.result = dist[*target_node_it]; std::string id = target.getUserId(); - while(parent[id] != ""){ + while (parent[id] != "") { result.path.push_back(id); id = parent[id]; } result.path.push_back(source.getUserId()); - std::reverse(result.path.begin(),result.path.end()); + std::reverse(result.path.begin(), result.path.end()); return result; } result.errorMessage = ERR_TARGET_NODE_NOT_REACHABLE; @@ -2081,7 +2203,7 @@ const MstResult Graph::kruskal() const { result.errorMessage = ERR_DIR_GRAPH; return result; } - auto nodeSet = Graph::getNodeSet(); + const auto nodeSet = Graph::getNodeSet(); auto n = nodeSet.size(); // check if all edges are weighted and store the weights @@ -2607,7 +2729,7 @@ bool Graph::isCyclicDirectedGraphBFS() const { } // Vertices that need to be traversed. - auto remain = Graph::getNodeSet().size(); + auto remain = nodeSet.size(); // While there are safe nodes that we can visit. while (!can_be_solved.empty()) { auto solved = can_be_solved.front(); diff --git a/test/GraphTest.cpp b/test/GraphTest.cpp index a0a46a3d2..e1a1460af 100644 --- a/test/GraphTest.cpp +++ b/test/GraphTest.cpp @@ -120,7 +120,7 @@ TEST(GraphTest, FindEdge_Test) { edgeSet.insert(make_shared>(edge)); edgeSet.insert(make_shared>(edge2)); CXXGraph::Graph graph(edgeSet); - unsigned long long edgeId = 0; + size_t edgeId = 0; ASSERT_TRUE(graph.findEdge(&node1,&node2,edgeId)); CXXGraph::UndirectedEdge edge3(3, node1, node3); @@ -684,3 +684,193 @@ TEST(ReverseDirectedGraphTest, test_exception) { CXXGraph::Graph mixedGraph(mixedEdgeSet); ASSERT_THROW(mixedGraph.reverseDirectedGraph(), std::runtime_error); } + +TEST(IsolatedNodeGraphTest, Test_AddNode1) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::DirectedEdge edge1(1, node1, node2); + CXXGraph::DirectedEdge edge2(2, node2, node1); + CXXGraph::DirectedEdge edge3(3, node1, node3); + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + CXXGraph::Graph graph(edgeSet); + + // Create an isolated node and add it to the graph + CXXGraph::Node node4("4", 4); + graph.addNode(make_shared>(node4)); + + // Check that the number of nodes in the graph is 4 + ASSERT_EQ(graph.getNodeSet().size(), 4); + ASSERT_EQ(graph.getIsolatedNodeSet().size(), 1); + ASSERT_EQ(graph.getEdgeSet().size(), 3); +} + +TEST(IsolatedNodeGraphTest, Test_AddNode2) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::DirectedEdge edge1(1, node1, node2); + CXXGraph::DirectedEdge edge2(2, node2, node1); + CXXGraph::DirectedEdge edge3(3, node1, node3); + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + CXXGraph::Graph graph(edgeSet); + + // Create an isolated node and add it to the graph + CXXGraph::Node node4("4", 4); + graph.addNode(&node4); + + // Check that the number of nodes in the graph is 4 + ASSERT_EQ(graph.getNodeSet().size(), 4); + ASSERT_EQ(graph.getIsolatedNodeSet().size(), 1); + ASSERT_EQ(graph.getEdgeSet().size(), 3); +} + +TEST(TestRemoveNode, Test_isolatedNode) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::DirectedEdge edge1(1, node1, node2); + CXXGraph::DirectedEdge edge2(2, node2, node1); + CXXGraph::DirectedEdge edge3(3, node1, node3); + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + CXXGraph::Graph graph(edgeSet); + + // Create isolated nodes and add them to the graph + CXXGraph::Node node4("4", 4); + CXXGraph::Node node5("5", 5); + CXXGraph::Node node6("6", 6); + graph.addNode(make_shared>(node4)); + graph.addNode(make_shared>(node5)); + graph.addNode(make_shared>(node6)); + + // Check the initial number of edges and nodes + ASSERT_EQ(graph.getNodeSet().size(), 6); + ASSERT_EQ(graph.getIsolatedNodeSet().size(), 3); + ASSERT_EQ(graph.getEdgeSet().size(), 3); + + // We now remove node 5 + graph.removeNode("5"); + + // Check the final number of edges and nodes + ASSERT_EQ(graph.getNodeSet().size(), 5); + ASSERT_EQ(graph.getIsolatedNodeSet().size(), 2); + ASSERT_EQ(graph.getEdgeSet().size(), 3); +} + +TEST(TestRemoveNode, Test_connectedNode) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::DirectedEdge edge1(1, node1, node2); + CXXGraph::DirectedEdge edge2(2, node2, node1); + CXXGraph::DirectedEdge edge3(3, node1, node3); + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + CXXGraph::Graph graph(edgeSet); + + // Create isolated nodes and add them to the graph + CXXGraph::Node node4("4", 4); + CXXGraph::Node node5("5", 5); + CXXGraph::Node node6("6", 6); + graph.addNode(make_shared>(node4)); + graph.addNode(make_shared>(node5)); + graph.addNode(make_shared>(node6)); + + // Check the initial number of edges and nodes + ASSERT_EQ(graph.getNodeSet().size(), 6); + ASSERT_EQ(graph.getIsolatedNodeSet().size(), 3); + ASSERT_EQ(graph.getEdgeSet().size(), 3); + + // We now remove node 2 + graph.removeNode("2"); + + // Check the final number of edges and nodes + ASSERT_EQ(graph.getNodeSet().size(), 5); + ASSERT_EQ(graph.getIsolatedNodeSet().size(), 3); + ASSERT_EQ(graph.getEdgeSet().size(), 1); +} + +TEST(TestGetNode, Test_1) { + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::DirectedEdge edge1(1, node1, node2); + CXXGraph::DirectedEdge edge2(2, node2, node1); + CXXGraph::DirectedEdge edge3(3, node1, node3); + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + CXXGraph::Graph graph(edgeSet); + + auto node_found = graph.getNode("2"); + ASSERT_TRUE(node_found.has_value()); + ASSERT_EQ(node_found.value()->getUserId(), "2"); + + auto node_notfound = graph.getNode("5"); + ASSERT_FALSE(node_notfound.has_value()); +} + +TEST(GraphTest, set_data_isolated) { + // Create the graph + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + std::pair *, const CXXGraph::Node *> pairNode( + &node1, &node2); + CXXGraph::DirectedEdge edge1(1, pairNode); + CXXGraph::DirectedEdge edge2(2, node2, node3); + CXXGraph::UndirectedEdge edge3(3, node1, node3); + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + CXXGraph::Graph graph(edgeSet); + + // Create isolated nodes and add them to the graph + CXXGraph::Node node4("4", 4); + CXXGraph::Node node5("5", 5); + CXXGraph::Node node6("6", 6); + graph.addNode(make_shared>(node4)); + graph.addNode(make_shared>(node5)); + graph.addNode(make_shared>(node6)); + + std::map initial_values; + // Construct map with the initial values of the nodes data + for (const auto &nodeIt : graph.getNodeSet()) { + initial_values[nodeIt->getUserId()] = nodeIt->getData(); + } + // Change the data contained in the nodes singularly + std::map new_values; + for (const auto &nodeIt : graph.getNodeSet()) { + int r = std::rand(); + graph.setNodeData(nodeIt->getUserId(), r); + new_values[nodeIt->getUserId()] = r; + } + // Check the final values of the node data + for (const auto &nodeIt : graph.getNodeSet()) { + ASSERT_EQ(nodeIt->getData(), new_values[nodeIt->getUserId()]); + } + + // Now set the data of all the nodes at once + std::map data_values; + for (const auto &nodeIt : graph.getNodeSet()) { + int r = std::rand(); + data_values[nodeIt->getUserId()] = r; + } + graph.setNodeData(data_values); + for (const auto &nodeIt : graph.getNodeSet()) { + ASSERT_EQ(nodeIt->getData(), data_values[nodeIt->getUserId()]); + } +}