diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a7e219..633df2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,3 +8,6 @@ target_include_directories (tinygraph PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) add_executable(tinygraph_test test.cpp) target_link_libraries (tinygraph_test LINK_PUBLIC tinygraph) + +add_executable(bellman_ford_test tests/bellman_ford_test.cpp) +target_link_libraries (bellman_ford_test LINK_PUBLIC tinygraph) diff --git a/data/graph.cpp b/data/graph.cpp index dd38e9f..ebf92a1 100644 --- a/data/graph.cpp +++ b/data/graph.cpp @@ -10,6 +10,7 @@ #include #include #include "graph.h" +#include namespace tinygraph { Graph::Graph() = default; @@ -66,5 +67,289 @@ namespace tinygraph { this->str(); return std::vector>(); } -} + void Graph::reset_paths() + { + distances.clear(); + parent.clear(); + } + + bool Graph::vertex_exists(const std::string& vertex) + { + for(auto& [vertex_name, vertex_ptr] : vertices) + { + if (vertex_name == vertex) + { + return true; + } + } + return false; + } + + + bool Graph::initialize_distances() { + number infinity; + for (auto& [vertex_name, vertex_ptr] : vertices) + { + for (auto& edge : vertex_ptr->connections) + { + if (edge->properties && edge->properties->find(path_property) != edge->properties->end()) + { + std::any property = edge->properties->at(path_property); + + if (property.type() == typeid(int)) + { + infinity = std::numeric_limits::max(); + } + else if (property.type() == typeid(float)) + { + infinity = std::numeric_limits::max(); + } + else if (property.type() == typeid(double)) + { + infinity = std::numeric_limits::max(); + } + else return false; + + for (auto& [vertex_name, vertex_ptr] : vertices) + { + distances[vertex_name] = infinity; + parent[vertex_name] = ""; + } + distances[source_name] = 0; + + return true; + + } + else if (edge->properties && edge->properties->find(path_property) == edge->properties->end()) + { + return false; + } + } + } + return false; + } + + Graph::number Graph::find_value(std::any& property) + { + if (property.type() == typeid(int)) + { + return std::any_cast(property); + } + else if (property.type() == typeid(float)) + { + return std::any_cast(property); + } + else if (property.type() == typeid(double)) + { + return std::any_cast(property); + } + + else return std::numeric_limits::min(); + } + + bool Graph::bellman_ford(const std::string& the_source_name, const std::string& sorting_property) + { + if (sorting_property.empty() || the_source_name.empty()) return false; + path_property = sorting_property; + source_name = the_source_name; + if (!vertex_exists(the_source_name)) + { + reset_paths(); + return false; + } + + if (!initialize_distances()) + { + reset_paths(); + return false; + } + + for (int i=0; iconnections) + { + if (edge->properties && edge->properties->find(sorting_property) != edge->properties->end()) + { + std::any property = edge->properties->at(path_property); + + number weight = find_value(property); + + if (typeid(weight) == typeid(int) && std::get(weight) == std::numeric_limits::min()) + { + reset_paths(); + return false; + } + + auto& ref_vertex_name = vertex_name; + + std::visit( + [this, &ref_vertex_name, &edge](auto& distance_from, auto& distance_to, auto& weight) { + + using type_used = std::common_type_t; + if (distance_from != std::numeric_limits::max() && distance_to > distance_from + weight) + { + this->distances[edge->to->name] = distance_from + weight; + this->parent[edge->to->name] = ref_vertex_name; + } + + }, + distances[vertex_name], + distances[edge->to->name], + weight + ); + } + + + else if (edge->properties && edge->properties->find(sorting_property) == edge->properties->end()) + { + reset_paths(); + return false; + } + } + } + } + + for (auto& [vertex_name, vertex_ptr] : vertices) + { + for (auto& edge : vertex_ptr->connections) + { + if (edge->properties && edge->properties->find(sorting_property) != edge->properties->end()) + { + std::any property = edge->properties->at(path_property); + + number weight = find_value(property); + + if (typeid(weight) == typeid(int) && std::get(weight) == std::numeric_limits::min()) + { + reset_paths(); + return false; + } + + bool returning = true; + + std::visit( + [this, &returning](auto& distance_from, auto& distance_to, auto& weight) { + using type_used = std::common_type_t; + if (distance_from != std::numeric_limits::max() && distance_to > distance_from + weight) + { + this->negative_cycle = negative; + returning = false; + } + + }, + distances[vertex_name], + distances[edge->to->name], + weight + ); + + if (!returning) + { + reset_paths(); + return false; + } + } + } + } + negative_cycle = non_negative; + return true; + } + + + std::vector Graph::find_shortest_path(const std::string& destination) + { + std::vector shortest_path; + + if (!vertex_exists(destination)) + { + return shortest_path; + } + + std::string current = destination; + + + while (current != source_name) + { + if (current.empty()) + { + shortest_path.clear(); + return shortest_path; + } + shortest_path.push_back(current); + current = parent[current]; + } + + shortest_path.push_back(current); + + std::reverse(shortest_path.begin(), shortest_path.end()); + + return shortest_path; + } + + void Graph::dfs(const std::string& current, const std::string& destination, std::map>& adj, std::map& visited, std::vector& path, bool& found) + { + visited[current] = true; + + path.push_back(current); + + if (current == destination) + { + found = true; + return; + } + + for (std::string testing : adj[current]) + { + if (!visited[testing] && !found) + { + dfs(testing, destination, adj, visited, path, found); + } + } + if (!found) path.pop_back(); + } + + bool Graph::dfsSetup(const std::string& source, const std::string& destination) + { + if (vertices.find(source) == vertices.end() || vertices.find(destination) == vertices.end()) return false; + + std::map> adj; + std::map visited; + + std::vector path; + + for (auto& [vertex_name, vertex_ptr] : vertices) + { + visited[vertex_name] = false; + } + + + + for (auto& [vertex_name, vertex_ptr] : vertices) + { + for (auto& edge : vertex_ptr->connections) + { + if (undirected) + { + adj[vertex_name].push_back(edge->to->name); + adj[edge->to->name].push_back(vertex_name); + } + else + { + adj[vertex_name].push_back(edge->to->name); + } + } + } + + bool found = false; + + dfs(source, destination, adj, visited, path, found); + + if (!found) path.clear(); + + return found; + + + } +} diff --git a/data/graph.h b/data/graph.h index 334bab6..a7d1e88 100644 --- a/data/graph.h +++ b/data/graph.h @@ -7,6 +7,7 @@ #include "types.h" #include +#include namespace tinygraph { class Graph { @@ -27,6 +28,37 @@ namespace tinygraph { std::vector> connected_components(); std::string str(); + + using number = std::variant; // more types can be added here + + std::map distances; + + std::map parent; + + std::string path_property; + + bool bellman_ford(const std::string& the_source_name, const std::string& sorting_property); + + bool vertex_exists(const std::string& vertex); + + void reset_paths(); + + std::string source_name; + + std::vector find_shortest_path(const std::string& destination); + + enum neg_cycle_state { unknown, negative, non_negative }; + + neg_cycle_state negative_cycle = unknown; + + bool initialize_distances(); + + number find_value(std::any& property); + + void dfs(const std::string& current, const std::string& destination, std::map>& adj, std::map& visited, std::vector& path); + + bool dfsSetup(const std::string& source, const std::string& destination); + }; } diff --git a/tests/bellman_ford_test.cpp b/tests/bellman_ford_test.cpp new file mode 100644 index 0000000..b3f2d21 --- /dev/null +++ b/tests/bellman_ford_test.cpp @@ -0,0 +1,300 @@ +#include "../tinygraph.h" +#include +#include +#include + +static constexpr char DISTANCE[] = "distance"; +static constexpr char NAME[] = "name"; + +void print_bellman_ford(std::unique_ptr &g) { + for (auto& [city, distance] : g->distances) { + auto& city_ref = city; + std::visit( + [&city_ref](auto distance) { + using distance_type = decltype(distance); + std::cout << "\t" << city_ref << " : " << std::flush; + if (distance == std::numeric_limits::max()) { + std::cout << "infinity" << std::endl; + } else + std::cout << distance << std::endl; + }, + distance); + } +} + +void handle_path(std::unique_ptr &g, + std::string destination) { + std::cout << "path: " << std::flush; + std::vector path = g->find_shortest_path(destination); + + for (std::string &parent : path) { + std::cout << parent << std::flush; + } + std::cout << std::endl; +} + +void handle_bellman_ford(std::unique_ptr &g, + std::string vertex_name, std::string function_name, + bool print_path = false, + std::string destination = "") { + bool value = g->bellman_ford(vertex_name, DISTANCE); + std::cout << function_name << std::endl; + if (value) + print_bellman_ford(g); + else + std::cout << "\tdidn't work" << std::endl; + + if (print_path) + handle_path(g, destination); +} + +void play_around_example() { + auto port = tinygraph::typestore_add("port"); + + auto g = std::make_unique(); + + std::shared_ptr v; + + v = g->add("A", port); + v->properties[NAME] = "dkfjdkfj"; + + v = g->add("B", port); + v->properties[NAME] = "lkja"; + + v = g->add("C", port); + v->properties[NAME] = "addjm"; + + std::shared_ptr> linkprops; + + bool unlinked = false; + + linkprops = g->link("A", "C", unlinked); + linkprops->insert({DISTANCE, 4}); + + linkprops = g->link("C", "B", unlinked); + linkprops->insert({DISTANCE, -3.2}); + + linkprops = g->link("A", "B", unlinked); + linkprops->insert({DISTANCE, 2}); + + handle_bellman_ford(g, "A", "more complex example", true, "C"); +} + +void more_complex_example() { + auto port = tinygraph::typestore_add("port"); + + auto g = std::make_unique(); + + std::shared_ptr v; + + v = g->add("A", port); + v->properties[NAME] = "dkfjdkfj"; + + v = g->add("B", port); + v->properties[NAME] = "lkja"; + + v = g->add("C", port); + v->properties[NAME] = "addjm"; + + v = g->add("D", port); + v->properties[NAME] = "kjse"; + + v = g->add("E", port); + v->properties[NAME] = "aegf"; + + std::shared_ptr> linkprops; + + bool unlinked = false; + + linkprops = g->link("A", "B", unlinked); + linkprops->insert({DISTANCE, -1}); + + linkprops = g->link("B", "C", unlinked); + linkprops->insert({DISTANCE, 3}); + + linkprops = g->link("B", "D", unlinked); + linkprops->insert({DISTANCE, 2}); + + linkprops = g->link("D", "B", unlinked); + linkprops->insert({DISTANCE, 1}); + + linkprops = g->link("B", "E", unlinked); + linkprops->insert({DISTANCE, 2}); + + linkprops = g->link("E", "D", unlinked); + linkprops->insert({DISTANCE, -3}); + + linkprops = g->link("D", "C", unlinked); + linkprops->insert({DISTANCE, 5}); + + linkprops = g->link("A", "C", unlinked); + linkprops->insert({DISTANCE, 4}); + + handle_bellman_ford(g, "A", "more complex example"); +} + +void negative_cycle_example() { + auto port = tinygraph::typestore_add("port"); + + auto g = std::make_unique(); + + std::shared_ptr v; + + v = g->add("0", port); + v->properties[NAME] = "dkfjdkfj"; + + v = g->add("1", port); + v->properties[NAME] = "lkja"; + + v = g->add("2", port); + v->properties[NAME] = "addjm"; + + v = g->add("3", port); + v->properties[NAME] = "kjse"; + + std::shared_ptr> linkprops; + + bool unlinked = false; + + linkprops = g->link("1", "0", unlinked); + linkprops->insert({DISTANCE, 4}); + + linkprops = g->link("1", "2", unlinked); + linkprops->insert({DISTANCE, -6}); + + linkprops = g->link("3", "1", unlinked); + linkprops->insert({DISTANCE, -2}); + + linkprops = g->link("2", "3", unlinked); + linkprops->insert({DISTANCE, 5}); + + handle_bellman_ford(g, "A", "negative cycles"); +} + +void double_example() { + auto port = tinygraph::typestore_add("port"); + + auto g = std::make_unique(); + + std::shared_ptr v; + + v = g->add("0", port); + v->properties[NAME] = "dkfjdkfj"; + + v = g->add("1", port); + v->properties[NAME] = "lkja"; + + v = g->add("2", port); + v->properties[NAME] = "addjm"; + + v = g->add("3", port); + v->properties[NAME] = "kjse"; + + v = g->add("4", port); + v->properties[NAME] = "aegf"; + + v = g->add("5", port); + v->properties[NAME] = "llk"; + + v = g->add("6", port); + v->properties[NAME] = "luh"; + + v = g->add("7", port); + v->properties[NAME] = "knu"; + + std::shared_ptr> linkprops; + + bool unlinked = false; + + linkprops = g->link("5", "1", unlinked); + linkprops->insert({DISTANCE, 0.32}); + + linkprops = g->link("0", "2", unlinked); + linkprops->insert({DISTANCE, 0.26}); + + linkprops = g->link("7", "3", unlinked); + linkprops->insert({DISTANCE, 0.39}); + + linkprops = g->link("0", "4", unlinked); + linkprops->insert({DISTANCE, 0.38}); + + linkprops = g->link("4", "5", unlinked); + linkprops->insert({DISTANCE, 0.35}); + + linkprops = g->link("3", "6", unlinked); + linkprops->insert({DISTANCE, 0.52}); + + linkprops = g->link("2", "7", unlinked); + linkprops->insert({DISTANCE, 0.34}); + + handle_bellman_ford(g, "0", "doubles", true, "6"); +} + +void float_example() { + auto port = tinygraph::typestore_add("port"); + + auto g = std::make_unique(); + + std::shared_ptr v; + + v = g->add("0", port); + v->properties[NAME] = "dkfjdkfj"; + + v = g->add("1", port); + v->properties[NAME] = "lkja"; + + v = g->add("2", port); + v->properties[NAME] = "addjm"; + + v = g->add("3", port); + v->properties[NAME] = "kjse"; + + v = g->add("4", port); + v->properties[NAME] = "aegf"; + + v = g->add("5", port); + v->properties[NAME] = "llk"; + + v = g->add("6", port); + v->properties[NAME] = "luh"; + + v = g->add("7", port); + v->properties[NAME] = "knu"; + + std::shared_ptr> linkprops; + + bool unlinked = false; + + linkprops = g->link("5", "1", unlinked); + linkprops->insert({DISTANCE, 0.32f}); + + linkprops = g->link("0", "2", unlinked); + linkprops->insert({DISTANCE, 0.26f}); + + linkprops = g->link("7", "3", unlinked); + linkprops->insert({DISTANCE, 0.39f}); + + linkprops = g->link("0", "4", unlinked); + linkprops->insert({DISTANCE, 0.38f}); + + linkprops = g->link("4", "5", unlinked); + linkprops->insert({DISTANCE, 0.35f}); + + linkprops = g->link("3", "6", unlinked); + linkprops->insert({DISTANCE, 0.52f}); + + linkprops = g->link("2", "7", unlinked); + linkprops->insert({DISTANCE, 0.34f}); + + handle_bellman_ford(g, "0", "floats"); +} + +int main() { + tinygraph::typestore_init(); + more_complex_example(); + float_example(); + negative_cycle_example(); + double_example(); + play_around_example(); + return 0; +}