Skip to content

Commit bf5232f

Browse files
committed
edge_view
1 parent c5854ca commit bf5232f

1 file changed

Lines changed: 127 additions & 101 deletions

File tree

include/osp/graph_algorithms/directed_graph_edge_view.hpp

Lines changed: 127 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -22,137 +22,149 @@ limitations under the License.
2222

2323
namespace osp {
2424

25+
/**
26+
* @brief A view over all edges in a directed graph.
27+
*
28+
* This class provides an iterator-based view to iterate over all edges in a directed graph.
29+
* The iteration order is lexicographical with respect to (source, target) pairs, determined by
30+
* the order of vertices and their adjacency lists.
31+
*
32+
* @tparam Graph_t The type of the graph, which must satisfy the `is_directed_graph_v` concept.
33+
*/
2534
template<typename Graph_t>
2635
class edge_view {
2736
private:
2837
static_assert(is_directed_graph_v<Graph_t>, "Graph_t must satisfy the directed_graph concept");
2938

30-
const Graph_t &graph;
39+
const Graph_t &graph_;
3140

3241
template<typename child_iterator_t>
33-
class directed_edge_iterator {
42+
class DirectedEdgeIterator {
3443
public:
35-
using iterator_category = std::bidirectional_iterator_tag;
44+
using iterator_category = std::forward_iterator_tag;
3645
using difference_type = std::ptrdiff_t;
3746
using value_type = directed_edge<Graph_t>;
3847
using pointer = value_type *;
3948
using reference = value_type &;
4049

41-
private:
42-
const Graph_t *graph; // Pointer to the graph
43-
vertex_idx_t<Graph_t> current_vertex; // Current source vertex
44-
child_iterator_t current_child; // Iterator to the current target vertex in current_vertex's adjacency list
45-
vertex_idx_t<Graph_t> current_edge_idx; // Global index of the current edge in the traversal order
50+
struct arrow_proxy {
51+
value_type value;
52+
const value_type *operator->() const noexcept { return &value; }
53+
};
4654

47-
public:
48-
directed_edge_iterator() : graph(nullptr), current_vertex(0), current_edge_idx(0) {}
49-
directed_edge_iterator(const directed_edge_iterator &other)
50-
: graph(other.graph), current_vertex(other.current_vertex), current_child(other.current_child),
51-
current_edge_idx(other.current_edge_idx) {}
52-
53-
directed_edge_iterator operator=(const directed_edge_iterator &other) {
54-
graph = other.graph;
55-
current_vertex = other.current_vertex;
56-
current_child = other.current_child;
57-
current_edge_idx = other.current_edge_idx;
58-
return *this;
59-
}
60-
61-
directed_edge_iterator(const Graph_t &graph_) : graph(&graph_), current_vertex(0), current_edge_idx(0) {
62-
63-
while (current_vertex != graph->num_vertices()) {
64-
if (graph->children(current_vertex).begin() != graph->children(current_vertex).end()) {
65-
current_child = graph->children(current_vertex).begin();
55+
private:
56+
const Graph_t *graph_; // Pointer to the graph
57+
vertex_idx_t<Graph_t> currentVertex_; // Current source vertex
58+
child_iterator_t currentChild_; // Iterator to the current target vertex in current_vertex's adjacency list
59+
vertex_idx_t<Graph_t> currentEdgeIdx_; // Global index of the current edge in the traversal order
60+
61+
void advanceToValid() {
62+
while (currentVertex_ != graph_->num_vertices()) {
63+
if (graph_->children(currentVertex_).begin() != graph_->children(currentVertex_).end()) {
64+
currentChild_ = graph_->children(currentVertex_).begin();
6665
break;
6766
}
68-
current_vertex++;
67+
currentVertex_++;
6968
}
7069
}
7170

72-
directed_edge_iterator(const vertex_idx_t<Graph_t> edge_idx, const Graph_t &graph_)
73-
: graph(&graph_), current_vertex(0), current_edge_idx(edge_idx) {
74-
75-
if (current_edge_idx < graph->num_edges()) {
76-
77-
vertex_idx_t<Graph_t> tmp = 0u;
71+
public:
72+
DirectedEdgeIterator() noexcept : graph_(nullptr), currentVertex_(0), currentEdgeIdx_(0) {}
7873

79-
if (tmp < current_edge_idx) {
74+
DirectedEdgeIterator(const DirectedEdgeIterator &other) = default;
75+
DirectedEdgeIterator(DirectedEdgeIterator &&other) noexcept = default;
8076

81-
while (current_vertex != graph->num_vertices()) {
77+
DirectedEdgeIterator &operator=(const DirectedEdgeIterator &other) = default;
78+
DirectedEdgeIterator &operator=(DirectedEdgeIterator &&other) noexcept = default;
8279

83-
current_child = graph->children(current_vertex).begin();
80+
explicit DirectedEdgeIterator(const Graph_t &graph) : graph_(&graph), currentVertex_(0), currentEdgeIdx_(0) {
81+
advanceToValid();
82+
}
8483

85-
while (current_child != graph->children(current_vertex).end()) {
84+
DirectedEdgeIterator(const vertex_idx_t<Graph_t> edge_idx, const Graph_t &graph)
85+
: graph_(&graph), currentVertex_(0), currentEdgeIdx_(edge_idx) {
86+
if (currentEdgeIdx_ < graph_->num_edges()) {
87+
vertex_idx_t<Graph_t> tmp = 0u;
88+
advanceToValid();
8689

87-
if (tmp == current_edge_idx) {
88-
break;
90+
while (currentVertex_ != graph_->num_vertices() && tmp < currentEdgeIdx_) {
91+
while (currentChild_ != graph_->children(currentVertex_).end()) {
92+
if (tmp == currentEdgeIdx_) {
93+
return;
94+
}
95+
currentChild_++;
96+
tmp++;
97+
}
98+
// Move to next vertex
99+
currentVertex_++;
100+
if (currentVertex_ != graph_->num_vertices()) {
101+
currentChild_ = graph_->children(currentVertex_).begin();
102+
// Skip empty adjacency lists
103+
while (currentVertex_ != graph_->num_vertices() &&
104+
graph_->children(currentVertex_).begin() == graph_->children(currentVertex_).end()) {
105+
currentVertex_++;
106+
if (currentVertex_ != graph_->num_vertices()) {
107+
currentChild_ = graph_->children(currentVertex_).begin();
89108
}
90-
91-
current_child++;
92-
tmp++;
93109
}
94-
95-
current_vertex++;
96110
}
97111
}
98-
99112
} else {
100-
current_edge_idx = graph->num_edges();
101-
current_vertex = graph->num_vertices();
113+
currentEdgeIdx_ = graph_->num_edges();
114+
currentVertex_ = graph_->num_vertices();
102115
}
103116
}
104117

105-
inline value_type operator*() const { return {current_vertex, *current_child}; }
106-
107-
inline directed_edge_iterator &operator++() {
118+
[[nodiscard]] value_type operator*() const { return {currentVertex_, *currentChild_}; }
119+
[[nodiscard]] arrow_proxy operator->() const { return {operator*()}; }
108120

109-
current_child++;
110-
current_edge_idx++;
121+
DirectedEdgeIterator &operator++() {
122+
currentChild_++;
123+
currentEdgeIdx_++;
111124

112-
if (current_child == graph->children(current_vertex).end()) {
113-
114-
current_vertex++;
115-
116-
while (current_vertex != graph->num_vertices()) {
117-
118-
if (graph->children(current_vertex).begin() != graph->children(current_vertex).end()) {
119-
current_child = graph->children(current_vertex).begin();
125+
if (currentChild_ == graph_->children(currentVertex_).end()) {
126+
currentVertex_++;
127+
// Skip empty vertices
128+
while (currentVertex_ != graph_->num_vertices()) {
129+
if (graph_->children(currentVertex_).begin() != graph_->children(currentVertex_).end()) {
130+
currentChild_ = graph_->children(currentVertex_).begin();
120131
break;
121132
}
122-
123-
current_vertex++;
133+
currentVertex_++;
124134
}
125135
}
126-
127136
return *this;
128137
}
129138

130-
inline directed_edge_iterator operator++(int) {
131-
directed_edge_iterator temp = *this;
139+
[[nodiscard]] DirectedEdgeIterator operator++(int) {
140+
DirectedEdgeIterator temp = *this;
132141
++(*this);
133142
return temp;
134143
}
135144

136-
inline bool operator==(const directed_edge_iterator &other) const {
137-
return current_edge_idx == other.current_edge_idx;
145+
[[nodiscard]] bool operator==(const DirectedEdgeIterator &other) const noexcept {
146+
return currentEdgeIdx_ == other.currentEdgeIdx_;
138147
}
139148

140-
inline bool operator!=(const directed_edge_iterator &other) const { return !(*this == other); }
149+
[[nodiscard]] bool operator!=(const DirectedEdgeIterator &other) const noexcept { return !(*this == other); }
141150
};
142151

143152
public:
144-
using dir_edge_iterator = directed_edge_iterator<
145-
decltype(std::declval<Graph_t>().children(std::declval<vertex_idx_t<Graph_t>>()).begin())>;
153+
using DirEdgeIterator = DirectedEdgeIterator<decltype(std::declval<Graph_t>().children(std::declval<vertex_idx_t<Graph_t>>()).begin())>;
154+
using iterator = DirEdgeIterator;
155+
using constIterator = DirEdgeIterator;
156+
157+
explicit edge_view(const Graph_t &graph) : graph_(graph) {}
146158

147-
edge_view(const Graph_t &graph_) : graph(graph_) {}
159+
[[nodiscard]] auto begin() const { return DirEdgeIterator(graph_); }
160+
[[nodiscard]] auto cbegin() const { return DirEdgeIterator(graph_); }
148161

149-
inline auto begin() const { return dir_edge_iterator(graph); }
150-
inline auto cbegin() const { return dir_edge_iterator(graph); }
162+
[[nodiscard]] auto end() const { return DirEdgeIterator(graph_.num_edges(), graph_); }
163+
[[nodiscard]] auto cend() const { return DirEdgeIterator(graph_.num_edges(), graph_); }
151164

152-
inline auto end() const { return dir_edge_iterator(graph.num_edges(), graph); }
153-
inline auto cend() const { return dir_edge_iterator(graph.num_edges(), graph); }
165+
[[nodiscard]] auto size() const { return graph_.num_edges(); }
154166

155-
inline auto size() const { return graph.num_edges(); }
167+
[[nodiscard]] bool empty() const { return graph_.num_edges() == 0; }
156168
};
157169

158170
/**
@@ -180,6 +192,11 @@ class out_edge_view {
180192
using pointer = value_type *;
181193
using reference = value_type &;
182194

195+
struct arrow_proxy {
196+
value_type value;
197+
const value_type *operator->() const noexcept { return &value; }
198+
};
199+
183200
private:
184201
vertex_idx_t<Graph_t> source_vertex;
185202
child_iterator_t current_child_it;
@@ -188,35 +205,36 @@ class out_edge_view {
188205
out_edge_iterator() = default;
189206
out_edge_iterator(vertex_idx_t<Graph_t> u, child_iterator_t it) : source_vertex(u), current_child_it(it) {}
190207

191-
inline value_type operator*() const { return {source_vertex, *current_child_it}; }
208+
[[nodiscard]] value_type operator*() const { return {source_vertex, *current_child_it}; }
209+
[[nodiscard]] arrow_proxy operator->() const { return {operator*()}; }
192210

193-
inline out_edge_iterator &operator++() {
211+
out_edge_iterator &operator++() {
194212
++current_child_it;
195213
return *this;
196214
}
197215

198-
inline out_edge_iterator operator++(int) {
216+
out_edge_iterator operator++(int) {
199217
out_edge_iterator temp = *this;
200218
++(*this);
201219
return temp;
202220
}
203221

204-
inline out_edge_iterator &operator--() {
222+
out_edge_iterator &operator--() {
205223
--current_child_it;
206224
return *this;
207225
}
208226

209-
inline out_edge_iterator operator--(int) {
227+
out_edge_iterator operator--(int) {
210228
out_edge_iterator temp = *this;
211229
--(*this);
212230
return temp;
213231
}
214232

215-
inline bool operator==(const out_edge_iterator &other) const {
233+
[[nodiscard]] bool operator==(const out_edge_iterator &other) const noexcept {
216234
return current_child_it == other.current_child_it;
217235
}
218236

219-
inline bool operator!=(const out_edge_iterator &other) const { return !(*this == other); }
237+
[[nodiscard]] bool operator!=(const out_edge_iterator &other) const noexcept { return !(*this == other); }
220238
};
221239

222240
public:
@@ -226,13 +244,14 @@ class out_edge_view {
226244

227245
out_edge_view(const Graph_t &graph_, vertex_idx_t<Graph_t> u) : graph(graph_), source_vertex(u) {}
228246

229-
inline auto begin() const { return iterator(source_vertex, graph.children(source_vertex).begin()); }
230-
inline auto cbegin() const { return begin(); }
247+
[[nodiscard]] auto begin() const { return iterator(source_vertex, graph.children(source_vertex).begin()); }
248+
[[nodiscard]] auto cbegin() const { return begin(); }
231249

232-
inline auto end() const { return iterator(source_vertex, graph.children(source_vertex).end()); }
233-
inline auto cend() const { return end(); }
250+
[[nodiscard]] auto end() const { return iterator(source_vertex, graph.children(source_vertex).end()); }
251+
[[nodiscard]] auto cend() const { return end(); }
234252

235-
inline auto size() const { return graph.out_degree(source_vertex); }
253+
[[nodiscard]] auto size() const { return graph.out_degree(source_vertex); }
254+
[[nodiscard]] bool empty() const { return graph.out_degree(source_vertex) == 0; }
236255
};
237256

238257
/**
@@ -260,6 +279,11 @@ class in_edge_view {
260279
using pointer = value_type *;
261280
using reference = value_type &;
262281

282+
struct arrow_proxy {
283+
value_type value;
284+
const value_type *operator->() const noexcept { return &value; }
285+
};
286+
263287
private:
264288
vertex_idx_t<Graph_t> target_vertex;
265289
parent_iterator_t current_parent_it;
@@ -268,35 +292,36 @@ class in_edge_view {
268292
in_edge_iterator() = default;
269293
in_edge_iterator(vertex_idx_t<Graph_t> v, parent_iterator_t it) : target_vertex(v), current_parent_it(it) {}
270294

271-
inline value_type operator*() const { return {*current_parent_it, target_vertex}; }
295+
[[nodiscard]] value_type operator*() const { return {*current_parent_it, target_vertex}; }
296+
[[nodiscard]] arrow_proxy operator->() const { return {operator*()}; }
272297

273-
inline in_edge_iterator &operator++() {
298+
in_edge_iterator &operator++() {
274299
++current_parent_it;
275300
return *this;
276301
}
277302

278-
inline in_edge_iterator operator++(int) {
303+
in_edge_iterator operator++(int) {
279304
in_edge_iterator temp = *this;
280305
++(*this);
281306
return temp;
282307
}
283308

284-
inline in_edge_iterator &operator--() {
309+
in_edge_iterator &operator--() {
285310
--current_parent_it;
286311
return *this;
287312
}
288313

289-
inline in_edge_iterator operator--(int) {
314+
in_edge_iterator operator--(int) {
290315
in_edge_iterator temp = *this;
291316
--(*this);
292317
return temp;
293318
}
294319

295-
inline bool operator==(const in_edge_iterator &other) const {
320+
[[nodiscard]] bool operator==(const in_edge_iterator &other) const noexcept {
296321
return current_parent_it == other.current_parent_it;
297322
}
298323

299-
inline bool operator!=(const in_edge_iterator &other) const { return !(*this == other); }
324+
[[nodiscard]] bool operator!=(const in_edge_iterator &other) const noexcept { return !(*this == other); }
300325
};
301326

302327
public:
@@ -306,13 +331,14 @@ class in_edge_view {
306331

307332
in_edge_view(const Graph_t &graph_, vertex_idx_t<Graph_t> v) : graph(graph_), target_vertex(v) {}
308333

309-
inline auto begin() const { return iterator(target_vertex, graph.parents(target_vertex).begin()); }
310-
inline auto cbegin() const { return begin(); }
334+
[[nodiscard]] auto begin() const { return iterator(target_vertex, graph.parents(target_vertex).begin()); }
335+
[[nodiscard]] auto cbegin() const { return begin(); }
311336

312-
inline auto end() const { return iterator(target_vertex, graph.parents(target_vertex).end()); }
313-
inline auto cend() const { return end(); }
337+
[[nodiscard]] auto end() const { return iterator(target_vertex, graph.parents(target_vertex).end()); }
338+
[[nodiscard]] auto cend() const { return end(); }
314339

315-
inline auto size() const { return graph.in_degree(target_vertex); }
340+
[[nodiscard]] auto size() const { return graph.in_degree(target_vertex); }
341+
[[nodiscard]] bool empty() const { return graph.in_degree(target_vertex) == 0; }
316342
};
317343

318344
} // namespace osp

0 commit comments

Comments
 (0)