diff --git a/include/graph/graph.hpp b/include/graph/graph.hpp index 4522c53..d3fc749 100644 --- a/include/graph/graph.hpp +++ b/include/graph/graph.hpp @@ -17,6 +17,8 @@ * @ingroup general_concepts */ +#define ENABLE_EDGELIST_RANGE + #include #include #include @@ -41,7 +43,6 @@ // VR Vertex Range // VI ui,vi Vertex Iterator // VVF Vertex Value Function: vvf(u) -> value -// VVP vvp Vertex Value Projection function vvp(x) --> vertex_descriptor<> // // E Edge type // uv,vw Edge reference @@ -49,8 +50,9 @@ // ER Edge Range // EI uvi,vwi Edge iterator // EVF evf Edge Value Function: evf(uv) -> value -// EVP evp Edge Value Production function: evp(y) -> edge_descriptor<> // +// ELR elr Edge List Range; an arbitrary range where its values can be projected to be an edge_descriptor. +// Proj proj Projection function: proj(y) -> edge_descriptor<...>, where y is the value type of an ELR #ifndef GRAPH_HPP # define GRAPH_HPP @@ -203,6 +205,14 @@ template concept sourced_adjacency_list = adjacency_list && sourced_edge> && requires(G&& g, edge_reference_t uv) { edge_id(g, uv); }; + +# ifdef ENABLE_EDGELIST_RANGE +template +concept basic_edgelist_range = ranges::forward_range && negation_v>; +template +concept edgelist_range = ranges::forward_range && negation_v>; +# endif + // // property concepts // diff --git a/include/graph/graph_descriptors.hpp b/include/graph/graph_descriptors.hpp index aee1da8..a9759a8 100644 --- a/include/graph/graph_descriptors.hpp +++ b/include/graph/graph_descriptors.hpp @@ -41,6 +41,11 @@ using copyable_vertex_t = vertex_descriptor; // {id, value} // template struct edge_descriptor { + using source_id_type = VId; + using target_id_type = VId; + using edge_type = E; + using value_type = EV; + VId source_id; VId target_id; E edge; @@ -49,17 +54,32 @@ struct edge_descriptor { template struct edge_descriptor { + using source_id_type = VId; + using target_id_type = VId; + using edge_type = E; + using value_type = void; + VId source_id; VId target_id; E edge; }; template struct edge_descriptor { + using source_id_type = VId; + using target_id_type = VId; + using edge_type = void; + using value_type = void; + VId source_id; VId target_id; }; template struct edge_descriptor { + using source_id_type = VId; + using target_id_type = VId; + using edge_type = void; + using value_type = EV; + VId source_id; VId target_id; EV value; @@ -67,23 +87,43 @@ struct edge_descriptor { template struct edge_descriptor { + using source_id_type = void; + using target_id_type = VId; + using edge_type = void; + using value_type = EV; + VId target_id; E edge; EV value; }; template struct edge_descriptor { + using source_id_type = void; + using target_id_type = VId; + using edge_type = E; + using value_type = void; + VId target_id; E edge; }; template struct edge_descriptor { + using source_id_type = void; + using target_id_type = VId; + using edge_type = void; + using value_type = EV; + VId target_id; EV value; }; template struct edge_descriptor { + using source_id_type = void; + using target_id_type = VId; + using edge_type = void; + using value_type = void; + VId target_id; }; diff --git a/include/graph/views/edgelist.hpp b/include/graph/views/edgelist.hpp index 9807ddc..542011d 100644 --- a/include/graph/views/edgelist.hpp +++ b/include/graph/views/edgelist.hpp @@ -8,10 +8,17 @@ // edgelist(g,uid) -> edge_descriptor -> {source_id, target_id, edge&} // edgelist(g,uid,evf) -> edge_descriptor -> {source_id, target_id, edge&, value} // +// edgelist(elr,proj) -> edge_descriptor -> {source_id, target_id, edge&}, where VId is defined by proj and E is range_value_t& +// edgelist(elr,proj) -> edge_descriptor -> {source_id, target_id, edge&, Value}, where VId and Value defined by proj and E is range_value_t& +// Note: proj(range_value_t&) is a projection and determines whether to return a value member or not +// // basic_edgelist(g) -> edge_descriptor -> {source_id, target_id} // // basic_edgelist(g,uid) -> edge_descriptor -> {source_id, target_id} // +// basic_edgelist(elr,proj) -> edge_descriptor -> {source_id, target_id}, where VId is defined by proj +// Note: proj(range_value_t&) is a projection and determines whether to return a value member or not +// // given: auto evf = [&g](edge_reference_t uv) { return edge_value(uv); } // // vertex_id first_id = ..., last_id = ...; @@ -26,13 +33,21 @@ // // for([uid, vid] : basic_edgelist(g,uid)) // -namespace std::graph::views { +namespace std::graph::views { template class edgelist_iterator; +#ifdef ENABLE_EDGELIST_RANGE +template +class edgelist_range_iterator; +#endif +/** + * @brief Common functionality for edgelist_iterator + * @tparam G Graph +*/ template class edgelist_iterator_base { using vertex_iterator = vertex_iterator_t; @@ -277,90 +292,476 @@ class edgelist_iterator : public edgelist_iterator_base { }; -template -using edgelist_view = ranges::subrange, vertex_iterator_t>; +/** + * @brief Iterator for a range with values that can be projected to a edge_descriptor. + * + * @tparam ELR Graph type + * @tparam Proj Edge Value Function type +*/ +#if 0 +template +class edgelist_range_iterator { +public: + using edge_range = ELR; + using edge_iterator = ranges::iterator_t; + using edge_type = ranges::range_value_t; + using edge_reference_type = ranges::range_reference_t; + using edge_value_type = invoke_result_t; + + using iterator_category = forward_iterator_tag; + using value_type = edge_descriptor; + using difference_type = ranges::range_difference_t; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using rvalue_reference = value_type&&; -namespace tag_invoke { - // ranges - TAG_INVOKE_DEF(edgelist); // edgelist(g) -> edges[uid,vid,uv] - // edgelist(g,fn) -> edges[uid,vid,uv,value] - // edgelist(g,uid,vid) -> edges[uid,vid,uv] - // edgelist(g,uid,vid,fn) -> edges[uid,vid,uv,value] +public: + edgelist_range_iterator(graph_type& g, vertex_iterator ui, const Proj& proj_fn) + : base_type(), g_(g), ui_(ui), uvi_(), proj_fn_(&proj_fn) {} + edgelist_range_iterator(graph_type& g, const Proj& proj_fn) + : edgelist_range_iterator(g, ranges::begin(vertices(g)), proj_fn) { + this->find_non_empty_vertex(g_, ui_, uvi_); + } - template - concept _has_edgelist_g_adl = adjacency_list && requires(G&& g) { - { edgelist(g) }; - }; - template - concept _has_edgelist_g_uid_adl = adjacency_list && requires(G&& g, vertex_id_t uid, vertex_id_t vid) { - { edgelist(g, uid, vid) }; - }; - template - concept _has_edgelist_g_evf_adl = adjacency_list && requires(G&& g, const EVF& evf) { - { edgelist(g, evf) }; - }; - template - concept _has_edgelist_g_uid_evf_adl = - adjacency_list && requires(G&& g, vertex_id_t uid, vertex_id_t vid, const EVF& evf) { - { edgelist(g, uid, vid, evf) }; - }; + constexpr edgelist_range_iterator() = default; + constexpr edgelist_range_iterator(const edgelist_range_iterator&) = default; + constexpr edgelist_range_iterator(edgelist_range_iterator&&) = default; + constexpr ~edgelist_range_iterator() = default; -} // namespace tag_invoke + constexpr edgelist_range_iterator& operator=(const edgelist_range_iterator&) = default; + constexpr edgelist_range_iterator& operator=(edgelist_range_iterator&&) = default; -// -// edgelist(g) -// edgelist(g,uid,vid) -// -template -requires ranges::forward_range> -constexpr auto edgelist(G&& g) { - if constexpr (tag_invoke::_has_edgelist_g_adl) { - return tag_invoke::edgelist(g); - } else { - using iterator_type = edgelist_iterator; - return edgelist_view(iterator_type(g), ranges::end(vertices(g))); +protected: + // avoid difficulty in undefined vertex reference value in value_type + // shadow_vertex_value_type: ptr if vertex_value_type is ref or ptr, value otherwise + using shadow_edge_type = remove_reference_t; + using shadow_value_type = + edge_descriptor>; + + union internal_value { + value_type value_; + shadow_value_type shadow_; + + internal_value(const internal_value& rhs) : shadow_(rhs.shadow_) {} + internal_value() : shadow_{} {} + ~internal_value() {} + internal_value& operator=(const internal_value& rhs) { value_.shadow = rhs.value_.shadow; } + }; + +public: + constexpr reference operator*() const { + if constexpr (unordered_edge) { + if (target_id(g_, *uvi_) != vertex_id(g_, ui_)) { + value_.shadow_.source_id = source_id(g_, *uvi_); + value_.shadow_.target_id = target_id(g_, *uvi_); + } else { + value_.shadow_.source_id = target_id(g_, *uvi_); + value_.shadow_.target_id = source_id(g_, *uvi_); + } + value_.shadow_.edge = &*uvi_; + value_.shadow_.value = invoke(*proj_fn_, *uvi_); + } else { + value_.shadow_ = {vertex_id(g_, ui_), target_id(g_, *uvi_), &*uvi_, invoke(*proj_fn_, *uvi_)}; + } + return value_.value_; } -} -template -requires ranges::forward_range> -constexpr auto edgelist(G&& g, vertex_id_t first, vertex_id_t last) { - assert(first <= last && static_cast(last) <= ranges::size(vertices(g))); - if constexpr (tag_invoke::_has_edgelist_g_uid_adl) - return tag_invoke::edgelist(g, first, last); - else { - using iterator_type = edgelist_iterator; - return edgelist_view(iterator_type(g, find_vertex(g, first)), find_vertex(g, last)); + constexpr edgelist_range_iterator& operator++() { + this->find_next_edge(g_, ui_, uvi_); + return *this; + } + constexpr edgelist_range_iterator operator++(int) const { + edgelist_range_iterator tmp(*this); + ++*this; + return tmp; } -} + constexpr bool operator==(const edgelist_range_iterator& rhs) const { return uvi_ == rhs.uvi_; } + //constexpr bool operator==(const edgelist_range_iterator& rhs) const { return uvi_ == rhs; } +private: // member variables + mutable internal_value value_; + _detail::ref_to_ptr g_; + vertex_iterator ui_; + edge_iterator uvi_; + const Proj* proj_fn_ = nullptr; + + friend bool operator==(const vertex_iterator& lhs, const edgelist_range_iterator& rhs) { return lhs == rhs.ui_; } +}; +#endif + + +template +using edgelist_view = ranges::subrange, vertex_iterator_t>; + + +// edgelist(g) -> edges[uid,vid,uv] +// edgelist(g,fn) -> edges[uid,vid,uv,value] +// edgelist(g,uid,vid) -> edges[uid,vid,uv] +// edgelist(g,uid,vid,fn) -> edges[uid,vid,uv,value] // -// edgelist(g,u,evf) -// edgelist(g,uid,evf) -// -template -requires ranges::forward_range> -constexpr auto edgelist(G&& g, const EVF& evf) { - if constexpr (tag_invoke::_has_edgelist_g_evf_adl) { - return tag_invoke::edgelist(g, evf); - } else { - using iterator_type = edgelist_iterator; - return edgelist_view(iterator_type(g, evf), ranges::end(vertices(g))); - } -} +// edgelist(elr,proj) -> edges[uid,vid,uv] ; proj determines whether value is included or not +// edgelist(elr,proj) -> edges[uid,vid,uv,value] +namespace _Edgelist { +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1681199 + void edgelist() = delete; // Block unqualified name lookup +#else // ^^^ no workaround / workaround vvv + void edgelist(); +#endif // ^^^ workaround ^^^ + + template + concept _Has_adjlist_all_ADL = _Has_class_or_enum_type<_G> // + && adjacency_list<_G> // + && requires(_G&& __g) { + { _Fake_copy_init(edgelist(__g)) }; // intentional ADL + }; + template + concept _Can_adjlist_all_eval = _Has_class_or_enum_type<_G> // + && adjacency_list<_G> // + && requires(_G&& __g, vertex_reference_t<_G> u) { + { _Fake_copy_init(vertices(__g)) }; + { _Fake_copy_init(edges(__g, u)) }; + }; + + template + concept _Has_adjlist_all_evf_ADL = _Has_class_or_enum_type<_G> // + && adjacency_list<_G> // + && invocable> // + && requires(_G&& __g, EVF evf) { + { _Fake_copy_init(edgelist(__g, evf)) }; // intentional ADL + }; + template + concept _Can_adjlist_all_evf_eval = _Has_class_or_enum_type<_G> // + && adjacency_list<_G> // + && invocable> // + && requires(_G&& __g, vertex_reference_t<_G> u) { + { _Fake_copy_init(vertices(__g)) }; + { _Fake_copy_init(edges(__g, u)) }; + }; + + + template + concept _Has_adjlist_idrng_ADL = _Has_class_or_enum_type<_G> // + && adjacency_list<_G> // + && requires(_G&& __g, vertex_id_t<_G> uid, vertex_id_t<_G> vid) { + { _Fake_copy_init(edgelist(__g, uid, vid)) }; // intentional ADL + }; + template + concept _Can_adjlist_idrng_eval = _Has_class_or_enum_type<_G> // + && adjacency_list<_G> // + && requires(_G&& __g, vertex_id_t<_G> uid) { + { _Fake_copy_init(vertices(__g)) }; + { _Fake_copy_init(edges(__g, uid)) }; + }; + + template + concept _Has_adjlist_idrng_evf_ADL = _Has_class_or_enum_type<_G> // + && adjacency_list<_G> // + && invocable> // + && requires(_G&& __g, vertex_id_t<_G> uid, vertex_id_t<_G> vid, EVF evf) { + { _Fake_copy_init(edgelist(__g, uid, vid, evf)) }; // intentional ADL + }; + template + concept _Can_adjlist_idrng_evf_eval = _Has_class_or_enum_type<_G> // + && adjacency_list<_G> // + && invocable> // + && requires(_G&& __g, vertex_id_t<_G> uid, EVF evf) { + { _Fake_copy_init(vertices(__g)) }; + { _Fake_copy_init(edges(__g, uid)) }; + }; + +#ifdef ENABLE_EDGELIST_RANGE + template + concept _Has_edgelist_all_proj_ADL = edgelist_range // + && invocable>; + template + concept _Can_edgelist_all_proj_eval = edgelist_range // + && invocable>; +#endif + + class _Cpo { + private: + enum class _St_adjlist_all { _None, _Non_member, _Auto_eval }; + enum class _St_adjlist_idrng { _None, _Non_member, _Auto_eval }; + enum class _St_edgelist_all { _None, _Non_member, _Auto_eval }; + + // edgelist(g) + template + [[nodiscard]] static consteval _Choice_t<_St_adjlist_all> _Choose_all() noexcept { + static_assert(is_lvalue_reference_v<_G>); + using _UnCV = remove_cvref_t<_G>; + + if constexpr (_Has_adjlist_all_ADL<_G, _UnCV>) { + return {_St_adjlist_all::_Non_member, noexcept(_Fake_copy_init(edgelist(declval<_G>())))}; // intentional ADL + } else if constexpr (_Can_adjlist_all_eval<_G, _UnCV>) { + return {_St_adjlist_all::_Auto_eval, noexcept(true)}; // default impl (revisit) + } else { + return {_St_adjlist_all::_None}; + } + } -template -requires ranges::forward_range> -constexpr auto edgelist(G&& g, vertex_id_t first, vertex_id_t last, const EVF& evf) { - assert(first <= last && static_cast(last) <= ranges::size(vertices(g))); - if constexpr (tag_invoke::_has_edgelist_g_uid_evf_adl) - return tag_invoke::edgelist(g, first, last, evf); - else { - using iterator_type = edgelist_iterator; - return edgelist_view(iterator_type(g, find_vertex(g, first)), find_vertex(g, last), evf); - } -} + template + static constexpr _Choice_t<_St_adjlist_all> _Choice_all = _Choose_all<_G>(); + // edgelist(g,evf) + template + [[nodiscard]] static consteval _Choice_t<_St_adjlist_all> _Choose_all_evf() noexcept { + static_assert(is_lvalue_reference_v<_G>); + using _UnCV = remove_cvref_t<_G>; + + if constexpr (_Has_adjlist_all_evf_ADL<_G, _UnCV, EVF>) { + return {_St_adjlist_all::_Non_member, + noexcept(_Fake_copy_init(edgelist(declval<_G>(), declval())))}; // intentional ADL + } else if constexpr (_Can_adjlist_all_evf_eval<_G, _UnCV, EVF>) { + return {_St_adjlist_all::_Auto_eval, noexcept(true)}; // default impl (revisit) + } else { + return {_St_adjlist_all::_None}; + } + } + + template + static constexpr _Choice_t<_St_adjlist_all> _Choice_all_evf = _Choose_all_evf<_G, EVF>(); + + + // edgelist(g,uid,vid) + template + [[nodiscard]] static consteval _Choice_t<_St_adjlist_idrng> _Choose_idrng() noexcept { + static_assert(is_lvalue_reference_v<_G>); + using _UnCV = remove_cvref_t<_G>; + + if constexpr (_Has_adjlist_idrng_ADL<_G, _UnCV>) { + return {_St_adjlist_idrng::_Non_member, + noexcept(_Fake_copy_init(edgelist(declval<_G>(), declval>(), + declval>())))}; // intentional ADL + } else if constexpr (_Can_adjlist_idrng_eval<_G, _UnCV>) { + return {_St_adjlist_idrng::_Auto_eval, noexcept(true)}; // default impl (revisit) + } else { + return {_St_adjlist_idrng::_None}; + } + } + + template + static constexpr _Choice_t<_St_adjlist_idrng> _Choice_idrng = _Choose_idrng<_G>(); + + // edgelist(g,uid,vid,evf) + template + [[nodiscard]] static consteval _Choice_t<_St_adjlist_idrng> _Choose_idrng_evf() noexcept { + static_assert(is_lvalue_reference_v<_G>); + using _UnCV = remove_cvref_t<_G>; + + if constexpr (_Has_adjlist_idrng_evf_ADL<_G, _UnCV, EVF>) { + return {_St_adjlist_idrng::_Non_member, + noexcept(_Fake_copy_init(edgelist(declval<_G>(), declval>(), declval>(), + declval())))}; // intentional ADL + } else if constexpr (_Can_adjlist_idrng_evf_eval<_G, _UnCV, EVF>) { + return {_St_adjlist_idrng::_Auto_eval, noexcept(true)}; // default impl (revisit) + } else { + return {_St_adjlist_idrng::_None}; + } + } + + template + static constexpr _Choice_t<_St_adjlist_idrng> _Choice_idrng_evf = _Choose_idrng_evf<_G, EVF>(); + +#ifdef ENABLE_EDGELIST_RANGE + // edgelist(elr,proj) + template + [[nodiscard]] static consteval _Choice_t<_St_edgelist_all> _Choose_elr_proj() noexcept { + static_assert(is_lvalue_reference_v); + using _UnCV = remove_cvref_t; + + if constexpr (_Has_edgelist_all_proj_ADL) { + return {_St_edgelist_all::_Non_member, + noexcept(_Fake_copy_init(edgelist(declval(), declval())))}; // intentional ADL + } else if constexpr (_Can_edgelist_all_proj_eval) { + return {_St_edgelist_all::_Auto_eval, noexcept(true)}; // default impl (revisit) + } else { + return {_St_edgelist_all::_None}; + } + } + + template + static constexpr _Choice_t<_St_edgelist_all> _Choice_elr_proj = _Choose_elr_proj(); +#endif + + public: + // edgelist(g) + /** + * @brief Get the edgelist of all edges in a graph. + * + * Complexity: O(E) + * + * Default implementation: + * using iterator_type = edgelist_iterator; + * edgelist_view(iterator_type(g), ranges::end(vertices(g))); + * + * @tparam G The graph type. + * @param g A graph instance. + * @return A range of edges in graph g where the range value_type is + * edge_descriptor,false,vertex_reference_t,void> + */ + template + requires(_Choice_all<_G&>._Strategy != _St_adjlist_all::_None) + [[nodiscard]] constexpr auto operator()(_G&& __g) const noexcept(_Choice_all<_G&>._No_throw) { + constexpr _St_adjlist_all _Strat_ref = _Choice_all<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_adjlist_all::_Non_member) { + return edgelist(__g); // intentional ADL + } else if constexpr (_Strat_ref == _St_adjlist_all::_Auto_eval) { + using iterator_type = edgelist_iterator<_G, void>; + return edgelist_view<_G, void>(iterator_type(__g), ranges::end(vertices(__g))); // default impl + } else { + static_assert(_Always_false<_G>, + "edgelist(g) is not defined and the default implementation cannot be evaluated"); + } + } + + // edgelist(g,evf) + /** + * @brief Get the edgelist of all edges in a graph, with edge values. + * + * Complexity: O(E) + * + * Default implementation: + * using iterator_type = edgelist_iterator; + * edgelist_view(iterator_type(g), ranges::end(vertices(g))); + * + * @tparam G The graph type. + * @param g A graph instance. + * @return A range of edges in graph g where the range value_type is + * edge_descriptor,false,vertex_reference_t,void> + */ + template + requires(_Choice_all_evf<_G&, EVF>._Strategy != _St_adjlist_all::_None) + [[nodiscard]] constexpr auto operator()(_G&& __g, const EVF& evf) const + noexcept(_Choice_all_evf<_G&, EVF>._No_throw) { + constexpr _St_adjlist_all _Strat_ref = _Choice_all_evf<_G&, EVF>._Strategy; + + if constexpr (_Strat_ref == _St_adjlist_all::_Non_member) { + return edgelist(__g); // intentional ADL + } else if constexpr (_Strat_ref == _St_adjlist_all::_Auto_eval) { + using iterator_type = edgelist_iterator<_G, EVF>; + return edgelist_view<_G, EVF>(iterator_type(__g, evf), ranges::end(vertices(__g))); + } else { + static_assert(_Always_false<_G>, + "edgelist(g,evf) is not defined and the default implementation cannot be evaluated"); + } + } + + // edgelist(__g,uid,vid) + /** + * @brief Get the edgelist of all edges in a graph. + * + * Complexity: O(E) + * + * Default implementation: + * using iterator_type = edgelist_iterator; + * edgelist_view(iterator_type(__g), ranges::end(vertices(__g))); + * + * @tparam G The graph type. + * @param __g A graph instance. + * @return A range of edges in graph __g where the range value_type is + * edge_descriptor,false,vertex_reference_t,void> + */ + template + requires(_Choice_idrng<_G&>._Strategy != _St_adjlist_idrng::_None) + [[nodiscard]] constexpr auto operator()(_G&& __g, vertex_id_t<_G> first, vertex_id_t<_G> last) const + noexcept(_Choice_idrng<_G&>._No_throw) { + constexpr _St_adjlist_idrng _Strat_ref = _Choice_idrng<_G&>._Strategy; + + if constexpr (_Strat_ref == _St_adjlist_idrng::_Non_member) { + return edgelist(__g); // intentional ADL + } else if constexpr (_Strat_ref == _St_adjlist_idrng::_Auto_eval) { + using iterator_type = edgelist_iterator<_G, void>; + return edgelist_view<_G, void>(iterator_type(__g, find_vertex(__g, first)), find_vertex(__g, last)); + } else { + static_assert(_Always_false<_G>, + "edgelist(g,uid,vid) is not defined and the default implementation cannot be evaluated"); + } + } + + // edgelist(g,uid,vid,evf) + /** + * @brief Get the edgelist of all edges in a graph, with edge values. + * + * Complexity: O(E) + * + * Default implementation: + * using iterator_type = edgelist_iterator<_G, void>; + * edgelist_view<_G, void>(iterator_type(g), ranges::end(vertices(g))); + * + * @tparam _G The graph type. + * @param g A graph instance. + * @return A range of edges in graph g where the range value_type is + * edge_descriptor,false,vertex_reference_t<_G>,void> + */ + template + requires(_Choice_idrng_evf<_G&, EVF>._Strategy != _St_adjlist_idrng::_None) + [[nodiscard]] constexpr auto operator()(_G&& __g, vertex_id_t<_G> first, vertex_id_t<_G> last, const EVF& evf) const + noexcept(_Choice_idrng<_G&>._No_throw) { + constexpr _St_adjlist_idrng _Strat_ref = _Choice_idrng_evf<_G&, EVF>._Strategy; + + if constexpr (_Strat_ref == _St_adjlist_idrng::_Non_member) { + return edgelist(__g); // intentional ADL + } else if constexpr (_Strat_ref == _St_adjlist_idrng::_Auto_eval) { + using iterator_type = edgelist_iterator<_G, EVF>; + return edgelist_view<_G, void>(iterator_type(__g, evf), ranges::end(vertices(__g))); // default impl + } else { + static_assert(_Always_false<_G>, + "edgelist(g,uid,vid,evf) is not defined and the default implementation cannot be evaluated"); + } + } + + +#ifdef ENABLE_EDGELIST_RANGE + // edgelist(elr,proj) + /** + * @brief Create an edgelist from an arbitrary range using a projection. + * + * The projection must return a value of one of the following types: + * edge_descriptor&, void> + * edge_descriptor&, Value> + * where Id is an integral type defined by proj, and Value is also a type defined by proj. + * + * Complexity: O(n) + * + * Default implementation: + * ??? + * + * @tparam ELR The Edge List Range type. This can be any forward_range. + * @tparam Proj The projection function type that converts a range_value_t to an edge_descriptor. + * @param elr The Edge List Range instance. + * @param proj The projection instance that converts a range_value_t to an edge_descriptor. If + * the range_value_t is already a valid edge_descriptor then identity can be used. + * @return A range of edge_descriptors projected from elr. The value member type can be void or non-void. + * If it is void, the value member will not exist in the returned edge_descriptor. + */ + template + requires(_Choice_elr_proj._Strategy != _St_adjlist_all::_None) + [[nodiscard]] constexpr auto operator()(ELR&& elr, const Proj& proj = identity()) const + noexcept(_Choice_elr_proj._No_throw) { + constexpr _St_adjlist_all _Strat_ref = _Choice_elr_proj._Strategy; + + if constexpr (_Strat_ref == _St_adjlist_all::_Non_member) { + return edgelist(elr); // intentional ADL + } else if constexpr (_Strat_ref == _St_adjlist_all::_Auto_eval) { + //using iterator_type = edgelist_iterator; + //return edgelist_view(iterator_type(elr, proj), ranges::end(vertices(elr))); + return 1; // bogus; must implement + } else { + static_assert(_Always_false, + "edgelist(elr,proj) is not defined and the default implementation cannot be evaluated"); + } + } +#endif //ENABLE_EDGELIST_RANGE + + }; // class _Cpo +} // namespace _Edgelist + +inline namespace _Cpos { + inline constexpr _Edgelist::_Cpo edgelist; +} } // namespace std::graph::views