1212#include < iostream>
1313#include < map>
1414#include < optional>
15+ #include < type_traits>
1516#include < vector>
1617
1718namespace reactor {
19+ class GraphElement {
20+ public:
21+ GraphElement () noexcept = default ;
22+ GraphElement (const GraphElement& graph) noexcept = default ;
23+ GraphElement (GraphElement&& graph) noexcept = default ;
24+
25+ virtual ~GraphElement () noexcept = default ;
26+ [[nodiscard]] virtual auto connected_to_downstream_actions () const noexcept -> bool = 0;
27+ [[nodiscard]] virtual auto connected_to_upstream_actions () const noexcept -> bool = 0;
28+ [[nodiscard]] virtual auto rating () const noexcept -> std::size_t = 0;
29+
30+ auto operator =([[maybe_unused]] const GraphElement& other) noexcept -> GraphElement& = default ;
31+ auto operator =([[maybe_unused]] GraphElement&& other) noexcept -> GraphElement& = default ;
32+ };
33+
1834// this graph is special, because to every edge properties are annotated
19- template <class E , class P > class Graph {
35+ template <class X > class Graph {
36+ // static_assert(std::is_base_of_v<GraphElement, X>);
37+ using E = X*;
38+ using P = ConnectionProperties;
39+
2040private:
21- std::map<E, std::vector<std::pair<P, E>>> graph_;
41+ using Path = std::vector<std::tuple<E, P, E>>;
42+ std::map<E, std::vector<std::pair<P, E>>> graph_{};
2243 std::set<E> nodes_{};
2344
2445 // custom compare operator this is required if u want special key values in std::map
@@ -39,8 +60,6 @@ public:
3960 : graph_(std::move(graph.graph_)) {}
4061 ~Graph () noexcept = default ;
4162
42- using Path = std::vector<std::pair<P, E>>;
43-
4463 auto operator =(const Graph other) noexcept -> Graph& {
4564 graph_ = other.graph_ ;
4665 return *this ;
@@ -55,8 +74,10 @@ public:
5574 void add_edge (E source, E destination, P properties) noexcept {
5675 nodes_.insert (source);
5776 nodes_.insert (destination);
77+
5878 if (graph_.find (source) == std::end (graph_)) {
59- Path edges{std::make_pair (properties, destination)};
79+ std::vector<std::pair<P, E>> edges;
80+ edges.emplace_back (properties, destination);
6081 graph_[source] = edges;
6182 } else {
6283 graph_[source].emplace_back (properties, destination);
@@ -95,79 +116,45 @@ public:
95116 // the return type looks a little bit cursed what is happening here ?
96117 // we have a map from the destination as a key to a list of paths through the graph.
97118 // A path here is modelled by a list of edges (with properties and the next vertex).
98- auto naive_spanning_tree (E source) noexcept -> std::vector<std::vector<std::pair< P, E>>> {
119+ auto naive_spanning_tree (E source) noexcept -> std::vector<std::vector<std::tuple<E, P, E>>> {
99120 return recursive_spanning_tree (source, std::vector<E>{});
100121 }
101122
102123 // this function goes recursively though the graph and tries to find every possible path
103124 auto recursive_spanning_tree (E source_node, std::vector<E> visited_nodes)
104- -> std::vector<std::vector<std::pair< P, E>>> {
125+ -> std::vector<std::vector<std::tuple<E, P, E>>> {
105126 std::vector<Path> paths{};
106127
107128 if (graph_[source_node].empty ()) {
108129 return std::vector<Path>{Path{}};
109130 }
110131
132+ // if this node has an action we need to append the path
133+ if (source_node->connected_to_downstream_actions ()) {
134+ paths.push_back (Path{});
135+ }
136+
111137 for (auto child : graph_[source_node]) {
112138 E current_node = child.second ;
113139
114- // means this node has not been visited yey
115- if (std::find (std::begin (visited_nodes), std::end (visited_nodes), current_node) == std::end (visited_nodes)) {
116-
117- // creating a temporary vector where the currently selected vertex is appended
118- auto temp_nodes = visited_nodes;
119- temp_nodes.push_back (current_node);
120-
121- for (auto path : recursive_spanning_tree (current_node, temp_nodes)) {
122- path.insert (std::begin (path), child);
123- paths.push_back (path);
124- }
140+ // we dont need to check for cycles because lf semantics assure that there wont be any cycles
141+ for (auto path : recursive_spanning_tree (current_node, visited_nodes)) {
142+ path.push_back (std::make_tuple (source_node, child.first , current_node));
143+ paths.push_back (path);
125144 }
126145 }
127146
128147 return paths;
129148 }
130149
131- auto shortest_path (E source, E destination) -> std::optional<Path> {
132- // TODO: maybe build proper djikstra here
133-
134- auto spanning_tre = naive_spanning_tree (source);
135- std::vector<Path> relevant_paths{};
136-
137- std::copy_if (spanning_tre.begin (), spanning_tre.end (), std::back_inserter (relevant_paths),
138- [&, destination](Path path) { return path[path.size () - 1 ].second == destination; });
139-
140- if (relevant_paths.empty ()) {
141- return std::nullopt ;
142- }
143-
144- Path best_path = *relevant_paths.begin ();
145-
146- for (auto path : relevant_paths) {
147- if (path.size () < best_path.size ()) {
148- best_path = path;
149- }
150- }
151-
152- return best_path;
153- }
154-
155150 [[nodiscard]] auto get_destinations (E source) const noexcept -> std::vector<std::pair<P, E>> {
156- return graph_[source];
157- }
158-
159- [[nodiscard]] auto get_upstream (E vertex) const noexcept -> std::optional<E> {
160- for (const auto & [source, sinks] : graph_) {
161- if (sinks.second .contains (vertex)) {
162- return source;
163- }
164- }
151+ return this ->graph_ .at (source);
165152 }
166153
167154 [[nodiscard]] auto to_mermaid () const noexcept -> std::string {
155+ std::string mermaid_string = " graph TD;\n " ;
168156 std::size_t index{0 };
169157 std::map<E, std::string> name_map{};
170- std::string mermaid_string = " graph TD;\n " ;
171158
172159 auto name_resolver = [&](E object) -> std::string {
173160 char names[] = " ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz" ; // NOLINT
@@ -188,6 +175,126 @@ public:
188175 return mermaid_string;
189176 }
190177
178+ void optimize (Graph<X>& optimized_graph) {
179+ optimized_graph.clear ();
180+
181+ static std::map<std::pair<ConnectionType, ConnectionType>, ConnectionType> construction_table = {
182+ // Normal + x
183+ {std::make_pair<ConnectionType, ConnectionType>(Normal, Normal), Normal},
184+ {std::make_pair<ConnectionType, ConnectionType>(Normal, Delayed), Delayed},
185+ {std::make_pair<ConnectionType, ConnectionType>(Normal, Enclaved), Enclaved},
186+ {std::make_pair<ConnectionType, ConnectionType>(Normal, Physical), Physical},
187+ {std::make_pair<ConnectionType, ConnectionType>(Normal, DelayedEnclaved), DelayedEnclaved},
188+ {std::make_pair<ConnectionType, ConnectionType>(Normal, PhysicalEnclaved), PhysicalEnclaved},
189+ {std::make_pair<ConnectionType, ConnectionType>(Normal, Plugin), Plugin},
190+ // Delayed + x
191+ {std::make_pair<ConnectionType, ConnectionType>(Delayed, Normal), Delayed},
192+ {std::make_pair<ConnectionType, ConnectionType>(Delayed, Delayed), Delayed},
193+ {std::make_pair<ConnectionType, ConnectionType>(Delayed, Enclaved), DelayedEnclaved},
194+ {std::make_pair<ConnectionType, ConnectionType>(Delayed, Physical), Invalid}, // !!!
195+ {std::make_pair<ConnectionType, ConnectionType>(Delayed, DelayedEnclaved), DelayedEnclaved},
196+ {std::make_pair<ConnectionType, ConnectionType>(Delayed, PhysicalEnclaved), Invalid}, // !!!
197+ {std::make_pair<ConnectionType, ConnectionType>(Delayed, Plugin), Invalid},
198+ // Enclaved + x
199+ {std::make_pair<ConnectionType, ConnectionType>(Enclaved, Normal), Enclaved},
200+ {std::make_pair<ConnectionType, ConnectionType>(Enclaved, Delayed), DelayedEnclaved},
201+ {std::make_pair<ConnectionType, ConnectionType>(Enclaved, Enclaved), Enclaved},
202+ {std::make_pair<ConnectionType, ConnectionType>(Enclaved, Physical), PhysicalEnclaved},
203+ {std::make_pair<ConnectionType, ConnectionType>(Enclaved, DelayedEnclaved), DelayedEnclaved},
204+ {std::make_pair<ConnectionType, ConnectionType>(Enclaved, PhysicalEnclaved), PhysicalEnclaved},
205+ {std::make_pair<ConnectionType, ConnectionType>(Enclaved, Plugin), Invalid},
206+ // Physical + x
207+ {std::make_pair<ConnectionType, ConnectionType>(Physical, Normal), Physical},
208+ {std::make_pair<ConnectionType, ConnectionType>(Physical, Delayed), Invalid}, // !!!
209+ {std::make_pair<ConnectionType, ConnectionType>(Physical, Enclaved), PhysicalEnclaved},
210+ {std::make_pair<ConnectionType, ConnectionType>(Physical, Physical), Physical},
211+ {std::make_pair<ConnectionType, ConnectionType>(Physical, DelayedEnclaved), Invalid}, // !!!
212+ {std::make_pair<ConnectionType, ConnectionType>(Physical, PhysicalEnclaved), PhysicalEnclaved},
213+ {std::make_pair<ConnectionType, ConnectionType>(Physical, Plugin), Invalid},
214+ // DelayedEnclaved + x
215+ {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Normal), DelayedEnclaved},
216+ {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Delayed), DelayedEnclaved},
217+ {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Enclaved), DelayedEnclaved},
218+ {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Physical), Invalid}, // !!!
219+ {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, DelayedEnclaved), DelayedEnclaved},
220+ {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, PhysicalEnclaved), Invalid}, // !!!
221+ {std::make_pair<ConnectionType, ConnectionType>(DelayedEnclaved, Plugin), Invalid},
222+ // PhysicalEnclaved + x
223+ {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Normal), PhysicalEnclaved},
224+ {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Delayed), Invalid}, // !!!
225+ {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Enclaved), PhysicalEnclaved},
226+ {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Physical), PhysicalEnclaved},
227+ {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, DelayedEnclaved), Invalid}, // !!!
228+ {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, PhysicalEnclaved), PhysicalEnclaved},
229+ {std::make_pair<ConnectionType, ConnectionType>(PhysicalEnclaved, Plugin), Invalid},
230+ // Plugin + x = Invalid
231+ {std::make_pair<ConnectionType, ConnectionType>(Plugin, Normal), Invalid}, // !!!
232+ {std::make_pair<ConnectionType, ConnectionType>(Plugin, Delayed), Invalid}, // !!!
233+ {std::make_pair<ConnectionType, ConnectionType>(Plugin, Enclaved), Invalid}, // !!!
234+ {std::make_pair<ConnectionType, ConnectionType>(Plugin, Physical), Invalid}, // !!!
235+ {std::make_pair<ConnectionType, ConnectionType>(Plugin, DelayedEnclaved), Invalid}, // !!!
236+ {std::make_pair<ConnectionType, ConnectionType>(Plugin, PhysicalEnclaved), Invalid}, // !!!
237+ {std::make_pair<ConnectionType, ConnectionType>(Plugin, Plugin), Invalid}, // !!!
238+ };
239+
240+ // getting all the sources from the graph
241+ auto keys = this ->keys ();
242+
243+ std::vector<E> has_downstreams{};
244+ std::copy_if (keys.begin (), keys.end (), std::back_inserter (has_downstreams),
245+ [](auto element) { return element->connected_to_downstream_actions (); });
246+
247+ std::vector<E> has_upstreams{};
248+ std::copy_if (keys.begin (), keys.end (), std::back_inserter (has_upstreams),
249+ [](auto element) { return element->connected_to_upstream_actions (); });
250+
251+ // generating all the possible destinations for all sources
252+ for (auto * source : has_upstreams) {
253+ auto spanning_tree = naive_spanning_tree (source);
254+
255+ for (auto & path : spanning_tree) {
256+ ConnectionProperties merged_properties{};
257+ auto * final_destination = std::get<2 >(*std::begin (path));
258+ std::size_t current_rating = 0 ;
259+
260+ for (auto edge : path) {
261+ auto property = std::get<1 >(edge);
262+ // auto source_port = std::get<0>(edge);
263+ auto destination_port = std::get<2 >(edge);
264+
265+ current_rating += destination_port->rating ();
266+
267+ if (current_rating > 0 ) {
268+ auto return_type =
269+ construction_table[std::pair<ConnectionType, ConnectionType>(merged_properties.type_ , property.type_ )];
270+ // invalid will split the connections
271+ if (return_type == Invalid) {
272+ // first add connection until this point
273+ optimized_graph.add_edge (destination_port, final_destination, merged_properties); // NOLINT
274+
275+ // resetting the properties and destination_port
276+ final_destination = destination_port;
277+ merged_properties = property;
278+
279+ } else {
280+
281+ // merging the connections
282+ merged_properties.type_ = return_type;
283+
284+ // adding up delays
285+ merged_properties.delay_ += property.delay_ ;
286+
287+ // updating target enclave if not nullptr
288+ merged_properties.enclave_ =
289+ (property.enclave_ != nullptr ) ? property.enclave_ : merged_properties.enclave_ ;
290+ }
291+ }
292+ }
293+ optimized_graph.add_edge (std::get<0 >(*(std::end (path) - 1 )), final_destination, merged_properties);
294+ }
295+ }
296+ }
297+
191298 friend auto operator <<(std::ostream& outstream, const Graph& graph) -> std::ostream& {
192299 for (auto const & [source, destinations] : graph.graph_ ) {
193300 for (auto destination : destinations) {
0 commit comments