@@ -19,6 +19,8 @@ namespace reactor {
1919template <class E , class P > class Graph {
2020private:
2121 std::map<E, std::vector<std::pair<P, E>>> graph_;
22+ std::size_t index{0 };
23+ std::map<E, std::string> name_map{};
2224
2325 // custom compare operator this is required if u want special key values in std::map
2426 // this is required for the Graph::get_edges() method
@@ -38,6 +40,8 @@ public:
3840 : graph_(std::move(graph.graph_)) {}
3941 ~Graph () noexcept = default ;
4042
43+ using Path = std::vector<std::pair<P, E>>;
44+
4145 auto operator =(const Graph other) noexcept -> Graph& {
4246 graph_ = other.graph_ ;
4347 return *this ;
@@ -51,7 +55,7 @@ public:
5155 // adds a single edge to the graph structure
5256 void add_edge (E source, E destination, P properties) noexcept {
5357 if (graph_.find (source) == std::end (graph_)) {
54- std::vector<std::pair<P, E>> edges{std::make_pair (properties, destination)};
58+ Path edges{std::make_pair (properties, destination)};
5559 graph_[source] = edges;
5660 } else {
5761 graph_[source].emplace_back (properties, destination);
@@ -85,33 +89,88 @@ public:
8589 return keys;
8690 }
8791
88- // returns the spanning tree of a given source including properties
89- [[nodiscard]] auto spanning_tree (E source) noexcept -> std::map<E, std::vector<std::pair<P, E>>> {
90- std::map<E, std::vector<std::pair<P, E>>> tree{};
91- std::vector<E> work_nodes{source};
92-
93- while (!work_nodes.empty ()) {
94- auto parent = *work_nodes.begin ();
95-
96- for (auto child : graph_[parent]) {
97- // figuring out the properties until this node
98- std::vector<std::pair<P, E>> parent_properties{};
99- if (tree.find (parent) != std::end (tree)) {
100- // this if should always be the case except the first time when tree is empty
101- parent_properties = tree[parent]; // TODO: make sure this is a copy otherwise we change this properties as
102- // well
103- }
92+ auto name_resolver (E object) -> std::string {
93+ char names[] = " ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz" ;
94+ if (name_map.find (object) == std::end (name_map)) {
95+ name_map[object] = names[index];
96+ index++;
97+ return std::string{names[index - 1 ], 1 };
98+ }
99+ return name_map[object];
100+ }
101+
102+ // the return type looks a little bit cursed what is happening here ?
103+ // we have a map from the destination as a key to a list of paths through the graph.
104+ // A path here is modelled by a list of edges (with properties and the next vertex).
105+ auto naive_spanning_tree (E source) noexcept -> std::vector<std::vector<std::pair<P, E>>> {
106+ auto result = recursive_spanning_tree (source, std::vector<E>{});
107+
108+ std::string mermaid_string = " graph TD;\n " ;
109+
110+ for (auto path : result ) {
111+ mermaid_string += std::string (" " ) + name_resolver (source);
112+ for (auto edge : path) {
113+ mermaid_string += std::string (" -->" )+ name_resolver (edge.second );
114+ }
115+ mermaid_string += std::string (" ;\n " );
116+
117+ }
118+ // std::cout << "EDGE: \n" << mermaid_string << std::endl;
119+ return result;
120+ }
121+
122+
123+ // this function goes recursively though the graph and tries to find every possible path
124+ auto recursive_spanning_tree (E source_node, std::vector<E> visited_nodes) -> std::vector<std::vector<std::pair<P, E>>> {
125+ std::vector<Path> paths{};
126+
127+ if (graph_[source_node].empty ()) {
128+ return std::vector<Path>{Path{}};
129+ }
130+
131+ for (auto child : graph_[source_node]) {
132+ E current_node = child.second ;
133+
134+ // means this node has not been visited yey
135+ if (std::find (std::begin (visited_nodes), std::end (visited_nodes), current_node) == std::end (visited_nodes)) {
104136
105- // appending the new property and inserting into the tree
106- parent_properties.push_back (child);
107- work_nodes.push_back (child.second );
108- tree[child.second ] = parent_properties;
137+ // creating a temporary vector where the currently selected vertex is appended
138+ auto temp_nodes = visited_nodes;
139+ temp_nodes.push_back (current_node);
140+
141+ for (auto path: recursive_spanning_tree (current_node, temp_nodes)) {
142+ path.insert (std::begin (path), child);
143+ paths.push_back (path);
144+ }
109145 }
146+ }
147+
148+ return paths;
149+ }
110150
111- work_nodes.erase (std::begin (work_nodes));
151+ auto shortest_path (E source, E destination) -> std::optional<Path> {
152+ // TODO: maybe build proper djikstra here
153+
154+ auto spanning_tre = naive_spanning_tree (source);
155+ std::vector<Path> relevant_paths{};
156+
157+ std::copy_if (spanning_tre.begin (), spanning_tre.end (), std::back_inserter (relevant_paths), [destination](Path path) {
158+ return (*path.end ()).second == destination;
159+ });
160+
161+ if (relevant_paths.empty ()) {
162+ return std::nullopt ;
112163 }
113164
114- return tree;
165+ Path best_path = *relevant_paths.begin ();
166+
167+ for (auto path : relevant_paths) {
168+ if (path.size () < best_path.size ()) {
169+ best_path = path;
170+ }
171+ }
172+
173+ return best_path;
115174 }
116175
117176 [[nodiscard]] auto get_destinations (E source) const noexcept -> std::vector<std::pair<P, E>> {
@@ -126,6 +185,17 @@ public:
126185 }
127186 }
128187
188+ auto to_mermaid () noexcept -> std::string {
189+ std::string mermaid_string = " graph TD;\n " ;
190+
191+ for (const auto & [source, destinations] : graph_) {
192+ for (auto dest : destinations) {
193+ mermaid_string += std::string (" " ) + name_resolver (source) + std::string (" -->" ) + name_resolver (dest.second ) + std::string (" ;\n " );
194+ }
195+ }
196+ return mermaid_string;
197+ }
198+
129199 friend auto operator <<(std::ostream& outstream, const Graph& graph) -> std::ostream& {
130200 for (auto const & [source, destinations] : graph.graph_ ) {
131201 for (auto destination : destinations) {
0 commit comments