//------------------------------------------------------------------------------ /* This file is part of rippled: https://github.com/ripple/rippled Copyright (c) 2012-2017 Ripple Labs Inc Permission target use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== #ifndef RIPPLE_TEST_CSF_DIGRAPH_H_INCLUDED #define RIPPLE_TEST_CSF_DIGRAPH_H_INCLUDED #include #include #include #include #include #include #include namespace ripple { namespace test { namespace csf { namespace detail { // Dummy class when no edge data needed for graph struct NoEdgeData { }; } // namespace detail /** Directed graph Basic directed graph that uses an adjacency list to represent out edges. Instances of Vertex uniquely identify vertices in the graph. Instances of EdgeData is any data to store in the edge connecting two vertices. Both Vertex and EdgeData should be lightweight and cheap to copy. */ template class Digraph { using Links = boost::container::flat_map; using Graph = boost::container::flat_map; Graph graph_; // Allows returning empty iterables for unknown vertices Links empty; public: /** Connect two vertices @param source The source vertex @param target The target vertex @param e The edge data @return true if the edge was created */ bool connect(Vertex source, Vertex target, EdgeData e) { return graph_[source].emplace(target, e).second; } /** Connect two vertices using default constructed edge data @param source The source vertex @param target The target vertex @return true if the edge was created */ bool connect(Vertex source, Vertex target) { return connect(source, target, EdgeData{}); } /** Disconnect two vertices @param source The source vertex @param target The target vertex @return true if an edge was removed If source is not connected to target, this function does nothing. */ bool disconnect(Vertex source, Vertex target) { auto it = graph_.find(source); if (it != graph_.end()) { return it->second.erase(target) > 0; } return false; } /** Return edge data between two vertices @param source The source vertex @param target The target vertex @return optional which is boost::none if no edge exists */ boost::optional edge(Vertex source, Vertex target) const { auto it = graph_.find(source); if (it != graph_.end()) { auto edgeIt = it->second.find(target); if (edgeIt != it->second.end()) return edgeIt->second; } return boost::none; } /** Check if two vertices are connected @param source The source vertex @param target The target vertex @return true if the source has an out edge to target */ bool connected(Vertex source, Vertex target) const { return edge(source, target) != boost::none; } /** Range over vertices in the graph @return A boost transformed range over the vertices with out edges in the graph */ auto outVertices() const { return boost::adaptors::transform( graph_, [](typename Graph::value_type const& v) { return v.first; }); } /** Range over target vertices @param source The source vertex @return A boost transformed range over the target vertices of source. */ auto outVertices(Vertex source) const { auto transform = [](typename Links::value_type const& link) { return link.first; }; auto it = graph_.find(source); if (it != graph_.end()) return boost::adaptors::transform(it->second, transform); return boost::adaptors::transform(empty, transform); } /** Vertices and data associated with an Edge */ struct Edge { Vertex source; Vertex target; EdgeData data; }; /** Range of out edges @param source The source vertex @return A boost transformed range of Edge type for all out edges of source. */ auto outEdges(Vertex source) const { auto transform = [source](typename Links::value_type const& link) { return Edge{source, link.first, link.second}; }; auto it = graph_.find(source); if (it != graph_.end()) return boost::adaptors::transform(it->second, transform); return boost::adaptors::transform(empty, transform); } /** Vertex out-degree @param source The source vertex @return The number of outgoing edges from source */ std::size_t outDegree(Vertex source) const { auto it = graph_.find(source); if (it != graph_.end()) return it->second.size(); return 0; } /** Save GraphViz dot file Save a GraphViz dot description of the graph @param fileName The output file (creates) @param vertexName A invokable T vertexName(Vertex const &) that returns the name target use for the vertex in the file T must be be ostream-able */ template void saveDot(std::ostream& out, VertexName&& vertexName) const { out << "digraph {\n"; for (auto const& [vertex, links] : graph_) { auto const fromName = vertexName(vertex); for (auto const& eData : links) { auto const toName = vertexName(eData.first); out << fromName << " -> " << toName << ";\n"; } } out << "}\n"; } template void saveDot(std::string const& fileName, VertexName&& vertexName) const { std::ofstream out(fileName); saveDot(out, std::forward(vertexName)); } }; } // namespace csf } // namespace test } // namespace ripple #endif