12
12
#include < iostream>
13
13
#include < map>
14
14
#include < optional>
15
+ #include < type_traits>
15
16
#include < vector>
16
17
17
18
namespace 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
+
18
34
// 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
+
20
40
private:
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_{};
22
43
std::set<E> nodes_{};
23
44
24
45
// custom compare operator this is required if u want special key values in std::map
@@ -39,8 +60,6 @@ public:
39
60
: graph_(std::move(graph.graph_)) {}
40
61
~Graph () noexcept = default ;
41
62
42
- using Path = std::vector<std::pair<P, E>>;
43
-
44
63
auto operator =(const Graph other) noexcept -> Graph& {
45
64
graph_ = other.graph_ ;
46
65
return *this ;
@@ -55,8 +74,10 @@ public:
55
74
void add_edge (E source, E destination, P properties) noexcept {
56
75
nodes_.insert (source);
57
76
nodes_.insert (destination);
77
+
58
78
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);
60
81
graph_[source] = edges;
61
82
} else {
62
83
graph_[source].emplace_back (properties, destination);
@@ -95,79 +116,45 @@ public:
95
116
// the return type looks a little bit cursed what is happening here ?
96
117
// we have a map from the destination as a key to a list of paths through the graph.
97
118
// 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>>> {
99
120
return recursive_spanning_tree (source, std::vector<E>{});
100
121
}
101
122
102
123
// this function goes recursively though the graph and tries to find every possible path
103
124
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>>> {
105
126
std::vector<Path> paths{};
106
127
107
128
if (graph_[source_node].empty ()) {
108
129
return std::vector<Path>{Path{}};
109
130
}
110
131
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
+
111
137
for (auto child : graph_[source_node]) {
112
138
E current_node = child.second ;
113
139
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);
125
144
}
126
145
}
127
146
128
147
return paths;
129
148
}
130
149
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
-
155
150
[[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);
165
152
}
166
153
167
154
[[nodiscard]] auto to_mermaid () const noexcept -> std::string {
155
+ std::string mermaid_string = " graph TD;\n " ;
168
156
std::size_t index {0 };
169
157
std::map<E, std::string> name_map{};
170
- std::string mermaid_string = " graph TD;\n " ;
171
158
172
159
auto name_resolver = [&](E object) -> std::string {
173
160
char names[] = " ABCDEFGHIJKLMNOPQRSTUVGXYZabcdefghijklmnopqrstuvgxyz" ; // NOLINT
@@ -188,6 +175,126 @@ public:
188
175
return mermaid_string;
189
176
}
190
177
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
+
191
298
friend auto operator <<(std::ostream& outstream, const Graph& graph) -> std::ostream& {
192
299
for (auto const & [source, destinations] : graph.graph_ ) {
193
300
for (auto destination : destinations) {
0 commit comments