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 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 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 std::tie(indices_ptr, count) = index_alloc_traits::allocate_at_least(index_alloc, count);
1134#else
1135 indices_ptr = index_alloc_traits::allocate(index_alloc, count);
1136#endif
1137 // initialize index set indices with invalid value
1138 for (size_type i = 0; i != count; ++i)
1139 index_alloc_traits::construct(index_alloc, std::addressof(indices_ptr[i]), std::numeric_limits<index_t>::max());
1140 }, indices);
1141
1142 return std::make_pair(indices, count);
1143 }
1144
1145 template <class A>
1146 constexpr void SparseIndexRangeHelper<A>::deallocate(const allocator_type& allocator, IndexVariant indices, size_type* offsets, size_type size) {
1147 if (offsets)
1148 {
1149 std::visit([&]<class index_t>(index_t *indices_ptr) {
1150 using index_alloc_type = typename alloc_traits::template rebind_alloc<index_t>;
1151 using index_alloc_traits = std::allocator_traits<index_alloc_type>;
1152 if (indices_ptr) {
1153 size_type count = offsets[size];
1154 index_alloc_type index_alloc(allocator);
1155 index_alloc_traits::deallocate(index_alloc, indices_ptr, count);
1156 } }, indices);
1157 size_alloc_type size_alloc(allocator);
1158 size_alloc_traits::deallocate(size_alloc, offsets, size + 1);
1159 }
1160 }
1161
1162
1163 template <class A>
1164 template <class V>
1165 constexpr V SparseIndexRangeHelper<A>::compress(std::span<size_type> offsets, IndexVariant indices, const allocator_type& alloc)
1166 {
1167 // check if there are undefined indices and remove them if necessary
1168 return std::visit([&alloc, offsets]<class old_index_t>(old_index_t *old_indices) -> V {
1169 size_type index_count = offsets.back();
1170 // count the max index and non-used entries
1171 constexpr old_index_t invalid_index = std::numeric_limits<old_index_t>::max();
1172 size_type count_invalid = 0;
1173 size_type max_index = 0;
1174 auto old_index_set = std::span(old_indices, index_count);
1175 std::for_each(std::cbegin(old_index_set), std::cend(old_index_set), [&count_invalid, &max_index](old_index_t j) {
1176 if (j == invalid_index)
1177 count_invalid++;
1178 else
1179 max_index = std::max<size_type>(max_index, j);
1180 });
1181 V new_indices = makeSmallestIndexVariant<V>(max_index);
1182
1183 // nothing to do if there are no undefined indices and the indices already use the selected index type
1184 if constexpr (std::constructible_from<V, old_index_t*>)
1185 if (count_invalid == 0 && std::holds_alternative<old_index_t*>(new_indices)) {
1186 new_indices = V{std::in_place_type<old_index_t *>, old_indices};
1187 return new_indices;
1188 }
1189
1190 // remove undefined indices and update offsets accordingly
1191 constexpr auto index_defined = [](old_index_t i){ return i != invalid_index; };
1192 // allocate new arrays for offsets and indices. We use scope guards to ensure that the allocated memory is freed in case of exceptions.
1193 size_alloc_type size_alloc(alloc);
1194 size_type *new_offsets = size_alloc_traits::allocate(size_alloc, offsets.size());
1195 typename Impl::SparseIndexRangeHelper<A>::template DeallocationGuard<size_alloc_type> offsetsScopeGuard{size_alloc, new_offsets, offsets.size()};
1196
1197 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 {
1198 using new_index_alloc_type = typename alloc_traits::template rebind_alloc<new_index_t>;
1199 using new_index_alloc_traits = std::allocator_traits<new_index_alloc_type>;
1200 new_index_alloc_type new_index_alloc(size_alloc);
1201 new_indices_ptr = new_index_alloc_traits::allocate(new_index_alloc, index_count - count_invalid);
1202 typename Impl::SparseIndexRangeHelper<A>::template DeallocationGuard<new_index_alloc_type> newIndicesScopeGuard{new_index_alloc, new_indices_ptr, index_count - count_invalid};
1203
1204 size_alloc_traits::construct(size_alloc, std::addressof(new_offsets[0]), 0);
1205 for (size_type i = 0; i + 1!= offsets.size(); ++i)
1206 {
1207 // count number of defined indices in index set
1208 auto old_index_set = std::span(old_indices + offsets[i], old_indices + offsets[i + 1]);
1209 size_type is_count = std::count_if(std::cbegin(old_index_set), std::cend(old_index_set), index_defined);
1210 // compute new offset for next index set
1211 size_alloc_traits::construct(size_alloc, std::addressof(new_offsets[i + 1]), new_offsets[i] + is_count);
1212 // copy defined indices of old index set to new index set
1213 auto new_index_set = std::span(new_indices_ptr + new_offsets[i], is_count);
1214 size_type j = 0;
1215 for (const auto old_idx : old_index_set)
1216 if (index_defined(old_idx))
1217 new_index_alloc_traits::construct(new_index_alloc, std::addressof(new_index_set[j++]), static_cast<new_index_t>(old_idx));
1218 }
1219
1220 // replace old offsets with new ones
1221 std::copy_n(new_offsets, offsets.size(), std::begin(offsets));
1222
1223 // replace old indices with new ones and free old memory
1224 using old_index_alloc_type = typename alloc_traits::template rebind_alloc<old_index_t>;
1225 typename Impl::SparseIndexRangeHelper<A>::template DeallocationGuard<old_index_alloc_type> oldIndicesScopeGuard{old_index_alloc_type(size_alloc), old_indices, index_count};
1226 return {std::exchange(newIndicesScopeGuard.ptr, nullptr)};
1227 }, new_indices);
1228
1229 return new_indices;
1230 }, indices);
1231 }
1232
1233
1234 template <class A>
1235 template<class index_t>
1236 constexpr bool SparseIndexRangeHelper<A>::addIndex(std::span<index_t> index_set, index_t j, std::invocable auto grow_index_set_fn)
1237 {
1238 constexpr index_t invalid_index = std::numeric_limits<index_t>::max();
1239
1240 // find correct insertion position for new index
1241 auto it = SparseIndexRangeHelper<A>::lowerBound(index_set, j);
1242
1243 // check if index is already in index set
1244 if (it != std::cend(index_set) && *it == j)
1245 return false;
1246
1247 // grow number of indices if exhausted
1248 if (it == std::cend(index_set) || index_set.back() != invalid_index) {
1249 // save distance to insertion position before growth
1250 size_type dist = std::distance(std::cbegin(index_set), it);
1251 // invoke growth function and update index set
1252 index_set = std::invoke(grow_index_set_fn);
1253 // check that growth function has made space for new index
1254 assert(index_set.size() > dist && "Too many entries in index set but the builder cannot grow the indices");
1255 assert(index_set.back() == invalid_index && "Growth function must initialize new indices with invalid value");
1256 // restore iterator to correct position after growth
1257 it = std::begin(index_set) + dist;
1258 }
1259
1260 // insert new index and shift other indices if necessary
1261 do {
1262 j = std::exchange(*it, j);
1263 it = (j == invalid_index) ? std::cend(index_set) : std::next(it);
1264 } while (it != std::cend(index_set));
1265 return true;
1266 }
1267
1268 template<class A>
1269 constexpr void SparseIndexRangeHelper<A>::growIndices(std::span<size_type> offsets, IndexVariant& indices, size_type new_count, const allocator_type& allocator, auto uninitialized_copy_fn){
1270 size_type old_count = offsets.back();
1271 if (new_count <= old_count)
1272 return;
1273
1274 // allocate new arrays for indices. We use scope guards to ensure that the allocated memory is freed in case of exceptions.
1275 std::visit([&indices, &allocator, &uninitialized_copy_fn, offsets, old_count, new_count]<typename index_t>(index_t * old_indices) {
1276 using index_alloc_type = typename alloc_traits::template rebind_alloc<index_t>;
1277 using index_alloc_traits = std::allocator_traits<index_alloc_type>;
1278 using dealloc_guard_type = typename Impl::SparseIndexRangeHelper<A>::template DeallocationGuard<index_alloc_type>;
1279 index_alloc_type index_alloc(allocator);
1280 index_t* new_indices = nullptr;
1281 size_type allocated_count = new_count;
1282#if __cpp_lib_allocate_at_least >= 202302L
1283 std::tie(new_indices, allocated_count) = index_alloc_traits::allocate_at_least(index_alloc, allocated_count);
1284#else
1285 new_indices = index_alloc_traits::allocate(index_alloc, allocated_count);
1286#endif
1287 dealloc_guard_type newIndicesScopeGuard{index_alloc, new_indices, allocated_count};
1288
1289 // copy values from old indices to new indices
1290 uninitialized_copy_fn(offsets, std::span<const index_t>(old_indices, old_count), std::span(new_indices, allocated_count));
1291 // offsets always need to be updated to reflect the new size of the indices array
1292 assert(offsets.back() == allocated_count);
1293
1294 // replace old indices with new ones and free old memory
1295 dealloc_guard_type oldIndicesScopeGuard{index_alloc, old_indices, old_count};
1296 indices = IndexVariant{std::in_place_type<index_t *>, std::exchange(newIndicesScopeGuard.ptr, nullptr)};
1297 }, indices);
1298 }
1299
1300 template<class A>
1301 constexpr void SparseIndexRangeHelper<A>::growLastIndexRange(std::span<size_type> offsets, IndexVariant& indices, double growth_factor, const allocator_type& allocator){
1302 const size_type old_count = offsets.back();
1303 const size_type new_count = std::max<size_type>(old_count*growth_factor, offsets.size() - 1);
1304 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){
1305 using index_alloc_type = typename alloc_traits::template rebind_alloc<index_t>;
1306 using index_alloc_traits = std::allocator_traits<index_alloc_type>;
1307 index_alloc_type index_alloc(allocator);
1308 // uninitialized copy of old indices into new indices
1309 for (size_type j = 0; j != old_indices.size(); ++j)
1310 index_alloc_traits::construct(index_alloc, std::addressof(new_indices[j]), old_indices[j]);
1311
1312 // initialize the remaining tail of the new indices with invalid value
1313 for (size_type j = old_indices.size(); j != new_indices.size(); ++j)
1314 index_alloc_traits::construct(index_alloc, std::addressof(new_indices[j]), std::numeric_limits<index_t>::max());
1315
1316 // update offset with new size
1317 offsets.back() = new_indices.size();
1318 });
1319 }
1320
1321 template<class A>
1322 constexpr void SparseIndexRangeHelper<A>::growAllIndexRanges(std::span<size_type> offsets, IndexVariant& indices, double target_growth_factor, const allocator_type& allocator){
1323
1324 if (target_growth_factor <= 1.0 or offsets.size() <= 1)
1325 return;
1326 constexpr size_type min_extra_indices = 1;
1327 size_type max_is_used = 0;
1328 size_type total_used_indices = 0;
1329 // 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
1330 std::visit([offsets, &max_is_used, &total_used_indices]<class index_t>(index_t const * indices_ptr) {
1331 // 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
1332 for (size_type i = 0; i + 1 != offsets.size(); ++i) {
1333 // get set of indices
1334 std::span index_set(indices_ptr + offsets[i], offsets[i+1] - offsets[i]);
1335
1336 // find insertion position for new index to get number of used indices in the index set
1337 auto it = lowerBound(index_set, std::numeric_limits<index_t>::max());
1338 size_type used_indices = std::distance(std::cbegin(index_set), it);
1339
1340 max_is_used = std::max(max_is_used, used_indices);
1341 total_used_indices += used_indices;
1342 }
1343 }, indices);
1344 size_type target_extra_per_index_set = std::max(static_cast<size_type>(max_is_used*(target_growth_factor - 1.)), min_extra_indices);
1345 size_type target_extra_indices = (offsets.size() - 1) * target_extra_per_index_set;
1346 size_type new_count = total_used_indices + target_extra_indices;
1347
1348 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){
1349 using index_alloc_type = typename alloc_traits::template rebind_alloc<index_t>;
1350 using index_alloc_traits = std::allocator_traits<index_alloc_type>;
1351 index_alloc_type index_alloc(allocator);
1352
1353 // indices may have grown by more than the target extra indices, so we compute an average extra indices per index set
1354 (void)new_count;
1355 assert(new_count >= new_indices.size() && "Allocated index storage must be at least as large as the new count of indices after growth");
1356
1357 const size_type extra_per_index_set = (new_indices.size() - total_used_indices) / (offsets.size() - 1);
1358 (void)min_extra_indices;
1359 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");
1360
1361 // carry offset to keep track of the current position in the new indices array
1362 size_type old_row_begin = 0;
1363 for (size_type i = 0; i + 1 != offsets.size(); ++i) {
1364 const size_type old_row_end = offsets[i+1];
1365 // get set of indices
1366 std::span old_index_set = old_indices.subspan(old_row_begin, old_row_end - old_row_begin);
1367
1368 // find insertion position for new index to get number of used indices in the index set
1369 auto it = SparseIndexRangeHelper<A>::lowerBound(old_index_set, std::numeric_limits<index_t>::max());
1370 size_type used_indices = std::distance(std::cbegin(old_index_set), it);
1371 // actual new size of i-th index set
1372 size_type new_size = used_indices + extra_per_index_set;
1373 new_size = std::min(new_size, new_indices.size() - offsets[i]); // ensure that we do not exceed the allocated size
1374
1375 std::span new_index_set = new_indices.subspan(offsets[i], new_size);
1376 // uninitialized copy of old indices into new indices
1377 for (size_type j = 0; j != used_indices; ++j)
1378 index_alloc_traits::construct(index_alloc, std::addressof(new_index_set[j]), old_index_set[j]);
1379 // initialize the remaining tail of the new index set with invalid value
1380 for (size_type j = used_indices; j != new_size; ++j)
1381 index_alloc_traits::construct(index_alloc, std::addressof(new_index_set[j]), std::numeric_limits<index_t>::max());
1382
1383 // 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
1384 old_row_begin = old_row_end;
1385 offsets[i+1] = offsets[i] + new_size;
1386 }
1387
1388 // 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
1389 for (size_type j = offsets.back(); j != new_indices.size(); ++j)
1390 index_alloc_traits::construct(index_alloc, std::addressof(new_indices[j]), std::numeric_limits<index_t>::max());
1391
1392 // update offset with new size
1393 offsets.back() = new_indices.size();
1394 });
1395 }
1396
1397 // ---------------------------------------------------------------------------
1398 // Impl::SparseIndexRangeHelper::DeallocationGuard declaration/implementation
1399 // ---------------------------------------------------------------------------
1400
1401 template <class A>
1402 template <class OtherAlloc>
1403 struct SparseIndexRangeHelper<A>::DeallocationGuard
1404 {
1405 using alloc_traits = std::allocator_traits<OtherAlloc>;
1406
1407 constexpr DeallocationGuard(OtherAlloc allocator, typename alloc_traits::pointer ptr, typename alloc_traits::size_type size)
1408 : allocator(allocator), ptr(ptr), size(size)
1409 {
1410 }
1411
1412 DeallocationGuard(const DeallocationGuard &) = delete;
1413 DeallocationGuard &operator=(const DeallocationGuard &) = delete;
1414
1415 constexpr ~DeallocationGuard()
1416 {
1417 if (ptr)
1418 alloc_traits::deallocate(allocator, ptr, size);
1419 }
1420
1421 OtherAlloc allocator;
1422 typename alloc_traits::pointer ptr;
1423 typename alloc_traits::size_type size;
1424 };
1425
1426} // namespace Impl
1427
1428
1429 // ---------------------------------------------------------------------------
1430 // SparseIndexRanges implementations
1431 // ---------------------------------------------------------------------------
1432
1433 template <class I, class A>
1434 template<class OtherAlloc>
1435 requires std::constructible_from<A, OtherAlloc>
1437 : size_(builder.size_),
1438 is_size_(builder.is_size_),
1439 offsets_(nullptr),
1440 indices_(nullptr),
1441 alloc_(builder.alloc_)
1442 {
1443 assert(builder.stage_ == SequencedSparseIndexRangeBuilder<OtherAlloc>::Stage::Built && "Sparsity pattern is not in correct build mode");
1444
1445 // Compress on builder-owned storage
1446 using IndexVariant = std::variant<I*>;
1447 IndexVariant indices_var = Impl::SparseIndexRangeHelper<A>::template compress<IndexVariant>(std::span(builder.offsets_, size_ + 1), builder.indices_, alloc_);
1448
1449 // Transfer ownership only after compression succeeds
1450 offsets_ = std::exchange(builder.offsets_, nullptr);
1451 indices_ = std::get<I*>(std::move(indices_var));
1452 size_ = std::exchange(builder.size_, 0);
1453 is_size_ = std::exchange(builder.is_size_, 0);
1454
1456 }
1457
1458 template <class I, class A>
1459 template<class OtherAlloc>
1460 requires std::constructible_from<A, OtherAlloc>
1462 : size_(builder.size_),
1463 is_size_(builder.is_size_),
1464 offsets_(nullptr),
1465 indices_(nullptr),
1466 alloc_(builder.alloc_)
1467 {
1468 assert(builder.stage_ == UnsequencedSparseIndexRangeBuilder<OtherAlloc>::Stage::BuildSetIndices && "Sparsity pattern is not in correct build mode");
1469
1470 // Compress on builder-owned storage
1471 using IndexVariant = std::variant<I*>;
1472 IndexVariant indices_var = Impl::SparseIndexRangeHelper<A>::template compress<IndexVariant>(std::span(builder.offsets_, size_ + 1), builder.indices_, alloc_);
1473
1474 // Transfer ownership only after compression succeeds
1475 offsets_ = std::exchange(builder.offsets_, nullptr);
1476 indices_ = std::get<I*>(std::move(indices_var));
1477 size_ = std::exchange(builder.size_, 0);
1478 is_size_ = std::exchange(builder.is_size_, 0);
1479
1481 }
1482
1483 template <class I, class A>
1485 : size_(std::exchange(other.size_, 0)),
1486 is_size_(std::exchange(other.is_size_, 0)),
1487 offsets_(std::exchange(other.offsets_, nullptr)),
1488 indices_(std::exchange(other.indices_, nullptr)),
1489 alloc_(std::move(other.alloc_))
1490 {}
1491
1492 template <class I, class A>
1494 {
1495 if (this == &other)
1496 return *this;
1497
1498 deallocate(alloc_, indices_, offsets_, size_);
1499
1500 is_size_ = std::exchange(other.is_size_, 0);
1501 size_ = std::exchange(other.size_, 0);
1502 offsets_ = std::exchange(other.offsets_, nullptr);
1503 indices_ = std::exchange(other.indices_, nullptr);
1504 alloc_ = std::move(other.alloc_);
1505 return *this;
1506 }
1507
1508 template <class I, class A>
1510 {
1511 if (this == &other)
1512 return;
1513
1516 if (alloc_ != other.alloc_)
1518 "Cannot swap SparseIndexRanges with unequal allocators when propagate_on_container_swap is false");
1519
1520 using std::swap;
1521 swap(size_, other.size_);
1522 swap(is_size_, other.is_size_);
1523 swap(offsets_, other.offsets_);
1524 swap(indices_, other.indices_);
1526 swap(alloc_, other.alloc_);
1527 }
1528
1529 template <class I, class A>
1531 {
1532 Impl::SparseIndexRangeHelper<A>::deallocate(alloc_, indices_, offsets_, size_);
1533 }
1534
1535 template <class I, class A>
1536 constexpr auto SparseIndexRanges<I,A>::size() const noexcept -> size_type
1537 {
1538 return size_;
1539 }
1540
1541 template <class I, class A>
1542 constexpr auto SparseIndexRanges<I,A>::range() const noexcept -> IntegralRange<index_type>
1543 {
1544 return {0, is_size_};
1545 }
1546
1547 template <class I, class A>
1549 {
1550 return offset(size_);
1551 }
1552
1553 template <class I, class A>
1555 {
1556 assert(offsets_);
1557 return std::span(offsets_, size_ + 1)[i];
1558 }
1559
1560 template <class I, class A>
1562 {
1563 for (size_type i = 0; i != size_; ++i)
1564 {
1565 os << "[" << i << "]: ";
1566 std::span index_set(indices_ + offsets_[i], offsets_[i + 1] - offsets_[i]);
1567 for (auto idx : index_set)
1568 os << static_cast<size_type>(idx) << " ";
1569 os << '\n';
1570 };
1571 return os;
1572 }
1573
1574 // ---------------------------------------------------------------------------
1575 // SparseIndexRanges::Iterator implementation
1576 // ---------------------------------------------------------------------------
1577
1578 template <class I, class A>
1579 constexpr SparseIndexRanges<I,A>::Iterator::Iterator(SparseIndexRanges const *sp, size_type i)
1580 : cis_(sp), i_(i)
1581 {}
1582
1583 template <class I, class A>
1585 {
1586 return IndexRangeView(cis_, i_);
1587 }
1588
1589 template <class I, class A>
1590 constexpr auto SparseIndexRanges<I,A>::Iterator::baseIterator() const -> const size_type &
1591 {
1592 return i_;
1593 }
1594
1595 template <class I, class A>
1597 {
1598 return i_;
1599 }
1600
1601 // ---------------------------------------------------------------------------
1602 // SparseIndexRanges::IndexIterator implementation
1603 // ---------------------------------------------------------------------------
1604
1605 template <class I, class A>
1607 : cis_(csi), i_(i), j_(j)
1608 {}
1609
1610 template <class I, class A>
1612 {
1613 return cis_->offsets_[i_] + j_;
1614 }
1615
1616 template <class I, class A>
1618 {
1619 return cis_->indices_[offset()];
1620 }
1621
1622 template <class I, class A>
1623 constexpr auto SparseIndexRanges<I,A>::IndexIterator::baseIterator() const -> const size_type &
1624 {
1625 return j_;
1626 }
1627
1628 template <class I, class A>
1630 {
1631 return j_;
1632 }
1633
1634 // ---------------------------------------------------------------------------
1635 // SparseIndexRanges::IndexRangeView implementation
1636 // ---------------------------------------------------------------------------
1637
1638 template <class I, class A>
1639 constexpr SparseIndexRanges<I,A>::IndexRangeView::IndexRangeView(const SparseIndexRanges *cis, size_type i)
1640 : cis_(cis), i_(i)
1641 {}
1642
1643 template <class I, class A>
1645 {
1646 return {cis_, i_, 0};
1647 }
1648
1649 template <class I, class A>
1651 {
1652 return {cis_, i_, size()};
1653 }
1654
1655 template <class I, class A>
1657 {
1658 return cis_->offsets_[i_ + 1] - cis_->offsets_[i_];
1659 }
1660
1661 template <class I, class A>
1663 {
1664 return {0, cis_->is_size_};
1665 }
1666
1667 template <class I, class A>
1669 {
1670 auto index_set = std::span(cis_->indices_ + cis_->offsets_[i_], size());
1671 auto lb = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, j);
1672 return lb != std::cend(index_set);
1673 }
1674
1675 template <class I, class A>
1677 {
1678 std::span index_set(cis_->indices_ + begin().offset(), end().offset() - begin().offset());
1679 auto it = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, j);
1680 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1681 return {};
1682 }
1683
1684 template <class I, class A>
1686 {
1687 std::span index_set(cis_->indices_ + begin().offset(), end().offset() - begin().offset());
1688 auto it = Impl::SparseIndexRangeHelper<A>::upperBound(index_set, j);
1689 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1690 }
1691
1692 template <class I, class A>
1694 {
1695 std::span index_set(cis_->indices_ + begin().offset(), end().offset() - begin().offset());
1696 auto it = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, j);
1697 if (it != std::cend(index_set) && *it == j)
1698 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1699 else
1700 return end();
1701 }
1702
1703 // ---------------------------------------------------------------------------
1704 // CompressedSparseIndexRanges implementations
1705 // ---------------------------------------------------------------------------
1706
1707 template <class A>
1708 template<class OtherAlloc>
1709 requires std::constructible_from<A, OtherAlloc>
1711 : size_(builder.size_),
1712 is_size_(builder.is_size_),
1713 offsets_(nullptr),
1714 indices_(static_cast<uint_least64_t *>(nullptr)),
1715 alloc_(builder.alloc_)
1716 {
1717 assert(builder.stage_ == SequencedSparseIndexRangeBuilder<OtherAlloc>::Stage::Built && "Sparsity pattern is not in correct build mode");
1718
1719 // Compress on builder-owned storage
1720 IndexVariant compressed_indices = Impl::SparseIndexRangeHelper<A>::compress(std::span<size_type>(builder.offsets_, size_ + 1), builder.indices_, alloc_);
1721
1722 // Transfer ownership only after compression succeeds
1723 offsets_ = std::exchange(builder.offsets_, nullptr);
1724 indices_ = std::move(compressed_indices);
1725 size_ = std::exchange(builder.size_, 0);
1726 is_size_ = std::exchange(builder.is_size_, 0);
1727
1729 }
1730
1731 template <class A>
1732 template<class OtherAlloc>
1733 requires std::constructible_from<A, OtherAlloc>
1735 : size_(builder.size_),
1736 is_size_(builder.is_size_),
1737 offsets_(nullptr),
1738 indices_(static_cast<uint_least64_t *>(nullptr)),
1739 alloc_(builder.alloc_)
1740 {
1741 assert(builder.stage_ == UnsequencedSparseIndexRangeBuilder<OtherAlloc>::Stage::BuildSetIndices && "Sparsity pattern is not in correct build mode");
1742
1743 // Compress on builder-owned storage
1744 IndexVariant compressed_indices = Impl::SparseIndexRangeHelper<A>::compress(std::span<size_type>(builder.offsets_, size_ + 1), builder.indices_, alloc_);
1745
1746 // Transfer ownership only after compression succeeds
1747 offsets_ = std::exchange(builder.offsets_, nullptr);
1748 indices_ = std::move(compressed_indices);
1749 size_ = std::exchange(builder.size_, 0);
1750 is_size_ = std::exchange(builder.is_size_, 0);
1751
1753 }
1754
1755 template <class A>
1757 : size_(std::exchange(other.size_, 0)),
1758 is_size_(std::exchange(other.is_size_, 0)),
1759 offsets_(std::exchange(other.offsets_, nullptr)),
1760 indices_(std::move(other.indices_)),
1761 alloc_(std::move(other.alloc_))
1762 {
1763 other.indices_ = IndexVariant{std::in_place_type<uint_least64_t *>, nullptr};
1764 }
1765
1766 template <class A>
1768 {
1769 if (this == &other)
1770 return *this;
1771
1772 deallocate(alloc_, indices_, offsets_, size_);
1773
1774 is_size_ = std::exchange(other.is_size_, 0);
1775 size_ = std::exchange(other.size_, 0);
1776 offsets_ = std::exchange(other.offsets_, nullptr);
1777 indices_ = std::move(other.indices_);
1778 other.indices_ = IndexVariant{std::in_place_type<uint_least64_t *>, nullptr};
1779 alloc_ = std::move(other.alloc_);
1780 return *this;
1781 }
1782
1783 template <class A>
1785 {
1786 if (this == &other)
1787 return;
1788
1791 if (alloc_ != other.alloc_)
1793 "Cannot swap CompressedSparseIndexRanges with unequal allocators when propagate_on_container_swap is false");
1794
1795 using std::swap;
1796 swap(size_, other.size_);
1797 swap(is_size_, other.is_size_);
1798 swap(offsets_, other.offsets_);
1799 swap(indices_, other.indices_);
1801 swap(alloc_, other.alloc_);
1802 }
1803
1804 template <class A>
1806 {
1807 Impl::SparseIndexRangeHelper<A>::deallocate(alloc_, indices_, offsets_, size_);
1808 }
1809
1810 template <class A>
1811 constexpr auto CompressedSparseIndexRanges<A>::size() const noexcept -> size_type
1812 {
1813 return size_;
1814 }
1815
1816 template <class A>
1818 {
1819 return {0, is_size_};
1820 }
1821
1822 template <class A>
1824 {
1825 return offset(size_);
1826 }
1827
1828 template <class A>
1830 {
1831 assert(offsets_);
1832 assert(i <= size_ && "Offset index out of bounds");
1833 return *(offsets_ + i);
1834 }
1835
1836 template <class A>
1838 {
1839 std::visit([&]<typename index_t>(index_t *indices)
1840 {
1841 for (size_type i = 0; i != size_; ++i)
1842 {
1843 os << "[" << i << "]: ";
1844 std::span index_set(indices + offsets_[i], offsets_[i + 1] - offsets_[i]);
1845 for (auto idx : index_set)
1846 os << static_cast<size_type>(idx) << " ";
1847 os << '\n';
1848 } }, indices_);
1849 return os;
1850 }
1851
1852 // ---------------------------------------------------------------------------
1853 // CompressedSparseIndexRanges::Iterator implementation
1854 // ---------------------------------------------------------------------------
1855
1856 template <class A>
1858 : cis_(sp), i_(i)
1859 {}
1860
1861 template <class A>
1863 {
1864 return IndexRangeView(cis_, i_);
1865 }
1866
1867 template <class A>
1869 {
1870 return i_;
1871 }
1872
1873 template <class A>
1875 {
1876 return i_;
1877 }
1878
1879 // ---------------------------------------------------------------------------
1880 // CompressedSparseIndexRanges::IndexIterator implementation
1881 // ---------------------------------------------------------------------------
1882
1883 template <class A>
1887
1888 template <class A>
1890 {
1891 return cis_->offsets_[i_] + j_;
1892 }
1893
1894 template <class A>
1896 {
1897 return std::visit([o = offset()](auto indices) -> index_type {
1898 return static_cast<index_type>(indices[o]);
1899 }, cis_->indices_);
1900 }
1901
1902 template <class A>
1904 {
1905 return j_;
1906 }
1907
1908 template <class A>
1910 {
1911 return j_;
1912 }
1913
1914 // ---------------------------------------------------------------------------
1915 // CompressedSparseIndexRanges::IndexRangeView implementation
1916 // ---------------------------------------------------------------------------
1917
1918 template <class A>
1919 constexpr CompressedSparseIndexRanges<A>::IndexRangeView::IndexRangeView(const CompressedSparseIndexRanges *cis, size_type i)
1920 : cis_(cis), i_(i)
1921 {}
1922
1923 template <class A>
1925 {
1926 return {cis_, i_, 0};
1927 }
1928
1929 template <class A>
1931 {
1932 return {cis_, i_, size()};
1933 }
1934
1935 template <class A>
1937 {
1938 return cis_->offsets_[i_ + 1] - cis_->offsets_[i_];
1939 }
1940
1941 template <class A>
1943 {
1944 return {0, cis_->is_size_};
1945 }
1946
1947 template <class A>
1949 {
1950 return std::visit([this, j]<typename index_t>(index_t *indices) -> bool {
1951 auto index_set = std::span(indices + cis_->offsets_[i_], size());
1952 auto lb = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, j);
1953 return lb != std::cend(index_set);
1954 }, cis_->indices_);
1955 }
1956
1957 template <class A>
1959 {
1960 return std::visit([&]<typename index_t>(index_t *indices) {
1961 std::span<const index_t> index_set(indices + begin().offset(), end().offset() - begin().offset());
1962 auto it = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, static_cast<index_t>(j));
1963 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1964 }, cis_->indices_);
1965 }
1966
1967 template <class A>
1969 {
1970 return std::visit([&]<typename index_t>(index_t *indices) {
1971 std::span<const index_t> index_set(indices + begin().offset(), end().offset() - begin().offset());
1972 auto it = Impl::SparseIndexRangeHelper<A>::upperBound(index_set, static_cast<index_t>(j));
1973 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1974 }, cis_->indices_);
1975 }
1976
1977 template <class A>
1979 {
1980 return std::visit([&]<typename index_t>(index_t *indices) {
1981 std::span<const index_t> index_set(indices + begin().offset(), end().offset() - begin().offset());
1982 auto it = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, static_cast<index_t>(j));
1983 if (it != std::cend(index_set) && *it == static_cast<index_t>(j))
1984 return IndexIterator(cis_, i_, std::distance(std::cbegin(index_set), it));
1985 else
1986 return end();
1987 }, cis_->indices_);
1988 }
1989
1990 // ---------------------------------------------------------------------------
1991 // UnsequencedSparseIndexRangeBuilder declaration
1992 // ---------------------------------------------------------------------------
1993
1994 template <class A>
1996 : size_(size),
1997 is_size_(is_size),
1998 offsets_(nullptr),
1999 indices_(static_cast<uint_least64_t *>(nullptr)),
2000 alloc_(allocator),
2001 stage_(Stage::BuildSizes)
2002 {
2003 offsets_ = Impl::SparseIndexRangeHelper<A>::initAllocateOffsets(alloc_, size_);
2004 std::span offsets(offsets_, size_ + 1);
2005 // initialize offsets with equal distribution
2006 for (size_type i = 0; i + 1 < offsets.size(); ++i)
2007 offsets[i] = avg_indices;
2008 offsets.back() = 0;
2009 }
2010
2011 template <class A>
2013 {
2014 Impl::SparseIndexRangeHelper<A>::deallocate(alloc_, indices_, offsets_, size_);
2015 }
2016
2017 template <class A>
2019 {
2020 assert(stage_ == Stage::BuildSizes && "Sparsity pattern is not in correct build mode");
2021 offsets_[i] = s;
2022 }
2023
2024 template <class A>
2026 {
2027 assert(stage_ == Stage::BuildSizes && "Sparsity pattern is not in correct build mode");
2028 offsets_[i] += increment;
2029 }
2030
2031 template <class A>
2033 {
2034 assert(stage_ == Stage::BuildSizes && "Sparsity pattern is not in correct build mode");
2035 stage_ = Stage::BuildSetIndices;
2036 // compute offsets from index set sizes
2037 std::exclusive_scan(offsets_, offsets_ + size_ + 1, offsets_, size_type{0});
2038
2039 std::visit([](auto ptr){
2040 assert(ptr == nullptr && "Indices pointer must be null before initialization");
2041 }, indices_);
2042 std::tie(indices_, offsets_[size_]) = Impl::SparseIndexRangeHelper<A>::initAllocateIndices(alloc_, offsets_[size_], is_size_);
2043 }
2044
2045 template <class A>
2047 {
2048 assert(i < size_ && "There are less index sets than expected for given index i");
2049 assert(j < is_size_ && "Out of bounds of index set range");
2050
2051 if (stage_ == Stage::BuildSizes)
2052 endSizes();
2053
2054 assert(stage_ == Stage::BuildSetIndices && "Sparsity pattern is not in correct build mode");
2055
2056 assert(offsets_[i + 1] >= offsets_[i] && "Invalid offsets");
2057 auto offsets = std::span(offsets_, size_ + 1);
2058
2059 std::visit([this, j, i, offsets]<class index_t>(index_t * indices) {
2060 index_t jj = static_cast<index_t>(j);
2061 Impl::SparseIndexRangeHelper<A>::addIndex(std::span(indices + offsets[i], indices + offsets[i + 1]), jj, [this, i, offsets]() -> std::span<index_t> {
2062 double growth_factor = 2.5;
2063 Impl::SparseIndexRangeHelper<A>::growAllIndexRanges(offsets, indices_, growth_factor, alloc_);
2064 index_t * indices = std::get<index_t *>(indices_);
2065 return std::span(indices + offsets[i], indices + offsets[i + 1]);
2066 });
2067 }, indices_);
2068 }
2069
2070 // ---------------------------------------------------------------------------
2071 // SequencedSparseIndexRangeBuilder implementation
2072 // ---------------------------------------------------------------------------
2073
2074 template<class A>
2076 : size_{size}
2077 , is_size_{is_size}
2078 , alloc_{allocator}
2079 {
2080 offsets_ = Impl::SparseIndexRangeHelper<A>::initAllocateOffsets(alloc_, size_);
2081 std::tie(indices_, offsets_[size_]) = Impl::SparseIndexRangeHelper<A>::initAllocateIndices(allocator, avg_indices * size_, is_size_);
2082 stage_ = Stage::Allocated;
2083 }
2084
2085 template<class A>
2087 {
2088 Impl::SparseIndexRangeHelper<A>::deallocate(alloc_, indices_, offsets_, size_);
2089 }
2090
2091 template<class A>
2093 {
2094 return {this};
2095 }
2096
2097 template<class A>
2099 {
2100 return {};
2101 }
2102
2103 template<class A>
2105 : builder_{builder}
2106 , i_{0}
2107 , is_count_{0}
2108 , stage_{Stage::BuildingIndexRanges}
2109 {
2110 assert(builder_->stage_ == Stage::Allocated && "IndexRangeBuilder can only be created once");
2111 builder_->stage_ = stage_;
2112 builder_->offsets_[i_ + 1] = size_type(builder_->offsets_[builder_->size_] > 0);
2113 }
2114
2115 template<class A>
2119
2120 template<class A>
2122 builder_ = std::exchange(other.builder_, nullptr);
2123 i_ = std::exchange(other.i_, 0);
2124 is_count_ = std::exchange(other.is_count_, 0);
2125 stage_ = std::exchange(other.stage_, Stage::Invalid);
2126 return *this;
2127 }
2128
2129 template<class A>
2131 assert(builder_->stage_ == stage_);
2132 assert(i_ < builder_->size_ && "There are less index sets than expected for given index i");
2133 assert(j < builder_->is_size_ && "Out of bounds of index set range");
2134 assert(*this != EndSentinel() && "The IndexRangeBuilder is past the last index set");
2135
2136 std::span<size_type> offsets(builder_->offsets_, builder_->size_ + 1);
2137
2138 assert(offsets[i_ + 1] >= offsets[i_] && "Invalid offsets");
2139
2140 // add index j to i-th index set. Grow offsets if needed.
2141 bool new_index_added = std::visit([offsets, j, i = i_, builder = builder_]<class index_t>(index_t * indices) {
2142 index_t jj = static_cast<index_t>(j);
2143 return Impl::SparseIndexRangeHelper<A>::addIndex(std::span(indices + offsets[i], indices + offsets[i + 1]), jj, [i, offsets, builder]() -> std::span<index_t> {
2144 double growth_factor = 2.0;
2145 const size_type old_size = offsets[i + 1] - offsets[i];
2146 // check if there is space left in the tail of the indices, if not, grow the indices by expanding the last index set
2147 if (offsets.back() - offsets[i + 1] == 0)
2148 Impl::SparseIndexRangeHelper<A>::growLastIndexRange(offsets, builder->indices_, growth_factor, builder->alloc_);
2149 // set new size of current index set by growth_factor times the current size or by the small index set threshold, whichever is larger
2150 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));
2151 // grow index set up to the current buffer boundary
2152 offsets[i + 1] = std::min(offsets[i + 1] + target_new_size, offsets.back());
2153 index_t* indices = std::get<index_t *>(builder->indices_);
2154 auto index_set = std::span(indices + offsets[i], indices + offsets[i + 1]);
2155 assert(index_set.size() > old_size && "Invalid offsets after growth");
2156 return index_set;
2157 });
2158 }, builder_->indices_);
2159
2160 if (new_index_added)
2161 ++is_count_;
2162 }
2163
2164 template<class A>
2165 template<std::forward_iterator It, std::sentinel_for<It> End>
2167 End end,
2168 bool do_sort,
2169 bool do_unique)
2170 {
2171 assert(builder_->stage_ == stage_);
2172 assert(i_ < builder_->size_ && "There are less index sets than expected for given index i");
2173 assert(*this != EndSentinel() && "The IndexRangeBuilder is past the last index set");
2174
2175 std::span<size_type> offsets(builder_->offsets_, builder_->size_ + 1);
2176 assert(offsets[i_ + 1] >= offsets[i_] && "Invalid offsets");
2177
2178 const size_type row_begin = offsets[i_];
2179 const size_type requested_count = static_cast<size_type>(std::distance(begin, end));
2180 const size_type available_count = offsets.back() - row_begin;
2181
2182 if (available_count < requested_count) {
2183 const double growth_factor =
2184 offsets.back() > 0
2185 ? static_cast<double>(offsets.back() + (requested_count - available_count) + 1) / static_cast<double>(offsets.back())
2186 : 2.0;
2187 Impl::SparseIndexRangeHelper<A>::growLastIndexRange(offsets,
2188 builder_->indices_,
2189 growth_factor,
2190 builder_->alloc_);
2191 }
2192
2193 assert(offsets.back() - row_begin >= requested_count && "Growing index set failed to provide enough space for new indices");
2194
2195 // Extend the active buffer for this row when the replacement needs more room.
2196 offsets[i_ + 1] = std::max(offsets[i_ + 1], row_begin + requested_count);
2197
2198 size_type final_count = requested_count;
2199 std::visit([&, row_begin]<class index_t>(index_t * indices) {
2200 constexpr index_t invalid_index = std::numeric_limits<index_t>::max();
2201 auto row = std::span(indices + row_begin, offsets[i_ + 1] - row_begin);
2202
2203 auto out = std::begin(row);
2204 for (auto it = begin; it != end; ++it, ++out) {
2205 assert(*it < builder_->is_size_ && "Out of bounds of index set range");
2206 *out = static_cast<index_t>(*it);
2207 }
2208
2209 if (do_sort)
2210 std::sort(std::begin(row), out);
2211 if (do_unique)
2212 out = std::unique(std::begin(row), out);
2213
2214 std::fill(out, std::end(row), invalid_index);
2215 final_count = static_cast<size_type>(std::distance(std::cbegin(row), out));
2216 }, builder_->indices_);
2217
2218 is_count_ = final_count;
2219 }
2220
2221 template<class A>
2223 assert(builder_->stage_ == stage_);
2224 assert(i_ < builder_->size_ && "There are less index sets than expected for given index i");
2225 assert(j < builder_->is_size_ && "Out of bounds of index set range");
2226 assert(*this != EndSentinel() && "The IndexRangeBuilder is past the last index set");
2227 size_type offsets_begin = builder_->offsets_[i_];
2228 size_type offsets_end = builder_->offsets_[i_ + 1];
2229 assert(offsets_end >= offsets_begin && "Invalid offsets");
2230
2231 return std::visit([&]<typename index_t>(const index_t *indices) {
2232 // get set of indices
2233 std::span index_set(indices + offsets_begin, offsets_end - offsets_begin);
2234 const index_t jj = static_cast<index_t>(j);
2235
2236 // find correct insertion position for new index
2237 auto it = Impl::SparseIndexRangeHelper<A>::lowerBound(index_set, jj);
2238
2239 // check if index is in the set
2240 return it != std::cend(index_set) && *it == jj;
2241 }, builder_->indices_);
2242 }
2243
2244 template<class A>
2246 return i_;
2247 }
2248
2249 template<class A>
2251 assert(builder_ && "IndexRangeBuilder is not valid");
2252 assert(builder_->stage_ == Stage::BuildingIndexRanges);
2253 assert(*this != EndSentinel() && "The IndexRangeBuilder is past the last index set");
2254
2255 auto offsets = std::span(builder_->offsets_, builder_->size_ + 1);
2256 size_type current_offset = offsets[i_];
2257 ++i_;
2258 // set next index set to have a small index set or less
2259 if (i_ < builder_->size_) {
2260 const std::size_t index_size = std::visit([]<class index_t>(index_t *) { return sizeof(index_t); }, builder_->indices_);
2261 // set up next index set to have SPARSE_SEARCH_THRESHOLD bytes of indices or less
2262 offsets[i_] = current_offset + std::exchange(is_count_, 0);
2263 offsets[i_ + 1] = std::min(offsets[i_] + std::max<size_type>(Impl::SPARSE_SEARCH_THRESHOLD/index_size, 1), offsets[builder_->size_]);
2264 } else {
2265 stage_ = builder_->stage_ = Stage::Built;
2266 }
2267 return *this;
2268 }
2269
2270 template<class A>
2272 assert(*this != EndSentinel() && "The IndexRangeBuilder is past the last index set");
2273 return is_count_;
2274 }
2275
2276 template<class A>
2280
2281} // namespace Dune::Experimental
2282
2283// Opt-in to std::ranges::borrowed_range for any type derived from Impl::SparseIndexRangesBase.
2284template <std::derived_from<Dune::Experimental::Impl::SparseIndexRangesBase> T>
2285constexpr bool std::ranges::enable_borrowed_range<T> = true;
2286
2287#endif // DUNE_ISTL_EXPERIMENTAL_SPARSEINDEXRANGES_HH
Traits for sets of indices.
#define DUNE_NO_UNIQUE_ADDRESS
STL namespace.
Definition experimental/bcrsmatrix.hh:21
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
void swap(T &v1, T &v2, bool mask)
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:1484
constexpr SparseIndexRanges(SequencedSparseIndexRangeBuilder< OtherAlloc > &&builder)
Construct from a finished SequencedSparseIndexRangeBuilder, taking ownership of its storage and compr...
Definition sparseindexranges.hh:1436
constexpr IntegralRange< index_type > range() const noexcept
Returns the integer range [0, n) that every stored index belongs to.
Definition sparseindexranges.hh:1542
constexpr void swap(SparseIndexRanges &other)
Swap contents with other honoring allocator swap propagation traits.
Definition sparseindexranges.hh:1509
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:1536
constexpr ~SparseIndexRanges()
Destroy the index sets and release all allocated memory.
Definition sparseindexranges.hh:1530
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:1561
constexpr size_type offset(size_type i) const
Definition sparseindexranges.hh:1554
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:1461
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:1548
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:1493
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:1767
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:1734
constexpr CompressedSparseIndexRanges(CompressedSparseIndexRanges &&other)
Move-construct, transferring ownership of storage from other. other is left in a valid but empty stat...
Definition sparseindexranges.hh:1756
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:1837
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:1811
constexpr size_type offset(size_type i) const
Definition sparseindexranges.hh:1829
constexpr void swap(CompressedSparseIndexRanges &other)
Swap contents with other honoring allocator swap propagation traits.
Definition sparseindexranges.hh:1784
constexpr IntegralRange< index_type > range() const noexcept
Returns the integer range [0, n) that every stored index belongs to.
Definition sparseindexranges.hh:1817
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:1823
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:1710
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:1805
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:2012
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:2018
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:1995
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:2046
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:2025
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:2098
constexpr SequencedSparseIndexRangeBuilder(SequencedSparseIndexRangeBuilder &&)=delete
constexpr SequencedSparseIndexRangeBuilder & operator=(SequencedSparseIndexRangeBuilder &&)=delete
constexpr IndexRangeBuilder begin()
Returns an IndexRangeBuilder positioned at the first index set.
Definition sparseindexranges.hh:2092
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:2075
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:2086
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:2222
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:2245
constexpr IndexRangeBuilder & operator*()
Dereference — returns a reference to this IndexRangeBuilder itself (enables range-for).
Definition sparseindexranges.hh:2277
constexpr void insert(index_type j)
Insert index j into the current index set. Duplicate insertions are silently ignored.
Definition sparseindexranges.hh:2130
constexpr size_type size() const
Returns the number of distinct indices inserted into the current index set so far.
Definition sparseindexranges.hh:2271
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:2166
constexpr IndexRangeBuilder & operator++()
Definition sparseindexranges.hh:2250
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:1584
Definition sparseindexranges.hh:646
constexpr size_type offset() const
Definition sparseindexranges.hh:1611
typename Facade::reference reference
Definition sparseindexranges.hh:650
constexpr reference operator*() const
Definition sparseindexranges.hh:1617
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:1685
constexpr IntegralRange< index_type > range() const
Returns an integer range [0, n) usable as a local index range over this set.
Definition sparseindexranges.hh:1662
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:1668
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:1676
constexpr IndexIterator end() const
Returns an iterator past the last index in this index set.
Definition sparseindexranges.hh:1650
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:1693
constexpr IndexIterator begin() const
Returns an iterator to the first index in this index set.
Definition sparseindexranges.hh:1644
constexpr size_type size() const
Returns the number of indices in this index set.
Definition sparseindexranges.hh:1656
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:1862
typename Facade::difference_type difference_type
Definition sparseindexranges.hh:919
constexpr reference operator*() const
Definition sparseindexranges.hh:1895
typename Facade::difference_type difference_type
Definition sparseindexranges.hh:949
constexpr size_type offset() const
Definition sparseindexranges.hh:1889
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:1978
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:1942
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:1968
constexpr size_type size() const
Returns the number of indices in this index set.
Definition sparseindexranges.hh:1936
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:1948
constexpr IndexIterator end() const
Returns an iterator past the last index in this index set.
Definition sparseindexranges.hh:1930
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:1958
constexpr IndexIterator begin() const
Returns an iterator to the first index in this index set.
Definition sparseindexranges.hh:1924
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)