@@ -22,137 +22,149 @@ limitations under the License.
2222
2323namespace 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+ */
2534template <typename Graph_t>
2635class 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