pasta::bit_vector  v1.0.0
Compact and Fast Rank and Select Data Structure for Bit Vectors
flat_rank_select.hpp
1/*******************************************************************************
2 * This file is part of pasta::bit_vector.
3 *
4 * Copyright (C) 2021 Florian Kurpicz <florian@kurpicz.org>
5 *
6 * pasta::bit_vector is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * pasta::bit_vector is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with pasta::bit_vector. If not, see <http://www.gnu.org/licenses/>.
18 *
19 ******************************************************************************/
20
21#pragma once
22
23#include "pasta/bit_vector/bit_vector.hpp"
24#include "pasta/bit_vector/support/find_l2_flat_with.hpp"
25#include "pasta/bit_vector/support/flat_rank.hpp"
26#include "pasta/bit_vector/support/l12_type.hpp"
27#include "pasta/bit_vector/support/optimized_for.hpp"
28#include "pasta/bit_vector/support/popcount.hpp"
29#include "pasta/bit_vector/support/select.hpp"
30
31#include <cstddef>
32#include <cstdint>
33#if defined(__x86_64__)
34# include <emmintrin.h>
35# include <immintrin.h>
36#endif
37#include "pasta/utils/debug_asserts.hpp"
38
39#include <limits>
40#include <tlx/container/simple_vector.hpp>
41#include <vector>
42
43namespace pasta {
44
45//! \addtogroup pasta_bit_vector_rank_select
46//! \{
47
48/*!
49 * \brief Select support for \ref BitVector that can be used as an alternative
50 * to \ref RankSelect for bit vectors up to length 2^40
51 *
52 * The select support is an extended and engineered version of the popcount
53 * select support by Zhou et al. \cite ZhouAK2013PopcountRankSelect. Similar
54 * to the \ref FlatRank support, the highest utility array (L0) is
55 * removed. For more details see \ref FlatRank and \ref BigL12Type.
56 *
57 * \tparam use_intrinsic Set \c true if intrinsic functions should be used to
58 * find L2-block where the select query has to search the last 512 bits.
59 * Currently slower than simple loop.
60 *
61 * \tparam OptimizedFor Compile time option to optimize data structure for
62 * either 0, 1, or neither type of query. Default is \c neither.
63 * \tparam use_intrinsic \c bool flag that specifies whether intrinsics should
64 * be used during select queries (currently using them is slower). Default is
65 * \c false.
66 *
67 * \tparam VectorType Type of the vector the rank and select data structure is
68 * constructed for, e.g., plain \c BitVector or a compressed bit vector.
69 */
70template <OptimizedFor optimized_for = OptimizedFor::DONT_CARE,
71 FindL2FlatWith find_with = FindL2FlatWith::LINEAR_SEARCH,
72 typename VectorType = BitVector>
73class FlatRankSelect final : public FlatRank<optimized_for> {
74 //! Get access to protected members of base class, as dependent
75 //! names are not considered.
76 using FlatRank<optimized_for>::data_size_;
77 //! Get access to protected members of base class, as dependent
78 //! names are not considered.
79 using FlatRank<optimized_for>::data_;
80 //! Get access to protected members of base class, as dependent
81 //! names are not considered.
82 using FlatRank<optimized_for>::l12_;
83 //! Get access to protected members of base class, as dependent
84 //! names are not considered.
85 using FlatRank<optimized_for>::l12_end_;
86
87 template <typename T>
88 using Array = tlx::SimpleVector<T, tlx::SimpleVectorMode::NoInitNoDestroy>;
89
90 // Members for the structure (needed only for select)
91 //! Positions of every \c SELECT_SAMPLE_RATE zero.
92 std::vector<uint32_t> samples0_;
93 //! Positions of every \c SELECT_SAMPLE_RATE one.
94 std::vector<uint32_t> samples1_;
95
96public:
97 //! Default constructor w/o parameter.
98 FlatRankSelect() = default;
99 /*!
100 * \brief Constructor. Creates the auxiliary information for
101 * efficient rank and select queries.
102 *
103 * \param bv Vector of type \c VectorType the rank and select structure is
104 * created for.
105 */
106 FlatRankSelect(VectorType& bv) : FlatRank<optimized_for, VectorType>(bv) {
107 init();
108 }
109
110 //! Default move constructor.
112
113 //! Default move assignment.
115
116 //! Destructor. Deleting manually created arrays.
117 ~FlatRankSelect() = default;
118
119 /*!
120 * \brief Get position of specific zero, i.e., select.
121 * \param rank Rank of zero the position is searched for.
122 * \return Position of the rank-th zero.
123 */
124 [[nodiscard("select0 computed but not used")]] size_t
125 select0(size_t rank) const {
126 size_t const l12_end = l12_end_;
127
128 size_t const sample_pos =
130 size_t l1_pos = samples0_[sample_pos];
131 l1_pos += ((rank - 1) % FlatRankSelectConfig::SELECT_SAMPLE_RATE) /
133 if constexpr (optimize_one_or_dont_care(optimized_for)) {
134 while (l1_pos + 1 < l12_end &&
135 ((l1_pos + 1) * FlatRankSelectConfig::L1_BIT_SIZE) -
136 l12_[l1_pos + 1].l1() <
137 rank) {
138 ++l1_pos;
139 }
140 rank -= (l1_pos * FlatRankSelectConfig::L1_BIT_SIZE) - l12_[l1_pos].l1();
141 } else {
142 while (l1_pos + 1 < l12_end && l12_[l1_pos + 1].l1() < rank) {
143 ++l1_pos;
144 }
145 rank -= l12_[l1_pos].l1();
146 }
147 size_t l2_pos = 0;
148 if constexpr (use_intrinsics(find_with)) {
149#if defined(__x86_64__)
150 __m128i value =
151 _mm_loadu_si128(reinterpret_cast<__m128i const*>(&l12_[l1_pos]));
152 __m128i const shuffle_mask = _mm_setr_epi8(10,
153 11,
154 8,
155 9,
156 7,
157 8,
158 5,
159 6,
160 -1,
161 1,
162 14,
163 15,
164 13,
165 14,
166 11,
167 12);
168 value = _mm_shuffle_epi8(value, shuffle_mask);
169 // The values consisting of a complete upper byte and half a lower byte,
170 // which have to be shifted to the right to obtain the correct value.
171 __m128i const upper_values = _mm_srli_epi16(value, 4);
172 // Mask that covers the last 12 bits of a 16 bit word
173 __m128i const lower_mask = _mm_set1_epi16(uint16_t{0b0000111111111111});
174 // The values consisting of a half upper byte and a complete lower byte,
175 // where we have to mask the lower 12 bytes to obtain the correct value.
176 __m128i const lower_values = _mm_and_si128(value, lower_mask);
177 // Both [upper|lower]_values contain half of the values we want. We
178 // blend them together to obtain all required values in a 128 bit word.
179 value = _mm_blend_epi16(upper_values, lower_values, 0b01010101);
180
181 if constexpr (optimize_one_or_dont_care(optimized_for)) {
182 __m128i const max_ones =
183 _mm_setr_epi16(uint16_t{5 * FlatRankSelectConfig::L2_BIT_SIZE},
187 std::numeric_limits<int16_t>::max(), // Sentinel
191
192 value = _mm_sub_epi16(max_ones, value);
193 } else {
194 // To circumvent that the last value is a zero and thus the comparison
195 // fails in the next step, we add a maximum value to this. As
196 // intrinsics only consider signed integers, we have to add a signed
197 // 16 bit max!
198 value = _mm_insert_epi16(value, std::numeric_limits<int16_t>::max(), 4);
199 }
200
201 // We want to compare the L2-values with the remaining number of bits
202 // (rank) that are remaining
203 __m128i cmp_value;
204 PASTA_ASSERT(rank <= std::numeric_limits<uint16_t>::max(),
205 "Rank is too large. This should not occur because in this "
206 "block the number of previous bits should reduce the "
207 "local rank further.");
208 if constexpr (optimize_one_or_dont_care(optimized_for)) {
209 cmp_value = _mm_set1_epi16(rank);
210 } else {
211 cmp_value = _mm_set1_epi16(rank - 1);
212 }
213 // We now have a 128 bit word, where all consecutive 16 bit words are
214 // either 0 (if values is less equal) or 16_BIT_MAX (if values is
215 // greater than)
216 __m128i cmp_result = _mm_cmpgt_epi16(value, cmp_value);
217
218 // Obtain the most significant bit of each 8 bit word in the
219 // result of the comparison. Note that the 16 MSBs will be 0.
220 // Within the other 16 bits, we have 2 zero bits for each
221 // element that is less than the rank.
222 uint32_t const result = _mm_movemask_epi8(cmp_result);
223
224 // Compute the number of entries that are less than the rank
225 // based on the movemask-operation above.
226 l2_pos = (16 - std::popcount(result)) / 2;
227 if constexpr (optimize_one_or_dont_care(optimized_for)) {
228 rank -= ((l2_pos * FlatRankSelectConfig::L2_BIT_SIZE) -
229 l12_[l1_pos][l2_pos]);
230 } else {
231 rank -= l12_[l1_pos][l2_pos];
232 }
233#endif
234 } else if constexpr (use_linear_search(find_with)) {
235 auto tmp = l12_[l1_pos].data >> 32;
236 if constexpr (optimize_one_or_dont_care(optimized_for)) {
237 while ((l2_pos + 2) * FlatRankSelectConfig::L2_BIT_SIZE -
238 ((tmp >> 12) & uint16_t(0b111111111111)) <
239 rank &&
240 l2_pos < 7) {
241 tmp >>= 12;
242 ++l2_pos;
243 }
244 } else {
245 while (((tmp >> 12) & uint16_t(0b111111111111)) < rank && l2_pos < 7) {
246 tmp >>= 12;
247 ++l2_pos;
248 }
249 }
250 if constexpr (optimize_one_or_dont_care(optimized_for)) {
251 rank -= (l2_pos * FlatRankSelectConfig::L2_BIT_SIZE) -
252 (l12_[l1_pos][l2_pos]);
253 } else {
254 rank -= (l12_[l1_pos][l2_pos]);
255 }
256 } else if constexpr (use_binary_search(find_with)) {
257 if constexpr (optimize_one_or_dont_care(optimized_for)) {
258 auto tmp = l12_[l1_pos].data >> 44;
259 if (uint16_t const mid = (3 + 2) * FlatRankSelectConfig::L2_BIT_SIZE -
260 ((tmp >> 36) & uint16_t(0b111111111111));
261 mid < rank) {
262 if (uint16_t const right =
264 ((tmp >> 60) & uint16_t(0b111111111111));
265 right < rank) {
266 if (uint16_t const leaf =
268 ((tmp >> 72) & uint16_t(0b111111111111));
269 leaf < rank) {
270 l2_pos = 7;
271 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
272 } else {
273 l2_pos = 6;
274 rank -= (right - FlatRankSelectConfig::L2_BIT_SIZE);
275 }
276 } else {
277 if (uint16_t const leaf =
279 ((tmp >> 48) & uint16_t(0b111111111111));
280 leaf < rank) {
281 l2_pos = 5;
282 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
283 } else {
284 l2_pos = 4;
285 rank -= (mid - FlatRankSelectConfig::L2_BIT_SIZE);
286 }
287 }
288 } else {
289 if (uint16_t const left =
291 ((tmp >> 12) & uint16_t(0b111111111111));
292 left < rank) {
293 if (uint16_t const leaf =
295 ((tmp >> 24) & uint16_t(0b111111111111));
296 leaf < rank) {
297 l2_pos = 3;
298 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
299 } else {
300 l2_pos = 2;
301 rank -= (left - FlatRankSelectConfig::L2_BIT_SIZE);
302 }
303 } else {
304 if (uint16_t const leaf =
306 (tmp & uint16_t(0b111111111111));
307 leaf < rank) {
308 l2_pos = 1;
309 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
310 }
311 }
312 }
313 } else {
314 auto tmp = l12_[l1_pos].data >> 44;
315 if (uint16_t const mid = ((tmp >> 36) & uint16_t(0b111111111111));
316 mid < rank) {
317 if (uint16_t const right = ((tmp >> 60) & uint16_t(0b111111111111));
318 right < rank) {
319 if (uint16_t const leaf = ((tmp >> 72) & uint16_t(0b111111111111));
320 leaf < rank) {
321 l2_pos = 7;
322 rank -= leaf;
323 } else {
324 l2_pos = 6;
325 rank -= right;
326 }
327 } else {
328 if (uint16_t const leaf = ((tmp >> 48) & uint16_t(0b111111111111));
329 leaf < rank) {
330 l2_pos = 5;
331 rank -= leaf;
332 } else {
333 l2_pos = 4;
334 rank -= mid;
335 }
336 }
337 } else {
338 if (uint16_t const left = ((tmp >> 12) & uint16_t(0b111111111111));
339 left < rank) {
340 if (uint16_t const leaf = ((tmp >> 24) & uint16_t(0b111111111111));
341 leaf < rank) {
342 l2_pos = 3;
343 rank -= leaf;
344 } else {
345 l2_pos = 2;
346 rank -= left;
347 }
348 } else {
349 if (uint16_t const leaf = (tmp & uint16_t(0b111111111111));
350 leaf < rank) {
351 l2_pos = 1;
352 rank -= leaf;
353 }
354 }
355 }
356 }
357 } else {
358 static_assert(use_linear_search(find_with) ||
359 use_binary_search(find_with) ||
360 use_intrinsics(find_with),
361 "Using unsupported search method for l2 entries");
362 }
363
364 size_t last_pos = (FlatRankSelectConfig::L2_WORD_SIZE * l2_pos) +
366 size_t popcount = 0;
367
368 while ((popcount = pasta::popcount_zeros<1>(data_ + last_pos)) < rank) {
369 ++last_pos;
370 rank -= popcount;
371 }
372 return (last_pos * 64) + select(~data_[last_pos], rank - 1);
373 }
374
375 /*!
376 * \brief Get position of specific one, i.e., select.
377 * \param rank Rank of one the position is searched for.
378 * \return Position of the rank-th one.
379 */
380 [[nodiscard("select1 computed but not used")]] size_t
381 select1(size_t rank) const {
382 size_t const l12_end = l12_end_;
383
384 size_t const sample_pos =
386 size_t l1_pos = samples1_[sample_pos];
387 if constexpr (optimize_one_or_dont_care(optimized_for)) {
388 while ((l1_pos + 1) < l12_end && l12_[l1_pos + 1].l1() < rank) {
389 ++l1_pos;
390 }
391 rank -= l12_[l1_pos].l1();
392 } else {
393 while (l1_pos + 1 < l12_end &&
394 ((l1_pos + 1) * FlatRankSelectConfig::L1_BIT_SIZE) -
395 l12_[l1_pos + 1].l1() <
396 rank) {
397 ++l1_pos;
398 }
399 rank -= (l1_pos * FlatRankSelectConfig::L1_BIT_SIZE) - l12_[l1_pos].l1();
400 }
401 size_t l2_pos = 0;
402 if constexpr (use_intrinsics(find_with)) {
403#if defined(__x86_64__)
404 __m128i value =
405 _mm_loadu_si128(reinterpret_cast<__m128i const*>(&l12_[l1_pos]));
406 __m128i const shuffle_mask = _mm_setr_epi8(10,
407 11,
408 8,
409 9,
410 7,
411 8,
412 5,
413 6,
414 -1,
415 1,
416 14,
417 15,
418 13,
419 14,
420 11,
421 12);
422 value = _mm_shuffle_epi8(value, shuffle_mask);
423 // The values consisting of a complete upper byte and half a lower byte,
424 // which have to be shifted to the right to obtain the correct value.
425 __m128i const upper_values = _mm_srli_epi16(value, 4);
426 // Mask that covers the last 12 bits of a 16 bit word
427 __m128i const lower_mask = _mm_set1_epi16(uint16_t{0b0000111111111111});
428 // The values consisting of a half upper byte and a complete lower byte,
429 // where we have to mask the lower 12 bytes to obtain the correct value.
430 __m128i const lower_values = _mm_and_si128(value, lower_mask);
431 // Both [upper|lower]_values contain half of the values we want. We
432 // blend them together to obtain all required values in a 128 bit word.
433 value = _mm_blend_epi16(upper_values, lower_values, 0b01010101);
434
435 if constexpr (optimize_one_or_dont_care(optimized_for)) {
436 // To circumvent that the last value is a zero and thus the comparison
437 // fails in the next step, we add a maximum value to this. As
438 // intrinsics only consider signed integers, we have to add a signed
439 // 16 bit max!
440 value = _mm_insert_epi16(value, std::numeric_limits<int16_t>::max(), 4);
441 } else {
442 __m128i const max_ones =
443 _mm_setr_epi16(uint16_t{5 * FlatRankSelectConfig::L2_BIT_SIZE},
447 std::numeric_limits<int16_t>::max(), // Sentinel
451
452 value = _mm_sub_epi16(max_ones, value);
453 }
454
455 // We want to compare the L2-values with the remaining number of bits
456 // (rank) that are remaining
457 PASTA_ASSERT(rank <= std::numeric_limits<uint16_t>::max(),
458 "Rank is too large. This should not occur because in this "
459 "block the number of previous bits should reduce the "
460 "local rank further.");
461 __m128i const cmp_value = _mm_set1_epi16(rank - 1);
462 // We now have a 128 bit word, where all consecutive 16 bit words are
463 // either 0 (if values is less equal) or 16_BIT_MAX (if values is
464 // greater than)
465 __m128i cmp_result = _mm_cmpgt_epi16(value, cmp_value);
466
467 // Obtain the most significant bit of each 8 bit word in the
468 // result of the comparison. Note that the 16 MSBs will be 0.
469 // Within the other 16 bits, we have 2 zero bits for each
470 // element that is less than the rank.
471 uint32_t const result = _mm_movemask_epi8(cmp_result);
472
473 // Compute the number of entries that are less than the rank
474 // based on the movemask-operation above.
475 l2_pos = (16 - std::popcount(result)) / 2;
476 if constexpr (optimize_one_or_dont_care(optimized_for)) {
477 rank -= l12_[l1_pos][l2_pos];
478 } else {
479 rank -= ((l2_pos * FlatRankSelectConfig::L2_BIT_SIZE) -
480 l12_[l1_pos][l2_pos]);
481 }
482#endif
483 } else if constexpr (use_linear_search(find_with)) {
484 auto tmp = l12_[l1_pos].data >> 32;
485 if constexpr (optimize_one_or_dont_care(optimized_for)) {
486 while (((tmp >> 12) & uint16_t(0b111111111111)) < rank && l2_pos < 7) {
487 tmp >>= 12;
488 ++l2_pos;
489 }
490 rank -= (l12_[l1_pos][l2_pos]);
491 } else {
492 while ((l2_pos + 2) * FlatRankSelectConfig::L2_BIT_SIZE -
493 ((tmp >> 12) & uint16_t(0b111111111111)) <
494 rank &&
495 l2_pos < 7) {
496 tmp >>= 12;
497 ++l2_pos;
498 }
499 rank -= (l2_pos * FlatRankSelectConfig::L2_BIT_SIZE) -
500 (l12_[l1_pos][l2_pos]);
501 }
502 } else if constexpr (use_binary_search(find_with)) {
503 if constexpr (optimize_one_or_dont_care(optimized_for)) {
504 auto tmp = l12_[l1_pos].data >> 44;
505 if (uint16_t const mid = ((tmp >> 36) & uint16_t(0b111111111111));
506 mid < rank) {
507 if (uint16_t const right = ((tmp >> 60) & uint16_t(0b111111111111));
508 right < rank) {
509 if (uint16_t const leaf = ((tmp >> 72) & uint16_t(0b111111111111));
510 leaf < rank) {
511 l2_pos = 7;
512 rank -= leaf;
513 } else {
514 l2_pos = 6;
515 rank -= right;
516 }
517 } else {
518 if (uint16_t const leaf = ((tmp >> 48) & uint16_t(0b111111111111));
519 leaf < rank) {
520 l2_pos = 5;
521 rank -= leaf;
522 } else {
523 l2_pos = 4;
524 rank -= mid;
525 }
526 }
527 } else {
528 if (uint16_t const left = ((tmp >> 12) & uint16_t(0b111111111111));
529 left < rank) {
530 if (uint16_t const leaf = ((tmp >> 24) & uint16_t(0b111111111111));
531 leaf < rank) {
532 l2_pos = 3;
533 rank -= leaf;
534 } else {
535 l2_pos = 2;
536 rank -= left;
537 }
538 } else {
539 if (uint16_t const leaf = (tmp & uint16_t(0b111111111111));
540 leaf < rank) {
541 l2_pos = 1;
542 rank -= leaf;
543 }
544 }
545 }
546 } else {
547 auto tmp = l12_[l1_pos].data >> 44;
548 if (uint16_t const mid = (3 + 2) * FlatRankSelectConfig::L2_BIT_SIZE -
549 ((tmp >> 36) & uint16_t(0b111111111111));
550 mid < rank) {
551 if (uint16_t const right =
553 ((tmp >> 60) & uint16_t(0b111111111111));
554 right < rank) {
555 if (uint16_t const leaf =
557 ((tmp >> 72) & uint16_t(0b111111111111));
558 leaf < rank) {
559 l2_pos = 7;
560 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
561 } else {
562 l2_pos = 6;
563 rank -= (right - FlatRankSelectConfig::L2_BIT_SIZE);
564 }
565 } else {
566 if (uint16_t const leaf =
568 ((tmp >> 48) & uint16_t(0b111111111111));
569 leaf < rank) {
570 l2_pos = 5;
571 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
572 } else {
573 l2_pos = 4;
574 rank -= (mid - FlatRankSelectConfig::L2_BIT_SIZE);
575 }
576 }
577 } else {
578 if (uint16_t const left =
580 ((tmp >> 12) & uint16_t(0b111111111111));
581 left < rank) {
582 if (uint16_t const leaf =
584 ((tmp >> 24) & uint16_t(0b111111111111));
585 leaf < rank) {
586 l2_pos = 3;
587 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
588 } else {
589 l2_pos = 2;
590 rank -= (left - FlatRankSelectConfig::L2_BIT_SIZE);
591 }
592 } else {
593 if (uint16_t const leaf =
595 (tmp & uint16_t(0b111111111111));
596 leaf < rank) {
597 l2_pos = 1;
598 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
599 }
600 }
601 }
602 }
603 } else {
604 static_assert(use_linear_search(find_with) ||
605 use_binary_search(find_with) ||
606 use_intrinsics(find_with),
607 "Using unsupported search method for l2 entries");
608 }
609
610 size_t last_pos = (FlatRankSelectConfig::L2_WORD_SIZE * l2_pos) +
612 size_t popcount = 0;
613
614 while ((popcount = pasta::popcount<1>(data_ + last_pos)) < rank) {
615 ++last_pos;
616 rank -= popcount;
617 }
618 return (last_pos * 64) + select(data_[last_pos], rank - 1);
619 }
620
621 /*!
622 * \brief Estimate for the space usage.
623 * \return Number of bytes used by this data structure.
624 */
625 [[nodiscard("space usage computed but not used")]] size_t
626 space_usage() const final {
627 return samples0_.size() * sizeof(uint32_t) +
628 samples1_.size() * sizeof(uint32_t) + sizeof(*this);
629 }
630
631private:
632 //! Function used initializing data structure to reduce LOCs of constructor.
633 void init() {
634 size_t const l12_end = l12_.size();
635 size_t next_sample0_value = 1;
636 size_t next_sample1_value = 1;
637 for (size_t l12_pos = 0; l12_pos < l12_end; ++l12_pos) {
638 if constexpr (optimize_one_or_dont_care(optimized_for)) {
639 if ((l12_pos * FlatRankSelectConfig::L1_BIT_SIZE) -
640 l12_[l12_pos].l1() >=
641 next_sample0_value) {
642 samples0_.push_back(l12_pos - 1);
643 next_sample0_value += FlatRankSelectConfig::SELECT_SAMPLE_RATE;
644 }
645 if (l12_[l12_pos].l1() >= next_sample1_value) {
646 samples1_.push_back(l12_pos - 1);
647 next_sample1_value += FlatRankSelectConfig::SELECT_SAMPLE_RATE;
648 }
649 } else {
650 if (l12_[l12_pos].l1() >= next_sample0_value) {
651 samples0_.push_back(l12_pos - 1);
652 next_sample0_value += FlatRankSelectConfig::SELECT_SAMPLE_RATE;
653 }
654 if ((l12_pos * FlatRankSelectConfig::L1_BIT_SIZE) -
655 l12_[l12_pos].l1() >=
656 next_sample1_value) {
657 samples1_.push_back(l12_pos - 1);
658 next_sample1_value += FlatRankSelectConfig::SELECT_SAMPLE_RATE;
659 }
660 }
661 }
662 // Add at least one entry.
663 if (samples0_.size() == 0) [[unlikely]] {
664 samples0_.push_back(0);
665 } else {
666 samples0_.push_back(samples0_.back());
667 }
668 if (samples1_.size() == 0) [[unlikely]] {
669 samples1_.push_back(0);
670 } else {
671 samples1_.push_back(samples1_.back());
672 }
673 }
674}; // class FlatRankSelect
675
676//! \}
677
678} // namespace pasta
679
680/******************************************************************************/
Select support for BitVector that can be used as an alternative to RankSelect for bit vectors up to l...
Definition: flat_rank_select.hpp:73
size_t select0(size_t rank) const
Get position of specific zero, i.e., select.
Definition: flat_rank_select.hpp:125
FlatRankSelect(FlatRankSelect &&)=default
Default move constructor.
size_t space_usage() const final
Estimate for the space usage.
Definition: flat_rank_select.hpp:626
size_t select1(size_t rank) const
Get position of specific one, i.e., select.
Definition: flat_rank_select.hpp:381
FlatRankSelect(VectorType &bv)
Constructor. Creates the auxiliary information for efficient rank and select queries.
Definition: flat_rank_select.hpp:106
FlatRankSelect()=default
Default constructor w/o parameter.
~FlatRankSelect()=default
Destructor. Deleting manually created arrays.
FlatRankSelect & operator=(FlatRankSelect &&)=default
Default move assignment.
Rank support for BitVector that can be used as an alternative to Rank for bit vectors up to length 2^...
Definition: flat_rank.hpp:82
tlx::SimpleVector< BigL12Type, tlx::SimpleVectorMode::NoInitNoDestroy > l12_
Array containing the information about the L1- and L2-blocks.
Definition: flat_rank.hpp:94
size_t l12_end_
Number of actual existing BigL12-blocks (important for scanning)
Definition: flat_rank.hpp:96
size_t data_size_
Size of the bit vector the rank support is constructed for.
Definition: flat_rank.hpp:89
VectorType::RawDataConstAccess data_
Pointer to the data of the bit vector.
Definition: flat_rank.hpp:91
OptimizedFor
Enum used to specify which queries the rank (and select) data structures should be optimized for.
Definition: optimized_for.hpp:40
constexpr bool use_intrinsics(FindL2FlatWith const find_with)
Helper function indicating whether intrinsic function should be used.
Definition: find_l2_flat_with.hpp:70
constexpr bool use_linear_search(FindL2FlatWith const find_with)
Helper function indicating whether a linear search function should be used.
Definition: find_l2_flat_with.hpp:48
FindL2FlatWith
Enum used to specify whether intrinsic functions should be used.
Definition: find_l2_flat_with.hpp:35
constexpr bool optimize_one_or_dont_care(OptimizedFor const optimized_for)
Helper function indicating if queries should be optimized for one queries or the caller does not care...
Definition: optimized_for.hpp:59
constexpr bool use_binary_search(FindL2FlatWith const find_with)
Helper function indicating whether a binary search function should be used.
Definition: find_l2_flat_with.hpp:59
@ DONT_CARE
It does not matter (both types are called equally often).
static constexpr size_t L2_WORD_SIZE
Number of 64-bit words covered by an L2-block.
Definition: flat_rank.hpp:50
static constexpr size_t L1_BIT_SIZE
Bits covered by an L1-block.
Definition: flat_rank.hpp:44
static constexpr size_t L2_BIT_SIZE
Bits covered by an L2-block.
Definition: flat_rank.hpp:42
static constexpr size_t SELECT_SAMPLE_RATE
Sample rate of positions for faster select queries.
Definition: flat_rank.hpp:57
static constexpr size_t L1_WORD_SIZE
Number of 64-bit words covered by an L1-block.
Definition: flat_rank.hpp:52