/* * ╔═╗╦═╗╔═╗╔═╗╦ ╦ C++ Graph library * ║ ╦╠╦╝╠═╣╠═╝╠═╣ Version 1.2.0 * ╚═╝╩╚═╩ ╩╩ ╩ ╩ https://github.com/Terae/Graph * * * Single header-file generated by lumen on 2025-09-05 * * * Licensed under the MIT License . * Copyright (c) 2017 Benjamin BIGEY * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to wholm the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef ROOT_GRAPH_FINAL_H #define ROOT_GRAPH_FINAL_H #include #include #include #include #include #ifdef INCLUDE_JSON_FILE #include "../third-party/json/single_include/nlohmann/json.hpp" #else #include #endif // C++ language standard detection #if (defined(__cplusplus) && __cplusplus >= 202302L) || \ (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L) #define GRAPH_HAS_CPP_23 #define GRAPH_HAS_CPP_20 #define GRAPH_HAS_CPP_17 #define GRAPH_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 202002L) || \ (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) #define GRAPH_HAS_CPP_20 #define GRAPH_HAS_CPP_17 #define GRAPH_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 201703L) || \ (defined(_MSC_VER) && _MSC_VER > 1900 && defined(_HAS_CXX17) && _HAS_CXX17 == 1) #define GRAPH_HAS_CPP_17 #define GRAPH_HAS_CPP_14 #elif (defined(__cplusplus) && __cplusplus >= 201402L) || \ (defined(_HAS_CXX14) && _HAS_CXX14 == 1) #define GRAPH_HAS_CPP_14 #endif // Include optional for C++17 and later #if defined(GRAPH_HAS_CPP_17) #include #endif // Include concepts for C++20 and later #if defined(GRAPH_HAS_CPP_20) #include template concept GraphKey = std::copyable && std::equality_comparable && std::totally_ordered; template concept GraphData = std::copyable; template concept GraphCost = std::copyable && std::equality_comparable && std::totally_ordered && requires(T t) { t + t; t - t; t * t; t / t; }; #endif #if defined(__clang__) #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30500 #error "unsupported Clang version - see https://github.com/terae/structure#supported-compilers" #endif #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 50000 #error "unsupported GCC version - see https://github.com/terae/structure#supported-compilers" #endif #endif #include #include #include #include #include #include #include #include /// allow to disable exceptions #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && not defined(GRAPH_NOEXCEPTION) #define GRAPH_THROW(exception_name) throw detail::exception_name::create(__FUNCTION__); #if defined(__clang__) #define GRAPH_THROW_WITH(exception_name, ...) throw detail::exception_name::create(__FUNCTION__, __VA_ARGS__); #elif defined(__GNUC__) #define GRAPH_THROW_WITH(exception_name, ...) throw detail::exception_name::create(__FUNCTION__, ##__VA_ARGS__); #endif #define GRAPH_TRY try #define GRAPH_CATCH(exception) catch(exception) #else #define GRAPH_THROW(exception) std::abort() #define GRAPH_TRY if (true) #define GRAPH_CATCH(exception) if (false) #endif #include #include #include /** * @brief Enumeration representing the nature of a graph * * Defines whether a graph is directed or undirected, which affects * how edges are handled and how certain algorithms behave. */ enum Nature { DIRECTED = 'd', UNDIRECTED = 'u' }; namespace detail { struct exception : std::exception { /// returns the explanatory string [[nodiscard]] const char* what() const noexcept override { return m.what(); } protected: explicit exception(const char* what_arg) : m(what_arg) {} static std::string name(const std::string &exception_type) { return "[graph.exception." + exception_type + "] "; } private: std::logic_error m; }; struct invalid_argument final : exception { static invalid_argument create(const std::string &function_name, const std::string &what_arg = "Invalid argument") { const std::string w{exception::name("invalid_argument") + what_arg + " when calling '" + function_name + "'."}; return invalid_argument(w.c_str()); } private: explicit invalid_argument(const char* what_arg) : exception(what_arg) {} }; struct unexpected_nullptr final : exception { static unexpected_nullptr create(const std::string &function_name, const std::string &what_arg = "Unexpected nullptr") { const std::string w{exception::name("unexpected_nullptr") + what_arg + " when calling '" + function_name + "'."}; return unexpected_nullptr(w.c_str()); } private: explicit unexpected_nullptr(const char* what_arg) : exception(what_arg) {} }; struct parse_error final : exception { static parse_error create(const std::string &function_name, const std::size_t byte, const std::string &what_arg = "Bad format") { const std::string w{exception::name("parse_error") + "parse error" + (byte != 0 ? " at " + std::to_string(byte) : "") + ": " + what_arg + " when calling '" + function_name + "'."}; return parse_error(w.c_str(), byte); } const std::size_t byte; private: explicit parse_error(const char* what_arg, std::size_t b) : exception(what_arg), byte(b) {} }; struct bad_graph : exception { protected: explicit bad_graph(const char* what_arg) : exception(what_arg) {} static std::string name(const std::string &exception_type) { return "[graph.exception.bad_graph." + exception_type + "] "; } }; struct negative_edge final : bad_graph { static negative_edge create(const std::string &function_name, const std::string &what_arg = "Edge with negative weight") { const std::string w{bad_graph::name("negative_edge") + what_arg + " when calling '" + function_name + "'."}; return negative_edge(w.c_str()); } private: explicit negative_edge(const char* what_arg) : bad_graph(what_arg) {} }; struct negative_weight_cycle final : bad_graph { static negative_weight_cycle create(const std::string &function_name, const std::string &what_arg = "Negative-weight cycle") { const std::string w{bad_graph::name("negative_weight_cycle") + what_arg + " when calling '" + function_name + "'."}; return negative_weight_cycle(w.c_str()); } private: explicit negative_weight_cycle(const char* what_arg) : bad_graph(what_arg) {} }; struct not_complete final : bad_graph { static not_complete create(const std::string &function_name, const std::string &what_arg = "Not complete graph") { const std::string w{bad_graph::name("not_complete") + what_arg + " when calling '" + function_name + "'."}; return not_complete(w.c_str()); } private: explicit not_complete(const char* what_arg) : bad_graph(what_arg) {} }; struct has_cycle final : bad_graph { static has_cycle create(const std::string &function_name, const std::string &what_arg = "Graph with a cycle") { const std::string w{bad_graph::name("has_cycle") + what_arg + " when calling '" + function_name + "'."}; return has_cycle(w.c_str()); } private: explicit has_cycle(const char* what_arg) : bad_graph(what_arg) {} }; /// SECTION degree template class basic_degree; template <> class basic_degree { public: using value_type = std::pair; using type = basic_degree; explicit basic_degree(value_type degree) : _deg(std::move(degree)) {}; basic_degree(std::size_t in, std::size_t out) : basic_degree(std::make_pair(in, out)) {}; [[nodiscard]] value_type get_degree() const { return _deg; } friend bool operator==(const type &t1, const type &t2) { return t1._deg == t2._deg; } friend bool operator==(const value_type &v, const type &t) { return t._deg == v; } friend bool operator==(const type &t, const value_type &v) { return t._deg == v; } friend bool operator< (const type &t1, const type &t2) { return t1._deg < t2._deg; } static basic_degree max() { return {std::numeric_limits::max(), std::numeric_limits::max()}; } static basic_degree min() { return {std::numeric_limits::min(), std::numeric_limits::min()}; } private: value_type _deg; }; template <> class basic_degree { public: using value_type = std::size_t; using type = basic_degree; explicit basic_degree(const value_type &d) : _deg(d) {} basic_degree(const std::size_t in, const std::size_t out) : basic_degree(std::max(in, out)) {} [[nodiscard]] value_type get_degree() const { return _deg; } friend bool operator==(const type &t1, const type &t2) { return t1._deg == t2._deg; } friend bool operator==(const value_type &v, const type &t) { return t._deg == v; } friend bool operator==(const type &t, const value_type &v) { return t._deg == v; } friend bool operator< (const type &t1, const type &t2) { return t1._deg < t2._deg; } static basic_degree max() { return basic_degree(std::numeric_limits::max()); } static basic_degree min() { return basic_degree(std::numeric_limits::min()); } private: value_type _deg; }; /// SECTION helpers template struct is_directed : std::false_type { }; template <> struct is_directed : std::true_type { }; template struct is_undirected : std::false_type { }; template <> struct is_undirected : std::true_type { }; #include /// distinguish value type between map::iterator and shared_ptr: @see https://stackoverflow.com/a/31409532 template using void_t = void; template struct is_map_iterator : std::false_type { }; template struct is_map_iterator()), decltype(*std::declval()), decltype(std::declval() == std::declval()), decltype((*std::declval()).second)> > : std::true_type { }; template ::value>::type> typename std::iterator_traits::value_type::second_type get_value(const V &v, const V &end) { if (v == end) { return static_cast::value_type::second_type>(nullptr); } return (*v).second; } #include /// @return the human-readable name of @tparam T template std::string type_name() { int status; std::string type_name{typeid(T).name()}; char* demangled_name{abi::__cxa_demangle(type_name.c_str(), nullptr, nullptr, &status)}; if (status == 0) { type_name = demangled_name; } std::free(demangled_name); const std::function replace_all = [](std::string & base, const std::string & to_replace, const std::string & replacement) { for (std::string::size_type i{0}; (i = base.find(to_replace, i)) != std::string::npos; ) { base.replace(i, to_replace.length(), replacement); i += replacement.length(); } }; replace_all(type_name, "std::__cxx11::basic_string, std::allocator > >", "std::string>"); replace_all(type_name, "std::__cxx11::basic_string, std::allocator >", "std::string"); return type_name; } template std::istream &read_T(std::istream &is, T &t) { is.ignore(std::numeric_limits::max(), '"'); std::string str; std::getline(is, str, '"'); std::stringstream ss; ss << str; if (!(ss >> t)) { GRAPH_THROW_WITH(parse_error, 5, "Bad type"); } return is; } template<> inline std::istream &read_T(std::istream &is, std::string &str) { is.ignore(std::numeric_limits::max(), '"'); std::getline(is, str, '"'); return is; } template std::istream &read_cost(std::istream &is, C &c) { const auto str = std::string(std::istreambuf_iterator(is), std::istreambuf_iterator()); if (str.find_first_of('"') == std::string::npos && str.find_first_of("infinity") != std::string::npos) c = std::numeric_limits::has_infinity ? std::numeric_limits::infinity() : std::numeric_limits::max(); else { std::stringstream ss; ss << str; read_T(ss, c); } return is; } } // C++11 compatibility for make_unique #if !defined(GRAPH_HAS_CPP_14) namespace std { template std::unique_ptr make_unique(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); } } #endif /** * @brief Base class for nodes in a graph * * This class represents a node in a graph, containing data and connections * to other nodes (edges). It is designed to work with the graph class * to provide a complete graph data structure. * * @tparam Data Type of data stored in the node * @tparam Cost Type of cost associated with edges * @tparam Container Type of container for iterators * @tparam constContainer Type of container for const iterators */ template class basic_node { public: /** * @brief Represents an edge in the graph * * An edge connects two nodes and has an associated cost. */ class edge { template friend class basic_node; template friend class graph; std::weak_ptr> _target; std::shared_ptr _cost; /** * @brief Create a tuple representation of the edge for comparison * @return Tuple containing cost and target node */ std::tuple> tie() const; public: /** * @brief Construct a new edge object * @param ptr Shared pointer to the target node * @param cost Cost of the edge */ explicit edge(const std::weak_ptr> &ptr, Cost cost); /** * @brief Copy constructor * @param other Edge to copy */ edge(const edge& other); /** * @brief Compare edges by cost * @param other Edge to compare with * @return true if this edge has lower cost than other */ bool operator<(const edge& other) const; /** * @brief Check equality of edges * @param other Edge to compare with * @return true if edges are equal */ bool operator==(const edge& other) const; /** * @brief Get the target node of this edge * @return constContainer pointing to the target node */ constContainer target() const; /** * @brief Get the cost of this edge * @return Reference to the cost value */ Cost& cost() const; }; using ListEdges = std::list; using EdgesIterator = typename ListEdges::iterator; using ConstEdgesIterator = typename ListEdges::const_iterator; private: template friend class graph; ListEdges _out_edges; const Cost infinity = std::numeric_limits::has_infinity ? std::numeric_limits::infinity() : std::numeric_limits::max(); std::size_t _in_degree{0}; void increment_in_degree(int n = 1); void decrement_in_degree(int n = 1); /// used for UNDIRECTED graphs: same Cost in memory for both directions bool set_edge(constContainer other, std::shared_ptr cost); std::tuple tie() const; ListEdges get_edges() const; template friend constexpr bool operator==(const basic_node &n1, const basic_node &n2) noexcept; template friend constexpr bool operator!=(const basic_node &n1, const basic_node &n2) noexcept; template friend constexpr bool operator< (const basic_node &n1, const basic_node &n2) noexcept; template friend constexpr bool operator<=(const basic_node &n1, const basic_node &n2) noexcept; template friend constexpr bool operator> (const basic_node &n1, const basic_node &n2) noexcept; template friend constexpr bool operator>=(const basic_node &n1, const basic_node &n2) noexcept; basic_node(const basic_node &n); basic_node &operator=(const basic_node &n); protected: Data _data; Container container_from_this; Container end_container; constContainer cend_container; public: /// @section exceptions using exception = detail::exception; using invalid_argument = detail::invalid_argument; using unexpected_nullptr = detail::unexpected_nullptr; /** * @brief Default constructor */ explicit basic_node(); /** * @brief Construct with data * @param data Data to store in the node */ explicit basic_node(const Data& data); /** * @brief Move assignment operator (deleted) */ basic_node &operator=(basic_node&& n) = delete; /** * @brief Destructor */ ~basic_node(); /** * @brief Get reference to node data * @return Reference to stored data */ Data &get(); /** * @brief Get copy of node data (const version) * @return Copy of stored data */ Data get() const; /** * @brief Get reference to edge cost * @param other Iterator to target node * @return Reference to edge cost */ Cost &get_cost(Container other); /** * @brief Get copy of edge cost (const version) * @param other Const iterator to target node * @return Copy of edge cost */ Cost get_cost(constContainer other) const; /** * @brief Access edge cost with operator[] * @param other Iterator to target node * @return Reference to edge cost */ Cost &operator[](Container other); /** * @brief Access edge cost with operator[] (const version) * @param other Const iterator to target node * @return Copy of edge cost */ Cost operator[](constContainer other) const; /** * @brief Set node data * @tparam T_data Type of data to set * @param data Data to set */ template void set(const T_data& data); /** * @brief Set edge cost * @tparam T_cost Type of cost to set * @param other Iterator to target node * @param cost Cost to set */ template void set_cost(Container other, const T_cost& cost); /** * @brief Add an edge to another node * @param other Const iterator to target node * @param cost Cost of the edge (default: 1) * @return Pair containing iterator to edge and boolean indicating if new edge was created */ std::pair add_edge(constContainer other, Cost cost = Cost(1)) { std::shared_ptr> ptr{ detail::get_value(other, cend_container) }; if (ptr == nullptr) { GRAPH_THROW(unexpected_nullptr) } for (EdgesIterator it{_out_edges.begin()}; it != _out_edges.end(); ++it) { if (it->_target.lock() == ptr) { it->cost() = cost; return std::make_pair(it, false); } } // Link doesn't exist ptr->increment_in_degree(); _out_edges.emplace_back( std::weak_ptr> (ptr), cost ); std::pair result{std::make_pair(--_out_edges.end(), true)}; return result; } /** * @brief Delete an edge to another node * @param other Const iterator to target node * @return true if edge was deleted, false otherwise */ bool del_edge(constContainer other); /** * @brief Delete an edge based on a predicate * @param other Const iterator to target node * @param predicate Function to determine if edge should be deleted * @return true if edge was deleted, false otherwise */ bool del_edge_if(constContainer other, std::function predicate); /** * @brief Clear all edges from this node * @return Number of edges cleared */ std::size_t clear_edges(); /** * @brief Get the degree of this node * @return Pair containing in-degree and out-degree */ [[nodiscard]] std::pair degree() const; /** * @brief Check if an adjacent node exists * @param other Iterator to potential adjacent node * @return true if adjacent node exists, false otherwise */ [[nodiscard]] bool existing_adjacent_node(constContainer other) const; }; /** * @brief A generalized class of Graph * * This class implements a graph data structure that can be either directed * or undirected. It supports various operations including node and edge * manipulation, graph traversal algorithms, and serialization. * * The graph is implemented as an adjacency list where each node contains * a list of its adjacent nodes and the cost of the connecting edges. * * @tparam Key Type of the keys. Each element in a Graph is uniquely identified * by its key value. Aliased as member type Graph::key_type * @tparam T Type of graphed value stored into a node. * Aliased as member type Graph::graphed_type * @tparam Cost Type of the cost between nodes. Default is std::size_t * @tparam Nat Nature of the graph (DIRECTED or UNDIRECTED). Default is UNDIRECTED * * @since version 1.0.0 */ template class graph { public: class node; using Degree = detail::basic_degree; class search_path; class shortest_paths; private: using PtrNode = std::shared_ptr; using MapNodes = std::map; MapNodes _nodes; std::size_t _num_edges = 0; const Cost infinity = std::numeric_limits::has_infinity ? std::numeric_limits::infinity() : std::numeric_limits::max(); class path_comparator; struct iterator_comparator; public: /// @section exceptions using bad_graph = detail::bad_graph; using exception = detail::exception; using invalid_argument = detail::invalid_argument; using negative_edge = detail::negative_edge; using not_complete = detail::not_complete; using parse_error = detail::parse_error; using unexpected_nullptr = detail::unexpected_nullptr; /// @section container types // Concept validation for better error messages in C++20+ #if defined(GRAPH_HAS_CPP_20) static_assert(GraphKey, "Key type must satisfy GraphKey concept (copyable, equality_comparable, and totally_ordered)"); static_assert(GraphData, "T type must satisfy GraphData concept (copyable)"); static_assert(GraphCost, "Cost type must satisfy GraphCost concept (copyable, equality_comparable, totally_ordered, and supports arithmetic operations)"); #endif using value_type = std::pair; using reference = value_type &; using key_type = Key; using graphed_type = T; using cost_type = Cost; using size_type = std::size_t; using iterator = typename MapNodes::iterator; using const_iterator = typename MapNodes::const_iterator; using reverse_iterator = typename MapNodes::reverse_iterator; using const_reverse_iterator = typename MapNodes::const_reverse_iterator; /** * @brief Returns an iterator to the first element of the graph * @return Iterator to the first element */ iterator begin() noexcept; /** * @brief Returns an iterator to the element following the last element of the graph * @return Iterator to the element following the last element */ iterator end() noexcept; /** * @brief Returns a const iterator to the first element of the graph * @return Const iterator to the first element */ const_iterator begin() const noexcept; /** * @brief Returns a const iterator to the first element of the graph * @return Const iterator to the first element */ const_iterator cbegin() const noexcept; /** * @brief Returns a const iterator to the element following the last element of the graph * @return Const iterator to the element following the last element */ const_iterator end() const noexcept; /** * @brief Returns a const iterator to the element following the last element of the graph * @return Const iterator to the element following the last element */ const_iterator cend() const noexcept; /** * @brief Returns a reverse iterator to the first element of the reversed graph * @return Reverse iterator to the first element */ reverse_iterator rbegin() noexcept; /** * @brief Returns a reverse iterator to the element following the last element of the reversed graph * @return Reverse iterator to the element following the last element */ reverse_iterator rend() noexcept; /** * @brief Returns a const reverse iterator to the first element of the reversed graph * @return Const reverse iterator to the first element */ const_reverse_iterator rbegin() const noexcept; /** * @brief Returns a const reverse iterator to the first element of the reversed graph * @return Const reverse iterator to the first element */ const_reverse_iterator crbegin() const noexcept; /** * @brief Returns a const reverse iterator to the element following the last element of the reversed graph * @return Const reverse iterator to the element following the last element */ const_reverse_iterator rend() const noexcept; /** * @brief Returns a const reverse iterator to the element following the last element of the reversed graph * @return Const reverse iterator to the element following the last element */ const_reverse_iterator crend() const noexcept; /** * @brief Default constructor * * Creates an empty graph with no nodes or edges. */ explicit graph(); /** * @brief Construct a graph from an input stream * * Creates a graph by parsing data from the provided input stream. * * @param is Input stream containing graph data */ explicit graph(std::istream& is); /** * @brief Copy constructor * * Creates a new graph as a copy of an existing graph. * * @param other Graph to copy */ graph(const graph& other); /** * @brief Move constructor * * Creates a new graph by moving the contents of an existing graph. * The moved-from graph is left in a valid but unspecified state. * * @param other Graph to move from */ graph(graph&& other) noexcept; /** * @brief Copy assignment operator * * Assigns the contents of one graph to another by copying. * * @param other Graph to copy from * @return Reference to this graph */ graph &operator=(const graph& other); /** * @brief Move assignment operator * * Assigns the contents of one graph to another by moving. * The moved-from graph is left in a valid but unspecified state. * * @param other Graph to move from * @return Reference to this graph */ graph &operator=(graph&& other) noexcept; /** * @brief Virtual destructor * * Destroys the graph and frees all associated resources. */ virtual ~graph(); /** * @brief Checks whether the graph is empty * * @return true if the graph contains no nodes, false otherwise */ [[nodiscard]] bool empty() const noexcept; /** * @brief Returns the number of nodes in the graph * * @return The number of nodes in the graph */ [[nodiscard]] size_type size() const noexcept; /** * @brief Returns the maximum possible number of nodes in the graph * * @return Maximum possible number of nodes */ [[nodiscard]] size_type max_size() const noexcept; /** * @brief Access or insert a node with the specified key * * If a node with the given key exists, returns a reference to its data. * Otherwise, creates a new node with default-constructed data. * * @param key Key of the node to access * @return Reference to the node's data */ graphed_type &operator[](const key_type& key); /** * @brief Access or insert a node with the specified key (move version) * * If a node with the given key exists, returns a reference to its data. * Otherwise, creates a new node with default-constructed data. * * @param key Key of the node to access (may be moved from) * @return Reference to the node's data */ graphed_type &operator[](key_type&& key); #if defined(GRAPH_HAS_CPP_17) /** * @brief Access a node with the specified key (const version) * * If a node with the given key exists, returns an optional containing its data. * Otherwise, returns an empty optional. * * @param key Key of the node to access * @return Optional containing the node's data or empty if not found */ std::optional operator[](key_type&& key) const; #else /** * @brief Access a node with the specified key (const version) * * If a node with the given key exists, returns its data. * Otherwise, throws an invalid_argument exception. * * @param key Key of the node to access * @return Copy of the node's data * @throws invalid_argument if node with key does not exist */ graphed_type operator[](key_type&& key) const; #endif /** * @brief Access the cost of an edge between two nodes (iterator version) * * If an edge between the nodes exists, returns a reference to its cost. * Otherwise, creates a new edge with infinite cost. * * @param from Iterator to the source node * @param to Iterator to the target node * @return Reference to the edge cost */ cost_type &operator()(iterator from, iterator to); /** * @brief Access the cost of an edge between two nodes (key version) * * If an edge between the nodes exists, returns a reference to its cost. * Otherwise, creates a new edge with infinite cost. * * @param from_key Key of the source node * @param to_key Key of the target node * @return Reference to the edge cost */ cost_type &operator()(const key_type& from_key, const key_type& to_key); #if defined(GRAPH_HAS_CPP_17) /** * @brief Access the cost of an edge between two nodes (const iterator version) * * If an edge between the nodes exists, returns an optional containing its cost. * Otherwise, returns an empty optional. * * @param from Iterator to the source node * @param to Iterator to the target node * @return Optional containing the edge cost or empty if edge doesn't exist */ std::optional operator()(const_iterator from, const_iterator to) const; /** * @brief Access the cost of an edge between two nodes (const key version) * * If an edge between the nodes exists, returns an optional containing its cost. * Otherwise, returns an empty optional. * * @param from_key Key of the source node * @param to_key Key of the target node * @return Optional containing the edge cost or empty if edge doesn't exist */ std::optional operator()(const key_type& from_key, const key_type& to_key) const; #else /** * @brief Access the cost of an edge between two nodes (const iterator version) * * If an edge between the nodes exists, returns its cost. * Otherwise, throws an invalid_argument exception. * * @param from Iterator to the source node * @param to Iterator to the target node * @return Copy of the edge cost * @throws invalid_argument if edge doesn't exist */ cost_type operator()(const_iterator from, const_iterator to) const; /** * @brief Access the cost of an edge between two nodes (const key version) * * If an edge between the nodes exists, returns its cost. * Otherwise, throws an invalid_argument exception. * * @param from_key Key of the source node * @param to_key Key of the target node * @return Copy of the edge cost * @throws invalid_argument if edge doesn't exist */ cost_type operator()(const key_type& from_key, const key_type& to_key) const; #endif /** * @brief Insert a node (deprecated) * @deprecated Use add_node instead */ [[deprecated]] std::pair insert(const value_type &); /** * @brief Insert a node at a specific position * * @param position Hint for where to insert the node * @param value Node to insert * @return Iterator to the inserted node */ iterator insert(const_iterator position, const value_type &); /** * @brief Insert a node with specified key and data * * @param position Hint for where to insert the node * @param key Key for the new node * @param data Data for the new node * @return Iterator to the inserted node */ iterator insert(const_iterator position, const key_type &, graphed_type &); /** * @brief Insert a node with specified key and node * * @param position Hint for where to insert the node * @param key Key for the new node * @param node Node to insert * @return Iterator to the inserted node */ iterator insert(const_iterator position, const key_type &, const node &); /** * @brief Emplace a node with the specified key * * Constructs a new node in-place with the given key. * * @param key Key for the new node * @return Pair containing iterator to the node and boolean indicating if insertion occurred */ std::pair emplace(const key_type &); /** * @brief Emplace a node with the specified key and data * * Constructs a new node in-place with the given key and data. * * @param key Key for the new node * @param data Data for the new node * @return Pair containing iterator to the node and boolean indicating if insertion occurred */ std::pair emplace(const key_type &, const graphed_type &); /** * @brief Emplace a node with the specified key and node * * Constructs a new node in-place with the given key and node. * * @param key Key for the new node * @param node Node to emplace * @return Pair containing iterator to the node and boolean indicating if insertion occurred */ std::pair emplace(const key_type &, const node &); /** * @brief Add a node with the specified key * * If a node with the given key doesn't exist, creates a new node with default data. * * @param key Key for the node * @return Pair containing iterator to the node and boolean indicating if insertion occurred */ std::pair add_node(const key_type &); /** * @brief Add a node with the specified key and data * * If a node with the given key doesn't exist, creates a new node with the given data. * * @param key Key for the node * @param data Data for the node * @return Pair containing iterator to the node and boolean indicating if insertion occurred */ std::pair add_node(const key_type &, const graphed_type &); /** * @brief Add a node with the specified key and node * * If a node with the given key doesn't exist, creates a new node with the given node's data. * * @param key Key for the node * @param node Node to copy data from * @return Pair containing iterator to the node and boolean indicating if insertion occurred */ std::pair add_node(const key_type &, const node &); /** * @brief Add an edge between two nodes (iterator version) * * Creates an edge between the nodes pointed to by the iterators. * * @param from Iterator to the source node * @param to Iterator to the target node * @param cost Cost of the edge (default: epsilon) * @return true if edge was added, false if it already existed */ bool add_edge(const_iterator from, const_iterator to, cost_type cost = std::numeric_limits::epsilon()); /** * @brief Add an edge between two nodes (key version) * * Creates an edge between the nodes with the given keys. * * @param from_key Key of the source node * @param to_key Key of the target node * @param cost Cost of the edge (default: epsilon) * @return true if edge was added, false if it already existed */ bool add_edge(const key_type& from_key, const key_type& to_key, cost_type cost = std::numeric_limits::epsilon()); /** * @brief Make the graph complete with specified edge cost * * Adds edges between all pairs of nodes with the given cost. * * @param cost Cost for all edges */ void make_complete(cost_type cost); /** * @brief Erase a node * * Removes the node pointed to by the iterator. * * @param position Iterator to the node to erase * @return Iterator following the erased node */ iterator erase(const_iterator position); /** * @brief Erase a range of nodes * * Removes the nodes in the range [first, last). * * @param first Iterator to the first node to erase * @param last Iterator to one past the last node to erase * @return Iterator following the last erased node */ iterator erase(const_iterator first, const_iterator last); /** * @brief Erase a node by key * * Removes the node with the given key. * * @param key Key of the node to erase * @return Number of nodes removed (0 or 1) */ size_type erase(const key_type& key); /** * @brief Delete a node * * Removes the node pointed to by the iterator. * * @param position Iterator to the node to delete * @return Iterator following the deleted node */ iterator del_node(const_iterator position); /** * @brief Delete a range of nodes * * Removes the nodes in the range [first, last). * * @param first Iterator to the first node to delete * @param last Iterator to one past the last node to delete * @return Iterator following the last deleted node */ iterator del_nodes(const_iterator first, const_iterator last); /** * @brief Delete a node by key * * Removes the node with the given key. * * @param key Key of the node to delete * @return Number of nodes removed (0 or 1) */ size_type del_node(const key_type& key); /** * @brief Clear all nodes and edges from the graph */ void clear() noexcept; /** * @brief Delete an edge between two nodes (iterator version) * * Removes the edge between the nodes pointed to by the iterators. * * @param from Iterator to the source node * @param to Iterator to the target node * @return Number of edges removed (0 or 1) */ size_type del_edge(const_iterator from, const_iterator to); /** * @brief Delete an edge between two nodes (key version) * * Removes the edge between the nodes with the given keys. * * @param from_key Key of the source node * @param to_key Key of the target node * @return Number of edges removed (0 or 1) */ size_type del_edge(const key_type& from_key, const key_type& to_key); /** * @brief Clear all edges from the graph */ void clear_edges(); /** * @brief Clear edges from a specific node (iterator version) * * Removes all edges from the node pointed to by the iterator. * * @param position Iterator to the node * @return Number of edges removed */ size_type clear_edges(const_iterator position); /** * @brief Clear edges from a specific node (key version) * * Removes all edges from the node with the given key. * * @param key Key of the node * @return Number of edges removed */ size_type clear_edges(const key_type& key); /** * @brief Swap the contents of two graphs * * Exchanges the contents of this graph with another graph. * * @param other Graph to swap with */ void swap(graph& other) noexcept; /** * @brief Count the number of nodes with a specific key * * @param key Key to count * @return Number of nodes with the given key (0 or 1) */ size_type count(const key_type &) const; /** * @brief Find a node with a specific key * * @param key Key to search for * @return Iterator to the node if found, end() otherwise */ iterator find(const key_type &); /** * @brief Find a node with a specific key (const version) * * @param key Key to search for * @return Const iterator to the node if found, cend() otherwise */ const_iterator find(const key_type &) const; /** * @brief Check if a node exists (iterator version) * * @param position Iterator to the node to check * @return true if the node exists, false otherwise */ bool existing_node(const_iterator position) const; /** * @brief Check if a node exists (key version) * * @param key Key of the node to check * @return true if the node exists, false otherwise */ bool existing_node(const key_type& key) const; /** * @brief Check if an edge exists between two nodes (iterator version) * * @param from Iterator to the source node * @param to Iterator to the target node * @return true if the edge exists, false otherwise */ bool existing_edge(const_iterator from, const_iterator to) const; /** * @brief Check if an edge exists between two nodes (key version) * * @param from_key Key of the source node * @param to_key Key of the target node * @return true if the edge exists, false otherwise */ bool existing_edge(const key_type& from_key, const key_type& to_key) const; /** * @brief Check if there exists a path connecting two nodes (iterator version) * * If from and to are equal, this function returns true. * * @param from Iterator to the source node * @param to Iterator to the target node * @return true if a path exists, false otherwise */ bool has_path_connecting(const_iterator from, const_iterator to) const; /** * @brief Check if there exists a path connecting two nodes (key version) * * If from_key and to_key are equal, this function returns true. * * @param from_key Key of the source node * @param to_key Key of the target node * @return true if a path exists, false otherwise */ bool has_path_connecting(const key_type& from_key, const key_type& to_key) const; /** * @brief Get the number of nodes in the graph * * @return Number of nodes */ [[nodiscard]] size_type get_nbr_nodes() const noexcept; /** * @brief Get the number of edges in the graph * * @return Number of edges */ [[nodiscard]] size_type get_nbr_edges() const noexcept; /** * @brief Get the nature of the graph * * @return Nature of the graph (DIRECTED or UNDIRECTED) */ [[nodiscard]] Nature get_nature() const; /** * @brief Get the degree of a node (iterator version) * * @param position Iterator to the node * @return Degree of the node */ Degree degree(const_iterator position) const; /** * @brief Get the degree of a node (key version) * * @param key Key of the node * @return Degree of the node */ Degree degree(const key_type& key) const; /** * @brief Get the node with maximum degree * * @return Pair containing iterator to the node and its degree */ std::pair degree_max() const; /** * @brief Get the node with minimum degree * * @return Pair containing iterator to the node and its degree */ std::pair degree_min() const; /** * @brief Get degrees of all nodes * * @return Map of node keys to their degrees */ std::map degrees() const; /** * @brief Get incoming edges of a node (directed graphs only) * * @tparam enable_if::value> SFINAE constraint for directed graphs * @param position Iterator to the node * @return Vector of incoming edges */ template ::value >> std::vector get_in_edges(const_iterator position) const; /** * @brief Get incoming edges of a node (directed graphs only, key version) * * @tparam enable_if::value> SFINAE constraint for directed graphs * @param key Key of the node * @return Vector of incoming edges */ template ::value >> std::vector get_in_edges(const key_type& key) const; /** * @brief Get outgoing edges of a node (directed graphs only) * * @tparam enable_if::value> SFINAE constraint for directed graphs * @param position Iterator to the node * @return Vector of outgoing edges */ template ::value >> std::vector get_out_edges(const_iterator position) const; /** * @brief Get outgoing edges of a node (directed graphs only, key version) * * @tparam enable_if::value> SFINAE constraint for directed graphs * @param key Key of the node * @return Vector of outgoing edges */ template ::value >> std::vector get_out_edges(const key_type& key) const; /** * @brief Get edges of a node (undirected graphs only) * * @tparam enable_if::value> SFINAE constraint for undirected graphs * @param position Iterator to the node * @return Vector of edges */ template ::value >> std::vector get_edges(const_iterator position) const; /** * @brief Get edges of a node (undirected graphs only, key version) * * @tparam enable_if::value> SFINAE constraint for undirected graphs * @param key Key of the node * @return Vector of edges */ template ::value >> std::vector get_edges(const key_type& key) const; /** * @brief Check if the graph contains cycles (directed graphs only) * * @tparam enable_if::value> SFINAE constraint for directed graphs * @return true if the graph contains cycles, false otherwise */ template ::value >> [[nodiscard]] bool is_cyclic() const; /* // TODO bool is_isomorphic() const; // TODO std::vector toposort() const; // TODO std::set, iterator_comparator> kosaraju_scc() const; // TODO std::set, iterator_comparator> tarjan_scc() const; // TODO size_type connected_components() const; // TODO graph &condensate(bool make_acyclic = true); // TODO std::vector maximum_clique() const; */ /** * @brief Output stream operator * * Displays a JSON representation of the graph. * * @tparam K Key type * @tparam D Data type * @tparam C Cost type * @tparam N Nature type * @param os Output stream * @param g Graph to output * @return Reference to the output stream */ template friend std::ostream & operator<<(std::ostream& os, const graph &g); /** * @brief Input stream operator * * Tries to load from JSON and then from FILE. * * @tparam K Key type * @tparam D Data type * @tparam C Cost type * @tparam N Nature type * @param is Input stream * @param g Graph to load into * @return Reference to the input stream */ template friend std::istream & operator>>(std::istream& is, graph &g); /** * @brief Load a graph from a file * * @param filename Path to the file to load * @note .DOT format is not supported */ void load(const char* filename); /** * @brief Generate a DOT representation of the graph * * @param graph_name Optional name for the graph (accepted characters: [a-zA-Z0-9_-]) * @return Unique pointer to the DOT string representation */ [[nodiscard]] std::unique_ptr generate_dot(const std::string& graph_name = "") const; /** * @brief Save the graph to a DOT file * * @param filename Path to the file to save to * @param graph_name Optional name for the graph (accepted characters: [a-zA-Z0-9_-]) */ void save_to_dot(const char* filename, const std::string& graph_name = "") const; /** * @brief Generate a JSON representation of the graph * * @return Unique pointer to the JSON representation */ [[nodiscard]] std::unique_ptr generate_json() const; /** * @brief Save the graph to a JSON file * * @param filename Path to the file to save to */ void save_to_json(const char* filename) const; /** * @brief Parse a graph from a JSON input stream * * @param is Input stream containing JSON data */ void parse_from_json(std::istream& is); /** * @brief Debug function to load from JSON Rust format (deprecated) * @deprecated Use parse_from_json instead */ [[deprecated]] void DEBUG_load_from_json_rust(const char*); /** * @brief Generate a GRP representation of the graph * * @return Unique pointer to the GRP string representation */ [[nodiscard]] std::unique_ptr generate_grp() const; /** * @brief Save the graph to a GRP file * * @param filename Path to the file to save to */ void save_to_grp(const char* filename) const; /** * @brief Parse a graph from a GRP input stream * * @param is Input stream containing GRP data */ void parse_from_grp(std::istream& is); /** * @brief Equality operator * * Compares two graphs for equality. * * @tparam K Key type of other graph * @tparam D Data type of other graph * @tparam C Cost type of other graph * @tparam N Nature type of other graph * @param other Graph to compare with * @return true if graphs are equal, false otherwise */ template bool operator==(const graph &other) const noexcept; /** * @brief Inequality operator * * Compares two graphs for inequality. * * @tparam K Key type of other graph * @tparam D Data type of other graph * @tparam C Cost type of other graph * @tparam N Nature type of other graph * @param other Graph to compare with * @return true if graphs are not equal, false otherwise */ template bool operator!=(const graph &other) const noexcept; class node : public basic_node { public: explicit node(); explicit node(const graphed_type &); node &operator=(const graphed_type &); private: friend class graph; void set_iterator_values(iterator this_, iterator end, const_iterator cend); }; /// @section Search algorithms search_path bfs(key_type start, key_type target) const; search_path bfs(key_type start, std::list target_list) const; search_path bfs(key_type start, std::function is_goal) const; search_path bfs(const_iterator start, const_iterator target) const; search_path bfs(const_iterator start, std::list target_list) const; search_path bfs(const_iterator start, std::function is_goal) const; search_path dfs(key_type start, key_type target) const; search_path dfs(key_type start, std::list target_list) const; search_path dfs(key_type start, std::function is_goal) const; search_path dfs(const_iterator start, const_iterator target) const; search_path dfs(const_iterator start, std::list target_list) const; search_path dfs(const_iterator start, std::function is_goal) const; search_path dls(key_type start, key_type target, size_type depth) const; search_path dls(key_type start, std::list target_list, size_type depth) const; search_path dls(key_type start, std::function is_goal, size_type depth) const; search_path dls(const_iterator start, const_iterator target, size_type depth) const; search_path dls(const_iterator start, std::list target_list, size_type depth) const; search_path dls(const_iterator start, std::function is_goal, size_type depth) const; search_path iddfs(key_type start, key_type target) const; search_path iddfs(key_type start, std::list target_list) const; search_path iddfs(key_type start, std::function is_goal) const; search_path iddfs(const_iterator start, const_iterator target) const; search_path iddfs(const_iterator start, std::list target_list) const; search_path iddfs(const_iterator start, std::function is_goal) const; search_path ucs(key_type start, key_type target) const; search_path ucs(key_type start, std::list target_list) const; search_path ucs(key_type start, std::function is_goal) const; search_path ucs(const_iterator start, const_iterator target) const; search_path ucs(const_iterator start, std::list target_list) const; search_path ucs(const_iterator start, std::function is_goal) const; search_path astar(key_type start, key_type target, std::function heuristic) const; search_path astar(key_type start, std::list target_list, std::function heuristic) const; search_path astar(key_type start, std::function is_goal, std::function heuristic) const; search_path astar(const_iterator start, const_iterator target, std::function heuristic) const; search_path astar(const_iterator start, std::list target_list, std::function heuristic) const; search_path astar(const_iterator start, std::function is_goal, std::function heuristic) const; shortest_paths dijkstra(key_type start) const; shortest_paths dijkstra(key_type start, key_type target) const; shortest_paths dijkstra(key_type start, std::list target_list) const; shortest_paths dijkstra(key_type start, std::function is_goal) const; shortest_paths dijkstra(const_iterator start) const; shortest_paths dijkstra(const_iterator start, const_iterator target) const; shortest_paths dijkstra(const_iterator start, std::list target_list) const; shortest_paths dijkstra(const_iterator start, std::function is_goal) const; shortest_paths bellman_ford(key_type start) const; shortest_paths bellman_ford(const_iterator start) const; class search_path final : std::deque> { template friend search_path graph::abstract_first_search(graph::const_iterator, std::function) const; friend search_path graph::dls (graph::const_iterator, std::function, size_type) const; friend search_path graph::iddfs (graph::const_iterator, std::function) const; friend search_path graph::ucs (graph::const_iterator, std::function) const; friend search_path graph::astar (graph::const_iterator, std::function, std::function) const; friend class shortest_paths; friend bool path_comparator::operator()(const search_path &, const search_path &) const; using Container = std::deque>; public: using value_type = typename Container::value_type; using reference = typename Container::reference; using const_reference = typename Container::const_reference; using iterator = typename Container::iterator; using const_iterator = typename Container::const_iterator; using reverse_iterator = typename Container::reverse_iterator; using const_reverse_iterator = typename Container::const_reverse_iterator; using Container::begin; using Container::cbegin; using Container::rbegin; using Container::crbegin; using Container::end; using Container::cend; using Container::rend; using Container::crend; search_path() = default; search_path(const search_path &); ~search_path() = default; using Container::empty; using Container::size; using Container::clear; using Container::front; using Container::pop_front; using Container::swap; using Container::push_back; using Container::emplace_back; cost_type total_cost() const; bool contain(const graph::const_iterator &) const; friend std::ostream &operator << (std::ostream &os, const graph::search_path &sp) { cost_type count{}; for (const std::pair &p : sp) { os << "-> " << p.first->first << " (" << (count += p.second) << ") "; } return os; } }; class shortest_paths final : std::map, iterator_comparator> { graph::const_iterator _start; friend shortest_paths graph::dijkstra (graph::const_iterator, std::function) const; friend shortest_paths graph::bellman_ford(graph::const_iterator) const; using Container = std::map, iterator_comparator>; explicit shortest_paths(graph::const_iterator start); public: using value_type = typename Container::value_type; using mapped_type = typename Container::mapped_type; using reference = typename Container::reference; using const_reference = typename Container::const_reference; using iterator = typename Container::iterator; using const_iterator = typename Container::const_iterator; using reverse_iterator = typename Container::reverse_iterator; using const_reverse_iterator = typename Container::const_reverse_iterator; using Container::begin; using Container::cbegin; using Container::rbegin; using Container::crbegin; using Container::end; using Container::cend; using Container::rend; using Container::crend; shortest_paths(const shortest_paths &); ~shortest_paths() = default; using Container::empty; using Container::size; /// @return the father of current in the optimal path from _start to current graph::const_iterator get_previous(graph::const_iterator current) const; /// @return the re-build path from the start node to the target search_path get_path(graph::key_type target) const; search_path get_path(graph::const_iterator target) const; friend std::ostream & operator<<(std::ostream & os, const shortest_paths & sp) { for (auto p : sp) { os << sp.get_path(p.first) << std::endl; } return os; } }; private: /// Helper functions and classes class path_comparator : public std::function { std::function _heuristic; public: explicit path_comparator(std::function heuristic); bool operator() (const search_path &, const search_path &) const; }; struct iterator_comparator : std::function { bool operator()(const const_iterator &, const const_iterator &) const; }; bool is_cyclic_rec(const_iterator current, std::list path) const; template search_path abstract_first_search(const_iterator start, std::function is_goal) const; }; template using graph_directed = graph; template using graph_undirected = graph; template basic_node::edge::edge(const std::weak_ptr> &ptr, Cost c) : _target(ptr), _cost(std::make_shared(c)) {} template basic_node::edge::edge(const edge &e) : _target(e._target), _cost(e._cost) {} template std::tuple> basic_node::edge::tie() const { return std::tie(*_cost, *_target.lock().get()); } template bool basic_node::edge::operator<(const edge &other) const { return tie() < other.tie(); } template bool basic_node::edge::operator==(const edge &other) const { return tie() == other.tie(); } template constContainer basic_node::edge::target() const { return _target.lock()->container_from_this; } template Cost & basic_node::edge::cost() const { return *_cost; } template std::tuple::edge >> basic_node::tie() const { return std::tie(_data, _in_degree, _out_edges); } template std::list::edge> basic_node::get_edges() const { return _out_edges; } template void basic_node::increment_in_degree(int n) { _in_degree += n; } template void basic_node::decrement_in_degree(int n) { _in_degree -= n; } template bool basic_node::set_edge(constContainer other, std::shared_ptr cost) { std::pair::EdgesIterator, bool> new_edge{add_edge(other, *cost)}; new_edge.first->_cost = cost; return new_edge.second; } template basic_node::basic_node() : basic_node(Data()) {} template basic_node::basic_node(const Data &d) : _data(d) {} template basic_node::basic_node(const basic_node &n) { *this = n; } template basic_node &basic_node::operator=(const basic_node &n) { _data = n._data; _out_edges = n._out_edges; _in_degree = n._in_degree; container_from_this = n.container_from_this; return *this; } template basic_node::~basic_node() = default; template Data & basic_node::get() { return _data; } template Data basic_node::get() const { return _data; } template Cost & basic_node::get_cost(Container other) { std::shared_ptr> ptr(detail::get_value(other, end_container)); if (ptr == nullptr) { GRAPH_THROW(unexpected_nullptr) } for (EdgesIterator it{_out_edges.begin()}; it != _out_edges.end(); ++it) if (it->_target.lock() == ptr) { return it->cost(); } /// Link doesn't exist _out_edges.emplace_back(std::weak_ptr> (detail::get_value(other, end_container)), infinity); ptr->increment_in_degree(); return (--_out_edges.end())->cost(); } template Cost basic_node::get_cost(constContainer other) const { std::shared_ptr> ptr(detail::get_value(other, cend_container)); if (ptr == nullptr) { GRAPH_THROW(unexpected_nullptr) } for (auto it = _out_edges.begin(); it != _out_edges.end(); ++it) if (it->_target.lock() == ptr) { return it->cost(); } /// Link doesn't exist return infinity; } template Cost & basic_node::operator[](Container other) { return get_cost(other); } template Cost basic_node::operator[](constContainer other) const { return get_cost(other); } template template void basic_node::set(const T_data &d) { _data = static_cast(d); } template template void basic_node::set_cost(Container other, const T_cost &c) { std::shared_ptr> ptr(detail::get_value(other, end_container)); if (ptr == nullptr) { GRAPH_THROW(unexpected_nullptr) } Cost new_cost{static_cast(c)}; bool existing_edge{false}; for (EdgesIterator it{_out_edges.begin()}; it != _out_edges.end(); ++it) { if (it->_target.lock() == ptr) { it->cost() = new_cost; existing_edge = true; } } if (!existing_edge) { this->add_edge(other, new_cost); } } template bool basic_node::del_edge(constContainer other) { return del_edge_if(other, [](const edge &) { return true; } ); } template bool basic_node::del_edge_if(constContainer other, std::function predicate) { for (EdgesIterator it{_out_edges.begin()}; it != _out_edges.end(); ) { if (it->_target.lock() == detail::get_value(other, cend_container)) { if (predicate(*it)) { it = _out_edges.erase(it); other->second->decrement_in_degree(); return true; } break; } ++it; } return false; } template std::size_t basic_node::clear_edges() { const std::size_t NUM{_out_edges.size()}; for (EdgesIterator it{_out_edges.begin()}; it != _out_edges.end(); ) { it->_target.lock()->decrement_in_degree(); it = _out_edges.erase(it); } return NUM; } template std::pair basic_node::degree() const { return std::make_pair(_in_degree, _out_edges.size()); } template bool basic_node::existing_adjacent_node(constContainer other) const { std::shared_ptr> ptr(detail::get_value(other, cend_container)); if (ptr == nullptr) { return false; } for (auto it = _out_edges.begin(); it != _out_edges.end(); ++it) { if (it->_target.lock() == ptr) { return true; } } /// Link doesn't exist return false; } template constexpr bool operator==(const basic_node &n1, const basic_node &n2) noexcept { return n1.tie() == n2.tie(); } template constexpr bool operator!=(const basic_node &n1, const basic_node &n2) noexcept { return !(n1 == n2); } template constexpr bool operator<(const basic_node &n1, const basic_node &n2) noexcept { return n1.tie() < n2.tie(); } template constexpr bool operator>(const basic_node &n1, const basic_node &n2) noexcept { return n2 < n1; } template constexpr bool operator<=(const basic_node &n1, const basic_node &n2) noexcept { return !(n2 < n1); } template constexpr bool operator>=(const basic_node &n1, const basic_node &n2) noexcept { return !(n1 < n2); } #include #include template typename graph::iterator graph::begin() noexcept { return _nodes.begin(); } template typename graph::iterator graph::end() noexcept { return _nodes.end(); } template typename graph::const_iterator graph::begin() const noexcept { return _nodes.begin(); } template typename graph::const_iterator graph::cbegin() const noexcept { return _nodes.cbegin(); } template typename graph::const_iterator graph::end() const noexcept { return _nodes.end(); } template typename graph::const_iterator graph::cend() const noexcept { return _nodes.cend(); } template typename graph::reverse_iterator graph::rbegin() noexcept { return _nodes.rbegin(); } template typename graph::reverse_iterator graph::rend() noexcept { return _nodes.rend(); } template typename graph::const_reverse_iterator graph::rbegin() const noexcept { return _nodes.rbegin(); } template typename graph::const_reverse_iterator graph::crbegin() const noexcept { return _nodes.crbegin(); } template typename graph::const_reverse_iterator graph::rend() const noexcept { return _nodes.rend(); } template typename graph::const_reverse_iterator graph::crend() const noexcept { return _nodes.crend(); } template graph::graph() { _nodes.clear(); } template graph::graph(std::istream &is) { is >> *this; } template graph::graph(const graph &other) { *this = other; } template graph::graph(graph &&other) noexcept : _nodes(std::move(other._nodes)) , _num_edges(other._num_edges) { other._num_edges = 0; } template graph &graph::operator=(const graph &other) { clear(); for (const_iterator it{other.cbegin()}; it != other.cend(); ++it) { add_node(it->first, it->second->get()); } for (const_iterator it{other.cbegin()}; it != other.cend(); ++it) { std::list list = it->second->get_edges(); for (typename node::edge e : list) { graph::const_iterator i{e.target()}; add_edge(it->first, i->first, e.cost()); } } return *this; } template graph &graph::operator=(graph&& other) noexcept { // Handle self-assignment if (this != &other) { //swap(other); // Move the resources directly for better performance _nodes = std::move(other._nodes); _num_edges = other._num_edges; // Reset the moved-from object to a valid state other._num_edges = 0; } return *this; } template graph::~graph() = default; template bool graph::empty() const noexcept { return _nodes.empty(); } template std::size_t graph::size() const noexcept { return _nodes.size(); } template std::size_t graph::max_size() const noexcept { return _nodes.max_size(); } template T &graph::operator[](const key_type &k) { return add_node(k).first->second->get(); } template T &graph::operator[](key_type &&k) { return add_node(k).first->second->get(); } #if defined(GRAPH_HAS_CPP_17) template std::optional graph::operator[](key_type &&k) const { const_iterator it{this->find(k)}; return it == cend() ? std::nullopt : std::optional(it->second->get()); } #else template T graph::operator[](key_type &&k) const { const_iterator it{this->find(k)}; if (it == cend()) { GRAPH_THROW_WITH(invalid_argument, "Unexistant node") } return it->second->get(); } #endif template typename graph::cost_type &graph::operator()(iterator it1, iterator it2) { if (!existing_edge(it1, it2)) { add_edge(it1, it2, infinity); } return it1->second->get_cost(it2); } template typename graph::cost_type &graph::operator()(const key_type &k1, const key_type &k2) { iterator it1{add_node(k1).first}; iterator it2{add_node(k2).first}; return operator()(it1, it2); } #if defined(GRAPH_HAS_CPP_17) template std::optional::cost_type> graph::operator()(const_iterator it1, const_iterator it2) const { return existing_edge(it1, it2) ? std::optional(it1->second->get_cost(it2)) : std::nullopt; } template std::optional::cost_type> graph::operator()(const key_type &k1, const key_type &k2) const { return operator()(this->find(k1), this->find(k2)); } #else template typename graph::cost_type graph::operator()(const_iterator it1, const_iterator it2) const { if (!existing_edge(it1, it2)) { GRAPH_THROW_WITH(invalid_argument, "Unexistant edge") } return it1->second->get_cost(it2); } template typename graph::cost_type graph::operator()(const key_type &k1, const key_type &k2) const { return operator()(this->find(k1), this->find(k2)); } #endif template std::pair::iterator, bool> graph::insert(const graph::value_type &val) { std::pair p{_nodes.insert(val)}; p.first->second->set_iterator_values(p.first, end(), cend()); return p; } template typename graph::iterator graph::insert(const_iterator position, const value_type &val) { iterator it{_nodes.insert(position, val)}; it->second->set_iterator_values(it, end(), cend()); return it; } template typename graph::iterator graph::insert(const_iterator position, const key_type &k, graphed_type &x) { return insert(position, std::make_pair(k, std::make_shared(x))); } template typename graph::iterator graph::insert(const_iterator position, const key_type &k, const node &n) { return insert(position, std::make_pair(k, std::make_shared(n))); } template std::pair::iterator, bool> graph::emplace(const key_type &k) { return emplace(k, node()); } template std::pair::iterator, bool> graph::emplace(const key_type &k, const graphed_type &x) { return emplace(k, node(x)); } template std::pair::iterator, bool> graph::emplace(const key_type &k, const node &n) { std::pair p{_nodes.emplace(k, std::make_shared(n))}; p.first->second->set_iterator_values(p.first, end(), cend()); return p; } template std::pair::iterator, bool> graph::add_node(const key_type &k) { return emplace(k); } template std::pair::iterator, bool> graph::add_node(const key_type &k, const graphed_type &x) { return emplace(k, x); } template std::pair::iterator, bool> graph::add_node(const key_type &k, const node &n) { return emplace(k, n); } template bool graph::add_edge(const_iterator it1, const_iterator it2, cost_type cost) { std::pair::const_iterator, bool> new_edge{it1->second->add_edge(it2, cost)}; if (get_nature() == UNDIRECTED) { it2->second->set_edge(it1, new_edge.first->_cost); } if (new_edge.second) { _num_edges++; } return true; } template bool graph::add_edge(const key_type &k1, const key_type &k2, cost_type cost) { return add_edge(emplace(k1).first, emplace(k2).first, cost); } template void graph::make_complete(cost_type cost) { clear_edges(); if (cost != infinity) { for (iterator it1{begin()}; it1 != end(); ++it1) { for (iterator it2{begin()}; it2 != end(); ++it2) { if (it1 != it2) { add_edge(it1, it2, cost); } } } } } template typename graph::iterator graph::erase(const_iterator position) { clear_edges(position); return _nodes.erase(position); } template typename graph::iterator graph::erase(const_iterator first, const_iterator last) { for (const_iterator it{first}; it != last && it != cend(); ++it) { clear_edges(it); } return _nodes.erase(first, last); } template std::size_t graph::erase(const key_type &k) { const_iterator it{this->find(k)}; if (it == cend()) { return 0; } clear_edges(it); return _nodes.erase(k); } template typename graph::iterator graph::del_node(const_iterator position) { return erase(position); } template typename graph::iterator graph::del_nodes(const_iterator first, const_iterator last) { return erase(first, last); } template std::size_t graph::del_node(const key_type &k) { return erase(k); } template void graph::clear() noexcept { _nodes.clear(); _num_edges = 0; } template std::size_t graph::del_edge(const_iterator it1, const_iterator it2) { size_type result{0}; if (it1 != end() && it2 != end()) { result = static_cast(it1->second->del_edge(it2)); if (get_nature() == UNDIRECTED) { it2->second->del_edge(it1); } _num_edges -= result; } return result; } template std::size_t graph::del_edge(const key_type &k1, const key_type &k2) { return del_edge(this->find(k1), this->find(k2)); } template void graph::clear_edges() { for (const_iterator it{cbegin()}; it != cend(); ++it) { clear_edges(it); } _num_edges = 0; } template std::size_t graph::clear_edges(const_iterator it) { size_type result{0}; if (it != cend()) { for (const_iterator it1{cbegin()}; it1 != cend(); ++it1) { del_edge(it1, it); } if (get_nature() == DIRECTED) { result += it->second->clear_edges(); } _num_edges -= result; } return result; } template std::size_t graph::clear_edges(const key_type &k) { return clear_edges(find(k)); } template void graph::swap(graph &other) noexcept { std::swap(_nodes, other._nodes); std::swap(_num_edges, other._num_edges); } namespace std { template void swap(graph &g1, graph &g2) noexcept { g1.swap(g2); } } template std::size_t graph::count(const key_type &k) const { return _nodes.count(k); } template typename graph::iterator graph::find(const key_type &k) { return _nodes.find(k); } template typename graph::const_iterator graph::find(const key_type &k) const { return _nodes.find(k); } template bool graph::existing_node(const_iterator it) const { return it != cend(); } template bool graph::existing_node(const key_type &k) const { return existing_node(find(k)); } template bool graph::existing_edge(const_iterator it1, const_iterator it2) const { if (it1 != cend() && it2 != cend()) { if (get_nature() == DIRECTED) { return it1->second->existing_adjacent_node(it2); } return it1->second->existing_adjacent_node(it2) && it2->second->existing_adjacent_node(it1); } return false; } template bool graph::existing_edge(const key_type &k1, const key_type &k2) const { return existing_edge(this->find(k1), this->find(k2)); } template bool graph::has_path_connecting(const_iterator from, const_iterator to) const { if (from == cend() || to == cend()) { GRAPH_THROW(unexpected_nullptr) } if (from == to) { return true; } return !dfs(from, to).empty(); } template bool graph::has_path_connecting(const key_type &from, const key_type &to) const { return has_path_connecting(this->find(from), this->find(to)); } template std::size_t graph::get_nbr_nodes() const noexcept { return _nodes.size(); } template std::size_t graph::get_nbr_edges() const noexcept { return _num_edges; } template Nature graph::get_nature() const { return Nat; } template typename graph::Degree graph::degree(const_iterator position) const { if (get_nature() == DIRECTED) { return position == cend() ? Degree::min() : Degree(position->second->degree().first, position->second->degree().second); } return position == cend() ? Degree::min() : Degree(position->second->degree().first, 0); } template typename graph::Degree graph::degree(const key_type &k) const { return degree(find(k)); } template std::pair::const_iterator, typename graph::Degree> graph::degree_max() const { if (empty()) { return std::make_pair(cend(), Degree::min()); } Degree max{Degree::min()}; const_iterator cit; for (const_iterator it{cbegin()}; it != cend(); ++it) { Degree tmp{degree(it)}; if (max < tmp) { max = tmp; cit = it; } } return std::make_pair(cit, max); } template std::pair::const_iterator, typename graph::Degree> graph::degree_min() const { if (empty()) { return std::make_pair(cend(), Degree::min()); } Degree min{Degree::max()}; const_iterator cit; for (const_iterator it{cbegin()}; it != cend(); ++it) { Degree tmp{degree(it)}; if (tmp < min) { min = tmp; cit = it; } } return std::make_pair(cit, min); } template std::map::Degree> graph::degrees() const { std::map result; for (const_iterator it{cbegin()}; it != cend(); ++it) { result.insert(std::make_pair(it->first, degree(it))); } return result; } template template std::vector::node::edge> graph::get_in_edges(const_iterator to) const { std::vector result; for (const_iterator it{cbegin()}; it != cend(); ++it) { if (it != to) { for (typename node::edge e : it->second->_out_edges) { if (e.target() == to) { result.push_back(e); } } } } return result; } template template std::vector::node::edge> graph::get_in_edges(const key_type &to) const { return get_in_edges(find(to)); } template template std::vector::node::edge> graph::get_out_edges(const_iterator from) const { return std::vector(from->second->_out_edges.begin(), from->second->_out_edges.end()); } template template std::vector::node::edge> graph::get_out_edges(const key_type &from) const { return get_out_edges(find(from)); } template template std::vector::node::edge> graph::get_edges(const_iterator i) const { return std::vector(i->second->_out_edges.begin(), i->second->_out_edges.end()); } template template std::vector::node::edge> graph::get_edges(const key_type &i) const { return get_edges(find(i)); } template template bool graph::is_cyclic() const { for (const_iterator it{cbegin()}; it != cend(); ++it) { std::list initPath; if (is_cyclic_rec(it, initPath)) { return true; } } return false; } template bool graph::is_cyclic_rec(const_iterator current, std::list path) const { for (const_iterator it : path) { if (it == current && (get_nature() == DIRECTED || path.back() != current)) { return true; } } /*if (std::find(path.cbegin(), path.cend(), current) != path.cend()) { return true; }*/ path.push_back(current); std::vector adjacent; if (get_nature() == DIRECTED) { adjacent = get_out_edges(current); } else { adjacent = get_edges(current); } for (typename std::vector::const_iterator it{adjacent.cbegin()}; it != adjacent.cend(); ++it) { if (is_cyclic_rec(it->target(), path)) { return true; } path.remove(it->target()); } return false; } /* template bool graph::is_isomorphic() const { // TODO } template std::vector::const_iterator> graph::toposort() const { // TODO } template std::set::const_iterator>, typename graph::iterator_comparator> graph::kosaraju_scc() const { // TODO } template std::set::const_iterator>, typename graph::iterator_comparator> graph::tarjan_scc() const { // TODO } template std::size_t graph::connected_components() const { // TODO } template graph &graph::condensate(bool make_acyclic) { // TODO } */ //template //std::vector::const_iterator> graph::maximum_clique() const { // TODO: fix the bug /* std::function heuristic = [](const_iterator it) -> size_type { return it->second->degree().first; }; std::function comparator = [&heuristic](const_iterator it1, const_iterator it2) -> size_type { return heuristic(it1) > heuristic(it2); }; std::list Q; for (const_iterator it{cbegin()}; it != cend(); ++it) { Q.emplace_back(it); } //std::copy(cbegin(), cend(), std::back_inserter(Q)); Q.sort(comparator); std::vector result; std::function calculate_max_size = [&Q, &heuristic]() -> size_type { for (size_type degree{heuristic(*Q.cbegin())}; degree > 0; --degree) { if (std::count_if(Q.cbegin(), Q.cend(), [degree, &heuristic](const_iterator it) -> bool { return heuristic(it) >= degree; }) >= degree) { return degree + 1; } } return 0; }; size_type max_size{calculate_max_size()}; while (!Q.empty() && result.size() <= max_size) { // DEBUG std::cout << "Values inside Q: "; for (const_iterator I : Q) { std::cout << I->first << " "; } std::cout << std::endl; const_iterator N{*Q.cbegin()}; // DEBUG std::cout << "N: " << N->first << std::endl; std::vector current_clique({N}); // TODO: loop until neighbors of N { for each neighbor of N inside of Q { . . . }; }; std::vector adj{get_out_edges(N)}; for (typename std::vector::const_iterator edge{adj.cbegin()}; edge != adj.cend(); ++edge) { // DEBUG std::cout << "Values inside current_clique: "; for (const_iterator I : current_clique) { std::cout << I->first << " "; } std::cout << std::endl; const_iterator X{edge->target()}; if (std::find(Q.cbegin(), Q.cend(), X) == Q.cend()) { continue; } bool part_of_clique{true}; for (const_iterator it : current_clique) { if (!existing_edge(it, X)) { part_of_clique = false; break; } } if (part_of_clique) { current_clique.push_back(X); } else { if (result.size() < current_clique.size()) { result = current_clique; // DEBUG std::cout << "new clique discovered: size of " << result.size() << std::endl; } Q.remove(N); std::function PAF = [&result, &heuristic](const_iterator it) -> bool { return heuristic(it) < result.size(); }; Q.erase(std::remove_if(Q.begin(), Q.end(), PAF), Q.cend()); max_size = calculate_max_size(); } } // DEBUG std::cout << std::endl; } return result; // candidates <- sort each node in function of its degree // max clique size <= degree_max() + 1 // current_clique_size = 0 // result = null // for each node N in candidates // current_clique = {N} // for each neighbor X of N ordered by its degree // if (X linked with all nodes of current_clique) then // current_clique += {X} // else // if (result.size < current_clique.size) // result = current_clique // candidates -= {N} // for each node M in candidates // if (M.degree <= result.size) // candidates -= {M} // clique = NULL // candidate = X (nodes of graph / candidate != NULL do // choose xi wih degree max // clique <- clique U {xi} // candidate <- candidats ∧ { xj / (xj, xi) € A} (intersection of candidate and neighbors of xi) */ //} template std::ostream &operator<<(std::ostream &os, const graph &g) { return os << *g.generate_grp() << std::endl; //return os << std::setw(4) << *g.generate_json() << std::endl; } template std::istream &operator>>(std::istream &is, graph &g) { GRAPH_TRY { g.parse_from_grp(is); } GRAPH_CATCH (...) { GRAPH_THROW_WITH(parse_error, 0, "istream corrupted, fail to parse the graph") } return is; } template void graph::load(const char* filename) { const char* dot = strrchr(filename, '.'); std::string extension; if (dot != nullptr && dot != filename) { extension = dot + 1; } std::ifstream in(filename); if (!in) { GRAPH_THROW_WITH(invalid_argument, ("Unexistant file: '" + std::string(filename) + "'").c_str()) } if (extension == "json") { parse_from_json(in); } else if (extension == "dot") { GRAPH_THROW_WITH(invalid_argument, "Load from DOT file non supported yet") } else { GRAPH_TRY { parse_from_grp(in); } GRAPH_CATCH (exception & e) { std::cerr << "Fail to load the file '" << filename << "'; wrong extension or corrupted file." << std::endl; } } in.close(); } template std::unique_ptr graph::generate_dot(const std::string &graph_name) const { std::string dot; const std::string tab{" "}; /// Displaying nature + graph name if (graph_name.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-") != std::string::npos) { GRAPH_THROW_WITH(invalid_argument, "Wrong graph name given; accepted characters: [a-zA-Z0-9_-]") } if (get_nature() == DIRECTED) { dot += "di"; } dot += "graph "; if (!graph_name.empty()) { dot += graph_name + " "; } dot += "{\n"; /// Displaying nodes' name for (const_iterator it{cbegin()}; it != cend(); ++it) { std::stringstream ss; ss << it->first; dot += tab + ss.str() + "\n"; } if (get_nature() == DIRECTED) { for_each(cbegin(), cend(), [this, &dot, tab](const value_type & element) { std::list child{element.second->get_edges()}; for_each(child.cbegin(), child.cend(), [this, &dot, tab, &element](const typename node::edge & i) { if (i.cost() != infinity) { std::stringstream ss; ss << tab << element.first << " -> " << i.target()->first << '\n'; dot += ss.str(); } }); }); } else { std::set> list_edges; for_each(cbegin(), cend(), [this, &dot, &list_edges](const value_type & element) { std::list child{element.second->get_edges()}; for_each(child.cbegin(), child.cend(), [&dot, &list_edges, &element](const typename node::edge & i) { const Key min{std::min(element.first, i.target()->first)}; const Key max{std::max(element.first, i.target()->first)}; list_edges.emplace(std::make_pair(min, max)); }); }); for_each(list_edges.cbegin(), list_edges.cend(), [&dot, tab](const std::pair &p) { std::stringstream ss; ss << tab << p.first << " -- " << p.second << '\n'; dot += ss.str(); }); } dot += '}'; return std::make_unique(std::forward(dot)); } template void graph::save_to_dot(const char* filename, const std::string &graph_name) const { std::ofstream out(filename); out << *generate_dot(graph_name) << std::endl; out.close(); } template std::unique_ptr graph::generate_json() const { nlohmann::json json; using Set = std::list; json["nature"] = get_nature(); size_type n{0}; for (const_iterator node{cbegin()}; node != cend(); ++node, ++n) { json["nodes"][n]["key"] = node->first; json["nodes"][n]["value"] = node->second->get(); } size_type p{0}; for (const_iterator node{cbegin()}; node != cend(); ++node) { Set child{node->second->get_edges()}; for (typename Set::const_iterator edge{child.begin()}; edge != child.end(); ++edge, ++p) { json["edges"][p]["from"] = node->first; json["edges"][p]["to"] = edge->target()->first; json["edges"][p]["cost"] = edge->cost(); } } return std::make_unique(std::forward(json)); } template void graph::save_to_json(const char* filename) const { std::ofstream out(filename); out << std::setw(4) << *generate_json() << std::endl; out.close(); } template void graph::parse_from_json(std::istream &is) { nlohmann::json json; is >> json; if (static_cast(json["nature"]) != get_nature()) { std::string error_msg{"Bad graph nature (expected '"}; error_msg += (Nat == DIRECTED ? "graph')" : "digraph')"); GRAPH_THROW_WITH(invalid_argument, error_msg.c_str()) } clear(); for (const nlohmann::json &node : json["nodes"]) { key_type k = node["key"]; graphed_type x = node["value"]; add_node(k, x); } for (const nlohmann::json &edge : json["edges"]) { key_type from = edge["from"]; key_type to = edge["to"]; cost_type c; if (edge["cost"].is_null()) { c = infinity; } else { c = edge["cost"]; } add_edge(from, to, c); } } template void graph::DEBUG_load_from_json_rust(const char* path) { nlohmann::json json; std::ifstream is(path); if (!is) { std::cerr << "Unexistent file '" << path << "'!" << std::endl; exit(1); } is >> json; clear(); for (const nlohmann::json &node : json["nodes"]) { add_node(node); } for (const nlohmann::json &edge : json["edges"]) { const_iterator begin{std::next(cbegin(), edge[0])}; const_iterator end {std::next(cbegin(), edge[1])}; add_edge(begin, end); } } template std::unique_ptr graph::generate_grp() const { using std::max; using std::ostringstream; using std::setw; using std::string; using std::stringstream; const auto tab{string(4, ' ')}; string data; /// Displaying nature + graph types if (get_nature() == DIRECTED) { data += "di"; } data += "graph<" + detail::type_name() + ", " + detail::type_name() + ", " + detail::type_name() + "> {\n" + tab + "nodes: {\n"; /// Displaying nodes: ""; "", size_type max_size{3}; for_each(cbegin(), cend(), [&max_size](const value_type & element) { ostringstream out; out << element.first; const size_type size{static_cast(out.tellp())}; max_size = max(max_size, size); }); for (const_iterator it{cbegin()}; it != cend(); ++it) { ostringstream out; out << it->first << "\","; stringstream ss; ss << tab << tab << '"' << std::left << setw(static_cast(max_size + 3)) << out.str() << '"' << it->second->get() << '"'; data += ss.str(); if (it != --cend()) { data += ";"; } data += "\n"; } data += tab + "}"; /// Displaying edges: ""; ""; "", if (get_nbr_edges() == 0) { data += "\n}"; } else { data += ",\n" + tab + "edges: {\n"; size_type max_size_1{0}, max_size_2{0}; for_each(cbegin(), cend(), [&max_size_1, &max_size_2, this](const value_type & element) { std::list child{element.second->get_edges()}; for_each(child.cbegin(), child.cend(), [ &, this](const typename node::edge & i) { ostringstream out_1, out_2; out_1 << element.first; out_2 << i.target()->first; const size_type size_1{static_cast(out_1.tellp())}; const size_type size_2{static_cast(out_2.tellp())}; max_size_1 = max(max_size_1, size_1); max_size_2 = max(max_size_2, size_2); }); }); size_type p{0}; for_each(cbegin(), cend(), [this, &data, &p, tab, max_size_1, max_size_2](const value_type & element) { std::list child{element.second->get_edges()}; for_each(child.cbegin(), child.cend(), [this, &data, &p, tab, &element, max_size_1, max_size_2](const typename node::edge & i) { ostringstream out_1, out_2; out_1 << '"' << element.first << "\","; out_2 << '"' << i.target()->first << "\","; stringstream ss; ss << std::left << tab << tab << setw(static_cast(max_size_1 + 4)) << out_1.str() << setw(static_cast(max_size_2 + 4)) << out_2.str(); if (i.cost() == infinity) { ss << "infinity"; } else { ss << '"' << i.cost() << '"'; } if (p < (this->get_nature() == DIRECTED ? this->get_nbr_edges() : 2 * this->get_nbr_edges()) - 1) { ss << ';'; } ss << '\n'; data += ss.str(); p++; }); }); data += tab + "}\n}"; } return std::make_unique(std::forward(data)); } template void graph::save_to_grp(const char* filename) const { std::ofstream out(filename); out << *generate_grp() << std::endl; out.close(); } template void graph::parse_from_grp(std::istream &is) { /// Nature std::string line; getline(is, line); if (line.substr(0, 5) == "graph") { if (get_nature() != UNDIRECTED) { GRAPH_THROW_WITH(invalid_argument, "Bad graph nature (expected 'graph')") } } else if (line.substr(0, 7) == "digraph") { if (get_nature() != DIRECTED) { GRAPH_THROW_WITH(invalid_argument, "Bad graph nature (expected 'digraph')") } } else { GRAPH_THROW_WITH(parse_error, is.tellg(), "Bad graph nature (expected '[di]graph')") } /// Nodes getline(is, line); if (line != " nodes: {") { GRAPH_THROW_WITH(parse_error, is.tellg(), "Bad format for nodes") } clear(); while (getline(is, line) && line.find('}') == std::string::npos) { std::istringstream iss{line}; Key key; T value; detail::read_T(iss, key); detail::read_T(iss, value); add_node(key, value); } /// Edges getline(is, line); if (line == "}") {} else if (line != " edges: {") { GRAPH_THROW_WITH(parse_error, is.tellg(), "Bad format for edges") } else { while (getline(is, line) && line.find('}') == std::string::npos) { std::istringstream iss{line}; key_type from; key_type to; cost_type cost; detail::read_T(iss, from); detail::read_T(iss, to); detail::read_cost(iss, cost); add_edge(from, to, cost); } getline(is, line); if (line != "}") { GRAPH_THROW_WITH(parse_error, is.tellg(), "Bad format at the end of the graph") } } } template template bool graph::operator==(const graph &other) const noexcept { typedef typename graph::const_iterator Iterator1; typedef typename graph::const_iterator Iterator2; typedef typename graph::node::edge Edge1; typedef typename graph ::node::edge Edge2; if (get_nature() != other.get_nature() || get_nbr_nodes() != other.get_nbr_nodes() || get_nbr_edges() != other.get_nbr_edges()) { return false; } Iterator1 it1{cbegin()}; Iterator2 it2{other.cbegin()}; for (; (it1 != cend() || it2 != other.cend()); ++it1, ++it2) { /// Test nodes value if (!(it1->first == it2->first) || !(it1->second->get() == it2->second->get()) || !(it1->second->degree() == it2->second->degree())) { return false; } /// Test out edges std::list edges1{it1->second->get_edges()}; std::list edges2{it2->second->get_edges()}; std::map child1; std::map child2; std::transform(edges1.begin(), edges1.end(), std::inserter(child1, child1.end()), [](const Edge1 & e) { return std::make_pair(e.target()->first, e); }); std::transform(edges2.begin(), edges2.end(), std::inserter(child2, child2.end()), [](const Edge2 & e) { return std::make_pair(e.target()->first, e); }); typename std::map::const_iterator edge1{child1.cbegin()}; typename std::map::const_iterator edge2{child2.cbegin()}; for (; (edge1 != child1.cend() || edge2 != child2.cend()); ++edge1, ++edge2) { Iterator1 target1{edge1->second.target()}; Iterator2 target2{edge2->second.target()}; if (!(target1->first == target2->first && edge1->second.cost() == edge2->second.cost())) { return false; } } } return true; } template template bool graph::operator!=(const graph &other) const noexcept { return !(*this == other); } template graph::node::node() : node(graphed_type()) {} template graph::node::node(const graphed_type &d) { this->_data = d; } template typename graph::node &graph::node::operator=(const graphed_type &d) { this->set(d); return *this; } template void graph::node::set_iterator_values(iterator this_, iterator end, const_iterator cend) { this->container_from_this = this_; this->end_container = end; this->cend_container = cend; } template template typename graph::search_path graph::abstract_first_search(const_iterator start, std::function is_goal) const { if (start == cend()) { GRAPH_THROW_WITH(invalid_argument, "Start point equals to graph::cend()") } std::list expanded; std::deque frontier; { search_path p; p.push_back({start, cost_type()}); frontier.push_front(std::move(p)); } while (!frontier.empty()) { search_path p{frontier.front()}; frontier.pop_front(); if (std::find(expanded.cbegin(), expanded.cend(), p.back().first) == expanded.end()) { const_iterator last{p.back().first}; expanded.push_back(last); if (is_goal(last)) { return p; } std::vector legalActions{get_nature() == DIRECTED ? get_out_edges(last) : get_edges(last)}; for (typename std::vector::const_iterator it{legalActions.cbegin()}; it != legalActions.cend(); ++it) { search_path newPath{p}; newPath.push_back({it->target(), it->cost()}); if (insertFront) { frontier.push_front(newPath); } else { frontier.push_back(newPath); } } } } search_path empty = {}; return empty; } template typename graph::search_path graph::bfs(key_type start, Key target) const { std::list l; l.emplace_back(find(target)); return bfs(find(start), l); } template typename graph::search_path graph::bfs(key_type start, std::list target_list) const { std::list l; for (key_type k : target_list) { l.emplace_back(find(k)); } return bfs(find(start), l); } template typename graph::search_path graph::bfs(key_type start, std::function is_goal) const { return bfs(find(start), [ &is_goal](const_iterator it) { return is_goal(it->first); }); } template typename graph::search_path graph::bfs(const_iterator start, const_iterator target) const { std::list l; l.emplace_back(target); return bfs(start, l); } template typename graph::search_path graph::bfs(const_iterator start, std::list target_list) const { return bfs(start, [ &target_list](const_iterator node) -> bool { return std::find(target_list.cbegin(), target_list.cend(), node) != target_list.cend(); }); } template typename graph::search_path graph::bfs(const_iterator start, std::function is_goal) const { return abstract_first_search(start, is_goal); } template typename graph::search_path graph::dfs(key_type start, Key target) const { std::list l; l.emplace_back(find(target)); return dfs(find(start), l); } template typename graph::search_path graph::dfs(key_type start, std::list target_list) const { std::list l; for (key_type k : target_list) { l.emplace_back(find(k)); } return dfs(find(start), l); } template typename graph::search_path graph::dfs(key_type start, std::function is_goal) const { return dfs(find(start), [ &is_goal](const_iterator it) { return is_goal(it->first); }); } template typename graph::search_path graph::dfs(const_iterator start, const_iterator target) const { std::list l; l.emplace_back(target); return dfs(start, l); } template typename graph::search_path graph::dfs(const_iterator start, std::list target_list) const { return dfs(start, [ &target_list](const_iterator node) -> bool { return std::find(target_list.cbegin(), target_list.cend(), node) != target_list.cend(); }); } template typename graph::search_path graph::dfs(const_iterator start, std::function is_goal) const { return abstract_first_search(start, is_goal); } template typename graph::search_path graph::dls(key_type start, Key target, size_type depth) const { std::list l; l.emplace_back(find(target)); return dls(find(start), l, depth); } template typename graph::search_path graph::dls(key_type start, std::list target_list, size_type depth) const { std::list l; for (key_type k : target_list) { l.emplace_back(find(k)); } return dls(find(start), l, depth); } template typename graph::search_path graph::dls(key_type start, std::function is_goal, size_type depth) const { return dls(find(start), [ &is_goal](const_iterator it) { return is_goal(it->first); }, depth); } template typename graph::search_path graph::dls(const_iterator start, const_iterator target, size_type depth) const { std::list l; l.emplace_back(target); return dls(start, l, depth); } template typename graph::search_path graph::dls(const_iterator start, std::list target_list, size_type depth) const { return dls(start, [ &target_list](const_iterator node) -> bool { return std::find(target_list.cbegin(), target_list.cend(), node) != target_list.cend(); }, depth); } template typename graph::search_path graph::dls(const_iterator start, std::function is_goal, size_type depth) const { if (start == cend()) { GRAPH_THROW_WITH(invalid_argument, "Start point equals to graph::cend()") } std::list expanded; std::deque frontier; { search_path p; p.push_back({start, cost_type()}); frontier.push_front(std::move(p)); } int l{0}; while (!frontier.empty() && l < depth) { search_path p{frontier.front()}; frontier.pop_front(); if (std::find(expanded.cbegin(), expanded.cend(), p.back().first) == expanded.end()) { const_iterator last{p.back().first}; expanded.push_back(last); if (is_goal(last)) { return p; } std::vector legalActions{get_nature() == DIRECTED ? get_out_edges(last) : get_edges(last)}; for (typename std::vector::const_iterator it{legalActions.cbegin()}; it != legalActions.cend(); ++it) { search_path newPath{p}; newPath.push_back({it->target(), it->cost()}); frontier.push_front(newPath); } } ++l; } search_path empty = {}; return empty; } template typename graph::search_path graph::iddfs(key_type start, Key target) const { std::list l; l.emplace_back(find(target)); return iddfs(find(start), l); } template typename graph::search_path graph::iddfs(key_type start, std::list target_list) const { std::list l; for (key_type k : target_list) { l.emplace_back(find(k)); } return iddfs(find(start), l); } template typename graph::search_path graph::iddfs(key_type start, std::function is_goal) const { return iddfs(find(start), [ &is_goal](const_iterator it) { return is_goal(it->first); }); } template typename graph::search_path graph::iddfs(const_iterator start, const_iterator target) const { std::list l; l.emplace_back(target); return iddfs(start, l); } template typename graph::search_path graph::iddfs(const_iterator start, std::list target_list) const { return iddfs(start, [ &target_list](const_iterator node) -> bool { return std::find(target_list.cbegin(), target_list.cend(), node) != target_list.cend(); }); } template typename graph::search_path graph::iddfs(const_iterator start, std::function is_goal) const { if (start == cend()) { GRAPH_THROW_WITH(invalid_argument, "Start point equals to graph::cend()") } for (size_type i{0}; i < std::numeric_limits::max(); ++i) { const search_path found{dls(start, is_goal, i)}; if (!found.empty()) { return found; } } search_path empty = {}; return empty; } template typename graph::search_path graph::ucs(key_type start, Key target) const { std::list l; l.emplace_back(find(target)); return ucs(find(start), l); } template typename graph::search_path graph::ucs(key_type start, std::list target_list) const { std::list l; for (key_type k : target_list) { l.emplace_back(find(k)); } return ucs(find(start), l); } template typename graph::search_path graph::ucs(key_type start, std::function is_goal) const { return ucs(find(start), [ &is_goal](const_iterator it) { return is_goal(it->first); }); } template typename graph::search_path graph::ucs(const_iterator start, const_iterator target) const { std::list l; l.emplace_back(target); return ucs(start, l); } template typename graph::search_path graph::ucs(const_iterator start, std::list target_list) const { return ucs(start, [ &target_list](const_iterator node) -> bool { return std::find(target_list.cbegin(), target_list.cend(), node) != target_list.cend(); }); } template typename graph::search_path graph::ucs(const_iterator start, std::function is_goal) const { if (start == cend()) { GRAPH_THROW_WITH(invalid_argument, "Start point equals to graph::cend()") } std::list expanded; std::priority_queue, path_comparator> frontier((path_comparator([](const_iterator) { return cost_type(); }))); { search_path p; p.push_back({start, cost_type()}); frontier.push(std::move(p)); } while (!frontier.empty()) { search_path p{frontier.top()}; frontier.pop(); if (std::find(expanded.cbegin(), expanded.cend(), p.back().first) == expanded.end()) { const_iterator last{p.back().first}; expanded.push_back(last); if (is_goal(last)) { return p; } std::vector legalActions{get_nature() == DIRECTED ? get_out_edges(last) : get_edges(last)}; for (typename std::vector::const_iterator it{legalActions.cbegin()}; it != legalActions.cend(); ++it) { search_path newPath{p}; newPath.push_back({it->target(), it->cost()}); frontier.push(newPath); } } } search_path empty = {}; return empty; } template typename graph::search_path graph::astar(key_type start, Key target, std::function heuristic) const { const_iterator it{this->find(target)}; return astar(find(start), [ &it](const_iterator node) -> bool { return it == node; }, heuristic); } template typename graph::search_path graph::astar(key_type start, std::list target_list, std::function heuristic) const { std::list l; for (key_type k : target_list) { l.emplace_back(find(k)); } return astar(find(start), l, heuristic); } template typename graph::search_path graph::astar(key_type start, std::function is_goal, std::function heuristic) const { return astar(find(start), [ &is_goal](const_iterator it) { return is_goal(it->first); }, heuristic); } template typename graph::search_path graph::astar(const_iterator start, const_iterator target, std::function heuristic) const { return astar(start, [ &target](const_iterator node) -> bool { return target == node; }, heuristic); } template typename graph::search_path graph::astar(const_iterator start, std::list target_list, std::function heuristic) const { return astar(start, [ &target_list](const_iterator node) -> bool { return std::find(target_list.cbegin(), target_list.cend(), node) != target_list.cend(); }, heuristic); } template typename graph::search_path graph::astar(const_iterator start, std::function is_goal, std::function heuristic) const { if (start == cend()) { GRAPH_THROW_WITH(invalid_argument, "Start point equals to graph::cend()") } const cost_type nul_cost{cost_type()}; std::list expanded; std::priority_queue, path_comparator> frontier((path_comparator(heuristic))); { search_path p; p.push_back({start, nul_cost}); frontier.push(std::move(p)); } while (!frontier.empty()) { search_path p{frontier.top()}; frontier.pop(); if (std::find(expanded.cbegin(), expanded.cend(), p.back().first) == expanded.end()) { const_iterator last{p.back().first}; expanded.push_back(last); if (is_goal(last)) { return p; } std::vector legalActions{get_nature() == DIRECTED ? get_out_edges(last) : get_edges(last)}; for (typename std::vector::const_iterator it{legalActions.cbegin()}; it != legalActions.cend(); ++it) { /// Dijkstra's algorithm cannot be computed with negative weights. if (it->cost() < nul_cost) { GRAPH_THROW(negative_edge) } search_path newPath{p}; newPath.push_back({it->target(), it->cost()}); frontier.push(newPath); } } } search_path empty = {}; return empty; } template typename graph::shortest_paths graph::dijkstra(key_type start) const { return dijkstra(find(start), [](const_iterator) { return false; }); } template typename graph::shortest_paths graph::dijkstra(key_type start, key_type target) const { const_iterator it{this->find(target)}; return dijkstra(find(start), [ &it](const_iterator node) -> bool { return it == node; }); } template typename graph::shortest_paths graph::dijkstra(key_type start, std::list target_list) const { std::list l; for (key_type k : target_list) { l.emplace_back(find(k)); } return dijkstra(find(start), l); } template typename graph::shortest_paths graph::dijkstra(key_type start, std::function is_goal) const { return dijkstra(find(start), [ &is_goal](const_iterator it) { return is_goal(it->first); }); } template typename graph::shortest_paths graph::dijkstra(const_iterator start) const { return dijkstra(start, [](const_iterator) { return false; }); } template typename graph::shortest_paths graph::dijkstra(const_iterator start, const_iterator target) const { return dijkstra(start, [ &target](const_iterator node) -> bool { return target == node; }); } template typename graph::shortest_paths graph::dijkstra(const_iterator start, std::list target_list) const { return dijkstra(start, [ &target_list](const_iterator node) -> bool { return std::find(target_list.cbegin(), target_list.cend(), node) != target_list.cend(); }); } template typename graph::shortest_paths graph::dijkstra(const_iterator start, std::function is_goal) const { if (start == cend()) { GRAPH_THROW_WITH(invalid_argument, "Start point equals to graph::cend()") } struct pair_iterator_comparator { bool operator()(const std::pair &lhs, const std::pair &rhs) const { return lhs.second < rhs.second; } }; /// Initialization const cost_type nul_cost{cost_type()}; std::priority_queue, std::vector>, pair_iterator_comparator> Q; shortest_paths result(start); for (const_iterator it{cbegin()}; it != cend(); ++it) { result.emplace(it, std::make_pair(cend(), infinity)); } Q.emplace(start, nul_cost); typename shortest_paths::iterator S{result.find(start)}; S->second.first = start; S->second.second = nul_cost; /// Dijkstra's algorithm while (!Q.empty()) { const_iterator u{Q.top().first}; Q.pop(); if (is_goal(u)) { break; } std::vector adj{get_out_edges(u)}; for (typename std::vector::const_iterator edge{adj.cbegin()}; edge != adj.cend(); ++edge) { const_iterator v{edge->target()}; cost_type cost{u->second->get_cost(v)}; /// Dijkstra's algorithm cannot be computed with negative weights. if (cost < nul_cost) { GRAPH_THROW(negative_edge) } cost_type dist_u{result[u].second}; cost_type dist_v{result[v].second}; cost_type alt{dist_u + cost}; if (alt < dist_v) { result[v].first = u; result[v].second = alt; Q.emplace(v, alt); } } } return result; } template typename graph::shortest_paths graph::bellman_ford(key_type start) const { return bellman_ford(find(start)); } template typename graph::shortest_paths graph::bellman_ford(const_iterator start) const { if (start == cend()) { GRAPH_THROW_WITH(invalid_argument, "Start point equals to graph::cend()") } /// Initialization shortest_paths result(start); for (const_iterator it{cbegin()}; it != cend(); ++it) { result.emplace(it, std::make_pair(cend(), infinity)); } typename shortest_paths::iterator S{result.find(start)}; S->second.first = start; S->second.second = cost_type(); /// Relax edges repeatedly bool finished{false}; for (size_type i{0}; i < get_nbr_edges() - 1 && !finished; ++i) { finished = true; for (const_iterator u{cbegin()}; u != cend(); ++u) { std::vector adj{get_out_edges(u)}; for (typename std::vector::const_iterator edge{adj.cbegin()}; edge != adj.cend(); ++edge) { const_iterator v{edge->target()}; cost_type cost{u->second->get_cost(v)}; cost_type dist_u{result[u].second}; cost_type dist_v{result[v].second}; cost_type alt{dist_u + cost}; if (alt < dist_v) { result[v].first = u; result[v].second = alt; finished = false; } } } } /// Check for negative-weigh cycles for (const_iterator u{cbegin()}; u != cend(); ++u) { std::vector adj{get_out_edges(u)}; for (typename std::vector::const_iterator edge{adj.cbegin()}; edge != adj.cend(); ++edge) { const_iterator v{edge->target()}; if (result[u].second + u->second->get_cost(v) < result[v].second) { GRAPH_THROW(negative_weight_cycle) } } } return result; } template graph::search_path::search_path(const search_path &p) : Container(p) {} template typename graph::cost_type graph::search_path::total_cost() const { cost_type total{}; for (const_iterator it{cbegin()}; it != cend(); ++it) { total += it->second; } return total; } template bool graph::search_path::contain(const graph::const_iterator &i) const { for (const_iterator it{cbegin()}; it != cend(); ++it) { if (it->first == i) { return true; } } return false; } template graph::shortest_paths::shortest_paths(graph::const_iterator start) : _start(start) {} template graph::shortest_paths::shortest_paths(const shortest_paths &p) : Container(p), _start(p._start) {} template typename graph::const_iterator graph::shortest_paths::get_previous(graph::const_iterator current) const { return this->find(current)->second.first; } template typename graph::search_path graph::shortest_paths::get_path(graph::key_type target) const { for (const_iterator it{cbegin()}; it != cend(); ++it) { if (it->first->first == target) { return get_path(it->first); } } return graph::search_path(); } template typename graph::search_path graph::shortest_paths::get_path(graph::const_iterator target) const { graph::shortest_paths::const_iterator current{this->find(target)}; if (current == cend() || current->second.second == (std::numeric_limits::has_infinity ? std::numeric_limits::infinity() : std::numeric_limits::max())) { return search_path(); } graph::search_path result; graph::shortest_paths::const_iterator previous{this->find(current->second.first)}; while (previous != cend() && current->first != _start) { result.emplace_front(current->first, current->second.second - previous->second.second); current = previous; previous = this->find(current->second.first); } if (target != _start) { result.emplace_front(_start, cost_type()); } return result; } template graph::path_comparator::path_comparator(std::function heuristic) : _heuristic(heuristic) {} template bool graph::path_comparator::operator() (const search_path &p1, const search_path &p2) const { return (p2.total_cost() + _heuristic(p2.back().first)) < (p1.total_cost() + _heuristic(p1.back().first)); } template bool graph::iterator_comparator::operator()(const const_iterator &lhs, const const_iterator &rhs) const { return lhs->first < rhs->first; } #endif