69 template <
class,
class,
class, std::
size_t,
bool>
70 friend class ::pixie::rmq::CartesianHybridBTree;
73 static_assert(std::is_unsigned_v<Index>,
74 "HybridBTreePlusMinusOne index type must be unsigned");
75 static_assert(LeafSize != 0 && LeafSize % 512 == 0,
76 "HybridBTreePlusMinusOne leaf size must be a positive "
78 static_assert(!UseHighSparseLayout || HighSparseLayoutLevels > 0,
79 "HybridBTreePlusMinusOne high sparse layout must cover "
80 "at least one level when enabled");
82 static constexpr std::size_t npos = std::numeric_limits<std::size_t>::max();
83 static constexpr Index invalid_index = std::numeric_limits<Index>::max();
84 static constexpr std::size_t kLeafSize = LeafSize;
85 static constexpr std::size_t kHighLevelFanout = 256;
86 static constexpr std::size_t kMiddleFanout = 192;
87 static constexpr bool kUseHighSparseLayout = UseHighSparseLayout;
88 static constexpr std::size_t kHighSparseLayoutLevels = HighSparseLayoutLevels;
104 std::size_t depth_count) {
105 build(bits, depth_count);
116 std::size_t depth_count,
118 build(bits, depth_count, rank_index);
124 void build(std::span<const std::uint64_t> bits, std::size_t depth_count) {
126 depth_count_ = depth_count;
127 external_rank_index_ =
nullptr;
134 void build(std::span<const std::uint64_t> bits,
135 std::size_t depth_count,
138 depth_count_ = depth_count;
139 external_rank_index_ = &rank_index;
146 std::size_t
size()
const {
return depth_count_; }
151 bool empty()
const {
return depth_count_ == 0; }
160 std::size_t bytes =
sizeof(*this);
161 if (external_rank_index_ ==
nullptr) {
162 bytes += pixie::optional_nested_owned_memory_bytes(owned_rank_index_);
164 bytes += pixie::vector_capacity_bytes(internal_selectors_);
165 bytes += pixie::vector_capacity_bytes(internal_min_positions_);
166 bytes += pixie::vector_capacity_bytes(internal_min_depths_);
167 bytes += pixie::vector_capacity_bytes(high_child_metadata_);
168 bytes += pixie::vector_capacity_bytes(high_sparse_min_slots_);
169 bytes += pixie::vector_capacity_bytes(internal_level_offsets_);
170 bytes += pixie::vector_capacity_bytes(min_summary_level_offsets_);
171 bytes += pixie::vector_capacity_bytes(high_level_offsets_);
172 bytes += pixie::vector_capacity_bytes(level_sizes_);
173 bytes += pixie::vector_capacity_bytes(level_position_spans_);
174 bytes += pixie::vector_capacity_bytes(level_fanouts_);
184 std::size_t
arg_min(std::size_t left, std::size_t right)
const {
185 if (left >= right || right > depth_count_ || level_sizes_.empty()) {
188 if (left + 1 == right) {
192 const std::size_t root_level = level_count() - 1;
193 if (left == 0 && right == depth_count_) {
194 return subtree_min_candidate(root_level, 0).position;
197 const std::size_t left_leaf = leaf_for_position(left);
198 const std::size_t right_leaf = leaf_for_position(right - 1);
199 if (left_leaf == right_leaf) {
200 return leaf_range_arg_min_relative(left_leaf, left, right);
203 const auto [level, node] = covering_node(left_leaf, right_leaf);
204 return query_node(level, node, left, right).position;
216 if (rank == 0 || depth_count_ == 0 || rank_index_or_null() ==
nullptr) {
219 const std::size_t delta_count = depth_count_ - 1;
220 const std::size_t position = rank_index().select0(rank);
221 return position < delta_count ? position : npos;
225 static constexpr std::size_t kLeafWords = LeafSize / 64;
226 static constexpr std::size_t kLeafChunks = LeafSize / 128;
227 static constexpr std::size_t kSelectorEntries = 256;
228 static constexpr std::size_t kSelectorBits = 2 * kSelectorEntries;
229 static constexpr std::size_t kSelectorWords = kSelectorBits / 64;
230 static constexpr std::size_t kEmbeddedSummaryWords = 2;
231 static constexpr std::size_t kEmbeddedSummaryBits =
232 64 * kEmbeddedSummaryWords;
233 static constexpr std::size_t kEmbeddedSummaryMaxEntries =
234 (kSelectorBits - kEmbeddedSummaryBits) / 2;
235 static constexpr std::size_t kEmbeddedSummaryPositionWord =
237 static constexpr std::size_t kEmbeddedSummaryDepthWord = kSelectorWords - 1;
238 static constexpr std::size_t kHighSparseTableLevels =
239 static_cast<std::size_t
>(std::bit_width(kHighLevelFanout));
240 static constexpr std::size_t kHighSparseSlotsPerNode =
241 kHighSparseTableLevels * kHighLevelFanout;
242 static constexpr bool kInvalidIndexEqualsNpos =
243 static_cast<std::size_t
>(invalid_index) == npos;
244 static_assert(kEmbeddedSummaryMaxEntries == 192);
245 static_assert(kMiddleFanout == kEmbeddedSummaryMaxEntries);
246 static_assert(
sizeof(std::size_t) <=
sizeof(std::uint64_t));
248 struct DepthCandidate {
249 std::size_t position = npos;
250 std::int64_t depth = std::numeric_limits<std::int64_t>::max();
253 struct HighChildMetadata {
254 std::size_t position_begin = 0;
255 std::size_t position_end = 0;
256 Index min_position = invalid_index;
257 std::int64_t min_depth = std::numeric_limits<std::int64_t>::max();
260 class alignas(64) Bp512Selector {
265 Bp512Selector() =
default;
274 template <
class EntryLess>
275 void build(std::size_t entry_count, EntryLess entry_less) {
276 if (entry_count > kSelectorEntries) {
277 throw std::length_error(
278 "HybridBTreePlusMinusOne local selector too large");
282 if (entry_count == 0) {
286 std::array<std::uint16_t, kSelectorEntries> stack{};
287 std::size_t stack_size = 0;
288 std::size_t write_position = 2 * entry_count;
290 for (std::size_t i = entry_count; i-- > 0;) {
291 while (stack_size != 0 && !entry_less(stack[stack_size - 1], i)) {
293 prepend_bp_bit(write_position,
true);
295 stack[stack_size++] =
static_cast<std::uint16_t
>(i);
296 prepend_bp_bit(write_position,
false);
299 while (write_position != 0) {
300 prepend_bp_bit(write_position,
true);
307 std::size_t arg_min(std::size_t slot_left,
308 std::size_t slot_right,
309 std::size_t entry_count)
const {
310 if (slot_left >= slot_right || slot_right > entry_count ||
311 entry_count > kSelectorEntries) {
314 if (slot_left + 1 == slot_right) {
318 const std::size_t bit_count = 2 * entry_count;
319 const std::size_t first_close = close_position(slot_left);
320 const std::size_t last_close = close_position(slot_right - 1);
321 if (first_close > last_close || last_close >= bit_count) {
325 const std::size_t shifted_min =
326 depth_arg_min(first_close + 1, last_close + 2, bit_count);
327 if (shifted_min == npos || shifted_min == 0) {
331 const std::size_t zero_rank = rank0_at(shifted_min, bit_count);
332 if (zero_rank == 0) {
335 const std::size_t entry = zero_rank - 1;
336 return entry < entry_count ? entry : npos;
347 void set_embedded_min_summary(std::size_t position, std::int64_t depth) {
348 bp_bits_[kEmbeddedSummaryPositionWord] =
349 static_cast<std::uint64_t
>(position);
350 bp_bits_[kEmbeddedSummaryDepthWord] = std::bit_cast<std::uint64_t>(depth);
356 std::size_t embedded_min_position()
const {
357 return static_cast<std::size_t
>(bp_bits_[kEmbeddedSummaryPositionWord]);
363 std::int64_t embedded_min_depth()
const {
364 return std::bit_cast<std::int64_t>(bp_bits_[kEmbeddedSummaryDepthWord]);
368 friend class HybridBTreePlusMinusOne;
373 void prepend_bp_bit(std::size_t& write_position,
bool bit) {
376 bp_bits_[write_position >> 6] |= std::uint64_t{1}
377 << (write_position & 63);
384 std::size_t close_position(std::size_t slot)
const {
385 return select0_512(bp_bits_.data(), slot);
391 std::size_t rank0_at(std::size_t position, std::size_t bit_count)
const {
392 position = std::min(position, bit_count);
393 return position - rank_512(bp_bits_.data(), position);
399 int prefix_excess(std::size_t position)
const {
400 position = std::min(position, kSelectorBits);
401 const std::size_t ones = rank_512(bp_bits_.data(), position);
402 return static_cast<int>(2 * ones) -
static_cast<int>(position);
408 std::size_t depth_arg_min(std::size_t left,
410 std::size_t bit_count)
const {
411 const std::size_t depth_count = bit_count + 1;
412 if (left >= right || right > depth_count) {
416 std::size_t position = left;
417 int best_depth = prefix_excess(position);
418 std::size_t best_position = position;
420 while (position < right) {
421 const std::size_t chunk_begin = (position / 128) * 128;
422 const std::size_t local_left = position - chunk_begin;
423 const std::size_t local_right =
424 std::min<std::size_t>(right - 1, chunk_begin + 128) - chunk_begin;
427 std::size_t candidate_position;
428 if (chunk_begin >= bit_count) {
429 candidate_depth = prefix_excess(bit_count);
430 candidate_position = bit_count;
432 const std::size_t word = chunk_begin >> 6;
433 const ExcessResult result =
434 excess_min_128(bp_bits_.data() + word, local_left, local_right);
435 candidate_depth = prefix_excess(chunk_begin) + result.min_excess;
436 candidate_position = chunk_begin + result.offset;
439 if (candidate_depth < best_depth) {
440 best_depth = candidate_depth;
441 best_position = candidate_position;
444 position = chunk_begin + local_right + 1;
447 return best_position;
450 std::array<std::uint64_t, kSelectorWords> bp_bits_{};
453 static_assert(
sizeof(Bp512Selector) == 64);
455 void serialize_metadata(BinaryWriter& writer)
const {
456 if (depth_count_ != 0 &&
457 (owned_rank_index_.has_value() || external_rank_index_ ==
nullptr)) {
458 throw std::invalid_argument(
459 "Depth RMQ serialization requires external rank support");
461 if (depth_count_ != 0) {
462 validate_serialized_state(*external_rank_index_,
463 DeserializationValidation::kQuick);
465 validate_serialized_state(RankSelectSupport<>(),
466 DeserializationValidation::kQuick);
469 writer.write_size(depth_count_);
470 writer.write_size(internal_selectors_.size());
471 for (
const Bp512Selector& selector : internal_selectors_) {
472 for (
const std::uint64_t word : selector.bp_bits_) {
473 writer.write_u64(word);
476 pixie::detail::write_vector(
477 writer, std::span<const Index>(internal_min_positions_));
478 pixie::detail::write_vector(
479 writer, std::span<const std::int64_t>(internal_min_depths_));
481 writer.write_size(high_child_metadata_.size());
482 for (
const HighChildMetadata& metadata : high_child_metadata_) {
483 writer.write_size(metadata.position_begin);
484 writer.write_size(metadata.position_end);
485 pixie::detail::write_integral(writer, metadata.min_position);
486 writer.write_i64(metadata.min_depth);
488 pixie::detail::write_vector(
489 writer, std::span<const std::uint8_t>(high_sparse_min_slots_));
490 pixie::detail::write_vector(
491 writer, std::span<const std::size_t>(internal_level_offsets_));
492 pixie::detail::write_vector(
493 writer, std::span<const std::size_t>(min_summary_level_offsets_));
494 pixie::detail::write_vector(
495 writer, std::span<const std::size_t>(high_level_offsets_));
496 pixie::detail::write_vector(writer,
497 std::span<const std::size_t>(level_sizes_));
498 pixie::detail::write_vector(
499 writer, std::span<const std::size_t>(level_position_spans_));
500 pixie::detail::write_vector(writer,
501 std::span<const std::size_t>(level_fanouts_));
502 writer.write_size(high_level_begin_);
506 std::span<const std::uint64_t> bits,
507 const RankSelectSupport<>& rank_index,
508 BinaryReader& reader) {
510 result.input_bits_ = bits;
511 result.depth_count_ = reader.read_size();
512 result.external_rank_index_ = &rank_index;
514 const std::size_t selector_count = reader.read_size();
515 const std::vector<Bp512Selector> empty_selectors;
516 if (selector_count > empty_selectors.max_size()) {
517 throw std::length_error(
518 "Serialized depth RMQ selector count is too large");
521 reader.remaining() / (kSelectorWords *
sizeof(std::uint64_t))) {
522 throw std::invalid_argument(
"Truncated serialized depth RMQ selectors");
524 result.internal_selectors_.resize(selector_count);
525 for (Bp512Selector& selector : result.internal_selectors_) {
526 for (std::uint64_t& word : selector.bp_bits_) {
527 word = reader.read_u64();
530 result.internal_min_positions_ = pixie::detail::read_vector<Index>(reader);
531 result.internal_min_depths_ =
532 pixie::detail::read_vector<std::int64_t>(reader);
534 const std::size_t metadata_count = reader.read_size();
535 const std::vector<HighChildMetadata> empty_metadata;
536 if (metadata_count > empty_metadata.max_size()) {
537 throw std::length_error(
538 "Serialized depth RMQ child metadata is too large");
540 constexpr std::size_t kSerializedMetadataBytes =
541 2 *
sizeof(std::uint64_t) +
sizeof(Index) +
sizeof(std::int64_t);
542 if (metadata_count > reader.remaining() / kSerializedMetadataBytes) {
543 throw std::invalid_argument(
544 "Truncated serialized depth RMQ child metadata");
546 result.high_child_metadata_.resize(metadata_count);
547 for (HighChildMetadata& metadata : result.high_child_metadata_) {
548 metadata.position_begin = reader.read_size();
549 metadata.position_end = reader.read_size();
550 metadata.min_position = pixie::detail::read_integral<Index>(reader);
551 metadata.min_depth = reader.read_i64();
553 result.high_sparse_min_slots_ =
554 pixie::detail::read_vector<std::uint8_t>(reader);
555 result.internal_level_offsets_ =
556 pixie::detail::read_vector<std::size_t>(reader);
557 result.min_summary_level_offsets_ =
558 pixie::detail::read_vector<std::size_t>(reader);
559 result.high_level_offsets_ =
560 pixie::detail::read_vector<std::size_t>(reader);
561 result.level_sizes_ = pixie::detail::read_vector<std::size_t>(reader);
562 result.level_position_spans_ =
563 pixie::detail::read_vector<std::size_t>(reader);
564 result.level_fanouts_ = pixie::detail::read_vector<std::size_t>(reader);
565 result.high_level_begin_ = reader.read_size();
569 void restore_external_sources(
570 std::span<const std::uint64_t> bits,
571 const RankSelectSupport<>& rank_index)
noexcept {
573 owned_rank_index_.reset();
574 external_rank_index_ = &rank_index;
577 void restore_empty_sources() noexcept {
579 owned_rank_index_.reset();
580 external_rank_index_ =
nullptr;
583 void validate_serialized_state(
const RankSelectSupport<>& rank_index,
584 DeserializationValidation validation)
const {
585 if (depth_count_ == 0) {
586 if (!input_bits_.empty() || owned_rank_index_.has_value() ||
587 !internal_selectors_.empty() || !internal_min_positions_.empty() ||
588 !internal_min_depths_.empty() || !high_child_metadata_.empty() ||
589 !high_sparse_min_slots_.empty() || !internal_level_offsets_.empty() ||
590 !min_summary_level_offsets_.empty() || !high_level_offsets_.empty() ||
591 !level_sizes_.empty() || !level_position_spans_.empty() ||
592 !level_fanouts_.empty() ||
593 high_level_begin_ != std::numeric_limits<std::size_t>::max()) {
594 throw std::invalid_argument(
595 "Invalid serialized empty depth RMQ metadata");
600 const std::size_t delta_count = depth_count_ - 1;
601 const std::size_t required_words =
602 delta_count == 0 ? 0 : 1 + (delta_count - 1) / 64;
603 if (required_words > input_bits_.size() ||
604 rank_index.size() < delta_count || owned_rank_index_.has_value()) {
605 throw std::invalid_argument(
606 "Invalid serialized depth RMQ source metadata");
609 const std::size_t level_count = level_sizes_.size();
610 if (level_count == 0 || internal_level_offsets_.size() != level_count ||
611 min_summary_level_offsets_.size() != level_count ||
612 high_level_offsets_.size() != level_count ||
613 level_position_spans_.size() != level_count ||
614 level_fanouts_.size() != level_count) {
615 throw std::invalid_argument(
616 "Invalid serialized depth RMQ topology sizes");
619 const std::size_t expected_leaf_count = 1 + (depth_count_ - 1) / LeafSize;
620 if (level_sizes_[0] != expected_leaf_count ||
621 level_position_spans_[0] != LeafSize || level_fanouts_[0] != 0 ||
622 internal_level_offsets_[0] != 0 ||
623 min_summary_level_offsets_[0] != npos || high_level_offsets_[0] != 0) {
624 throw std::invalid_argument(
"Invalid serialized depth RMQ leaf topology");
627 std::size_t current_count = expected_leaf_count;
628 std::size_t current_span = LeafSize;
629 std::size_t expected_internal_count = 0;
630 for (std::size_t level = 1; level < level_count; ++level) {
631 const std::size_t expected_fanout =
632 current_count > kHighLevelFanout * kHighLevelFanout
635 current_count = ceil_div(current_count, expected_fanout);
636 current_span = saturating_product(current_span, expected_fanout);
637 if (level_fanouts_[level] != expected_fanout ||
638 level_sizes_[level] != current_count ||
639 level_position_spans_[level] != current_span ||
640 internal_level_offsets_[level] != expected_internal_count) {
641 throw std::invalid_argument(
642 "Invalid serialized depth RMQ level topology");
645 std::numeric_limits<std::size_t>::max() - expected_internal_count) {
646 throw std::length_error(
647 "Serialized depth RMQ internal count is too large");
649 expected_internal_count += current_count;
651 if (current_count != 1 ||
652 internal_selectors_.size() != expected_internal_count) {
653 throw std::invalid_argument(
654 "Invalid serialized depth RMQ selector topology");
657 const std::size_t root_level = level_count - 1;
658 const std::size_t expected_high_begin =
660 ? std::numeric_limits<std::size_t>::max()
661 : (level_fanouts_[root_level] == kHighLevelFanout ? root_level
663 if (high_level_begin_ != expected_high_begin) {
664 throw std::invalid_argument(
665 "Invalid serialized depth RMQ high-level topology");
668 std::size_t high_node_count = 0;
669 std::size_t side_summary_count = 0;
670 for (std::size_t level = 1; level < level_count; ++level) {
671 const bool high_level = level >= expected_high_begin;
672 if (high_level_offsets_[level] != high_node_count) {
673 throw std::invalid_argument(
674 "Invalid serialized depth RMQ high-level offsets");
677 high_node_count += level_sizes_[level];
680 !high_level && level_fanouts_[level] <= kEmbeddedSummaryMaxEntries;
681 const std::size_t expected_summary_offset =
682 embeds ? npos : side_summary_count;
683 if (min_summary_level_offsets_[level] != expected_summary_offset) {
684 throw std::invalid_argument(
685 "Invalid serialized depth RMQ summary offsets");
688 side_summary_count += level_sizes_[level];
691 if (internal_min_positions_.size() != side_summary_count ||
692 internal_min_depths_.size() != side_summary_count ||
694 std::numeric_limits<std::size_t>::max() / kHighLevelFanout ||
695 high_child_metadata_.size() != high_node_count * kHighLevelFanout ||
697 std::numeric_limits<std::size_t>::max() / kHighSparseSlotsPerNode ||
698 high_sparse_min_slots_.size() !=
699 high_node_count * kHighSparseSlotsPerNode) {
700 throw std::invalid_argument(
701 "Invalid serialized depth RMQ metadata counts");
704 for (
const Index position : internal_min_positions_) {
705 if (position == invalid_index ||
706 static_cast<std::size_t
>(position) >= depth_count_) {
707 throw std::invalid_argument(
708 "Invalid serialized depth RMQ minimum position");
711 for (std::size_t level = expected_high_begin; level < level_count;
713 for (std::size_t node = 0; node < level_sizes_[level]; ++node) {
714 const std::size_t count = entry_count(level, node);
715 const std::size_t first_child = node * level_fanouts_[level];
716 const std::size_t flat = high_level_offsets_[level] + node;
717 for (std::size_t slot = 0; slot < count; ++slot) {
718 const HighChildMetadata& metadata =
719 high_child_metadata_[flat * kHighLevelFanout + slot];
720 const std::size_t child = first_child + slot;
721 const std::size_t expected_begin =
722 child * level_position_spans_[level - 1];
723 const std::size_t expected_end = std::min(
724 depth_count_, expected_begin + level_position_spans_[level - 1]);
725 if (metadata.position_begin != expected_begin ||
726 metadata.position_end != expected_end ||
727 metadata.min_position == invalid_index ||
728 metadata.min_position < expected_begin ||
729 metadata.min_position >= expected_end) {
730 throw std::invalid_argument(
731 "Invalid serialized depth RMQ child metadata");
736 if (validation == DeserializationValidation::kFull) {
737 validate_exact_metadata();
741 void validate_exact_metadata()
const {
742 for (std::size_t level = 1; level < level_count(); ++level) {
743 for (std::size_t node = 0; node < level_sizes_[level]; ++node) {
744 const std::size_t count = entry_count(level, node);
745 const std::size_t first_child = node * level_fanouts_[level];
746 std::array<DepthCandidate, kSelectorEntries> child_minima{};
747 for (std::size_t slot = 0; slot < count; ++slot) {
749 subtree_min_candidate(level - 1, first_child + slot);
752 Bp512Selector expected_selector;
753 expected_selector.build(count,
754 [&](std::size_t left, std::size_t right) {
755 return strictly_better_candidate(
756 child_minima[left], child_minima[right]);
758 std::size_t best_slot = 0;
759 for (std::size_t slot = 1; slot < count; ++slot) {
760 if (strictly_better_candidate(child_minima[slot],
761 child_minima[best_slot])) {
765 const DepthCandidate expected_minimum = child_minima[best_slot];
766 if (level_embeds_min_summary(level)) {
767 expected_selector.set_embedded_min_summary(expected_minimum.position,
768 expected_minimum.depth);
770 const std::size_t flat = min_summary_flat_index(level, node);
771 if (internal_min_positions_[flat] != expected_minimum.position ||
772 internal_min_depths_[flat] != expected_minimum.depth) {
773 throw std::invalid_argument(
774 "Serialized depth RMQ minimum disagrees with source");
777 if (selector_at(level, node).bp_bits_ != expected_selector.bp_bits_) {
778 throw std::invalid_argument(
779 "Serialized depth RMQ selector disagrees with source");
782 if (!is_high_level(level)) {
785 const std::size_t high_flat = high_flat_index(level, node);
786 for (std::size_t slot = 0; slot < kHighLevelFanout; ++slot) {
787 HighChildMetadata expected;
789 const std::size_t child = first_child + slot;
790 expected.position_begin = node_position_begin(level - 1, child);
791 expected.position_end = node_position_end(level - 1, child);
792 expected.min_position =
793 static_cast<Index
>(child_minima[slot].position);
794 expected.min_depth = child_minima[slot].depth;
796 const HighChildMetadata& actual =
797 high_child_metadata_at(high_flat, slot);
798 if (actual.position_begin != expected.position_begin ||
799 actual.position_end != expected.position_end ||
800 actual.min_position != expected.min_position ||
801 actual.min_depth != expected.min_depth) {
802 throw std::invalid_argument(
803 "Serialized depth RMQ child metadata disagrees with source");
807 std::array<std::uint8_t, kHighSparseSlotsPerNode> expected_sparse{};
808 for (std::size_t slot = 0; slot < count; ++slot) {
809 expected_sparse[slot] =
static_cast<std::uint8_t
>(slot);
811 for (std::size_t sparse_level = 1;
812 sparse_level < kHighSparseTableLevels; ++sparse_level) {
813 const std::size_t span = std::size_t{1} << sparse_level;
817 const std::size_t half_span = span >> 1;
818 const std::size_t previous = (sparse_level - 1) * kHighLevelFanout;
819 const std::size_t current = sparse_level * kHighLevelFanout;
820 for (std::size_t slot = 0; slot + span <= count; ++slot) {
821 const std::size_t left = expected_sparse[previous + slot];
822 const std::size_t right =
823 expected_sparse[previous + slot + half_span];
824 expected_sparse[current + slot] =
static_cast<std::uint8_t
>(
825 strictly_better_candidate(child_minima[right],
831 const std::uint8_t* actual_sparse =
832 high_sparse_min_slots_begin(high_flat);
833 if (!std::ranges::equal(
835 std::span(actual_sparse, kHighSparseSlotsPerNode))) {
836 throw std::invalid_argument(
837 "Serialized depth RMQ sparse metadata disagrees with source");
846 bool missing_position(std::size_t position)
const {
847 if constexpr (kInvalidIndexEqualsNpos) {
848 return position == npos;
850 return position == npos ||
851 position ==
static_cast<std::size_t
>(invalid_index);
858 DepthCandidate better_candidate(DepthCandidate left,
859 DepthCandidate right)
const {
860 if (missing_position(left.position)) {
863 if (missing_position(right.position)) {
866 if (right.depth < left.depth) {
869 if (left.depth < right.depth) {
872 return right.position < left.position ? right : left;
878 bool strictly_better_candidate(DepthCandidate left,
879 DepthCandidate right)
const {
880 if (missing_position(left.position)) {
883 if (missing_position(right.position)) {
886 if (left.depth != right.depth) {
887 return left.depth < right.depth;
889 return left.position < right.position;
896 owned_rank_index_.reset();
897 internal_selectors_.clear();
898 internal_min_positions_.clear();
899 internal_min_depths_.clear();
900 high_child_metadata_.clear();
901 high_sparse_min_slots_.clear();
902 internal_level_offsets_.clear();
903 min_summary_level_offsets_.clear();
904 high_level_offsets_.clear();
905 level_sizes_.clear();
906 level_position_spans_.clear();
907 level_fanouts_.clear();
908 high_level_begin_ = std::numeric_limits<std::size_t>::max();
910 if (depth_count_ == 0) {
913 if (depth_count_ >
static_cast<std::size_t
>(invalid_index)) {
914 throw std::length_error(
915 "HybridBTreePlusMinusOne index type is too small");
918 static_cast<std::size_t
>(std::numeric_limits<std::int64_t>::max())) {
919 throw std::length_error(
920 "HybridBTreePlusMinusOne depth range is too large");
922 if (input_bits_.size() < (depth_count_ - 1 + 63) / 64) {
923 throw std::invalid_argument(
924 "HybridBTreePlusMinusOne bit span is too small");
927 const std::size_t delta_count = depth_count_ - 1;
928 if (external_rank_index_ ==
nullptr) {
929 owned_rank_index_.emplace(input_bits_, delta_count,
930 RankSelectSupport<>::SelectSupport::kSelect0);
931 }
else if (external_rank_index_->size() < delta_count) {
932 throw std::invalid_argument(
933 "HybridBTreePlusMinusOne external rank index is too small");
936 initialize_layout((depth_count_ + LeafSize - 1) / LeafSize);
937 for (std::size_t level = 1; level < level_count(); ++level) {
938 for (std::size_t node = 0; node < level_sizes_[level]; ++node) {
939 build_internal_node(level, node);
947 void initialize_layout(std::size_t leaf_count) {
948 level_sizes_.push_back(leaf_count);
949 level_position_spans_.push_back(LeafSize);
950 level_fanouts_.push_back(0);
952 std::size_t current_count = leaf_count;
953 std::size_t current_span = LeafSize;
954 while (current_count > kHighLevelFanout * kHighLevelFanout) {
955 level_fanouts_.push_back(kMiddleFanout);
956 current_count = ceil_div(current_count, kMiddleFanout);
957 current_span = saturating_product(current_span, kMiddleFanout);
958 level_sizes_.push_back(current_count);
959 level_position_spans_.push_back(current_span);
961 while (current_count > 1) {
962 level_fanouts_.push_back(kHighLevelFanout);
963 current_count = ceil_div(current_count, kHighLevelFanout);
964 current_span = saturating_product(current_span, kHighLevelFanout);
965 level_sizes_.push_back(current_count);
966 level_position_spans_.push_back(current_span);
968 internal_level_offsets_.assign(level_count(), 0);
969 min_summary_level_offsets_.assign(level_count(), npos);
970 high_level_offsets_.assign(level_count(), 0);
971 if (level_count() <= 1) {
975 std::size_t internal_count = 0;
976 for (std::size_t level = 1; level < level_count(); ++level) {
977 internal_level_offsets_[level] = internal_count;
978 internal_count += level_sizes_[level];
980 internal_selectors_.resize(internal_count);
982 if constexpr (UseHighSparseLayout) {
983 const std::size_t root_level = level_count() - 1;
984 std::size_t high_layout_levels = 0;
985 for (std::size_t level = root_level;
986 level > 0 && high_layout_levels < HighSparseLayoutLevels &&
987 fanout_at_level(level) == kHighLevelFanout;
989 ++high_layout_levels;
991 high_level_begin_ = high_layout_levels == 0
993 : root_level + 1 - high_layout_levels;
995 std::size_t high_node_count = 0;
996 for (std::size_t level = high_level_begin_; level < level_count();
998 high_level_offsets_[level] = high_node_count;
999 high_node_count += level_sizes_[level];
1001 high_child_metadata_.resize(high_node_count * kHighLevelFanout);
1002 high_sparse_min_slots_.resize(high_node_count * kHighSparseSlotsPerNode);
1004 high_level_begin_ = level_count();
1007 std::size_t side_summary_count = 0;
1008 for (std::size_t level = 1; level < level_count(); ++level) {
1009 if (!level_embeds_min_summary(level)) {
1010 min_summary_level_offsets_[level] = side_summary_count;
1011 side_summary_count += level_sizes_[level];
1014 internal_min_positions_.resize(side_summary_count, invalid_index);
1015 internal_min_depths_.resize(side_summary_count,
1016 std::numeric_limits<std::int64_t>::max());
1022 void build_internal_node(std::size_t level, std::size_t node) {
1023 const std::size_t count = entry_count(level, node);
1024 const std::size_t first_child = node * fanout_at_level(level);
1025 const bool high_level = is_high_level(level);
1026 const std::size_t high_flat = high_level ? high_flat_index(level, node) : 0;
1028 std::array<DepthCandidate, kSelectorEntries> child_minima{};
1029 for (std::size_t slot = 0; slot < count; ++slot) {
1030 child_minima[slot] = subtree_min_candidate(level - 1, first_child + slot);
1034 for (std::size_t slot = 0; slot < count; ++slot) {
1035 const std::size_t child = first_child + slot;
1036 const DepthCandidate child_min = child_minima[slot];
1037 HighChildMetadata& metadata =
1038 mutable_high_child_metadata_at(high_flat, slot);
1039 metadata.position_begin = node_position_begin(level - 1, child);
1040 metadata.position_end = node_position_end(level - 1, child);
1041 metadata.min_position =
static_cast<Index
>(child_min.position);
1042 metadata.min_depth = child_min.depth;
1044 build_high_sparse_min_slots(level, node, count);
1047 Bp512Selector& selector = mutable_selector_at(level, node);
1048 selector.build(count, [&](std::size_t left, std::size_t right) {
1049 return strictly_better_candidate(child_minima[left], child_minima[right]);
1052 const std::size_t slot = selector.arg_min(0, count, count);
1053 const DepthCandidate minimum = child_minima[slot];
1054 if (level_embeds_min_summary(level)) {
1055 selector.set_embedded_min_summary(minimum.position, minimum.depth);
1057 const std::size_t flat = min_summary_flat_index(level, node);
1058 internal_min_positions_[flat] =
static_cast<Index
>(minimum.position);
1059 internal_min_depths_[flat] = minimum.depth;
1066 static std::size_t ceil_div(std::size_t value, std::size_t divisor) {
1067 return (value + divisor - 1) / divisor;
1073 static std::size_t saturating_product(std::size_t left, std::size_t right) {
1074 if (left != 0 && right > std::numeric_limits<std::size_t>::max() / left) {
1075 return std::numeric_limits<std::size_t>::max();
1077 return left * right;
1083 std::uint64_t word_or_zero(std::size_t word)
const {
1084 return word < input_bits_.size() ? input_bits_[word] : 0;
1094 const std::uint64_t* chunk_words_or_copy(
1095 std::size_t first_word,
1096 std::array<std::uint64_t, 2>& storage)
const {
1097 if (first_word + 1 < input_bits_.size()) {
1098 return input_bits_.data() + first_word;
1100 storage[0] = word_or_zero(first_word);
1101 storage[1] = word_or_zero(first_word + 1);
1102 return storage.data();
1108 const RankSelectSupport<>* rank_index_or_null()
const {
1109 return external_rank_index_ !=
nullptr
1110 ? external_rank_index_
1111 : (owned_rank_index_ ? &*owned_rank_index_ :
nullptr);
1117 const RankSelectSupport<>& rank_index()
const {
1118 return *rank_index_or_null();
1124 std::int64_t depth_at_position(std::size_t position)
const {
1125 const std::size_t delta_count = depth_count_ == 0 ? 0 : depth_count_ - 1;
1126 position = std::min(position, delta_count);
1127 const std::uint64_t ones = rank_index().rank(position);
1128 return static_cast<std::int64_t
>(ones) -
1129 static_cast<std::int64_t
>(position - ones);
1135 DepthCandidate scan_leaf_range_with_base(std::size_t leaf,
1136 std::size_t left_offset,
1137 std::size_t right_offset,
1138 std::int64_t base_depth)
const {
1139 const std::size_t begin = node_position_begin(0, leaf);
1140 const std::size_t count = entry_count(0, leaf);
1141 if (count == 0 || left_offset > right_offset || left_offset >= count) {
1144 right_offset = std::min(right_offset, count - 1);
1146 DepthCandidate answer;
1147 std::int64_t chunk_base_excess = 0;
1148 const std::size_t first_word = leaf * kLeafWords;
1149 for (std::size_t chunk = 0; chunk < kLeafChunks; ++chunk) {
1150 const std::size_t chunk_begin = chunk * 128;
1151 if (chunk_begin >= count || chunk_begin > right_offset) {
1155 std::array<std::uint64_t, 2> chunk_storage{};
1156 const std::uint64_t* chunk_words =
1157 chunk_words_or_copy(first_word + 2 * chunk, chunk_storage);
1159 const std::size_t chunk_end =
1160 std::min<std::size_t>(count - 1, chunk_begin + 127);
1161 if (left_offset > chunk_end) {
1162 chunk_base_excess += prefix_excess_128(chunk_words, 128);
1166 const std::size_t local_left =
1167 std::max(left_offset, chunk_begin) - chunk_begin;
1168 const std::size_t local_right =
1169 std::min(right_offset, chunk_end) - chunk_begin;
1170 const ExcessResult result =
1171 excess_min_128(chunk_words, local_left, local_right);
1172 const std::size_t offset = chunk_begin + result.offset;
1173 if (result.offset != npos && offset < count) {
1174 answer = better_candidate(
1175 answer, {begin + offset,
1176 base_depth + chunk_base_excess + result.min_excess});
1179 chunk_base_excess += prefix_excess_128(chunk_words, 128);
1191 std::size_t leaf_range_arg_min_relative(std::size_t leaf,
1193 std::size_t right)
const {
1194 if (left >= right) {
1198 const std::size_t begin = node_position_begin(0, leaf);
1199 const std::size_t count = entry_count(0, leaf);
1200 std::size_t left_offset = left - begin;
1201 std::size_t right_offset = right - begin - 1;
1202 if (count == 0 || left_offset >= count || left_offset > right_offset) {
1205 right_offset = std::min(right_offset, count - 1);
1207 std::size_t best_position = npos;
1208 std::int64_t best_depth = std::numeric_limits<std::int64_t>::max();
1209 std::int64_t chunk_base_excess = 0;
1210 bool first_chunk =
true;
1212 const std::size_t first_word = leaf * kLeafWords;
1213 std::size_t chunk = left_offset / 128;
1214 for (; chunk < kLeafChunks; ++chunk) {
1215 const std::size_t chunk_begin = chunk * 128;
1216 if (chunk_begin >= count || chunk_begin > right_offset) {
1220 std::array<std::uint64_t, 2> chunk_storage{};
1221 const std::uint64_t* chunk_words =
1222 chunk_words_or_copy(first_word + 2 * chunk, chunk_storage);
1224 const std::size_t chunk_end =
1225 std::min<std::size_t>(count - 1, chunk_begin + 127);
1226 const std::size_t local_left =
1227 std::max(left_offset, chunk_begin) - chunk_begin;
1228 const std::size_t local_right =
1229 std::min(right_offset, chunk_end) - chunk_begin;
1230 const int left_prefix =
1231 first_chunk ? prefix_excess_128(chunk_words, local_left) : 0;
1232 const std::int64_t local_base =
1233 first_chunk ? -
static_cast<std::int64_t
>(left_prefix)
1234 : chunk_base_excess;
1235 const ExcessResult result =
1236 excess_min_128(chunk_words, local_left, local_right);
1237 const std::size_t offset = chunk_begin + result.offset;
1238 if (result.offset != npos && offset < count) {
1239 const std::int64_t candidate_depth = local_base + result.min_excess;
1240 if (best_position == npos || candidate_depth < best_depth) {
1241 best_position = begin + offset;
1242 best_depth = candidate_depth;
1246 const int chunk_excess = prefix_excess_128(chunk_words, 128);
1249 static_cast<std::int64_t
>(chunk_excess) - left_prefix;
1250 first_chunk =
false;
1252 chunk_base_excess += chunk_excess;
1256 return best_position;
1262 DepthCandidate leaf_range_min(std::size_t leaf,
1264 std::size_t right)
const {
1265 if (left >= right) {
1269 const std::size_t begin = node_position_begin(0, leaf);
1270 const std::size_t slot_left = left - begin;
1271 const std::size_t slot_right = right - begin;
1272 return scan_leaf_range_with_base(leaf, slot_left, slot_right - 1,
1273 depth_at_position(begin));
1279 std::pair<std::size_t, std::size_t> covering_node(
1280 std::size_t left_leaf,
1281 std::size_t right_leaf)
const {
1282 std::size_t level = 0;
1283 std::size_t left_node = left_leaf;
1284 std::size_t right_node = right_leaf;
1285 while (left_node != right_node) {
1287 const std::size_t fanout = fanout_at_level(level);
1288 left_node /= fanout;
1289 right_node /= fanout;
1291 return {level, left_node};
1297 std::size_t leaf_for_position(std::size_t position)
const {
1298 return position / LeafSize;
1304 std::size_t child_for_position(std::size_t child_level,
1305 std::size_t position)
const {
1306 return position / level_position_spans_[child_level];
1312 DepthCandidate query_child_slots(std::size_t level,
1314 std::size_t slot_left,
1315 std::size_t slot_right,
1317 std::size_t right)
const {
1318 if (slot_left >= slot_right) {
1322 const std::size_t count = entry_count(level, node);
1323 const std::size_t slot =
1324 slot_left + 1 == slot_right
1326 : selector_arg_min(level, node, slot_left, slot_right, count);
1331 const std::size_t child_level = level - 1;
1332 const std::size_t first_child = node * fanout_at_level(level);
1333 const std::size_t child = first_child + slot;
1334 const HighChildMetadata* high_children =
1335 is_high_level(level) ? high_child_metadata_begin(level, node) : nullptr;
1336 const DepthCandidate child_min =
1337 high_children !=
nullptr ? high_child_min_candidate(level, node, slot)
1338 : subtree_min_candidate(child_level, child);
1339 const std::size_t child_begin =
1340 high_children !=
nullptr ? high_children[slot].position_begin
1341 : node_position_begin(child_level, child);
1342 const std::size_t child_end = high_children !=
nullptr
1343 ? high_children[slot].position_end
1344 : node_position_end(child_level, child);
1345 if ((left <= child_begin && child_end <= right) ||
1346 contains_position(left, right, child_min.position)) {
1350 const std::size_t last_slot = slot_right - 1;
1351 const std::size_t left_child_begin =
1352 high_children !=
nullptr
1353 ? high_children[slot_left].position_begin
1354 : node_position_begin(child_level, first_child + slot_left);
1355 const std::size_t left_child_end =
1356 high_children !=
nullptr
1357 ? high_children[slot_left].position_end
1358 : node_position_end(child_level, first_child + slot_left);
1359 DepthCandidate answer = query_node(child_level, first_child + slot_left,
1360 std::max(left, left_child_begin),
1361 std::min(right, left_child_end));
1363 if (slot_left != last_slot) {
1364 const std::size_t right_child_begin =
1365 high_children !=
nullptr
1366 ? high_children[last_slot].position_begin
1367 : node_position_begin(child_level, first_child + last_slot);
1368 const std::size_t right_child_end =
1369 high_children !=
nullptr
1370 ? high_children[last_slot].position_end
1371 : node_position_end(child_level, first_child + last_slot);
1372 answer = better_candidate(answer,
1373 query_node(child_level, first_child + last_slot,
1374 std::max(left, right_child_begin),
1375 std::min(right, right_child_end)));
1378 if (slot_left + 1 < last_slot) {
1379 answer = better_candidate(
1381 full_child_slot_range_min(level, node, slot_left + 1, last_slot));
1390 DepthCandidate full_child_slot_range_min(std::size_t level,
1392 std::size_t slot_left,
1393 std::size_t slot_right)
const {
1394 if (slot_left >= slot_right) {
1398 const std::size_t slot =
1399 slot_left + 1 == slot_right
1401 : selector_arg_min(level, node, slot_left, slot_right,
1402 entry_count(level, node));
1406 return child_min_candidate(level, node, slot);
1412 DepthCandidate query_node(std::size_t level,
1415 std::size_t right)
const {
1416 if (left >= right) {
1420 const std::size_t begin = node_position_begin(level, node);
1421 const std::size_t end = node_position_end(level, node);
1422 if (left <= begin && end <= right) {
1423 return subtree_min_candidate(level, node);
1426 return leaf_range_min(node, left, right);
1429 const std::size_t child_level = level - 1;
1430 const std::size_t left_child = child_for_position(child_level, left);
1431 const std::size_t right_child = child_for_position(child_level, right - 1);
1432 const std::size_t first_child = node * fanout_at_level(level);
1433 const std::size_t left_slot = left_child - first_child;
1434 const std::size_t right_slot = right_child - first_child + 1;
1435 return query_child_slots(level, node, left_slot, right_slot, left, right);
1441 bool contains_position(std::size_t left,
1443 std::size_t position)
const {
1444 return !missing_position(position) && left <= position && position < right;
1450 std::size_t level_count()
const {
return level_sizes_.size(); }
1455 std::size_t entry_count(std::size_t level, std::size_t node)
const {
1457 const std::size_t begin = node_position_begin(0, node);
1458 return std::min<std::size_t>(LeafSize, depth_count_ - begin);
1460 const std::size_t first_child = node * fanout_at_level(level);
1461 return std::min<std::size_t>(fanout_at_level(level),
1462 level_sizes_[level - 1] - first_child);
1468 std::size_t node_position_begin(std::size_t level, std::size_t node)
const {
1469 return node * level_position_spans_[level];
1475 std::size_t node_position_end(std::size_t level, std::size_t node)
const {
1476 return std::min(depth_count_, node_position_begin(level, node) +
1477 level_position_spans_[level]);
1483 DepthCandidate subtree_min_candidate(std::size_t level,
1484 std::size_t node)
const {
1486 return leaf_range_min(node, node_position_begin(0, node),
1487 node_position_end(0, node));
1489 if (level_embeds_min_summary(level)) {
1490 const Bp512Selector& selector = selector_at(level, node);
1491 return {selector.embedded_min_position(), selector.embedded_min_depth()};
1493 const std::size_t flat = min_summary_flat_index(level, node);
1494 return {
static_cast<std::size_t
>(internal_min_positions_[flat]),
1495 internal_min_depths_[flat]};
1501 DepthCandidate child_min_candidate(std::size_t level,
1503 std::size_t slot)
const {
1504 if (is_high_level(level)) {
1505 return high_child_min_candidate(level, node, slot);
1507 return subtree_min_candidate(level - 1,
1508 node * fanout_at_level(level) + slot);
1514 DepthCandidate high_child_min_candidate(std::size_t level,
1516 std::size_t slot)
const {
1517 const HighChildMetadata& metadata =
1518 high_child_metadata_at(high_flat_index(level, node), slot);
1519 return {
static_cast<std::size_t
>(metadata.min_position),
1520 metadata.min_depth};
1526 const Bp512Selector& selector_at(std::size_t level, std::size_t node)
const {
1527 return internal_selectors_[internal_flat_index(level, node)];
1533 Bp512Selector& mutable_selector_at(std::size_t level, std::size_t node) {
1534 return internal_selectors_[internal_flat_index(level, node)];
1540 std::size_t selector_arg_min(std::size_t level,
1542 std::size_t slot_left,
1543 std::size_t slot_right,
1544 std::size_t count)
const {
1545 if constexpr (UseHighSparseLayout) {
1546 if (is_high_level(level)) {
1547 return high_sparse_arg_min(level, node, slot_left, slot_right, count);
1550 return selector_at(level, node).arg_min(slot_left, slot_right, count);
1556 bool is_high_level(std::size_t level)
const {
1557 if constexpr (!UseHighSparseLayout) {
1561 return level > 0 && level >= high_level_begin_ && level < level_count();
1567 std::size_t internal_flat_index(std::size_t level, std::size_t node)
const {
1568 return internal_level_offsets_[level] + node;
1574 bool level_embeds_min_summary(std::size_t level)
const {
1575 return level > 0 && !is_high_level(level) &&
1576 fanout_at_level(level) <= kEmbeddedSummaryMaxEntries;
1582 std::size_t min_summary_flat_index(std::size_t level,
1583 std::size_t node)
const {
1584 return min_summary_level_offsets_[level] + node;
1590 std::size_t fanout_at_level(std::size_t level)
const {
1591 return level_fanouts_[level];
1597 std::size_t high_flat_index(std::size_t level, std::size_t node)
const {
1598 return high_level_offsets_[level] + node;
1604 const HighChildMetadata* high_child_metadata_begin(std::size_t level,
1605 std::size_t node)
const {
1606 return high_child_metadata_.data() +
1607 high_flat_index(level, node) * kHighLevelFanout;
1613 const HighChildMetadata& high_child_metadata_at(std::size_t high_flat,
1614 std::size_t slot)
const {
1615 return high_child_metadata_[high_flat * kHighLevelFanout + slot];
1621 HighChildMetadata& mutable_high_child_metadata_at(std::size_t high_flat,
1623 return high_child_metadata_[high_flat * kHighLevelFanout + slot];
1629 std::uint8_t* mutable_high_sparse_min_slots_begin(std::size_t high_flat) {
1630 return high_sparse_min_slots_.data() + high_flat * kHighSparseSlotsPerNode;
1636 const std::uint8_t* high_sparse_min_slots_begin(std::size_t high_flat)
const {
1637 return high_sparse_min_slots_.data() + high_flat * kHighSparseSlotsPerNode;
1643 std::size_t better_high_child_slot(std::size_t level,
1645 std::size_t left_slot,
1646 std::size_t right_slot)
const {
1647 const DepthCandidate left =
1648 high_child_min_candidate(level, node, left_slot);
1649 const DepthCandidate right =
1650 high_child_min_candidate(level, node, right_slot);
1651 return better_candidate(left, right).position == right.position ? right_slot
1658 void build_high_sparse_min_slots(std::size_t level,
1660 std::size_t count) {
1661 const std::size_t high_flat = high_flat_index(level, node);
1662 std::uint8_t* table = mutable_high_sparse_min_slots_begin(high_flat);
1663 for (std::size_t slot = 0; slot < count; ++slot) {
1664 table[slot] =
static_cast<std::uint8_t
>(slot);
1667 for (std::size_t table_level = 1; table_level < kHighSparseTableLevels;
1669 const std::size_t span = std::size_t{1} << table_level;
1673 const std::size_t half_span = span >> 1;
1674 const std::uint8_t* previous =
1675 table + (table_level - 1) * kHighLevelFanout;
1676 std::uint8_t* current = table + table_level * kHighLevelFanout;
1677 for (std::size_t slot = 0; slot + span <= count; ++slot) {
1678 current[slot] =
static_cast<std::uint8_t
>(better_high_child_slot(
1679 level, node, previous[slot], previous[slot + half_span]));
1687 std::size_t high_sparse_arg_min(std::size_t level,
1689 std::size_t slot_left,
1690 std::size_t slot_right,
1691 std::size_t count)
const {
1692 if (slot_left >= slot_right || slot_right > count) {
1695 const std::size_t length = slot_right - slot_left;
1700 const std::size_t high_flat = high_flat_index(level, node);
1701 const std::size_t table_level = std::bit_width(length) - 1;
1702 const std::size_t span = std::size_t{1} << table_level;
1703 const std::uint8_t* table =
1704 high_sparse_min_slots_begin(high_flat) + table_level * kHighLevelFanout;
1705 return better_high_child_slot(level, node, table[slot_left],
1706 table[slot_right - span]);
1709 std::span<const std::uint64_t> input_bits_;
1710 std::size_t depth_count_ = 0;
1711 std::optional<RankSelectSupport<>> owned_rank_index_;
1712 const RankSelectSupport<>* external_rank_index_ =
nullptr;
1713 std::vector<Bp512Selector> internal_selectors_;
1714 std::vector<Index> internal_min_positions_;
1715 std::vector<std::int64_t> internal_min_depths_;
1716 std::vector<HighChildMetadata> high_child_metadata_;
1717 std::vector<std::uint8_t> high_sparse_min_slots_;
1718 std::vector<std::size_t> internal_level_offsets_;
1719 std::vector<std::size_t> min_summary_level_offsets_;
1720 std::vector<std::size_t> high_level_offsets_;
1721 std::vector<std::size_t> level_sizes_;
1722 std::vector<std::size_t> level_position_spans_;
1723 std::vector<std::size_t> level_fanouts_;
1724 std::size_t high_level_begin_ = std::numeric_limits<std::size_t>::max();
1755 :
public RmqBase<CartesianHybridBTree<T,
1759 UseTopSparseOverlay>,
1765 UseTopSparseOverlay>> {
1769 struct TopCandidate {
1770 Index position = std::numeric_limits<Index>::max();
1772 static_assert(
sizeof(TopCandidate) ==
sizeof(Index));
1774 struct LoadedState {
1776 std::size_t bp_bit_count_ = 0;
1777 std::vector<TopCandidate> top_sparse_candidates_;
1778 std::size_t top_block_size_ = 4096;
1779 std::size_t top_block_count_ = 0;
1780 std::size_t top_sparse_levels_ = 0;
1781 std::optional<RankSelectSupport<>> bp_index_;
1782 BpDepthRmq bp_depth_rmq_;
1787 static constexpr std::array<std::uint8_t, 8> kSerializationMagic = {
1788 'P',
'I',
'X',
'I',
'E',
'R',
'M',
'Q'};
1789 static constexpr std::uint32_t kSerializationVersion = 4;
1790 static constexpr std::size_t kSerializationHeaderBytes = 48;
1793 static_assert(std::is_unsigned_v<Index>,
1794 "CartesianHybridBTree index type must be unsigned");
1795 static_assert(LeafSize != 0 && LeafSize % 512 == 0,
1796 "CartesianHybridBTree leaf size must be a positive "
1803 static constexpr Index invalid_index = std::numeric_limits<Index>::max();
1804 static constexpr std::size_t kMinTopSparseBlockSize = 4096;
1805 static constexpr std::size_t kMaxTopSparseBlocks = std::size_t{1} << 14;
1806 static constexpr bool kUseTopSparseOverlay = UseTopSparseOverlay;
1807 static constexpr bool kSerializationSupported =
1808 std::same_as<T, std::int64_t> &&
1809 std::same_as<Compare, std::less<std::int64_t>> &&
1810 std::same_as<Index, std::size_t> && LeafSize == 512 &&
1811 UseTopSparseOverlay;
1825 Compare compare = Compare())
1826 : values_(values), compare_(compare) {
1839 requires(kSerializationSupported)
1843 const std::size_t artifact_begin = writer.size_bytes();
1844 pixie::detail::write_magic(writer, kSerializationMagic);
1845 writer.write_u32(kSerializationVersion);
1846 writer.write_u8(pixie::detail::kLittleEndianMarker);
1847 writer.write_u8(
sizeof(std::uint64_t));
1848 writer.write_u8(
sizeof(T));
1849 writer.write_u8(
sizeof(Index));
1850 const std::size_t artifact_size_position = writer.write_u64_placeholder();
1851 writer.write_size(values_.size());
1852 writer.write_size(LeafSize);
1853 writer.write_u32(
static_cast<std::uint32_t
>(UseTopSparseOverlay));
1854 writer.write_u32(0);
1856 writer.write_size(bp_bit_count_);
1857 writer.write_size(top_block_size_);
1858 writer.write_size(top_block_count_);
1859 writer.write_size(top_sparse_levels_);
1860 bp_bits_.serialize(writer);
1861 writer.write_size(top_sparse_candidates_.size());
1862 for (
const TopCandidate candidate : top_sparse_candidates_) {
1863 pixie::detail::write_integral(writer, candidate.position);
1865 writer.write_u8(
static_cast<std::uint8_t
>(bp_index_.has_value()));
1867 bp_index_->serialize(writer);
1869 bp_depth_rmq_.serialize_metadata(writer);
1871 const std::size_t unpadded_size = writer.size_bytes() - artifact_begin;
1873 (
sizeof(std::uint64_t) - unpadded_size %
sizeof(std::uint64_t)) %
1874 sizeof(std::uint64_t));
1875 const std::size_t artifact_size = writer.size_bytes() - artifact_begin;
1876 writer.patch_u64(artifact_size_position,
1877 static_cast<std::uint64_t
>(artifact_size));
1900 std::span<const std::int64_t> values,
1902 requires(kSerializationSupported)
1905 const std::size_t available_size = candidate.
remaining();
1906 pixie::detail::require_magic(candidate, kSerializationMagic);
1907 if (candidate.
read_u32() != kSerializationVersion ||
1908 candidate.
read_u8() != pixie::detail::kLittleEndianMarker ||
1909 candidate.
read_u8() !=
sizeof(std::uint64_t) ||
1910 candidate.
read_u8() !=
sizeof(T) ||
1911 candidate.
read_u8() !=
sizeof(Index)) {
1912 throw std::invalid_argument(
"Incompatible serialized RMQ artifact");
1914 const std::size_t artifact_size = pixie::detail::checked_artifact_size(
1915 candidate.
read_u64(), kSerializationHeaderBytes, available_size);
1916 const std::size_t source_value_count = candidate.
read_size();
1917 if (candidate.
read_size() != LeafSize ||
1919 static_cast<std::uint32_t
>(UseTopSparseOverlay) ||
1921 throw std::invalid_argument(
"Incompatible serialized RMQ configuration");
1925 candidate.
read_subreader(artifact_size - kSerializationHeaderBytes);
1927 state.bp_bit_count_ = payload.
read_size();
1928 state.top_block_size_ = payload.
read_size();
1929 state.top_block_count_ = payload.
read_size();
1930 state.top_sparse_levels_ = payload.
read_size();
1931 state.bp_bits_ = deserialize_aligned_storage(payload);
1933 const std::size_t candidate_count = payload.
read_size();
1934 const std::vector<TopCandidate> empty_candidates;
1935 if (candidate_count > empty_candidates.max_size()) {
1936 throw std::length_error(
1937 "Serialized RMQ top candidate count is too large");
1939 if (candidate_count > payload.
remaining() /
sizeof(Index)) {
1940 throw std::invalid_argument(
"Truncated serialized RMQ top candidates");
1942 state.top_sparse_candidates_.resize(candidate_count);
1943 for (TopCandidate& candidate : state.top_sparse_candidates_) {
1944 candidate.
position = pixie::detail::read_integral<Index>(payload);
1947 const std::uint8_t has_rank_index = payload.
read_u8();
1948 if (has_rank_index > 1) {
1949 throw std::invalid_argument(
"Invalid serialized RMQ rank-index marker");
1951 const std::size_t bp_word_count =
1952 state.bp_bit_count_ == 0 ? 0 : 1 + (state.bp_bit_count_ - 1) / 64;
1953 if (bp_word_count > state.bp_bits_.
as_words64().size()) {
1954 throw std::invalid_argument(
"Serialized RMQ BP storage is too small");
1956 if (has_rank_index != 0) {
1958 payload, state.bp_bits_.
as_words64().first(bp_word_count),
1964 state.bp_index_ ? *state.bp_index_ : empty_rank_index;
1965 state.bp_depth_rmq_ = BpDepthRmq::deserialize_metadata(
1966 state.bp_bits_.
as_words64(), rank_index, payload);
1969 if (source_value_count != values.size()) {
1970 throw std::invalid_argument(
1971 "Serialized RMQ source value count is inconsistent");
1973 validate_loaded_state(values, state, validation);
1975 return Self(LoadTag{}, values, std::move(state));
1982 : values_(other.values_),
1983 compare_(other.compare_),
1984 bp_bits_(other.bp_bits_),
1985 bp_bit_count_(other.bp_bit_count_),
1986 top_sparse_candidates_(other.top_sparse_candidates_),
1987 top_block_size_(other.top_block_size_),
1988 top_block_count_(other.top_block_count_),
1989 top_sparse_levels_(other.top_sparse_levels_) {
1997 if (
this == &other) {
2000 values_ = other.values_;
2001 compare_ = other.compare_;
2002 bp_bits_ = other.bp_bits_;
2003 bp_bit_count_ = other.bp_bit_count_;
2004 top_sparse_candidates_ = other.top_sparse_candidates_;
2005 top_block_size_ = other.top_block_size_;
2006 top_block_count_ = other.top_block_count_;
2007 top_sparse_levels_ = other.top_sparse_levels_;
2016 : values_(other.values_),
2017 compare_(std::move(other.compare_)),
2018 bp_bits_(std::move(other.bp_bits_)),
2019 bp_bit_count_(other.bp_bit_count_),
2020 top_sparse_candidates_(std::move(other.top_sparse_candidates_)),
2021 top_block_size_(other.top_block_size_),
2022 top_block_count_(other.top_block_count_),
2023 top_sparse_levels_(other.top_sparse_levels_) {
2024 other.values_ = std::span<const T>();
2025 other.bp_bit_count_ = 0;
2026 other.top_block_size_ = kMinTopSparseBlockSize;
2027 other.top_block_count_ = 0;
2028 other.top_sparse_levels_ = 0;
2036 if (
this == &other) {
2039 values_ = other.values_;
2040 compare_ = std::move(other.compare_);
2041 bp_bits_ = std::move(other.bp_bits_);
2042 bp_bit_count_ = other.bp_bit_count_;
2043 top_sparse_candidates_ = std::move(other.top_sparse_candidates_);
2044 top_block_size_ = other.top_block_size_;
2045 top_block_count_ = other.top_block_count_;
2046 top_sparse_levels_ = other.top_sparse_levels_;
2047 other.values_ = std::span<const T>();
2048 other.bp_bit_count_ = 0;
2049 other.top_block_size_ = kMinTopSparseBlockSize;
2050 other.top_block_count_ = 0;
2051 other.top_sparse_levels_ = 0;
2070 if (left >= right || right > values_.size()) {
2073 if constexpr (!UseTopSparseOverlay) {
2074 return cartesian_arg_min(left, right);
2076 if (right - left <= top_block_size_) {
2077 return cartesian_arg_min(left, right);
2079 const std::size_t top_answer = top_sparse_arg_min(left, right);
2080 if (top_answer != npos) {
2083 return cartesian_arg_min(left, right);
2095 return bp_storage_words().first(bp_word_count());
2102 if (value_count == 0) {
2103 return kMinTopSparseBlockSize;
2105 return std::max(kMinTopSparseBlockSize,
2106 ceil_div(value_count, kMaxTopSparseBlocks));
2113 if (value_count == 0) {
2137 return sizeof(*this) + bp_bits_.allocated_bytes() +
2138 pixie::vector_capacity_bytes(top_sparse_candidates_) +
2139 pixie::optional_nested_owned_memory_bytes(bp_index_) +
2140 pixie::nested_owned_memory_bytes(bp_depth_rmq_);
2147 const std::span<const std::byte> bytes = reader.
read_bytes(
size);
2148 if (
size > std::numeric_limits<std::size_t>::max() / 8) {
2149 throw std::length_error(
"Serialized RMQ storage is too large");
2152 std::ranges::copy(bytes, result.writable_bytes().begin());
2156 template <
class State>
2157 static void validate_loaded_state(std::span<const T> values,
2159 DeserializationValidation validation) {
2160 if (values.size() > (
static_cast<std::size_t
>(invalid_index) - 1) / 2) {
2161 throw std::length_error(
"Serialized RMQ value count is too large");
2163 const std::size_t expected_bp_bit_count = 2 * values.size();
2164 if (state.bp_bit_count_ != expected_bp_bit_count) {
2165 throw std::invalid_argument(
"Invalid serialized RMQ BP bit count");
2168 std::size_t expected_padded_bits = 0;
2169 if (expected_bp_bit_count != 0) {
2170 const std::size_t depth_count = expected_bp_bit_count + 1;
2171 if (depth_count > std::numeric_limits<std::int64_t>::max()) {
2172 throw std::length_error(
"Serialized RMQ BP depth count is too large");
2174 const std::size_t leaf_count = 1 + (depth_count - 1) / LeafSize;
2175 if (leaf_count > std::numeric_limits<std::size_t>::max() / LeafSize) {
2176 throw std::length_error(
"Serialized RMQ padded BP size is too large");
2178 expected_padded_bits = leaf_count * LeafSize;
2180 if (state.bp_bits_.size_bits() != expected_padded_bits) {
2181 throw std::invalid_argument(
"Invalid serialized RMQ BP storage size");
2184 if (values.empty()) {
2185 if (state.top_block_size_ != kMinTopSparseBlockSize ||
2186 state.top_block_count_ != 0 || state.top_sparse_levels_ != 0 ||
2187 !state.top_sparse_candidates_.empty() ||
2188 state.bp_index_.has_value() || !state.bp_depth_rmq_.empty()) {
2189 throw std::invalid_argument(
"Invalid serialized empty RMQ metadata");
2191 const RankSelectSupport<> empty_rank;
2192 state.bp_depth_rmq_.validate_serialized_state(empty_rank, validation);
2196 const std::size_t expected_block_size =
2198 const std::size_t expected_block_count =
2200 const std::size_t expected_levels = std::bit_width(expected_block_count);
2201 if (state.top_block_size_ != expected_block_size ||
2202 state.top_block_count_ != expected_block_count ||
2203 state.top_sparse_levels_ != expected_levels ||
2204 expected_block_count >
2205 std::numeric_limits<std::size_t>::max() / expected_levels ||
2206 state.top_sparse_candidates_.size() !=
2207 expected_block_count * expected_levels ||
2208 !state.bp_index_.has_value() ||
2209 state.bp_index_->size() != expected_bp_bit_count ||
2210 state.bp_index_->supports_select1() ||
2211 !state.bp_index_->supports_select0() ||
2212 state.bp_depth_rmq_.size() != expected_bp_bit_count + 1) {
2213 throw std::invalid_argument(
"Invalid serialized RMQ index metadata");
2216 for (std::size_t level = 0;
2217 validation == DeserializationValidation::kQuick &&
2218 level < expected_levels;
2220 const std::size_t span = std::size_t{1} << level;
2221 for (std::size_t block = 0; block < expected_block_count; ++block) {
2222 const std::size_t position =
static_cast<std::size_t
>(
2223 state.top_sparse_candidates_[level * expected_block_count + block]
2225 const bool populated = block + span <= expected_block_count;
2227 if (position !=
static_cast<std::size_t
>(invalid_index)) {
2228 throw std::invalid_argument(
2229 "Invalid serialized RMQ sparse-table padding");
2233 const std::size_t begin = block * expected_block_size;
2234 const std::size_t end =
2235 std::min(values.size(), (block + span) * expected_block_size);
2236 if (position < begin || position >= end) {
2237 throw std::invalid_argument(
2238 "Invalid serialized RMQ sparse-table candidate");
2242 state.bp_depth_rmq_.validate_serialized_state(*state.bp_index_, validation);
2243 if (validation == DeserializationValidation::kFull) {
2244 validate_exact_source(values, state, expected_padded_bits);
2248 template <
class State>
2249 static void validate_exact_source(std::span<const T> values,
2251 std::size_t expected_padded_bits) {
2252 const std::span<const std::uint64_t>
bp_words = state.bp_bits_.as_words64();
2253 const auto bp_bit = [
bp_words](std::size_t position) {
2254 return ((
bp_words[position >> 6] >> (position & 63)) & 1u) != 0;
2256 const auto require_bp_bit = [&](std::size_t position,
bool expected) {
2257 if (bp_bit(position) != expected) {
2258 throw std::invalid_argument(
2259 "Serialized RMQ Cartesian BP disagrees with source values");
2262 const auto better_position = [&](std::size_t left, std::size_t right) {
2263 if (right ==
static_cast<std::size_t
>(invalid_index)) {
2266 if (left ==
static_cast<std::size_t
>(invalid_index)) {
2269 if (std::less<T>{}(values[right], values[left])) {
2272 if (std::less<T>{}(values[left], values[right])) {
2275 return std::min(left, right);
2278 utils::SuccinctIncreasingStack stack(values.size());
2279 std::size_t write_position = 2 * values.size();
2280 std::size_t block_minimum =
static_cast<std::size_t
>(invalid_index);
2281 for (std::size_t position = values.size(); position-- > 0;) {
2282 while (!stack.empty()) {
2283 const std::size_t top_position = values.size() - 1 - stack.top();
2284 if (std::less<T>{}(values[top_position], values[position])) {
2288 require_bp_bit(--write_position,
true);
2290 stack.push(values.size() - 1 - position);
2291 require_bp_bit(--write_position,
false);
2293 block_minimum = better_position(block_minimum, position);
2294 if (position % state.top_block_size_ == 0) {
2295 const std::size_t block = position / state.top_block_size_;
2296 if (
static_cast<std::size_t
>(
2297 state.top_sparse_candidates_[block].position) !=
2299 throw std::invalid_argument(
2300 "Serialized RMQ block minimum disagrees with source values");
2302 block_minimum =
static_cast<std::size_t
>(invalid_index);
2305 while (write_position != 0) {
2306 require_bp_bit(--write_position,
true);
2309 for (std::size_t position = 2 * values.size();
2310 position < expected_padded_bits; ++position) {
2311 if (bp_bit(position)) {
2312 throw std::invalid_argument(
"Serialized RMQ BP padding is non-zero");
2316 for (std::size_t level = 1; level < state.top_sparse_levels_; ++level) {
2317 const std::size_t span = std::size_t{1} << level;
2318 const std::size_t half_span = span >> 1;
2319 const std::size_t current_offset = level * state.top_block_count_;
2320 const std::size_t previous_offset = (level - 1) * state.top_block_count_;
2321 for (std::size_t block = 0; block < state.top_block_count_; ++block) {
2322 const std::size_t actual =
static_cast<std::size_t
>(
2323 state.top_sparse_candidates_[current_offset + block].position);
2324 if (block + span > state.top_block_count_) {
2325 if (actual !=
static_cast<std::size_t
>(invalid_index)) {
2326 throw std::invalid_argument(
2327 "Invalid serialized RMQ sparse-table padding");
2331 const std::size_t left =
static_cast<std::size_t
>(
2332 state.top_sparse_candidates_[previous_offset + block].position);
2333 const std::size_t right =
static_cast<std::size_t
>(
2334 state.top_sparse_candidates_[previous_offset + block + half_span]
2336 if (actual != better_position(left, right)) {
2337 throw std::invalid_argument(
2338 "Serialized RMQ sparse-table candidate disagrees with source");
2344 void validate_serialized_state(DeserializationValidation validation)
const {
2345 validate_loaded_state(values_, *
this, validation);
2351 bp_bits_(std::move(state.bp_bits_)),
2352 bp_bit_count_(state.bp_bit_count_),
2353 top_sparse_candidates_(std::move(state.top_sparse_candidates_)),
2354 top_block_size_(state.top_block_size_),
2355 top_block_count_(state.top_block_count_),
2356 top_sparse_levels_(state.top_sparse_levels_),
2357 bp_index_(std::move(state.bp_index_)),
2358 bp_depth_rmq_(std::move(state.bp_depth_rmq_)) {
2360 bp_depth_rmq_.restore_external_sources(bp_bits_.as_words64(), *bp_index_);
2362 bp_depth_rmq_.restore_empty_sources();
2370 std::size_t cartesian_arg_min(std::size_t left, std::size_t right)
const {
2371 const std::size_t first_close = select_close_position(left + 1);
2372 const std::size_t last_close = select_close_position(right);
2373 if (first_close == npos || last_close == npos || first_close > last_close) {
2377 const std::size_t shifted_min =
2378 bp_depth_rmq_.arg_min(first_close + 1, last_close + 2);
2379 if (shifted_min == npos || shifted_min == 0) {
2382 const RankSelectSupport<>& bp_index = *bp_index_;
2383 const std::size_t answer = bp_index.rank0(shifted_min) - 1;
2384 return answer < values_.size() ? answer : npos;
2393 top_sparse_candidates_.clear();
2394 top_block_size_ = kMinTopSparseBlockSize;
2395 top_block_count_ = 0;
2396 top_sparse_levels_ = 0;
2399 if (values_.empty()) {
2402 if (values_.size() > (
static_cast<std::size_t
>(invalid_index) - 1) / 2) {
2403 throw std::length_error(
2404 "CartesianHybridBTree RMQ index type is too small");
2407 bp_bit_count_ = 2 * values_.size();
2408 bp_bits_.resize(padded_bp_bit_capacity());
2409 std::ranges::fill(bp_storage_words(), std::uint64_t{0});
2411 if constexpr (UseTopSparseOverlay) {
2412 build_top_sparse_table();
2420 void build_bp_bits() {
2421 utils::SuccinctIncreasingStack stack(values_.size());
2422 std::size_t write_position = bp_bit_count_;
2423 std::span<std::uint64_t> words = bp_storage_words();
2424 const auto prepend_open = [&]() {
2426 words[write_position >> 6] |= std::uint64_t{1} << (write_position & 63);
2429 for (std::size_t i = values_.size(); i-- > 0;) {
2430 while (!stack.empty() &&
2431 !compare_(values_[stack_index(stack.top())], values_[i])) {
2435 stack.push(stack_key(i));
2439 while (write_position != 0) {
2448 std::size_t stack_key(std::size_t value_index)
const {
2449 return values_.size() - 1 - value_index;
2456 std::size_t stack_index(std::size_t key)
const {
2457 return values_.size() - 1 - key;
2463 void reset_bp_indexes() {
2465 bp_depth_rmq_ = BpDepthRmq();
2466 if (bp_bit_count_ == 0) {
2469 const std::span<const std::uint64_t> words =
bp_words();
2470 const std::span<const std::uint64_t> padded_words = bp_storage_words();
2472 bp_index_.emplace(words, bp_bit_count_,
2473 RankSelectSupport<>::SelectSupport::kSelect0,
2475 bp_depth_rmq_ = BpDepthRmq(padded_words, bp_bit_count_ + 1, *bp_index_);
2481 std::size_t bp_word_count()
const {
return ceil_div(bp_bit_count_, 64); }
2486 std::size_t padded_bp_bit_capacity()
const {
2487 if (bp_bit_count_ == 0) {
2490 const std::size_t depth_count = bp_bit_count_ + 1;
2491 return ceil_div(depth_count, LeafSize) * LeafSize;
2497 std::span<std::uint64_t> bp_storage_words() {
2498 return bp_bits_.writable_words64();
2504 std::span<const std::uint64_t> bp_storage_words()
const {
2505 return bp_bits_.as_words64();
2511 void build_top_sparse_table() {
2512 top_sparse_candidates_.clear();
2514 top_block_count_ = ceil_div(values_.size(), top_block_size_);
2515 top_sparse_levels_ =
2516 top_block_count_ == 0 ? 0 : std::bit_width(top_block_count_);
2517 if (top_block_count_ == 0) {
2521 top_sparse_candidates_.assign(top_sparse_levels_ * top_block_count_,
2523 for (std::size_t block = 0; block < top_block_count_; ++block) {
2524 const std::size_t begin = block * top_block_size_;
2525 const std::size_t end = std::min(values_.size(), begin + top_block_size_);
2526 std::size_t minimum = begin;
2527 for (std::size_t position = begin + 1; position < end; ++position) {
2528 if (strictly_better_value_position(position, minimum)) {
2532 top_sparse_candidates_[block] = make_top_candidate(minimum);
2535 for (std::size_t level = 1; level < top_sparse_levels_; ++level) {
2536 const std::size_t span = std::size_t{1} << level;
2537 const std::size_t half_span = span >> 1;
2538 TopCandidate* current =
2539 top_sparse_candidates_.data() + level * top_block_count_;
2540 const TopCandidate* previous =
2541 top_sparse_candidates_.data() + (level - 1) * top_block_count_;
2542 for (std::size_t block = 0; block + span <= top_block_count_; ++block) {
2544 better_top_candidate(previous[block], previous[block + half_span]);
2552 static std::size_t ceil_div(std::size_t value, std::size_t divisor) {
2553 return value == 0 ? 0 : 1 + (value - 1) / divisor;
2559 TopCandidate make_top_candidate(std::size_t position)
const {
2560 if (position >= values_.size()) {
2563 return {
static_cast<Index
>(position)};
2569 bool valid_value_position(std::size_t position)
const {
2570 return position != npos &&
2571 position !=
static_cast<std::size_t
>(invalid_index) &&
2572 position < values_.size();
2579 bool strictly_better_value_position(std::size_t left,
2580 std::size_t right)
const {
2581 if (!valid_value_position(left)) {
2584 if (!valid_value_position(right)) {
2587 if (compare_(values_[left], values_[right])) {
2590 if (compare_(values_[right], values_[left])) {
2593 return left < right;
2599 TopCandidate better_top_candidate(TopCandidate left,
2600 TopCandidate right)
const {
2601 const std::size_t left_position =
static_cast<std::size_t
>(left.position);
2602 const std::size_t right_position =
static_cast<std::size_t
>(right.position);
2603 return strictly_better_value_position(right_position, left_position) ? right
2610 TopCandidate top_sparse_block_arg_min(std::size_t block_left,
2611 std::size_t block_right)
const {
2612 if (block_left >= block_right || block_right > top_block_count_ ||
2613 top_sparse_levels_ == 0) {
2616 const std::size_t length = block_right - block_left;
2617 const std::size_t level = std::bit_width(length) - 1;
2618 const std::size_t span = std::size_t{1} << level;
2619 const TopCandidate* table =
2620 top_sparse_candidates_.data() + level * top_block_count_;
2621 return better_top_candidate(table[block_left], table[block_right - span]);
2627 bool top_candidate_inside(TopCandidate candidate,
2629 std::size_t right)
const {
2630 const std::size_t position =
static_cast<std::size_t
>(candidate.position);
2631 return valid_value_position(position) && left <= position &&
2639 std::size_t top_sparse_arg_min(std::size_t left, std::size_t right)
const {
2640 if (top_block_count_ <= 1) {
2644 const std::size_t padded_block_left = left / top_block_size_;
2645 const std::size_t padded_block_right = (right - 1) / top_block_size_ + 1;
2646 if (padded_block_left + 1 >= padded_block_right) {
2650 const TopCandidate padded =
2651 top_sparse_block_arg_min(padded_block_left, padded_block_right);
2652 if (top_candidate_inside(padded, left, right)) {
2653 return static_cast<std::size_t
>(padded.position);
2656 const std::size_t first_full_block =
2657 (left + top_block_size_ - 1) / top_block_size_;
2658 const std::size_t full_block_right = right / top_block_size_;
2659 if (first_full_block >= full_block_right) {
2663 TopCandidate answer =
2664 top_sparse_block_arg_min(first_full_block, full_block_right);
2666 const std::size_t left_border_end = first_full_block * top_block_size_;
2667 if (left < left_border_end) {
2668 answer = better_top_candidate(
2669 answer, make_top_candidate(cartesian_arg_min(left, left_border_end)));
2672 const std::size_t right_border_begin = full_block_right * top_block_size_;
2673 if (right_border_begin < right) {
2674 answer = better_top_candidate(
2676 make_top_candidate(cartesian_arg_min(right_border_begin, right)));
2679 return valid_value_position(
static_cast<std::size_t
>(answer.position))
2680 ?
static_cast<std::size_t
>(answer.position)
2687 std::size_t select_close_position(std::size_t rank)
const {
2688 if (rank == 0 || rank > values_.size() || !bp_index_) {
2691 const std::size_t position = bp_index_->select0(rank);
2692 return position < bp_bit_count_ ? position : npos;
2695 std::span<const T> values_;
2697 pixie::AlignedStorage bp_bits_;
2698 std::size_t bp_bit_count_ = 0;
2699 std::vector<TopCandidate> top_sparse_candidates_;
2700 std::size_t top_block_size_ = kMinTopSparseBlockSize;
2701 std::size_t top_block_count_ = 0;
2702 std::size_t top_sparse_levels_ = 0;
2703 std::optional<RankSelectSupport<>> bp_index_;
2704 BpDepthRmq bp_depth_rmq_;