Project
Loading...
Searching...
No Matches
SparseVector.h
Go to the documentation of this file.
1// Copyright 2019-2023 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
15
16#ifndef RANS_INTERNAL_CONTAINER_SPARSEVECTOR_H_
17#define RANS_INTERNAL_CONTAINER_SPARSEVECTOR_H_
18
19#include <algorithm>
20#include <cassert>
21#include <cstdint>
22#include <iterator>
23#include <string>
24#include <vector>
25
26#include <fairlogger/Logger.h>
27
29
30namespace o2::rans::internal
31{
32
33template <class container_T>
34class SparseVectorIterator;
35
36template <class source_T, class value_T>
38{
40
41 public:
43 using value_type = value_T;
44 using bucket_type = std::vector<value_type>;
45 using container_type = std::vector<bucket_type>;
46 using size_type = size_t;
47 using difference_type = std::ptrdiff_t;
51 using const_pointer = const value_type*;
54
55 private:
56 friend iterator;
57 friend const_iterator;
58
59 using lut_iterator = typename container_type::iterator;
60
61 public:
62 explicit SparseVector(const_reference neutralElement = {}) : mNullElement{neutralElement},
63 mContainer(mNBuckets),
64 mBegin{mContainer.end()},
65 mEnd{mContainer.end()} {};
66
67 SparseVector(const SparseVector& other) : mNullElement{other.mNullElement},
68 mUsedBuckets{other.mUsedBuckets},
69 mContainer{other.mContainer},
70 mBegin{mContainer.end()},
71 mEnd{mContainer.end()}
72 {
73 if (!this->empty()) {
74 mBegin = std::find_if_not(mContainer.begin(), mContainer.end(), [](const auto& vec) { return vec.empty(); });
75 mEnd = std::find_if_not(mContainer.rbegin(), mContainer.rend(), [](const auto& vec) { return vec.empty(); }).base();
76 }
77 };
78
80
82 {
83 SparseVector tmp = other;
84 std::swap(tmp, *this);
85 return *this;
86 };
87
89 ~SparseVector() = default;
90
91 [[nodiscard]] inline const_reference operator[](source_type sourceSymbol) const
92 {
93 const auto [lutIndex, bucketIndex] = splitIndex(sourceSymbol);
94 return mContainer[lutIndex][bucketIndex];
95 };
96
97 [[nodiscard]] inline reference operator[](source_type sourceSymbol)
98 {
99 auto [lutIndex, bucketIndex] = splitIndex(sourceSymbol);
100 bucket_type& bucket = mContainer[lutIndex];
101 if (bucket.empty()) {
102 bucket = makeBucket(lutIndex);
103 return bucket[bucketIndex];
104 } else {
105 return bucket[bucketIndex];
106 }
107 };
108
109 [[nodiscard]] inline const_reference at(source_type sourceSymbol) const
110 {
111 auto [lutIndex, bucketIndex] = splitIndex(sourceSymbol);
112 const bucket_type& bucket = mContainer[lutIndex];
113 if (bucket.empty()) {
114 return mNullElement;
115 } else {
116 return bucket[bucketIndex];
117 }
118 };
119
120 [[nodiscard]] inline reference at(source_type sourceSymbol) { return this->operator[](sourceSymbol); };
121
122 [[nodiscard]] inline size_type size() const noexcept { return mUsedBuckets * mBucketSize; };
123
124 [[nodiscard]] inline bool empty() const noexcept { return mUsedBuckets == 0; };
125
126 [[nodiscard]] inline static constexpr source_type getOffset() noexcept { return mOffset; };
127
128 [[nodiscard]] inline bucket_type* data() noexcept { return mContainer.data(); };
129
130 [[nodiscard]] inline const bucket_type* data() const noexcept { return mContainer.data(); };
131
132 [[nodiscard]] inline const_iterator cbegin() const noexcept { return empty() ? const_iterator{*this} : const_iterator{*this, mBegin}; };
133
134 [[nodiscard]] inline const_iterator cend() const noexcept { return empty() ? const_iterator{*this} : const_iterator{*this, mEnd}; };
135
136 [[nodiscard]] inline const_iterator begin() const noexcept { return cbegin(); };
137
138 [[nodiscard]] inline const_iterator end() const noexcept { return cend(); };
139
140 [[nodiscard]] inline iterator begin() noexcept { return empty() ? iterator{*this} : iterator{*this, mBegin}; };
141
142 [[nodiscard]] inline iterator end() noexcept { return empty() ? iterator{*this} : iterator{*this, mEnd}; };
143
144 [[nodiscard]] inline container_type release() && noexcept { return std::move(this->mContainer); };
145
146 [[nodiscard]] inline static size_type getBucketSize() noexcept { return mBucketSize; };
147
148 [[nodiscard]] inline static size_type getNBuckets() noexcept { return mNBuckets; };
149
150 [[nodiscard]] inline static constexpr std::pair<uint32_t, uint32_t> splitIndex(source_type sourceSymbol) noexcept
151 {
152 if constexpr (sizeof(source_type) < 4) {
153 difference_type idx = static_cast<difference_type>(sourceSymbol) - mOffset;
154 return {0, idx};
155 } else {
156
157 if constexpr (std::is_signed_v<source_type>) {
158 difference_type idx = static_cast<difference_type>(sourceSymbol) - mOffset;
159 return {static_cast<uint32_t>(idx >> mBucketWidth), static_cast<uint16_t>(idx)};
160 } else {
161 return {static_cast<uint32_t>(sourceSymbol) >> mBucketWidth, static_cast<uint16_t>(sourceSymbol)};
162 }
163 }
164 }
165
166 [[nodiscard]] inline static constexpr source_type joinIndex(std::uint32_t lutID, std::uint32_t bucketID) noexcept
167 {
168 auto index = static_cast<difference_type>((lutID << mBucketWidth) | bucketID) + mOffset;
169 assert(index >= static_cast<difference_type>(std::numeric_limits<source_type>::min()));
170 assert(index <= static_cast<difference_type>(std::numeric_limits<source_type>::max()));
171 return index;
172 };
173
175 {
176 using std::swap;
177 swap(a.mNullElement, b.mNullElement);
178 swap(a.mUsedBuckets, b.mUsedBuckets);
179 swap(a.mContainer, b.mContainer);
180 swap(a.mBegin, b.mBegin);
181 swap(a.mEnd, b.mEnd);
182 };
183
184 private:
185 inline bucket_type& makeBucket(size_type index)
186 {
187 mContainer[index] = bucket_type(mBucketSize, mNullElement);
188 updateIterators(index);
189 ++mUsedBuckets;
190 return mContainer[index];
191 };
192
193 inline void updateIterators(size_type newPos)
194 {
195 lut_iterator newBucketIter = mContainer.begin() + newPos;
196 // init if empty
197 if (empty()) {
198 mBegin = newBucketIter;
199 mEnd = ++newBucketIter;
200 } else {
201 mBegin = newBucketIter < mBegin ? newBucketIter : mBegin;
202 mEnd = ++newBucketIter > mEnd ? newBucketIter : mEnd;
203 }
204 };
205
206 value_type mNullElement{};
207 size_type mUsedBuckets{};
208 container_type mContainer{};
209 lut_iterator mBegin{};
210 lut_iterator mEnd{};
211
212 inline static constexpr difference_type mOffset{std::numeric_limits<source_type>::min()};
213 inline static constexpr size_type mBucketWidth = sizeof(source_type) > 1 ? 16 : 8;
214 inline static constexpr size_type mBucketSize = utils::pow2(mBucketWidth);
215 inline static constexpr size_type mNBuckets = utils::pow2(utils::toBits<source_type>() - mBucketWidth);
216};
217
218/*
219Iterator conventions:
220* mContainer->empty() is never true
221* mUsedBuckets determines how many buckets are currently occupied
222* if there are no used buckets, begin() and end() must point to mContainer.end();
223* if a find_next operation does not find any further allocated buckets, we are at the end.
224* trying to increment a one-past the end or singular iterator is undefined behavior
225* "end() is not a valid position indicator for a bucket unless mUsedBuckets==0, in which case begin()==end()"
226*/
227
228template <class container_T>
230{
231 public:
232 using container_value_type = std::conditional_t<std::is_const_v<container_T>, const typename container_T::value_type, typename container_T::value_type>;
233
234 using lut_iterator = std::conditional_t<std::is_const_v<container_value_type>,
235 typename container_T::container_type::const_iterator,
236 typename container_T::container_type::iterator>;
237 using bucket_iterator = std::conditional_t<std::is_const_v<container_value_type>,
238 typename std::iterator_traits<lut_iterator>::value_type::const_iterator,
239 typename std::iterator_traits<lut_iterator>::value_type::iterator>;
240
241 public:
242 class PtrHelper;
243
244 using source_type = typename container_T::source_type;
245 using difference_type = std::ptrdiff_t;
246 using value_type = std::pair<source_type, container_value_type&>;
249 using iterator_category = std::bidirectional_iterator_tag;
250
251 inline SparseVectorIterator() noexcept = default;
252
253 inline SparseVectorIterator(container_T& container) noexcept : mContainer{&container}, mLutIter{mContainer->mContainer.end()} {};
254
255 inline SparseVectorIterator(container_T& container, lut_iterator lutIter) noexcept : mContainer{&container}, mLutIter{lutIter}
256 {
257 if (mLutIter != mContainer->mContainer.end()) {
258 mBucketIter = mLutIter->begin();
259 }
260 };
261
262 inline SparseVectorIterator(container_T& container, lut_iterator lutIter, bucket_iterator bucketIter) noexcept : mContainer{&container}, mLutIter{lutIter}, mBucketIter{bucketIter} {};
263
264 // pointer arithmetics
266 {
267 // this is always legitimate. Incrementing a singular and one-past the end SparseVectorIterator is UB.
268 ++mBucketIter;
269 // handle end of current bucket
270 if (mBucketIter == mLutIter->end()) {
271 // find next, non-empty bucket.
272 auto newEndIter = std::find_if(++mLutIter, mContainer->mContainer.end(), [](const auto& container) { return !container.empty(); });
273 // if no non-empty buckets can be found, we point to one-past our current bucket which is always a legal iterator, otherwise to the one that has been found.
274 mLutIter = newEndIter == mContainer->mContainer.end() ? mLutIter : newEndIter;
275 // point to the first element of the new bucket (if that is legal)
276 mBucketIter = mLutIter != mContainer->mContainer.end() ? mLutIter->begin() : bucket_iterator{};
277 }
278 return *this;
279 };
280
281 inline SparseVectorIterator operator++(int) noexcept
282 {
283 auto res = *this;
284 ++(*this);
285 return res;
286 };
287
288 // pointer arithmetics
290 {
291 // base case: not at the beginning of an allocated bucket.
292 if ((mBucketIter != mLutIter->begin()) && (mBucketIter != bucket_iterator{})) {
293 --mBucketIter;
294 } else {
295 // if not, we need to find the nearest allocated bucket before this.
296 auto nextRBucket = std::find_if(std::make_reverse_iterator(mLutIter), mContainer->mContainer.rend(), [](const auto& container) { return !container.empty(); });
297 // if there are no more allocated buckets before this bucket, we stay where we are, otherwise we point to the new location
298 mLutIter = nextRBucket != mContainer->mContainer.rend() ? --nextRBucket.base() : mLutIter;
299 // we can always point to the end. decrementing a singular SparseVectorIterator or the first element is UB.
300 mBucketIter = --mLutIter->end();
301 }
302 return *this;
303 };
304
305 inline SparseVectorIterator operator--(int) noexcept
306 {
307 auto res = *this;
308 --(*this);
309 return res;
310 };
311
312 // comparison
313 inline bool operator==(const SparseVectorIterator& other) const noexcept { return this->mLutIter == other.mLutIter &&
314 this->mBucketIter == other.mBucketIter; };
315 inline bool operator!=(const SparseVectorIterator& other) const noexcept { return !(*this == other); };
316
317 // dereference
318 inline value_type operator*() const noexcept
319 {
320 size_t lut = std::distance(mContainer->mContainer.begin(), mLutIter);
321 size_t bucket = std::distance(mLutIter->begin(), mBucketIter);
322 source_type index = container_T::joinIndex(lut, bucket);
323 assert(mBucketIter != bucket_iterator{});
324 return {index, *mBucketIter};
325 };
326
327 inline pointer operator->() noexcept
328 {
329 return {operator*()};
330 };
331
332 inline pointer operator->() const noexcept
333 {
334 return {operator*()};
335 };
336
337 // convert to const iter
338 inline operator SparseVectorIterator<const container_T>() { return {*mContainer, mLutIter, mBucketIter}; };
339
341 {
342 public:
343 PtrHelper() = default;
344 PtrHelper(value_type value) : mValue{std::move(value)} {};
345
346 value_type* operator->() { return &mValue; }
347 value_type* operator->() const { return &mValue; }
348
349 private:
350 value_type mValue{};
351 };
352
353 inline container_T& getContainer() const { return *mContainer; };
354
355 inline lut_iterator getLUTIterator() const { return mLutIter; };
356 inline bucket_iterator getBucketIterator() const { return mBucketIter; };
357
358 private:
359 container_T* mContainer{};
360 lut_iterator mLutIter{};
361 bucket_iterator mBucketIter{};
362};
363
364} // namespace o2::rans::internal
365
366#endif /* RANS_INTERNAL_CONTAINER_SPARSEVECTOR_H_ */
uint32_t res
Definition RawData.h:0
common helper classes and functions
std::conditional_t< std::is_const_v< container_T >, const typename container_T::value_type, typename container_T::value_type > container_value_type
SparseVectorIterator operator--(int) noexcept
SparseVectorIterator(container_T &container, lut_iterator lutIter) noexcept
bucket_iterator getBucketIterator() const
typename container_T::source_type source_type
std::conditional_t< std::is_const_v< container_value_type >, typename std::iterator_traits< lut_iterator >::value_type::const_iterator, typename std::iterator_traits< lut_iterator >::value_type::iterator > bucket_iterator
SparseVectorIterator operator++(int) noexcept
value_type operator*() const noexcept
SparseVectorIterator & operator--() noexcept
SparseVectorIterator(container_T &container, lut_iterator lutIter, bucket_iterator bucketIter) noexcept
std::bidirectional_iterator_tag iterator_category
bool operator==(const SparseVectorIterator &other) const noexcept
std::pair< source_type, container_value_type & > value_type
std::conditional_t< std::is_const_v< container_value_type >, typename container_T::container_type::const_iterator, typename container_T::container_type::iterator > lut_iterator
SparseVectorIterator & operator++() noexcept
bool operator!=(const SparseVectorIterator &other) const noexcept
const value_type & const_reference
const value_type * const_pointer
reference operator[](source_type sourceSymbol)
reference at(source_type sourceSymbol)
const_iterator begin() const noexcept
void swap(SparseVector &a, SparseVector &b)
size_type size() const noexcept
static constexpr source_type getOffset() noexcept
container_type release() &&noexcept
SparseVector(SparseVector &&other)=default
SparseVector(const_reference neutralElement={})
bool empty() const noexcept
const_reference at(source_type sourceSymbol) const
const_iterator cend() const noexcept
static constexpr std::pair< uint32_t, uint32_t > splitIndex(source_type sourceSymbol) noexcept
SparseVector(const SparseVector &other)
static constexpr source_type joinIndex(std::uint32_t lutID, std::uint32_t bucketID) noexcept
bucket_type * data() noexcept
const_reference operator[](source_type sourceSymbol) const
SparseVector & operator=(SparseVector &&other)=default
SparseVectorIterator< this_type > iterator
const_iterator end() const noexcept
std::vector< bucket_type > container_type
const_iterator cbegin() const noexcept
static size_type getNBuckets() noexcept
static size_type getBucketSize() noexcept
std::vector< value_type > bucket_type
const bucket_type * data() const noexcept
SparseVector & operator=(const SparseVector &other)
SparseVectorIterator< const this_type > const_iterator
GLuint GLuint end
Definition glcorearb.h:469
GLuint index
Definition glcorearb.h:781
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
constexpr size_t pow2(size_t n) noexcept
Definition utils.h:165
Defining DataPointCompositeObject explicitly as copiable.
VectorOfTObjectPtrs other
std::vector< o2::ctf::BufferType > vec