pasta_bit_vector  1.0.1
Bit Vector with Compact and Fast Rank and Select Support
Loading...
Searching...
No Matches
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"
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, VectorType> {
74 //! Get access to protected members of base class, as dependent
75 //! names are not considered.
76 using FlatRank<optimized_for, VectorType>::data_size_;
77 //! Get access to protected members of base class, as dependent
78 //! names are not considered.
79 using FlatRank<optimized_for, VectorType>::data_;
80 //! Get access to protected members of base class, as dependent
81 //! names are not considered.
82 using FlatRank<optimized_for, VectorType>::l12_;
83 //! Get access to protected members of base class, as dependent
84 //! names are not considered.
85 using FlatRank<optimized_for, VectorType>::l12_end_;
86 //! Get access to protected members of base class, as dependent
87 //! names are not considered.
88 using FlatRank<optimized_for, VectorType>::data_access_;
89
90 template <typename T>
91 using Array = tlx::SimpleVector<T, tlx::SimpleVectorMode::NoInitNoDestroy>;
92
93 // Members for the structure (needed only for select)
94 //! Positions of every \c SELECT_SAMPLE_RATE zero.
95 std::vector<uint32_t> samples0_;
96 //! Positions of every \c SELECT_SAMPLE_RATE one.
97 std::vector<uint32_t> samples1_;
98
99public:
100 //! Default constructor w/o parameter.
101 FlatRankSelect() = default;
102 /*!
103 * \brief Constructor. Creates the auxiliary information for
104 * efficient rank and select queries.
105 *
106 * \param bv Vector of type \c VectorType the rank and select structure is
107 * created for.
108 */
109 FlatRankSelect(VectorType& bv) : FlatRank<optimized_for, VectorType>(bv) {
110 init();
111 }
112
113 //! Default move constructor.
115
116 //! Default move assignment.
118
119 //! Destructor. Deleting manually created arrays.
120 ~FlatRankSelect() = default;
121
122 /*!
123 * \brief Get position of specific zero, i.e., select.
124 * \param rank Rank of zero the position is searched for.
125 * \return Position of the rank-th zero.
126 */
127 [[nodiscard("select0 computed but not used")]] size_t
128 select0(size_t rank) const {
129 size_t const l12_end = l12_end_;
130
131 size_t const sample_pos =
133 size_t l1_pos = samples0_[sample_pos];
134 l1_pos += ((rank - 1) % FlatRankSelectConfig::SELECT_SAMPLE_RATE) /
136 if constexpr (optimize_one_or_dont_care(optimized_for)) {
137 while (l1_pos + 1 < l12_end &&
138 ((l1_pos + 1) * FlatRankSelectConfig::L1_BIT_SIZE) -
139 l12_[l1_pos + 1].l1() <
140 rank) {
141 ++l1_pos;
142 }
143 rank -= (l1_pos * FlatRankSelectConfig::L1_BIT_SIZE) - l12_[l1_pos].l1();
144 } else {
145 while (l1_pos + 1 < l12_end && l12_[l1_pos + 1].l1() < rank) {
146 ++l1_pos;
147 }
148 rank -= l12_[l1_pos].l1();
149 }
150 size_t l2_pos = 0;
151 if constexpr (use_intrinsics(find_with)) {
152#if defined(__x86_64__)
153 __m128i value =
154 _mm_loadu_si128(reinterpret_cast<__m128i const*>(&l12_[l1_pos]));
155 __m128i const shuffle_mask = _mm_setr_epi8(10,
156 11,
157 8,
158 9,
159 7,
160 8,
161 5,
162 6,
163 -1,
164 1,
165 14,
166 15,
167 13,
168 14,
169 11,
170 12);
171 value = _mm_shuffle_epi8(value, shuffle_mask);
172 // The values consisting of a complete upper byte and half a lower byte,
173 // which have to be shifted to the right to obtain the correct value.
174 __m128i const upper_values = _mm_srli_epi16(value, 4);
175 // Mask that covers the last 12 bits of a 16 bit word
176 __m128i const lower_mask = _mm_set1_epi16(uint16_t{0b0000111111111111});
177 // The values consisting of a half upper byte and a complete lower byte,
178 // where we have to mask the lower 12 bytes to obtain the correct value.
179 __m128i const lower_values = _mm_and_si128(value, lower_mask);
180 // Both [upper|lower]_values contain half of the values we want. We
181 // blend them together to obtain all required values in a 128 bit word.
182 value = _mm_blend_epi16(upper_values, lower_values, 0b01010101);
183
184 if constexpr (optimize_one_or_dont_care(optimized_for)) {
185 __m128i const max_ones =
186 _mm_setr_epi16(uint16_t{5 * FlatRankSelectConfig::L2_BIT_SIZE},
190 std::numeric_limits<int16_t>::max(), // Sentinel
194
195 value = _mm_sub_epi16(max_ones, value);
196 } else {
197 // To circumvent that the last value is a zero and thus the comparison
198 // fails in the next step, we add a maximum value to this. As
199 // intrinsics only consider signed integers, we have to add a signed
200 // 16 bit max!
201 value = _mm_insert_epi16(value, std::numeric_limits<int16_t>::max(), 4);
202 }
203
204 // We want to compare the L2-values with the remaining number of bits
205 // (rank) that are remaining
206 __m128i cmp_value;
207 PASTA_ASSERT(rank <= std::numeric_limits<uint16_t>::max(),
208 "Rank is too large. This should not occur because in this "
209 "block the number of previous bits should reduce the "
210 "local rank further.");
211 if constexpr (optimize_one_or_dont_care(optimized_for)) {
212 cmp_value = _mm_set1_epi16(rank);
213 } else {
214 cmp_value = _mm_set1_epi16(rank - 1);
215 }
216 // We now have a 128 bit word, where all consecutive 16 bit words are
217 // either 0 (if values is less equal) or 16_BIT_MAX (if values is
218 // greater than)
219 __m128i cmp_result = _mm_cmpgt_epi16(value, cmp_value);
220
221 // Obtain the most significant bit of each 8 bit word in the
222 // result of the comparison. Note that the 16 MSBs will be 0.
223 // Within the other 16 bits, we have 2 zero bits for each
224 // element that is less than the rank.
225 uint32_t const result = _mm_movemask_epi8(cmp_result);
226
227 // Compute the number of entries that are less than the rank
228 // based on the movemask-operation above.
229 l2_pos = (16 - std::popcount(result)) / 2;
230 if constexpr (optimize_one_or_dont_care(optimized_for)) {
231 rank -= ((l2_pos * FlatRankSelectConfig::L2_BIT_SIZE) -
232 l12_[l1_pos][l2_pos]);
233 } else {
234 rank -= l12_[l1_pos][l2_pos];
235 }
236#endif
237 } else if constexpr (use_linear_search(find_with)) {
238 auto tmp = l12_[l1_pos].data >> 32;
239 if constexpr (optimize_one_or_dont_care(optimized_for)) {
240 while ((l2_pos + 2) * FlatRankSelectConfig::L2_BIT_SIZE -
241 ((tmp >> 12) & uint16_t(0b111111111111)) <
242 rank &&
243 l2_pos < 7) {
244 tmp >>= 12;
245 ++l2_pos;
246 }
247 } else {
248 while (((tmp >> 12) & uint16_t(0b111111111111)) < rank && l2_pos < 7) {
249 tmp >>= 12;
250 ++l2_pos;
251 }
252 }
253 if constexpr (optimize_one_or_dont_care(optimized_for)) {
254 rank -= (l2_pos * FlatRankSelectConfig::L2_BIT_SIZE) -
255 (l12_[l1_pos][l2_pos]);
256 } else {
257 rank -= (l12_[l1_pos][l2_pos]);
258 }
259 } else if constexpr (use_binary_search(find_with)) {
260 if constexpr (optimize_one_or_dont_care(optimized_for)) {
261 auto tmp = l12_[l1_pos].data >> 44;
262 if (uint16_t const mid = (3 + 2) * FlatRankSelectConfig::L2_BIT_SIZE -
263 ((tmp >> 36) & uint16_t(0b111111111111));
264 mid < rank) {
265 if (uint16_t const right =
267 ((tmp >> 60) & uint16_t(0b111111111111));
268 right < rank) {
269 if (uint16_t const leaf =
271 ((tmp >> 72) & uint16_t(0b111111111111));
272 leaf < rank) {
273 l2_pos = 7;
274 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
275 } else {
276 l2_pos = 6;
277 rank -= (right - FlatRankSelectConfig::L2_BIT_SIZE);
278 }
279 } else {
280 if (uint16_t const leaf =
282 ((tmp >> 48) & uint16_t(0b111111111111));
283 leaf < rank) {
284 l2_pos = 5;
285 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
286 } else {
287 l2_pos = 4;
288 rank -= (mid - FlatRankSelectConfig::L2_BIT_SIZE);
289 }
290 }
291 } else {
292 if (uint16_t const left =
294 ((tmp >> 12) & uint16_t(0b111111111111));
295 left < rank) {
296 if (uint16_t const leaf =
298 ((tmp >> 24) & uint16_t(0b111111111111));
299 leaf < rank) {
300 l2_pos = 3;
301 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
302 } else {
303 l2_pos = 2;
304 rank -= (left - FlatRankSelectConfig::L2_BIT_SIZE);
305 }
306 } else {
307 if (uint16_t const leaf =
309 (tmp & uint16_t(0b111111111111));
310 leaf < rank) {
311 l2_pos = 1;
312 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
313 }
314 }
315 }
316 } else {
317 auto tmp = l12_[l1_pos].data >> 44;
318 if (uint16_t const mid = ((tmp >> 36) & uint16_t(0b111111111111));
319 mid < rank) {
320 if (uint16_t const right = ((tmp >> 60) & uint16_t(0b111111111111));
321 right < rank) {
322 if (uint16_t const leaf = ((tmp >> 72) & uint16_t(0b111111111111));
323 leaf < rank) {
324 l2_pos = 7;
325 rank -= leaf;
326 } else {
327 l2_pos = 6;
328 rank -= right;
329 }
330 } else {
331 if (uint16_t const leaf = ((tmp >> 48) & uint16_t(0b111111111111));
332 leaf < rank) {
333 l2_pos = 5;
334 rank -= leaf;
335 } else {
336 l2_pos = 4;
337 rank -= mid;
338 }
339 }
340 } else {
341 if (uint16_t const left = ((tmp >> 12) & uint16_t(0b111111111111));
342 left < rank) {
343 if (uint16_t const leaf = ((tmp >> 24) & uint16_t(0b111111111111));
344 leaf < rank) {
345 l2_pos = 3;
346 rank -= leaf;
347 } else {
348 l2_pos = 2;
349 rank -= left;
350 }
351 } else {
352 if (uint16_t const leaf = (tmp & uint16_t(0b111111111111));
353 leaf < rank) {
354 l2_pos = 1;
355 rank -= leaf;
356 }
357 }
358 }
359 }
360 } else {
361 static_assert(use_linear_search(find_with) ||
362 use_binary_search(find_with) ||
363 use_intrinsics(find_with),
364 "Using unsupported search method for l2 entries");
365 }
366
367 size_t last_pos = (FlatRankSelectConfig::L2_WORD_SIZE * l2_pos) +
369 size_t popcount = 0;
370
371 while ((popcount = std::popcount(~data_access_[last_pos])) < rank) {
372 ++last_pos;
373 rank -= popcount;
374 }
375 return (last_pos * 64) + select(~data_access_[last_pos], rank - 1);
376 }
377
378 /*!
379 * \brief Get position of specific one, i.e., select.
380 * \param rank Rank of one the position is searched for.
381 * \return Position of the rank-th one.
382 */
383 [[nodiscard("select1 computed but not used")]] size_t
384 select1(size_t rank) const {
385 size_t const l12_end = l12_end_;
386
387 size_t const sample_pos =
389 size_t l1_pos = samples1_[sample_pos];
390 if constexpr (optimize_one_or_dont_care(optimized_for)) {
391 while ((l1_pos + 1) < l12_end && l12_[l1_pos + 1].l1() < rank) {
392 ++l1_pos;
393 }
394 rank -= l12_[l1_pos].l1();
395 } else {
396 while (l1_pos + 1 < l12_end &&
397 ((l1_pos + 1) * FlatRankSelectConfig::L1_BIT_SIZE) -
398 l12_[l1_pos + 1].l1() <
399 rank) {
400 ++l1_pos;
401 }
402 rank -= (l1_pos * FlatRankSelectConfig::L1_BIT_SIZE) - l12_[l1_pos].l1();
403 }
404 size_t l2_pos = 0;
405 if constexpr (use_intrinsics(find_with)) {
406#if defined(__x86_64__)
407 __m128i value =
408 _mm_loadu_si128(reinterpret_cast<__m128i const*>(&l12_[l1_pos]));
409 __m128i const shuffle_mask = _mm_setr_epi8(10,
410 11,
411 8,
412 9,
413 7,
414 8,
415 5,
416 6,
417 -1,
418 1,
419 14,
420 15,
421 13,
422 14,
423 11,
424 12);
425 value = _mm_shuffle_epi8(value, shuffle_mask);
426 // The values consisting of a complete upper byte and half a lower byte,
427 // which have to be shifted to the right to obtain the correct value.
428 __m128i const upper_values = _mm_srli_epi16(value, 4);
429 // Mask that covers the last 12 bits of a 16 bit word
430 __m128i const lower_mask = _mm_set1_epi16(uint16_t{0b0000111111111111});
431 // The values consisting of a half upper byte and a complete lower byte,
432 // where we have to mask the lower 12 bytes to obtain the correct value.
433 __m128i const lower_values = _mm_and_si128(value, lower_mask);
434 // Both [upper|lower]_values contain half of the values we want. We
435 // blend them together to obtain all required values in a 128 bit word.
436 value = _mm_blend_epi16(upper_values, lower_values, 0b01010101);
437
438 if constexpr (optimize_one_or_dont_care(optimized_for)) {
439 // To circumvent that the last value is a zero and thus the comparison
440 // fails in the next step, we add a maximum value to this. As
441 // intrinsics only consider signed integers, we have to add a signed
442 // 16 bit max!
443 value = _mm_insert_epi16(value, std::numeric_limits<int16_t>::max(), 4);
444 } else {
445 __m128i const max_ones =
446 _mm_setr_epi16(uint16_t{5 * FlatRankSelectConfig::L2_BIT_SIZE},
450 std::numeric_limits<int16_t>::max(), // Sentinel
454
455 value = _mm_sub_epi16(max_ones, value);
456 }
457
458 // We want to compare the L2-values with the remaining number of bits
459 // (rank) that are remaining
460 PASTA_ASSERT(rank <= std::numeric_limits<uint16_t>::max(),
461 "Rank is too large. This should not occur because in this "
462 "block the number of previous bits should reduce the "
463 "local rank further.");
464 __m128i const cmp_value = _mm_set1_epi16(rank - 1);
465 // We now have a 128 bit word, where all consecutive 16 bit words are
466 // either 0 (if values is less equal) or 16_BIT_MAX (if values is
467 // greater than)
468 __m128i cmp_result = _mm_cmpgt_epi16(value, cmp_value);
469
470 // Obtain the most significant bit of each 8 bit word in the
471 // result of the comparison. Note that the 16 MSBs will be 0.
472 // Within the other 16 bits, we have 2 zero bits for each
473 // element that is less than the rank.
474 uint32_t const result = _mm_movemask_epi8(cmp_result);
475
476 // Compute the number of entries that are less than the rank
477 // based on the movemask-operation above.
478 l2_pos = (16 - std::popcount(result)) / 2;
479 if constexpr (optimize_one_or_dont_care(optimized_for)) {
480 rank -= l12_[l1_pos][l2_pos];
481 } else {
482 rank -= ((l2_pos * FlatRankSelectConfig::L2_BIT_SIZE) -
483 l12_[l1_pos][l2_pos]);
484 }
485#endif
486 } else if constexpr (use_linear_search(find_with)) {
487 auto tmp = l12_[l1_pos].data >> 32;
488 if constexpr (optimize_one_or_dont_care(optimized_for)) {
489 while (((tmp >> 12) & uint16_t(0b111111111111)) < rank && l2_pos < 7) {
490 tmp >>= 12;
491 ++l2_pos;
492 }
493 rank -= (l12_[l1_pos][l2_pos]);
494 } else {
495 while ((l2_pos + 2) * FlatRankSelectConfig::L2_BIT_SIZE -
496 ((tmp >> 12) & uint16_t(0b111111111111)) <
497 rank &&
498 l2_pos < 7) {
499 tmp >>= 12;
500 ++l2_pos;
501 }
502 rank -= (l2_pos * FlatRankSelectConfig::L2_BIT_SIZE) -
503 (l12_[l1_pos][l2_pos]);
504 }
505 } else if constexpr (use_binary_search(find_with)) {
506 if constexpr (optimize_one_or_dont_care(optimized_for)) {
507 auto tmp = l12_[l1_pos].data >> 44;
508 if (uint16_t const mid = ((tmp >> 36) & uint16_t(0b111111111111));
509 mid < rank) {
510 if (uint16_t const right = ((tmp >> 60) & uint16_t(0b111111111111));
511 right < rank) {
512 if (uint16_t const leaf = ((tmp >> 72) & uint16_t(0b111111111111));
513 leaf < rank) {
514 l2_pos = 7;
515 rank -= leaf;
516 } else {
517 l2_pos = 6;
518 rank -= right;
519 }
520 } else {
521 if (uint16_t const leaf = ((tmp >> 48) & uint16_t(0b111111111111));
522 leaf < rank) {
523 l2_pos = 5;
524 rank -= leaf;
525 } else {
526 l2_pos = 4;
527 rank -= mid;
528 }
529 }
530 } else {
531 if (uint16_t const left = ((tmp >> 12) & uint16_t(0b111111111111));
532 left < rank) {
533 if (uint16_t const leaf = ((tmp >> 24) & uint16_t(0b111111111111));
534 leaf < rank) {
535 l2_pos = 3;
536 rank -= leaf;
537 } else {
538 l2_pos = 2;
539 rank -= left;
540 }
541 } else {
542 if (uint16_t const leaf = (tmp & uint16_t(0b111111111111));
543 leaf < rank) {
544 l2_pos = 1;
545 rank -= leaf;
546 }
547 }
548 }
549 } else {
550 auto tmp = l12_[l1_pos].data >> 44;
551 if (uint16_t const mid = (3 + 2) * FlatRankSelectConfig::L2_BIT_SIZE -
552 ((tmp >> 36) & uint16_t(0b111111111111));
553 mid < rank) {
554 if (uint16_t const right =
556 ((tmp >> 60) & uint16_t(0b111111111111));
557 right < rank) {
558 if (uint16_t const leaf =
560 ((tmp >> 72) & uint16_t(0b111111111111));
561 leaf < rank) {
562 l2_pos = 7;
563 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
564 } else {
565 l2_pos = 6;
566 rank -= (right - FlatRankSelectConfig::L2_BIT_SIZE);
567 }
568 } else {
569 if (uint16_t const leaf =
571 ((tmp >> 48) & uint16_t(0b111111111111));
572 leaf < rank) {
573 l2_pos = 5;
574 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
575 } else {
576 l2_pos = 4;
577 rank -= (mid - FlatRankSelectConfig::L2_BIT_SIZE);
578 }
579 }
580 } else {
581 if (uint16_t const left =
583 ((tmp >> 12) & uint16_t(0b111111111111));
584 left < rank) {
585 if (uint16_t const leaf =
587 ((tmp >> 24) & uint16_t(0b111111111111));
588 leaf < rank) {
589 l2_pos = 3;
590 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
591 } else {
592 l2_pos = 2;
593 rank -= (left - FlatRankSelectConfig::L2_BIT_SIZE);
594 }
595 } else {
596 if (uint16_t const leaf =
598 (tmp & uint16_t(0b111111111111));
599 leaf < rank) {
600 l2_pos = 1;
601 rank -= (leaf - FlatRankSelectConfig::L2_BIT_SIZE);
602 }
603 }
604 }
605 }
606 } else {
607 static_assert(use_linear_search(find_with) ||
608 use_binary_search(find_with) ||
609 use_intrinsics(find_with),
610 "Using unsupported search method for l2 entries");
611 }
612
613 size_t last_pos = (FlatRankSelectConfig::L2_WORD_SIZE * l2_pos) +
615 size_t popcount = 0;
616
617 while ((popcount = std::popcount(data_access_[last_pos])) < rank) {
618 ++last_pos;
619 rank -= popcount;
620 }
621 return (last_pos * 64) + select(data_access_[last_pos], rank - 1);
622 }
623
624 /*!
625 * \brief Estimate for the space usage.
626 * \return Number of bytes used by this data structure.
627 */
628 [[nodiscard("space usage computed but not used")]] size_t
629 space_usage() const final {
630 return samples0_.size() * sizeof(uint32_t) +
631 samples1_.size() * sizeof(uint32_t) + sizeof(*this);
632 }
633
634private:
635 //! Function used initializing data structure to reduce LOCs of constructor.
636 void init() {
637 size_t const l12_end = l12_.size();
638 size_t next_sample0_value = 1;
639 size_t next_sample1_value = 1;
640 for (size_t l12_pos = 0; l12_pos < l12_end; ++l12_pos) {
641 if constexpr (optimize_one_or_dont_care(optimized_for)) {
642 if ((l12_pos * FlatRankSelectConfig::L1_BIT_SIZE) -
643 l12_[l12_pos].l1() >=
644 next_sample0_value) {
645 samples0_.push_back(l12_pos - 1);
646 next_sample0_value += FlatRankSelectConfig::SELECT_SAMPLE_RATE;
647 }
648 if (l12_[l12_pos].l1() >= next_sample1_value) {
649 samples1_.push_back(l12_pos - 1);
650 next_sample1_value += FlatRankSelectConfig::SELECT_SAMPLE_RATE;
651 }
652 } else {
653 if (l12_[l12_pos].l1() >= next_sample0_value) {
654 samples0_.push_back(l12_pos - 1);
655 next_sample0_value += FlatRankSelectConfig::SELECT_SAMPLE_RATE;
656 }
657 if ((l12_pos * FlatRankSelectConfig::L1_BIT_SIZE) -
658 l12_[l12_pos].l1() >=
659 next_sample1_value) {
660 samples1_.push_back(l12_pos - 1);
661 next_sample1_value += FlatRankSelectConfig::SELECT_SAMPLE_RATE;
662 }
663 }
664 }
665 // Add at least one entry.
666 if (samples0_.size() == 0) [[unlikely]] {
667 samples0_.push_back(0);
668 } else {
669 samples0_.push_back(samples0_.back());
670 }
671 if (samples1_.size() == 0) [[unlikely]] {
672 samples1_.push_back(0);
673 } else {
674 samples1_.push_back(samples1_.back());
675 }
676 }
677}; // class FlatRankSelect
678
679//! \}
680
681} // namespace pasta
682
683/******************************************************************************/
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:128
FlatRankSelect(FlatRankSelect &&)=default
Default move constructor.
size_t space_usage() const final
Estimate for the space usage.
Definition flat_rank_select.hpp:629
size_t select1(size_t rank) const
Get position of specific one, i.e., select.
Definition flat_rank_select.hpp:384
FlatRankSelect(VectorType &bv)
Constructor. Creates the auxiliary information for efficient rank and select queries.
Definition flat_rank_select.hpp:109
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_
Definition flat_rank.hpp:97
BitVector::RawDataConstAccess data_access_
Definition flat_rank.hpp:95
size_t l12_end_
Definition flat_rank.hpp:99
uint64_t const * data_
Definition flat_rank.hpp:92
size_t data_size_
Definition flat_rank.hpp:89
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).
uint64_t popcount(uint64_t const *const buffer)
Compute popcount of a specific number of 64-bit words.
Definition popcount.hpp:41
uint64_t select(uint64_t x, uint64_t k)
Select set bit in 64-bit word and return its position starting from the LSB (starting from the left).
Definition select.hpp:144
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