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](const_edge_iterator iter,
auto&&... args){
273 return this->set_partner_edge_weight(iter, std::forward<
decltype(args)>(args)...);
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 [](
const auto targetNode,
const auto node) {
return targetNode >= node; },
614 [](
const auto index) {
return index + 1; });
619 void erase_node(
const size_type node)
621 graph_errors::check_node_index_range(
"erase_node", order(), node);
623 if constexpr(!is_directed(flavour))
625 std::vector<size_type> partitionsToVisit{};
627 for(
const auto& edge : m_Edges.partition(node))
629 const auto target{edge.target_node()};
630 if(target != node) partitionsToVisit.push_back(target);
633 std::ranges::sort(partitionsToVisit);
634 auto duplicates{std::ranges::unique(partitionsToVisit)};
636 for(
const auto partition : std::ranges::subrange{partitionsToVisit.begin(), duplicates.begin()})
638 auto fn{[node](
const edge_type& e) {
return e.target_node() == node; }};
640 if constexpr(edge_type::flavour == edge_flavour::partial_embedded)
643 for(
auto iter{m_Edges.begin_partition(partition)}; iter != m_Edges.end_partition(partition);)
647 iter = m_Edges.erase_from_partition(iter);
648 decrement_comp_indices(iter, m_Edges.end_partition(partition), 1);
658 erase_from_partition_if(partition, fn);
664 for(size_type i{}; i < m_Edges.num_partitions(); ++i)
666 if(i == node)
continue;
667 erase_from_partition_if(i, [node](
const edge_type& e) {
668 return (e.target_node() == node);
673 m_Edges.erase_slot(node);
675 [](
const auto targetNode,
const auto node) {
return targetNode > node; },
676 [](
const auto index) {
return index - 1; });
679 template<
class... Args>
680 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>))
681 void join(
const edge_index_type node1,
const edge_index_type node2, Args&&... args)
683 graph_errors::check_node_index_range(
"join", order(), node1, node2);
685 add_to_partition(node1, node2, std::forward<Args>(args)...);
687 if constexpr(!is_directed(flavour))
689 reciprocal_join(node1, node2);
693 template<
class... Args>
694 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>))
695 void join(
const edge_index_type node1,
const edge_index_type node2, edge_meta_data_type meta1, edge_meta_data_type meta2, Args&&... args)
697 graph_errors::check_node_index_range(
"join", order(), node1, node2);
699 add_to_partition(node1, node2, meta1, std::forward<Args>(args)...);
701 if constexpr(!is_directed(flavour))
703 reciprocal_join(node1, node2, std::move(meta2));
707 template<
class... Args>
708 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>)
709 std::pair<const_edge_iterator, const_edge_iterator>
710 insert_join(const_edge_iterator citer1, const_edge_iterator citer2, Args&&... args)
712 const auto node1{citer1.partition_index()}, node2{citer2.partition_index()};
713 const auto dist2{
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(node2), citer2))};
714 if(node1 == node2)
return insert_join(citer1, dist2, std::forward<Args>(args)...);
716 citer1 = insert_to_partition(citer1, node2, dist2, std::forward<Args>(args)...);
717 return insert_reciprocal_join(citer1, cbegin_edges(node2) + dist2);
720 template<
class... Args>
721 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>)
722 std::pair<const_edge_iterator, const_edge_iterator>
723 insert_join(const_edge_iterator citer1, const_edge_iterator citer2, edge_meta_data_type meta1, edge_meta_data_type meta2, Args&&... args)
725 const auto node1{citer1.partition_index()}, node2{citer2.partition_index()};
726 const auto dist2{
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(node2), citer2))};
727 if(node1 == node2)
return insert_join(citer1, dist2, meta1, meta2, std::forward<Args>(args)...);
729 citer1 = insert_to_partition(citer1, node2, dist2, meta1, std::forward<Args>(args)...);
730 return insert_reciprocal_join(citer1, cbegin_edges(node2) + dist2, meta2);
733 template<
class... Args>
734 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>)
735 std::pair<const_edge_iterator, const_edge_iterator>
736 insert_join(const_edge_iterator citer1,
const edge_index_type pos2, Args&&... args)
738 const auto node{citer1.partition_index()};
739 graph_errors::check_edge_insertion_index(
"insert_join", node, std::ranges::distance(cedges(node)) + 1, pos2);
741 citer1 = insert_to_partition(citer1, node, pos2, std::forward<Args>(args)...);
743 return insert_reciprocal_join(citer1, pos2);
746 template<
class... Args>
747 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>)
748 std::pair<const_edge_iterator, const_edge_iterator>
749 insert_join(const_edge_iterator citer1,
const edge_index_type pos2, edge_meta_data_type meta1, edge_meta_data_type meta2, Args&&... args)
751 const auto node{citer1.partition_index()};
752 graph_errors::check_edge_insertion_index(
"insert_join", node, std::ranges::distance(cedges(node)) + 1, pos2);
754 citer1 = insert_to_partition(citer1, node, pos2, meta1, std::forward<Args>(args)...);
756 return insert_reciprocal_join(citer1, pos2, meta2);
759 void erase_edge(const_edge_iterator citer)
764 if(!order() || (citer == cend_edges(citer.partition_index())))
return;
766 if constexpr (!is_directed(flavour))
768 const auto source{citer.partition_index()};
769 const auto partner{citer->target_node()};
772 [=](
const edge_type& potentialPartner){
773 if(potentialPartner.target_node() == source)
775 if constexpr(std::is_empty_v<edge_weight_type>)
777 else if constexpr(shared_weight_v)
778 return std::addressof(citer->weight()) == std::addressof(potentialPartner.weight());
779 else if constexpr(std::is_empty_v<edge_meta_data_type>)
780 return citer->weight() == potentialPartner.weight();
789 if(source != partner)
791 if constexpr (edge_type::flavour == edge_flavour::partial_embedded)
793 const auto partnerLocalIndex{citer->complementary_index()};
794 citer = m_Edges.erase_from_partition(citer);
795 decrement_comp_indices(to_edge_iterator(citer), m_Edges.end_partition(source), 1);
797 auto partnerIter{m_Edges.erase_from_partition(partner, partnerLocalIndex)};
798 decrement_comp_indices(partnerIter, m_Edges.end_partition(partner), 1);
802 auto found{std::ranges::find_if(cedges(partner), pred)};
804 if(found == cend_edges(partner))
805 throw std::logic_error{graph_errors::erase_edge_error(partner, {source,
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(source), citer))})};
807 const auto partnerDist{std::ranges::distance(cbegin_edges(partner), found)};
809 m_Edges.erase_from_partition(citer);
810 m_Edges.erase_from_partition(cbegin_edges(partner) + partnerDist);
815 if constexpr (edge_type::flavour == edge_flavour::partial_embedded)
817 const auto compIndex{citer->complementary_index()};
818 const auto pos{std::ranges::distance(cbegin_edges(source), citer)};
819 const auto separation{std::ranges::distance(citer, cbegin_edges(source) + compIndex)};
820 if((separation == 1) || (separation == -1))
822 const auto delta{separation == 1 ? 0 : -1};
823 citer = m_Edges.erase_from_partition(citer+delta, citer+(2+delta));
825 decrement_comp_indices(to_edge_iterator(citer), m_Edges.end_partition(source), 2);
829 const bool negativeSeparation{separation < 0};
832 [
this, source](
auto index){
833 auto i{m_Edges.erase_from_partition(m_Edges.begin_partition(source) + index)};
834 decrement_comp_indices(i, m_Edges.end_partition(source), 1);
838 erase(negativeSeparation ? pos : compIndex);
839 erase(negativeSeparation ? compIndex : pos);
844 auto found{std::ranges::find_if(cbegin_edges(source), citer, pred)};
845 if(found == citer) found = std::ranges::find_if(std::ranges::next(citer), cend_edges(source), pred);
847 if(found == cend_edges(source))
848 throw std::logic_error{graph_errors::erase_edge_error(source, {source,
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(source), citer))})};
850 auto dist{std::ranges::distance(cbegin_edges(source), citer)};
851 if(std::ranges::distance(cbegin_edges(source), found) < dist) --dist;
853 m_Edges.erase_from_partition(found);
854 m_Edges.erase_from_partition(cbegin_edges(source) + dist);
860 m_Edges.erase_from_partition(citer);
864 void clear()
noexcept
869 template<
class Comp,
class Proj = std::
identity>
870 constexpr void sort_edges(const_edge_iterator begin, const_edge_iterator end, Comp comp, Proj proj = {})
871 requires ((edge_type::flavour == edge_flavour::partial) && std::sortable<edge_iterator, Comp, Proj>)
873 if(begin.partition_index() != end.partition_index())
return;
875 std::ranges::sort(to_edge_iterator(begin), to_edge_iterator(end), std::move(comp), std::move(proj));
878 template<
class Comp,
class Proj = std::
identity>
879 constexpr void sort_edges(const_edges_range r, Comp comp, Proj proj = {})
880 requires ((edge_type::flavour == edge_flavour::partial) && std::sortable<edge_iterator, Comp, Proj>)
882 sort_edges(r.begin(), r.end(), std::move(comp), std::move(proj));
885 template<
class Comp,
class Proj = std::
identity>
886 constexpr void stable_sort_edges(const_edge_iterator begin, const_edge_iterator end, Comp comp, Proj proj = {})
887 requires ((edge_type::flavour == edge_flavour::partial) && merge_sortable<edge_iterator, Comp, Proj>)
889 if(begin.partition_index() != end.partition_index())
return;
891 sequoia::stable_sort(to_edge_iterator(begin), to_edge_iterator(end), std::move(comp), std::move(proj));
894 template<
class Comp,
class Proj = std::
identity>
895 constexpr void stable_sort_edges(const_edges_range r, Comp comp, Proj proj = {})
896 requires ((edge_type::flavour == edge_flavour::partial) && merge_sortable<edge_iterator, Comp, Proj>)
898 stable_sort_edges(r.begin(), r.end(), std::move(comp), std::move(proj));
902 constexpr static bool direct_init_v{std::is_same_v<edge_type, edge_init_type>};
903 constexpr static bool direct_copy_v{direct_init_v};
904 constexpr static bool shared_weight_v{graph_impl::has_shared_weight_v<edge_type>};
907 edge_storage_type m_Edges;
912 void erase_from_partition_if(
const edge_index_type partitionIndex, Pred pred)
914 auto discarded{std::ranges::remove_if(m_Edges.begin_partition(partitionIndex), m_Edges.end_partition(partitionIndex), pred)};
915 m_Edges.erase_from_partition(discarded.begin(), discarded.end());
918 template<
alloc... Allocators>
920 constexpr edge_storage_type make_edges(edges_initializer edges,
const Allocators&... as)
922 return process_edges(validate(preprocess(edges)), as...);
926 constexpr static const edges_initializer& preprocess(
const edges_initializer& edges)
927 requires (is_embedded(flavour) || is_directed(flavour))
933 constexpr static edge_storage_type preprocess(edges_initializer edges)
934 requires (direct_init_v && !is_embedded(flavour) && !is_directed(flavour))
936 return preprocess(edge_storage_type{edges});
940 constexpr static auto preprocess(edges_initializer edges)
941 requires (!direct_init_v && !is_embedded(flavour) && !is_directed(flavour))
943 using namespace data_structures;
944 using sequence_t = partitioned_sequence<edge_init_type>;
946 return preprocess(sequence_t{edges});
949 template<
class IntermediateEdges>
951 constexpr static IntermediateEdges preprocess(IntermediateEdges edges)
952 requires (!is_embedded(flavour) && !is_directed(flavour))
954 constexpr bool clusterEdges{!std::is_empty_v<edge_weight_type> && !std::totally_ordered<edge_weight_type>};
956 for(edge_index_type i{}; i < edges.num_partitions(); ++i)
960 if constexpr(clusterEdges)
962 auto lowerIter{edges.begin_partition(i)}, upperIter{edges.begin_partition(i)};
963 while(lowerIter != edges.end_partition(i))
965 upperIter = std::ranges::find_if_not(upperIter, edges.end_partition(i), [target{lowerIter->target_node()}](
const auto& wt) { return target == wt.target_node(); });
968 return e1.weight() == e2.weight();
971 lowerIter = upperIter;
980 constexpr static const edges_initializer& validate(
const edges_initializer& edges)
981 requires (is_embedded(flavour))
983 for(
auto nodeEdgesIter{edges.begin()}; nodeEdgesIter != edges.end(); ++nodeEdgesIter)
985 const auto nodeIndex{
static_cast<std::size_t
>(std::ranges::distance(edges.begin(), nodeEdgesIter))};
986 const auto& nodeEdges{*nodeEdgesIter};
987 for(
auto edgeIter{nodeEdges.begin()}; edgeIter != nodeEdges.end(); ++edgeIter)
989 const auto edgeIndex{
static_cast<std::size_t
>(std::ranges::distance(nodeEdges.begin(), edgeIter))};
990 const auto& edge{*edgeIter};
991 const auto target{edge.target_node()};
993 graph_errors::check_edge_index_range(
"process_complementary_edges", {nodeIndex, edgeIndex},
"target", edges.size(), target);
994 const auto compIndex{edge.complementary_index()};
996 const bool doValidate{
998 if constexpr(!is_directed(flavour))
return true;
999 else return (edge.target_node() != nodeIndex) || (edge.source_node() == edge.target_node());
1005 auto targetEdgesIter{edges.begin() + target};
1006 graph_errors::check_edge_index_range(
"process_complementary_edges", {nodeIndex, edgeIndex},
"complementary", targetEdgesIter->size(), compIndex);
1008 if((target == nodeIndex) && (compIndex == edgeIndex))
1010 throw std::logic_error{graph_errors::self_referential_error({nodeIndex, edgeIndex}, target, compIndex)};
1012 else if(
const auto& targetEdge{*(targetEdgesIter->begin() + compIndex)}; targetEdge.complementary_index() != edgeIndex)
1014 throw std::logic_error{graph_errors::reciprocated_error_message({nodeIndex, edgeIndex},
"complementary", targetEdge.complementary_index(), edgeIndex)};
1018 if constexpr(!is_directed(flavour))
1020 graph_errors::check_reciprocated_index({nodeIndex, edgeIndex},
"target", targetEdge.target_node(), nodeIndex);
1024 graph_errors::check_reciprocated_index({nodeIndex, edgeIndex},
"target", targetEdge.target_node(), target);
1025 graph_errors::check_reciprocated_index({nodeIndex, edgeIndex},
"source", targetEdge.source_node(), edge.source_node());
1027 if constexpr(is_embedded(flavour))
1029 graph_errors::check_inversion_consistency(nodeIndex, {edgeIndex, edge.inverted()}, {compIndex, targetEdge.inverted()});
1033 if constexpr(!std::is_empty_v<edge_weight_type>)
1035 if(edge.weight() != targetEdge.weight())
1036 throw std::logic_error{graph_errors::mismatched_weights_message(
"process_complementary_edges", {nodeIndex, edgeIndex})};
1041 if constexpr(is_directed(flavour))
1043 const auto source{edge.source_node()};
1044 graph_errors::check_edge_index_range(
"process_complementary_edges", {nodeIndex, edgeIndex},
"source", edges.size(), source);
1045 graph_errors::check_embedded_edge(nodeIndex, source, target);
1047 if((edge.target_node() == nodeIndex) && (edge.source_node() != edge.target_node()))
1049 auto sourceEdgesIter{edges.begin() + source};
1050 graph_errors::check_edge_index_range(
"process_complementary_edges", {nodeIndex, edgeIndex},
"complementary", sourceEdgesIter->size(), compIndex);
1052 const auto& sourceEdge{*(sourceEdgesIter->begin() + compIndex)};
1053 graph_errors::check_reciprocated_index({nodeIndex, edgeIndex},
"target", sourceEdge.target_node(), nodeIndex);
1063 constexpr static const edges_initializer& validate(
const edges_initializer& edges)
1064 requires (!is_embedded(flavour) && is_directed(flavour))
1066 for(
auto nodeEdgesIter{edges.begin()}; nodeEdgesIter != edges.end(); ++nodeEdgesIter)
1068 const auto nodeIndex{
static_cast<std::size_t
>(std::ranges::distance(edges.begin(), nodeEdgesIter))};
1069 const auto& nodeEdges{*nodeEdgesIter};
1070 for(
auto edgeIter{nodeEdges.begin()}; edgeIter != nodeEdges.end(); ++edgeIter)
1072 const auto edgeIndex{
static_cast<std::size_t
>(std::ranges::distance(nodeEdges.begin(), edgeIter))};
1073 const auto& edge{*edgeIter};
1075 const auto target{edge.target_node()};
1076 graph_errors::check_edge_index_range(
"process_edges", {nodeIndex, edgeIndex},
"target", edges.size(), target);
1084 template<
class Edges>
1085 using partition_iterator_range = std::ranges::subrange<typename Edges::const_partition_iterator>;
1087 template<
class IntermediateEdges>
1089 constexpr static decltype(
auto) validate(IntermediateEdges&& edges)
1090 requires (!is_embedded(flavour) && !is_directed(flavour))
1092 using range_t = partition_iterator_range<IntermediateEdges>;
1093 using namespace graph_errors;
1098 [](edge_index_type i, range_t hostRange) {
1099 if(std::ranges::distance(hostRange) % 2)
throw std::logic_error{odd_num_loops_error(
"connectivity_base", i)};
1101 [&](edge_index_type i, edge_index_type target, range_t hostRange, range_t targetRange) {
1102 const auto brokenEdge{
1103 [&, i, target, hostRange, targetRange]() -> std::optional<edge_indices> {
1104 if(
auto reciprocalCount{std::ranges::distance(targetRange)}; !reciprocalCount)
1106 return edge_indices{i,
static_cast<std::size_t
>(std::ranges::distance(edges.cbegin_partition(i), hostRange.begin()))};
1108 else if(
auto count{std::ranges::distance(hostRange)}; count > reciprocalCount)
1110 return edge_indices{i,
static_cast<std::size_t
>(std::ranges::distance(edges.cbegin_partition(i), hostRange.begin()) + reciprocalCount)};
1112 else if(count < reciprocalCount)
1114 return edge_indices{target,
static_cast<std::size_t
>(std::ranges::distance(edges.cbegin_partition(target), targetRange.begin()) + count)};
1122 throw std::logic_error{absent_reciprocated_partial_edge_message(
"connectivity_base", brokenEdge.value())};
1126 return std::forward<IntermediateEdges>(edges);
1129 template<std::ranges::view View>
1130 constexpr static auto find_cluster_end(View v) ->
decltype(v.begin())
1132 return !v.empty() ? std::ranges::find_if_not(v, [&front{v.front()}](
const auto& e){
return e == front; }) : v.end();
1135 template<
class Edges,
alloc... Allocators>
1137 constexpr edge_storage_type process_edges(Edges&& orderedEdges,
const Allocators&... as)
1138 requires direct_init_v
1140 return {std::forward<Edges>(orderedEdges), as...};
1143 template<
alloc... Allocators>
1145 constexpr edge_storage_type process_edges(edges_initializer edges,
const Allocators&... as)
1146 requires (!direct_init_v && !is_embedded(flavour))
1148 edge_storage_type storage(as...);
1149 storage.reserve_partitions(edges.size());
1151 for(
auto nodeEdgesIter{edges.begin()}; nodeEdgesIter != edges.end(); ++nodeEdgesIter)
1155 const auto nodeIndex{
static_cast<edge_index_type
>(std::ranges::distance(edges.begin(), nodeEdgesIter))};
1156 const auto& nodeEdges{*nodeEdgesIter};
1157 for(
const auto& edge : nodeEdges)
1159 storage.push_back_to_partition(nodeIndex, edge.target_node(), edge.weight());
1166 template<
alloc... Allocators>
1168 constexpr edge_storage_type process_edges(edges_initializer edges,
const Allocators&... as)
1169 requires (!direct_init_v && is_embedded(flavour))
1171 edge_storage_type storage(as...);
1172 storage.reserve_partitions(edges.size());
1174 for(
auto nodeEdgesIter{edges.begin()}; nodeEdgesIter != edges.end(); ++nodeEdgesIter)
1178 const auto nodeIndex{
static_cast<edge_index_type
>(std::ranges::distance(edges.begin(), nodeEdgesIter))};
1179 const auto& nodeEdges{*nodeEdgesIter};
1180 for(
const edge_init_type& edge : nodeEdges)
1182 storage.push_back_to_partition(nodeIndex, edge_type{edge});
1189 template<
class Edges,
alloc... Allocators>
1191 constexpr edge_storage_type process_edges(
const Edges& orderedEdges,
const Allocators&... as)
1192 requires (!direct_init_v && !is_embedded(flavour) && !is_directed(flavour))
1194 using range_t = partition_iterator_range<Edges>;
1196 edge_storage_type storage(as...);
1197 storage.reserve_partitions(orderedEdges.num_partitions());
1200 [&storage,
this](edge_index_type host, edge_index_type target, edge_index_type compIndex, range_t hostRange){
1201 storage.push_back_to_partition(host, (compIndex == npos) ? edge_type{hostRange.front()}
1202 : edge_type{target, *(cbegin_edges(target) + compIndex)});
1208 [&storage]() { storage.add_slot(); },
1209 [addToStorage,&orderedEdges](edge_index_type host, range_t hostRange) {
1210 for(; hostRange.begin() != hostRange.end(); hostRange.advance(1))
1212 const bool hasCompIndex{(std::ranges::distance(hostRange) % 2) && shared_weight_v};
1213 const auto compIndex{hasCompIndex ?
static_cast<edge_index_type
>(std::ranges::distance(orderedEdges.cbegin_partition(host), hostRange.begin()) - 1) : npos};
1215 addToStorage(host, host, compIndex, hostRange);
1218 [addToStorage,&orderedEdges](edge_index_type host, edge_index_type target, range_t hostRange, range_t targetRange) {
1219 for(; hostRange.begin() != hostRange.end(); hostRange.advance(1))
1221 const bool hasCompIndex{(host > target) && shared_weight_v};
1222 const auto compIndex{hasCompIndex ?
static_cast<edge_index_type
>(std::ranges::distance(orderedEdges.cbegin_partition(target), targetRange.end()) - std::ranges::distance(hostRange)) : npos};
1224 addToStorage(host, target, compIndex, hostRange);
1232 template<
class Edges,
1233 std::invocable PerNodeFn,
1234 std::invocable<edge_index_type, partition_iterator_range<Edges>> PerLoopFn,
1235 std::invocable<edge_index_type, edge_index_type, partition_iterator_range<Edges>, partition_iterator_range<Edges>> PerLinkFn
1237 constexpr static void visit_edges(
const Edges& orderedEdges, PerNodeFn perNode, PerLoopFn perLoop, PerLinkFn perLink)
1238 requires (!is_embedded(flavour) && !is_directed(flavour))
1240 constexpr bool clusterEdges{!std::is_empty_v<edge_weight_type> && !std::totally_ordered<edge_weight_type>};
1242 for(edge_index_type i{}; i < orderedEdges.num_partitions(); ++i)
1246 auto lowerIter{orderedEdges.cbegin_partition(i)}, upperIter{orderedEdges.cbegin_partition(i)};
1247 while(lowerIter != orderedEdges.cend_partition(i))
1249 upperIter = std::ranges::upper_bound(lowerIter, orderedEdges.cend_partition(i), *lowerIter,
graph_impl::edge_comparer{});
1250 if constexpr(clusterEdges) upperIter = find_cluster_end(std::ranges::subrange{lowerIter, upperIter});
1252 if(
const auto target{lowerIter->target_node()}; target == i)
1254 perLoop(i, {lowerIter, upperIter});
1258 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());
1260 const auto comparisonEdge{
1262 auto compEdge{*lowerIter};
1263 compEdge.target_node(i);
1270 if constexpr(clusterEdges)
1272 auto clusters{std::views::drop_while(eqrange, [&wt{lowerIter->weight()}](
const auto& e) {
return e.weight() != wt; })};
1273 eqrange = {clusters.begin(), find_cluster_end(clusters)};
1276 perLink(i, target, {lowerIter, upperIter}, eqrange);
1279 lowerIter = upperIter;
1284 template<
alloc... Allocators>
1286 edge_storage_type copy_edges(
const connectivity_base& in,
const Allocators&... as)
1287 requires direct_copy_v
1289 return edge_storage_type{in.m_Edges, as...};
1294 requires (!direct_copy_v)
1296 if constexpr(has_partitions_allocator<edge_storage_type>)
1298 return copy_edges(in, in.m_Edges.get_allocator(), in.m_Edges.get_partitions_allocator());
1302 return copy_edges(in, in.m_Edges.get_allocator());
1306 template<
alloc... Allocators>
1308 edge_storage_type copy_edges(
const connectivity_base& in,
const Allocators&... as)
1309 requires (!direct_copy_v && (
sizeof...(Allocators) > 0))
1311 edge_storage_type storage({std::allocator_traits<Allocators>::select_on_container_copy_construction(as)}...);
1313 if constexpr(edge_type::flavour == edge_flavour::partial)
1317 else if constexpr(edge_type::flavour == edge_flavour::partial_embedded)
1320 [
this, &storage](
const size_type node, const_edge_iterator first, const_edge_iterator i) {
1321 const auto dist{
static_cast<edge_index_type
>(std::ranges::distance(first, i))};
1322 const bool encountered{(i->target_node() < node)
1323 || ((i->target_node() == node) && (i->complementary_index() < dist))};
1327 storage.push_back_to_partition(node, i->target_node(), i->complementary_index(), i->weight());
1331 const auto compI{i->complementary_index()};
1332 storage.push_back_to_partition(node, i->target_node(), compI, *(cbegin_edges(i->target_node()) + compI));
1337 copy_edges(in, storage, processor);
1343 template<std::invocable<
size_type, const_edge_iterator, const_edge_iterator> Processor>
1344 void copy_edges(
const connectivity_base& in, edge_storage_type& storage, Processor processor)
1346 reserve_nodes(in.m_Edges.num_partitions());
1347 if constexpr(!graph_impl::has_reservable_partitions<edge_storage_type>)
1349 storage.reserve(in.m_Edges.size());
1351 for(size_type i{}; i<in.order(); ++i)
1354 if constexpr(graph_impl::has_reservable_partitions<edge_storage_type>)
1356 storage.reserve_partition(i, std::ranges::distance(in.cbegin_edges(i), in.cend_edges(i)));
1358 for(
auto inIter{in.cbegin_edges(i)}; inIter != in.cend_edges(i); ++inIter)
1360 processor(i, in.cbegin_edges(i), inIter);
1366 constexpr const_edge_iterator to_const_edge_iterator(const_reverse_edge_iterator criter)
const
1368 const auto source{criter.partition_index()};
1369 return m_Edges.cbegin_partition(source) + std::ranges::distance(criter, m_Edges.crend_partition(source)) - 1;
1373 constexpr edge_iterator to_edge_iterator(const_edge_iterator citer)
1375 const auto source{citer.partition_index()};
1376 const auto dist{std::ranges::distance(cbegin_edges(source), citer)};
1377 return m_Edges.begin_partition(source) + dist;
1380 template<std::invocable<edge_weight_type&> Fn>
1381 constexpr std::invoke_result_t<Fn, edge_weight_type&> mutate_source_edge_weight(const_edge_iterator citer, Fn fn)
1383 return fn(to_edge_iterator(citer)->weight());
1386 template<
class... Args>
1387 constexpr void set_source_edge_weight(edge_iterator iter, Args&&... args)
1389 iter->weight(std::forward<Args>(args)...);
1392 template<
class Setter>
1393 requires std::is_invocable_r_v<edge_iterator, Setter, edge_iterator>
1394 constexpr const_edge_iterator manipulate_partner_edge_weight(const_edge_iterator citer, Setter setter)
1396 const auto partner{citer->target_node()};
1398 if constexpr(edge_type::flavour == edge_flavour::partial_embedded)
1400 const auto comp{citer->complementary_index()};
1401 return setter(m_Edges.begin_partition(partner) + comp);
1406 auto found{end_edges(partner)};
1407 if(
const auto source{citer.partition_index()}; source == partner)
1409 for(
auto iter{m_Edges.begin_partition(partner)}; iter != m_Edges.end_partition(partner); ++iter)
1411 if(std::ranges::distance(m_Edges.begin_partition(partner), iter) == std::ranges::distance(cbegin_edges(partner), citer))
1416 found = setter(iter);
1423 for(
auto iter{m_Edges.begin_partition(partner)}; iter != m_Edges.end_partition(partner); ++iter)
1425 if((iter->target_node() == source) && (iter->weight() == citer->weight()))
1427 found = setter(iter);
1433 if(found != end_edges(partner))
1439 const auto node{citer.partition_index()};
1440 const auto edgeIndex{
static_cast<std::size_t
>(std::ranges::distance(cbegin_edges(node), citer))};
1441 throw std::logic_error{ graph_errors::absent_partner_weight_message(
"manipulate_partner_edge_weight", {node, edgeIndex}) };
1446 template<std::invocable<edge_weight_type&> Fn>
1447 constexpr void mutate_partner_edge_weight(const_edge_iterator citer, Fn fn)
1449 manipulate_partner_edge_weight(citer, [fn](edge_iterator iter) -> edge_iterator { fn(iter->weight());
return iter; });
1452 template<
class... Args>
1454 constexpr const_edge_iterator set_partner_edge_weight(const_edge_iterator citer, Args&&... args)
1456 return manipulate_partner_edge_weight(citer, [&args...](edge_iterator iter) -> edge_iterator { iter->weight(std::forward<Args>(args)...); return iter; });
1459 template<
class... MetaData>
1460 requires std::is_copy_constructible_v<edge_type> && (std::is_same_v<MetaData, edge_meta_data_type> && ...)
1461 void reciprocal_join(
const edge_index_type node1,
const edge_index_type node2, MetaData... md)
1464 if constexpr(edge_type::flavour == edge_flavour::partial)
1466 m_Edges.push_back_to_partition(node2, node1, std::move(md)..., *crbegin_edges(node1));
1468 else if constexpr(edge_type::flavour == edge_flavour::partial_embedded)
1470 const edge_index_type compIndex(std::ranges::distance(cbegin_edges(node1), cend_edges(node1)) - 1);
1471 m_Edges.push_back_to_partition(node2, node1, compIndex, std::move(md)..., *crbegin_edges(node1));
1476 template<
class... MetaData>
1477 requires (is_embedded(flavour) && std::is_copy_constructible_v<edge_type> && (std::is_same_v<edge_meta_data_type, MetaData> && ...))
1478 std::pair<const_edge_iterator, const_edge_iterator>
1479 insert_reciprocal_join(const_edge_iterator citer1,
const edge_index_type pos2, MetaData... args)
1481 const auto node{citer1.partition_index()};
1483 auto pos1{
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(node), citer1))};
1484 if(pos2 <= pos1) ++pos1;
1486 const auto dist1{
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(node), citer1))};
1489 auto citer2{m_Edges.insert_to_partition(cbegin_edges(node) + pos2, node, pos1, std::move(args)..., *citer1)};
1492 increment_comp_indices(++to_edge_iterator(citer2), end_edges(node), 1);
1496 auto iter1{begin_edges(node) + pos1};
1497 increment_comp_indices(++to_edge_iterator(citer2), iter1, 1);
1498 increment_comp_indices(iter1 + 1, end_edges(node), 1);
1501 citer1 = cbegin_edges(node) + pos1;
1502 return {citer1, citer2};
1505 template<
class... MetaData>
1506 requires (is_embedded(flavour) && std::is_copy_constructible_v<edge_type> && (std::is_same_v<edge_meta_data_type, MetaData> && ...))
1507 std::pair<const_edge_iterator, const_edge_iterator>
1508 insert_reciprocal_join(const_edge_iterator citer1, const_edge_iterator citer2, MetaData... md)
1510 const auto node1{citer1.partition_index()}, node2{citer2.partition_index()};
1511 const auto dist1{
static_cast<edge_index_type
>(std::ranges::distance(cbegin_edges(node1), citer1))};
1514 citer2 = m_Edges.insert_to_partition(citer2, node1, dist1, md..., *citer1);
1515 increment_comp_indices(++to_edge_iterator(citer2), end_edges(node2), 1);
1517 citer1 = cbegin_edges(node1) + dist1;
1518 return {citer1, citer2};
1521 template<
class... Args>
1522 void add_to_partition(
const edge_index_type node1,
const edge_index_type node2, Args&&... args)
1524 if constexpr (edge_type::flavour == edge_flavour::partial)
1526 m_Edges.push_back_to_partition(node1, node2, std::forward<Args>(args)...);
1528 else if constexpr (edge_type::flavour == edge_flavour::partial_embedded)
1530 const edge_index_type dist(std::ranges::distance(cbegin_edges(node2), cend_edges(node2)));
1531 const edge_index_type i{ node1 == node2 ? dist + 1 : dist };
1532 m_Edges.push_back_to_partition(node1, node2, i, std::forward<Args>(args)...);
1536 template<
class... Args>
1537 const_edge_iterator insert_to_partition(const_edge_iterator citer1,
const edge_index_type node2,
const edge_index_type pos2, Args&&... args)
1539 const auto node1{ citer1.partition_index() };
1540 citer1 = m_Edges.insert_to_partition(citer1, node2, pos2, std::forward<Args>(args)...);
1541 increment_comp_indices(++to_edge_iterator(citer1), end_edges(node1), 1);
1546 template<std::input_or_output_iterator Iter, std::sentinel_for<Iter> Sentinel,
class Fn>
1547 requires std::is_invocable_r_v<edge_index_type, Fn, edge_index_type>
1548 constexpr void modify_comp_indices(Iter first, Sentinel last, Fn fn)
noexcept
1550 while(first != last)
1552 const auto source{first.partition_index()};
1553 const auto other{first->target_node()};
1555 auto setPartnerCompIndex{
1556 [
this, other, fn](edge_index_type cmpIndex){
1557 const auto edgeIter{m_Edges.begin_partition(other) + cmpIndex};
1558 edgeIter->complementary_index(fn(edgeIter->complementary_index()));
1562 const auto compIndex{first->complementary_index()};
1565 setPartnerCompIndex(compIndex);
1569 const auto dist{
static_cast<edge_index_type
>(std::ranges::distance(begin_edges(source), first))};
1570 if(
const auto shiftedCompIndex{fn(compIndex)}; shiftedCompIndex <= dist)
1572 setPartnerCompIndex(compIndex);
1576 setPartnerCompIndex(shiftedCompIndex);
1584 template<std::input_or_output_iterator Iter, std::sentinel_for<Iter> Sentinel>
1585 constexpr void decrement_comp_indices(Iter first, Sentinel last,
const edge_index_type num)
noexcept
1587 modify_comp_indices(first, last, [num](
const auto compIndex) {
return compIndex >= num ? compIndex - num : edge_index_type{}; });
1590 template<std::input_or_output_iterator Iter, std::sentinel_for<Iter> Sentinel>
1591 constexpr void increment_comp_indices(Iter first, Sentinel last,
const edge_index_type num)
noexcept
1593 modify_comp_indices(first, last, [num](
const auto compIndex) {
return compIndex + num; });
1596 template<
class Pred,
class Modifier>
1597 constexpr void fix_edge_data(
const edge_index_type node, Pred pred, Modifier modifier)
noexcept
1599 for(size_type i{}; i < m_Edges.num_partitions(); ++i)
1601 for(
auto& edge : m_Edges.partition(i))
1603 const auto target{edge.target_node()};
1604 if(pred(target, node)) edge.target_node(modifier(target));
1610 template<std::input_or_output_iterator Iterator>
1614 constexpr static bool is_const{is_const_reference_v<typename std::iterator_traits<Iterator>::reference>};
1616 using value_type =
typename std::iterator_traits<Iterator>::value_type::weight_type;
1617 using reference = std::conditional_t<is_const, const value_type&, value_type&>;
1626 static reference get(Iterator i)
1639 template<graph_flavour Flavour,
class EdgeStorage>
1646 template<
class EdgeStorage>
1647 requires (!std::is_empty_v<typename EdgeStorage::value_type::weight_type>)
1657 using edge_weights_range = std::ranges::subrange<edge_weight_iterator>;
1658 using const_edge_weights_range = std::ranges::subrange<const_edge_weight_iterator>;
1660 using edge_index_type =
typename base_type::edge_index_type;
1735 constexpr edge_weights_range edge_weights(
const edge_index_type node) {
return {begin_edge_weights(node), end_edge_weights(node)}; }
1738 constexpr const_edge_weights_range edge_weights(
const edge_index_type node)
const {
return {begin_edge_weights(node), end_edge_weights(node)}; }
1741 constexpr const_edge_weights_range cedge_weights(
const edge_index_type node)
const {
return {begin_edge_weights(node), end_edge_weights(node)}; }
1743 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:143
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.
Graph connectivity_base, used as a building block for concrete graphs.
Definition: Connectivity.hpp:184
Definition: Connectivity.hpp:1641
Definition: Connectivity.hpp:1612
Definition: Connectivity.hpp:52
Definition: Connectivity.hpp:127
Definition: Connectivity.hpp:96
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