diff --git a/NEWS.md b/NEWS.md index 83c4dbcef1..bc2855eb35 100644 --- a/NEWS.md +++ b/NEWS.md @@ -20,6 +20,7 @@ To see all issues & pull requests closed by this release see the * [#3056](https://github.com/pgRouting/pgrouting/issues/3056): edwardMoore: use the shortest_path process and driver * [#3060](https://github.com/pgRouting/pgrouting/issues/3060): dagShortestPath: use the shortest_path process and driver * [#3064](https://github.com/pgRouting/pgrouting/issues/3064): Astar: create and use a process and driver for Astar +* [#3075](https://github.com/pgRouting/pgrouting/issues/3075): Spanning tree: create and use a process and driver ## pgRouting 4.0 diff --git a/doc/src/release_notes.rst b/doc/src/release_notes.rst index a0dc6b24f9..795aa5c0be 100644 --- a/doc/src/release_notes.rst +++ b/doc/src/release_notes.rst @@ -45,6 +45,7 @@ To see all issues & pull requests closed by this release see the * :issue:`3056`: edwardMoore: use the shortest_path process and driver * :issue:`3060`: dagShortestPath: use the shortest_path process and driver * :issue:`3064`: Astar: create and use a process and driver for Astar +* :issue:`3075`: Spanning tree: create and use a process and driver pgRouting 4.0 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/include/breadthFirstSearch/breadthFirstSearch.hpp b/include/breadthFirstSearch/breadthFirstSearch.hpp index 8be0a23980..3d613b661a 100644 --- a/include/breadthFirstSearch/breadthFirstSearch.hpp +++ b/include/breadthFirstSearch/breadthFirstSearch.hpp @@ -43,7 +43,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. //****************************************** namespace pgrouting { -namespace functions { +namespace algorithms { template class Pgr_breadthFirstSearch { @@ -113,6 +113,22 @@ class Pgr_breadthFirstSearch { return results; } }; + +} // namespace algorithms + +namespace functions { + +template +std::vector breadthFirstSearch( + G &graph, + const std::set &roots, + bool, + int64_t max_depth) { + pgrouting::algorithms::Pgr_breadthFirstSearch fn_breadthFirstSearch; + auto results = fn_breadthFirstSearch.breadthFirstSearch(graph, roots, max_depth); + return results; +} + } // namespace functions } // namespace pgrouting diff --git a/include/c_common/enums.h b/include/c_common/enums.h index f740a4a971..6ffad2994b 100644 --- a/include/c_common/enums.h +++ b/include/c_common/enums.h @@ -38,7 +38,13 @@ enum Which { /** metrics **/ BANDWIDTH, /** with edges that have x y */ - ASTAR = 400, BDASTAR + ASTAR = 400, BDASTAR, + /** For spanning tree like results */ + KRUSKAL = 500, KRUSKALDD, KRUSKALDFS, KRUSKALBFS, + PRIM = 510, PRIMDD, PRIMDFS, PRIMBFS, + DFS = 520, + BFS = 530, + DIJKSTRADD = 540 }; #endif // INCLUDE_C_COMMON_ENUMS_H_ diff --git a/include/cpp_common/to_postgres.hpp b/include/cpp_common/to_postgres.hpp index a2d145c792..026b59c6c2 100644 --- a/include/cpp_common/to_postgres.hpp +++ b/include/cpp_common/to_postgres.hpp @@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include #include +#include #include #include "c_types/contractionHierarchies_rt.h" @@ -61,11 +62,26 @@ size_t get_viaRoute(std::deque&, Routes_t**); */ size_t get_tuples(const std::deque&, Path_rt*&); + /* * @brief get tuples from a Path to a MST_rt */ size_t get_tuples(const std::deque&, MST_rt*&); +/* + * @brief get tuples for spanning tree driver + */ +size_t get_tuples( + const std::vector&, + const std::deque&, + const std::vector>&, + MST_rt*&); + +/* + * @brief get tuples from a vector of MST_rt to a MST_rt + */ +size_t get_tuples(const std::vector&, MST_rt*&); + /** @brief Vector of vertices id are saved on a C array * * @param[in] graph Created graph with the base Graph diff --git a/include/cpp_common/utilities.hpp b/include/cpp_common/utilities.hpp index be50fc8d0c..6d59a2933d 100644 --- a/include/cpp_common/utilities.hpp +++ b/include/cpp_common/utilities.hpp @@ -26,9 +26,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #define INCLUDE_CPP_COMMON_UTILITIES_HPP_ #pragma once +#include +#include #include +#include #include "c_common/enums.h" +#include "c_types/mst_rt.h" namespace pgrouting { @@ -37,6 +41,7 @@ std::string get_name(Which, bool, bool); std::string get_name(Which, bool, bool, bool); char estimate_drivingSide(char, Which); void get_new_queries(const std::string&, const std::string&, std::string&, std::string&); +std::vector only_root_result(const std::set&); } // namespace pgrouting diff --git a/include/dijkstra/drivingDist.hpp b/include/dijkstra/drivingDist.hpp index 44a2ad3e5a..95824ec83b 100644 --- a/include/dijkstra/drivingDist.hpp +++ b/include/dijkstra/drivingDist.hpp @@ -485,7 +485,7 @@ std::deque drivingDistance_no_equicost( namespace pgrouting { -namespace algorithm { +namespace functions { template std::deque drivingDistance( @@ -510,7 +510,7 @@ std::deque drivingDistance( } } -} // namespace algorithm +} // namespace functions } // namespace pgrouting diff --git a/include/drivers/breadthFirstSearch/breadthFirstSearch_driver.h b/include/drivers/breadthFirstSearch/breadthFirstSearch_driver.h deleted file mode 100644 index 652a896bc3..0000000000 --- a/include/drivers/breadthFirstSearch/breadthFirstSearch_driver.h +++ /dev/null @@ -1,76 +0,0 @@ -/*PGR-GNU***************************************************************** -File: breadthFirstSearch_driver.h - -Generated with Template by: -Copyright (c) 2007-2026 pgRouting developers -Mail: project@pgrouting.org - -Function's developer: -Copyright (c) 2019 Gudesa Venkata Sai Akhil -Mail: gvs.akhil1997@gmail.com - ------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - ********************************************************************PGR-GNU*/ - -#ifndef INCLUDE_DRIVERS_BREADTHFIRSTSEARCH_BREADTHFIRSTSEARCH_DRIVER_H_ -#define INCLUDE_DRIVERS_BREADTHFIRSTSEARCH_BREADTHFIRSTSEARCH_DRIVER_H_ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#ifdef __cplusplus -} -#endif - -#include "cpp_common/undefPostgresDefine.hpp" - -#ifdef __cplusplus -# include -# include -using MST_rt = struct MST_rt; -#else -# include -# include -typedef struct MST_rt MST_rt; -#endif - - - -#ifdef __cplusplus -extern "C" { -#endif - -void pgr_do_breadthFirstSearch( - const char*, - ArrayType*, - int64_t, - bool, - MST_rt**, size_t*, - char**, char**, char**); - - -#ifdef __cplusplus - } -#endif - -#endif // INCLUDE_DRIVERS_BREADTHFIRSTSEARCH_BREADTHFIRSTSEARCH_DRIVER_H_ diff --git a/include/drivers/driving_distance/driving_distance_driver.h b/include/drivers/driving_distance/driving_distance_driver.h deleted file mode 100644 index be2433c714..0000000000 --- a/include/drivers/driving_distance/driving_distance_driver.h +++ /dev/null @@ -1,71 +0,0 @@ -/*PGR-GNU***************************************************************** -File: driving_distance_driver.h - -Copyright (c) 2013-2026 pgRouting developers -Mail: project@pgrouting.org - -Copyright (c) 2015 Celia Virginia Vergara Castillo -vicky at erosion.dev - -Copyright (c) 2023 Aryan Gupta -guptaaryan1010 AT gmail.com ------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - ********************************************************************PGR-GNU*/ - -#ifndef INCLUDE_DRIVERS_DRIVING_DISTANCE_DRIVING_DISTANCE_DRIVER_H_ -#define INCLUDE_DRIVERS_DRIVING_DISTANCE_DRIVING_DISTANCE_DRIVER_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#ifdef __cplusplus -} -#endif - -#include "cpp_common/undefPostgresDefine.hpp" - -#ifdef __cplusplus -# include -# include -using MST_rt = struct MST_rt; -#else -# include -# include -typedef struct MST_rt MST_rt; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -void pgr_do_drivingDistance( - const char*, - ArrayType*, - double, bool, bool, - MST_rt**, size_t*, - char**, char**, char **); - -#ifdef __cplusplus -} -#endif - -#endif // INCLUDE_DRIVERS_DRIVING_DISTANCE_DRIVING_DISTANCE_DRIVER_H_ diff --git a/include/drivers/spanningTree/mst_common.h b/include/drivers/spanningTree/mst_common.h deleted file mode 100644 index b490d3e879..0000000000 --- a/include/drivers/spanningTree/mst_common.h +++ /dev/null @@ -1,57 +0,0 @@ -/*PGR-GNU***************************************************************** -File: mst_common.h - -Copyright (c) 2007-2026 pgRouting developers -Mail: project@pgrouting.org - -Copyright (c) 2018 Vicky Vergara -Mail: vicky at georepublic dot de - ------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - ********************************************************************PGR-GNU*/ - -#ifndef INCLUDE_DRIVERS_SPANNINGTREE_MST_COMMON_H_ -#define INCLUDE_DRIVERS_SPANNINGTREE_MST_COMMON_H_ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief defines ordering - * - * @param[in] fn_suffix - * @param[in] err_msg - * - * @return - * 0 = no matter - * 1 = DFS - * 2 = BFS - */ -int -get_order(char * fn_suffix, char ** err_msg); - -char * -get_name(int fn_id, char * fn_suffix, char ** err_msg); - -#ifdef __cplusplus -} -#endif - -#endif // INCLUDE_DRIVERS_SPANNINGTREE_MST_COMMON_H_ diff --git a/include/spanningTree/details.hpp b/include/drivers/spanningTree_driver.hpp similarity index 55% rename from include/spanningTree/details.hpp rename to include/drivers/spanningTree_driver.hpp index 3996e9a843..58f6ce2439 100644 --- a/include/spanningTree/details.hpp +++ b/include/drivers/spanningTree_driver.hpp @@ -1,11 +1,12 @@ /*PGR-GNU***************************************************************** -File: details.hpp +File: spanningTree_driver.hpp -Copyright (c) 2015-2026 pgRouting developers +Copyright (c) 2026-2026 pgRouting developers Mail: project@pgrouting.org -Copyright (c) 2018 Vicky Vergara - +Design of one process & driver file by +Copyright (c) 2026 Celia Virginia Vergara Castillo +Mail: vicky at erosion.dev ------ @@ -25,26 +26,33 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ -#ifndef INCLUDE_SPANNINGTREE_DETAILS_HPP_ -#define INCLUDE_SPANNINGTREE_DETAILS_HPP_ -#pragma once +#ifndef INCLUDE_DRIVERS_SPANNINGTREE_DRIVER_HPP_ +#define INCLUDE_DRIVERS_SPANNINGTREE_DRIVER_HPP_ -#include +#include #include +#include +#include + +#include "c_common/enums.h" -#include "c_types/mst_rt.h" +using MST_rt = struct MST_rt; +using ArrayType = struct ArrayType; namespace pgrouting { +namespace drivers { -namespace details { +void do_spanningTree( + const std::string&, + ArrayType*, -std::vector -clean_vids(std::vector vids); + bool, int64_t, double, bool, -std::vector -get_no_edge_graph_result(const std::vector &vids); + Which, + MST_rt*&, size_t&, + std::ostringstream&, std::ostringstream&, std::ostringstream&); -} // namespace details +} // namespace drivers } // namespace pgrouting -#endif // INCLUDE_SPANNINGTREE_DETAILS_HPP_ +#endif // INCLUDE_DRIVERS_SPANNINGTREE_DRIVER_HPP_ diff --git a/include/drivers/traversal/depthFirstSearch_driver.h b/include/drivers/traversal/depthFirstSearch_driver.h deleted file mode 100644 index 68b2a858d8..0000000000 --- a/include/drivers/traversal/depthFirstSearch_driver.h +++ /dev/null @@ -1,76 +0,0 @@ -/*PGR-GNU***************************************************************** -File: depthFirstSearch_driver.h - -Generated with Template by: -Copyright (c) 2007-2026 pgRouting developers -Mail: project@pgrouting.org - -Function's developer: -Copyright (c) 2020 Ashish Kumar -Mail: ashishkr23438@gmail.com - ------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - ********************************************************************PGR-GNU*/ - -#ifndef INCLUDE_DRIVERS_TRAVERSAL_DEPTHFIRSTSEARCH_DRIVER_H_ -#define INCLUDE_DRIVERS_TRAVERSAL_DEPTHFIRSTSEARCH_DRIVER_H_ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#ifdef __cplusplus -} -#endif - -#include "cpp_common/undefPostgresDefine.hpp" - -#ifdef __cplusplus -# include -# include -using Edge_t = struct Edge_t; -using MST_rt = struct MST_rt; -#else -# include -# include -typedef struct Edge_t Edge_t; -typedef struct MST_rt MST_rt; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -void pgr_do_depthFirstSearch( - const char*, - ArrayType*, - - bool, int64_t, - - MST_rt**, size_t*, - char**, char**, char**); - -#ifdef __cplusplus -} -#endif - -#endif // INCLUDE_DRIVERS_TRAVERSAL_DEPTHFIRSTSEARCH_DRIVER_H_ diff --git a/include/drivers/spanningTree/prim_driver.h b/include/process/spanningTree_process.h similarity index 60% rename from include/drivers/spanningTree/prim_driver.h rename to include/process/spanningTree_process.h index 668dab6b9b..45e7e25cca 100644 --- a/include/drivers/spanningTree/prim_driver.h +++ b/include/process/spanningTree_process.h @@ -1,13 +1,12 @@ /*PGR-GNU***************************************************************** -File: prim_driver.h +File: spanningTree_process.h -Generated with Template by: -Copyright (c) 2007-2026 pgRouting developers +Copyright (c) 2026-2026 pgRouting developers Mail: project@pgrouting.org -Function's developer: -Copyright (c) 2018 Aditya Pratap Singh -Mail: adityapratap.singh28@gmail.com +Design of one process & driver file by +Copyright (c) 2026 Celia Virginia Vergara Castillo +Mail: vicky at erosion.dev ------ @@ -27,49 +26,41 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ -#ifndef INCLUDE_DRIVERS_SPANNINGTREE_PRIM_DRIVER_H_ -#define INCLUDE_DRIVERS_SPANNINGTREE_PRIM_DRIVER_H_ +#ifndef INCLUDE_PROCESS_SPANNINGTREE_PROCESS_H_ +#define INCLUDE_PROCESS_SPANNINGTREE_PROCESS_H_ #pragma once #ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#ifdef __cplusplus -} -#endif - -#include "cpp_common/undefPostgresDefine.hpp" - -#ifdef __cplusplus -# include -# include +#include +#include using MST_rt = struct MST_rt; +using ArrayType = struct ArrayType; #else -# include -# include +#include +#include typedef struct MST_rt MST_rt; +typedef struct ArrayType ArrayType; #endif +#include "c_common/enums.h" #ifdef __cplusplus extern "C" { #endif -void pgr_do_prim( +void pgr_process_spanningTree( const char*, ArrayType*, - const char*, int64_t, double, + bool, int64_t, double, bool, + + const char*, + enum Which, - MST_rt**, size_t*, - char**, char**, char**); + MST_rt**, size_t*); #ifdef __cplusplus } #endif -#endif // INCLUDE_DRIVERS_SPANNINGTREE_PRIM_DRIVER_H_ +#endif // INCLUDE_PROCESS_SPANNINGTREE_PROCESS_H_ diff --git a/include/spanningTree/kruskal.hpp b/include/spanningTree/kruskal.hpp index 77f4df4dac..ce45abce68 100644 --- a/include/spanningTree/kruskal.hpp +++ b/include/spanningTree/kruskal.hpp @@ -30,6 +30,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #pragma once #include +#include #include #include @@ -37,28 +38,36 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include "cpp_common/interruption.hpp" namespace pgrouting { -namespace functions { +namespace algorithms { template class Pgr_kruskal : public Pgr_mst { public: ~Pgr_kruskal() override = default; - std::vector kruskal(G &graph); + std::vector kruskal(G &graph) { + return this->mst(graph); + } std::vector kruskalBFS( G &graph, - std::vector roots, - int64_t max_depth); + const std::set& roots, + int64_t max_depth) { + return this->mstBFS(graph, roots, max_depth); + } std::vector kruskalDFS( G &graph, - std::vector roots, - int64_t max_depth); + const std::set& roots, + int64_t max_depth) { + return this->mstDFS(graph, roots, max_depth); + } std::vector kruskalDD( G &graph, - std::vector roots, - double distance); + const std::set& roots, + double distance) { + return this->mstDD(graph, roots, distance); + } private: typedef typename G::B_G B_G; @@ -66,58 +75,32 @@ class Pgr_kruskal : public Pgr_mst { typedef typename G::E E; /* Does all the work */ - void generate_mst(const G &graph) override; + void generate_mst(const G &graph) override { + this->clear(); + /* abort in case of an interruption occurs (e.g. the query is being cancelled) */ + CHECK_FOR_INTERRUPTS(); + boost::kruskal_minimum_spanning_tree( + graph.graph, + std::inserter(this->m_spanning_tree.edges, this->m_spanning_tree.edges.begin()), + boost::weight_map(get(&G::G_T_E::cost, graph.graph))); + } }; +} // namespace algorithms -template -void -Pgr_kruskal::generate_mst(const G &graph) { - this->clear(); - /* abort in case of an interruption occurs (e.g. the query is being cancelled) */ - CHECK_FOR_INTERRUPTS(); - boost::kruskal_minimum_spanning_tree( - graph.graph, - std::inserter(this->m_spanning_tree.edges, this->m_spanning_tree.edges.begin()), - boost::weight_map(get(&G::G_T_E::cost, graph.graph))); -} - +namespace functions { -template std::vector -Pgr_kruskal::kruskal( - G &graph) { - return this->mst(graph); -} +kruskal(pgrouting::UndirectedGraph&); - -template std::vector -Pgr_kruskal::kruskalBFS( - G &graph, - std::vector roots, - int64_t max_depth) { - return this->mstBFS(graph, roots, max_depth); -} +kruskalBFS(pgrouting::UndirectedGraph&, const std::set&, int64_t); -template std::vector -Pgr_kruskal::kruskalDFS( - G &graph, - std::vector roots, - int64_t max_depth) { - return this->mstDFS(graph, roots, max_depth); -} +kruskalDFS(pgrouting::UndirectedGraph&, const std::set&, int64_t); -template std::vector -Pgr_kruskal::kruskalDD( - G &graph, - std::vector roots, - double distance) { - return this->mstDD(graph, roots, distance); -} - +kruskalDD(pgrouting::UndirectedGraph&, const std::set&, double); } // namespace functions } // namespace pgrouting diff --git a/include/spanningTree/mst.hpp b/include/spanningTree/mst.hpp index 7f6ba56e50..174d4b06b0 100644 --- a/include/spanningTree/mst.hpp +++ b/include/spanningTree/mst.hpp @@ -43,10 +43,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include "cpp_common/base_graph.hpp" #include "cpp_common/interruption.hpp" -#include "spanningTree/details.hpp" +#include "cpp_common/utilities.hpp" namespace pgrouting { -namespace functions { +namespace algorithms { template class Pgr_mst { @@ -99,13 +99,14 @@ class Pgr_mst { std::vector mstBFS( const G &graph, - std::vector roots, + const std::set &roots, int64_t max_depth) { m_suffix = "BFS"; m_get_component = true; m_distance = -1; m_max_depth = max_depth; - m_roots = details::clean_vids(roots); + m_roots = roots; + m_roots.erase(0); this->generate_mst(graph); return bfs_order(graph); @@ -113,13 +114,14 @@ class Pgr_mst { std::vector mstDFS( const G &graph, - std::vector roots, + const std::set &roots, int64_t max_depth) { m_suffix = "DFS"; m_get_component = false; m_distance = -1; m_max_depth = max_depth; - m_roots = details::clean_vids(roots); + m_roots = roots; + m_roots.erase(0); this->generate_mst(graph); return dfs_order(graph); @@ -127,20 +129,21 @@ class Pgr_mst { std::vector mstDD( const G &graph, - std::vector roots, + const std::set &roots, double distance) { m_suffix = "DD"; m_get_component = false; m_distance = distance; m_max_depth = -1; - m_roots = details::clean_vids(roots); + m_roots = roots; + m_roots.erase(0); this->generate_mst(graph); return dfs_order(graph); } protected: - std::vector m_roots; + std::set m_roots; bool m_get_component; int64_t m_max_depth; double m_distance; @@ -336,11 +339,11 @@ class Pgr_mst { calculate_component(graph); - std::vector roots; + std::set roots; if (!m_roots.empty()) { roots = m_roots; } else { - roots = m_tree_id; + roots.insert(m_tree_id.begin(), m_tree_id.end()); } using bfs_visitor = visitors::Edges_order_bfs_visitor; @@ -372,7 +375,7 @@ class Pgr_mst { } }; -} // namespace functions +} // namespace algorithms } // namespace pgrouting #endif // INCLUDE_SPANNINGTREE_MST_HPP_ diff --git a/include/spanningTree/prim.hpp b/include/spanningTree/prim.hpp index dec89f6de8..4972eb4771 100644 --- a/include/spanningTree/prim.hpp +++ b/include/spanningTree/prim.hpp @@ -39,10 +39,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include "spanningTree/mst.hpp" #include "cpp_common/interruption.hpp" -//****************************************** - namespace pgrouting { -namespace functions { +namespace algorithms { template class Pgr_prim : public Pgr_mst { @@ -52,23 +50,39 @@ class Pgr_prim : public Pgr_mst { public: ~Pgr_prim() override = default; - std::vector prim(G &graph); + + std::vector prim(G &graph) { + return this->mst(graph); + } std::vector primBFS( G &graph, - std::vector roots, - int64_t max_depth); + const std::set& roots, + int64_t max_depth) { + return this->mstBFS(graph, roots, max_depth); + } std::vector primDFS( G &graph, - std::vector roots, - int64_t max_depth); + const std::set& roots, + int64_t max_depth) { + return this->mstDFS(graph, roots, max_depth); + } std::vector primDD( G &graph, - std::vector roots, - double distance); + const std::set& roots, + double distance) { + return this->mstDD(graph, roots, distance); + } + + private: + // Member + std::vector predecessors; + std::vector distances; + std::vector data; + std::set m_unassigned; private: // Functions @@ -80,123 +94,85 @@ class Pgr_prim : public Pgr_mst { void primTree( const G &graph, - int64_t root_vertex); + int64_t root_vertex) { + clear(); - void generate_mst(const G &graph) override; - - private: - // Member - std::vector predecessors; - std::vector distances; - std::vector data; - std::set m_unassigned; -}; + predecessors.resize(graph.num_vertices()); + distances.resize(graph.num_vertices()); + auto v_root(graph.get_V(root_vertex)); -template -void -Pgr_prim::primTree( - const G &graph, - int64_t root_vertex) { - clear(); + using prim_visitor = visitors::Prim_dijkstra_visitor; - predecessors.resize(graph.num_vertices()); - distances.resize(graph.num_vertices()); + /* abort in case of an interruption occurs (e.g. the query is being cancelled) */ + CHECK_FOR_INTERRUPTS(); - auto v_root(graph.get_V(root_vertex)); + boost::prim_minimum_spanning_tree( + graph.graph, + &predecessors[0], + boost::distance_map(&distances[0]). + weight_map(get(&G::G_T_E::cost, graph.graph)) + .root_vertex(v_root) + .visitor(prim_visitor(data))); - using prim_visitor = visitors::Prim_dijkstra_visitor; + for (const auto v : data) { + /* + * its not a tree, its a forest + * - v is not on current tree + */ + if (std::isinf(distances[v])) continue; + m_unassigned.erase(v); - /* abort in case of an interruption occurs (e.g. the query is being cancelled) */ - CHECK_FOR_INTERRUPTS(); - boost::prim_minimum_spanning_tree( - graph.graph, - &predecessors[0], - boost::distance_map(&distances[0]). - weight_map(get(&G::G_T_E::cost, graph.graph)) - .root_vertex(v_root) - .visitor(prim_visitor(data))); + auto u = predecessors[v]; - for (const auto v : data) { - /* - * its not a tree, its a forest - * - v is not on current tree - */ - if (std::isinf(distances[v])) continue; - m_unassigned.erase(v); + /* + * Not a valid edge + */ + if (u == v) continue; + auto cost = distances[v] - distances[u]; + auto edge = graph.get_edge(u, v, cost); + this->m_spanning_tree.edges.insert(edge); + } + } - auto u = predecessors[v]; - /* - * Not a valid edge - */ - if (u == v) continue; + void generate_mst(const G &graph) override { + this->clear(); - auto cost = distances[u] - distances[v]; - auto edge = graph.get_edge(u, v, cost); - this->m_spanning_tree.edges.insert(edge); - } -} + size_t totalNodes = num_vertices(graph.graph); + m_unassigned.clear(); + for (V v = 0; v < totalNodes; ++v) { + m_unassigned.insert(m_unassigned.end(), v); + } -template -void -Pgr_prim::generate_mst(const G &graph) { - this->clear(); - - size_t totalNodes = num_vertices(graph.graph); - - m_unassigned.clear(); - for (V v = 0; v < totalNodes; ++v) { - m_unassigned.insert(m_unassigned.end(), v); - } - - while (!m_unassigned.empty()) { - auto root = *m_unassigned.begin(); - m_unassigned.erase(m_unassigned.begin()); - primTree( - graph, - graph.graph[root].id); - } -} + while (!m_unassigned.empty()) { + auto root = *m_unassigned.begin(); + m_unassigned.erase(m_unassigned.begin()); + primTree( + graph, + graph.graph[root].id); + } + } +}; -template -std::vector -Pgr_prim::prim( - G &graph) { - return this->mst(graph); -} +} // namespace algorithms -template -std::vector -Pgr_prim::primBFS( - G &graph, - std::vector roots, - int64_t max_depth) { - return this->mstBFS(graph, roots, max_depth); -} +namespace functions { -template -std::vector -Pgr_prim::primDFS( - G &graph, - std::vector roots, - int64_t max_depth) { - return this->mstDFS(graph, roots, max_depth); -} + std::vector + prim(pgrouting::UndirectedGraph&); -template -std::vector -Pgr_prim::primDD( - G &graph, - std::vector roots, - double distance) { - return this->mstDD(graph, roots, distance); -} + std::vector + primBFS(pgrouting::UndirectedGraph&, const std::set&, int64_t); + std::vector + primDFS(pgrouting::UndirectedGraph&, const std::set&, int64_t); + std::vector + primDD(pgrouting::UndirectedGraph&, const std::set&, double); } // namespace functions } // namespace pgrouting diff --git a/include/traversal/depthFirstSearch.hpp b/include/traversal/depthFirstSearch.hpp index 698ebfc884..b731d80285 100644 --- a/include/traversal/depthFirstSearch.hpp +++ b/include/traversal/depthFirstSearch.hpp @@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #define INCLUDE_TRAVERSAL_DEPTHFIRSTSEARCH_HPP_ #pragma once +#include #include #include #include @@ -41,11 +42,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include "c_types/mst_rt.h" namespace pgrouting { -namespace functions { +namespace algorithms { //************************************************************* -template < class G > +template class Pgr_depthFirstSearch { public: typedef typename G::V V; @@ -72,14 +73,14 @@ class Pgr_depthFirstSearch { * @see [boost::undirected_dfs] * (https://www.boost.org/libs/graph/doc/undirected_dfs.html) */ - std::vector < MST_rt > depthFirstSearch( + std::vector depthFirstSearch( G &graph, - std::vector < int64_t > roots, + const std::set& roots, bool directed, int64_t max_depth) { - std::vector < MST_rt > results; + std::vector results; - for (auto root : roots) { + for (const auto& root : roots) { results.push_back({root, 0, root, root, -1, 0.0, 0.0}); if (graph.has_vertex(root)) { @@ -118,14 +119,14 @@ class Pgr_depthFirstSearch { bool depthFirstSearch_single_vertex( G &graph, V root, - std::vector < E > &visited_order, + std::vector &visited_order, bool directed, int64_t max_depth) { - using dfs_visitor = visitors::Dfs_visitor < V, E, G >; + using dfs_visitor = visitors::Dfs_visitor ; // Exterior property storage containers - std::vector < boost::default_color_type > colors(boost::num_vertices(graph.graph)); - std::map < E, boost::default_color_type > edge_color; + std::vector colors(boost::num_vertices(graph.graph)); + std::map edge_color; auto i_map = boost::get(boost::vertex_index, graph.graph); @@ -171,16 +172,16 @@ class Pgr_depthFirstSearch { * * @returns `results` vector */ - template < typename T > - std::vector < MST_rt > get_results( + template + std::vector get_results( T visited_order, int64_t root, int64_t max_depth, const G &graph) { - std::vector < MST_rt > results; + std::vector results; - std::vector < double > agg_cost(graph.num_vertices(), 0); - std::vector < int64_t > depth(graph.num_vertices(), 0); + std::vector agg_cost(graph.num_vertices(), 0); + std::vector depth(graph.num_vertices(), 0); for (const auto edge : visited_order) { auto u = graph.source(edge); @@ -204,6 +205,36 @@ class Pgr_depthFirstSearch { return results; } }; + +} // namespace algorithms + +namespace functions { + +/** @brief Calls the main function defined in the C++ Header file. + * + * Also sorts the root vertices in an increasing order, + * and removes the duplicated vertices. Then calls the function + * defined in the C++ Header file - `pgr_depthFirstSearch.hpp` + * + * @param graph the graph containing the edges + * @param roots the root vertices + * @param directed whether the graph is directed or undirected + * @param max_depth the maximum depth of traversal + * + * @returns results, when results are found + */ +template +std::vector +depthFirstSearch( + G &graph, + const std::set& roots, + bool directed, + int64_t max_depth) { + algorithms::Pgr_depthFirstSearch fn_depthFirstSearch; + auto results = fn_depthFirstSearch.depthFirstSearch(graph, roots, directed, max_depth); + return results; +} + } // namespace functions } // namespace pgrouting diff --git a/locale/en/LC_MESSAGES/pgrouting_doc_strings.po b/locale/en/LC_MESSAGES/pgrouting_doc_strings.po index 36ee5496a3..e3d55a1fc8 100644 --- a/locale/en/LC_MESSAGES/pgrouting_doc_strings.po +++ b/locale/en/LC_MESSAGES/pgrouting_doc_strings.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: pgRouting v4.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-02-19 14:11+0000\n" +"POT-Creation-Date: 2026-02-21 01:08+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -3956,6 +3956,11 @@ msgid "" "create and use a process and driver for Astar" msgstr "" +msgid "" +"`#3075 `__: Spanning " +"tree: create and use a process and driver" +msgstr "" + msgid "All releases" msgstr "" diff --git a/locale/pot/pgrouting_doc_strings.pot b/locale/pot/pgrouting_doc_strings.pot index 45dc4d6f81..bf3e750913 100644 --- a/locale/pot/pgrouting_doc_strings.pot +++ b/locale/pot/pgrouting_doc_strings.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: pgRouting v4.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-02-19 14:11+0000\n" +"POT-Creation-Date: 2026-02-21 01:08+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -3547,6 +3547,9 @@ msgstr "" msgid "`#3064 `__: Astar: create and use a process and driver for Astar" msgstr "" +msgid "`#3075 `__: Spanning tree: create and use a process and driver" +msgstr "" + msgid "All releases" msgstr "" diff --git a/src/allpairs/allpairs_process.cpp b/src/allpairs/allpairs_process.cpp index de544210f2..45b55e0524 100644 --- a/src/allpairs/allpairs_process.cpp +++ b/src/allpairs/allpairs_process.cpp @@ -81,7 +81,7 @@ void pgr_process_allpairs( time_msg(name.c_str(), start_t, clock()); if (!err.str().empty() && (*result_tuples)) { - if (*result_tuples) pfree(*result_tuples); + pfree(*result_tuples); (*result_tuples) = nullptr; (*result_count) = 0; } diff --git a/src/astar/astar_process.cpp b/src/astar/astar_process.cpp index 00a2c616b3..0a000068d2 100644 --- a/src/astar/astar_process.cpp +++ b/src/astar/astar_process.cpp @@ -92,6 +92,8 @@ void pgr_process_astar( time_msg(name.c_str(), start_t, clock()); if (!err.str().empty() && (*result_tuples)) { + pfree(*result_tuples); + (*result_tuples) = nullptr; (*result_count) = 0; } diff --git a/src/breadthFirstSearch/CMakeLists.txt b/src/breadthFirstSearch/CMakeLists.txt index a1ad93819a..ca3c081e48 100644 --- a/src/breadthFirstSearch/CMakeLists.txt +++ b/src/breadthFirstSearch/CMakeLists.txt @@ -1,9 +1,10 @@ # This file is part of the pgRouting project. # Copyright (c) 2019-2026 pgRouting developers # License: GPL-2 See https://github.com/pgRouting/pgrouting/blob/main/LICENSE + ADD_LIBRARY(breadthFirstSearch OBJECT breadthFirstSearch.c - breadthFirstSearch_driver.cpp binaryBreadthFirstSearch.c + binaryBreadthFirstSearch_driver.cpp - ) +) diff --git a/src/breadthFirstSearch/breadthFirstSearch.c b/src/breadthFirstSearch/breadthFirstSearch.c index 5124292b8a..ad201a5ae4 100644 --- a/src/breadthFirstSearch/breadthFirstSearch.c +++ b/src/breadthFirstSearch/breadthFirstSearch.c @@ -9,7 +9,6 @@ Function's developer: Copyright (c) 2019 Gudesa Venkata Sai Akhil Mail: gvs.akhil1997 at gmail.com - ------ This program is free software; you can redistribute it and/or modify @@ -29,60 +28,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include + #include "c_common/postgres_connection.h" -#include "c_common/debug_macro.h" -#include "c_common/e_report.h" -#include "c_common/time_msg.h" #include "c_types/mst_rt.h" -#include "drivers/breadthFirstSearch/breadthFirstSearch_driver.h" + +#include "process/spanningTree_process.h" PGDLLEXPORT Datum _pgr_breadthfirstsearch(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(_pgr_breadthfirstsearch); -static void -process( - char *edges_sql, - ArrayType *starts, - int64_t max_depth, - bool directed, - - MST_rt **result_tuples, - size_t *result_count) { - if (max_depth < 0) pgr_throw_error("Negative value found on 'max_depth'", ""); - pgr_SPI_connect(); - char* log_msg = NULL; - char* notice_msg = NULL; - char* err_msg = NULL; - (*result_tuples) = NULL; - (*result_count) = 0; - - clock_t start_t = clock(); - pgr_do_breadthFirstSearch( - edges_sql, - starts, - max_depth, - directed, - - result_tuples, - result_count, - - &log_msg, - ¬ice_msg, - &err_msg); - - time_msg(" processing pgr_breadthFirstSearch", start_t, clock()); - - if (err_msg && (*result_tuples)) { - pfree(*result_tuples); - (*result_tuples) = NULL; - (*result_count) = 0; - } - - pgr_global_report(&log_msg, ¬ice_msg, &err_msg); - - pgr_SPI_finish(); -} PGDLLEXPORT Datum _pgr_breadthfirstsearch(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; @@ -92,25 +47,32 @@ PGDLLEXPORT Datum _pgr_breadthfirstsearch(PG_FUNCTION_ARGS) { size_t result_count = 0; if (SRF_IS_FIRSTCALL()) { - MemoryContext oldcontext; + MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - process( - text_to_cstring(PG_GETARG_TEXT_P(0)), - PG_GETARG_ARRAYTYPE_P(1), - PG_GETARG_INT64(2), - PG_GETARG_BOOL(3), - &result_tuples, - &result_count); + pgr_process_spanningTree( + text_to_cstring(PG_GETARG_TEXT_P(0)), + PG_GETARG_ARRAYTYPE_P(1), + + PG_GETARG_BOOL(3), + PG_GETARG_INT64(2), + 0, + false, + + NULL, + BFS, + &result_tuples, + &result_count); funcctx->max_calls = result_count; funcctx->user_fctx = result_tuples; - if (get_call_result_type(fcinfo, NULL, &tuple_desc) != TYPEFUNC_COMPOSITE) { + if (get_call_result_type(fcinfo, NULL, &tuple_desc) + != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " - "that cannot accept type record"))); + "that cannot accept type record"))); } funcctx->tuple_desc = tuple_desc; @@ -119,20 +81,21 @@ PGDLLEXPORT Datum _pgr_breadthfirstsearch(PG_FUNCTION_ARGS) { funcctx = SRF_PERCALL_SETUP(); tuple_desc = funcctx->tuple_desc; - result_tuples = (MST_rt *)funcctx->user_fctx; + result_tuples = (MST_rt*) funcctx->user_fctx; if (funcctx->call_cntr < funcctx->max_calls) { - HeapTuple tuple; - Datum result; - Datum *values; - bool *nulls; + HeapTuple tuple; + Datum result; + Datum *values; + bool* nulls; + + size_t num = 8; + values = palloc(num * sizeof(Datum)); + nulls = palloc(num * sizeof(bool)); - size_t numb = 8; - values = palloc(numb * sizeof(Datum)); - nulls = palloc(numb * sizeof(bool)); size_t i; - for (i = 0; i < numb; ++i) { + for (i = 0; i < num; ++i) { nulls[i] = false; } diff --git a/src/breadthFirstSearch/breadthFirstSearch_driver.cpp b/src/breadthFirstSearch/breadthFirstSearch_driver.cpp deleted file mode 100644 index 011509463b..0000000000 --- a/src/breadthFirstSearch/breadthFirstSearch_driver.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/*PGR-GNU***************************************************************** -File: breadthFirstSearch_driver.cpp - -Generated with Template by: -Copyright (c) 2015-2026 pgRouting developers -Mail: project@pgrouting.org - -Function's developer: -Copyright (c) 2019 Gudesa Venkata Sai Akhil -Mail: gvs.akhil1997@gmail.com - ------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - ********************************************************************PGR-GNU*/ - -#include "drivers/breadthFirstSearch/breadthFirstSearch_driver.h" - -#include -#include -#include -#include - -#include "cpp_common/pgdata_getters.hpp" -#include "cpp_common/alloc.hpp" -#include "cpp_common/assert.hpp" - -#include "breadthFirstSearch/breadthFirstSearch.hpp" - -namespace { - - -template std::vector breadthFirstSearch( - G &graph, - std::set sources, - int64_t max_depth) { - pgrouting::functions::Pgr_breadthFirstSearch fn_breadthFirstSearch; - auto results = fn_breadthFirstSearch.breadthFirstSearch(graph, sources, max_depth); - return results; -} - -} // namespace - -void -pgr_do_breadthFirstSearch( - const char *edges_sql, - ArrayType* starts, - int64_t max_depth, - bool directed, - - MST_rt **return_tuples, - size_t *return_count, - char ** log_msg, - char ** notice_msg, - char ** err_msg) { - using pgrouting::pgr_alloc; - using pgrouting::to_pg_msg; - using pgrouting::pgr_free; - using pgrouting::pgget::get_intSet; - - std::ostringstream log; - std::ostringstream err; - std::ostringstream notice; - const char *hint = nullptr; - - try { - // NOLINTBEGIN(clang-analyzer-cplusplus.NewDelete) - pgassert(!(*log_msg)); - pgassert(!(*notice_msg)); - pgassert(!(*err_msg)); - pgassert(!(*return_tuples)); - pgassert(*return_count == 0); - // NOLINTEND(clang-analyzer-cplusplus.NewDelete) - - - - hint = edges_sql; - auto edges = pgrouting::pgget::get_edges(std::string(edges_sql), true, false); - - if (edges.empty()) { - *notice_msg = to_pg_msg("No edges found"); - *log_msg = hint? to_pg_msg(hint) : to_pg_msg(log); - return; - } - hint = nullptr; - - auto roots = get_intSet(starts); - - std::vector results; - if (directed) { - pgrouting::DirectedGraph digraph; - digraph.insert_edges(edges); - results = breadthFirstSearch(digraph, roots, max_depth); - - } else { - pgrouting::UndirectedGraph undigraph; - undigraph.insert_edges(edges); - results = breadthFirstSearch(undigraph, roots, max_depth); - } - - auto count = results.size(); - - if (count == 0) { - (*return_tuples) = NULL; - (*return_count) = 0; - notice << "No traversal found"; - *log_msg = to_pg_msg(notice); - return; - } - - (*return_tuples) = pgr_alloc(count, (*return_tuples)); - (*return_count) = count; - for (size_t i = 0; i < count; i++) { - *((*return_tuples) + i) = results[i]; - } - - *log_msg = to_pg_msg(log); - *notice_msg = to_pg_msg(notice); - } catch (AssertFailedException &except) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << except.what(); - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } catch (const std::string &ex) { - *err_msg = to_pg_msg(ex); - *log_msg = hint? to_pg_msg(hint) : to_pg_msg(log); - } catch (std::exception &except) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << except.what(); - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } catch(...) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << "Caught unknown exception!"; - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } -} diff --git a/src/cpp_common/to_postgres.cpp b/src/cpp_common/to_postgres.cpp index ec712339c6..4fa565d537 100644 --- a/src/cpp_common/to_postgres.cpp +++ b/src/cpp_common/to_postgres.cpp @@ -26,6 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include #include +#include #include #include #include @@ -98,7 +99,7 @@ void get_path( const pgrouting::Path &path, MST_rt* &tuples, size_t &sequence) { - for (const auto e : path) { + for (const auto &e : path) { tuples[sequence] = { path.start_id(), 0, @@ -224,5 +225,64 @@ get_tuples( return sequence; } +size_t +get_tuples( + const std::vector &results, + MST_rt* &tuples) { + pgassert(!tuples); + + auto count = results.size(); + if (count == 0) return 0; + + tuples = pgr_alloc(count, tuples); + + for (size_t i = 0; i < count; i++) { + tuples[i] = results[i]; + } + return count; +} + + +size_t +get_tuples( + const std::vector &results, + const std::deque &paths, + const std::vector>& depths, + MST_rt* &tuples) { + pgassert(!tuples); + + if (!results.empty()) { + /* + * These are not driving distance results + */ + return get_tuples(results, tuples); + } + + /* + * This are the driving distance results + */ + auto count = get_tuples(paths, tuples); + if (count == 0) return 0; + + for (size_t i = 0; i < count; i++) { + const auto& row = tuples[i]; + /* given the depth assign the correct depth */ + int64_t depth = -1; + for (const auto &d : depths) { + /* look for the correct path */ + auto itr = d.find(row.from_v); + if (itr == d.end() || !(itr->second == 0)) continue; + auto node_itr = d.find(row.node); + if (node_itr != d.end()) { + depth = node_itr->second; + } + break; + } + tuples[i].depth = depth; + } + + return count; +} + } // namespace to_postgres } // namespace pgrouting diff --git a/src/cpp_common/utilities.cpp b/src/cpp_common/utilities.cpp index e405e10506..9735d37818 100644 --- a/src/cpp_common/utilities.cpp +++ b/src/cpp_common/utilities.cpp @@ -24,10 +24,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include "cpp_common/utilities.hpp" +#include +#include #include #include +#include #include "c_common/enums.h" +#include "c_types/mst_rt.h" namespace pgrouting { @@ -55,6 +59,39 @@ get_name(Which which) { case BANDWIDTH: return "pgr_bandwidth"; break; + case KRUSKAL: + return "pgr_kruskal"; + break; + case KRUSKALDD: + return "pgr_kruskalDD"; + break; + case KRUSKALDFS: + return "pgr_kruskalDFS"; + break; + case KRUSKALBFS: + return "pgr_kruskalBFS"; + break; + case PRIM: + return "pgr_prim"; + break; + case PRIMDD: + return "pgr_primDD"; + break; + case PRIMDFS: + return "pgr_primDFS"; + break; + case PRIMBFS: + return "pgr_primBFS"; + break; + case DFS: + return "pgr_depthFirstSearch"; + break; + case BFS: + return "pgr_breadthFirstSearch"; + break; + case DIJKSTRADD: + return "pgr_drivingDistance"; + break; default: return "unknown"; break; @@ -148,6 +185,11 @@ get_new_queries( const std::string &points_sql, std::string &edges_of_points_query, std::string &edges_no_points_query) { + if (points_sql.empty()) { + edges_no_points_query = edges_sql; + return; + } + edges_of_points_query = std::string("WITH ") + " edges AS (" + edges_sql + "), " + " points AS (" + points_sql + ")" @@ -161,4 +203,14 @@ get_new_queries( + " WHERE NOT EXISTS (SELECT edge_id FROM points WHERE id = edge_id)"; } -} // namespace pgrouting +std::vector +only_root_result(const std::set &vids) { + std::vector results; + if (vids.empty()) return results; + for (auto const root : vids) { + if (root != 0) results.push_back({root, 0, root, root, -1, 0.0, 0.0}); + } + return results; +} + +} // namespace pgrouting diff --git a/src/dijkstra/shortestPath_process.cpp b/src/dijkstra/shortestPath_process.cpp index 56f22da79f..d5169a551c 100644 --- a/src/dijkstra/shortestPath_process.cpp +++ b/src/dijkstra/shortestPath_process.cpp @@ -99,7 +99,7 @@ void pgr_process_shortestPath( time_msg(name.c_str(), start_t, clock()); if (!err.str().empty() && (*result_tuples)) { - if (*result_tuples) pfree(*result_tuples); + pfree(*result_tuples); (*result_tuples) = nullptr; (*result_count) = 0; } diff --git a/src/driving_distance/CMakeLists.txt b/src/driving_distance/CMakeLists.txt index 9097750505..975390d06e 100644 --- a/src/driving_distance/CMakeLists.txt +++ b/src/driving_distance/CMakeLists.txt @@ -5,6 +5,5 @@ ADD_LIBRARY(driving_distance OBJECT driving_distance.c driving_distance_withPoints.c - driving_distance_driver.cpp driving_distance_withPoints_driver.cpp ) diff --git a/src/driving_distance/driving_distance.c b/src/driving_distance/driving_distance.c index dda69922eb..8d3f856330 100644 --- a/src/driving_distance/driving_distance.c +++ b/src/driving_distance/driving_distance.c @@ -28,117 +28,78 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include + #include "c_common/postgres_connection.h" #include "c_types/mst_rt.h" -#include "c_common/debug_macro.h" -#include "c_common/e_report.h" -#include "c_common/time_msg.h" -#include "drivers/driving_distance/driving_distance_driver.h" +#include "process/spanningTree_process.h" PGDLLEXPORT Datum _pgr_drivingdistancev4(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(_pgr_drivingdistancev4); -static -void process( - char* edges_sql, - ArrayType *starts, - double distance, - bool directed, - bool equicost, - MST_rt **result_tuples, - size_t *result_count) { - pgr_SPI_connect(); - char* log_msg = NULL; - char* notice_msg = NULL; - char* err_msg = NULL; - - clock_t start_t = clock(); - pgr_do_drivingDistance( - edges_sql, - starts, - distance, - directed, - equicost, - result_tuples, result_count, - &log_msg, - ¬ice_msg, - &err_msg); - - time_msg("processing pgr_drivingDistance", - start_t, clock()); - - if (err_msg && (*result_tuples)) { - pfree(*result_tuples); - (*result_tuples) = NULL; - (*result_count) = 0; - } - - pgr_global_report(&log_msg, ¬ice_msg, &err_msg); - - pgr_SPI_finish(); -} - - PGDLLEXPORT Datum _pgr_drivingdistancev4(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; - TupleDesc tuple_desc; + TupleDesc tuple_desc; - MST_rt *result_tuples = 0; + MST_rt *result_tuples = NULL; size_t result_count = 0; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; - funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - process( + pgr_process_spanningTree( text_to_cstring(PG_GETARG_TEXT_P(0)), PG_GETARG_ARRAYTYPE_P(1), - PG_GETARG_FLOAT8(2), + PG_GETARG_BOOL(3), + 0, + PG_GETARG_FLOAT8(2), PG_GETARG_BOOL(4), - &result_tuples, &result_count); + NULL, + DIJKSTRADD, + &result_tuples, + &result_count); funcctx->max_calls = result_count; - funcctx->user_fctx = result_tuples; if (get_call_result_type(fcinfo, NULL, &tuple_desc) - != TYPEFUNC_COMPOSITE) + != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); + } funcctx->tuple_desc = tuple_desc; - MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); - tuple_desc = funcctx->tuple_desc; result_tuples = (MST_rt*) funcctx->user_fctx; if (funcctx->call_cntr < funcctx->max_calls) { HeapTuple tuple; Datum result; - Datum *values; - bool* nulls; + Datum *values; + bool* nulls; + + size_t num = 8; + values = palloc(num * sizeof(Datum)); + nulls = palloc(num * sizeof(bool)); - size_t numb = 8; - values = palloc(numb * sizeof(Datum)); - nulls = palloc(numb * sizeof(bool)); size_t i; - for (i = 0; i < numb; ++i) { + for (i = 0; i < num; ++i) { nulls[i] = false; } - values[0] = Int32GetDatum((int32_t)funcctx->call_cntr + 1); + + values[0] = Int64GetDatum((int64_t)funcctx->call_cntr + 1); values[1] = Int64GetDatum(result_tuples[funcctx->call_cntr].depth); values[2] = Int64GetDatum(result_tuples[funcctx->call_cntr].from_v); values[3] = Int64GetDatum(result_tuples[funcctx->call_cntr].pred); @@ -149,17 +110,12 @@ _pgr_drivingdistancev4(PG_FUNCTION_ARGS) { tuple = heap_form_tuple(tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); - - pfree(values); - pfree(nulls); - SRF_RETURN_NEXT(funcctx, result); } else { SRF_RETURN_DONE(funcctx); } } - /* Deprecated code starts here * This code is used on v3.5 and under * @@ -174,14 +130,13 @@ PG_FUNCTION_INFO_V1(_pgr_drivingdistance); PGDLLEXPORT Datum _pgr_drivingdistance(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; - TupleDesc tuple_desc; + TupleDesc tuple_desc; - MST_rt *result_tuples = 0; + MST_rt *result_tuples = NULL; size_t result_count = 0; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; - funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -193,50 +148,55 @@ _pgr_drivingdistance(PG_FUNCTION_ARGS) { errhint("Consider upgrade pgRouting"))); #endif - process( + pgr_process_spanningTree( text_to_cstring(PG_GETARG_TEXT_P(0)), PG_GETARG_ARRAYTYPE_P(1), - PG_GETARG_FLOAT8(2), + PG_GETARG_BOOL(3), + 0, + PG_GETARG_FLOAT8(2), PG_GETARG_BOOL(4), - &result_tuples, &result_count); + NULL, + DIJKSTRADD, + &result_tuples, + &result_count); funcctx->max_calls = result_count; - funcctx->user_fctx = result_tuples; if (get_call_result_type(fcinfo, NULL, &tuple_desc) - != TYPEFUNC_COMPOSITE) + != TYPEFUNC_COMPOSITE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function returning record called in context " "that cannot accept type record"))); + } funcctx->tuple_desc = tuple_desc; - MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); - tuple_desc = funcctx->tuple_desc; result_tuples = (MST_rt*) funcctx->user_fctx; if (funcctx->call_cntr < funcctx->max_calls) { HeapTuple tuple; Datum result; - Datum *values; - bool* nulls; + Datum *values; + bool* nulls; + + size_t num = 6; + values = palloc(num * sizeof(Datum)); + nulls = palloc(num * sizeof(bool)); - size_t numb = 6; - values = palloc(numb * sizeof(Datum)); - nulls = palloc(numb * sizeof(bool)); size_t i; - for (i = 0; i < numb; ++i) { + for (i = 0; i < num; ++i) { nulls[i] = false; } - values[0] = Int32GetDatum((int32_t)funcctx->call_cntr + 1); + + values[0] = UInt64GetDatum(funcctx->call_cntr + 1); values[1] = Int64GetDatum(result_tuples[funcctx->call_cntr].from_v); values[2] = Int64GetDatum(result_tuples[funcctx->call_cntr].node); values[3] = Int64GetDatum(result_tuples[funcctx->call_cntr].edge); @@ -245,10 +205,6 @@ _pgr_drivingdistance(PG_FUNCTION_ARGS) { tuple = heap_form_tuple(tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); - - pfree(values); - pfree(nulls); - SRF_RETURN_NEXT(funcctx, result); } else { SRF_RETURN_DONE(funcctx); diff --git a/src/driving_distance/driving_distance_driver.cpp b/src/driving_distance/driving_distance_driver.cpp deleted file mode 100644 index 0faacee3fa..0000000000 --- a/src/driving_distance/driving_distance_driver.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/*PGR-GNU***************************************************************** -File: driving_distance_driver.cpp - -Copyright (c) 2007-2026 pgRouting developers -Mail: project@pgrouting.org - -Copyright (c) 2015 Celia Virginia Vergara Castillo -Mail: vicky at erosion.dev - -Copyright (c) 2023 Aryan Gupta -guptaaryan1010 AT gmail.com ------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - ********************************************************************PGR-GNU*/ - -#include "drivers/driving_distance/driving_distance_driver.h" - -#include -#include -#include -#include -#include - -#include "cpp_common/pgdata_getters.hpp" -#include "dijkstra/drivingDist.hpp" - -#include "c_types/mst_rt.h" -#include "cpp_common/to_postgres.hpp" -#include "cpp_common/assert.hpp" - - -void -pgr_do_drivingDistance( - const char *edges_sql, - ArrayType* starts, - double distance, - bool directed, - bool equiCostFlag, - MST_rt **return_tuples, size_t *return_count, - char **log_msg, - char **notice_msg, - char **err_msg) { - using pgrouting::Path; - using pgrouting::pgr_alloc; - using pgrouting::to_pg_msg; - using pgrouting::pgr_free; - using pgrouting::algorithm::drivingDistance; - using pgrouting::pgget::get_intSet; - - std::ostringstream log; - std::ostringstream err; - std::ostringstream notice; - const char *hint = nullptr; - - try { - pgassert(!(*log_msg)); - pgassert(!(*notice_msg)); - pgassert(!(*err_msg)); - pgassert(!(*return_tuples)); - pgassert(*return_count == 0); - pgassert((*return_tuples) == NULL); - - using pgrouting::to_postgres::get_tuples; - - auto roots = get_intSet(starts); - - hint = edges_sql; - auto edges = pgrouting::pgget::get_edges(std::string(edges_sql), true, false); - - if (edges.empty()) { - *notice_msg = to_pg_msg("No edges found"); - *log_msg = hint? to_pg_msg(hint) : to_pg_msg(log); - return; - } - hint = nullptr; - - std::deque paths; - std::vector> depths; - - if (directed) { - pgrouting::DirectedGraph digraph; - digraph.insert_edges(edges); - paths = drivingDistance(digraph, roots, distance, equiCostFlag, depths, true); - } else { - pgrouting::UndirectedGraph undigraph; - undigraph.insert_edges(edges); - paths = drivingDistance(undigraph, roots, distance, equiCostFlag, depths, true); - } - - *return_count = get_tuples(paths, *return_tuples); - - if (*return_count == 0) { - *log_msg = to_pg_msg("No paths found"); - return; - } - - for (size_t i = 0; i < *return_count; i++) { - auto row = (*return_tuples)[i]; - /* given the depth assign the correct depth */ - int64_t depth = -1; - for (const auto &d : depths) { - /* look for the correct path */ - auto itr = d.find(row.from_v); - if (itr == d.end() || !(itr->second == 0)) continue; - depth = d.at(row.node); - } - (*return_tuples)[i].depth = depth; - } - - *log_msg = to_pg_msg(log); - *notice_msg = to_pg_msg(notice); - } catch (AssertFailedException &except) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << except.what(); - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } catch (const std::string &ex) { - *err_msg = to_pg_msg(ex); - *log_msg = hint? to_pg_msg(hint) : to_pg_msg(log); - } catch (std::exception &except) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << except.what(); - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } catch( ... ) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << "Caught unknown exception!"; - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } -} diff --git a/src/driving_distance/driving_distance_withPoints_driver.cpp b/src/driving_distance/driving_distance_withPoints_driver.cpp index 59d290d6da..99cfcf2351 100644 --- a/src/driving_distance/driving_distance_withPoints_driver.cpp +++ b/src/driving_distance/driving_distance_withPoints_driver.cpp @@ -72,7 +72,7 @@ pgr_do_withPointsDD( using pgrouting::utilities::get_combinations; using pgrouting::pgget::get_points; using pgrouting::pgget::get_edges; - using pgrouting::algorithm::drivingDistance; + using pgrouting::functions::drivingDistance; std::ostringstream log; std::ostringstream notice; diff --git a/src/ordering/ordering_process.cpp b/src/ordering/ordering_process.cpp index 2d7b11f28f..22e2190352 100644 --- a/src/ordering/ordering_process.cpp +++ b/src/ordering/ordering_process.cpp @@ -71,7 +71,7 @@ void pgr_process_ordering( time_msg(name.c_str(), start_t, clock()); if (!err.str().empty() && (*result_tuples)) { - if (*result_tuples) pfree(*result_tuples); + pfree(*result_tuples); (*result_tuples) = nullptr; (*result_count) = 0; } diff --git a/src/spanningTree/CMakeLists.txt b/src/spanningTree/CMakeLists.txt index 001a86e874..bce63d4ad1 100644 --- a/src/spanningTree/CMakeLists.txt +++ b/src/spanningTree/CMakeLists.txt @@ -3,12 +3,12 @@ # License: GPL-2 See https://github.com/pgRouting/pgrouting/blob/main/LICENSE ADD_LIBRARY(spanningTree OBJECT - mst_common.cpp - details.cpp - kruskal.c - kruskal_driver.cpp - prim.c - prim_driver.cpp + + prim.cpp + kruskal.cpp + + spanningTree_process.cpp + spanningTree_driver.cpp ) diff --git a/src/spanningTree/details.cpp b/src/spanningTree/details.cpp deleted file mode 100644 index bd778de498..0000000000 --- a/src/spanningTree/details.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/*PGR-GNU***************************************************************** -File: details.cpp - -Copyright (c) 2015-2026 pgRouting developers -Mail: project@pgrouting.org - -Copyright (c) 2018 Vicky Vergara - - ------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - ********************************************************************PGR-GNU*/ - -#include "spanningTree/details.hpp" -#include -#include - -namespace pgrouting { -namespace details { - -std::vector -clean_vids(std::vector vids) { - std::sort(vids.begin(), vids.end()); - vids.erase( - std::unique(vids.begin(), vids.end()), - vids.end()); - vids.erase( - std::remove(vids.begin(), vids.end(), 0), - vids.end()); - return vids; -} - -std::vector -get_no_edge_graph_result(const std::vector &vids) { - std::vector results; - if (vids.empty()) return results; - for (auto const root : clean_vids(vids)) { - results.push_back({root, 0, root, root, -1, 0.0, 0.0}); - } - return results; -} - -} // namespace details -} // namespace pgrouting diff --git a/src/spanningTree/kruskal.c b/src/spanningTree/kruskal.c index 5c8e2ea3f1..e59fbad1ba 100644 --- a/src/spanningTree/kruskal.c +++ b/src/spanningTree/kruskal.c @@ -29,75 +29,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #include #include "c_common/postgres_connection.h" -#include "utils/array.h" -#include "c_common/debug_macro.h" -#include "c_common/e_report.h" -#include "c_common/time_msg.h" #include "c_types/mst_rt.h" -#include "drivers/spanningTree/mst_common.h" -#include "drivers/spanningTree/kruskal_driver.h" +#include "process/spanningTree_process.h" PGDLLEXPORT Datum _pgr_kruskalv4(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(_pgr_kruskalv4); -static -void -process( - char* edges_sql, - ArrayType *roots, - char * fn_suffix, - int64_t max_depth, - double distance, - - MST_rt **result_tuples, - size_t *result_count) { - pgr_SPI_connect(); - char* log_msg = NULL; - char* notice_msg = NULL; - char* err_msg = NULL; - (*result_tuples) = NULL; - (*result_count) = 0; - - - char * fn_name = get_name(0, fn_suffix, &err_msg); - if (err_msg) { - pgr_global_report(&log_msg, ¬ice_msg, &err_msg); - return; - } - - /* checks previously done on sql now done here */ - if (strcmp(fn_suffix, "DD") == 0 && distance < 0) { - pgr_throw_error("Negative value found on 'distance'", "Must be positive"); - } else if ((strcmp(fn_suffix, "BFS") == 0 || strcmp(fn_suffix, "DFS") == 0) && max_depth < 0) { - pgr_throw_error("Negative value found on 'max_depth'", "Must be positive"); - } - - clock_t start_t = clock(); - pgr_do_kruskal( - edges_sql, - roots, - fn_suffix, - max_depth, - distance, - - result_tuples, - result_count, - &log_msg, - ¬ice_msg, - &err_msg); - time_msg(fn_name, start_t, clock()); - - if (err_msg) { - if (*result_tuples) pfree(*result_tuples); - } - pgr_global_report(&log_msg, ¬ice_msg, &err_msg); - - pgr_SPI_finish(); -} - PGDLLEXPORT Datum _pgr_kruskalv4(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; TupleDesc tuple_desc; @@ -110,12 +50,17 @@ PGDLLEXPORT Datum _pgr_kruskalv4(PG_FUNCTION_ARGS) { funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - process( + pgr_process_spanningTree( text_to_cstring(PG_GETARG_TEXT_P(0)), PG_GETARG_ARRAYTYPE_P(1), - text_to_cstring(PG_GETARG_TEXT_P(2)), + + false, PG_GETARG_INT64(3), PG_GETARG_FLOAT8(4), + false, + + text_to_cstring(PG_GETARG_TEXT_P(2)), + KRUSKAL, &result_tuples, &result_count); @@ -143,7 +88,7 @@ PGDLLEXPORT Datum _pgr_kruskalv4(PG_FUNCTION_ARGS) { Datum *values; bool* nulls; - size_t num = 8; + size_t num = 8; values = palloc(num * sizeof(Datum)); nulls = palloc(num * sizeof(bool)); @@ -201,13 +146,17 @@ PGDLLEXPORT Datum _pgr_kruskal(PG_FUNCTION_ARGS) { errhint("Consider upgrade pgRouting"))); #endif - /* Edge sql, tree roots, fn_suffix, max_depth, distance */ - process( + pgr_process_spanningTree( text_to_cstring(PG_GETARG_TEXT_P(0)), PG_GETARG_ARRAYTYPE_P(1), - text_to_cstring(PG_GETARG_TEXT_P(2)), + + false, PG_GETARG_INT64(3), PG_GETARG_FLOAT8(4), + false, + + text_to_cstring(PG_GETARG_TEXT_P(2)), + KRUSKAL, &result_tuples, &result_count); diff --git a/src/spanningTree/kruskal.cpp b/src/spanningTree/kruskal.cpp new file mode 100644 index 0000000000..c8fd45fb67 --- /dev/null +++ b/src/spanningTree/kruskal.cpp @@ -0,0 +1,63 @@ +/*PGR-GNU***************************************************************** +File: kruskal.cpp + +Copyright (c) 2026-2026 pgRouting developers +Mail: project@pgrouting.org + +Function's developer: +Copyright (c) 2018 Aditya Pratap Singh +Mail: adityapratap.singh28@gmail.com + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +#include "spanningTree/kruskal.hpp" + +#include +#include + +namespace pgrouting { +namespace functions { + +using Kruskal = pgrouting::algorithms::Pgr_kruskal; + +std::vector +kruskal(pgrouting::UndirectedGraph &graph) { + Kruskal kruskalfn; + return kruskalfn.kruskal(graph); +} + +std::vector +kruskalBFS(pgrouting::UndirectedGraph &graph, const std::set& roots, int64_t max_depth) { + Kruskal kruskalfn; + return kruskalfn.kruskalBFS(graph, roots, max_depth); +} +std::vector +kruskalDFS(pgrouting::UndirectedGraph &graph, const std::set& roots, int64_t max_depth) { + Kruskal kruskalfn; + return kruskalfn.kruskalDFS(graph, roots, max_depth); +} + +std::vector +kruskalDD(pgrouting::UndirectedGraph &graph, const std::set& roots, double distance) { + Kruskal kruskalfn; + return kruskalfn.kruskalDD(graph, roots, distance); +} + +} // namespace functions +} // namespace pgrouting diff --git a/src/spanningTree/kruskal_driver.cpp b/src/spanningTree/kruskal_driver.cpp deleted file mode 100644 index c26b41db58..0000000000 --- a/src/spanningTree/kruskal_driver.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/*PGR-GNU***************************************************************** -File: kruskal_driver.cpp - -Generated with Template by: -Copyright (c) 2015-2026 pgRouting developers -Mail: project@pgrouting.org - -Function's developer: -Copyright (c) 2018 Aditya Pratap Singh -Mail: adityapratap.singh28@gmail.com ------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - ********************************************************************PGR-GNU*/ - -#include "drivers/spanningTree/kruskal_driver.h" - -#include -#include -#include -#include - -#include "cpp_common/pgdata_getters.hpp" -#include "cpp_common/alloc.hpp" -#include "cpp_common/assert.hpp" - -#include "spanningTree/kruskal.hpp" -#include "spanningTree/details.hpp" - - -void -pgr_do_kruskal( - const char *edges_sql, - ArrayType* starts, - - const char *fn_suffix, - - int64_t max_depth, - double distance, - - MST_rt **return_tuples, - size_t *return_count, - - char ** log_msg, - char ** notice_msg, - char ** err_msg) { - using pgrouting::pgr_alloc; - using pgrouting::to_pg_msg; - using pgrouting::pgr_free; - using pgrouting::pgget::get_intArray; - - std::ostringstream log; - std::ostringstream err; - std::ostringstream notice; - const char *hint = nullptr; - - try { - // NOLINTBEGIN(clang-analyzer-cplusplus.NewDelete) - pgassert(!(*log_msg)); - pgassert(!(*notice_msg)); - pgassert(!(*err_msg)); - pgassert(!(*return_tuples)); - pgassert(*return_count == 0); - // NOLINTEND(clang-analyzer-cplusplus.NewDelete) - - auto roots = get_intArray(starts, false); - - hint = edges_sql; - auto edges = pgrouting::pgget::get_edges(std::string(edges_sql), true, false); - hint = nullptr; - - std::string suffix(fn_suffix); - - std::vector results; - - pgrouting::UndirectedGraph undigraph; - undigraph.insert_edges(edges); - pgrouting::functions::Pgr_kruskal kruskal; - - if (edges.empty()) { - results = pgrouting::details::get_no_edge_graph_result(roots); - *notice_msg = to_pg_msg("No edges found"); - *log_msg = to_pg_msg(edges_sql); - } else { - if (suffix == "") { - results = kruskal.kruskal(undigraph); - } else if (suffix == "BFS") { - results = kruskal.kruskalBFS(undigraph, roots, max_depth); - } else if (suffix == "DFS") { - results = kruskal.kruskalDFS(undigraph, roots, max_depth); - } else if (suffix == "DD") { - results = kruskal.kruskalDD(undigraph, roots, distance); - } else { - err << "Unknown Kruskal function"; - *err_msg = to_pg_msg(err); - return; - } - } - - auto count = results.size(); - - (*return_tuples) = pgr_alloc(count, (*return_tuples)); - for (size_t i = 0; i < count; i++) { - *((*return_tuples) + i) = results[i]; - } - (*return_count) = count; - - pgassert(*err_msg == NULL); - *log_msg = to_pg_msg(log); - *notice_msg = to_pg_msg(notice); - } catch (AssertFailedException &except) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << except.what(); - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } catch (const std::string &ex) { - *err_msg = to_pg_msg(ex); - *log_msg = hint? to_pg_msg(hint) : to_pg_msg(log); - } catch (std::exception &except) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << except.what(); - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } catch(...) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << "Caught unknown exception!"; - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } -} diff --git a/src/spanningTree/mst_common.cpp b/src/spanningTree/mst_common.cpp deleted file mode 100644 index d2d94e6636..0000000000 --- a/src/spanningTree/mst_common.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/*PGR-GNU***************************************************************** -File: mst_common.cpp - -Copyright (c) 2018-2026 pgRouting developers -Mail: project@pgrouting.org - -Copyright (c) 2018 Vicky Vergara -Mail: vicky at georepublic dot de - ------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - ********************************************************************PGR-GNU*/ - -#include "drivers/spanningTree/mst_common.h" - -#include -#include - -#include "cpp_common/assert.hpp" -#include "cpp_common/alloc.hpp" - -int -get_order(char * fn_suffix, char ** err_msg) { - using pgrouting::to_pg_msg; - std::ostringstream err; - try { - pgassert(!(*err_msg)); - std::string suffix(fn_suffix); - if (suffix.empty()) return 0; - if (suffix == "DFS") return 1; - if (suffix == "BFS") return 2; - if (suffix == "DD") return 1; - err << "Unknown function suffix" << suffix; - *err_msg = to_pg_msg(err); - } catch (std::exception &except) { - err << except.what(); - *err_msg = to_pg_msg(err); - } - return -1; -} - - -char * -get_name(int fn_id, char * fn_suffix, char ** err_msg) { - using pgrouting::to_pg_msg; - std::ostringstream err; - try { - pgassert(!(*err_msg)); - std::string name; - switch (fn_id) { - case 0: name = "pgr_kruskal"; - break; - case 1: name = "pgr_prim"; - break; - default : name = "unknown"; - err << "Unknown function name"; - *err_msg = to_pg_msg(err); - } - std::string suffix(fn_suffix); - name += suffix; - char * full_name = to_pg_msg(name.c_str()); - return full_name; - } catch (std::exception &except) { - err << except.what(); - *err_msg = to_pg_msg(err); - } - return nullptr; -} diff --git a/src/spanningTree/prim.c b/src/spanningTree/prim.c index 531bade939..01905fc7ad 100644 --- a/src/spanningTree/prim.c +++ b/src/spanningTree/prim.c @@ -8,6 +8,7 @@ Mail: project@pgrouting.org Function's developer: Copyright (c) 2018 Aditya Pratap Singh Mail: adityapratap.singh28@gmail.com + ------ This program is free software; you can redistribute it and/or modify @@ -27,75 +28,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include + #include "c_common/postgres_connection.h" -#include "c_common/debug_macro.h" -#include "c_common/e_report.h" -#include "c_common/time_msg.h" #include "c_types/mst_rt.h" -#include "drivers/spanningTree/mst_common.h" -#include "drivers/spanningTree/prim_driver.h" +#include "process/spanningTree_process.h" PGDLLEXPORT Datum _pgr_primv4(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(_pgr_primv4); -static -void -process( - char *edges_sql, - ArrayType *roots, - char * fn_suffix, - int64_t max_depth, - double distance, - - MST_rt **result_tuples, - size_t *result_count) { - pgr_SPI_connect(); - char* log_msg = NULL; - char* notice_msg = NULL; - char* err_msg = NULL; - - - char * fn_name = get_name(1, fn_suffix, &err_msg); - if (err_msg) { - pgr_global_report(&log_msg, ¬ice_msg, &err_msg); - return; - } - - if (strcmp(fn_suffix, "DD") == 0 && distance < 0) { - pgr_throw_error("Negative value found on 'distance'", "Must be positive"); - } else if ((strcmp(fn_suffix, "BFS") == 0 || strcmp(fn_suffix, "DFS") == 0) && max_depth < 0) { - pgr_throw_error("Negative value found on 'max_depth'", "Must be positive"); - } - - clock_t start_t = clock(); - pgr_do_prim( - edges_sql, - roots, - - fn_suffix, - - max_depth, - distance, - - result_tuples, - result_count, - &log_msg, - ¬ice_msg, - &err_msg); - - - time_msg(fn_name, start_t, clock()); - - if (err_msg) { - if (*result_tuples) pfree(*result_tuples); - } - pgr_global_report(&log_msg, ¬ice_msg, &err_msg); - - pgr_SPI_finish(); -} - PGDLLEXPORT Datum _pgr_primv4(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; @@ -109,17 +51,20 @@ PGDLLEXPORT Datum _pgr_primv4(PG_FUNCTION_ARGS) { funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - /* Edge sql, tree roots, fn_suffix, max_depth, distance */ - process( + pgr_process_spanningTree( text_to_cstring(PG_GETARG_TEXT_P(0)), PG_GETARG_ARRAYTYPE_P(1), - text_to_cstring(PG_GETARG_TEXT_P(2)), + + false, PG_GETARG_INT64(3), PG_GETARG_FLOAT8(4), + false, + + text_to_cstring(PG_GETARG_TEXT_P(2)), + PRIM, &result_tuples, &result_count); - funcctx->max_calls = result_count; funcctx->user_fctx = result_tuples; if (get_call_result_type(fcinfo, NULL, &tuple_desc) @@ -144,7 +89,7 @@ PGDLLEXPORT Datum _pgr_primv4(PG_FUNCTION_ARGS) { Datum *values; bool* nulls; - size_t num = 8; + size_t num = 8; values = palloc(num * sizeof(Datum)); nulls = palloc(num * sizeof(bool)); @@ -202,16 +147,20 @@ PGDLLEXPORT Datum _pgr_prim(PG_FUNCTION_ARGS) { errhint("Consider upgrade pgRouting"))); #endif - process( + pgr_process_spanningTree( text_to_cstring(PG_GETARG_TEXT_P(0)), PG_GETARG_ARRAYTYPE_P(1), - text_to_cstring(PG_GETARG_TEXT_P(2)), + + false, PG_GETARG_INT64(3), PG_GETARG_FLOAT8(4), + false, + + text_to_cstring(PG_GETARG_TEXT_P(2)), + PRIM, &result_tuples, &result_count); - funcctx->max_calls = result_count; funcctx->user_fctx = result_tuples; if (get_call_result_type(fcinfo, NULL, &tuple_desc) diff --git a/include/drivers/spanningTree/kruskal_driver.h b/src/spanningTree/prim.cpp similarity index 50% rename from include/drivers/spanningTree/kruskal_driver.h rename to src/spanningTree/prim.cpp index 0aff517181..40ad2d4d2a 100644 --- a/include/drivers/spanningTree/kruskal_driver.h +++ b/src/spanningTree/prim.cpp @@ -1,8 +1,7 @@ /*PGR-GNU***************************************************************** -File: kruskal_driver.h +File: prim.cpp -Generated with Template by: -Copyright (c) 2007-2026 pgRouting developers +Copyright (c) 2026-2026 pgRouting developers Mail: project@pgrouting.org Function's developer: @@ -27,48 +26,38 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ -#ifndef INCLUDE_DRIVERS_SPANNINGTREE_KRUSKAL_DRIVER_H_ -#define INCLUDE_DRIVERS_SPANNINGTREE_KRUSKAL_DRIVER_H_ -#pragma once +#include "spanningTree/prim.hpp" -#ifdef __cplusplus -extern "C" { -#endif +#include +#include -#include -#include +namespace pgrouting { +namespace functions { -#ifdef __cplusplus -} -#endif - -#include "cpp_common/undefPostgresDefine.hpp" - -#ifdef __cplusplus -# include -# include -using MST_rt = struct MST_rt; -#else -# include -# include -typedef struct MST_rt MST_rt; -#endif - -#ifdef __cplusplus -extern "C" { -#endif +using Prim = pgrouting::algorithms::Pgr_prim; -void pgr_do_kruskal( - const char*, - ArrayType*, - - const char*, int64_t, double, +std::vector +prim(pgrouting::UndirectedGraph &graph) { + Prim primfn; + return primfn.prim(graph); +} - MST_rt**, size_t*, - char**, char**, char**); +std::vector +primBFS(pgrouting::UndirectedGraph &graph, const std::set& roots, int64_t max_depth) { + Prim primfn; + return primfn.primBFS(graph, roots, max_depth); +} +std::vector +primDFS(pgrouting::UndirectedGraph &graph, const std::set& roots, int64_t max_depth) { + Prim primfn; + return primfn.primDFS(graph, roots, max_depth); +} -#ifdef __cplusplus +std::vector +primDD(pgrouting::UndirectedGraph &graph, const std::set& roots, double distance) { + Prim primfn; + return primfn.primDD(graph, roots, distance); } -#endif -#endif // INCLUDE_DRIVERS_SPANNINGTREE_KRUSKAL_DRIVER_H_ +} // namespace functions +} // namespace pgrouting diff --git a/src/spanningTree/prim_driver.cpp b/src/spanningTree/prim_driver.cpp deleted file mode 100644 index df9cb58a63..0000000000 --- a/src/spanningTree/prim_driver.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/*PGR-GNU***************************************************************** -File: prim_driver.cpp - -Generated with Template by: -Copyright (c) 2015-2026 pgRouting developers -Mail: project@pgrouting.org - -Function's developer: -Copyright (c) 2018 Aditya Pratap Singh -Mail: adityapratap.singh28@gmail.com ------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - ********************************************************************PGR-GNU*/ - -#include "drivers/spanningTree/prim_driver.h" - -#include -#include -#include -#include - -#include "cpp_common/pgdata_getters.hpp" -#include "cpp_common/alloc.hpp" -#include "cpp_common/assert.hpp" - -#include "c_types/mst_rt.h" - -#include "spanningTree/prim.hpp" -#include "spanningTree/details.hpp" - - -void -pgr_do_prim( - const char *edges_sql, - ArrayType* starts, - - const char *fn_suffix, - - int64_t max_depth, - double distance, - - MST_rt **return_tuples, - size_t *return_count, - - char ** log_msg, - char ** notice_msg, - char ** err_msg) { - using pgrouting::pgr_alloc; - using pgrouting::to_pg_msg; - using pgrouting::pgr_free; - using pgrouting::pgget::get_intArray; - - std::ostringstream log; - std::ostringstream err; - std::ostringstream notice; - const char *hint = nullptr; - - try { - // NOLINTBEGIN(clang-analyzer-cplusplus.NewDelete) - pgassert(!(*log_msg)); - pgassert(!(*notice_msg)); - pgassert(!(*err_msg)); - pgassert(!(*return_tuples)); - pgassert(*return_count == 0); - // NOLINTEND(clang-analyzer-cplusplus.NewDelete) - - auto roots = get_intArray(starts, false); - - hint = edges_sql; - auto edges = pgrouting::pgget::get_edges(std::string(edges_sql), true, false); - hint = nullptr; - - std::string suffix(fn_suffix); - - std::vector results; - - pgrouting::UndirectedGraph undigraph; - undigraph.insert_min_edges_no_parallel(edges); - pgrouting::functions::Pgr_prim prim; - - if (edges.empty()) { - results = pgrouting::details::get_no_edge_graph_result(roots); - *notice_msg = to_pg_msg("No edges found"); - *log_msg = to_pg_msg(edges_sql); - } else { - if (suffix == "") { - results = prim.prim(undigraph); - } else if (suffix == "BFS") { - results = prim.primBFS(undigraph, roots, max_depth); - } else if (suffix == "DFS") { - results = prim.primDFS(undigraph, roots, max_depth); - } else if (suffix == "DD") { - results = prim.primDD(undigraph, roots, distance); - } else { - err << "Unknown Prim function"; - *err_msg = to_pg_msg(err); - return; - } - } - - auto count = results.size(); - - (*return_tuples) = pgr_alloc(count, (*return_tuples)); - for (size_t i = 0; i < count; i++) { - *((*return_tuples) + i) = results[i]; - } - (*return_count) = count; - - pgassert(*err_msg == NULL); - *log_msg = to_pg_msg(log); - *notice_msg = to_pg_msg(notice); - } catch (AssertFailedException &except) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << except.what(); - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } catch (const std::string &ex) { - *err_msg = to_pg_msg(ex); - *log_msg = hint? to_pg_msg(hint) : to_pg_msg(log); - } catch (std::exception &except) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << except.what(); - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } catch(...) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << "Caught unknown exception!"; - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } -} diff --git a/src/spanningTree/spanningTree_driver.cpp b/src/spanningTree/spanningTree_driver.cpp new file mode 100644 index 0000000000..202ad4a54a --- /dev/null +++ b/src/spanningTree/spanningTree_driver.cpp @@ -0,0 +1,259 @@ +/*PGR-GNU***************************************************************** +File: spanningTree_driver.cpp + +Copyright (c) 2026-2026 pgRouting developers +Mail: project@pgrouting.org + +Design of one process & driver file by +Copyright (c) 2026 Celia Virginia Vergara Castillo +Mail: vicky at erosion.dev + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +#include "drivers/spanningTree_driver.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "c_types/mst_rt.h" +#include "cpp_common/pgdata_getters.hpp" +#include "cpp_common/utilities.hpp" +#include "cpp_common/to_postgres.hpp" + +#include "spanningTree/prim.hpp" +#include "spanningTree/kruskal.hpp" +#include "breadthFirstSearch/breadthFirstSearch.hpp" +#include "traversal/depthFirstSearch.hpp" +#include "dijkstra/drivingDist.hpp" + +namespace { + +template +std::vector +process( + const std::vector &edges, + const std::set &roots, + bool directed, + int64_t max_depth, + Func name) { + G graph; + graph.insert_edges(edges); + return name(graph, roots, directed, max_depth); +} + +template +std::vector +process( + const std::vector &edges, + const std::set &roots, + int64_t max_depth, + Func name) { + using pgrouting::UndirectedGraph; + UndirectedGraph graph; + graph.insert_edges(edges); + return name(graph, roots, max_depth); +} + +template +std::deque +process( + const std::vector &edges, + const std::set &roots, + double distance, + bool equicost, + std::vector> &depths, + Func name) { + G graph; + graph.insert_edges(edges); + return name(graph, roots, distance, equicost, depths, true); +} + +template +std::vector +process( + const std::vector &edges, + const std::set &roots, + double distance, + Func name) { + using pgrouting::UndirectedGraph; + UndirectedGraph graph; + graph.insert_edges(edges); + return name(graph, roots, distance); +} + +template +std::vector +process( + const std::vector &edges, + const Func name) { + using pgrouting::UndirectedGraph; + UndirectedGraph graph; + graph.insert_edges(edges); + return name(graph); +} + +} // namespace + +namespace pgrouting { +namespace drivers { + +void +do_spanningTree( + const std::string &edges_sql, + ArrayType* starts, + + bool directed, + int64_t max_depth, + double distance, + bool equiCost, + + Which which, + MST_rt* &return_tuples, size_t &return_count, + std::ostringstream &log, + std::ostringstream ¬ice, + std::ostringstream &err) { + std::string hint = ""; + + try { + if (edges_sql.empty()) { + err << "Empty edges SQL"; + return; + } + + using pgget::get_edges; + using pgget::get_intSet; + using to_postgres::get_tuples; + using pgrouting::Path; + using pgrouting::UndirectedGraph; + using pgrouting::DirectedGraph; + + using functions::depthFirstSearch; + using functions::breadthFirstSearch; + using functions::drivingDistance; + + bool normal = true; + auto roots = get_intSet(starts); + + std::vector edges; + + hint = edges_sql; + edges = get_edges(edges_sql, normal, false); + hint = ""; + + if (edges.empty()) { + notice << "No edges found"; + log << edges_sql; + + /* TODO(later) standardize BFS */ + if (which == BFS) return; + + auto emptyresults = pgrouting::only_root_result(roots); + return_count = get_tuples(emptyresults, return_tuples); + return; + } + + std::vector results; + std::deque paths; + std::vector> depths; + + if (directed) { + switch (which) { + case DFS: + results = process(edges, roots, directed, max_depth, + &depthFirstSearch); + break; + case BFS: + results = process(edges, roots, directed, max_depth, + &breadthFirstSearch); + break; + case DIJKSTRADD: + paths = process(edges, roots, distance, equiCost, depths, + &drivingDistance); + break; + default: + err << "Unknown function on directed graph " << get_name(which); + return; + } + } else { + switch (which) { + case KRUSKAL: + results = process(edges, &pgrouting::functions::kruskal); + break; + case KRUSKALBFS: + results = process(edges, roots, max_depth, &pgrouting::functions::kruskalBFS); + break; + case KRUSKALDFS: + results = process(edges, roots, max_depth, &pgrouting::functions::kruskalDFS); + break; + case KRUSKALDD: + results = process(edges, roots, distance, &pgrouting::functions::kruskalDD); + break; + case PRIM: + results = process(edges, &pgrouting::functions::prim); + break; + case PRIMBFS: + results = process(edges, roots, max_depth, &pgrouting::functions::primBFS); + break; + case PRIMDFS: + results = process(edges, roots, max_depth, &pgrouting::functions::primDFS); + break; + case PRIMDD: + results = process(edges, roots, distance, &pgrouting::functions::primDD); + break; + case DFS: + results = process(edges, roots, directed, max_depth, + &depthFirstSearch); + break; + case BFS: + results = process(edges, roots, directed, max_depth, + &breadthFirstSearch); + break; + case DIJKSTRADD: + paths = process(edges, roots, distance, equiCost, depths, + &drivingDistance); + break; + default: + err << "Unknown function on undirected graph " << get_name(which); + return; + } + } + + return_count = get_tuples(results, paths, depths, return_tuples); + } catch (AssertFailedException &except) { + err << except.what(); + } catch (const std::pair& ex) { + err << ex.first; + log << ex.second; + } catch (const std::string &ex) { + err << ex; + log << hint; + } catch (std::exception &except) { + err << except.what(); + } catch (...) { + err << "Caught unknown exception!"; + } +} + +} // namespace drivers +} // namespace pgrouting diff --git a/src/spanningTree/spanningTree_process.cpp b/src/spanningTree/spanningTree_process.cpp new file mode 100644 index 0000000000..b085966950 --- /dev/null +++ b/src/spanningTree/spanningTree_process.cpp @@ -0,0 +1,145 @@ +/*PGR-GNU***************************************************************** +File: spanningTree_process.cpp + +Copyright (c) 2026-2026 pgRouting developers +Mail: project@pgrouting.org + +Design of one process & driver file by +Copyright (c) 2026 Celia Virginia Vergara Castillo +Mail: vicky at erosion.dev + +------ + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + ********************************************************************PGR-GNU*/ + +#include "process/spanningTree_process.h" + +extern "C" { +#include "c_common/postgres_connection.h" +#include "c_common/e_report.h" +#include "c_common/time_msg.h" +} + +#include +#include + +#include "c_types/mst_rt.h" +#include "cpp_common/report_messages.hpp" +#include "cpp_common/utilities.hpp" +#include "cpp_common/assert.hpp" +#include "cpp_common/alloc.hpp" + +#include "drivers/spanningTree_driver.hpp" + +void pgr_process_spanningTree( + const char* edges_sql, + ArrayType* roots, + + bool directed, + int64_t max_depth, + double distance, + bool equicost, + + const char* fn_suffix, + enum Which which, + MST_rt **result_tuples, size_t *result_count) { + pgassert(edges_sql); + pgassert(!(*result_tuples)); + pgassert(*result_count == 0); + + std::string suffix = fn_suffix? std::string(fn_suffix) : ""; + int val = 0; + + switch (which) { + case PRIM: + case KRUSKAL: + if (suffix == "DD") { + val = 1; + } else if (suffix == "DFS") { + val = 2; + } else if (suffix == "BFS") { + val = 3; + } + + which = static_cast(static_cast(which) + val); + + switch (val) { + case 1: + if (distance < 0) { + pgr_throw_error("Negative value found on 'distance'", "Must be non negative"); + } + break; + case 2: + case 3: + if (max_depth < 0) { + pgr_throw_error("Negative value found on 'max_depth'", "Must be non negative"); + } + break; + default: + break; + } + break; + + case BFS: + case DFS: + if (max_depth < 0) { + pgr_throw_error("Negative value found on 'max_depth'", "Must be non negative"); + } + break; + + case DIJKSTRADD: + if (distance < 0) { + pgr_throw_error("Negative value found on 'distance'", "Must be positive"); + } + break; + + default: + break; + } + + pgr_SPI_connect(); + + std::ostringstream log; + std::ostringstream err; + std::ostringstream notice; + + clock_t start_t = clock(); + pgrouting::drivers::do_spanningTree( + edges_sql? edges_sql : "", + roots, + + directed, + max_depth, + distance, + equicost, + + which, + (*result_tuples), (*result_count), + log, notice, err); + + auto name = std::string(" processing ") + pgrouting::get_name(which); + time_msg(name.c_str(), start_t, clock()); + + if (!err.str().empty() && (*result_tuples)) { + pfree(*result_tuples); + (*result_tuples) = nullptr; + (*result_count) = 0; + } + + pgrouting::report_messages(log, notice, err); + pgr_SPI_finish(); +} diff --git a/src/traversal/CMakeLists.txt b/src/traversal/CMakeLists.txt index ffc597c779..03da45ad75 100644 --- a/src/traversal/CMakeLists.txt +++ b/src/traversal/CMakeLists.txt @@ -1,7 +1,7 @@ # This file is part of the pgRouting project. # Copyright (c) 2020-2026 pgRouting developers # License: GPL-2 See https://github.com/pgRouting/pgrouting/blob/main/LICENSE + ADD_LIBRARY(traversal OBJECT depthFirstSearch.c - depthFirstSearch_driver.cpp - ) +) diff --git a/src/traversal/depthFirstSearch.c b/src/traversal/depthFirstSearch.c index c7353863d4..d7ecbe2387 100644 --- a/src/traversal/depthFirstSearch.c +++ b/src/traversal/depthFirstSearch.c @@ -8,6 +8,7 @@ Mail: project@pgrouting.org Function's developer: Copyright (c) 2020 Ashish Kumar Mail: ashishkr23438 at gmail.com + ------ This program is free software; you can redistribute it and/or modify @@ -27,61 +28,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ********************************************************************PGR-GNU*/ #include + #include "c_common/postgres_connection.h" -#include "c_common/debug_macro.h" -#include "c_common/e_report.h" -#include "c_common/time_msg.h" #include "c_types/mst_rt.h" -#include "drivers/traversal/depthFirstSearch_driver.h" +#include "process/spanningTree_process.h" PGDLLEXPORT Datum _pgr_depthfirstsearch(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(_pgr_depthfirstsearch); -static -void -process( - char* edges_sql, - ArrayType *roots, - bool directed, - int64_t max_depth, - - MST_rt **result_tuples, - size_t *result_count) { - if (max_depth < 0) pgr_throw_error("Negative value found on 'max_depth'", ""); - pgr_SPI_connect(); - char* log_msg = NULL; - char* notice_msg = NULL; - char* err_msg = NULL; - (*result_tuples) = NULL; - (*result_count) = 0; - - clock_t start_t = clock(); - pgr_do_depthFirstSearch( - edges_sql, - roots, - - directed, - max_depth, - - result_tuples, - result_count, - &log_msg, - ¬ice_msg, - &err_msg); - time_msg("processing pgr_depthFirstSearch", start_t, clock()); - - if (err_msg && (*result_tuples)) { - pfree(*result_tuples); - (*result_tuples) = NULL; - (*result_count) = 0; - } - - pgr_global_report(&log_msg, ¬ice_msg, &err_msg); - - pgr_SPI_finish(); -} PGDLLEXPORT Datum _pgr_depthfirstsearch(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; @@ -95,11 +51,17 @@ PGDLLEXPORT Datum _pgr_depthfirstsearch(PG_FUNCTION_ARGS) { funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - process( + pgr_process_spanningTree( text_to_cstring(PG_GETARG_TEXT_P(0)), PG_GETARG_ARRAYTYPE_P(1), + PG_GETARG_BOOL(2), PG_GETARG_INT64(3), + 0, + false, + + NULL, + DFS, &result_tuples, &result_count); @@ -146,7 +108,6 @@ PGDLLEXPORT Datum _pgr_depthfirstsearch(PG_FUNCTION_ARGS) { values[6] = Float8GetDatum(result_tuples[funcctx->call_cntr].agg_cost); values[7] = Int64GetDatum(result_tuples[funcctx->call_cntr].pred); - tuple = heap_form_tuple(tuple_desc, values, nulls); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); diff --git a/src/traversal/depthFirstSearch_driver.cpp b/src/traversal/depthFirstSearch_driver.cpp deleted file mode 100644 index b483bd4d98..0000000000 --- a/src/traversal/depthFirstSearch_driver.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/*PGR-GNU***************************************************************** -File: depthFirstSearch_driver.cpp - -Generated with Template by: -Copyright (c) 2015-2026 pgRouting developers -Mail: project@pgrouting.org - -Function's developer: -Copyright (c) 2020 Ashish Kumar -Mail: ashishkr23438@gmail.com ------- - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - - ********************************************************************PGR-GNU*/ - -#include "drivers/traversal/depthFirstSearch_driver.h" - -#include -#include -#include - -#include "cpp_common/pgdata_getters.hpp" -#include "cpp_common/alloc.hpp" -#include "cpp_common/assert.hpp" - -#include "spanningTree/details.hpp" -#include "traversal/depthFirstSearch.hpp" - -/** @file depthFirstSearch_driver.cpp - * @brief Handles actual calling of function in the `pgr_depthFirstSearch.hpp` file. - * - */ - -namespace { - -/** @brief Calls the main function defined in the C++ Header file. - * - * Also sorts the root vertices in an increasing order, - * and removes the duplicated vertices. Then calls the function - * defined in the C++ Header file - `pgr_depthFirstSearch.hpp` - * - * @param graph the graph containing the edges - * @param roots the root vertices - * @param directed whether the graph is directed or undirected - * @param max_depth the maximum depth of traversal - * - * @returns results, when results are found - */ - -template < class G > -std::vector < MST_rt > -pgr_depthFirstSearch( - G &graph, - std::vector < int64_t > roots, - bool directed, - int64_t max_depth) { - std::sort(roots.begin(), roots.end()); - roots.erase( - std::unique(roots.begin(), roots.end()), - roots.end()); - - pgrouting::functions::Pgr_depthFirstSearch < G > fn_depthFirstSearch; - auto results = fn_depthFirstSearch.depthFirstSearch(graph, roots, directed, max_depth); - return results; -} - -} // namespace - -void -pgr_do_depthFirstSearch( - const char *edges_sql, - ArrayType* starts, - - bool directed, - int64_t max_depth, - - MST_rt **return_tuples, - size_t *return_count, - - char ** log_msg, - char ** notice_msg, - char ** err_msg) { - using pgrouting::pgr_alloc; - using pgrouting::to_pg_msg; - using pgrouting::pgr_free; - using pgrouting::pgget::get_intArray; - - std::ostringstream log; - std::ostringstream err; - std::ostringstream notice; - const char *hint = nullptr; - - try { - pgassert(!(*log_msg)); - pgassert(!(*notice_msg)); - pgassert(!(*err_msg)); - pgassert(!(*return_tuples)); - pgassert(*return_count == 0); - - auto roots = get_intArray(starts, false); - std::vector results; - - hint = edges_sql; - auto edges = pgrouting::pgget::get_edges(std::string(edges_sql), true, false); - hint = nullptr; - - if (edges.empty()) { - results = pgrouting::details::get_no_edge_graph_result(roots); - *notice_msg = to_pg_msg("No edges found"); - *log_msg = to_pg_msg(edges_sql); - } else { - if (directed) { - pgrouting::DirectedGraph digraph; - digraph.insert_edges(edges); - - results = pgr_depthFirstSearch( - digraph, - roots, - directed, - max_depth); - } else { - pgrouting::UndirectedGraph undigraph; - undigraph.insert_edges(edges); - - results = pgr_depthFirstSearch( - undigraph, - roots, - directed, - max_depth); - } - } - - auto count = results.size(); - - if (count == 0) { - (*return_tuples) = NULL; - (*return_count) = 0; - notice << "No traversal found"; - *log_msg = to_pg_msg(notice); - return; - } - - (*return_tuples) = pgr_alloc(count, (*return_tuples)); - for (size_t i = 0; i < count; i++) { - *((*return_tuples) + i) = results[i]; - } - (*return_count) = count; - - pgassert(*err_msg == NULL); - *log_msg = to_pg_msg(log); - *notice_msg = to_pg_msg(notice); - } catch (AssertFailedException &except) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << except.what(); - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } catch (const std::string &ex) { - *err_msg = to_pg_msg(ex); - *log_msg = hint? to_pg_msg(hint) : to_pg_msg(log); - } catch (std::exception &except) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << except.what(); - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } catch(...) { - (*return_tuples) = pgr_free(*return_tuples); - (*return_count) = 0; - err << "Caught unknown exception!"; - *err_msg = to_pg_msg(err); - *log_msg = to_pg_msg(log); - } -} diff --git a/tools/scripts/code_checker.sh b/tools/scripts/code_checker.sh index 9e3c490cb8..3f52522119 100755 --- a/tools/scripts/code_checker.sh +++ b/tools/scripts/code_checker.sh @@ -27,6 +27,7 @@ INCLUDE_ORDER=" -build/include_order:src/metrics/metrics_process.cpp, -build/include_order:src/ordering/ordering_process.cpp, -build/include_order:src/astar/astar_process.cpp, +-build/include_order:src/spanningTree/spanningTree_process.cpp, -build/include_order:src/allpairs/allpairs_process.cpp" if test -z "$DIRECTORY"; then