33#include "sequoia/PlatformSpecific/Preprocessor.hpp"
41 namespace data_structures
43 template <
class,
class,
class>
class partitioned_sequence;
50 template<
class EdgeType,
class EdgeStorageType>
53 using edge_type = EdgeType;
54 using edge_weight_type =
typename edge_type::weight_type;
55 using edge_index_type =
typename edge_type::index_type;
56 using edge_storage_type = EdgeStorageType;
57 using const_edge_iterator =
typename edge_storage_type::const_partition_iterator;
58 using size_type =
typename edge_storage_type::size_type;
62 const edge_weight_type* m_WeightPtr{};
63 edge_index_type node_index{}, edge_index{};
66 edge_storage_type& m_Storage;
67 std::vector<data> m_WeightMap;
69 explicit edge_maker(edge_storage_type& storage)
73 void operator()(
const size_type node, const_edge_iterator first, const_edge_iterator i)
75 auto weightPtr{&i->weight()};
76 auto found{std::ranges::lower_bound(m_WeightMap, weightPtr, [](
const edge_weight_type* lhs,
const edge_weight_type* rhs) {
return lhs < rhs; }, [](
const data& d) {
return d.m_WeightPtr; })};
77 if((found == m_WeightMap.end()) || (found->m_WeightPtr != weightPtr))
79 auto appender{[=,
this](
auto e){
80 m_Storage.push_back_to_partition(node, std::move(e));
81 m_WeightMap.emplace(found, &i->weight(), node,
static_cast<edge_index_type
>(std::ranges::distance(first, i)));
85 appender(edge_type{i->target_node(), i->weight()});
89 m_Storage.push_back_to_partition(node, i->target_node(), *(m_Storage.cbegin_partition(found->node_index) + found->edge_index));
94 template<std::input_or_output_iterator Iter>
98 using edge_weight_type = std::remove_cvref_t<decltype(std::declval<Iter>()->weight())>;
100 template<
class Fn1,
class Fn2,
class... Args1>
103 , m_OldEdgeWeight{m_Iter->weight()}
105 auto partnerIter{fn1(m_Iter, std::forward<Args1>(args1)...)};
106 m_PartiallyComplete =
true;
108 fn2(m_Iter, partnerIter);
109 m_PartiallyComplete =
false;
114 if(m_PartiallyComplete)
116 m_Iter->weight(std::move(m_OldEdgeWeight));
120 bool m_PartiallyComplete{};
122 edge_weight_type m_OldEdgeWeight;
125 template<
class Edges>
129 using edge_index_type =
typename Edges::index_type;
131 join_sentinel(Edges& e,
const edge_index_type node1,
const edge_index_type pos)
135 , m_InitialSize{e.size()}
140 if(m_Edges.size() == m_InitialSize)
142 m_Edges.erase_from_partition(m_Node1, m_Pos);
147 edge_index_type m_Node1{}, m_Pos{};
148 std::size_t m_InitialSize{};
155 constexpr bool operator()(
const Edge& e1,
const Edge& e2)
const noexcept
157 using edge_weight_type =
typename Edge::weight_type;
158 constexpr bool sort_weights{!std::is_empty_v<edge_weight_type> && std::totally_ordered<edge_weight_type>};
160 if constexpr(!sort_weights)
162 return e1.target_node() < e2.target_node();
166 return (e1.target_node() == e2.target_node()) ? e1.weight() < e2.weight() : e1.target_node() < e2.target_node();
182 template<graph_flavour Flavour,
class EdgeStorage>
187 using edge_storage_type = EdgeStorage;
189 using edge_type =
typename edge_storage_type::value_type;
190 using edge_weight_type =
typename edge_type::weight_type;
191 using edge_meta_data_type =
typename edge_type::meta_data_type;
192 using edge_index_type =
typename edge_type::index_type;
193 using edge_init_type = edge_init_type_generator_t<edge_type>;
194 using edges_initializer = std::initializer_list<std::initializer_list<edge_init_type>>;
196 using size_type =
typename edge_storage_type::size_type;
197 using const_edge_iterator =
typename edge_storage_type::const_partition_iterator;
198 using const_reverse_edge_iterator =
typename edge_storage_type::const_reverse_partition_iterator;
199 using const_edges_range = std::ranges::subrange<const_edge_iterator>;
201 static_assert(std::is_unsigned_v<edge_index_type>);
203 constexpr static auto npos{std::numeric_limits<edge_index_type>::max()};
204 constexpr static graph_flavour flavour{Flavour};
208 constexpr connectivity_base(std::initializer_list<std::initializer_list<edge_init_type>> edges)
209 : m_Edges{make_edges(edges)}
213 : m_Edges{copy_edges(other)}
217 constexpr size_type order()
const noexcept {
return m_Edges.num_partitions(); }
220 constexpr size_type size()
const noexcept
222 return !is_directed(flavour) ? m_Edges.size() / 2 : m_Edges.size();
226 constexpr const_edge_iterator cbegin_edges(
const edge_index_type node)
const
228 graph_errors::check_node_index_range(
"cbegin_edges", order(), node);
230 return m_Edges.cbegin_partition(node);
234 constexpr const_edge_iterator cend_edges(
const edge_index_type node)
const
236 graph_errors::check_node_index_range(
"cend_edges", order(), node);
238 return m_Edges.cend_partition(node);
242 constexpr const_reverse_edge_iterator crbegin_edges(
const edge_index_type node)
const
244 graph_errors::check_node_index_range(
"crbegin_edges", order(), node);
246 return m_Edges.crbegin_partition(node);
250 constexpr const_reverse_edge_iterator crend_edges(
const edge_index_type node)
const
252 graph_errors::check_node_index_range(
"crend_edges", order(), node);
254 return m_Edges.crend_partition(node);
258 constexpr const_edges_range cedges(
const edge_index_type node)
const
260 graph_errors::check_node_index_range(
"cedges", order(), node);
262 return {m_Edges.cbegin_partition(node), m_Edges.cend_partition(node)};
265 template<
class... Args>
267 constexpr void set_edge_weight(const_edge_iterator citer, Args&&... args)
269 if constexpr(!shared_weight_v && !is_directed(flavour))
272 [
this] <
class... PartnerArgs> (const_edge_iterator iter, PartnerArgs&&... partnerArgs){
273 return this->set_partner_edge_weight(iter, std::forward<PartnerArgs>(partnerArgs)...);
278 [
this](edge_iterator iter, const_edge_iterator partnerIter){
279 this->set_source_edge_weight(iter, partnerIter->weight());
287 set_source_edge_weight(to_edge_iterator(citer), std::forward<Args>(args)...);
291 template<
class... Args>
293 constexpr void set_edge_weight(const_reverse_edge_iterator criter, Args&&... args)
295 set_edge_weight(to_const_edge_iterator(criter), std::forward<Args>(args)...);
298 template<std::invocable<edge_weight_type&> Fn>
299 requires (!std::is_empty_v<edge_weight_type>)
300 constexpr std::invoke_result_t<Fn, edge_weight_type&> mutate_edge_weight(const_edge_iterator citer, Fn fn)
302 if constexpr(!shared_weight_v && !is_directed(flavour))
304 mutate_partner_edge_weight(citer, fn);
307 return mutate_source_edge_weight(citer, fn);
310 template<std::invocable<edge_weight_type&> Fn>
311 requires (!std::is_empty_v<edge_weight_type>)
312 constexpr std::invoke_result_t<Fn, edge_weight_type&> mutate_edge_weight(const_reverse_edge_iterator criter, Fn fn)
314 return mutate_edge_weight(to_const_edge_iterator(criter), std::move(fn));
317 template<
class... Args>
319 constexpr void set_edge_meta_data(const_edge_iterator citer, Args&&... args)
321 to_edge_iterator(citer)->meta_data(std::forward<Args>(args)...);
324 template<
class... Args>
326 constexpr void set_edge_meta_data(const_reverse_edge_iterator criter, Args&&... args)
328 set_edge_meta_data(to_const_edge_iterator(criter), std::forward<Args>(args)...);
332 template<std::invocable<edge_meta_data_type&> Fn>
333 requires (!std::is_empty_v<edge_meta_data_type>)
334 constexpr std::invoke_result_t<Fn, edge_meta_data_type&> mutate_edge_meta_data(const_edge_iterator citer, Fn fn)
336 return to_edge_iterator(citer)->mutate_meta_data(std::move(fn));
340 template<std::invocable<edge_meta_data_type&> Fn>
341 requires (!std::is_empty_v<edge_meta_data_type>)
342 constexpr std::invoke_result_t<Fn, edge_meta_data_type&> mutate_edge_meta_data(const_reverse_edge_iterator criter, Fn fn)
344 return mutate_edge_meta_data(to_const_edge_iterator(criter), std::move(fn));
353 return lhs.m_Edges == rhs.m_Edges;
356 using edge_iterator =
typename edge_storage_type::partition_iterator;
357 using reverse_edge_iterator =
typename edge_storage_type::reverse_partition_iterator;
358 using edges_range = std::ranges::subrange<edge_iterator>;
360 template<
alloc... Allocators>
361 requires (
sizeof...(Allocators) > 0)
366 template<
alloc... Allocators>
367 requires (
sizeof...(Allocators) > 0)
369 : m_Edges{make_edges(edges, as...)}
372 template<
alloc... Allocators>
373 requires (
sizeof...(Allocators) > 0)
375 : m_Edges{copy_edges(c, as...)}
380 template<
alloc... Allocators>
382 : m_Edges{std::move(c.m_Edges), as...}
395 if constexpr(has_allocator_type_v<edge_storage_type>)
397 return in.m_Edges.get_allocator();
402 auto partitionsAllocGetter{
404 if constexpr(has_partitions_allocator<edge_storage_type>)
406 return in.m_Edges.get_partitions_allocator();
417 template<
alloc... Allocs>
418 requires ((
sizeof...(Allocs) > 0)
419 && (std::allocator_traits<Allocs>::propagate_on_container_copy_assignment::value && ...))
420 void reset(
const Allocs&... allocs)
422 m_Edges.reset(allocs...);
426 noexcept(
noexcept(std::ranges::swap(this->m_Edges, rhs.m_Edges)))
428 std::ranges::swap(m_Edges, rhs.m_Edges);
432 constexpr void swap_edges(edge_index_type node, edge_index_type i, edge_index_type j)
434 graph_errors::check_edge_swap_indices(node, i, j,
static_cast<std::size_t
>(std::ranges::distance(cedges(node))));
436 std::ranges::iter_swap(begin_edges(node) + i, begin_edges(node) + j);
440 constexpr void swap_nodes(edge_index_type i, edge_index_type j)
442 graph_errors::check_node_index_range(
"swap_nodes", order(), i, j);
447 [
this, i, j](edge_index_type n){
448 for(
auto& e : edges(n))
450 if (e.target_node() == i) e.target_node(j);
451 else if(e.target_node() == j) e.target_node(i);
456 if constexpr(!is_directed(flavour))
459 auto getTarget{[](
const auto& e){
return e.target_node(); }};
460 auto targetEdges{std::views::transform(edges(i), getTarget) | std::ranges::to<std::vector>()};
461 std::ranges::transform(edges(j), std::back_inserter(targetEdges), getTarget);
462 std::ranges::sort(targetEdges);
463 auto duplicates{std::ranges::unique(targetEdges)};
465 for(
auto iter{targetEdges.begin()}; iter != duplicates.begin(); ++iter)
472 for(edge_index_type n{}; n < static_cast<edge_index_type>(order()); ++n)
478 m_Edges.swap_partitions(i, j);
482 auto get_edge_allocator()
const
484 return m_Edges.get_allocator();
489 requires has_partitions_allocator<edge_storage_type>
491 return m_Edges.get_partitions_allocator();
494 void reserve_nodes(
const size_type size)
496 m_Edges.reserve_partitions(size);
500 size_type node_capacity()
const noexcept
502 return m_Edges.num_partitions_capacity();
505 void reserve_edges(
const edge_index_type partition,
const edge_index_type size)
506 requires graph_impl::has_reservable_partitions<edge_storage_type>
508 m_Edges.reserve_partition(partition, size);
511 void reserve_edges(
const edge_index_type size)
512 requires (!graph_impl::has_reservable_partitions<edge_storage_type>)
514 m_Edges.reserve(size);
518 size_type edges_capacity(
const edge_index_type partition)
const
519 requires graph_impl::has_reservable_partitions<edge_storage_type>
521 return m_Edges.partition_capacity(partition);
525 size_type edges_capacity()
const noexcept
526 requires (!graph_impl::has_reservable_partitions<edge_storage_type>)
528 return m_Edges.capacity();
533 m_Edges.shrink_to_fit();
538 constexpr edge_iterator begin_edges(
const edge_index_type node)
540 graph_errors::check_node_index_range(
"begin_edges", order(), node);
542 return m_Edges.begin_partition(node);
546 constexpr edge_iterator end_edges(
const edge_index_type node)
548 graph_errors::check_node_index_range(
"end_edges", order(), node);
550 return m_Edges.end_partition(node);
554 constexpr const_edge_iterator begin_edges(
const edge_index_type node)
const
556 graph_errors::check_node_index_range(
"begin_edges", order(), node);
558 return m_Edges.begin_partition(node);
562 constexpr const_edge_iterator end_edges(
const edge_index_type node)
const
564 graph_errors::check_node_index_range(
"end_edges", order(), node);
566 return m_Edges.end_partition(node);
570 constexpr reverse_edge_iterator rbegin_edges(
const edge_index_type node)
572 graph_errors::check_node_index_range(
"rbegin_edges", order(), node);
574 return m_Edges.rbegin_partition(node);
578 constexpr reverse_edge_iterator rend_edges(
const edge_index_type node)
580 graph_errors::check_node_index_range(
"rend_edges", order(), node);
582 return m_Edges.rend_partition(node);
586 constexpr const_reverse_edge_iterator rbegin_edges(
const edge_index_type node)
const
588 graph_errors::check_node_index_range(
"rbegin_edges", order(), node);
590 return m_Edges.rbegin_partition(node);
594 constexpr const_reverse_edge_iterator rend_edges(
const edge_index_type node)
const
596 graph_errors::check_node_index_range(
"rend_edges", order(), node);
598 return m_Edges.rend_partition(node);
602 constexpr edges_range edges(
const edge_index_type node) {
return {begin_edges(node), end_edges(node)}; }
609 size_type insert_node(
const size_type node)
611 m_Edges.insert_slot(node);
613 [node](
const auto targetNode) {
return targetNode >= node; },
614 [](
const auto index) {
return index + 1; }
620 void erase_node(
const size_type node)
622 graph_errors::check_node_index_range(
"erase_node", order(), node);
624 if constexpr(!is_directed(flavour))
626 std::vector<size_type> partitionsToVisit{};
628 for(
const auto& edge : m_Edges.partition(node))
630 const auto target{edge.target_node()};
631 if(target != node) partitionsToVisit.push_back(target);
634 std::ranges::sort(partitionsToVisit);
635 auto duplicates{std::ranges::unique(partitionsToVisit)};
637 for(
const auto partition : std::ranges::subrange{partitionsToVisit.begin(), duplicates.begin()})
639 auto fn{[node](
const edge_type& e) {
return e.target_node() == node; }};
641 if constexpr(edge_type::flavour == edge_flavour::partial_embedded)
644 for(
auto iter{m_Edges.begin_partition(partition)}; iter != m_Edges.end_partition(partition);)
648 iter = m_Edges.erase_from_partition(iter);
649 decrement_comp_indices(iter, m_Edges.end_partition(partition), 1);
659 erase_from_partition_if(partition, fn);
665 for(size_type i{}; i < m_Edges.num_partitions(); ++i)
667 if(i == node)
continue;
668 erase_from_partition_if(i, [node](
const edge_type& e) {
669 return (e.target_node() == node);
674 m_Edges.erase_slot(node);
676 [node](
const auto targetNode) {
return targetNode > node; },
677 [](
const auto index) {
return index - 1; }
681 template<
class... Args>
682 requires (std::is_empty_v<edge_meta_data_type>&&
initializable_from<edge_weight_type, Args...> && (is_directed(flavour) || std::is_copy_constructible_v<edge_type>))
683 void join(
const edge_index_type node1,
const edge_index_type node2, Args&&... args)
685 graph_errors::check_node_index_range(
"join", order(), node1, node2);
687 add_to_partition(node1, node2, std::forward<Args>(args)...);
689 if constexpr(!is_directed(flavour))
691 reciprocal_join(node1, node2);
695 template<
class... Args>
696 requires (!std::is_empty_v<edge_meta_data_type>&&
initializable_from<edge_weight_type, Args...> && (is_directed(flavour) || std::is_copy_constructible_v<edge_type>))
697 void join(
const edge_index_type node1,
const edge_index_type node2, edge_meta_data_type meta1, edge_meta_data_type meta2, Args&&... args)
699 graph_errors::check_node_index_range(
"join", order(), node1, node2);
701 add_to_partition(node1, node2, meta1, std::forward<Args>(args)...);
703 if constexpr(!is_directed(flavour))
705 reciprocal_join(node1, node2, std::move(meta2));
709 template<
class... Args>
710 requires (std::is_empty_v<edge_meta_data_type> &&
initializable_from<edge_weight_type, Args...>&& is_embedded(flavour) && std::is_copy_constructible_v<edge_type>)
711 std::pair<const_edge_iterator, const_edge_iterator>
712 insert_join(const_edge_iterator citer1, const_edge_iterator citer2, Args&&... args)
714 const auto node1{citer1.partition_index()}, node2{citer2.partition_index()};
715 const auto dist2{
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(node2), citer2))};
716 if(node1 == node2)
return insert_join(citer1, dist2, std::forward<Args>(args)...);
718 citer1 = insert_to_partition(citer1, node2, dist2, std::forward<Args>(args)...);
719 return insert_reciprocal_join(citer1, cbegin_edges(node2) + dist2);
722 template<
class... Args>
723 requires (!std::is_empty_v<edge_meta_data_type>,
initializable_from<edge_weight_type, Args...>&& is_embedded(flavour) && std::is_copy_constructible_v<edge_type>)
724 std::pair<const_edge_iterator, const_edge_iterator>
725 insert_join(const_edge_iterator citer1, const_edge_iterator citer2, edge_meta_data_type meta1, edge_meta_data_type meta2, Args&&... args)
727 const auto node1{citer1.partition_index()}, node2{citer2.partition_index()};
728 const auto dist2{
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(node2), citer2))};
729 if(node1 == node2)
return insert_join(citer1, dist2, meta1, meta2, std::forward<Args>(args)...);
731 citer1 = insert_to_partition(citer1, node2, dist2, meta1, std::forward<Args>(args)...);
732 return insert_reciprocal_join(citer1, cbegin_edges(node2) + dist2, meta2);
735 template<
class... Args>
736 requires (std::is_empty_v<edge_meta_data_type> &&
initializable_from<edge_weight_type, Args...>&& is_embedded(flavour) && std::is_copy_constructible_v<edge_type>)
737 std::pair<const_edge_iterator, const_edge_iterator>
738 insert_join(const_edge_iterator citer1,
const edge_index_type pos2, Args&&... args)
740 const auto node{citer1.partition_index()};
741 graph_errors::check_edge_insertion_index(
"insert_join", node, std::ranges::distance(cedges(node)) + 1, pos2);
743 citer1 = insert_to_partition(citer1, node, pos2, std::forward<Args>(args)...);
745 return insert_reciprocal_join(citer1, pos2);
748 template<
class... Args>
749 requires (!std::is_empty_v<edge_meta_data_type>,
initializable_from<edge_weight_type, Args...>&& is_embedded(flavour) && std::is_copy_constructible_v<edge_type>)
750 std::pair<const_edge_iterator, const_edge_iterator>
751 insert_join(const_edge_iterator citer1,
const edge_index_type pos2, edge_meta_data_type meta1, edge_meta_data_type meta2, Args&&... args)
753 const auto node{citer1.partition_index()};
754 graph_errors::check_edge_insertion_index(
"insert_join", node, std::ranges::distance(cedges(node)) + 1, pos2);
756 citer1 = insert_to_partition(citer1, node, pos2, meta1, std::forward<Args>(args)...);
758 return insert_reciprocal_join(citer1, pos2, meta2);
761 void erase_edge(const_edge_iterator citer)
766 if(!order() || (citer == cend_edges(citer.partition_index())))
return;
768 if constexpr (!is_directed(flavour))
770 const auto source{citer.partition_index()};
771 const auto partner{citer->target_node()};
774 [=](
const edge_type& potentialPartner){
775 if(potentialPartner.target_node() == source)
777 if constexpr(std::is_empty_v<edge_weight_type>)
779 else if constexpr(shared_weight_v)
780 return std::addressof(citer->weight()) == std::addressof(potentialPartner.weight());
781 else if constexpr(std::is_empty_v<edge_meta_data_type>)
782 return citer->weight() == potentialPartner.weight();
791 if(source != partner)
793 if constexpr (edge_type::flavour == edge_flavour::partial_embedded)
795 const auto partnerLocalIndex{citer->complementary_index()};
796 citer = m_Edges.erase_from_partition(citer);
797 decrement_comp_indices(to_edge_iterator(citer), m_Edges.end_partition(source), 1);
799 auto partnerIter{m_Edges.erase_from_partition(partner, partnerLocalIndex)};
800 decrement_comp_indices(partnerIter, m_Edges.end_partition(partner), 1);
804 auto found{std::ranges::find_if(cedges(partner), pred)};
806 if(found == cend_edges(partner))
807 throw std::logic_error{graph_errors::erase_edge_error(partner, {source,
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(source), citer))})};
809 const auto partnerDist{std::ranges::distance(cbegin_edges(partner), found)};
811 m_Edges.erase_from_partition(citer);
812 m_Edges.erase_from_partition(cbegin_edges(partner) + partnerDist);
817 if constexpr (edge_type::flavour == edge_flavour::partial_embedded)
819 const auto compIndex{citer->complementary_index()};
820 const auto pos{std::ranges::distance(cbegin_edges(source), citer)};
821 const auto separation{std::ranges::distance(citer, cbegin_edges(source) + compIndex)};
822 if((separation == 1) || (separation == -1))
824 const auto delta{separation == 1 ? 0 : -1};
825 citer = m_Edges.erase_from_partition(citer+delta, citer+(2+delta));
827 decrement_comp_indices(to_edge_iterator(citer), m_Edges.end_partition(source), 2);
831 const bool negativeSeparation{separation < 0};
834 [
this, source](
auto index){
835 auto i{m_Edges.erase_from_partition(m_Edges.begin_partition(source) + index)};
836 decrement_comp_indices(i, m_Edges.end_partition(source), 1);
840 erase(negativeSeparation ? pos : compIndex);
841 erase(negativeSeparation ? compIndex : pos);
846 auto found{std::ranges::find_if(cbegin_edges(source), citer, pred)};
847 if(found == citer) found = std::ranges::find_if(std::ranges::next(citer), cend_edges(source), pred);
849 if(found == cend_edges(source))
850 throw std::logic_error{graph_errors::erase_edge_error(source, {source,
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(source), citer))})};
852 auto dist{std::ranges::distance(cbegin_edges(source), citer)};
853 if(std::ranges::distance(cbegin_edges(source), found) < dist) --dist;
855 m_Edges.erase_from_partition(found);
856 m_Edges.erase_from_partition(cbegin_edges(source) + dist);
862 m_Edges.erase_from_partition(citer);
866 void clear()
noexcept
871 template<
class Comp,
class Proj = std::
identity>
872 constexpr void sort_edges(const_edge_iterator begin, const_edge_iterator end, Comp comp, Proj proj = {})
873 requires ((edge_type::flavour == edge_flavour::partial) && std::sortable<edge_iterator, Comp, Proj>)
875 if(begin.partition_index() != end.partition_index())
return;
877 std::ranges::sort(to_edge_iterator(begin), to_edge_iterator(end), std::move(comp), std::move(proj));
880 template<
class Comp,
class Proj = std::
identity>
881 constexpr void sort_edges(const_edges_range r, Comp comp, Proj proj = {})
882 requires ((edge_type::flavour == edge_flavour::partial) && std::sortable<edge_iterator, Comp, Proj>)
884 sort_edges(r.begin(), r.end(), std::move(comp), std::move(proj));
887 template<
class Comp,
class Proj = std::
identity>
888 constexpr void stable_sort_edges(const_edge_iterator begin, const_edge_iterator end, Comp comp, Proj proj = {})
889 requires ((edge_type::flavour == edge_flavour::partial) && merge_sortable<edge_iterator, Comp, Proj>)
891 if(begin.partition_index() != end.partition_index())
return;
893 sequoia::stable_sort(to_edge_iterator(begin), to_edge_iterator(end), std::move(comp), std::move(proj));
896 template<
class Comp,
class Proj = std::
identity>
897 constexpr void stable_sort_edges(const_edges_range r, Comp comp, Proj proj = {})
898 requires ((edge_type::flavour == edge_flavour::partial) && merge_sortable<edge_iterator, Comp, Proj>)
900 stable_sort_edges(r.begin(), r.end(), std::move(comp), std::move(proj));
904 constexpr static bool direct_init_v{std::is_same_v<edge_type, edge_init_type>};
905 constexpr static bool direct_copy_v{direct_init_v};
906 constexpr static bool shared_weight_v{graph_impl::has_shared_weight_v<edge_type>};
909 edge_storage_type m_Edges;
914 void erase_from_partition_if(
const edge_index_type partitionIndex, Pred pred)
916 auto discarded{std::ranges::remove_if(m_Edges.begin_partition(partitionIndex), m_Edges.end_partition(partitionIndex), pred)};
917 m_Edges.erase_from_partition(discarded.begin(), discarded.end());
920 template<
alloc... Allocators>
922 constexpr edge_storage_type make_edges(edges_initializer edges,
const Allocators&... as)
924 return process_edges(validate(preprocess(edges)), as...);
928 constexpr static const edges_initializer& preprocess(
const edges_initializer& edges)
929 requires (is_embedded(flavour) || is_directed(flavour))
935 constexpr static edge_storage_type preprocess(edges_initializer edges)
936 requires (direct_init_v && !is_embedded(flavour) && !is_directed(flavour))
938 return preprocess(edge_storage_type{edges});
942 constexpr static auto preprocess(edges_initializer edges)
943 requires (!direct_init_v && !is_embedded(flavour) && !is_directed(flavour))
945 using namespace data_structures;
948 return preprocess(sequence_t{edges});
951 template<
class IntermediateEdges>
953 constexpr static IntermediateEdges preprocess(IntermediateEdges edges)
954 requires (!is_embedded(flavour) && !is_directed(flavour))
956 constexpr bool clusterEdges{!std::is_empty_v<edge_weight_type> && !std::totally_ordered<edge_weight_type>};
958 for(edge_index_type i{}; i < edges.num_partitions(); ++i)
962 if constexpr(clusterEdges)
964 auto lowerIter{edges.begin_partition(i)}, upperIter{edges.begin_partition(i)};
965 while(lowerIter != edges.end_partition(i))
967 upperIter = std::ranges::find_if_not(upperIter, edges.end_partition(i), [target{lowerIter->target_node()}](
const auto& wt) { return target == wt.target_node(); });
970 return e1.weight() == e2.weight();
973 lowerIter = upperIter;
982 constexpr static const edges_initializer& validate(
const edges_initializer& edges)
983 requires (is_embedded(flavour))
985 for(
auto nodeEdgesIter{edges.begin()}; nodeEdgesIter != edges.end(); ++nodeEdgesIter)
987 const auto nodeIndex{
static_cast<std::size_t
>(std::ranges::distance(edges.begin(), nodeEdgesIter))};
988 const auto& nodeEdges{*nodeEdgesIter};
989 for(
auto edgeIter{nodeEdges.begin()}; edgeIter != nodeEdges.end(); ++edgeIter)
991 const auto edgeIndex{
static_cast<std::size_t
>(std::ranges::distance(nodeEdges.begin(), edgeIter))};
992 const auto& edge{*edgeIter};
993 const auto target{edge.target_node()};
995 graph_errors::check_edge_index_range(
"process_complementary_edges", {nodeIndex, edgeIndex},
"target", edges.size(), target);
996 const auto compIndex{edge.complementary_index()};
998 const bool doValidate{
1000 if constexpr(!is_directed(flavour))
return true;
1001 else return (edge.target_node() != nodeIndex) || (edge.source_node() == edge.target_node());
1007 auto targetEdgesIter{edges.begin() + target};
1008 graph_errors::check_edge_index_range(
"process_complementary_edges", {nodeIndex, edgeIndex},
"complementary", targetEdgesIter->size(), compIndex);
1010 if((target == nodeIndex) && (compIndex == edgeIndex))
1012 throw std::logic_error{graph_errors::self_referential_error({nodeIndex, edgeIndex}, target, compIndex)};
1014 else if(
const auto& targetEdge{*(targetEdgesIter->begin() + compIndex)}; targetEdge.complementary_index() != edgeIndex)
1016 throw std::logic_error{graph_errors::reciprocated_error_message({nodeIndex, edgeIndex},
"complementary", targetEdge.complementary_index(), edgeIndex)};
1020 if constexpr(!is_directed(flavour))
1022 graph_errors::check_reciprocated_index({nodeIndex, edgeIndex},
"target", targetEdge.target_node(), nodeIndex);
1026 graph_errors::check_reciprocated_index({nodeIndex, edgeIndex},
"target", targetEdge.target_node(), target);
1027 graph_errors::check_reciprocated_index({nodeIndex, edgeIndex},
"source", targetEdge.source_node(), edge.source_node());
1029 if constexpr(is_embedded(flavour))
1031 graph_errors::check_inversion_consistency(nodeIndex, {edgeIndex, edge.inverted()}, {compIndex, targetEdge.inverted()});
1035 if constexpr(!std::is_empty_v<edge_weight_type>)
1037 if(edge.weight() != targetEdge.weight())
1038 throw std::logic_error{graph_errors::mismatched_weights_message(
"process_complementary_edges", {nodeIndex, edgeIndex})};
1043 if constexpr(is_directed(flavour))
1045 const auto source{edge.source_node()};
1046 graph_errors::check_edge_index_range(
"process_complementary_edges", {nodeIndex, edgeIndex},
"source", edges.size(), source);
1047 graph_errors::check_embedded_edge(nodeIndex, source, target);
1049 if((edge.target_node() == nodeIndex) && (edge.source_node() != edge.target_node()))
1051 auto sourceEdgesIter{edges.begin() + source};
1052 graph_errors::check_edge_index_range(
"process_complementary_edges", {nodeIndex, edgeIndex},
"complementary", sourceEdgesIter->size(), compIndex);
1054 const auto& sourceEdge{*(sourceEdgesIter->begin() + compIndex)};
1055 graph_errors::check_reciprocated_index({nodeIndex, edgeIndex},
"target", sourceEdge.target_node(), nodeIndex);
1065 constexpr static const edges_initializer& validate(
const edges_initializer& edges)
1066 requires (!is_embedded(flavour) && is_directed(flavour))
1068 for(
auto nodeEdgesIter{edges.begin()}; nodeEdgesIter != edges.end(); ++nodeEdgesIter)
1070 const auto nodeIndex{
static_cast<std::size_t
>(std::ranges::distance(edges.begin(), nodeEdgesIter))};
1071 const auto& nodeEdges{*nodeEdgesIter};
1072 for(
auto edgeIter{nodeEdges.begin()}; edgeIter != nodeEdges.end(); ++edgeIter)
1074 const auto edgeIndex{
static_cast<std::size_t
>(std::ranges::distance(nodeEdges.begin(), edgeIter))};
1075 const auto& edge{*edgeIter};
1077 const auto target{edge.target_node()};
1078 graph_errors::check_edge_index_range(
"process_edges", {nodeIndex, edgeIndex},
"target", edges.size(), target);
1086 template<
class Edges>
1087 using partition_iterator_range = std::ranges::subrange<typename Edges::const_partition_iterator>;
1089 template<
class IntermediateEdges>
1091 constexpr static decltype(
auto) validate(IntermediateEdges&& edges)
1092 requires (!is_embedded(flavour) && !is_directed(flavour))
1094 using range_t = partition_iterator_range<IntermediateEdges>;
1095 using namespace graph_errors;
1100 [](edge_index_type i, range_t hostRange) {
1101 if(std::ranges::distance(hostRange) % 2)
throw std::logic_error{odd_num_loops_error(
"connectivity_base", i)};
1103 [&](edge_index_type i, edge_index_type target, range_t hostRange, range_t targetRange) {
1104 const auto brokenEdge{
1105 [&, i, target, hostRange, targetRange]() -> std::optional<edge_indices> {
1106 if(
auto reciprocalCount{std::ranges::distance(targetRange)}; !reciprocalCount)
1108 return edge_indices{i,
static_cast<std::size_t
>(std::ranges::distance(edges.cbegin_partition(i), hostRange.begin()))};
1110 else if(
auto count{std::ranges::distance(hostRange)}; count > reciprocalCount)
1112 return edge_indices{i,
static_cast<std::size_t
>(std::ranges::distance(edges.cbegin_partition(i), hostRange.begin()) + reciprocalCount)};
1114 else if(count < reciprocalCount)
1116 return edge_indices{target,
static_cast<std::size_t
>(std::ranges::distance(edges.cbegin_partition(target), targetRange.begin()) + count)};
1124 throw std::logic_error{absent_reciprocated_partial_edge_message(
"connectivity_base", brokenEdge.value())};
1128 return std::forward<IntermediateEdges>(edges);
1131 template<std::ranges::view View>
1132 constexpr static auto find_cluster_end(View v) ->
decltype(v.begin())
1134 return !v.empty() ? std::ranges::find_if_not(v, [&front{v.front()}](
const auto& e){
return e == front; }) : v.end();
1137 template<
class Edges,
alloc... Allocators>
1139 constexpr edge_storage_type process_edges(Edges&& orderedEdges,
const Allocators&... as)
1140 requires direct_init_v
1142 return {std::forward<Edges>(orderedEdges), as...};
1145 template<
alloc... Allocators>
1147 constexpr edge_storage_type process_edges(edges_initializer edges,
const Allocators&... as)
1148 requires (!direct_init_v && !is_embedded(flavour))
1150 edge_storage_type storage(as...);
1151 storage.reserve_partitions(edges.size());
1153 for(
auto nodeEdgesIter{edges.begin()}; nodeEdgesIter != edges.end(); ++nodeEdgesIter)
1157 const auto nodeIndex{
static_cast<edge_index_type
>(std::ranges::distance(edges.begin(), nodeEdgesIter))};
1158 const auto& nodeEdges{*nodeEdgesIter};
1159 for(
const auto& edge : nodeEdges)
1161 storage.push_back_to_partition(nodeIndex, edge.target_node(), edge.weight());
1168 template<
alloc... Allocators>
1170 constexpr edge_storage_type process_edges(edges_initializer edges,
const Allocators&... as)
1171 requires (!direct_init_v && is_embedded(flavour))
1173 edge_storage_type storage(as...);
1174 storage.reserve_partitions(edges.size());
1176 for(
auto nodeEdgesIter{edges.begin()}; nodeEdgesIter != edges.end(); ++nodeEdgesIter)
1180 const auto nodeIndex{
static_cast<edge_index_type
>(std::ranges::distance(edges.begin(), nodeEdgesIter))};
1181 const auto& nodeEdges{*nodeEdgesIter};
1182 for(
const edge_init_type& edge : nodeEdges)
1184 storage.push_back_to_partition(nodeIndex, edge_type{edge});
1191 template<
class Edges,
alloc... Allocators>
1193 constexpr edge_storage_type process_edges(
const Edges& orderedEdges,
const Allocators&... as)
1194 requires (!direct_init_v && !is_embedded(flavour) && !is_directed(flavour))
1196 using range_t = partition_iterator_range<Edges>;
1198 edge_storage_type storage(as...);
1199 storage.reserve_partitions(orderedEdges.num_partitions());
1202 [&storage,
this](edge_index_type host, edge_index_type target, edge_index_type compIndex, range_t hostRange){
1203 storage.push_back_to_partition(host, (compIndex == npos) ? edge_type{hostRange.front()}
1204 : edge_type{target, *(cbegin_edges(target) + compIndex)});
1210 [&storage]() { storage.add_slot(); },
1211 [addToStorage,&orderedEdges](edge_index_type host, range_t hostRange) {
1212 for(; hostRange.begin() != hostRange.end(); hostRange.advance(1))
1214 const bool hasCompIndex{(std::ranges::distance(hostRange) % 2) && shared_weight_v};
1215 const auto compIndex{hasCompIndex ?
static_cast<edge_index_type
>(std::ranges::distance(orderedEdges.cbegin_partition(host), hostRange.begin()) - 1) : npos};
1217 addToStorage(host, host, compIndex, hostRange);
1220 [addToStorage,&orderedEdges](edge_index_type host, edge_index_type target, range_t hostRange, range_t targetRange) {
1221 for(; hostRange.begin() != hostRange.end(); hostRange.advance(1))
1223 const bool hasCompIndex{(host > target) && shared_weight_v};
1224 const auto compIndex{hasCompIndex ?
static_cast<edge_index_type
>(std::ranges::distance(orderedEdges.cbegin_partition(target), targetRange.end()) - std::ranges::distance(hostRange)) : npos};
1226 addToStorage(host, target, compIndex, hostRange);
1234 template<
class Edges,
1235 std::invocable PerNodeFn,
1236 std::invocable<edge_index_type, partition_iterator_range<Edges>> PerLoopFn,
1237 std::invocable<edge_index_type, edge_index_type, partition_iterator_range<Edges>, partition_iterator_range<Edges>> PerLinkFn
1239 constexpr static void visit_edges(
const Edges& orderedEdges, PerNodeFn perNode, PerLoopFn perLoop, PerLinkFn perLink)
1240 requires (!is_embedded(flavour) && !is_directed(flavour))
1242 constexpr bool clusterEdges{!std::is_empty_v<edge_weight_type> && !std::totally_ordered<edge_weight_type>};
1244 for(edge_index_type i{}; i < orderedEdges.num_partitions(); ++i)
1248 auto lowerIter{orderedEdges.cbegin_partition(i)}, upperIter{orderedEdges.cbegin_partition(i)};
1249 while(lowerIter != orderedEdges.cend_partition(i))
1251 upperIter = std::ranges::upper_bound(lowerIter, orderedEdges.cend_partition(i), *lowerIter,
graph_impl::edge_comparer{});
1252 if constexpr(clusterEdges) upperIter = find_cluster_end(std::ranges::subrange{lowerIter, upperIter});
1254 if(
const auto target{lowerIter->target_node()}; target == i)
1256 perLoop(i, {lowerIter, upperIter});
1260 graph_errors::check_edge_index_range(
"", {i,
static_cast<std::size_t
>(std::ranges::distance(orderedEdges.cbegin_partition(i), lowerIter))},
"target", orderedEdges.num_partitions(), lowerIter->target_node());
1262 const auto comparisonEdge{
1264 auto compEdge{*lowerIter};
1265 compEdge.target_node(i);
1272 if constexpr(clusterEdges)
1274 auto clusters{std::views::drop_while(eqrange, [&wt{lowerIter->weight()}](
const auto& e) {
return e.weight() != wt; })};
1275 eqrange = {clusters.begin(), find_cluster_end(clusters)};
1278 perLink(i, target, {lowerIter, upperIter}, eqrange);
1281 lowerIter = upperIter;
1286 template<
alloc... Allocators>
1288 edge_storage_type copy_edges(
const connectivity_base& in,
const Allocators&... as)
1289 requires direct_copy_v
1291 return edge_storage_type{in.m_Edges, as...};
1296 requires (!direct_copy_v)
1298 if constexpr(has_partitions_allocator<edge_storage_type>)
1300 return copy_edges(in, in.m_Edges.get_allocator(), in.m_Edges.get_partitions_allocator());
1304 return copy_edges(in, in.m_Edges.get_allocator());
1308 template<
alloc... Allocators>
1310 edge_storage_type copy_edges(
const connectivity_base& in,
const Allocators&... as)
1311 requires (!direct_copy_v && (
sizeof...(Allocators) > 0))
1313 edge_storage_type storage({std::allocator_traits<Allocators>::select_on_container_copy_construction(as)}...);
1315 if constexpr(edge_type::flavour == edge_flavour::partial)
1319 else if constexpr(edge_type::flavour == edge_flavour::partial_embedded)
1322 [
this, &storage](
const size_type node, const_edge_iterator first, const_edge_iterator i) {
1323 const auto dist{
static_cast<edge_index_type
>(std::ranges::distance(first, i))};
1324 const bool encountered{(i->target_node() < node)
1325 || ((i->target_node() == node) && (i->complementary_index() < dist))};
1329 storage.push_back_to_partition(node, i->target_node(), i->complementary_index(), i->weight());
1333 const auto compI{i->complementary_index()};
1334 storage.push_back_to_partition(node, i->target_node(), compI, *(cbegin_edges(i->target_node()) + compI));
1339 copy_edges(in, storage, processor);
1345 template<std::invocable<
size_type, const_edge_iterator, const_edge_iterator> Processor>
1346 void copy_edges(
const connectivity_base& in, edge_storage_type& storage, Processor processor)
1348 reserve_nodes(in.m_Edges.num_partitions());
1349 if constexpr(!graph_impl::has_reservable_partitions<edge_storage_type>)
1351 storage.reserve(in.m_Edges.size());
1353 for(size_type i{}; i<in.order(); ++i)
1356 if constexpr(graph_impl::has_reservable_partitions<edge_storage_type>)
1358 storage.reserve_partition(i, std::ranges::distance(in.cbegin_edges(i), in.cend_edges(i)));
1360 for(
auto inIter{in.cbegin_edges(i)}; inIter != in.cend_edges(i); ++inIter)
1362 processor(i, in.cbegin_edges(i), inIter);
1368 constexpr const_edge_iterator to_const_edge_iterator(const_reverse_edge_iterator criter)
const
1370 const auto source{criter.partition_index()};
1371 return m_Edges.cbegin_partition(source) + std::ranges::distance(criter, m_Edges.crend_partition(source)) - 1;
1375 constexpr edge_iterator to_edge_iterator(const_edge_iterator citer)
1377 const auto source{citer.partition_index()};
1378 const auto dist{std::ranges::distance(cbegin_edges(source), citer)};
1379 return m_Edges.begin_partition(source) + dist;
1382 template<std::invocable<edge_weight_type&> Fn>
1383 constexpr std::invoke_result_t<Fn, edge_weight_type&> mutate_source_edge_weight(const_edge_iterator citer, Fn fn)
1385 return fn(to_edge_iterator(citer)->weight());
1388 template<
class... Args>
1389 constexpr void set_source_edge_weight(edge_iterator iter, Args&&... args)
1391 iter->weight(std::forward<Args>(args)...);
1394 template<
class Setter>
1395 requires std::is_invocable_r_v<edge_iterator, Setter, edge_iterator>
1396 constexpr const_edge_iterator manipulate_partner_edge_weight(const_edge_iterator citer, Setter setter)
1398 const auto partner{citer->target_node()};
1400 if constexpr(edge_type::flavour == edge_flavour::partial_embedded)
1402 const auto comp{citer->complementary_index()};
1403 return setter(m_Edges.begin_partition(partner) + comp);
1408 auto found{end_edges(partner)};
1409 if(
const auto source{citer.partition_index()}; source == partner)
1411 for(
auto iter{m_Edges.begin_partition(partner)}; iter != m_Edges.end_partition(partner); ++iter)
1413 if(std::ranges::distance(m_Edges.begin_partition(partner), iter) == std::ranges::distance(cbegin_edges(partner), citer))
1418 found = setter(iter);
1425 for(
auto iter{m_Edges.begin_partition(partner)}; iter != m_Edges.end_partition(partner); ++iter)
1427 if((iter->target_node() == source) && (iter->weight() == citer->weight()))
1429 found = setter(iter);
1435 if(found != end_edges(partner))
1441 const auto node{citer.partition_index()};
1442 const auto edgeIndex{
static_cast<std::size_t
>(std::ranges::distance(cbegin_edges(node), citer))};
1443 throw std::logic_error{ graph_errors::absent_partner_weight_message(
"manipulate_partner_edge_weight", {node, edgeIndex}) };
1448 template<std::invocable<edge_weight_type&> Fn>
1449 constexpr void mutate_partner_edge_weight(const_edge_iterator citer, Fn fn)
1451 manipulate_partner_edge_weight(citer, [fn](edge_iterator iter) -> edge_iterator { fn(iter->weight());
return iter; });
1454 template<
class... Args>
1456 constexpr const_edge_iterator set_partner_edge_weight(const_edge_iterator citer, Args&&... args)
1458 return manipulate_partner_edge_weight(citer, [&args...](edge_iterator iter) -> edge_iterator { iter->weight(std::forward<Args>(args)...); return iter; });
1461 template<
class... MetaData>
1462 requires std::is_copy_constructible_v<edge_type> && (std::is_same_v<MetaData, edge_meta_data_type> && ...)
1463 void reciprocal_join(
const edge_index_type node1,
const edge_index_type node2, MetaData... md)
1466 if constexpr(edge_type::flavour == edge_flavour::partial)
1468 m_Edges.push_back_to_partition(node2, node1, std::move(md)..., *crbegin_edges(node1));
1470 else if constexpr(edge_type::flavour == edge_flavour::partial_embedded)
1472 const edge_index_type compIndex(std::ranges::distance(cbegin_edges(node1), cend_edges(node1)) - 1);
1473 m_Edges.push_back_to_partition(node2, node1, compIndex, std::move(md)..., *crbegin_edges(node1));
1478 template<
class... MetaData>
1479 requires (is_embedded(flavour) && std::is_copy_constructible_v<edge_type> && (std::is_same_v<edge_meta_data_type, MetaData> && ...))
1480 std::pair<const_edge_iterator, const_edge_iterator>
1481 insert_reciprocal_join(const_edge_iterator citer1,
const edge_index_type pos2, MetaData... args)
1483 const auto node{citer1.partition_index()};
1485 auto pos1{
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(node), citer1))};
1486 if(pos2 <= pos1) ++pos1;
1488 const auto dist1{
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(node), citer1))};
1491 auto citer2{m_Edges.insert_to_partition(cbegin_edges(node) + pos2, node, pos1, std::move(args)..., *citer1)};
1494 increment_comp_indices(++to_edge_iterator(citer2), end_edges(node), 1);
1498 auto iter1{begin_edges(node) + pos1};
1499 increment_comp_indices(++to_edge_iterator(citer2), iter1, 1);
1500 increment_comp_indices(iter1 + 1, end_edges(node), 1);
1503 citer1 = cbegin_edges(node) + pos1;
1504 return {citer1, citer2};
1507 template<
class... MetaData>
1508 requires (is_embedded(flavour) && std::is_copy_constructible_v<edge_type> && (std::is_same_v<edge_meta_data_type, MetaData> && ...))
1509 std::pair<const_edge_iterator, const_edge_iterator>
1510 insert_reciprocal_join(const_edge_iterator citer1, const_edge_iterator citer2, MetaData... md)
1512 const auto node1{citer1.partition_index()}, node2{citer2.partition_index()};
1513 const auto dist1{
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(node1), citer1))};
1516 citer2 = m_Edges.insert_to_partition(citer2, node1, dist1, md..., *citer1);
1517 increment_comp_indices(++to_edge_iterator(citer2), end_edges(node2), 1);
1519 citer1 = cbegin_edges(node1) + dist1;
1520 return {citer1, citer2};
1523 template<
class... Args>
1524 void add_to_partition(
const edge_index_type node1,
const edge_index_type node2, Args&&... args)
1526 if constexpr (edge_type::flavour == edge_flavour::partial)
1528 m_Edges.push_back_to_partition(node1, node2, std::forward<Args>(args)...);
1530 else if constexpr (edge_type::flavour == edge_flavour::partial_embedded)
1532 const edge_index_type dist(std::ranges::distance(cbegin_edges(node2), cend_edges(node2)));
1533 const edge_index_type i{ node1 == node2 ? dist + 1 : dist };
1534 m_Edges.push_back_to_partition(node1, node2, i, std::forward<Args>(args)...);
1538 template<
class... Args>
1539 const_edge_iterator insert_to_partition(const_edge_iterator citer1,
const edge_index_type node2,
const edge_index_type pos2, Args&&... args)
1541 const auto node1{ citer1.partition_index() };
1542 citer1 = m_Edges.insert_to_partition(citer1, node2, pos2, std::forward<Args>(args)...);
1543 increment_comp_indices(++to_edge_iterator(citer1), end_edges(node1), 1);
1548 template<std::input_or_output_iterator Iter, std::sentinel_for<Iter> Sentinel,
class Fn>
1549 requires std::is_invocable_r_v<edge_index_type, Fn, edge_index_type>
1550 constexpr void modify_comp_indices(Iter first, Sentinel last, Fn fn)
noexcept
1552 while(first != last)
1554 const auto source{first.partition_index()};
1555 const auto other{first->target_node()};
1557 auto setPartnerCompIndex{
1558 [
this, other, fn](edge_index_type cmpIndex){
1559 const auto edgeIter{m_Edges.begin_partition(other) + cmpIndex};
1560 edgeIter->complementary_index(fn(edgeIter->complementary_index()));
1564 const auto compIndex{first->complementary_index()};
1567 setPartnerCompIndex(compIndex);
1571 const auto dist{
static_cast<edge_index_type
>(std::ranges::distance(begin_edges(source), first))};
1572 if(
const auto shiftedCompIndex{fn(compIndex)}; shiftedCompIndex <= dist)
1574 setPartnerCompIndex(compIndex);
1578 setPartnerCompIndex(shiftedCompIndex);
1586 template<std::input_or_output_iterator Iter, std::sentinel_for<Iter> Sentinel>
1587 constexpr void decrement_comp_indices(Iter first, Sentinel last,
const edge_index_type num)
noexcept
1589 modify_comp_indices(first, last, [num](
const auto compIndex) {
return compIndex >= num ? compIndex - num : edge_index_type{}; });
1592 template<std::input_or_output_iterator Iter, std::sentinel_for<Iter> Sentinel>
1593 constexpr void increment_comp_indices(Iter first, Sentinel last,
const edge_index_type num)
noexcept
1595 modify_comp_indices(first, last, [num](
const auto compIndex) {
return compIndex + num; });
1598 template<
class Pred,
class Modifier>
1599 constexpr void fix_edge_data(Pred pred, Modifier modifier)
noexcept
1601 for(size_type i{}; i < m_Edges.num_partitions(); ++i)
1603 for(
auto& edge : m_Edges.partition(i))
1605 const auto target{edge.target_node()};
1606 if(pred(target)) edge.target_node(modifier(target));
1612 template<std::input_or_output_iterator Iterator>
1616 constexpr static bool is_const{is_const_reference_v<typename std::iterator_traits<Iterator>::reference>};
1618 using value_type =
typename std::iterator_traits<Iterator>::value_type::weight_type;
1619 using reference = std::conditional_t<is_const, const value_type&, value_type&>;
1628 static reference get(Iterator i)
1641 template<graph_flavour Flavour,
class EdgeStorage>
1648 template<
class EdgeStorage>
1649 requires (!std::is_empty_v<typename EdgeStorage::value_type::weight_type>)
1659 using edge_weights_range = std::ranges::subrange<edge_weight_iterator>;
1660 using const_edge_weights_range = std::ranges::subrange<const_edge_weight_iterator>;
1662 using edge_index_type =
typename base_type::edge_index_type;
1737 constexpr edge_weights_range edge_weights(
const edge_index_type node) {
return {begin_edge_weights(node), end_edge_weights(node)}; }
1740 constexpr const_edge_weights_range edge_weights(
const edge_index_type node)
const {
return {begin_edge_weights(node), end_edge_weights(node)}; }
1743 constexpr const_edge_weights_range cedge_weights(
const edge_index_type node)
const {
return {begin_edge_weights(node), end_edge_weights(node)}; }
1745 using connectivity_base<graph_flavour::directed, EdgeStorage>::connectivity_base;
A collection of constexpr algorithms.
constexpr void cluster(Iter begin, Iter end, Comp comp={}, Proj proj={})
An algorithm which clusters together elements which compare equal.
Definition: Algorithms.hpp:144
Helper for dealing with allocator propagation during copy assignment.
Traits for data structures.
Meta-programming elements for graph implementation.
Error messages for graphs.
Traits and Concepts for graphs.
Traits and Concepts for Sharing Policies.
Traits which are sufficiently general to appear in the sequoia namespace.
Definition: PartitionedData.hpp:991
Graph connectivity_base, used as a building block for concrete graphs.
Definition: Connectivity.hpp:184
Definition: Connectivity.hpp:1643
Definition: Connectivity.hpp:1614
Definition: Connectivity.hpp:52
Definition: Connectivity.hpp:127
Definition: Connectivity.hpp:96
Definition: TestLogger.hpp:277
An iterator with policies controlling dereferencing and auxiliary data.
Definition: Iterator.hpp:234
A concept for allocators.
Definition: Concepts.hpp:48
Concept to work around the fact that currently the stl typically underconstrains operator==.
Definition: Concepts.hpp:105
A concept similar to std::constructible_from, but which considers braced-init.
Definition: Concepts.hpp:75
Helper class to assist with copy assignment for allocator-aware types.
Definition: AssignmentUtilities.hpp:67
static constexpr void assign(T &to, const T &from, AllocGetters... allocGetters)
Definition: AssignmentUtilities.hpp:70
Standard meta-programming utility.
Definition: TypeTraits.hpp:27
Definition: Connectivity.hpp:152
Definition: Connectivity.hpp:172