Pixie
Loading...
Searching...
No Matches
cartesian_rmm.h
1#pragma once
2
69
70#include <pixie/memory_usage.h>
71#include <pixie/rmm/btree.h>
72#include <pixie/rmq.h>
73#include <pixie/rmq/utils/succinct_monotone_stack.h>
74
75#include <cstddef>
76#include <cstdint>
77#include <functional>
78#include <limits>
79#include <optional>
80#include <span>
81#include <stdexcept>
82#include <type_traits>
83#include <utility>
84#include <vector>
85
86namespace pixie::rmq {
87
88namespace detail {
89
102template <class Index = std::size_t,
103 std::size_t HighCacheLines = 4,
104 std::size_t LowFanout = 32>
105class RmMPlusMinusOne {
106 public:
107 static_assert(std::is_unsigned_v<Index>,
108 "RmMPlusMinusOne index type must be unsigned");
109
110 static constexpr std::size_t npos = std::numeric_limits<std::size_t>::max();
111 static constexpr Index invalid_index = std::numeric_limits<Index>::max();
112 static constexpr std::size_t kBlockSize =
113 pixie::experimental::RmMBTree<HighCacheLines, LowFanout>::kBlockBits;
114
115 RmMPlusMinusOne() = default;
116
125 RmMPlusMinusOne(std::span<const std::uint64_t> bits,
126 std::size_t depth_count) {
127 build(bits, depth_count);
128 }
129
136 RmMPlusMinusOne(std::span<const std::uint64_t> bits,
137 std::size_t depth_count,
138 std::size_t one_count) {
139 build(bits, depth_count, one_count);
140 }
141
145 void build(std::span<const std::uint64_t> bits, std::size_t depth_count) {
146 build(bits, depth_count, std::nullopt);
147 }
148
152 void build(std::span<const std::uint64_t> bits,
153 std::size_t depth_count,
154 std::optional<std::size_t> one_count) {
155 words_ = bits;
156 depth_count_ = depth_count;
157 rmm_ = RmM();
158
159 if (depth_count_ == 0) {
160 return;
161 }
162 if (depth_count_ > static_cast<std::size_t>(invalid_index)) {
163 throw std::length_error("RMQ rmM btree index type is too small");
164 }
165 rmm_ = RmM(words_, depth_count_ - 1,
166 RankSelectSupport<>::SelectSupport::kSelect0, one_count);
167 }
168
172 std::size_t size() const { return depth_count_; }
173
177 bool empty() const { return depth_count_ == 0; }
178
185 std::size_t arg_min(std::size_t left, std::size_t right) const {
186 if (left >= right || right > depth_count_) {
187 return npos;
188 }
189 if (right == left + 1) {
190 return left;
191 }
192
193 const std::size_t bit_left = left;
194 const std::size_t bit_right = right - 2;
195 const auto minimum_after_left =
196 rmm_.range_min_query_result(bit_left, bit_right);
197 if (minimum_after_left.value >= 0) {
198 return left;
199 }
200
201 const std::size_t bit_position = minimum_after_left.position;
202 return bit_position == RmM::npos ? npos : bit_position + 1;
203 }
204
208 std::size_t select0(std::size_t rank) const { return rmm_.select0(rank); }
209
213 std::size_t rank0(std::size_t end_position) const {
214 return rmm_.rank0(end_position);
215 }
216
223 std::size_t memory_usage_bytes() const {
224 return sizeof(*this) + pixie::nested_owned_memory_bytes(rmm_);
225 }
226
227 private:
229
230 std::span<const std::uint64_t> words_;
231 std::size_t depth_count_ = 0;
232 RmM rmm_;
233};
234
235} // namespace detail
236
250template <class T,
251 class Compare = std::less<T>,
252 class Index = std::size_t,
253 std::size_t HighCacheLines = 4,
254 std::size_t LowFanout = 32>
255class CartesianRmM
256 : public RmqBase<CartesianRmM<T, Compare, Index, HighCacheLines, LowFanout>,
257 T> {
258 public:
259 static_assert(std::is_unsigned_v<Index>,
260 "CartesianRmM index type must be unsigned");
261
262 static constexpr std::size_t npos =
264 T>::npos;
265 static constexpr Index invalid_index = std::numeric_limits<Index>::max();
266
267 CartesianRmM() = default;
268
275 explicit CartesianRmM(std::span<const T> values, Compare compare = Compare())
276 : values_(values), compare_(compare) {
277 build();
278 }
279
280 CartesianRmM(const CartesianRmM& other)
281 : values_(other.values_),
282 compare_(other.compare_),
283 bp_bits_(other.bp_bits_),
284 bp_bit_count_(other.bp_bit_count_) {
285 reset_bp_index();
286 }
287
288 CartesianRmM& operator=(const CartesianRmM& other) {
289 if (this == &other) {
290 return *this;
291 }
292 values_ = other.values_;
293 compare_ = other.compare_;
294 bp_bits_ = other.bp_bits_;
295 bp_bit_count_ = other.bp_bit_count_;
296 reset_bp_index();
297 return *this;
298 }
299
300 CartesianRmM(CartesianRmM&& other) noexcept
301 : values_(other.values_),
302 compare_(std::move(other.compare_)),
303 bp_bits_(std::move(other.bp_bits_)),
304 bp_bit_count_(other.bp_bit_count_) {
305 other.values_ = std::span<const T>();
306 other.bp_bit_count_ = 0;
307 reset_bp_index();
308 }
309
310 CartesianRmM& operator=(CartesianRmM&& other) noexcept {
311 if (this == &other) {
312 return *this;
313 }
314 values_ = other.values_;
315 compare_ = std::move(other.compare_);
316 bp_bits_ = std::move(other.bp_bits_);
317 bp_bit_count_ = other.bp_bit_count_;
318 other.values_ = std::span<const T>();
319 other.bp_bit_count_ = 0;
320 reset_bp_index();
321 return *this;
322 }
323
327 std::size_t size_impl() const { return values_.size(); }
328
332 T value_at_impl(std::size_t position) const { return values_[position]; }
333
337 std::size_t arg_min_impl(std::size_t left, std::size_t right) const {
338 if (left >= right || right > values_.size()) {
339 return npos;
340 }
341 const std::size_t first_close = bp_support_.select0(left + 1);
342 const std::size_t last_close = bp_support_.select0(right);
343 if (first_close == BpSupport::npos || last_close == BpSupport::npos ||
344 first_close > last_close) {
345 return npos;
346 }
347
348 const std::size_t shifted_min =
349 bp_support_.arg_min(first_close + 1, last_close + 2);
350 if (shifted_min == npos || shifted_min == 0) {
351 return npos;
352 }
353 const std::size_t answer = bp_support_.rank0(shifted_min) - 1;
354 return answer < values_.size() ? answer : npos;
355 }
356
360 std::size_t bp_bit_count() const { return bp_bit_count_; }
361
365 std::span<const std::uint64_t> bp_words() const { return bp_bits_; }
366
374 std::size_t memory_usage_bytes_impl() const {
375 return sizeof(*this) + pixie::vector_capacity_bytes(bp_bits_) +
376 pixie::nested_owned_memory_bytes(bp_support_);
377 }
378
379 private:
381
382 void build() {
383 bp_bits_.clear();
384 bp_bit_count_ = 0;
385 bp_support_ = BpSupport();
386
387 if (values_.empty()) {
388 return;
389 }
390 if (values_.size() > (static_cast<std::size_t>(invalid_index) - 1) / 2) {
391 throw std::length_error("Cartesian rmM RMQ index type is too small");
392 }
393
394 bp_bit_count_ = 2 * values_.size();
395 bp_bits_.assign((bp_bit_count_ + 63) / 64, 0);
396 build_bp_bits();
397 reset_bp_index();
398 }
399
400 void build_bp_bits() {
401 utils::SuccinctIncreasingStack stack(values_.size());
402 std::size_t write_position = bp_bit_count_;
403 std::vector<std::uint64_t>& words = bp_bits_;
404 const auto prepend_open = [&]() {
405 --write_position;
406 words[write_position >> 6] |= std::uint64_t{1} << (write_position & 63);
407 };
408
409 for (std::size_t i = values_.size(); i-- > 0;) {
410 while (!stack.empty() &&
411 !compare_(values_[stack_index(stack.top())], values_[i])) {
412 stack.pop();
413 prepend_open();
414 }
415 stack.push(stack_key(i));
416 --write_position;
417 }
418
419 while (write_position != 0) {
420 prepend_open();
421 }
422 }
423
428 std::size_t stack_key(std::size_t value_index) const {
429 return values_.size() - 1 - value_index;
430 }
431
436 std::size_t stack_index(std::size_t key) const {
437 return values_.size() - 1 - key;
438 }
439
440 void reset_bp_index() {
441 bp_support_ = BpSupport();
442 if (bp_bit_count_ == 0) {
443 return;
444 }
445 bp_support_ = BpSupport(std::span<const std::uint64_t>(bp_bits_),
446 bp_bit_count_ + 1, values_.size());
447 }
448
449 std::span<const T> values_;
450 Compare compare_;
451 std::vector<std::uint64_t> bp_bits_;
452 std::size_t bp_bit_count_ = 0;
453 BpSupport bp_support_;
454};
455
456} // namespace pixie::rmq
Cache-aligned btree implementation of the range min-max index.
Definition btree.h:83
Ferrada-Navarro Cartesian-tree RMQ using rmM support.
Definition cartesian_rmm.h:257
std::size_t memory_usage_bytes_impl() const
Return owned auxiliary memory usage in bytes.
Definition cartesian_rmm.h:374
std::size_t size_impl() const
Return the number of indexed values.
Definition cartesian_rmm.h:327
std::span< const std::uint64_t > bp_words() const
Return the packed BP words used by the RMQ encoding.
Definition cartesian_rmm.h:365
std::size_t bp_bit_count() const
Return the number of BP bits in the Cartesian-tree RMQ encoding.
Definition cartesian_rmm.h:360
CartesianRmM(std::span< const T > values, Compare compare=Compare())
Build a Cartesian-tree RMQ index over values.
Definition cartesian_rmm.h:275
std::size_t arg_min_impl(std::size_t left, std::size_t right) const
Return the first minimum position in [left, right).
Definition cartesian_rmm.h:337
T value_at_impl(std::size_t position) const
Return the value at an indexed position.
Definition cartesian_rmm.h:332
CRTP facade for static range-minimum-query indexes.
Definition rmq.h:28
Depth RMQ adapter over the experimental rmM btree.
Definition cartesian_rmm.h:105
RmMPlusMinusOne(std::span< const std::uint64_t > bits, std::size_t depth_count)
Build an rmM-backed ±1 RMQ over external packed delta bits.
Definition cartesian_rmm.h:125
void build(std::span< const std::uint64_t > bits, std::size_t depth_count)
Rebuild this adapter over external packed delta bits.
Definition cartesian_rmm.h:145
bool empty() const
Whether the indexed depth sequence is empty.
Definition cartesian_rmm.h:177
std::size_t memory_usage_bytes() const
Return owned auxiliary memory usage in bytes.
Definition cartesian_rmm.h:223
std::size_t size() const
Return the number of indexed depth positions.
Definition cartesian_rmm.h:172
std::size_t arg_min(std::size_t left, std::size_t right) const
Return the first minimum depth position in [left, right).
Definition cartesian_rmm.h:185
void build(std::span< const std::uint64_t > bits, std::size_t depth_count, std::optional< std::size_t > one_count)
Rebuild this adapter with an optional exact count of +1 delta bits.
Definition cartesian_rmm.h:152
std::size_t rank0(std::size_t end_position) const
Count closing parentheses in the prefix [0, end_position).
Definition cartesian_rmm.h:213
RmMPlusMinusOne(std::span< const std::uint64_t > bits, std::size_t depth_count, std::size_t one_count)
Build with an exact count of +1 delta bits.
Definition cartesian_rmm.h:136
std::size_t select0(std::size_t rank) const
Return the one-based rank-th closing parenthesis position.
Definition cartesian_rmm.h:208
Definition rmq.h:15
Common interface for static range-minimum-query indexes.