@@ -168,23 +168,24 @@ class edge_view {
168168};
169169
170170/* *
171- * @brief A view over the outgoing edges of a specific vertex in a directed graph.
171+ * @brief A view over the incident edges of a specific vertex in a directed graph.
172172 *
173- * This class provides an iterator-based view to iterate over the outgoing edges
174- * of a given vertex `u` . It is a lightweight, non-owning view.
173+ * This class provides an iterator-based view to iterate over either outgoing or incoming edges
174+ * of a given vertex. It is a lightweight, non-owning view.
175175 *
176176 * @tparam Graph_t The type of the graph, which must satisfy the `is_directed_graph_v` concept.
177+ * @tparam IsOutgoing If true, iterates over outgoing edges; otherwise, incoming edges.
177178 */
178- template <typename Graph_t>
179- class out_edge_view {
179+ template <typename Graph_t, bool IsOutgoing >
180+ class IncidentEdgeView {
180181 private:
181182 static_assert (is_directed_graph_v<Graph_t>, " Graph_t must satisfy the directed_graph concept" );
182183
183184 const Graph_t &graph_;
184- vertex_idx_t <Graph_t> sourceVertex_ ;
185+ vertex_idx_t <Graph_t> anchorVertex_ ;
185186
186187 template <typename child_iterator_t >
187- class OutEdgeIterator {
188+ class IncidentEdgeIterator {
188189 public:
189190 using iterator_category = typename std::iterator_traits<child_iterator_t >::iterator_category;
190191 using difference_type = std::ptrdiff_t ;
@@ -198,145 +199,106 @@ class out_edge_view {
198199 };
199200
200201 private:
201- vertex_idx_t <Graph_t> sourceVertex_ ;
202- child_iterator_t currentChildIt_ ;
202+ vertex_idx_t <Graph_t> anchorVertex_ ;
203+ child_iterator_t currentIt_ ;
203204
204205 public:
205- OutEdgeIterator () = default ;
206- OutEdgeIterator (vertex_idx_t <Graph_t> u, child_iterator_t it) : sourceVertex_ (u), currentChildIt_ (it) {}
206+ IncidentEdgeIterator () = default ;
207+ IncidentEdgeIterator (vertex_idx_t <Graph_t> u, child_iterator_t it) : anchorVertex_ (u), currentIt_ (it) {}
207208
208- [[nodiscard]] value_type operator *() const { return {sourceVertex_, *currentChildIt_}; }
209+ [[nodiscard]] value_type operator *() const {
210+ if constexpr (IsOutgoing) {
211+ return {anchorVertex_, *currentIt_};
212+ } else {
213+ return {*currentIt_, anchorVertex_};
214+ }
215+ }
209216 [[nodiscard]] arrow_proxy operator ->() const { return {operator *()}; }
210217
211- OutEdgeIterator &operator ++() {
212- ++currentChildIt_ ;
218+ IncidentEdgeIterator &operator ++() {
219+ ++currentIt_ ;
213220 return *this ;
214221 }
215222
216- OutEdgeIterator operator ++(int ) {
217- OutEdgeIterator temp = *this ;
223+ IncidentEdgeIterator operator ++(int ) {
224+ IncidentEdgeIterator temp = *this ;
218225 ++(*this );
219226 return temp;
220227 }
221228
222- OutEdgeIterator &operator --() {
223- --currentChildIt_ ;
229+ IncidentEdgeIterator &operator --() {
230+ --currentIt_ ;
224231 return *this ;
225232 }
226233
227- OutEdgeIterator operator --(int ) {
228- OutEdgeIterator temp = *this ;
234+ IncidentEdgeIterator operator --(int ) {
235+ IncidentEdgeIterator temp = *this ;
229236 --(*this );
230237 return temp;
231238 }
232239
233- [[nodiscard]] bool operator ==(const OutEdgeIterator &other) const noexcept {
234- return currentChildIt_ == other.currentChildIt_ ;
240+ [[nodiscard]] bool operator ==(const IncidentEdgeIterator &other) const noexcept {
241+ return currentIt_ == other.currentIt_ ;
235242 }
236243
237- [[nodiscard]] bool operator !=(const OutEdgeIterator &other) const noexcept { return !(*this == other); }
244+ [[nodiscard]] bool operator !=(const IncidentEdgeIterator &other) const noexcept { return !(*this == other); }
238245 };
239246
247+ // Helper to deduce iterator type based on direction
248+ using base_iterator_type =
249+ std::conditional_t <IsOutgoing, decltype (std::declval<Graph_t>().children(std::declval<vertex_idx_t <Graph_t>>()).begin()),
250+ decltype (std::declval<Graph_t>().parents(std::declval<vertex_idx_t <Graph_t>>()).begin())>;
251+
240252 public:
241- using iterator = OutEdgeIterator< decltype (std::declval<Graph_t>().children(std::declval< vertex_idx_t <Graph_t>>()).begin()) >;
253+ using iterator = IncidentEdgeIterator<base_iterator_type >;
242254 using constIterator = iterator;
243255
244- out_edge_view (const Graph_t &graph, vertex_idx_t <Graph_t> u) : graph_(graph), sourceVertex_ (u) {}
256+ IncidentEdgeView (const Graph_t &graph, vertex_idx_t <Graph_t> u) : graph_(graph), anchorVertex_ (u) {}
245257
246- [[nodiscard]] auto begin () const { return iterator (sourceVertex_, graph_.children (sourceVertex_).begin ()); }
258+ [[nodiscard]] auto begin () const {
259+ if constexpr (IsOutgoing) {
260+ return iterator (anchorVertex_, graph_.children (anchorVertex_).begin ());
261+ } else {
262+ return iterator (anchorVertex_, graph_.parents (anchorVertex_).begin ());
263+ }
264+ }
247265 [[nodiscard]] auto cbegin () const { return begin (); }
248266
249- [[nodiscard]] auto end () const { return iterator (sourceVertex_, graph_.children (sourceVertex_).end ()); }
267+ [[nodiscard]] auto end () const {
268+ if constexpr (IsOutgoing) {
269+ return iterator (anchorVertex_, graph_.children (anchorVertex_).end ());
270+ } else {
271+ return iterator (anchorVertex_, graph_.parents (anchorVertex_).end ());
272+ }
273+ }
250274 [[nodiscard]] auto cend () const { return end (); }
251275
252- [[nodiscard]] auto size () const { return graph_.out_degree (sourceVertex_); }
253- [[nodiscard]] bool empty () const { return graph_.out_degree (sourceVertex_) == 0 ; }
276+ [[nodiscard]] auto size () const {
277+ if constexpr (IsOutgoing) {
278+ return graph_.out_degree (anchorVertex_);
279+ } else {
280+ return graph_.in_degree (anchorVertex_);
281+ }
282+ }
283+ [[nodiscard]] bool empty () const {
284+ if constexpr (IsOutgoing) {
285+ return graph_.out_degree (anchorVertex_) == 0 ;
286+ } else {
287+ return graph_.in_degree (anchorVertex_) == 0 ;
288+ }
289+ }
254290};
255291
256292/* *
257- * @brief A view over the incoming edges of a specific vertex in a directed graph.
258- *
259- * This class provides an iterator-based view to iterate over the incoming edges
260- * of a given vertex `v`. It is a lightweight, non-owning view.
261- *
262- * @tparam Graph_t The type of the graph, which must satisfy the `is_directed_graph_v` concept.
293+ * @brief A view over the outgoing edges of a specific vertex in a directed graph.
263294 */
264295template <typename Graph_t>
265- class in_edge_view {
266- private:
267- static_assert (is_directed_graph_v<Graph_t>, " Graph_t must satisfy the directed_graph concept" );
268-
269- const Graph_t &graph_;
270- vertex_idx_t <Graph_t> targetVertex_;
271-
272- template <typename parent_iterator_t >
273- class InEdgeIterator {
274- public:
275- using iterator_category = typename std::iterator_traits<parent_iterator_t >::iterator_category;
276- using difference_type = std::ptrdiff_t ;
277- using value_type = directed_edge<Graph_t>;
278- using pointer = value_type *;
279- using reference = value_type &;
280-
281- struct arrow_proxy {
282- value_type value;
283- const value_type *operator ->() const noexcept { return &value; }
284- };
285-
286- private:
287- vertex_idx_t <Graph_t> targetVertex_;
288- parent_iterator_t currentParentIt_;
289-
290- public:
291- InEdgeIterator () = default ;
292- InEdgeIterator (vertex_idx_t <Graph_t> v, parent_iterator_t it) : targetVertex_(v), currentParentIt_(it) {}
296+ using OutEdgeView = IncidentEdgeView<Graph_t, true >;
293297
294- [[nodiscard]] value_type operator *() const { return {*currentParentIt_, targetVertex_}; }
295- [[nodiscard]] arrow_proxy operator ->() const { return {operator *()}; }
296-
297- InEdgeIterator &operator ++() {
298- ++currentParentIt_;
299- return *this ;
300- }
301-
302- InEdgeIterator operator ++(int ) {
303- InEdgeIterator temp = *this ;
304- ++(*this );
305- return temp;
306- }
307-
308- InEdgeIterator &operator --() {
309- --currentParentIt_;
310- return *this ;
311- }
312-
313- InEdgeIterator operator --(int ) {
314- InEdgeIterator temp = *this ;
315- --(*this );
316- return temp;
317- }
318-
319- [[nodiscard]] bool operator ==(const InEdgeIterator &other) const noexcept {
320- return currentParentIt_ == other.currentParentIt_ ;
321- }
322-
323- [[nodiscard]] bool operator !=(const InEdgeIterator &other) const noexcept { return !(*this == other); }
324- };
325-
326- public:
327- using iterator = InEdgeIterator<decltype (std::declval<Graph_t>().parents(std::declval<vertex_idx_t <Graph_t>>()).begin())>;
328- using constIterator = iterator;
329-
330- in_edge_view (const Graph_t &graph, vertex_idx_t <Graph_t> v) : graph_(graph), targetVertex_(v) {}
331-
332- [[nodiscard]] auto begin () const { return iterator (targetVertex_, graph_.parents (targetVertex_).begin ()); }
333- [[nodiscard]] auto cbegin () const { return begin (); }
334-
335- [[nodiscard]] auto end () const { return iterator (targetVertex_, graph_.parents (targetVertex_).end ()); }
336- [[nodiscard]] auto cend () const { return end (); }
337-
338- [[nodiscard]] auto size () const { return graph_.in_degree (targetVertex_); }
339- [[nodiscard]] bool empty () const { return graph_.in_degree (targetVertex_) == 0 ; }
340- };
298+ /* *
299+ * @brief A view over the incoming edges of a specific vertex in a directed graph.
300+ */
301+ template <typename Graph_t>
302+ using InEdgeView = IncidentEdgeView<Graph_t, false >;
341303
342304} // namespace osp
0 commit comments