From 050d6001a22760d2086ae0fa938aa52e819c870d Mon Sep 17 00:00:00 2001 From: Marco Poletti Date: Sun, 9 Nov 2014 18:18:40 +0000 Subject: [PATCH] Optimize SemistaticGraph using shallow copy semantics to share data with the NormalizedComponent. Decreases injection time by ~15%. --- CMakeLists.txt | 6 +- .../data_structures/semistatic_graph.defn.h | 70 +++----- .../impl/data_structures/semistatic_graph.h | 43 +++-- .../semistatic_graph.templates.h | 155 +++++++++++++++--- .../impl/storage/injector_storage.defn.h | 2 +- include/fruit/normalized_component.h | 1 + src/injector_storage.cpp | 31 +--- tests/data_structures/semistatic_graph.cpp | 31 ++-- 8 files changed, 205 insertions(+), 134 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 047adac1..f81f3818 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,9 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") endif() -add_definitions("-std=c++11 -W -Wall") +add_definitions(-std=c++11 -W -Wall) if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") - add_definitions("-g -O2 -Werror -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -ftemplate-backtrace-limit=0") + add_definitions(-g -O2 -Werror -DFRUIT_EXTRA_DEBUG -D_GLIBCXX_DEBUG -ftemplate-backtrace-limit=0) endif() include_directories(${CMAKE_SOURCE_DIR}/include) @@ -15,7 +15,7 @@ include_directories(${CMAKE_SOURCE_DIR}/include) #set(CMAKE_CXX_COMPILER "/usr/bin/clang++") # Unsafe, only for debugging/benchmarking. -#add_definitions("-DFRUIT_NO_LOOP_CHECK") +#add_definitions(-DFRUIT_NO_LOOP_CHECK) set(INSTALL_LIBRARY_DIR lib CACHE PATH "Installation directory for libraries") diff --git a/include/fruit/impl/data_structures/semistatic_graph.defn.h b/include/fruit/impl/data_structures/semistatic_graph.defn.h index d3507403..95873e8a 100644 --- a/include/fruit/impl/data_structures/semistatic_graph.defn.h +++ b/include/fruit/impl/data_structures/semistatic_graph.defn.h @@ -31,20 +31,20 @@ inline SemistaticGraph::node_iterator::node_iterator(typename std: template inline Node& SemistaticGraph::node_iterator::getNode() { - assert(itr->edgesBeginOffset != invalidEdgesBeginOffset); + assert(itr->edgesBegin != 1); return itr->node; } template inline bool SemistaticGraph::node_iterator::isTerminal() { - assert(itr->edgesBeginOffset != invalidEdgesBeginOffset); - return itr->edgesBeginOffset == 0; + assert(itr->edgesBegin != 1); + return itr->edgesBegin == 0; } template inline void SemistaticGraph::node_iterator::setTerminal() { - assert(itr->edgesBeginOffset != invalidEdgesBeginOffset); - itr->edgesBeginOffset = 0; + assert(itr->edgesBegin != 1); + itr->edgesBegin = 0; } template @@ -60,19 +60,19 @@ inline SemistaticGraph::const_node_iterator::const_node_iterator( template inline const Node& SemistaticGraph::const_node_iterator::getNode() { - assert(itr->edgesBeginOffset != invalidEdgesBeginOffset); + assert(itr->edgesBegin != 1); return itr->node; } template inline bool SemistaticGraph::const_node_iterator::isTerminal() { - assert(itr->edgesBeginOffset != invalidEdgesBeginOffset); + assert(itr->edgesBegin != 1); return itr->edgesBeginOffset == 0; } template inline void SemistaticGraph::const_node_iterator::setTerminal() { - assert(itr->edgesBeginOffset != invalidEdgesBeginOffset); + assert(itr->edgesBegin != 1); itr->edgesBeginOffset = 0; } @@ -83,9 +83,10 @@ inline bool SemistaticGraph::const_node_iterator::operator==(const template -inline typename SemistaticGraph::edge_iterator SemistaticGraph::node_iterator::neighborsBegin( - SemistaticGraph& graph) { - return edge_iterator{graph.edgesStorage.data() + itr->edgesBeginOffset}; +inline typename SemistaticGraph::edge_iterator SemistaticGraph::node_iterator::neighborsBegin() { + assert(itr->edgesBegin != 0); + assert(itr->edgesBegin != 1); + return edge_iterator{reinterpret_cast(itr->edgesBegin)}; } template @@ -133,7 +134,7 @@ inline typename SemistaticGraph::const_node_iterator SemistaticGra return const_node_iterator{nodes.end()}; } else { auto itr = nodes.begin() + internalNodeIdPtr->id; - if (itr->edgesBeginOffset == invalidEdgesBeginOffset) { + if (itr->edgesBegin == 1) { return const_node_iterator{nodes.end()}; } return const_node_iterator{itr}; @@ -147,7 +148,7 @@ inline typename SemistaticGraph::node_iterator SemistaticGraphid; - if (itr->edgesBeginOffset == invalidEdgesBeginOffset) { + if (itr->edgesBegin == 1) { return node_iterator{nodes.end()}; } return node_iterator{itr}; @@ -156,6 +157,7 @@ inline typename SemistaticGraph::node_iterator SemistaticGraph inline SemistaticGraphInternalNodeId SemistaticGraph::getOrAllocateInternalNodeId(NodeId nodeId) { + // TODO: Optimize this, do a single lookup. InternalNodeId* internalNodeIdPtr = nodeIndexMap.find(nodeId); if (internalNodeIdPtr != nullptr) { return *internalNodeIdPtr; @@ -164,49 +166,21 @@ inline SemistaticGraphInternalNodeId SemistaticGraph::getOrAllocat std::size_t nodeIndex = nodes.size(); nodeIndexMap.insert(nodeId, InternalNodeId{nodeIndex}, [](InternalNodeId x, InternalNodeId){ return x;}); nodes.push_back(NodeData{ -#ifndef NDEBUG +#ifdef FRUIT_EXTRA_DEBUG nodeId, #endif - Node(), ~std::size_t(0)}); + Node(), 1}); return InternalNodeId{nodeIndex}; } } template -template -void SemistaticGraph::setNode(NodeId nodeId, Node node, EdgeIter edgesBegin, EdgeIter edgesEnd, Combine combine) { - InternalNodeId internalNodeId = getOrAllocateInternalNodeId(nodeId); - NodeData& nodeData = nodes[internalNodeId.id]; -#ifndef NDEBUG - nodeData.key = nodeId; -#endif - if (nodeData.edgesBeginOffset == ~std::size_t(0)) { - // The node did not exist. - nodeData.node = node; - } else { - nodeData.node = combine(nodeData.node, node); - } - nodeData.edgesBeginOffset = edgesStorage.size(); - for (EdgeIter i = edgesBegin; i != edgesEnd; ++i) { - edgesStorage.push_back(getOrAllocateInternalNodeId(*i)); - } -} - -template -template -void SemistaticGraph::setTerminalNode(NodeId nodeId, Node node, Combine combine) { - InternalNodeId internalNodeId = getOrAllocateInternalNodeId(nodeId); +void SemistaticGraph::changeNodeToTerminal(NodeId nodeId) { + assert(nodeIndexMap.find(nodeId) != nullptr); + InternalNodeId internalNodeId = nodeIndexMap.at(nodeId); NodeData& nodeData = nodes[internalNodeId.id]; -#ifndef NDEBUG - nodeData.key = nodeId; -#endif - if (nodeData.edgesBeginOffset == ~std::size_t(0)) { - // The node did not exist. - nodeData.node = node; - } else { - nodeData.node = combine(nodeData.node, node); - } - nodeData.edgesBeginOffset = 0; + assert(nodeData.edgesBegin != 1); + nodeData.edgesBegin = 0; } } // namespace impl diff --git a/include/fruit/impl/data_structures/semistatic_graph.h b/include/fruit/impl/data_structures/semistatic_graph.h index 255ca654..b3079320 100644 --- a/include/fruit/impl/data_structures/semistatic_graph.h +++ b/include/fruit/impl/data_structures/semistatic_graph.h @@ -19,6 +19,10 @@ #include "semistatic_map.h" +#ifdef FRUIT_EXTRA_DEBUG +#include +#endif + namespace fruit { namespace impl { @@ -50,20 +54,20 @@ class SemistaticGraph { // nodeIndexMap contains all known NodeIds, including ones known only due to an outgoing edge ending there from another node. SemistaticMap nodeIndexMap; - static constexpr std::size_t invalidEdgesBeginOffset = ~std::size_t(0); - struct NodeData { -#ifndef NDEBUG +#ifdef FRUIT_EXTRA_DEBUG NodeId key; #endif Node node; - // (edgesStorage.begin() + edgesBeginOffset) is the beginning of the edges range. // If edgesBeginOffset==0, this is a terminal node. - // If edgesBeginOffset==invalidEdgesBeginOffset, this node doesn't exist, it's just referenced by another node. - std::size_t edgesBeginOffset; + // If edgesBeginOffset==1, this node doesn't exist, it's just referenced by another node. + // Otherwise, reinterpret_cast(edgesBegin) is the beginning of the edges range. + std::uintptr_t edgesBegin; }; + std::size_t firstUnusedIndex; + std::vector nodes; // Stores vectors of dependencies as contiguous chunks of elements. @@ -73,6 +77,9 @@ class SemistaticGraph { InternalNodeId getOrAllocateInternalNodeId(NodeId nodeId); + // TODO: Remove. + void printEdgesBegin(std::ostream& os, std::uintptr_t edgesBegin); + public: class edge_iterator; @@ -95,7 +102,7 @@ class SemistaticGraph { // Assumes !isTerminal(). // neighborsEnd() is NOT provided/stored for efficiency, the client code is expected to know the number of neighbors. - edge_iterator neighborsBegin(SemistaticGraph& graph); + edge_iterator neighborsBegin(); bool operator==(const node_iterator&) const; }; @@ -159,6 +166,14 @@ class SemistaticGraph { SemistaticGraph(SemistaticGraph&&) = default; SemistaticGraph(const SemistaticGraph&) = default; + // Creates a copy of x with the additional nodes in [first, last). The requirements on NodeIter as the same as for the 2-arg + // constructor. + // The nodes in [first, last) must NOT be already in x, but can be neighbors of nodes in x. + // The new graph will share data with `x', so must be destroyed before `x' is destroyed. + // Also, after this is called, `x' must not be modified until this object has been destroyed. + template + SemistaticGraph(const SemistaticGraph& x, NodeIter first, NodeIter last); + SemistaticGraph& operator=(const SemistaticGraph&) = default; SemistaticGraph& operator=(SemistaticGraph&&) = default; @@ -174,18 +189,10 @@ class SemistaticGraph { node_iterator find(NodeId nodeId); const_node_iterator find(NodeId nodeId) const; - // Sets nodeId as a non-terminal node with outgoing edges [edgesBegin, edgesEnd). - // If the node already exists, combine(oldNode, newNode) is called and the result (also of type Node) is used as the node value. - template - void setNode(NodeId nodeId, Node node, EdgeIter edgesBegin, EdgeIter edgesEnd, Combine combine); - - // Sets nodeId as a terminal node. - // If the node already exists, combine(oldNode, newNode) is called and the result (also of type Node) is used as the node value. - // For cases where the node is known to exist, use the setTerminal() method on an iterator, it's faster. - template - void setTerminalNode(NodeId nodeId, Node node, Combine combine); + // Changes the node with ID nodeId (that must exist) to a terminal node. + void changeNodeToTerminal(NodeId nodeId); -#ifndef NDEBUG +#ifdef FRUIT_EXTRA_DEBUG // Emits a runtime error if some node was not created but there is an edge pointing to it. void checkFullyConstructed(); #endif diff --git a/include/fruit/impl/data_structures/semistatic_graph.templates.h b/include/fruit/impl/data_structures/semistatic_graph.templates.h index e91f5032..e4b00779 100644 --- a/include/fruit/impl/data_structures/semistatic_graph.templates.h +++ b/include/fruit/impl/data_structures/semistatic_graph.templates.h @@ -26,7 +26,7 @@ #include -#ifndef NDEBUG +#ifdef FRUIT_EXTRA_DEBUG #include #endif @@ -48,12 +48,28 @@ struct indexing_iterator { } }; +#ifdef FRUIT_EXTRA_DEBUG +template +void SemistaticGraph::printEdgesBegin(std::ostream& os, std::uintptr_t edgesBegin) { + if (edgesBegin == 0) { + os << "[no edges, terminal node]"; + } else if (edgesBegin == 1) { + os << "[node not fully constructed]"; + } else { + SemistaticGraphInternalNodeId* p = reinterpret_cast(edgesBegin); + if (edgesStorage.data() <= p && p < edgesStorage.data() + edgesStorage.size()) { + os << "edgesStorage.data() + " << (p - edgesStorage.data()); + } else { + os << p; + } + } +} +#endif + template template SemistaticGraph::SemistaticGraph(NodeIter first, NodeIter last) { - std::size_t num_edges = 0; - std::size_t firstUnusedIndex; // Step 1: assign IDs to all nodes, fill `nodeIndexMap' and set `firstUnusedIndex'. std::unordered_set nodeIds; @@ -77,22 +93,18 @@ SemistaticGraph::SemistaticGraph(NodeIter first, NodeIter last) { // Note that not all of these will be assigned in the loop below. nodes.resize(firstUnusedIndex, NodeData{ -#ifndef NDEBUG +#ifdef FRUIT_EXTRA_DEBUG NodeId(), #endif - Node(), ~std::size_t(0)}); + Node(), 1}); -#ifndef NDEBUG - { #ifdef FRUIT_EXTRA_DEBUG + { std::cerr << "SemistaticGraph constructed with the following known types:" << std::endl; -#endif std::size_t i = 0; for (typename std::unordered_set::iterator itr = nodeIds.begin(); itr != nodeIds.end(); ++i, ++itr) { nodes[i].key = *itr; -#ifdef FRUIT_EXTRA_DEBUG std::cerr << i << ": " << *itr << std::endl; -#endif } } #endif @@ -101,43 +113,136 @@ SemistaticGraph::SemistaticGraph(NodeIter first, NodeIter last) { edgesStorage.reserve(num_edges + 1); edgesStorage.resize(1); -#ifdef FRUIT_EXTRA_DEBUG - std::cerr << "Nodes:" << std::endl; -#endif for (NodeIter i = first; i != last; ++i) { std::size_t nodeId = nodeIndexMap.at(i->getId()).id; -#ifdef FRUIT_EXTRA_DEBUG - std::cerr << nodeId << ": " << i->getId() << " depends on"; -#endif nodes[nodeId].node = i->getValue(); if (i->isTerminal()) { - nodes[nodeId].edgesBeginOffset = 0; -#ifdef FRUIT_EXTRA_DEBUG - std::cerr << " (none, terminal)"; -#endif + nodes[nodeId].edgesBegin = 0; } else { - nodes[nodeId].edgesBeginOffset = edgesStorage.size(); + nodes[nodeId].edgesBegin = reinterpret_cast(edgesStorage.data() + edgesStorage.size()); for (auto j = i->getEdgesBegin(); j != i->getEdgesEnd(); ++j) { + std::size_t otherNodeId = nodeIndexMap.at(*j).id; + edgesStorage.push_back(InternalNodeId{otherNodeId}); + } + } + } + #ifdef FRUIT_EXTRA_DEBUG + std::cerr << "Nodes:" << std::endl; + for (NodeIter i = first; i != last; ++i) { + std::size_t nodeId = nodeIndexMap.at(i->getId()).id; + std::cerr << nodeId << ": " << i->getId() << " depends on"; + if (i->isTerminal()) { + std::cerr << " (none, terminal)"; + } else { + for (auto j = i->getEdgesBegin(); j != i->getEdgesEnd(); ++j) { std::cerr << " " << *j; + } + } + std::cerr << std::endl; + std::cerr << "nodes[" << nodeId << "].edgesBegin == "; // << nodes[nodeId].edgesBegin << std::endl; + printEdgesBegin(std::cerr, nodes[nodeId].edgesBegin); + std::cerr << std::endl; + } +#endif +} + +template +template +SemistaticGraph::SemistaticGraph(const SemistaticGraph& x, NodeIter first, NodeIter last) + // TODO: Do a shallow copy of the index map too. + : nodeIndexMap(x.nodeIndexMap), firstUnusedIndex(x.firstUnusedIndex), nodes(x.nodes) { + + // TODO: The code below is very similar to the other constructor, extract the common parts in separate functions. + + std::size_t num_new_edges = 0; + + // Step 1: assign IDs to new nodes, fill `nodeIndexMap' and update `firstUnusedIndex'. + std::unordered_set nodeIds; + for (NodeIter i = first; i != last; ++i) { + ++firstUnusedIndex; + nodeIndexMap.insert(i->getId(), InternalNodeId{firstUnusedIndex - 1}, [this](InternalNodeId x, InternalNodeId) { + // There was already an index for this TypeId, we don't need to allocate an index after all. + --firstUnusedIndex; + return x; + }); + if (!i->isTerminal()) { + for (auto j = i->getEdgesBegin(); j != i->getEdgesEnd(); ++j) { + ++firstUnusedIndex; + nodeIndexMap.insert(*j, InternalNodeId{firstUnusedIndex - 1}, [this](InternalNodeId x, InternalNodeId) { + // There was already an index for this TypeId, we don't need to allocate an index after all. + --firstUnusedIndex; + return x; + }); + ++num_new_edges; + } + } + } + + // Step 2: fill `nodes' and `edgesStorage' + + // Note that not all of these will be assigned in the loop below. + nodes.resize(firstUnusedIndex, NodeData{ +#ifdef FRUIT_EXTRA_DEBUG + NodeId(), #endif + Node(), 1}); + +#ifdef FRUIT_EXTRA_DEBUG + { + std::cerr << "SemistaticGraph constructed with the following known types:" << std::endl; + std::size_t i = 0; + for (typename std::unordered_set::iterator itr = nodeIds.begin(); itr != nodeIds.end(); ++i, ++itr) { + nodes[i].key = *itr; + std::cerr << i << ": " << *itr << std::endl; + } + } +#endif + + // edgesStorage[0] is unused, that's the reason for the +1 + edgesStorage.reserve(num_new_edges + 1); + edgesStorage.resize(1); + + for (NodeIter i = first; i != last; ++i) { + std::size_t nodeId = nodeIndexMap.at(i->getId()).id; + nodes[nodeId].node = i->getValue(); + if (i->isTerminal()) { + nodes[nodeId].edgesBegin = 0; + } else { + nodes[nodeId].edgesBegin = reinterpret_cast(edgesStorage.data() + edgesStorage.size()); + for (auto j = i->getEdgesBegin(); j != i->getEdgesEnd(); ++j) { std::size_t otherNodeId = nodeIndexMap.at(*j).id; edgesStorage.push_back(InternalNodeId{otherNodeId}); } } + } + #ifdef FRUIT_EXTRA_DEBUG + std::cerr << "Nodes:" << std::endl; + for (NodeIter i = first; i != last; ++i) { + std::size_t nodeId = nodeIndexMap.at(i->getId()).id; + std::cerr << nodeId << ": " << i->getId() << " depends on"; + if (i->isTerminal()) { + std::cerr << " (none, terminal)"; + } else { + for (auto j = i->getEdgesBegin(); j != i->getEdgesEnd(); ++j) { + std::cerr << " " << *j; + } + } + std::cerr << std::endl; + std::cerr << "nodes[" << nodeId << "].edgesBegin == "; //<< nodes[nodeId].edgesBegin << std::endl; + printEdgesBegin(std::cerr, nodes[nodeId].edgesBegin); std::cerr << std::endl; - std::cerr << "nodes[" << nodeId << "].edgesBeginOffset == " << nodes[nodeId].edgesBeginOffset << std::endl; -#endif } +#endif } -#ifndef NDEBUG +#ifdef FRUIT_EXTRA_DEBUG template void SemistaticGraph::checkFullyConstructed() { for (std::size_t i = 0; i < nodes.size(); ++i) { NodeData& data = nodes[i]; - if (data.edgesBeginOffset == invalidEdgesBeginOffset) { + if (data.edgesBegin == 1) { std::cerr << "Fruit bug: the dependency graph was not fully constructed." << std::endl; abort(); } diff --git a/include/fruit/impl/storage/injector_storage.defn.h b/include/fruit/impl/storage/injector_storage.defn.h index 7bbf0a52..62c524c7 100644 --- a/include/fruit/impl/storage/injector_storage.defn.h +++ b/include/fruit/impl/storage/injector_storage.defn.h @@ -194,7 +194,7 @@ inline const std::vector& InjectorStorage::getMultibindings() { inline void InjectorStorage::ensureConstructed(typename SemistaticGraph::node_iterator nodeItr) { NormalizedBindingData& bindingData = nodeItr.getNode(); if (!nodeItr.isTerminal()) { - BindingData::destroy_t destroy = bindingData.create(*this, nodeItr.neighborsBegin(typeRegistry)); + BindingData::destroy_t destroy = bindingData.create(*this, nodeItr.neighborsBegin()); nodeItr.setTerminal(); if (destroy != nullptr) { onDestruction.push_back(destroy); diff --git a/include/fruit/normalized_component.h b/include/fruit/normalized_component.h index d1239233..242291c8 100644 --- a/include/fruit/normalized_component.h +++ b/include/fruit/normalized_component.h @@ -17,6 +17,7 @@ #ifndef FRUIT_NORMALIZED_COMPONENT_H #define FRUIT_NORMALIZED_COMPONENT_H +#include "fruit_forward_decls.h" #include "impl/storage/normalized_component_storage.h" namespace fruit { diff --git a/src/injector_storage.cpp b/src/injector_storage.cpp index c967c9ee..c1abe8f7 100644 --- a/src/injector_storage.cpp +++ b/src/injector_storage.cpp @@ -110,7 +110,7 @@ InjectorStorage::InjectorStorage(ComponentStorage&& component) singletonStorageBegin = new char[total_size + 1]; singletonStorageLastUsed = singletonStorageBegin; -#ifndef NDEBUG +#ifdef FRUIT_EXTRA_DEBUG typeRegistry.checkFullyConstructed(); #endif } @@ -144,40 +144,23 @@ InjectorStorage::InjectorStorage(const NormalizedComponentStorage& normalizedCom }); component.typeRegistry.erase(itr, component.typeRegistry.end()); - typeRegistry = Graph(normalizedComponent.typeRegistry); + typeRegistry = Graph(normalizedComponent.typeRegistry, + NormalizedComponentStorage::BindingDataNodeIter{component.typeRegistry.begin()}, + NormalizedComponentStorage::BindingDataNodeIter{component.typeRegistry.end()}); - // Step 3: Add the new bindings. - for (auto& p : component.typeRegistry) { - TypeId typeId = p.first; - BindingData& b = p.second; - auto combine = [](const NormalizedBindingData& b1, const NormalizedBindingData&) { - assert(false); - return b1; - }; - if (b.isCreated()) { - // Storing as terminal. - typeRegistry.setTerminalNode(typeId, NormalizedBindingData{b.getStoredSingleton()}, combine); - } else { - // Non-terminal, might have deps. - const BindingDeps* bindingDeps = b.getDeps(); - typeRegistry.setNode(typeId, NormalizedBindingData{b.getCreate()}, - bindingDeps->deps, bindingDeps->deps + bindingDeps->num_deps, combine); - } - } - - // Step 4: Update total_size taking into account the new bindings. + // Step 3: Update total_size taking into account the new bindings. for (auto& p : component.typeRegistry) { total_size += maximumRequiredSpace(p.first); } - // Step 5: Add multibindings. + // Step 4: Add multibindings. addMultibindings(typeRegistryForMultibindings, total_size, std::move(component.typeRegistryForMultibindings)); // The +1 is because we waste the first byte (singletonStorageLastUsed points to the beginning of storage). singletonStorageBegin = new char[total_size + 1]; singletonStorageLastUsed = singletonStorageBegin; -#ifndef NDEBUG +#ifdef FRUIT_EXTRA_DEBUG typeRegistry.checkFullyConstructed(); #endif } diff --git a/tests/data_structures/semistatic_graph.cpp b/tests/data_structures/semistatic_graph.cpp index 16319063..c876fd0d 100644 --- a/tests/data_structures/semistatic_graph.cpp +++ b/tests/data_structures/semistatic_graph.cpp @@ -78,7 +78,7 @@ void test_1_node_self_edge() { assert(!(graph.find(2) == graph.end())); assert(graph.at(2).getNode() == "foo"); assert(graph.at(2).isTerminal() == false); - edge_iterator itr = graph.at(2).neighborsBegin(graph); + edge_iterator itr = graph.at(2).neighborsBegin(); assert(itr.getNodeIterator(graph).getNode() == "foo"); assert(itr.getNodeIterator(graph).isTerminal() == false); assert(graph.find(5) == graph.end()); @@ -93,7 +93,7 @@ void test_2_nodes_one_edge() { assert(graph.at(2).isTerminal() == false); assert(graph.at(3).getNode() == "bar"); assert(graph.at(3).isTerminal() == false); - edge_iterator itr = graph.at(3).neighborsBegin(graph); + edge_iterator itr = graph.at(3).neighborsBegin(); assert(itr.getNodeIterator(graph).getNode() == "foo"); assert(itr.getNodeIterator(graph).isTerminal() == false); assert(graph.find(5) == graph.end()); @@ -108,7 +108,7 @@ void test_3_nodes_two_edges() { assert(graph.at(2).isTerminal() == false); assert(graph.at(3).getNode() == "bar"); assert(graph.at(3).isTerminal() == false); - edge_iterator itr = graph.at(3).neighborsBegin(graph); + edge_iterator itr = graph.at(3).neighborsBegin(); assert(itr.getNodeIterator(graph).getNode() == "foo"); assert(itr.getNodeIterator(graph).isTerminal() == false); ++itr; @@ -119,18 +119,19 @@ void test_3_nodes_two_edges() { assert(graph.find(5) == graph.end()); } -void test_set_node() { - vector values = {{2, "foo", {}, false}, {4, "baz", {}, true}}; - Graph graph(values.begin(), values.end()); +void test_add_node() { + vector old_values = {{2, "foo", {}, false}, {4, "baz", {}, true}}; + Graph old_graph(old_values.begin(), old_values.end()); vector edges = {2, 4}; - graph.setNode(3, "bar", edges.begin(), edges.end(), [](string, string y) {return y;}); + vector new_values = {{3, "bar", {2, 4}, false}}; + Graph graph(old_graph, new_values.begin(), new_values.end()); assert(graph.find(0) == graph.end()); assert(!(graph.find(2) == graph.end())); assert(graph.at(2).getNode() == "foo"); assert(graph.at(2).isTerminal() == false); assert(graph.at(3).getNode() == "bar"); assert(graph.at(3).isTerminal() == false); - edge_iterator itr = graph.at(3).neighborsBegin(graph); + edge_iterator itr = graph.at(3).neighborsBegin(); assert(itr.getNodeIterator(graph).getNode() == "foo"); assert(itr.getNodeIterator(graph).isTerminal() == false); ++itr; @@ -144,12 +145,12 @@ void test_set_node() { void test_set_terminal() { vector values = {{2, "foo", {}, false}, {3, "bar", {2, 4}, false}, {4, "baz", {}, true}}; Graph graph(values.begin(), values.end()); - graph.setTerminalNode(3, "bar2", [](string, string y) { return y;}); + graph.changeNodeToTerminal(3); assert(graph.find(0) == graph.end()); assert(!(graph.find(2) == graph.end())); assert(graph.at(2).getNode() == "foo"); assert(graph.at(2).isTerminal() == false); - assert(graph.at(3).getNode() == "bar2"); + assert(graph.at(3).getNode() == "bar"); assert(graph.at(3).isTerminal() == true); assert(graph.at(4).getNode() == "baz"); assert(graph.at(4).isTerminal() == true); @@ -166,7 +167,7 @@ void test_move_constructor() { assert(graph.at(2).isTerminal() == false); assert(graph.at(3).getNode() == "bar"); assert(graph.at(3).isTerminal() == false); - edge_iterator itr = graph.at(3).neighborsBegin(graph); + edge_iterator itr = graph.at(3).neighborsBegin(); assert(itr.getNodeIterator(graph).getNode() == "foo"); assert(itr.getNodeIterator(graph).isTerminal() == false); assert(graph.find(5) == graph.end()); @@ -182,7 +183,7 @@ void test_copy_constructor() { assert(graph.at(2).isTerminal() == false); assert(graph.at(3).getNode() == "bar"); assert(graph.at(3).isTerminal() == false); - edge_iterator itr = graph.at(3).neighborsBegin(graph); + edge_iterator itr = graph.at(3).neighborsBegin(); assert(itr.getNodeIterator(graph).getNode() == "foo"); assert(itr.getNodeIterator(graph).isTerminal() == false); assert(graph.find(5) == graph.end()); @@ -199,7 +200,7 @@ void test_move_assignment() { assert(graph.at(2).isTerminal() == false); assert(graph.at(3).getNode() == "bar"); assert(graph.at(3).isTerminal() == false); - edge_iterator itr = graph.at(3).neighborsBegin(graph); + edge_iterator itr = graph.at(3).neighborsBegin(); assert(itr.getNodeIterator(graph).getNode() == "foo"); assert(itr.getNodeIterator(graph).isTerminal() == false); assert(graph.find(5) == graph.end()); @@ -216,7 +217,7 @@ void test_copy_assignment() { assert(graph.at(2).isTerminal() == false); assert(graph.at(3).getNode() == "bar"); assert(graph.at(3).isTerminal() == false); - edge_iterator itr = graph.at(3).neighborsBegin(graph); + edge_iterator itr = graph.at(3).neighborsBegin(); assert(itr.getNodeIterator(graph).getNode() == "foo"); assert(itr.getNodeIterator(graph).isTerminal() == false); assert(graph.find(5) == graph.end()); @@ -230,7 +231,7 @@ int main() { test_1_node_self_edge(); test_2_nodes_one_edge(); test_3_nodes_two_edges(); - test_set_node(); + test_add_node(); test_set_terminal(); test_move_constructor(); test_copy_constructor();