Sequoia
Loading...
Searching...
No Matches
PartitionedData.hpp
Go to the documentation of this file.
1
2// Copyright Oliver J. Rosten 2018. //
3// Distributed under the GNU GENERAL PUBLIC LICENSE, Version 3.0. //
4// (See accompanying file LICENSE.md or copy at //
5// https://www.gnu.org/licenses/gpl-3.0.en.html) //
7
8#pragma once
9
19#include "sequoia/PlatformSpecific/Preprocessor.hpp"
20
21#include <string>
22#include <numeric>
23#include <stdexcept>
24
25namespace sequoia
26{
27 namespace data_structures
28 {
29 //===================================A Custom Iterator===================================//
30
31 template<class Container, std::integral IndexType>
32 using partition_iterator
33 = utilities::iterator<
34 typename Container::iterator,
35 utilities::identity_dereference_policy<typename Container::iterator, partition_impl::partition_index_policy<false, IndexType>>>;
36
37 template<class Container, std::integral IndexType>
38 using const_partition_iterator
39 = utilities::iterator<
40 typename Container::const_iterator,
41 utilities::identity_dereference_policy<typename Container::const_iterator, partition_impl::partition_index_policy<false, IndexType>>>;
42
43 template<class Container, std::integral IndexType>
44 using reverse_partition_iterator
45 = utilities::iterator<
46 typename Container::reverse_iterator,
47 utilities::identity_dereference_policy<typename Container::reverse_iterator, partition_impl::partition_index_policy<true, IndexType>>>;
48
49 template<class Container, std::integral IndexType>
50 using const_reverse_partition_iterator
51 = utilities::iterator<
52 typename Container::const_reverse_iterator,
53 utilities::identity_dereference_policy<typename Container::const_reverse_iterator, partition_impl::partition_index_policy<true, IndexType>>>;
54
55
56 //===================================Storage using buckets===================================//
57
61 template<class T, class Container=std::vector<std::vector<T>>>
63 {
64 public:
65 using value_type = T;
66 using container_type = Container;
67 using bucket_type = typename container_type::value_type;
68 using size_type = typename container_type::size_type;
69 using index_type = size_type;
70 using allocator_type = typename container_type::allocator_type;
71 //using partitions_allocator_type = typename bucket_type::allocator_type;
72
77 using partition_range = std::ranges::subrange<partition_iterator>;
78 using const_partition_range = std::ranges::subrange<const_partition_iterator>;
79
80 constexpr static auto npos{partition_iterator::npos};
81
82 bucketed_sequence() noexcept(noexcept(allocator_type{})) = default;
83
84 explicit bucketed_sequence(const allocator_type& allocator) noexcept
85 : m_Buckets(allocator)
86 {}
87
88 bucketed_sequence(std::initializer_list<std::initializer_list<T>> list,
89 const allocator_type& allocator = allocator_type{})
90 : m_Buckets(allocator)
91 {
92 m_Buckets.reserve(list.size());
93 for(auto iter{list.begin()}; iter != list.end(); ++iter)
94 {
95 add_slot();
96 const auto dist{std::ranges::distance(list.begin(), iter)};
97 m_Buckets[dist].reserve(iter->size());
98 for(const auto& element : (*iter))
99 {
100 push_back_to_partition(dist, element);
101 }
102 }
103 }
104
105 bucketed_sequence(const bucketed_sequence&) = default;
106
107 bucketed_sequence(const bucketed_sequence& other, const allocator_type& allocator)
108 : m_Buckets(std::allocator_traits<allocator_type>::select_on_container_copy_construction(allocator))
109 {
110 m_Buckets.reserve(other.m_Buckets.size());
111
112 for(auto i{other.m_Buckets.cbegin()}; i != other.m_Buckets.cend(); ++i)
113 {
114 const auto& bucket{*i};
115 const auto dist{std::ranges::distance(other.m_Buckets.cbegin(), i)};
116 add_slot();
117 m_Buckets.back().reserve(other.m_Buckets[dist].size());
118 for(const auto& elt : bucket)
119 {
120 m_Buckets.back().push_back(elt);
121 }
122 }
123 }
124
125 bucketed_sequence(bucketed_sequence&&) noexcept = default;
126
127 bucketed_sequence(bucketed_sequence&& other, const allocator_type& allocator)
128 : m_Buckets(std::move(other).m_Buckets, allocator)
129 {}
130
131 bucketed_sequence& operator=(bucketed_sequence&&) noexcept = default;
132
133 bucketed_sequence& operator=(const bucketed_sequence&) = default;
134
135 void swap(bucketed_sequence& other)
136 noexcept(noexcept(std::ranges::swap(this->m_Buckets, other.m_Buckets)))
137 {
138 std::ranges::swap(m_Buckets, other.m_Buckets);
139 }
140
141 friend void swap(bucketed_sequence& lhs, bucketed_sequence& rhs)
142 noexcept(noexcept(lhs.swap(rhs)))
143 {
144 lhs.swap(rhs);
145 }
146
147 [[nodiscard]]
148 bool empty() const noexcept
149 {
150 return m_Buckets.empty();
151 }
152
153 [[nodiscard]]
154 size_type size() const
155 {
156 return std::accumulate(std::cbegin(m_Buckets), std::cend(m_Buckets), size_type{},
157 [](const size_type& current, const auto& bucket){
158 return current + bucket.size();
159 });
160 }
161
162 [[nodiscard]]
163 size_type size_of_partition(size_type i) const
164 {
165 return static_cast<size_type>(std::ranges::distance(partition(i)));
166 }
167
168 [[nodiscard]]
169 size_type num_partitions() const noexcept { return m_Buckets.size(); }
170
171 void swap_partitions(const size_type i, const size_type j)
172 {
173 if((i < num_partitions()) && (j < num_partitions()))
174 {
175 if(i != j) std::ranges::swap(m_Buckets[i], m_Buckets[j]);
176 }
177 }
178
179 [[nodiscard]]
180 allocator_type get_allocator() const
181 {
182 return m_Buckets.get_allocator();
183 }
184
185 void add_slot()
186 {
187 m_Buckets.emplace_back();
188 }
189
190 void insert_slot(const size_type pos)
191 {
192 if(pos < num_partitions())
193 {
194 auto iter{m_Buckets.begin() + pos};
195 m_Buckets.emplace(iter);
196 }
197 else
198 {
199 add_slot();
200 }
201 }
202
203 void erase_slot(const size_type n)
204 {
205 if(n < m_Buckets.size())
206 {
207 m_Buckets.erase(m_Buckets.begin() + n);
208 }
209 }
210
211 void reserve_partition(const size_type partition, const size_type size)
212 {
213 if(partition < num_partitions())
214 m_Buckets[partition].reserve(size);
215 }
216
217 [[nodiscard]]
218 size_type partition_capacity(const size_type partition) const
219 {
220 return partition < num_partitions() ? m_Buckets[partition].capacity() : 0;
221 }
222
223 void reserve_partitions(const size_type numPartitions)
224 {
225 m_Buckets.reserve(numPartitions);
226 }
227
228 [[nodiscard]]
229 size_type num_partitions_capacity() const noexcept
230 {
231
232 return m_Buckets.capacity();
233 }
234
235 void shrink_num_partitions_to_fit()
236 {
237 m_Buckets.shrink_to_fit();
238 }
239
240 void shrink_to_fit(const size_type partition)
241 {
242 if(partition < num_partitions()) m_Buckets[partition].shrink_to_fit();
243 }
244
245 void shrink_to_fit()
246 {
247 shrink_num_partitions_to_fit();
248 for(auto& b : m_Buckets) b.shrink_to_fit();
249 }
250
251 void clear() noexcept
252 {
253 m_Buckets.clear();
254 }
255
256 template<class... Args>
257 void push_back_to_partition(const size_type index, Args&&... args)
258 {
259 check_range("push_back_to_partition", index);
260
261 m_Buckets[index].emplace_back(std::forward<Args>(args)...);
262 }
263
264 template<class... Args>
265 partition_iterator insert_to_partition(const_partition_iterator pos, Args&&... args)
266 {
267 const auto source{pos.partition_index()};
268 check_range("insert_to_partition", source);
269
270 auto iter{m_Buckets[source].emplace(pos.base_iterator(), std::forward<Args>(args)...)};
271
272 return partition_iterator{iter, source};
273 }
274
275 template<class... Args>
276 partition_iterator insert_to_partition(const size_type index, const size_type pos, Args&&... args)
277 {
278 check_range("insert_to_partition", index, pos);
279 return insert_to_partition(std::ranges::next(begin_partition_raw(index), pos, end_partition_raw(index)), std::forward<Args>(args)...);
280 }
281
282 partition_iterator erase_from_partition(const_partition_iterator iter)
283 {
284 const auto partition{iter.partition_index()};
285 if(const auto n{num_partitions()}; partition >= n)
286 {
287 return end_partition(n);
288 }
289 else if(iter == cend_partition(partition))
290 {
291 return end_partition(partition);
292 }
293
294 const auto next{m_Buckets[partition].erase(iter.base_iterator())};
295 return {next, partition};
296 }
297
299 {
300 const auto firstPartition{first.partition_index()}, lastPartition{last.partition_index()};
301 if(const auto n{num_partitions()}; (firstPartition >= n) && (lastPartition >= n))
302 {
303 return end_partition(n);
304 }
305 else if(lastPartition == firstPartition)
306 {
307 if(std::ranges::distance(first, last) > 0)
308 {
309 const auto next{m_Buckets[firstPartition].erase(first.base_iterator(), last.base_iterator())};
310 return {next, firstPartition};
311 }
312
313 return std::ranges::next(begin_partition(firstPartition), std::ranges::distance(cbegin_partition(firstPartition), first));
314 }
315
316 throw std::domain_error{std::string{"bucketed_sequence::erase - Invalid range specified by iterators belonging to partitions ["}
317 .append(std::to_string(firstPartition)).append(", ").append(std::to_string(lastPartition)).append("]")};
318 }
319
320 partition_iterator erase_from_partition(const size_type index, const size_type pos)
321 {
322 check_for_empty("erase_from_partition");
323 return erase_from_partition(std::ranges::next(begin_partition_raw(index), pos, end_partition_raw(index)));
324 }
325
326 [[nodiscard]]
327 partition_iterator begin_partition(const size_type i)
328 {
329 check_for_empty("begin_partition");
330 return (i < m_Buckets.size()) ? partition_iterator{m_Buckets[i].begin(), i} : partition_iterator{m_Buckets.back().end(), npos};
331 }
332
333 [[nodiscard]]
334 partition_iterator end_partition(const size_type i)
335 {
336 check_for_empty("end_partition");
337 return (i < m_Buckets.size()) ? partition_iterator{m_Buckets[i].end(), i} : partition_iterator{m_Buckets.back().end(), npos};
338 }
339
340 [[nodiscard]]
341 const_partition_iterator begin_partition(const size_type i) const
342 {
343 check_for_empty("begin_partition");
344 return begin_partition_raw(i);
345 }
346
347 [[nodiscard]]
348 const_partition_iterator end_partition(const size_type i) const
349 {
350 check_for_empty("end_partition");
351 return end_partition_raw(i);
352 }
353
354 [[nodiscard]]
355 reverse_partition_iterator rbegin_partition(const size_type i)
356 {
357 check_for_empty("rbegin_partition");
358 return (i < m_Buckets.size()) ? reverse_partition_iterator{m_Buckets[i].rbegin(), i} : reverse_partition_iterator{m_Buckets.front().rend(), npos};
359 }
360
361 [[nodiscard]]
362 reverse_partition_iterator rend_partition(const size_type i)
363 {
364 check_for_empty("rend_partition");
365 return (i < m_Buckets.size()) ? reverse_partition_iterator{m_Buckets[i].rend(), i} : reverse_partition_iterator{m_Buckets.front().rend(), npos};
366 }
367
368 [[nodiscard]]
369 const_reverse_partition_iterator rbegin_partition(const size_type i) const
370 {
371 check_for_empty("rbegin_partition");
372 return (i < m_Buckets.size()) ? const_reverse_partition_iterator{m_Buckets[i].crbegin(), i} : const_reverse_partition_iterator{m_Buckets.front().crend(), npos};
373
374 }
375
376 [[nodiscard]]
377 const_reverse_partition_iterator rend_partition(const size_type i) const
378 {
379 check_for_empty("rend_partition");
380 return (i < m_Buckets.size()) ? const_reverse_partition_iterator{m_Buckets[i].crend(), i} : const_reverse_partition_iterator{m_Buckets.front().crend(), npos};
381 }
382
383 [[nodiscard]]
384 const_partition_iterator cbegin_partition(const size_type i) const
385 {
386 return begin_partition(i);
387 }
388
389 [[nodiscard]]
390 const_partition_iterator cend_partition(const size_type i) const
391 {
392 return end_partition(i);
393 }
394
395 [[nodiscard]]
396 const_reverse_partition_iterator crbegin_partition(const size_type i) const
397 {
398 return rbegin_partition(i);
399 }
400
401 [[nodiscard]]
402 const_reverse_partition_iterator crend_partition(const size_type i) const
403 {
404 return rend_partition(i);
405 }
406
407 [[nodiscard]]
408 partition_range partition(const size_type i)
409 {
410 check_for_empty("partition");
411
412 return (i < m_Buckets.size()) ? partition_range{partition_iterator{m_Buckets[i].begin(), i}, partition_iterator{m_Buckets[i].end(), i}}
413 : partition_range{partition_iterator{m_Buckets.back().end(), npos}, partition_iterator{m_Buckets.back().end(), npos}};
414 }
415
416 [[nodiscard]]
417 const_partition_range partition(const size_type i) const
418 {
419 check_for_empty("partition");
420
421 return (i < m_Buckets.size()) ? const_partition_range{const_partition_iterator{m_Buckets[i].begin(), i}, const_partition_iterator{m_Buckets[i].end(), i}}
422 : const_partition_range{const_partition_iterator{m_Buckets.back().end(), npos}, const_partition_iterator{m_Buckets.back().end(), npos}};
423 }
424
425 [[nodiscard]]
426 const_partition_range cpartition(const size_type i) const { return partition(i); }
427
428 [[nodiscard]]
429 const_partition_iterator operator[](const size_type i) const { return cbegin_partition(i); }
430
431 [[nodiscard]]
432 partition_iterator operator[](const size_type i) { return begin_partition(i); }
433
434 void reset(const allocator_type& allocator) noexcept
435 requires (std::allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value)
436 {
437 const container_type buckets(allocator);
438 m_Buckets = buckets;
439 }
440
441 [[nodiscard]]
442 friend bool operator==(const bucketed_sequence& lhs, const bucketed_sequence& rhs) noexcept = default;
443 private:
444 container_type m_Buckets;
445
446 void check_range(std::string_view method, const size_type index) const
447 {
448 if(index >= m_Buckets.size())
449 {
450 throw std::out_of_range{std::string{"bucketed_sequence::"}.append(method).append("index ").append(std::to_string(index)).append(" out of range")};
451 }
452 }
453
454 void check_range(std::string_view method, const size_type index, const size_type pos) const
455 {
456 check_range(method, index);
457 const auto bucketSize{m_Buckets[index].size()};
458 if(pos > bucketSize)
459 {
460 throw std::out_of_range{std::string{"bucketed_sequence::"}.append(method).append("pos ").append(std::to_string(pos)).append(" out of range")};
461 }
462 }
463
464 void check_for_empty(std::string_view method) const
465 {
466 if(m_Buckets.empty()) throw std::out_of_range{std::string{"bucketed_sequence::"}.append(method).append(": no buckets\n")};
467 }
468
469 [[nodiscard]]
470 const_partition_iterator begin_partition_raw(const size_type i) const
471 {
472 return (i < m_Buckets.size()) ? const_partition_iterator{m_Buckets[i].cbegin(), i} : const_partition_iterator{m_Buckets.back().cend(), npos};
473 }
474
475 [[nodiscard]]
476 const_partition_iterator end_partition_raw(const size_type i) const
477 {
478 return (i < m_Buckets.size()) ? const_partition_iterator{m_Buckets[i].cend(), i} : const_partition_iterator{m_Buckets.back().cend(), npos};
479 }
480 };
481
482 //===================================Contiguous storage===================================//
483
487 template<class T, class Container, class Partitions>
489 {
490 public:
491 using value_type = T;
492 using container_type = Container;
493 using partitions_type = Partitions;
494 using index_type = typename partitions_type::value_type;
495 using size_type = std::common_type_t<index_type, typename container_type::size_type>;
496
501 using partition_range = std::ranges::subrange<partition_iterator>;
502 using const_partition_range = std::ranges::subrange<const_partition_iterator>;
503
504 constexpr partitioned_sequence_base() = default;
505
506 constexpr partitioned_sequence_base(const partitioned_sequence_base&) = default;
507
508 [[nodiscard]]
509 constexpr bool empty() const noexcept { return m_Data.empty(); }
510
511 [[nodiscard]]
512 constexpr auto size() const noexcept { return m_Data.size(); }
513
514 [[nodiscard]]
515 constexpr size_type size_of_partition(index_type i) const
516 {
517 return static_cast<size_type>(std::ranges::distance(partition(i)));
518 }
519
520 [[nodiscard]]
521 constexpr auto num_partitions() const noexcept { return m_Partitions.size(); }
522
523 [[nodiscard]]
524 constexpr partition_iterator begin_partition(const index_type i) noexcept
525 {
526 return get_begin_iterator<partition_iterator>(i, m_Data.begin());
527 }
528
529 [[nodiscard]]
530 constexpr partition_iterator end_partition(const index_type i) noexcept
531 {
532 return get_end_iterator<partition_iterator>(i, m_Data.begin());
533 }
534
535 [[nodiscard]]
536 constexpr const_partition_iterator begin_partition(const index_type i) const noexcept
537 {
538 return get_begin_iterator<const_partition_iterator>(i, m_Data.cbegin());
539 }
540
541 [[nodiscard]]
542 constexpr const_partition_iterator end_partition(const index_type i) const noexcept
543 {
544 return get_end_iterator<const_partition_iterator>(i, m_Data.cbegin());
545 }
546
547 [[nodiscard]]
548 constexpr const_partition_iterator cbegin_partition(const index_type i) const noexcept
549 {
550 return begin_partition(i);
551 }
552
553 [[nodiscard]]
554 constexpr const_partition_iterator cend_partition(const index_type i) const noexcept
555 {
556 return end_partition(i);
557 }
558
559 [[nodiscard]]
560 constexpr reverse_partition_iterator rbegin_partition(const index_type i) noexcept
561 {
562 return get_end_iterator<reverse_partition_iterator>(i, m_Data.rend());
563 }
564
565 [[nodiscard]]
566 constexpr reverse_partition_iterator rend_partition(const index_type i) noexcept
567 {
568 return get_begin_iterator<reverse_partition_iterator>(i, m_Data.rend());
569 }
570
571 [[nodiscard]]
572 constexpr const_reverse_partition_iterator rbegin_partition(const index_type i) const noexcept
573 {
574 return get_end_iterator<const_reverse_partition_iterator>(i, m_Data.crend());
575 }
576
577 [[nodiscard]]
578 constexpr const_reverse_partition_iterator rend_partition(const index_type i) const noexcept
579 {
580 return get_begin_iterator<const_reverse_partition_iterator>(i, m_Data.crend());
581 }
582
583 [[nodiscard]]
584 constexpr const_reverse_partition_iterator crbegin_partition(const index_type i) const noexcept
585 {
586 return rbegin_partition(i);
587 }
588
589 [[nodiscard]]
590 constexpr const_reverse_partition_iterator crend_partition(const index_type i) const noexcept
591 {
592 return rend_partition(i);
593 }
594
595 [[nodiscard]]
596 constexpr partition_range partition(const index_type i) noexcept { return {begin_partition(i), end_partition(i)}; }
597
598 [[nodiscard]]
599 constexpr const_partition_range partition(const index_type i) const noexcept { return {begin_partition(i), end_partition(i)}; }
600
601 [[nodiscard]]
602 constexpr const_partition_range cpartition(const index_type i) const noexcept { return partition(i); }
603
604 [[nodiscard]]
605 constexpr const_partition_iterator operator[](const index_type i) const noexcept { return cbegin_partition(i); }
606
607 [[nodiscard]]
608 constexpr partition_iterator operator[](const index_type i) noexcept { return begin_partition(i); }
609
610 constexpr void swap_partitions(index_type i, index_type j)
611 {
612 if((i < num_partitions()) && (j < num_partitions()))
613 {
614 if(i != j)
615 {
616 if(i > j) std::ranges::swap(i, j);
617
618 const auto len_i{std::ranges::distance(partition(i))};
619 const auto len_j{std::ranges::distance(partition(j))};
620
621 auto begin_i{begin_partition(i).base_iterator()}, begin_j{begin_partition(j).base_iterator()};
622 auto end_i{end_partition(i).base_iterator()}, end_j{end_partition(j).base_iterator()};
623 for(auto iter_i{begin_i}, iter_j{begin_j}; (iter_i != end_i) && (iter_j != end_j); ++iter_i, ++iter_j)
624 {
625 std::ranges::iter_swap(iter_i, iter_j);
626 }
627
628 if(len_i > len_j)
629 {
630 std::ranges::rotate(begin_i + len_j, begin_i + len_i, end_j);
631 }
632 else if(len_j > len_i)
633 {
634 std::ranges::rotate(begin_i + len_i, begin_j + len_i, end_j);
635 }
636
637 if(len_i != len_j)
638 {
639 m_Partitions.mutate(maths::unsafe_t{},
640 m_Partitions.begin() + i,
641 m_Partitions.begin() + j,
642 [len_i, len_j](const index_type index) -> index_type { return static_cast<index_type>(index + len_j - len_i);});
643 }
644 }
645 }
646 }
647
648 template<alloc Allocator, alloc PartitionsAllocator>
649 requires (std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value
650 && std::allocator_traits<PartitionsAllocator>::propagate_on_container_copy_assignment::value)
651 void reset(const Allocator& allocator, const PartitionsAllocator& partitionsAllocator) noexcept
652 {
653 const partitions_type partitions(partitionsAllocator);
654 m_Partitions = partitions;
655
656 const container_type container{allocator};
657 m_Data = container;
658 }
659
660 [[nodiscard]]
661 friend constexpr bool operator==(const partitioned_sequence_base&, const partitioned_sequence_base&) noexcept = default;
662 protected:
663 explicit constexpr partitioned_sequence_base(const std::pair<partitions_type, container_type>& data)
664 : m_Partitions{data.first}
665 , m_Data{data.second}
666 {}
667
668 constexpr partitioned_sequence_base(partitioned_sequence_base&&) noexcept = default;
669
670 constexpr partitioned_sequence_base& operator=(partitioned_sequence_base&&) noexcept = default;
671
672 constexpr partitioned_sequence_base& operator=(const partitioned_sequence_base&) = default;
673
674 ~partitioned_sequence_base() = default;
675
676 void swap(partitioned_sequence_base& other)
677 noexcept(noexcept(std::ranges::swap(this->m_Partitions, other.m_Partitions)) && noexcept(std::ranges::swap(this->m_Data, other.m_Data)))
678 {
679 std::ranges::swap(m_Partitions, other.m_Partitions);
680 std::ranges::swap(m_Data, other.m_Data);
681 }
682
683 [[nodiscard]]
684 auto get_allocator() const
685 {
686 return m_Data.get_allocator();
687 }
688
689 [[nodiscard]]
690 auto get_partitions_allocator() const
691 {
692 return m_Partitions.get_allocator();
693 }
694
695 template<alloc Allocator>
696 constexpr explicit partitioned_sequence_base(const Allocator& allocator) noexcept
697 : m_Data(allocator)
698 {}
699
700 template<alloc Allocator, alloc PartitionsAllocator>
701 constexpr partitioned_sequence_base(const Allocator& allocator, const PartitionsAllocator& partitionsAllocator) noexcept
702 : m_Partitions(partitionsAllocator)
703 , m_Data(allocator)
704 {}
705
706 template<alloc Allocator, alloc PartitionsAllocator>
707 constexpr partitioned_sequence_base(std::initializer_list<std::initializer_list<T>> list, const Allocator& allocator, const PartitionsAllocator& partitionsAllocator)
708 : m_Partitions(partitionsAllocator)
709 , m_Data(allocator)
710 {
711 init(list);
712 }
713
714 template<alloc Allocator, alloc PartitionsAllocator>
715 constexpr partitioned_sequence_base(const partitioned_sequence_base& other, const Allocator& allocator, const PartitionsAllocator& partitionsAllocator)
716 : m_Partitions{other.m_Partitions, partitionsAllocator}
717 , m_Data(copy(other.m_Data, allocator))
718 {}
719
720 template<alloc Allocator, alloc PartitionsAllocator>
721 constexpr partitioned_sequence_base(partitioned_sequence_base&& in, const Allocator& allocator, const PartitionsAllocator& partitionsAllocator)
722 : m_Partitions{std::move(in.m_Partitions), partitionsAllocator}
723 , m_Data{std::move(in.m_Data), allocator}
724 {}
725
726 void add_slot()
727 {
728 m_Partitions.push_back(m_Data.size());
729 }
730
731 void insert_slot(const size_t pos)
732 {
733 if(pos < num_partitions())
734 {
735 auto iter{m_Partitions.begin() + pos};
736 const index_type newPartitionBound{(pos == 0) ? 0 : *(iter - 1)};
737 m_Partitions.insert(iter, newPartitionBound);
738 }
739 else
740 {
741 add_slot();
742 }
743 }
744
745 void erase_slot(const index_type n)
746 {
747 if(n < m_Partitions.size())
748 {
749 index_type erased{};
750 const index_type offset{(n > 0) ? m_Partitions[n - 1] : index_type{}};
751 if(m_Partitions[n] > 0)
752 {
753 erased = m_Partitions[n] - offset;
754 m_Data.erase(m_Data.begin() + offset, m_Data.begin() + m_Partitions[n]);
755 }
756 m_Partitions.erase(m_Partitions.begin() + n);
757 m_Partitions.mutate(maths::unsafe_t{}, m_Partitions.begin() + n, m_Partitions.end(), [erased](const auto index){ return index - erased; });
758 }
759 }
760
761 void reserve(const size_type size)
762 {
763 m_Data.reserve(size);
764 }
765
766 [[nodiscard]]
767 index_type capacity() const noexcept
768 {
769 return m_Data.capacity();
770 }
771
772 void reserve_partitions(const size_type numPartitions)
773 {
774 m_Partitions.reserve(numPartitions);
775 }
776
777 [[nodiscard]]
778 index_type num_partitions_capacity() const noexcept
779 {
780 return m_Partitions.capacity();
781 }
782
783 void shrink_to_fit()
784 {
785 m_Partitions.shrink_to_fit();
786 m_Data.shrink_to_fit();
787 }
788
789 void clear() noexcept
790 {
791 m_Partitions.clear();
792 m_Data.clear();
793 }
794
795 template<class... Args>
796 void push_back_to_partition(const index_type index, Args&&... args)
797 {
798 check_range("push_back_to_partition", index);
799
800 auto iter{m_Data.end()};
801 if(index == m_Partitions.size() - 1)
802 {
803 m_Data.emplace_back(std::forward<Args>(args)...);
804 iter = --m_Data.end();
805 }
806 else
807 {
808 iter = m_Data.emplace(m_Data.begin() + m_Partitions[index], std::forward<Args>(args)...);
809 }
810
811 increment_partition_indices(index);
812 }
813
814 template<class... Args>
815 partition_iterator insert_to_partition(const_partition_iterator pos, Args&&... args)
816 {
817 const auto source{pos.partition_index()};
818 check_range("insert_to_partition", source);
819
820 auto iter{m_Data.emplace(pos.base_iterator(), std::forward<Args>(args)...)};
821 increment_partition_indices(source);
822
823 return partition_iterator{iter, source};
824 }
825
826 template<class... Args>
827 partition_iterator insert_to_partition(const size_type index, const size_type pos, Args&&... args)
828 {
829 return insert_to_partition(std::ranges::next(cbegin_partition(index), pos, cend_partition(index)), std::forward<Args>(args)...);
830 }
831
832 partition_iterator erase_from_partition(const_partition_iterator iter)
833 {
834 const auto partition{iter.partition_index()};
835 if(const auto n{num_partitions()}; partition >= n)
836 {
837 return end_partition(n);
838 }
839 else if(iter == cend_partition(partition))
840 {
841 return end_partition(partition);
842 }
843
844 const auto next{m_Data.erase(iter.base_iterator())};
845 decrement_partition_indices(partition, num_partitions(), 1);
846
847 return {next, iter.partition_index()};
848 }
849
851 {
852 const auto firstPartition{first.partition_index()}, lastPartition{last.partition_index()};
853 if(const auto n{num_partitions()}; (firstPartition >= n) && (lastPartition >= n))
854 {
855 return end_partition(n);
856 }
857 else if(lastPartition == firstPartition)
858 {
859 if(const auto numErased{std::ranges::distance(first, last)}; numErased > 0)
860 {
861 const auto next{m_Data.erase(first.base_iterator(), last.base_iterator())};
862 decrement_partition_indices(firstPartition, num_partitions(), numErased);
863
864 return {next, firstPartition};
865 }
866
867 return std::ranges::next(begin_partition(firstPartition), std::ranges::distance(cbegin_partition(firstPartition), first));
868 }
869
870 throw std::domain_error{std::string{"partitioned_sequence::erase - Invalid range specified by iterators belonging to partitions ["}
871 .append(std::to_string(firstPartition)).append(", ").append(std::to_string(lastPartition)).append("]")};
872 }
873
874 partition_iterator erase_from_partition(const index_type index, const size_type pos)
875 {
876 return erase_from_partition(std::ranges::next(cbegin_partition(index), pos, cend_partition(index)));
877 }
878 private:
879 constexpr static index_type npos{partition_iterator::npos};
880
881 SEQUOIA_NO_UNIQUE_ADDRESS partitions_type m_Partitions;
882 container_type m_Data;
883
884 partitioned_sequence_base(std::initializer_list<std::initializer_list<T>> list)
885 {
886 init(list);
887 }
888
889 void init(std::initializer_list<std::initializer_list<T>> list)
890 {
891 m_Partitions.reserve(list.size());
892 m_Data.reserve(std::accumulate(list.begin(), list.end(), std::size_t{}, [](std::size_t n, auto l) { return n += l.size(); }));
893 for(auto iter{list.begin()}; iter != list.end(); ++iter)
894 {
895 add_slot();
896 const auto dist{std::ranges::distance(list.begin(), iter)};
897 for(const auto& element : (*iter))
898 {
899 push_back_to_partition(dist, element);
900 }
901 }
902 }
903
904 template<alloc Allocator>
905 [[nodiscard]]
906 static container_type copy(const container_type& other, const Allocator& a)
907 {
908 container_type container(std::allocator_traits<Allocator>::select_on_container_copy_construction(a));
909
910 container.reserve(other.size());
911 for(const auto& elt : other)
912 {
913 container.push_back(elt);
914 }
915
916 return container;
917 }
918
919 void increment_partition_indices(const size_type first) noexcept
920 {
921 m_Partitions.mutate(maths::unsafe_t{},
922 std::ranges::next(m_Partitions.begin(), first, m_Partitions.end()),
923 m_Partitions.end(),
924 [](index_type index){ return ++index; });
925 }
926
927 void decrement_partition_indices(const size_type first, const size_type last, const index_type num) noexcept
928 {
929 m_Partitions.mutate(maths::unsafe_t{},
930 std::ranges::next(m_Partitions.begin(), first, m_Partitions.end()),
931 std::ranges::next(m_Partitions.begin(), last, m_Partitions.end()),
932 [num](index_type index){ return index -= num; });
933 }
934
935 void check_range(std::string_view method, const size_type index) const
936 {
937 if(index >= m_Partitions.size())
938 {
939 throw std::out_of_range{std::string{"partition_sequence::"}.append(method).append("index ").append(std::to_string(index)).append(" out of range")};
940 }
941 }
942
943 void check_range(std::string_view method, const size_type index, const index_type pos) const
944 {
945 check_range(method, index);
946 const index_type maxPos{index ? m_Partitions[index] - m_Partitions[index - 1] : m_Partitions[index]};
947 if(pos > maxPos)
948 {
949 throw std::out_of_range{std::string{"partition_sequence::"}.append(method).append("pos ").append(std::to_string(pos)).append(" out of range")};
950 }
951 }
952
953 template<class PartitionIterator, std::input_or_output_iterator Iterator>
954 [[nodiscard]]
955 constexpr PartitionIterator get_begin_iterator(const index_type i, Iterator iter) const noexcept
956 {
957 const index_type index{i >= m_Partitions.size() ? npos : i};
958 const auto offset{(i > 0 && i < m_Partitions.size()) ? m_Partitions[i-1] : index_type{}};
959 return PartitionIterator::reversed() ? PartitionIterator{iter, index} - offset : PartitionIterator{iter, index} + offset;
960 }
961
962 template<class PartitionIterator, std::input_or_output_iterator Iterator>
963 [[nodiscard]]
964 constexpr PartitionIterator get_end_iterator(const index_type i, Iterator iter) const
965 {
966 index_type index{PartitionIterator::reversed() ? index_type{} : npos};
967 index_type offset{
968 [sz{m_Data.size()}] () {
969 if (sz > std::numeric_limits<index_type>::max())
970 throw std::out_of_range{"Partition offset out of range"};
971 return static_cast<index_type>(sz);
972 }()
973 };
974
975 if(i < m_Partitions.size())
976 {
977 index = i;
978 offset = m_Partitions[i];
979 }
980
981 return PartitionIterator::reversed() ? PartitionIterator{iter, index} - offset : PartitionIterator{iter, index} + offset;
982 }
983 };
984
989 template<class T, class Container=std::vector<T>, class Partitions=maths::monotonic_sequence<std::size_t, std::ranges::greater>>
990 class partitioned_sequence : public partitioned_sequence_base<T, Container, Partitions>
991 {
992 private:
994 public:
995 using container_type = typename partitioned_sequence_base<T, Container, Partitions>::container_type;
996 using partitions_type = typename partitioned_sequence_base<T, Container, Partitions>::partitions_type;
997 using allocator_type = typename container_type::allocator_type;
998 using partitions_allocator_type = typename partitions_type::allocator_type;
999
1000 partitioned_sequence() = default;
1001
1002 partitioned_sequence(const allocator_type& allocator, const partitions_allocator_type& partitionAllocator) noexcept
1003 : partitioned_sequence_base<T, Container, Partitions>(allocator, partitionAllocator)
1004 {}
1005
1006 partitioned_sequence(std::initializer_list<std::initializer_list<T>> list, const allocator_type& allocator=allocator_type{}, const partitions_allocator_type& partitionAllocator=partitions_allocator_type{})
1007 : partitioned_sequence_base<T, Container, Partitions>(list, allocator, partitionAllocator)
1008 {}
1009
1011
1012 partitioned_sequence(const partitioned_sequence& s, const allocator_type& allocator, const partitions_allocator_type& partitionAllocator)
1013 : partitioned_sequence_base<T, Container, Partitions>(s, allocator, partitionAllocator)
1014 {}
1015
1016 partitioned_sequence(partitioned_sequence&&) noexcept = default;
1017
1018 partitioned_sequence(partitioned_sequence&& s, const allocator_type& allocator, const partitions_allocator_type& partitionAllocator)
1019 : partitioned_sequence_base<T, Container, Partitions>(std::move(s), allocator, partitionAllocator)
1020 {}
1021
1022 ~partitioned_sequence() = default;
1023
1024 partitioned_sequence& operator=(const partitioned_sequence&) = default;
1025 partitioned_sequence& operator=(partitioned_sequence&&) noexcept = default;
1026
1027 using base_t::swap;
1028
1029 friend void swap(partitioned_sequence& lhs, partitioned_sequence& rhs)
1030 noexcept(noexcept(lhs.swap(rhs)))
1031 {
1032 lhs.swap(rhs);
1033 }
1034
1035 using base_t::add_slot;
1036 using base_t::insert_slot;
1037 using base_t::erase_slot;
1038
1039 using base_t::reserve;
1040 using base_t::capacity;
1041 using base_t::reserve_partitions;
1042 using base_t::num_partitions_capacity;
1043 using base_t::shrink_to_fit;
1044 using base_t::get_allocator;
1045 using base_t::get_partitions_allocator;
1046
1047 using base_t::clear;
1048 using base_t::push_back_to_partition;
1049 using base_t::insert_to_partition;
1050 using base_t::erase_from_partition;
1051 };
1052
1053 template<class T>
1055
1056 template<std::integral IndexType, std::size_t Npartitions>
1057 struct static_partitions_maker<maths::static_monotonic_sequence<IndexType, Npartitions, std::ranges::greater>>
1058 {
1060
1061 constexpr static std::size_t num_partitions_v{Npartitions};
1062
1063 template<class T>
1064 [[nodiscard]]
1065 constexpr static partitions_type make_partitions(std::initializer_list<std::initializer_list<T>> list)
1066 {
1067 constexpr auto upper{std::numeric_limits<IndexType>::max()};
1068 const auto fn{
1069 [](std::initializer_list<T> l) -> IndexType {
1070 if(l.size() > upper)
1071 throw std::out_of_range{"Partition size out of bounds"};
1072
1073 return static_cast<IndexType>(l.size());
1074 }
1075 };
1076
1077 auto sizes{utilities::to_array<IndexType, num_partitions_v>(list, fn)};
1078 if(!sizes.empty())
1079 {
1080 for(auto iter{sizes.begin() + 1}; iter != sizes.end(); ++iter)
1081 {
1082 const auto prev{*(iter - 1)};
1083 auto& curr{*iter};
1084 if(upper - prev < curr)
1085 throw std::out_of_range{"Partition index out of bounds"};
1086
1087 curr += prev;
1088 }
1089 }
1090
1091 return sizes;
1092 }
1093
1094 };
1095
1096 template<class T, std::size_t Npartitions, std::size_t Nelements, class Partitions=maths::static_monotonic_sequence<std::size_t, Npartitions, std::ranges::greater>>
1098 public partitioned_sequence_base<T, std::array<T, Nelements>, Partitions>
1099 {
1101 public:
1102 using container_type = typename base_t::container_type;
1103 using partitions_type = typename base_t::partitions_type;
1104 using size_type = typename base_t::size_type;
1105 using index_type = typename base_t::index_type;
1106
1107 constexpr static std::size_t num_partitions_v{Npartitions};
1108 constexpr static std::size_t num_elements_v{Nelements};
1109
1110 constexpr static_partitioned_sequence() = default;
1111
1112 constexpr static_partitioned_sequence(std::initializer_list<std::initializer_list<T>> list)
1113 : base_t{fill(std::make_index_sequence<num_elements_v>(), list)}
1114 {}
1115
1116 constexpr static_partitioned_sequence(const static_partitioned_sequence&) = default;
1117 constexpr static_partitioned_sequence(static_partitioned_sequence&&) noexcept = default;
1118
1119 constexpr static_partitioned_sequence& operator=(const static_partitioned_sequence&) = default;
1120 constexpr static_partitioned_sequence& operator=(static_partitioned_sequence&&) noexcept = default;
1121
1122 using base_t::swap;
1123
1124 friend void swap(static_partitioned_sequence& lhs, static_partitioned_sequence& rhs)
1125 noexcept(noexcept(lhs.swap(rhs)))
1126 {
1127 lhs.swap(rhs);
1128 }
1129 private:
1130 template<size_type... Inds>
1131 [[nodiscard]]
1132 constexpr static std::pair<partitions_type, container_type> fill(std::index_sequence<Inds...>, std::initializer_list<std::initializer_list<T>> list)
1133 {
1134 if(list.size() != num_partitions_v)
1135 throw std::logic_error{std::string{"Inconsistent number of elements supplied by initializer lists: expected "}
1136 .append(std::to_string(num_partitions_v)).append(" but got ").append(std::to_string(list.size()))};
1137
1139
1140 const size_type total{std::accumulate(list.begin(), list.end(), size_type{}, [](size_type val, std::initializer_list<T> l){ return val + l.size(); })};
1141
1142 if(total != num_elements_v)
1143 throw std::logic_error{std::string{"Inconsistent number of elements supplied by initializer lists: expected "}
1144 .append(std::to_string(num_elements_v)).append(" but got ").append(std::to_string(total))};
1145
1146 if constexpr(sizeof...(Inds) > 0)
1147 {
1148 index_type partitionIndex{};
1149 return {partitions, container_type{make_element(Inds, list, partitions, partitionIndex)...}};
1150 }
1151 else
1152 {
1153 return {partitions, container_type{}};
1154 }
1155 }
1156
1157
1158 [[nodiscard]]
1159 constexpr static T make_element(size_type i, std::initializer_list<std::initializer_list<T>> list, const partitions_type& partitions, index_type& partitionIndex)
1160 {
1161 auto boundary{partitions[partitionIndex]};
1162 while(i == boundary)
1163 {
1164 boundary = partitions[++partitionIndex];
1165 }
1166
1167 auto index{partitionIndex ? i - partitions[partitionIndex - 1] : i};
1168
1169 return T{*((list.begin() + partitionIndex)->begin() + index)};
1170 }
1171 };
1172 }
1173}
A collection of constexpr algorithms.
Utility to convert an initializer_list into an array, potentially transforming the initializer_list i...
Implementation for an iterator with policies controlling dereferencing and auxiliary data.
Classes implementing the concept of a monotonic sequence.
Metaprogramming components for partitioned data.
Storage for partitioned data such that data within each partition is contiguous.
Definition: PartitionedData.hpp:63
Base class for partitioned sequences where data is contiguous across all partitions.
Definition: PartitionedData.hpp:489
Definition: PartitionedData.hpp:991
Definition: PartitionedData.hpp:1099
Definition: MonotonicSequence.hpp:303
An iterator with policies controlling dereferencing and auxiliary data.
Definition: Iterator.hpp:234
Definition: PartitionedData.hpp:1054
Definition: MonotonicSequence.hpp:22