dune-istl 2.12-git
Loading...
Searching...
No Matches
sparseindexranges.hh
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright © DUNE Project contributors, see file LICENSE.md in module root
2// SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception
3// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4// vi: set et ts=4 sw=2 sts=2:
5
6#ifndef DUNE_ISTL_EXPERIMENTAL_SPARSEINDEXRANGES_HH
7#define DUNE_ISTL_EXPERIMENTAL_SPARSEINDEXRANGES_HH
8
9#include "indexrange.hh"
10
15
18
19#include <algorithm>
20#include <cassert>
21#include <compare>
22#include <concepts>
23#include <cstdint>
24#include <limits>
25#include <memory>
26#include <numeric>
27#include <ranges>
28#include <span>
29#include <utility>
30#include <variant>
31#include <iostream>
32#include <functional>
33
34namespace Dune::Experimental
35{
36
37namespace Impl
38{
40 struct SparseIndexRangesBase {};
41
55#if !defined(DUNE_ISTL_SPARSE_SEARCH_THRESHOLD)
56 static constexpr std::size_t SPARSE_SEARCH_THRESHOLD = 0;
57#else
58 static constexpr std::size_t SPARSE_SEARCH_THRESHOLD = DUNE_ISTL_SPARSE_SEARCH_THRESHOLD;
59#endif
60
61 template<class A>
62 struct SparseIndexRangeHelper {
63
65 using allocator_type = A;
67 using alloc_traits = std::allocator_traits<allocator_type>;
68
70 using size_type = typename std::allocator_traits<allocator_type>::size_type;
72 using size_alloc_type = typename alloc_traits::template rebind_alloc<typename alloc_traits::size_type>;
74 using size_alloc_traits = std::allocator_traits<size_alloc_type>;
75
77 using index_type = uint_fast64_t;
78
81
83 template<class OtherAlloc>
84 struct DeallocationGuard;
85
87 inline void debug(std::span<size_type> offsets, IndexVariant indices);
88
90 static constexpr std::pair<IndexVariant,size_type> initAllocateIndices(const allocator_type &allocator, size_type count, size_type max_size);
92 static constexpr size_type* initAllocateOffsets(const allocator_type &allocator, size_type size);
94 static constexpr void deallocate(const allocator_type& allocator, IndexVariant indices, size_type* offsets, size_type size);
95
97 template<class V = IndexVariant>
98 static constexpr V compress(std::span<size_type> offsets, IndexVariant indices, const allocator_type& allocator);
99
101 template<class index_t>
102 static constexpr bool addIndex(std::span<index_t> index_set, index_t j, std::invocable auto grow_index_set_fn);
103
105 static constexpr void growIndices(std::span<size_type> offsets, IndexVariant& indices, size_type new_count, const allocator_type& allocator, auto uninitialized_copy_fn);
106
108 static constexpr void growLastIndexRange(std::span<size_type> offsets, IndexVariant& indices, double growth_factor, const allocator_type& allocator);
109
130 static constexpr void growAllIndexRanges(std::span<size_type> offsets, IndexVariant& indices, double growth_factor, const allocator_type& allocator);
131
135 template<class IS>
136 static constexpr auto lowerBound(IS index_set, auto j) {
137 using value_type = std::ranges::range_value_t<IS>;
138 // Use portable architecture-aware threshold from config header
139 if (index_set.size() * sizeof(value_type) <= Impl::SPARSE_SEARCH_THRESHOLD)
140 return std::find_if(std::cbegin(index_set), std::cend(index_set), [j](value_type index){ return index >= j; });
141 else
142 return std::lower_bound(std::cbegin(index_set), std::cend(index_set), j);
143 }
144
147 template<class IS>
148 static constexpr auto upperBound(IS index_set, auto j) {
149 using value_type = std::ranges::range_value_t<IS>;
150 // Use portable architecture-aware threshold from config header
151 if (index_set.size() * sizeof(value_type) <= Impl::SPARSE_SEARCH_THRESHOLD)
152 return std::find_if(std::cbegin(index_set), std::cend(index_set), [j](value_type index){ return index > j; });
153 else
154 return std::upper_bound(std::cbegin(index_set), std::cend(index_set), j);
155 }
156
157 private:
158 template<class V, std::size_t... I>
159 static constexpr V makeIndexVariantWithAlternative(std::size_t selected, std::index_sequence<I...>)
160 {
161 V result{};
162 const bool assigned = ((selected == I ? (result = V{std::in_place_index<I>, nullptr}, true) : false) || ...);
163 if (!assigned)
164 DUNE_THROW(RangeError, "Selected index variant alternative is out of range: " << selected);
165 return result;
166 }
167
168 template<class V, std::size_t... I>
169 static constexpr std::size_t selectSmallestIndexStorage(size_type max_index, std::index_sequence<I...>)
170 {
171 constexpr std::size_t variant_size = sizeof...(I);
172 constexpr std::size_t invalid = variant_size;
173 std::size_t best = invalid;
175 std::size_t fallback = 0;
176 size_type fallback_max = 0;
177
178 auto consider = [&]<std::size_t J>() {
179 using pointer_type = std::variant_alternative_t<J, V>;
180 static_assert(std::is_pointer_v<pointer_type>, "Variant alternatives must be pointer types");
182 static_assert(std::is_integral_v<index_t>, "Variant pointer element types must be integral");
183
184 const auto max_supported = static_cast<size_type>(std::numeric_limits<index_t>::max());
185 if (max_supported >= fallback_max) {
186 fallback = J;
187 fallback_max = max_supported;
188 }
189
190 if (max_index <= max_supported) {
191 constexpr std::size_t storage_size = sizeof(index_t);
192 if (storage_size < best_size) {
193 best = J;
194 best_size = storage_size;
195 }
196 }
197 };
198
199 (consider.template operator()<I>(), ...);
200
201 if (best != invalid)
202 return best;
203
204 assert(false && "Variant V does not provide an index type that can represent max_index");
205 return fallback;
206 }
207
208 template<class V>
209 static constexpr V makeSmallestIndexVariant(size_type max_index)
210 {
211 constexpr std::size_t variant_size = std::variant_size_v<V>;
212 static_assert(variant_size > 0, "Variant V must not be empty");
213 const auto selected = selectSmallestIndexStorage<V>(max_index, std::make_index_sequence<variant_size>{});
214 return makeIndexVariantWithAlternative<V>(selected, std::make_index_sequence<variant_size>{});
215 }
216 };
217
218} // namespace Impl
219
220
221 template<class I, class A>
222 class SparseIndexRanges;
223
224 template<class A>
225 class CompressedSparseIndexRanges;
226
227 // ---------------------------------------------------------------------------
228 // UnsequencedSparseIndexRangeBuilder declaration
229 // ---------------------------------------------------------------------------
230
242 template<class A = std::allocator<void>>
244 {
245 enum class Stage : uint_least8_t
246 {
247 BuildSizes,
248 BuildSetIndices,
249 Invalid
250 };
251
252 using IndexVariant = typename Impl::SparseIndexRangeHelper<A>::IndexVariant;
253
254 public:
255
256 using allocator_type = A;
257
260
262 using index_type = uint_fast64_t;
263
268 constexpr UnsequencedSparseIndexRangeBuilder(size_type size, size_type is_size, size_type avg_indices = 0, allocator_type allocator = allocator_type());
270
275
277 constexpr void setSize(size_type i, size_type s);
279 constexpr void incrementSize(size_type i, size_type increment = 1);
281 constexpr void addIndex(size_type i, index_type j);
282
283 private:
285 constexpr void endSizes();
286
287 template<class OtherAlloc>
289 template<class I, class OtherAlloc>
290 friend class SparseIndexRanges;
291
292 size_type size_;
293 size_type is_size_;
294 size_type *offsets_;
295 IndexVariant indices_;
297 Stage stage_;
298 };
299
300 // ---------------------------------------------------------------------------
301 // SequencedSparseIndexRangeBuilder declaration
302 // ---------------------------------------------------------------------------
303
327 template<class A = std::allocator<void>>
329 {
330 enum class Stage : uint_least8_t
331 {
332 Allocated,
333 BuildingIndexRanges,
334 Built,
335 Invalid
336 };
337
338 using IndexVariant = typename Impl::SparseIndexRangeHelper<A>::IndexVariant;
339
340 public:
341
342 using allocator_type = A;
343
346
348 using index_type = uint_fast64_t;
349
352 constexpr SequencedSparseIndexRangeBuilder(size_type size, size_type is_size, size_type avg_indices = 2048, allocator_type allocator = allocator_type());
353
355
360
361 struct EndSentinel {};
362
375 {
377
378 public:
379 constexpr IndexRangeBuilder(const IndexRangeBuilder &) = delete;
380 constexpr IndexRangeBuilder &operator=(const IndexRangeBuilder &) = delete;
381
382 constexpr IndexRangeBuilder(IndexRangeBuilder && other);
383 constexpr IndexRangeBuilder &operator=(IndexRangeBuilder && other);
384
385 constexpr ~IndexRangeBuilder() = default;
386
388 constexpr void insert(index_type j);
389
391 constexpr bool contains(index_type j) const;
392
414 template<std::forward_iterator It, std::sentinel_for<It> End>
415 constexpr void overrideIndices(It begin, End end, bool do_sort = true, bool do_unique = true);
416
418 constexpr size_type index() const;
419
422 constexpr IndexRangeBuilder& operator++();
423
425 constexpr size_type size() const;
426
428 constexpr friend bool operator==(const IndexRangeBuilder& is_builder, EndSentinel) {
429 assert(is_builder.stage_ != Stage::Invalid);
430 return is_builder.stage_ == Stage::Built;
431 }
432
434 constexpr IndexRangeBuilder& operator*();
435
436 private:
439 size_type i_;
440 size_type is_count_; // number of indices inserted in the current index set
441 Stage stage_; // stage of this builder, used to check end sentinel without relying on the address of the builder (which may be moved after the last increment).
442 };
443
445 constexpr IndexRangeBuilder begin();
446
448 constexpr EndSentinel end();
449 private:
450
451 template<class OtherAlloc>
453 template<class I, class OtherAlloc>
454 friend class SparseIndexRanges;
455 size_type size_;
456 size_type is_size_;
457 size_type *offsets_;
458 IndexVariant indices_;
460 Stage stage_;
461 };
462
463
487 template <class I = std::uint_least32_t, class A = std::allocator<I>>
489 {
490 using alloc_type = A;
492
493 using size_alloc_type = typename alloc_traits::template rebind_alloc<typename alloc_traits::size_type>;
495
496 // Nested type declarations
497 class IndexIterator;
498
499 constexpr SparseIndexRanges() = delete;
500 constexpr SparseIndexRanges(const SparseIndexRanges &other) = delete;
501 constexpr SparseIndexRanges &operator=(const SparseIndexRanges &other) = delete;
502
503 public:
504
506 using size_type = typename alloc_traits::size_type;
507
509 using index_type = I;
510
512 using allocator_type = A;
513
515 class Iterator;
516
518 class IndexRangeView;
519
521 template<class OtherAlloc>
522 requires std::constructible_from<A, OtherAlloc>
524
526 template<class OtherAlloc>
527 requires std::constructible_from<A, OtherAlloc>
529
532
535
537 constexpr void swap(SparseIndexRanges &other);
538
540 friend constexpr void swap(SparseIndexRanges &lhs, SparseIndexRanges &rhs)
541 {
542 lhs.swap(rhs);
543 }
544
547
549 constexpr size_type size() const noexcept;
550
552 constexpr size_type count() const;
553
555 constexpr IntegralRange<index_type> range() const noexcept;
556
559 constexpr size_type offset(size_type i) const;
560
563 constexpr IndexRangeView operator[](size_type i) const { return {this, i}; };
564
566 constexpr Iterator begin() const { return {this, 0}; }
567
569 constexpr Iterator end() const { return {this, size()}; }
570
573
575 constexpr friend bool operator==(const SparseIndexRanges &p1, const SparseIndexRanges &p2)
576 {
577 if (std::addressof(p1) == std::addressof(p2))
578 return true;
579
580 if (p1.size_ != p2.size_ || p1.is_size_ != p2.is_size_)
581 return false;
582
583 if (!std::equal(p1.offsets_, p1.offsets_ + p1.size_ + 1, p2.offsets_))
584 return false;
585
586 return std::equal(p1.indices_, p1.indices_ + p1.count(), p2.indices_);;
587 }
588
589 private:
590
591 size_type size_;
592 index_type is_size_;
593 size_type *offsets_;
594 index_type *indices_;
595 DUNE_NO_UNIQUE_ADDRESS alloc_type alloc_;
596 };
597
598 // ---------------------------------------------------------------------------
599 // SparseIndexRanges::Iterator declaration
600 // ---------------------------------------------------------------------------
601
611 template <class I, class A>
613 : public Dune::IteratorFacade<Iterator, std::random_access_iterator_tag, IndexRangeView, IndexRangeView, Dune::ProxyArrowResult<IndexRangeView>>
614 {
616
617 constexpr Iterator(SparseIndexRanges const *sp, size_type i);
618
619 public:
620 using reference = typename Facade::reference;
622
623 constexpr Iterator() = default;
624
625 constexpr reference operator*() const;
626
627 private:
629 friend SparseIndexRanges;
630
631 constexpr const size_type &baseIterator() const;
632
633 constexpr size_type &baseIterator();
634
635 SparseIndexRanges const *cis_;
636 size_type i_;
637 };
638
639 // ---------------------------------------------------------------------------
640 // SparseIndexRanges::IndexIterator declaration
641 // ---------------------------------------------------------------------------
642
643 template <class I, class A>
645 : public Dune::IteratorFacade<IndexIterator, std::random_access_iterator_tag, index_type, index_type, Dune::ProxyArrowResult<index_type>>
646 {
648
649 public:
650 using reference = typename Facade::reference;
652
653 constexpr IndexIterator(SparseIndexRanges const *csi, size_type i_, size_type j);
654
655 constexpr IndexIterator() = default;
656
657 constexpr size_type offset() const;
658
659 constexpr reference operator*() const;
660
661 private:
663
664 constexpr const size_type &baseIterator() const;
665
666 constexpr size_type &baseIterator();
667
668 SparseIndexRanges const *cis_;
669 size_type i_;
670 size_type j_;
671 };
672
673 // ---------------------------------------------------------------------------
674 // SparseIndexRanges::IndexRangeView declaration
675 // ---------------------------------------------------------------------------
676
686 template <class I, class A>
688 : public Impl::SparseIndexRangesBase
689 {
690 friend class SparseIndexRanges<I,A>::Iterator;
691 friend class SparseIndexRanges<I,A>;
692
693 constexpr IndexRangeView(const SparseIndexRanges *cis, size_type i);
694
695 public:
696 IndexRangeView() = default;
697 IndexRangeView(const IndexRangeView&) = default;
698 IndexRangeView& operator=(const IndexRangeView&) = default;
699
702
704
705 IndexRangeView(IndexRangeView&&) = default;
706 IndexRangeView& operator=(IndexRangeView&&) = default;
707
709 constexpr IndexIterator begin() const;
711 constexpr IndexIterator end() const;
712
714 constexpr size_type size() const;
715
717 constexpr bool empty() const { return size() == 0; }
718
720 constexpr index_type front() const { return *begin(); }
721
723 constexpr index_type back() const { return *(end() - 1); }
724
726 constexpr index_type operator[](size_type i) const { return *(begin() + i); }
727
730
732 constexpr bool contains(index_type j) const;
733
735 constexpr IndexIterator lower_bound(index_type i) const;
736
737
738 constexpr IndexIterator upper_bound(index_type i) const;
739
740
741 constexpr IndexIterator find(index_type i) const;
742
744 template<std::invocable<index_type> F>
745 constexpr void for_each(F &&f) const
746 {
747 const std::span<const index_type> index_set(cis_->indices_ + cis_->offset(i_), cis_->indices_ + cis_->offset(i_ + 1));
748 std::for_each(std::cbegin(index_set), std::cend(index_set), std::forward<F>(f));
749 }
750
751 private:
752 SparseIndexRanges const *cis_;
753 size_type i_;
754 };
755
782 template <class A = std::allocator<void>>
784 {
785 using alloc_type = A;
787
788 using size_alloc_type = typename alloc_traits::template rebind_alloc<typename alloc_traits::size_type>;
790
791 using IndexVariant = typename Impl::SparseIndexRangeHelper<A>::IndexVariant;
792
793 // Nested type declarations
794 class IndexIterator;
795
796 constexpr CompressedSparseIndexRanges() = delete;
797 constexpr CompressedSparseIndexRanges(const CompressedSparseIndexRanges &other) = delete;
798 constexpr CompressedSparseIndexRanges &operator=(const CompressedSparseIndexRanges &other) = delete;
799
800 public:
801
803 using size_type = typename alloc_traits::size_type;
804
806 using index_type = uint_fast64_t;
807
809 using allocator_type = A;
810
812 class Iterator;
813
815 class IndexRangeView;
816
818 template<class OtherAlloc>
819 requires std::constructible_from<A, OtherAlloc>
821
823 template<class OtherAlloc>
824 requires std::constructible_from<A, OtherAlloc>
826
829
832
834 constexpr void swap(CompressedSparseIndexRanges &other);
835
838 {
839 lhs.swap(rhs);
840 }
841
844
846 constexpr size_type size() const noexcept;
847
849 constexpr size_type count() const;
850
852 constexpr IntegralRange<index_type> range() const noexcept;
853
856 constexpr size_type offset(size_type i) const;
857
860 constexpr IndexRangeView operator[](size_type i) const { return {this, i}; };
861
863 constexpr Iterator begin() const { return {this, 0}; }
864
866 constexpr Iterator end() const { return {this, size()}; }
867
870
872 constexpr friend bool operator==(const CompressedSparseIndexRanges &p1, const CompressedSparseIndexRanges &p2)
873 {
874 if (std::addressof(p1) == std::addressof(p2))
875 return true;
876
877 if (p1.size_ != p2.size_ || p1.is_size_ != p2.is_size_)
878 return false;
879
880 if (!std::equal(p1.offsets_, p1.offsets_ + p1.size_ + 1, p2.offsets_))
881 return false;
882
883 return std::visit([&]<typename index_t_1, typename index_t_2>(index_t_1 *indices1, index_t_2 *indices2) -> bool
884 { return std::equal(indices1, indices1 + p1.count(), indices2); }, p1.indices_, p2.indices_);
885 }
886
887 private:
888
889 size_type size_; // number of index sets, e.g., rows of the matrix
890 index_type is_size_; // size of the index sets, e.g., number of columns of the matrix
891 size_type *offsets_;
892 IndexVariant indices_;
893 DUNE_NO_UNIQUE_ADDRESS alloc_type alloc_;
894 };
895
896 // ---------------------------------------------------------------------------
897 // CompressedSparseIndexRanges::Iterator declaration
898 // ---------------------------------------------------------------------------
899
909 template <class A>
911 : public Dune::IteratorFacade<Iterator, std::random_access_iterator_tag, IndexRangeView, IndexRangeView, Dune::ProxyArrowResult<IndexRangeView>>
912 {
914
915 constexpr Iterator(CompressedSparseIndexRanges const *sp, size_type i);
916
917 public:
918 using reference = typename Facade::reference;
920
921 constexpr Iterator() = default;
922
923 constexpr reference operator*() const;
924
925 private:
928
929 constexpr const size_type &baseIterator() const;
930
931 constexpr size_type &baseIterator();
932
933 CompressedSparseIndexRanges const *cis_;
934 size_type i_;
935 };
936
937 // ---------------------------------------------------------------------------
938 // CompressedSparseIndexRanges::IndexIterator declaration
939 // ---------------------------------------------------------------------------
940
941 template <class A>
943 : public Dune::IteratorFacade<IndexIterator, std::random_access_iterator_tag, index_type, index_type, Dune::ProxyArrowResult<index_type>>
944 {
946
947 public:
948 using reference = typename Facade::reference;
950
951 constexpr IndexIterator(CompressedSparseIndexRanges const *csi, size_type i_, size_type j);
952
953 constexpr IndexIterator() = default;
954
955 constexpr size_type offset() const;
956
957 constexpr reference operator*() const;
958
959 private:
961
962 constexpr const size_type &baseIterator() const;
963
964 constexpr size_type &baseIterator();
965
966 CompressedSparseIndexRanges const *cis_;
967 size_type i_;
968 size_type j_;
969 };
970
971 // ---------------------------------------------------------------------------
972 // CompressedSparseIndexRanges::IndexRangeView declaration
973 // ---------------------------------------------------------------------------
974
984 template <class A>
986 : public Impl::SparseIndexRangesBase
987 {
989 friend class CompressedSparseIndexRanges<A>;
990
991 constexpr IndexRangeView(const CompressedSparseIndexRanges *cis, size_type i);
992
993 public:
994 IndexRangeView() = default;
995 IndexRangeView(const IndexRangeView&) = default;
996 IndexRangeView& operator=(const IndexRangeView&) = default;
997
1000
1002
1003 IndexRangeView(IndexRangeView&&) = default;
1004 IndexRangeView& operator=(IndexRangeView&&) = default;
1005
1007 constexpr IndexIterator begin() const;
1009 constexpr IndexIterator end() const;
1010
1012 constexpr size_type size() const;
1013
1015 constexpr bool empty() const { return size() == 0; }
1016
1018 constexpr index_type front() const { return *begin(); }
1019
1021 constexpr index_type back() const { return *(end() - 1); }
1022
1024 constexpr index_type operator[](size_type i) const { return *(begin() + i); }
1025
1028
1030 constexpr bool contains(index_type j) const;
1031
1033 constexpr IndexIterator lower_bound(index_type i) const;
1034
1035
1036 constexpr IndexIterator upper_bound(index_type i) const;
1037
1038
1039 constexpr IndexIterator find(index_type i) const;
1040
1042 template<std::invocable<index_type> F>
1043 constexpr void for_each(F &&f) const
1044 {
1045 std::visit([&]<typename index_t>(index_t *indices) {
1046 const auto index_set = std::span(indices + cis_->offset(i_), indices + cis_->offset(i_ + 1));
1047 std::for_each(std::cbegin(index_set), std::cend(index_set), std::forward<F>(f));
1048 }, cis_->indices_);
1049 }
1050
1051 private:
1052 CompressedSparseIndexRanges const *cis_;
1053 size_type i_;
1054 };
1055
1056 template<Concepts::BorrowedOrderedIndexRange R>
1057 requires std::derived_from<R, Impl::SparseIndexRangesBase>
1059 {
1061 using index_type = typename index_range_type::index_type;
1062 using size_type = typename index_range_type::size_type;
1063
1064 constexpr static IntegralRange<index_type> range(const index_range_type& index_set)
1065 {
1066 return index_set.range();
1067 }
1068
1069 constexpr static size_type offset(const index_range_type& index_set, const index_type& value)
1070 {
1071 return static_cast<size_type>(std::distance(std::cbegin(index_set), index_set.lower_bound(value)));
1072 }
1073 };
1074
1075 // ---------------------------------------------------------------------------
1076 // Impl::SparseIndexRangeHelper implementations
1077 // ---------------------------------------------------------------------------
1078
1079namespace Impl
1080{
1081
1082 template <class A>
1083 void SparseIndexRangeHelper<A>::debug(std::span<size_type> offsets, IndexVariant indices)
1084 {
1085 std::cout << "Index Set Range: [0, " << offsets.size() - 1 << ")" << std::endl;
1086 std::cout << "Offsets: ";
1087 for (auto i : offsets)
1088 std::cout << i << " ";
1089 std::cout << "\n";
1090 std::visit([&]<class index_t>(index_t *indices_ptr) {
1091 std::cout << "Indices [" << Dune::className<index_t>() << "]: | ";
1092 for (std::size_t i = 0; i + 1 != offsets.size(); ++i) {
1093 for (auto j : std::span(indices_ptr + offsets[i], offsets[i + 1] - offsets[i]))
1094 std::cout << (j == std::numeric_limits<index_t>::max() ? "*" : std::to_string(static_cast<size_type>(j))) << " ";
1095 std::cout << "| ";
1096 }
1097 std::cout << "\n";
1098 }, indices);
1099 }
1100
1101
1102 template <class A>
1103 constexpr auto SparseIndexRangeHelper<A>::initAllocateOffsets(const allocator_type &allocator, size_type size) -> size_type* {
1104 size_alloc_type size_alloc(allocator);
1105 size_type* offsets = size_alloc_traits::allocate(size_alloc, size+1);
1106 for (size_type i = 0; i != size+1; ++i)
1107 size_alloc_traits::construct(size_alloc, std::addressof(offsets[i]), size_type{0});
1108 return offsets;
1109 }
1110
1111 template <class A>
1112 constexpr auto SparseIndexRangeHelper<A>::initAllocateIndices(const allocator_type &allocator, size_type count, size_type max_size) -> std::pair<IndexVariant,size_type> {
1113 IndexVariant indices;
1114 // use the smallest possible one for the index set indices array
1115 size_type max_index = std::max<size_type>(max_size, 1) - 1;
1116 assert(max_index < std::numeric_limits<uint_least64_t>::max() && "Too many indices for supported index types");
1118 indices = IndexVariant{std::in_place_type<uint_least8_t *>, nullptr};
1119 else if (max_index < std::numeric_limits<uint_least16_t>::max())
1120 indices = IndexVariant{std::in_place_type<uint_least16_t *>, nullptr};
1121 else if (max_index < std::numeric_limits<uint_least32_t>::max())
1122 indices = IndexVariant{std::in_place_type<uint_least32_t *>, nullptr};
1123 else
1124 indices = IndexVariant{std::in_place_type<uint_least64_t *>, nullptr};
1125
1126 // visit assigned pointer
1127 std::visit([&count, &allocator]<class index_t>(index_t *&indices_ptr) {
1128 using index_alloc_type = typename alloc_traits::template rebind_alloc<index_t>;
1129 using index_alloc_traits = std::allocator_traits<index_alloc_type>;
1130 index_alloc_type index_alloc(allocator);
1131 // actual allocation of storage
1132#if __cpp_lib_allocate_at_least >= 202302L
1133 auto alloc_result = index_alloc_traits::allocate_at_least(index_alloc, count);
1134 indices_ptr = alloc_result.ptr;
1135 count = alloc_result.count;
1136#else
1137 indices_ptr = index_alloc_traits::allocate(index_alloc, count);
1138#endif
1139 // initialize index set indices with invalid value
1140 for (size_type i = 0; i != count; ++i)
1141 index_alloc_traits::construct(index_alloc, std::addressof(indices_ptr[i]), std::numeric_limits<index_t>::max());
1142 }, indices);
1143
1144 return std::make_pair(indices, count);
1145 }
1146
1147 template <class A>
1148 constexpr void SparseIndexRangeHelper<A>::deallocate(const allocator_type& allocator, IndexVariant indices, size_type* offsets, size_type size) {
1149 if (offsets)
1150 {
1151 std::visit([&]<class index_t>(index_t *indices_ptr) {
1152 using index_alloc_type = typename alloc_traits::template rebind_alloc<index_t>;
1153 using index_alloc_traits = std::allocator_traits<index_alloc_type>;
1154 if (indices_ptr) {
1155 size_type count = offsets[size];
1156 index_alloc_type index_alloc(allocator);
1157 index_alloc_traits::deallocate(index_alloc, indices_ptr, count);
1158 } }, indices);
1159 size_alloc_type size_alloc(allocator);
1160 size_alloc_traits::deallocate(size_alloc, offsets, size + 1);
1161 }
1162 }
1163
1164
1165 template <class A>
1166 template <class V>
1167 constexpr V SparseIndexRangeHelper<A>::compress(std::span<size_type> offsets, IndexVariant indices, const allocator_type& alloc)
1168 {
1169 // check if there are undefined indices and remove them if necessary
1170 return std::visit([&alloc, offsets]<class old_index_t>(old_index_t *old_indices) -> V {
1171 size_type index_count = offsets.back();
1172 // count the max index and non-used entries
1173 constexpr old_index_t invalid_index = std::numeric_limits<old_index_t>::max();
1174 size_type count_invalid = 0;
1175 size_type max_index = 0;
1176 auto old_index_set = std::span(old_indices, index_count);
1177 std::for_each(std::cbegin(old_index_set), std::cend(old_index_set), [&count_invalid, &max_index](old_index_t j) {
1178 if (j == invalid_index)
1179 count_invalid++;
1180 else
1181 max_index = std::max<size_type>(max_index, j);
1182 });
1183 V new_indices = makeSmallestIndexVariant<V>(max_index);
1184
1185 // nothing to do if there are no undefined indices and the indices already use the selected index type
1186 if constexpr (std::constructible_from<V, old_index_t*>)
1187 if (count_invalid == 0 && std::holds_alternative<old_index_t*>(new_indices)) {
1188 new_indices = V{std::in_place_type<old_index_t *>, old_indices};
1189 return new_indices;
1190 }
1191
1192 // remove undefined indices and update offsets accordingly
1193 constexpr auto index_defined = [](old_index_t i){ return i != invalid_index; };
1194 // allocate new arrays for offsets and indices. We use scope guards to ensure that the allocated memory is freed in case of exceptions.
1195 size_alloc_type size_alloc(alloc);
1196 size_type *new_offsets = size_alloc_traits::allocate(size_alloc, offsets.size());
1197 typename Impl::SparseIndexRangeHelper<A>::template DeallocationGuard<size_alloc_type> offsetsScopeGuard{size_alloc, new_offsets, offsets.size()};
1198
1199 new_indices = std::visit([offsets, old_indices, index_count, count_invalid, index_defined, &new_offsets, &size_alloc]<typename new_index_t>(new_index_t * new_indices_ptr) -> V {
1200 using new_index_alloc_type = typename alloc_traits::template rebind_alloc<new_index_t>;
1201 using new_index_alloc_traits = std::allocator_traits<new_index_alloc_type>;
1202 new_index_alloc_type new_index_alloc(size_alloc);
1203 new_indices_ptr = new_index_alloc_traits::allocate(new_index_alloc, index_count - count_invalid);
1204 typename Impl::SparseIndexRangeHelper<A>::template DeallocationGuard<new_index_alloc_type> newIndicesScopeGuard{new_index_alloc, new_indices_ptr, index_count - count_invalid};
1205
1206 size_alloc_traits::construct(size_alloc, std::addressof(new_offsets[0]), 0);
1207 for (size_type i = 0; i + 1!= offsets.size(); ++i)
1208 {
1209 // count number of defined indices in index set
1210 auto old_index_set = std::span(old_indices + offsets[i], old_indices + offsets[i + 1]);
1211 size_type is_count = std::count_if(std::cbegin(old_index_set), std::cend(old_index_set), index_defined);
1212 // compute new offset for next index set
1213 size_alloc_traits::construct(size_alloc, std::addressof(new_offsets[i + 1]), new_offsets[i] + is_count);
1214 // copy defined indices of old index set to new index set
1215 auto new_index_set = std::span(new_indices_ptr + new_offsets[i], is_count);
1216 size_type j = 0;
1217 for (const auto old_idx : old_index_set)
1218 if (index_defined(old_idx))
1219 new_index_alloc_traits::construct(new_index_alloc, std::addressof(new_index_set[j++]), static_cast<new_index_t>(old_idx));
1220 }
1221
1222 // replace old offsets with new ones
1223 std::copy_n(new_offsets, offsets.size(), std::begin(offsets));
1224
1225 // replace old indices with new ones and free old memory
1226 using old_index_alloc_type = typename alloc_traits::template rebind_alloc<old_index_t>;
1227 typename Impl::SparseIndexRangeHelper<A>::template DeallocationGuard<old_index_alloc_type> oldIndicesScopeGuard{old_index_alloc_type(size_alloc), old_indices, index_count};
1228 return {std::exchange(newIndicesScopeGuard.ptr, nullptr)};
1229 }, new_indices);
1230
1231 return new_indices;
1232 }, indices);
1233 }
1234
1235
1236 template <class A>
1237 template<class index_t>
1238 constexpr bool SparseIndexRangeHelper<A>::addIndex(std::span<index_t> index_set, index_t j, std::invocable auto grow_index_set_fn)
1239 {
1240 constexpr index_t invalid_index = std::numeric_limits<index_t>::max();
1241
1242 // find correct insertion position for new index
1243 auto it = SparseIndexRangeHelper<A>::lowerBound(index_set, j);
1244
1245 // check if index is already in index set
1246 if (it != std::cend(index_set) && *it == j)
1247 return false;
1248
1249 // grow number of indices if exhausted
1250 if (it == std::cend(index_set) || index_set.back() != invalid_index) {
1251 // save distance to insertion position before growth
1252 size_type dist = std::distance(std::cbegin(index_set), it);
1253 // invoke growth function and update index set
1254 index_set = std::invoke(grow_index_set_fn);
1255 // check that growth function has made space for new index
1256 assert(index_set.size() > dist && "Too many entries in index set but the builder cannot grow the indices");
1257 assert(index_set.back() == invalid_index && "Growth function must initialize new indices with invalid value");
1258 // restore iterator to correct position after growth
1259 it = std::begin(index_set) + dist;
1260 }
1261
1262 // insert new index and shift other indices if necessary
1263 do {
1264 j = std::exchange(*it, j);
1265 it = (j == invalid_index) ? std::cend(index_set) : std::next(it);
1266 } while (it != std::cend(index_set));
1267 return true;
1268 }
1269
1270 template<class A>
1271 constexpr void SparseIndexRangeHelper<A>::growIndices(std::span<size_type> offsets, IndexVariant& indices, size_type new_count, const allocator_type& allocator, auto uninitialized_copy_fn){
1272 size_type old_count = offsets.back();
1273 if (new_count <= old_count)
1274 return;
1275
1276 // allocate new arrays for indices. We use scope guards to ensure that the allocated memory is freed in case of exceptions.
1277 std::visit([&indices, &allocator, &uninitialized_copy_fn, offsets, old_count, new_count]<typename index_t>(index_t * old_indices) {
1278 using index_alloc_type = typename alloc_traits::template rebind_alloc<index_t>;
1279 using index_alloc_traits = std::allocator_traits<index_alloc_type>;
1280 using dealloc_guard_type = typename Impl::SparseIndexRangeHelper<A>::template DeallocationGuard<index_alloc_type>;
1281 index_alloc_type index_alloc(allocator);
1282 index_t* new_indices = nullptr;
1283 size_type allocated_count = new_count;
1284#if __cpp_lib_allocate_at_least >= 202302L
1285 auto alloc_result = index_alloc_traits::allocate_at_least(index_alloc, allocated_count);
1286 new_indices = alloc_result.ptr;
1287 allocated_count = alloc_result.count;
1288#else
1289 new_indices = index_alloc_traits::allocate(index_alloc, allocated_count);
1290#endif
1291 dealloc_guard_type newIndicesScopeGuard{index_alloc, new_indices, allocated_count};
1292
1293 // copy values from old indices to new indices
1294 uninitialized_copy_fn(offsets, std::span<const index_t>(old_indices, old_count), std::span(new_indices, allocated_count));
1295 // offsets always need to be updated to reflect the new size of the indices array
1296 assert(offsets.back() == allocated_count);
1297
1298 // replace old indices with new ones and free old memory
1299 dealloc_guard_type oldIndicesScopeGuard{index_alloc, old_indices, old_count};
1300 indices = IndexVariant{std::in_place_type<index_t *>, std::exchange(newIndicesScopeGuard.ptr, nullptr)};
1301 }, indices);
1302 }
1303
1304 template<class A>
1305 constexpr void SparseIndexRangeHelper<A>::growLastIndexRange(std::span<size_type> offsets, IndexVariant& indices, double growth_factor, const allocator_type& allocator){
1306 const size_type old_count = offsets.back();
1307 const size_type new_count = std::max<size_type>(old_count*growth_factor, offsets.size() - 1);
1308 SparseIndexRangeHelper<A>::growIndices(offsets, indices, new_count, allocator, [allocator]<typename index_t>(std::span<size_type> offsets, std::span<const index_t> old_indices, std::span<index_t> new_indices){
1309 using index_alloc_type = typename alloc_traits::template rebind_alloc<index_t>;
1310 using index_alloc_traits = std::allocator_traits<index_alloc_type>;
1311 index_alloc_type index_alloc(allocator);
1312 // uninitialized copy of old indices into new indices
1313 for (size_type j = 0; j != old_indices.size(); ++j)
1314 index_alloc_traits::construct(index_alloc, std::addressof(new_indices[j]), old_indices[j]);
1315
1316 // initialize the remaining tail of the new indices with invalid value
1317 for (size_type j = old_indices.size(); j != new_indices.size(); ++j)
1318 index_alloc_traits::construct(index_alloc, std::addressof(new_indices[j]), std::numeric_limits<index_t>::max());
1319
1320 // update offset with new size
1321 offsets.back() = new_indices.size();
1322 });
1323 }
1324
1325 template<class A>
1326 constexpr void SparseIndexRangeHelper<A>::growAllIndexRanges(std::span<size_type> offsets, IndexVariant& indices, double target_growth_factor, const allocator_type& allocator){
1327
1328 if (target_growth_factor <= 1.0 or offsets.size() <= 1)
1329 return;
1330 constexpr size_type min_extra_indices = 1;
1331 size_type max_is_used = 0;
1332 size_type total_used_indices = 0;
1333 // compute the total number of used indices across all index sets and the maximum number of used indices in any index set to estimate the new count of indices after growth
1334 std::visit([offsets, &max_is_used, &total_used_indices]<class index_t>(index_t const * indices_ptr) {
1335 // grow number of indices for all index sets by target_growth_factor times the current size or by the small index set threshold, whichever is larger
1336 for (size_type i = 0; i + 1 != offsets.size(); ++i) {
1337 // get set of indices
1338 std::span index_set(indices_ptr + offsets[i], offsets[i+1] - offsets[i]);
1339
1340 // find insertion position for new index to get number of used indices in the index set
1341 auto it = lowerBound(index_set, std::numeric_limits<index_t>::max());
1342 size_type used_indices = std::distance(std::cbegin(index_set), it);
1343
1344 max_is_used = std::max(max_is_used, used_indices);
1345 total_used_indices += used_indices;
1346 }
1347 }, indices);
1348 size_type target_extra_per_index_set = std::max(static_cast<size_type>(max_is_used*(target_growth_factor - 1.)), min_extra_indices);
1349 size_type target_extra_indices = (offsets.size() - 1) * target_extra_per_index_set;
1350 size_type new_count = total_used_indices + target_extra_indices;
1351
1352 growIndices(offsets, indices, new_count, allocator, [allocator, min_extra_indices, total_used_indices, new_count]<typename index_t>(std::span<size_type> offsets, std::span<const index_t> old_indices, std::span<index_t> new_indices){
1353 using index_alloc_type = typename alloc_traits::template rebind_alloc<index_t>;
1354 using index_alloc_traits = std::allocator_traits<index_alloc_type>;
1355 index_alloc_type index_alloc(allocator);
1356
1357 // indices may have grown by more than the target extra indices, so we compute an average extra indices per index set
1358 (void)new_count;
1359 assert(new_count >= new_indices.size() && "Allocated index storage must be at least as large as the new count of indices after growth");
1360
1361 const size_type extra_per_index_set = (new_indices.size() - total_used_indices) / (offsets.size() - 1);
1362 (void)min_extra_indices;
1363 assert(extra_per_index_set >= min_extra_indices && "New index storage must accommodate at least min_extra_indices extra index per used index to ensure progress");
1364
1365 // carry offset to keep track of the current position in the new indices array
1366 size_type old_row_begin = 0;
1367 for (size_type i = 0; i + 1 != offsets.size(); ++i) {
1368 const size_type old_row_end = offsets[i+1];
1369 // get set of indices
1370 std::span old_index_set = old_indices.subspan(old_row_begin, old_row_end - old_row_begin);
1371
1372 // find insertion position for new index to get number of used indices in the index set
1373 auto it = SparseIndexRangeHelper<A>::lowerBound(old_index_set, std::numeric_limits<index_t>::max());
1374 size_type used_indices = std::distance(std::cbegin(old_index_set), it);
1375 // actual new size of i-th index set
1376 size_type new_size = used_indices + extra_per_index_set;
1377 new_size = std::min(new_size, new_indices.size() - offsets[i]); // ensure that we do not exceed the allocated size
1378
1379 std::span new_index_set = new_indices.subspan(offsets[i], new_size);
1380 // uninitialized copy of old indices into new indices
1381 for (size_type j = 0; j != used_indices; ++j)
1382 index_alloc_traits::construct(index_alloc, std::addressof(new_index_set[j]), old_index_set[j]);
1383 // initialize the remaining tail of the new index set with invalid value
1384 for (size_type j = used_indices; j != new_size; ++j)
1385 index_alloc_traits::construct(index_alloc, std::addressof(new_index_set[j]), std::numeric_limits<index_t>::max());
1386
1387 // update offset for next index set by the new size of the current index set and move begin of old row to end of old row
1388 old_row_begin = old_row_end;
1389 offsets[i+1] = offsets[i] + new_size;
1390 }
1391
1392 // due to averaging, it may happen that we do not use all the allocated indices, so we initialize the remaining tail of the new indices with invalid value
1393 for (size_type j = offsets.back(); j != new_indices.size(); ++j)
1394 index_alloc_traits::construct(index_alloc, std::addressof(new_indices[j]), std::numeric_limits<index_t>::max());
1395
1396 // update offset with new size
1397 offsets.back() = new_indices.size();
1398 });
1399 }
1400
1401 // ---------------------------------------------------------------------------
1402 // Impl::SparseIndexRangeHelper::DeallocationGuard declaration/implementation
1403 // ---------------------------------------------------------------------------
1404
1405 template <class A>
1406 template <class OtherAlloc>
1407 struct SparseIndexRangeHelper<A>::DeallocationGuard
1408 {
1409 using alloc_traits = std::allocator_traits<OtherAlloc>;
1410
1411 constexpr DeallocationGuard(OtherAlloc allocator, typename alloc_traits::pointer ptr, typename alloc_traits::size_type size)
1412 : allocator(allocator), ptr(ptr), size(size)
1413 {
1414 }
1415
1416 DeallocationGuard(const DeallocationGuard &) = delete;
1417 DeallocationGuard &operator=(const DeallocationGuard &) = delete;
1418
1419 constexpr ~DeallocationGuard()
1420 {
1421 if (ptr)
1422 alloc_traits::deallocate(allocator, ptr, size);
1423 }
1424
1425 OtherAlloc allocator;
1426 typename alloc_traits::pointer ptr;
1427 typename alloc_traits::size_type size;
1428 };
1429
1430} // namespace Impl
1431
1432
1433 // ---------------------------------------------------------------------------
1434 // SparseIndexRanges implementations
1435 // ---------------------------------------------------------------------------
1436
1437 template <class I, class A>
1438 template<class OtherAlloc>
1439 requires std::constructible_from<A, OtherAlloc>
1441 : size_(builder.size_),
1442 is_size_(builder.is_size_),
1443 offsets_(nullptr),
1444 indices_(nullptr),
1445 alloc_(builder.alloc_)
1446 {
1447 assert(builder.stage_ == SequencedSparseIndexRangeBuilder<OtherAlloc>::Stage::Built && "Sparsity pattern is not in correct build mode");
1448
1449 // Compress on builder-owned storage
1450 using IndexVariant = std::variant<I*>;
1451 IndexVariant indices_var = Impl::SparseIndexRangeHelper<A>::template compress<IndexVariant>(std::span(builder.offsets_, size_ + 1), builder.indices_, alloc_);
1452
1453 // Transfer ownership only after compression succeeds
1454 offsets_ = std::exchange(builder.offsets_, nullptr);
1455 indices_ = std::get<I*>(std::move(indices_var));
1456 size_ = std::exchange(builder.size_, 0);
1457 is_size_ = std::exchange(builder.is_size_, 0);
1458
1460 }
1461
1462 template <class I, class A>
1463 template<class OtherAlloc>
1464 requires std::constructible_from<A, OtherAlloc>
1466 : size_(builder.size_),
1467 is_size_(builder.is_size_),
1468 offsets_(nullptr),
1469 indices_(nullptr),
1470 alloc_(builder.alloc_)
1471 {
1472 assert(builder.stage_ == UnsequencedSparseIndexRangeBuilder<OtherAlloc>::Stage::BuildSetIndices && "Sparsity pattern is not in correct build mode");
1473
1474 // Compress on builder-owned storage
1475 using IndexVariant = std::variant<I*>;
1476 IndexVariant indices_var = Impl::SparseIndexRangeHelper<A>::template compress<IndexVariant>(std::span(builder.offsets_, size_ + 1), builder.indices_, alloc_);
1477
1478 // Transfer ownership only after compression succeeds
1479 offsets_ = std::exchange(builder.offsets_, nullptr);
1480 indices_ = std::get<I*>(std::move(indices_var));
1481 size_ = std::exchange(builder.size_, 0);
1482 is_size_ = std::exchange(builder.is_size_, 0);
1483
1485 }
1486
1487 template <class I, class A>
1489 : size_(std::exchange(other.size_, 0)),
1490 is_size_(std::exchange(other.is_size_, 0)),
1491 offsets_(std::exchange(other.offsets_, nullptr)),
1492 indices_(std::exchange(other.indices_, nullptr)),
1493 alloc_(std::move(other.alloc_))
1494 {}
1495
1496 template <class I, class A>
1498 {
1499 if (this == &other)
1500 return *this;
1501
1502 deallocate(alloc_, indices_, offsets_, size_);
1503
1504 is_size_ = std::exchange(other.is_size_, 0);
1505 size_ = std::exchange(other.size_, 0);
1506 offsets_ = std::exchange(other.offsets_, nullptr);
1507 indices_ = std::exchange(other.indices_, nullptr);
1508 alloc_ = std::move(other.alloc_);
1509 return *this;
1510 }
1511
1512 template <class I, class A>
1514 {
1515 if (this == &other)
1516 return;
1517
1520 if (alloc_ != other.alloc_)
1522 "Cannot swap SparseIndexRanges with unequal allocators when propagate_on_container_swap is false");
1523
1524 using std::swap;
1525 swap(size_, other.size_);
1526 swap(is_size_, other.is_size_);
1527 swap(offsets_, other.offsets_);
1528 swap(indices_, other.indices_);
1530 swap(alloc_, other.alloc_);
1531 }
1532
1533 template <class I, class A>
1535 {
1536 Impl::SparseIndexRangeHelper<A>::deallocate(alloc_, indices_, offsets_, size_);
1537 }
1538
1539 template <class I, class A>
1540 constexpr auto SparseIndexRanges<I,A>::size() const noexcept -> size_type
1541 {
1542 return size_;
1543 }
1544
1545 template <class I, class A>
1546 constexpr auto SparseIndexRanges<I,A>::range() const noexcept -> IntegralRange<index_type>
1547 {
1548 return {0, is_size_};
1549 }
1550
1551 template <class I, class A>
1553 {
1554 return offset(size_);
1555 }
1556
1557 template <class I, class A>
1559 {
1560 assert(offsets_);
1561 return std::span(offsets_, size_ + 1)[i];
1562 }
1563
1564 template <class I, class A>
1566 {
1567 for (size_type i = 0; i != size_; ++i)
1568 {
1569 os << "[" << i << "]: ";
1570 std::span index_set(indices_ + offsets_[i], offsets_[i + 1] - offsets_[i]);
1571 for (auto idx : index_set)
1572 os << static_cast<size_type>(idx) << " ";
1573 os << '\n';
1574 };
1575 return os;
1576 }
1577
1578 // ---------------------------------------------------------------------------
1579 // SparseIndexRanges::Iterator implementation
1580 // ---------------------------------------------------------------------------
1581
1582 template <class I, class A>
1583 constexpr SparseIndexRanges<I,A>::Iterator::Iterator(SparseIndexRanges const *sp, size_type i)
1584 : cis_(sp), i_(i)
1585 {}
1586
1587 template <class I, class A>
1589 {
1590 return IndexRangeView(cis_, i_);
1591 }
1592
1593 template <class I, class A>
1594 constexpr auto SparseIndexRanges<I,A>::Iterator::baseIterator() const -> const size_type &
1595 {
1596 return i_;
1597 }
1598
1599 template <class I, class A>
1601 {
1602 return i_;
1603 }
1604
1605 // ---------------------------------------------------------------------------
1606 // SparseIndexRanges::IndexIterator implementation
1607 // ---------------------------------------------------------------------------
1608
1609 template <class I, class A>
1611 : cis_(csi), i_(i), j_(j)
1612 {}
1613
1614 template <class I, class A>
1616 {
1617 return cis_->offsets_[i_] + j_;
1618 }
1619
1620 template <class I, class A>
1622 {
1623 return cis_->indices_[offset()];
1624 }
1625
1626 template <class I, class A>
1627 constexpr auto SparseIndexRanges<I,A>::IndexIterator::baseIterator() const -> const size_type &
1628 {
1629 return j_;
1630 }
1631
1632 template <class I, class A>
1634 {
1635 return j_;
1636 }
1637
1638 // ---------------------------------------------------------------------------
1639 // SparseIndexRanges::IndexRangeView implementation
1640 // ---------------------------------------------------------------------------
1641
1642 template <class I, class A>
1643 constexpr SparseIndexRanges<I,A>::IndexRangeView::IndexRangeView(const SparseIndexRanges *cis, size_type i)
1644 : cis_(cis), i_(i)
1645 {}
1646
1647 template <class I, class A>
1649 {
1650 return {cis_, i_, 0};
1651 }
1652
1653 template <class I, class A>
1655 {
1656 return {cis_, i_, size()};
1657 }
1658
1659 template <class I, class A>
1661 {
1662 return cis_->offsets_[i_ + 1] - cis_->offsets_[i_];
1663 }
1664
1665 template <class I, class A>
1667 {
1668 return {0, cis_->is_size_};
1669 }
1670
1671 template <class I, class A>
1673 {
1674 auto index_set = std::span(cis_->indices_ + cis_->offsets_[i_], size());
1675 auto lb = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, j);
1676 return lb != std::cend(index_set);
1677 }
1678
1679 template <class I, class A>
1681 {
1682 std::span index_set(cis_->indices_ + begin().offset(), end().offset() - begin().offset());
1683 auto it = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, j);
1684 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1685 return {};
1686 }
1687
1688 template <class I, class A>
1690 {
1691 std::span index_set(cis_->indices_ + begin().offset(), end().offset() - begin().offset());
1692 auto it = Impl::SparseIndexRangeHelper<A>::upperBound(index_set, j);
1693 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1694 }
1695
1696 template <class I, class A>
1698 {
1699 std::span index_set(cis_->indices_ + begin().offset(), end().offset() - begin().offset());
1700 auto it = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, j);
1701 if (it != std::cend(index_set) && *it == j)
1702 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1703 else
1704 return end();
1705 }
1706
1707 // ---------------------------------------------------------------------------
1708 // CompressedSparseIndexRanges implementations
1709 // ---------------------------------------------------------------------------
1710
1711 template <class A>
1712 template<class OtherAlloc>
1713 requires std::constructible_from<A, OtherAlloc>
1715 : size_(builder.size_),
1716 is_size_(builder.is_size_),
1717 offsets_(nullptr),
1718 indices_(static_cast<uint_least64_t *>(nullptr)),
1719 alloc_(builder.alloc_)
1720 {
1721 assert(builder.stage_ == SequencedSparseIndexRangeBuilder<OtherAlloc>::Stage::Built && "Sparsity pattern is not in correct build mode");
1722
1723 // Compress on builder-owned storage
1724 IndexVariant compressed_indices = Impl::SparseIndexRangeHelper<A>::compress(std::span<size_type>(builder.offsets_, size_ + 1), builder.indices_, alloc_);
1725
1726 // Transfer ownership only after compression succeeds
1727 offsets_ = std::exchange(builder.offsets_, nullptr);
1728 indices_ = std::move(compressed_indices);
1729 size_ = std::exchange(builder.size_, 0);
1730 is_size_ = std::exchange(builder.is_size_, 0);
1731
1733 }
1734
1735 template <class A>
1736 template<class OtherAlloc>
1737 requires std::constructible_from<A, OtherAlloc>
1739 : size_(builder.size_),
1740 is_size_(builder.is_size_),
1741 offsets_(nullptr),
1742 indices_(static_cast<uint_least64_t *>(nullptr)),
1743 alloc_(builder.alloc_)
1744 {
1745 assert(builder.stage_ == UnsequencedSparseIndexRangeBuilder<OtherAlloc>::Stage::BuildSetIndices && "Sparsity pattern is not in correct build mode");
1746
1747 // Compress on builder-owned storage
1748 IndexVariant compressed_indices = Impl::SparseIndexRangeHelper<A>::compress(std::span<size_type>(builder.offsets_, size_ + 1), builder.indices_, alloc_);
1749
1750 // Transfer ownership only after compression succeeds
1751 offsets_ = std::exchange(builder.offsets_, nullptr);
1752 indices_ = std::move(compressed_indices);
1753 size_ = std::exchange(builder.size_, 0);
1754 is_size_ = std::exchange(builder.is_size_, 0);
1755
1757 }
1758
1759 template <class A>
1761 : size_(std::exchange(other.size_, 0)),
1762 is_size_(std::exchange(other.is_size_, 0)),
1763 offsets_(std::exchange(other.offsets_, nullptr)),
1764 indices_(std::move(other.indices_)),
1765 alloc_(std::move(other.alloc_))
1766 {
1767 other.indices_ = IndexVariant{std::in_place_type<uint_least64_t *>, nullptr};
1768 }
1769
1770 template <class A>
1772 {
1773 if (this == &other)
1774 return *this;
1775
1776 deallocate(alloc_, indices_, offsets_, size_);
1777
1778 is_size_ = std::exchange(other.is_size_, 0);
1779 size_ = std::exchange(other.size_, 0);
1780 offsets_ = std::exchange(other.offsets_, nullptr);
1781 indices_ = std::move(other.indices_);
1782 other.indices_ = IndexVariant{std::in_place_type<uint_least64_t *>, nullptr};
1783 alloc_ = std::move(other.alloc_);
1784 return *this;
1785 }
1786
1787 template <class A>
1789 {
1790 if (this == &other)
1791 return;
1792
1795 if (alloc_ != other.alloc_)
1797 "Cannot swap CompressedSparseIndexRanges with unequal allocators when propagate_on_container_swap is false");
1798
1799 using std::swap;
1800 swap(size_, other.size_);
1801 swap(is_size_, other.is_size_);
1802 swap(offsets_, other.offsets_);
1803 swap(indices_, other.indices_);
1805 swap(alloc_, other.alloc_);
1806 }
1807
1808 template <class A>
1810 {
1811 Impl::SparseIndexRangeHelper<A>::deallocate(alloc_, indices_, offsets_, size_);
1812 }
1813
1814 template <class A>
1815 constexpr auto CompressedSparseIndexRanges<A>::size() const noexcept -> size_type
1816 {
1817 return size_;
1818 }
1819
1820 template <class A>
1822 {
1823 return {0, is_size_};
1824 }
1825
1826 template <class A>
1828 {
1829 return offset(size_);
1830 }
1831
1832 template <class A>
1834 {
1835 assert(offsets_);
1836 assert(i <= size_ && "Offset index out of bounds");
1837 return *(offsets_ + i);
1838 }
1839
1840 template <class A>
1842 {
1843 std::visit([&]<typename index_t>(index_t *indices)
1844 {
1845 for (size_type i = 0; i != size_; ++i)
1846 {
1847 os << "[" << i << "]: ";
1848 std::span index_set(indices + offsets_[i], offsets_[i + 1] - offsets_[i]);
1849 for (auto idx : index_set)
1850 os << static_cast<size_type>(idx) << " ";
1851 os << '\n';
1852 } }, indices_);
1853 return os;
1854 }
1855
1856 // ---------------------------------------------------------------------------
1857 // CompressedSparseIndexRanges::Iterator implementation
1858 // ---------------------------------------------------------------------------
1859
1860 template <class A>
1862 : cis_(sp), i_(i)
1863 {}
1864
1865 template <class A>
1867 {
1868 return IndexRangeView(cis_, i_);
1869 }
1870
1871 template <class A>
1873 {
1874 return i_;
1875 }
1876
1877 template <class A>
1879 {
1880 return i_;
1881 }
1882
1883 // ---------------------------------------------------------------------------
1884 // CompressedSparseIndexRanges::IndexIterator implementation
1885 // ---------------------------------------------------------------------------
1886
1887 template <class A>
1891
1892 template <class A>
1894 {
1895 return cis_->offsets_[i_] + j_;
1896 }
1897
1898 template <class A>
1900 {
1901 return std::visit([o = offset()](auto indices) -> index_type {
1902 return static_cast<index_type>(indices[o]);
1903 }, cis_->indices_);
1904 }
1905
1906 template <class A>
1908 {
1909 return j_;
1910 }
1911
1912 template <class A>
1914 {
1915 return j_;
1916 }
1917
1918 // ---------------------------------------------------------------------------
1919 // CompressedSparseIndexRanges::IndexRangeView implementation
1920 // ---------------------------------------------------------------------------
1921
1922 template <class A>
1923 constexpr CompressedSparseIndexRanges<A>::IndexRangeView::IndexRangeView(const CompressedSparseIndexRanges *cis, size_type i)
1924 : cis_(cis), i_(i)
1925 {}
1926
1927 template <class A>
1929 {
1930 return {cis_, i_, 0};
1931 }
1932
1933 template <class A>
1935 {
1936 return {cis_, i_, size()};
1937 }
1938
1939 template <class A>
1941 {
1942 return cis_->offsets_[i_ + 1] - cis_->offsets_[i_];
1943 }
1944
1945 template <class A>
1947 {
1948 return {0, cis_->is_size_};
1949 }
1950
1951 template <class A>
1953 {
1954 return std::visit([this, j]<typename index_t>(index_t *indices) -> bool {
1955 auto index_set = std::span(indices + cis_->offsets_[i_], size());
1956 auto lb = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, j);
1957 return lb != std::cend(index_set);
1958 }, cis_->indices_);
1959 }
1960
1961 template <class A>
1963 {
1964 return std::visit([&]<typename index_t>(index_t *indices) {
1965 std::span<const index_t> index_set(indices + begin().offset(), end().offset() - begin().offset());
1966 auto it = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, static_cast<index_t>(j));
1967 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1968 }, cis_->indices_);
1969 }
1970
1971 template <class A>
1973 {
1974 return std::visit([&]<typename index_t>(index_t *indices) {
1975 std::span<const index_t> index_set(indices + begin().offset(), end().offset() - begin().offset());
1976 auto it = Impl::SparseIndexRangeHelper<A>::upperBound(index_set, static_cast<index_t>(j));
1977 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1978 }, cis_->indices_);
1979 }
1980
1981 template <class A>
1983 {
1984 return std::visit([&]<typename index_t>(index_t *indices) {
1985 std::span<const index_t> index_set(indices + begin().offset(), end().offset() - begin().offset());
1986 auto it = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, static_cast<index_t>(j));
1987 if (it != std::cend(index_set) && *it == static_cast<index_t>(j))
1988 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1989 else
1990 return end();
1991 }, cis_->indices_);
1992 }
1993
1994 // ---------------------------------------------------------------------------
1995 // UnsequencedSparseIndexRangeBuilder declaration
1996 // ---------------------------------------------------------------------------
1997
1998 template <class A>
2000 : size_(size),
2001 is_size_(is_size),
2002 offsets_(nullptr),
2003 indices_(static_cast<uint_least64_t *>(nullptr)),
2004 alloc_(allocator),
2005 stage_(Stage::BuildSizes)
2006 {
2007 offsets_ = Impl::SparseIndexRangeHelper<A>::initAllocateOffsets(alloc_, size_);
2008 std::span offsets(offsets_, size_ + 1);
2009 // initialize offsets with equal distribution
2010 for (size_type i = 0; i + 1 < offsets.size(); ++i)
2011 offsets[i] = avg_indices;
2012 offsets.back() = 0;
2013 }
2014
2015 template <class A>
2017 {
2018 Impl::SparseIndexRangeHelper<A>::deallocate(alloc_, indices_, offsets_, size_);
2019 }
2020
2021 template <class A>
2023 {
2024 assert(stage_ == Stage::BuildSizes && "Sparsity pattern is not in correct build mode");
2025 offsets_[i] = s;
2026 }
2027
2028 template <class A>
2030 {
2031 assert(stage_ == Stage::BuildSizes && "Sparsity pattern is not in correct build mode");
2032 offsets_[i] += increment;
2033 }
2034
2035 template <class A>
2037 {
2038 assert(stage_ == Stage::BuildSizes && "Sparsity pattern is not in correct build mode");
2039 stage_ = Stage::BuildSetIndices;
2040 // compute offsets from index set sizes
2041 std::exclusive_scan(offsets_, offsets_ + size_ + 1, offsets_, size_type{0});
2042
2043 std::visit([](auto ptr){
2044 assert(ptr == nullptr && "Indices pointer must be null before initialization");
2045 }, indices_);
2046 std::tie(indices_, offsets_[size_]) = Impl::SparseIndexRangeHelper<A>::initAllocateIndices(alloc_, offsets_[size_], is_size_);
2047 }
2048
2049 template <class A>
2051 {
2052 assert(i < size_ && "There are less index sets than expected for given index i");
2053 assert(j < is_size_ && "Out of bounds of index set range");
2054
2055 if (stage_ == Stage::BuildSizes)
2056 endSizes();
2057
2058 assert(stage_ == Stage::BuildSetIndices && "Sparsity pattern is not in correct build mode");
2059
2060 assert(offsets_[i + 1] >= offsets_[i] && "Invalid offsets");
2061 auto offsets = std::span(offsets_, size_ + 1);
2062
2063 std::visit([this, j, i, offsets]<class index_t>(index_t * indices) {
2064 index_t jj = static_cast<index_t>(j);
2065 Impl::SparseIndexRangeHelper<A>::addIndex(std::span(indices + offsets[i], indices + offsets[i + 1]), jj, [this, i, offsets]() -> std::span<index_t> {
2066 double growth_factor = 2.5;
2067 Impl::SparseIndexRangeHelper<A>::growAllIndexRanges(offsets, indices_, growth_factor, alloc_);
2068 index_t * indices = std::get<index_t *>(indices_);
2069 return std::span(indices + offsets[i], indices + offsets[i + 1]);
2070 });
2071 }, indices_);
2072 }
2073
2074 // ---------------------------------------------------------------------------
2075 // SequencedSparseIndexRangeBuilder implementation
2076 // ---------------------------------------------------------------------------
2077
2078 template<class A>
2080 : size_{size}
2081 , is_size_{is_size}
2082 , alloc_{allocator}
2083 {
2084 offsets_ = Impl::SparseIndexRangeHelper<A>::initAllocateOffsets(alloc_, size_);
2085 std::tie(indices_, offsets_[size_]) = Impl::SparseIndexRangeHelper<A>::initAllocateIndices(allocator, avg_indices * size_, is_size_);
2086 stage_ = Stage::Allocated;
2087 }
2088
2089 template<class A>
2091 {
2092 Impl::SparseIndexRangeHelper<A>::deallocate(alloc_, indices_, offsets_, size_);
2093 }
2094
2095 template<class A>
2097 {
2098 return {this};
2099 }
2100
2101 template<class A>
2103 {
2104 return {};
2105 }
2106
2107 template<class A>
2109 : builder_{builder}
2110 , i_{0}
2111 , is_count_{0}
2112 , stage_{Stage::BuildingIndexRanges}
2113 {
2114 assert(builder_->stage_ == Stage::Allocated && "IndexRangeBuilder can only be created once");
2115 builder_->stage_ = stage_;
2116 builder_->offsets_[i_ + 1] = size_type(builder_->offsets_[builder_->size_] > 0);
2117 }
2118
2119 template<class A>
2123
2124 template<class A>
2126 builder_ = std::exchange(other.builder_, nullptr);
2127 i_ = std::exchange(other.i_, 0);
2128 is_count_ = std::exchange(other.is_count_, 0);
2129 stage_ = std::exchange(other.stage_, Stage::Invalid);
2130 return *this;
2131 }
2132
2133 template<class A>
2135 assert(builder_->stage_ == stage_);
2136 assert(i_ < builder_->size_ && "There are less index sets than expected for given index i");
2137 assert(j < builder_->is_size_ && "Out of bounds of index set range");
2138 assert(*this != EndSentinel() && "The IndexRangeBuilder is past the last index set");
2139
2140 std::span<size_type> offsets(builder_->offsets_, builder_->size_ + 1);
2141
2142 assert(offsets[i_ + 1] >= offsets[i_] && "Invalid offsets");
2143
2144 // add index j to i-th index set. Grow offsets if needed.
2145 bool new_index_added = std::visit([offsets, j, i = i_, builder = builder_]<class index_t>(index_t * indices) {
2146 index_t jj = static_cast<index_t>(j);
2147 return Impl::SparseIndexRangeHelper<A>::addIndex(std::span(indices + offsets[i], indices + offsets[i + 1]), jj, [i, offsets, builder]() -> std::span<index_t> {
2148 double growth_factor = 2.0;
2149 const size_type old_size = offsets[i + 1] - offsets[i];
2150 // check if there is space left in the tail of the indices, if not, grow the indices by expanding the last index set
2151 if (offsets.back() - offsets[i + 1] == 0)
2152 Impl::SparseIndexRangeHelper<A>::growLastIndexRange(offsets, builder->indices_, growth_factor, builder->alloc_);
2153 // set new size of current index set by growth_factor times the current size or by the small index set threshold, whichever is larger
2154 size_type target_new_size = std::max<size_type>(std::max<size_type>(1, old_size * growth_factor), Impl::SPARSE_SEARCH_THRESHOLD/sizeof(index_type));
2155 // grow index set up to the current buffer boundary
2156 offsets[i + 1] = std::min(offsets[i + 1] + target_new_size, offsets.back());
2157 index_t* indices = std::get<index_t *>(builder->indices_);
2158 auto index_set = std::span(indices + offsets[i], indices + offsets[i + 1]);
2159 assert(index_set.size() > old_size && "Invalid offsets after growth");
2160 return index_set;
2161 });
2162 }, builder_->indices_);
2163
2164 if (new_index_added)
2165 ++is_count_;
2166 }
2167
2168 template<class A>
2169 template<std::forward_iterator It, std::sentinel_for<It> End>
2171 End end,
2172 bool do_sort,
2173 bool do_unique)
2174 {
2175 assert(builder_->stage_ == stage_);
2176 assert(i_ < builder_->size_ && "There are less index sets than expected for given index i");
2177 assert(*this != EndSentinel() && "The IndexRangeBuilder is past the last index set");
2178
2179 std::span<size_type> offsets(builder_->offsets_, builder_->size_ + 1);
2180 assert(offsets[i_ + 1] >= offsets[i_] && "Invalid offsets");
2181
2182 const size_type row_begin = offsets[i_];
2183 const size_type requested_count = static_cast<size_type>(std::distance(begin, end));
2184 const size_type available_count = offsets.back() - row_begin;
2185
2186 if (available_count < requested_count) {
2187 const double growth_factor =
2188 offsets.back() > 0
2189 ? static_cast<double>(offsets.back() + (requested_count - available_count) + 1) / static_cast<double>(offsets.back())
2190 : 2.0;
2191 Impl::SparseIndexRangeHelper<A>::growLastIndexRange(offsets,
2192 builder_->indices_,
2193 growth_factor,
2194 builder_->alloc_);
2195 }
2196
2197 assert(offsets.back() - row_begin >= requested_count && "Growing index set failed to provide enough space for new indices");
2198
2199 // Extend the active buffer for this row when the replacement needs more room.
2200 offsets[i_ + 1] = std::max(offsets[i_ + 1], row_begin + requested_count);
2201
2202 size_type final_count = requested_count;
2203 std::visit([&, row_begin]<class index_t>(index_t * indices) {
2204 constexpr index_t invalid_index = std::numeric_limits<index_t>::max();
2205 auto row = std::span(indices + row_begin, offsets[i_ + 1] - row_begin);
2206
2207 auto out = std::begin(row);
2208 for (auto it = begin; it != end; ++it, ++out) {
2209 assert(*it < builder_->is_size_ && "Out of bounds of index set range");
2210 *out = static_cast<index_t>(*it);
2211 }
2212
2213 if (do_sort)
2214 std::sort(std::begin(row), out);
2215 if (do_unique)
2216 out = std::unique(std::begin(row), out);
2217
2218 std::fill(out, std::end(row), invalid_index);
2219 final_count = static_cast<size_type>(std::distance(std::cbegin(row), out));
2220 }, builder_->indices_);
2221
2222 is_count_ = final_count;
2223 }
2224
2225 template<class A>
2227 assert(builder_->stage_ == stage_);
2228 assert(i_ < builder_->size_ && "There are less index sets than expected for given index i");
2229 assert(j < builder_->is_size_ && "Out of bounds of index set range");
2230 assert(*this != EndSentinel() && "The IndexRangeBuilder is past the last index set");
2231 size_type offsets_begin = builder_->offsets_[i_];
2232 size_type offsets_end = builder_->offsets_[i_ + 1];
2233 assert(offsets_end >= offsets_begin && "Invalid offsets");
2234
2235 return std::visit([&]<typename index_t>(const index_t *indices) {
2236 // get set of indices
2237 std::span index_set(indices + offsets_begin, offsets_end - offsets_begin);
2238 const index_t jj = static_cast<index_t>(j);
2239
2240 // find correct insertion position for new index
2241 auto it = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, jj);
2242
2243 // check if index is in the set
2244 return it != std::cend(index_set) && *it == jj;
2245 }, builder_->indices_);
2246 }
2247
2248 template<class A>
2250 return i_;
2251 }
2252
2253 template<class A>
2255 assert(builder_ && "IndexRangeBuilder is not valid");
2256 assert(builder_->stage_ == Stage::BuildingIndexRanges);
2257 assert(*this != EndSentinel() && "The IndexRangeBuilder is past the last index set");
2258
2259 auto offsets = std::span(builder_->offsets_, builder_->size_ + 1);
2260 size_type current_offset = offsets[i_];
2261 ++i_;
2262 // set next index set to have a small index set or less
2263 if (i_ < builder_->size_) {
2264 const std::size_t index_size = std::visit([]<class index_t>(index_t *) { return sizeof(index_t); }, builder_->indices_);
2265 // set up next index set to have SPARSE_SEARCH_THRESHOLD bytes of indices or less
2266 offsets[i_] = current_offset + std::exchange(is_count_, 0);
2267 offsets[i_ + 1] = std::min(offsets[i_] + std::max<size_type>(Impl::SPARSE_SEARCH_THRESHOLD/index_size, 1), offsets[builder_->size_]);
2268 } else {
2269 stage_ = builder_->stage_ = Stage::Built;
2270 }
2271 return *this;
2272 }
2273
2274 template<class A>
2276 assert(*this != EndSentinel() && "The IndexRangeBuilder is past the last index set");
2277 return is_count_;
2278 }
2279
2280 template<class A>
2284
2285} // namespace Dune::Experimental
2286
2287// Opt-in to std::ranges::borrowed_range for any type derived from Impl::SparseIndexRangesBase.
2288template <std::derived_from<Dune::Experimental::Impl::SparseIndexRangesBase> T>
2289constexpr bool std::ranges::enable_borrowed_range<T> = true;
2290
2291#endif // DUNE_ISTL_EXPERIMENTAL_SPARSEINDEXRANGES_HH
Traits for sets of indices.
#define DUNE_NO_UNIQUE_ADDRESS
STL namespace.
Definition defaultmatrixvectortraits.hh:14
int size() const
SLList< T, A > & operator=(const SLList< T, A > &other)
bool empty() const
void increment()
void construct(pointer p, const_reference value)
void deallocate(pointer p, std::size_t n)
bigunsignedint< k > operator*(const bigunsignedint< k > &x, std::uintmax_t y)
std::ptrdiff_t index() const
#define DUNE_THROW(E,...)
const_iterator find(int proc) const
constexpr std::bool_constant<((II==value)||...)> contains(std::integer_sequence< T, II... >, std::integral_constant< T, value >)
Traits for sets of indices.
Definition indexrange.hh:55
Compact storage of multiple sorted sparse index sets sharing a single allocation.
Definition sparseindexranges.hh:489
constexpr SparseIndexRanges(SparseIndexRanges &&other)
Move-construct, transferring ownership of storage from other. other is left in a valid but empty stat...
Definition sparseindexranges.hh:1488
constexpr SparseIndexRanges(SequencedSparseIndexRangeBuilder< OtherAlloc > &&builder)
Construct from a finished SequencedSparseIndexRangeBuilder, taking ownership of its storage and compr...
Definition sparseindexranges.hh:1440
constexpr IntegralRange< index_type > range() const noexcept
Returns the integer range [0, n) that every stored index belongs to.
Definition sparseindexranges.hh:1546
constexpr void swap(SparseIndexRanges &other)
Swap contents with other honoring allocator swap propagation traits.
Definition sparseindexranges.hh:1513
constexpr friend bool operator==(const SparseIndexRanges &p1, const SparseIndexRanges &p2)
Returns true if p1 and p2 contain identical index sets with identical values.
Definition sparseindexranges.hh:575
constexpr Iterator end() const
Returns an iterator past the last index set.
Definition sparseindexranges.hh:569
constexpr Iterator begin() const
Returns an iterator to the first index set (index set 0).
Definition sparseindexranges.hh:566
constexpr size_type size() const noexcept
Returns the number of index sets (e.g., the number of rows in a sparse matrix).
Definition sparseindexranges.hh:1540
constexpr ~SparseIndexRanges()
Destroy the index sets and release all allocated memory.
Definition sparseindexranges.hh:1534
friend constexpr void swap(SparseIndexRanges &lhs, SparseIndexRanges &rhs)
Swap contents of lhs and rhs, honoring allocator swap propagation traits.
Definition sparseindexranges.hh:540
std::ostream & print(std::ostream &os) const
Writes all index sets to os, one per line in the form "[i]: j0 j1 ...". Returns os.
Definition sparseindexranges.hh:1565
constexpr size_type offset(size_type i) const
Definition sparseindexranges.hh:1558
I index_type
Type of the indices.
Definition sparseindexranges.hh:509
typename alloc_traits::size_type size_type
Type to access the index sets.
Definition sparseindexranges.hh:506
constexpr SparseIndexRanges(UnsequencedSparseIndexRangeBuilder< OtherAlloc > &&builder)
Construct from a finished UnsequencedSparseIndexRangeBuilder, taking ownership of its storage and com...
Definition sparseindexranges.hh:1465
constexpr size_type count() const
Returns the total number of indices stored across all index sets (sum of all individual set sizes).
Definition sparseindexranges.hh:1552
A allocator_type
Allocator type used for memory management.
Definition sparseindexranges.hh:512
constexpr SparseIndexRanges & operator=(SparseIndexRanges &&other)
Move-assign, releasing current storage and transferring ownership from other. other is left in a vali...
Definition sparseindexranges.hh:1497
Compact storage of multiple sorted sparse index sets sharing a single allocation.
Definition sparseindexranges.hh:784
constexpr Iterator begin() const
Returns an iterator to the first index set (index set 0).
Definition sparseindexranges.hh:863
constexpr friend bool operator==(const CompressedSparseIndexRanges &p1, const CompressedSparseIndexRanges &p2)
Returns true if p1 and p2 contain identical index sets with identical values.
Definition sparseindexranges.hh:872
constexpr CompressedSparseIndexRanges & operator=(CompressedSparseIndexRanges &&other)
Move-assign, releasing current storage and transferring ownership from other. other is left in a vali...
Definition sparseindexranges.hh:1771
uint_fast64_t index_type
Type of the indices.
Definition sparseindexranges.hh:806
constexpr CompressedSparseIndexRanges(UnsequencedSparseIndexRangeBuilder< OtherAlloc > &&builder)
Construct from a finished UnsequencedSparseIndexRangeBuilder, taking ownership of its storage and com...
Definition sparseindexranges.hh:1738
constexpr CompressedSparseIndexRanges(CompressedSparseIndexRanges &&other)
Move-construct, transferring ownership of storage from other. other is left in a valid but empty stat...
Definition sparseindexranges.hh:1760
std::ostream & print(std::ostream &os) const
Writes all index sets to os, one per line in the form "[i]: j0 j1 ...". Returns os.
Definition sparseindexranges.hh:1841
constexpr size_type size() const noexcept
Returns the number of index sets (e.g., the number of rows in a sparse matrix).
Definition sparseindexranges.hh:1815
constexpr size_type offset(size_type i) const
Definition sparseindexranges.hh:1833
constexpr void swap(CompressedSparseIndexRanges &other)
Swap contents with other honoring allocator swap propagation traits.
Definition sparseindexranges.hh:1788
constexpr IntegralRange< index_type > range() const noexcept
Returns the integer range [0, n) that every stored index belongs to.
Definition sparseindexranges.hh:1821
constexpr size_type count() const
Returns the total number of indices stored across all index sets (sum of all individual set sizes).
Definition sparseindexranges.hh:1827
typename alloc_traits::size_type size_type
Type to access the index sets.
Definition sparseindexranges.hh:803
constexpr CompressedSparseIndexRanges(SequencedSparseIndexRangeBuilder< OtherAlloc > &&builder)
Construct from a finished SequencedSparseIndexRangeBuilder, taking ownership of its storage and compr...
Definition sparseindexranges.hh:1714
constexpr Iterator end() const
Returns an iterator past the last index set.
Definition sparseindexranges.hh:866
constexpr ~CompressedSparseIndexRanges()
Destroy the index sets and release all allocated memory.
Definition sparseindexranges.hh:1809
friend constexpr void swap(CompressedSparseIndexRanges &lhs, CompressedSparseIndexRanges &rhs)
Swap contents of lhs and rhs, honoring allocator swap propagation traits.
Definition sparseindexranges.hh:837
A allocator_type
Allocator type used for memory management.
Definition sparseindexranges.hh:809
Builder that allows inserting indices into any index set in any order.
Definition sparseindexranges.hh:244
constexpr ~UnsequencedSparseIndexRangeBuilder()
Definition sparseindexranges.hh:2016
constexpr void setSize(size_type i, size_type s)
Set the reserved size of the i-th index set to s. Must be called before addIndex().
Definition sparseindexranges.hh:2022
constexpr UnsequencedSparseIndexRangeBuilder(const UnsequencedSparseIndexRangeBuilder &)=delete
constexpr UnsequencedSparseIndexRangeBuilder & operator=(UnsequencedSparseIndexRangeBuilder &&)=delete
uint_fast64_t index_type
Type of the indices.
Definition sparseindexranges.hh:262
constexpr UnsequencedSparseIndexRangeBuilder(size_type size, size_type is_size, size_type avg_indices=0, allocator_type allocator=allocator_type())
Construct an unsequenced builder for size index sets with index range [0, is_size).
Definition sparseindexranges.hh:1999
typename std::allocator_traits< allocator_type >::size_type size_type
Type of the size parameters.
Definition sparseindexranges.hh:259
A allocator_type
Definition sparseindexranges.hh:256
constexpr void addIndex(size_type i, index_type j)
Insert index j into index set i. Duplicate insertions are silently ignored.
Definition sparseindexranges.hh:2050
constexpr UnsequencedSparseIndexRangeBuilder(UnsequencedSparseIndexRangeBuilder &&)=delete
constexpr UnsequencedSparseIndexRangeBuilder & operator=(const UnsequencedSparseIndexRangeBuilder &)=delete
constexpr void incrementSize(size_type i, size_type increment=1)
Increment the reserved size of the i-th index set by increment. Must be called before addIndex().
Definition sparseindexranges.hh:2029
Builder that fills index sets one at a time, in sequential order.
Definition sparseindexranges.hh:329
uint_fast64_t index_type
Type of the indices.
Definition sparseindexranges.hh:348
constexpr SequencedSparseIndexRangeBuilder(const SequencedSparseIndexRangeBuilder &)=delete
constexpr EndSentinel end()
Returns the end sentinel used to detect when all index sets have been filled.
Definition sparseindexranges.hh:2102
constexpr SequencedSparseIndexRangeBuilder(SequencedSparseIndexRangeBuilder &&)=delete
constexpr SequencedSparseIndexRangeBuilder & operator=(SequencedSparseIndexRangeBuilder &&)=delete
constexpr IndexRangeBuilder begin()
Returns an IndexRangeBuilder positioned at the first index set.
Definition sparseindexranges.hh:2096
constexpr SequencedSparseIndexRangeBuilder & operator=(const SequencedSparseIndexRangeBuilder &)=delete
constexpr SequencedSparseIndexRangeBuilder(size_type size, size_type is_size, size_type avg_indices=2048, allocator_type allocator=allocator_type())
Definition sparseindexranges.hh:2079
typename std::allocator_traits< allocator_type >::size_type size_type
Type of the size parameters.
Definition sparseindexranges.hh:345
A allocator_type
Definition sparseindexranges.hh:342
constexpr ~SequencedSparseIndexRangeBuilder()
Definition sparseindexranges.hh:2090
Handle for inserting indices into the current index set of a SequencedSparseIndexRangeBuilder.
Definition sparseindexranges.hh:375
constexpr friend bool operator==(const IndexRangeBuilder &is_builder, EndSentinel)
Returns true when all index sets have been filled (used to detect the end sentinel).
Definition sparseindexranges.hh:428
constexpr bool contains(index_type j) const
Returns true if index j has already been inserted into the current index set.
Definition sparseindexranges.hh:2226
constexpr IndexRangeBuilder(const IndexRangeBuilder &)=delete
constexpr size_type index() const
Returns the zero-based number of the index set currently being filled.
Definition sparseindexranges.hh:2249
constexpr IndexRangeBuilder & operator*()
Dereference — returns a reference to this IndexRangeBuilder itself (enables range-for).
Definition sparseindexranges.hh:2281
constexpr void insert(index_type j)
Insert index j into the current index set. Duplicate insertions are silently ignored.
Definition sparseindexranges.hh:2134
constexpr size_type size() const
Returns the number of distinct indices inserted into the current index set so far.
Definition sparseindexranges.hh:2275
constexpr IndexRangeBuilder & operator=(const IndexRangeBuilder &)=delete
constexpr void overrideIndices(It begin, End end, bool do_sort=true, bool do_unique=true)
Replace the current index set with the indices in [begin, end).
Definition sparseindexranges.hh:2170
constexpr IndexRangeBuilder & operator++()
Definition sparseindexranges.hh:2254
Random-access iterator over the index sets of a SparseIndexRanges.
Definition sparseindexranges.hh:614
typename Facade::difference_type difference_type
Definition sparseindexranges.hh:621
typename Facade::reference reference
Definition sparseindexranges.hh:620
constexpr reference operator*() const
Definition sparseindexranges.hh:1588
Definition sparseindexranges.hh:646
constexpr size_type offset() const
Definition sparseindexranges.hh:1615
typename Facade::reference reference
Definition sparseindexranges.hh:650
constexpr reference operator*() const
Definition sparseindexranges.hh:1621
typename Facade::difference_type difference_type
Definition sparseindexranges.hh:651
Read-only view of a single index set inside a SparseIndexRanges.
Definition sparseindexranges.hh:689
typename SparseIndexRanges< I, A >::index_type index_type
Type of the indices.
Definition sparseindexranges.hh:701
constexpr IndexIterator upper_bound(index_type i) const
Definition sparseindexranges.hh:1689
constexpr IntegralRange< index_type > range() const
Returns an integer range [0, n) usable as a local index range over this set.
Definition sparseindexranges.hh:1666
constexpr index_type back() const
Returns the last index in this index set. Precondition: the set is not empty.
Definition sparseindexranges.hh:723
constexpr void for_each(F &&f) const
Calls f(j) for every index j in this index set, in ascending order.
Definition sparseindexranges.hh:745
constexpr bool contains(index_type j) const
Returns true if index j is contained in this index set. Complexity: O(size()) for small sets,...
Definition sparseindexranges.hh:1672
constexpr IndexIterator lower_bound(index_type i) const
Returns an iterator to the first index >= i in this index set, or end() if none exists.
Definition sparseindexranges.hh:1680
constexpr IndexIterator end() const
Returns an iterator past the last index in this index set.
Definition sparseindexranges.hh:1654
constexpr index_type operator[](size_type i) const
Returns the index at local position i in this index set. Precondition: i < size().
Definition sparseindexranges.hh:726
constexpr index_type front() const
Returns the first index in this index set. Precondition: the set is not empty.
Definition sparseindexranges.hh:720
typename SparseIndexRanges< I, A >::size_type size_type
Definition sparseindexranges.hh:703
constexpr IndexIterator find(index_type i) const
Definition sparseindexranges.hh:1697
constexpr IndexIterator begin() const
Returns an iterator to the first index in this index set.
Definition sparseindexranges.hh:1648
constexpr size_type size() const
Returns the number of indices in this index set.
Definition sparseindexranges.hh:1660
Random-access iterator over the index sets of a CompressedSparseIndexRanges.
Definition sparseindexranges.hh:912
typename Facade::reference reference
Definition sparseindexranges.hh:918
constexpr reference operator*() const
Definition sparseindexranges.hh:1866
typename Facade::difference_type difference_type
Definition sparseindexranges.hh:919
constexpr reference operator*() const
Definition sparseindexranges.hh:1899
typename Facade::difference_type difference_type
Definition sparseindexranges.hh:949
constexpr size_type offset() const
Definition sparseindexranges.hh:1893
typename Facade::reference reference
Definition sparseindexranges.hh:948
Read-only view of a single index set inside a CompressedSparseIndexRanges.
Definition sparseindexranges.hh:987
constexpr IndexIterator find(index_type i) const
Definition sparseindexranges.hh:1982
constexpr index_type front() const
Returns the first index in this index set. Precondition: the set is not empty.
Definition sparseindexranges.hh:1018
constexpr index_type operator[](size_type i) const
Returns the index at local position i in this index set. Precondition: i < size().
Definition sparseindexranges.hh:1024
constexpr index_type back() const
Returns the last index in this index set. Precondition: the set is not empty.
Definition sparseindexranges.hh:1021
constexpr void for_each(F &&f) const
Calls f(j) for every index j in this index set, in ascending order.
Definition sparseindexranges.hh:1043
constexpr IntegralRange< index_type > range() const
Returns an integer range [0, n) usable as a local index range over this set.
Definition sparseindexranges.hh:1946
typename CompressedSparseIndexRanges< A >::index_type index_type
Type of the indices.
Definition sparseindexranges.hh:999
constexpr IndexIterator upper_bound(index_type i) const
Definition sparseindexranges.hh:1972
constexpr size_type size() const
Returns the number of indices in this index set.
Definition sparseindexranges.hh:1940
constexpr bool contains(index_type j) const
Returns true if index j is contained in this index set. Complexity: O(size()) for small sets,...
Definition sparseindexranges.hh:1952
constexpr IndexIterator end() const
Returns an iterator past the last index in this index set.
Definition sparseindexranges.hh:1934
constexpr IndexIterator lower_bound(index_type i) const
Returns an iterator to the first index >= i in this index set, or end() if none exists.
Definition sparseindexranges.hh:1962
constexpr IndexIterator begin() const
Returns an iterator to the first index in this index set.
Definition sparseindexranges.hh:1928
typename CompressedSparseIndexRanges< A >::size_type size_type
Definition sparseindexranges.hh:1001
static constexpr size_type offset(const index_range_type &index_set, const index_type &value)
Definition sparseindexranges.hh:1069
R index_range_type
Definition sparseindexranges.hh:1060
typename index_range_type::index_type index_type
Definition sparseindexranges.hh:1061
typename index_range_type::size_type size_type
Definition sparseindexranges.hh:1062
static constexpr IntegralRange< index_type > range(const index_range_type &index_set)
Definition sparseindexranges.hh:1064
T addressof(T... args)
T back(T... args)
T cbegin(T... args)
T copy_n(T... args)
T count_if(T... args)
T distance(T... args)
T cend(T... args)
T endl(T... args)
T equal(T... args)
T exchange(T... args)
T exclusive_scan(T... args)
T fill(T... args)
T find_if(T... args)
T for_each(T... args)
T invoke(T... args)
T lower_bound(T... args)
T make_pair(T... args)
T max(T... args)
T min(T... args)
T next(T... args)
T size(T... args)
T sort(T... args)
T subspan(T... args)
T swap(T... args)
T tie(T... args)
T unique(T... args)
T upper_bound(T... args)
T visit(T... args)