Bit vector  0.9
Fast and space efficient dynamic bit vector library.
node.hpp
1 #ifndef BV_NODE_HPP
2 #define BV_NODE_HPP
3 
4 #include <cassert>
5 #include <cstdint>
6 #include <cstring>
7 
8 #ifndef CACHE_LINE
9 // Apparently the most common cache line size is 64.
10 #define CACHE_LINE 64
11 #endif
12 
13 #include "branch_selection.hpp"
14 
15 namespace bv {
16 
64 template <class leaf_type, class dtype, uint64_t leaf_size, uint8_t branches>
65 class node {
66  protected:
71  uint8_t meta_data_;
75  uint8_t child_count_; // Bad word alignment. betewen 16 and 48 dead bits...
85  void* children_[branches];
86 
87  static_assert(leaf_size >= 256, "leaf size needs to be a at least 256");
88  static_assert((leaf_size % 128) == 0,
89  "leaf size needs to be divisible by 128");
90  static_assert(leaf_size < 0xffffff, "leaf size must fit in 24 bits");
91  static_assert((branches == 8) || (branches == 16) || (branches == 32) ||
92  (branches == 64) || (branches == 128),
93  "branching factor needs to be a reasonable power of 2");
94 
95  public:
99  node() {
100  meta_data_ = 0;
101  child_count_ = 0;
104  }
105 
106  template <class qds>
107  void generate_query_structure(qds* qs) {
108  if (has_leaves()) {
109  leaf_type** children = reinterpret_cast<leaf_type**>(children_);
110  for (size_t i = 0; i < child_count_; i++) {
111  children[i]->commit();
112  qs->append(children[i]);
113  }
114  } else {
115  node** children = reinterpret_cast<node**>(children_);
116  for (size_t i = 0; i < child_count_; i++) {
117  children[i]->generate_query_structure(qs);
118  }
119  }
120  }
121 
131  void has_leaves(bool leaves) {
132  meta_data_ = leaves ? meta_data_ | 0b10000000 : meta_data_ & 0b01111111;
133  }
134 
140  bool has_leaves() const { return meta_data_ >> 7; }
141 
150  bool at(dtype index) const {
151  uint8_t child_index = child_sizes_.find(index + 1);
152  index -= child_index != 0 ? child_sizes_.get(child_index - 1) : 0;
153  if (has_leaves()) {
154  [[unlikely]] return reinterpret_cast<leaf_type*>(
155  children_[child_index])
156  ->at(index);
157  } else {
158  return reinterpret_cast<node*>(children_[child_index])->at(index);
159  }
160  }
161 
174  int set(dtype index, bool v) {
175  uint8_t child_index = child_sizes_.find(index + 1);
176  index -= child_index != 0 ? child_sizes_.get(child_index - 1) : 0;
177  dtype change = 0;
178  if (has_leaves()) {
179  leaf_type* child =
180  reinterpret_cast<leaf_type*>(children_[child_index]);
181  [[unlikely]] change = child->set(index, v);
182  } else {
183  node* child = reinterpret_cast<node*>(children_[child_index]);
184  change = child->set(index, v);
185  }
186  uint8_t c_count = child_count_;
187  child_sums_.increment(child_index, c_count, change);
188  return change;
189  }
190 
202  dtype rank(dtype index) const {
203  uint8_t child_index = child_sizes_.find(index);
204  dtype res = 0;
205  if (child_index != 0) {
206  res = child_sums_.get(child_index - 1);
207  [[likely]] index -= child_sizes_.get(child_index - 1);
208  }
209  if (has_leaves()) {
210  leaf_type* child =
211  reinterpret_cast<leaf_type*>(children_[child_index]);
212  [[unlikely]] return res + child->rank(index);
213  } else {
214  node* child = reinterpret_cast<node*>(children_[child_index]);
215  return res + child->rank(index);
216  }
217  }
218 
230  dtype select(dtype count) const {
231  uint8_t child_index = child_sums_.find(count);
232  dtype res = 0;
233  if (child_index != 0) {
234  res = child_sizes_.get(child_index - 1);
235  [[likely]] count -= child_sums_.get(child_index - 1);
236  }
237  if (has_leaves()) {
238  leaf_type* child =
239  reinterpret_cast<leaf_type*>(children_[child_index]);
240  [[unlikely]] return res + child->select(count);
241  } else {
242  node* child = reinterpret_cast<node*>(children_[child_index]);
243  return res + child->select(count);
244  }
245  }
246 
257  template <class allocator>
258  void deallocate(allocator* alloc) {
259  if (has_leaves()) {
260  leaf_type** children = reinterpret_cast<leaf_type**>(children_);
261  for (uint8_t i = 0; i < child_count_; i++) {
262  leaf_type* l = children[i];
263  alloc->deallocate_leaf(l);
264  }
265  } else {
266  node** children = reinterpret_cast<node**>(children_);
267  for (uint8_t i = 0; i < child_count_; i++) {
268  node* n = children[i];
269  n->deallocate(alloc);
270  alloc->deallocate_node(n);
271  }
272  }
273  }
274 
280  uint8_t child_count() const { return child_count_; }
281 
289  void** children() { return children_; }
290 
299 
308 
314  dtype size() const {
315  return child_count_ > 0 ? child_sizes_.get(child_count_ - 1) : 0;
316  }
317 
323  dtype p_sum() const {
324  return child_count_ > 0 ? child_sums_.get(child_count_ - 1) : 0;
325  }
326 
336  template <class child>
337  void append_child(child* new_child) {
338  child_sizes_.append(child_count_, new_child->size());
339  child_sums_.append(child_count_, new_child->p_sum());
340  children_[child_count_] = new_child;
341  child_count_++;
342  }
343 
353  void* child(uint8_t i) { return children_[i]; }
354 
368  template <class allocator>
369  void insert(dtype index, bool value, allocator* alloc) {
370  if (has_leaves()) {
371  leaf_insert(index, value, alloc);
372  } else {
373  [[likely]] node_insert(index, value, alloc);
374  }
375  }
376 
388  template <class allocator>
389  bool remove(dtype index, allocator* alloc) {
390  if (has_leaves()) {
391  return leaf_remove(index, alloc);
392  } else {
393  [[likely]] return node_remove(index, alloc);
394  }
395  }
396 
406  void clear_first(uint8_t elems) {
409  for (uint8_t i = 0; i < child_count_ - elems; i++) {
410  children_[i] = children_[i + elems];
411  }
412  child_count_ -= elems;
413  }
414 
425  void transfer_append(node* other, uint8_t elems) {
426  void** o_children = other->children();
427  branching* o_sizes = other->child_sizes();
428  branching* o_sums = other->child_sums();
429  uint8_t local_index = child_count_;
430  for (uint8_t i = 0; i < elems; i++) {
431  children_[local_index] = o_children[i];
432  local_index++;
433  }
434  child_sizes_.append(elems, child_count_, o_sizes);
435  child_sums_.append(elems, child_count_, o_sums);
436  child_count_ += elems;
437  other->clear_first(elems);
438  }
439 
449  void clear_last(uint8_t elems) {
452  child_count_ -= elems;
453  }
454 
466  void transfer_prepend(node* other, uint8_t elems) {
467  void** o_children = other->children();
468  branching* o_sizes = other->child_sizes();
469  branching* o_sums = other->child_sums();
470  uint8_t o_size = other->child_count();
471  memmove(children_ + elems, children_, child_count_ * sizeof(void*));
472  for (uint8_t i = 0; i < elems; i++) {
473  children_[i] = o_children[o_size - elems + i];
474  }
475  child_sizes_.prepend(elems, child_count_, o_size, o_sizes);
476  child_sums_.prepend(elems, child_count_, o_size, o_sums);
477  child_count_ += elems;
478  other->clear_last(elems);
479  }
480 
490  void append_all(node* other) {
491  void** o_children = other->children();
492  branching* o_sizes = other->child_sizes();
493  branching* o_sums = other->child_sums();
494  uint8_t o_size = other->child_count();
495  for (uint8_t i = 0; i < o_size; i++) {
496  children_[child_count_ + i] = o_children[i];
497  }
498  child_sizes_.append(o_size, child_count_, o_sizes);
499  child_sums_.append(o_size, child_count_, o_sums);
500  child_count_ += o_size;
501  }
502 
511  uint64_t bits_size() const {
512  uint64_t ret = sizeof(node) * 8;
513  if (has_leaves()) {
514  leaf_type* const* children =
515  reinterpret_cast<leaf_type* const*>(children_);
516  for (uint8_t i = 0; i < child_count_; i++) {
517  ret += children[i]->bits_size();
518  }
519  } else {
520  node* const* children = reinterpret_cast<node* const*>(children_);
521  for (uint8_t i = 0; i < child_count_; i++) {
522  ret += children[i]->bits_size();
523  }
524  }
525  return ret;
526  }
527 
528  void flush() {
529  if (has_leaves()) {
530  leaf_type** children = reinterpret_cast<leaf_type**>(children_);
531  for (uint8_t i = 0; i < child_count_; i++) {
532  children[i]->commit();
533  }
534  } else {
535  node** children = reinterpret_cast<node**>(children_);
536  for (uint8_t i = 0; i < child_count_; i++) {
537  children[i]->flush();
538  }
539  }
540  }
541 
563  uint64_t validate() const {
564  uint64_t ret = 1;
565  uint64_t child_s_sum = 0;
566  uint64_t child_p_sum = 0;
567  if (has_leaves()) {
568  leaf_type* const* children =
569  reinterpret_cast<leaf_type* const*>(children_);
570  for (uint8_t i = 0; i < child_count_; i++) {
571  uint64_t child_size = children[i]->size();
572  assert(child_size >= leaf_size / 3);
573  child_s_sum += child_size;
574  assert(child_sizes_.get(i) == child_s_sum);
575  child_p_sum += children[i]->p_sum();
576  assert(child_sums_.get(i) == child_p_sum);
577  assert(children[i]->capacity() * WORD_BITS <= leaf_size);
578  assert(children[i]->size() <= leaf_size);
579  ret += children[i]->validate();
580  }
581  } else {
582  node* const* children = reinterpret_cast<node* const*>(children_);
583  for (uint8_t i = 0; i < child_count_; i++) {
584  uint64_t child_size = children[i]->size();
585  assert(child_size >= branches / 3);
586  child_s_sum += child_size;
587  assert(child_sizes_.get(i) == child_s_sum);
588  child_p_sum += children[i]->p_sum();
589  assert(child_sums_.get(i) == child_p_sum);
590  ret += children[i]->validate();
591  }
592  }
593  return ret;
594  }
595 
606  void print(bool internal_only) const {
607  std::cout << "{\n\"type\": \"node\",\n"
608  << "\"has_leaves\": " << has_leaves() << ",\n"
609  << "\"child_count\": " << int(child_count_) << ",\n"
610  << "\"size\": " << size() << ",\n"
611  << "\"child_sizes\": [";
612  for (uint8_t i = 0; i < branches; i++) {
613  std::cout << child_sizes_.get(i);
614  if (i != branches - 1) {
615  std::cout << ", ";
616  }
617  }
618  std::cout << "],\n"
619  << "\"p_sum\": " << p_sum() << ",\n"
620  << "\"child_sums\": [";
621  for (uint8_t i = 0; i < branches; i++) {
622  std::cout << child_sums_.get(i);
623  if (i != branches - 1) {
624  std::cout << ", ";
625  }
626  }
627  std::cout << "],\n"
628  << "\"children\": [\n";
629  if (has_leaves()) {
630  leaf_type* const* children =
631  reinterpret_cast<leaf_type* const*>(children_);
632  for (uint8_t i = 0; i < child_count_; i++) {
633  children[i]->print(internal_only);
634  if (i != child_count_ - 1) {
635  std::cout << ",";
636  }
637  std::cout << "\n";
638  }
639  } else {
640  node* const* children = reinterpret_cast<node* const*>(children_);
641  for (uint8_t i = 0; i < child_count_; i++) {
642  children[i]->print(internal_only);
643  if (i != child_count_ - 1) {
644  std::cout << ",";
645  }
646  std::cout << "\n";
647  }
648  }
649  std::cout << "]}";
650  }
651 
652  protected:
671  template <class allocator>
672  void rebalance_leaf(uint8_t index, leaf_type* leaf, allocator* alloc) {
673  // Number of leaves that can fit in the "left" sibling (with potential
674  // reallocation).
675  uint32_t l_cap = 0;
676  if (index > 0) {
677  l_cap = child_sizes_.get(index - 1);
678  l_cap -= index > 1 ? child_sizes_.get(index - 2) : 0;
679  [[likely]] l_cap = leaf_size - l_cap;
680  }
681  // Number of leaves that can fir in the "right" sibling (with potential
682  // reallocation).
683  uint32_t r_cap = 0;
684  if (index < child_count_ - 1) {
685  r_cap = child_sizes_.get(index + 1);
686  r_cap -= child_sizes_.get(index);
687  [[likely]] r_cap = leaf_size - r_cap;
688  }
689  if (l_cap < 2 * leaf_size / 9 && r_cap < 2 * leaf_size / 9) {
690  // Rebalancing without creating a new leaf is impossible
691  // (impracitcal).
692  leaf_type* a_child;
693  leaf_type* b_child;
694  leaf_type* new_child;
695  if (index == 0) {
696  // If the full leaf is the first child, a new leaf is created
697  // between indexes 0 and 1.
698  dtype n_elem = child_sizes_.get(1) / 3;
699  dtype n_cap = (n_elem + 2 * WORD_BITS) / WORD_BITS;
700  n_cap += n_cap % 2;
701  n_cap = n_cap * WORD_BITS > leaf_size ? leaf_size / WORD_BITS
702  : n_cap;
703  a_child = reinterpret_cast<leaf_type*>(children_[0]);
704  new_child = alloc->template allocate_leaf<leaf_type>(n_cap);
705  b_child = reinterpret_cast<leaf_type*>(children_[1]);
706  new_child->transfer_append(b_child, b_child->size() - n_elem);
707  new_child->transfer_prepend(a_child, a_child->size() - n_elem);
708  [[unlikely]] index++;
709  } else {
710  // If the full leaf is not the first child, a new leaf is
711  // created to the left of the full leaf.
712  a_child = reinterpret_cast<leaf_type*>(children_[index - 1]);
713  b_child = reinterpret_cast<leaf_type*>(children_[index]);
714  dtype n_elem = (a_child->size() + b_child->size()) / 3;
715  dtype n_cap = (n_elem + 2 * WORD_BITS) / WORD_BITS;
716  n_cap += n_cap % 2;
717  n_cap = n_cap * WORD_BITS > leaf_size ? leaf_size / WORD_BITS
718  : n_cap;
719  new_child = alloc->template allocate_leaf<leaf_type>(n_cap);
720  new_child->transfer_append(b_child, b_child->size() - n_elem);
721  new_child->transfer_prepend(a_child, a_child->size() - n_elem);
722  }
723  // Update cumulative sizes and sums.
724  for (uint8_t i = child_count_; i > index; i--) {
725  children_[i] = children_[i - 1];
726  }
727  children_[index] = new_child;
728  child_sizes_.insert(index, child_count_, a_child->size(),
729  new_child->size());
730  child_sums_.insert(index, child_count_, a_child->p_sum(),
731  new_child->p_sum());
732  [[unlikely]] child_count_++;
733  } else if (r_cap > l_cap) {
734  // Right sibling has more space than the left sibling. Move elements
735  // to right sibling
736  leaf_type* sibling =
737  reinterpret_cast<leaf_type*>(children_[index + 1]);
738  uint32_t n_size = sibling->size() + r_cap / 2;
739  if (sibling->capacity() * WORD_BITS < n_size) {
740  n_size = n_size / WORD_BITS + 1;
741  n_size += n_size % 2;
742  n_size = n_size * WORD_BITS <= leaf_size
743  ? n_size
744  : leaf_size / WORD_BITS;
745  children_[index + 1] = alloc->reallocate_leaf(
746  sibling, sibling->capacity(), n_size);
747  sibling = reinterpret_cast<leaf_type*>(children_[index + 1]);
748  }
749  sibling->transfer_prepend(leaf, r_cap / 2);
751  index, index != 0 ? child_sizes_.get(index - 1) + leaf->size()
752  : leaf->size());
754  index, index != 0 ? child_sums_.get(index - 1) + leaf->p_sum()
755  : leaf->p_sum());
756  } else {
757  // Left sibling has more space than the right sibling. Move elements
758  // to the left sibling.
759  leaf_type* sibling =
760  reinterpret_cast<leaf_type*>(children_[index - 1]);
761  uint32_t n_size = sibling->size() + l_cap / 2;
762  if (sibling->capacity() * WORD_BITS < n_size) {
763  n_size = n_size / WORD_BITS + 1;
764  n_size += n_size % 2;
765  n_size = n_size * WORD_BITS <= leaf_size
766  ? n_size
767  : leaf_size / WORD_BITS;
768  children_[index - 1] = alloc->reallocate_leaf(
769  sibling, sibling->capacity(), n_size);
770  sibling = reinterpret_cast<leaf_type*>(children_[index - 1]);
771  }
772  sibling->transfer_append(leaf, l_cap / 2);
773  child_sizes_.set(index - 1,
774  index > 1
775  ? child_sizes_.get(index - 2) + sibling->size()
776  : sibling->size());
777  child_sums_.set(index - 1, index > 1 ? child_sums_.get(index - 2) +
778  sibling->p_sum()
779  : sibling->p_sum());
780  [[likely]] (void(0));
781  }
782  }
783 
795  template <class allocator>
796  void leaf_insert(dtype index, bool value, allocator* alloc) {
797  uint8_t child_index = child_sizes_.find(index);
798  leaf_type* child = reinterpret_cast<leaf_type*>(children_[child_index]);
799 #ifdef DEBUG
800  if (child->size() > leaf_size) {
801  std::cerr << child->size() << " > " << leaf_size << "\n";
802  std::cerr << "invalid leaf size" << std::endl;
803  assert(child->size() <= leaf_size);
804  }
805 #endif
806  if (child->need_realloc()) {
807  if (child->size() >= leaf_size) {
808  rebalance_leaf(child_index, child, alloc);
809  } else {
810  dtype cap = child->capacity();
811  children_[child_index] =
812  alloc->reallocate_leaf(child, cap, cap + 2);
813  }
814  return leaf_insert(index, value, alloc);
815  }
816  if (child_index != 0) {
817  [[likely]] index -= child_sizes_.get(child_index - 1);
818  }
819  child_sizes_.increment(child_index, child_count_, 1u);
820  child_sums_.increment(child_index, child_count_, value);
821  child->insert(index, value);
822  }
823 
841  template <class allocator>
842  void rebalance_node(uint8_t index, allocator* alloc) {
843  // Number of elements that can be added to the left sibling.
844  uint32_t l_cap = 0;
845  if (index > 0) {
846  [[likely]] l_cap =
847  branches -
848  reinterpret_cast<node*>(children_[index - 1])->child_count();
849  }
850  // Number of elements that can be aded to the right sibling.
851  uint32_t r_cap = 0;
852  if (index < child_count_ - 1) {
853  [[likely]] r_cap =
854  branches -
855  reinterpret_cast<node*>(children_[index + 1])->child_count();
856  }
857  node* a_node;
858  node* b_node;
859  if (l_cap <= 1 && r_cap <= 1) {
860  // There is no room in either sibling.
861  if (index == 0) {
862  a_node = reinterpret_cast<node*>(children_[0]);
863  b_node = reinterpret_cast<node*>(children_[1]);
864  [[unlikely]] index++;
865  } else {
866  a_node = reinterpret_cast<node*>(children_[index - 1]);
867  b_node = reinterpret_cast<node*>(children_[index]);
868  }
869  node* new_child = alloc->template allocate_node<node>();
870  new_child->has_leaves(a_node->has_leaves());
871  new_child->transfer_append(b_node, branches / 3);
872  new_child->transfer_prepend(a_node, branches / 3);
873  for (size_t i = child_count_; i > index; i--) {
874  children_[i] = children_[i - 1];
875  }
876  child_sizes_.insert(index, child_count_, a_node->size(),
877  new_child->size());
878  child_sums_.insert(index, child_count_, a_node->p_sum(),
879  new_child->p_sum());
880  children_[index] = new_child;
881  child_count_++;
882  [[unlikely]] return;
883  } else if (l_cap > r_cap) {
884  // There is more room in the left sibling.
885  a_node = reinterpret_cast<node*>(children_[index - 1]);
886  b_node = reinterpret_cast<node*>(children_[index]);
887  a_node->transfer_append(b_node, l_cap / 2);
888  index--;
889  } else {
890  // There is more room in the right sibling.
891  a_node = reinterpret_cast<node*>(children_[index]);
892  b_node = reinterpret_cast<node*>(children_[index + 1]);
893  b_node->transfer_prepend(a_node, r_cap / 2);
894  }
895  // Fix cumulative sums and sizes.
896  if (index == 0) {
897  child_sizes_.set(0, a_node->size());
898  [[unlikely]] child_sums_.set(0, a_node->p_sum());
899  } else {
900  child_sizes_.set(index,
901  child_sizes_.get(index - 1) + a_node->size());
902  child_sums_.set(index,
903  child_sums_.get(index - 1) + a_node->p_sum());
904  }
905  }
906 
918  template <class allocator>
919  void node_insert(dtype index, bool value, allocator* alloc) {
920  uint8_t child_index = child_sizes_.find(index);
921  node* child = reinterpret_cast<node*>(children_[child_index]);
922 #ifdef DEBUG
923  if (child_index >= child_count_) {
924  std::cout << int(child_index) << " >= " << int(child_count_)
925  << std::endl;
926  assert(child_index < child_count_);
927  }
928 #endif
929  if (child->child_count() == branches) {
930  rebalance_node(child_index, alloc);
931  child_index = child_sizes_.find(index);
932  [[unlikely]] child =
933  reinterpret_cast<node*>(children_[child_index]);
934  }
935  if (child_index != 0) {
936  [[likely]] index -= child_sizes_.get(child_index - 1);
937  }
938  child_sizes_.increment(child_index, child_count_, 1u);
939  child_sums_.increment(child_index, child_count_, value);
940  child->insert(index, value, alloc);
941  }
942 
958  template <class allocator>
959  void rebalance_leaves_right(leaf_type* a, leaf_type* b, allocator* alloc) {
960  dtype a_cap = a->capacity();
961  dtype addition = (b->size() - leaf_size / 3) / 2;
962  if (a_cap * WORD_BITS < a->size() + addition) {
963  dtype n_cap = 1 + (a->size() + addition) / WORD_BITS;
964  n_cap += n_cap % 2;
965  n_cap =
966  n_cap * WORD_BITS <= leaf_size ? n_cap : leaf_size / WORD_BITS;
967  a = alloc->reallocate_leaf(a, a_cap, n_cap);
968  children_[0] = a;
969  }
970  a->transfer_append(b, addition);
971  child_sizes_.set(0, a->size());
972  child_sums_.set(0, a->p_sum());
973  }
974 
988  template <class allocator>
989  void rebalance_leaves_left(leaf_type* a, leaf_type* b, uint8_t idx,
990  allocator* alloc) {
991  dtype b_cap = b->capacity();
992  dtype addition = (a->size() - leaf_size / 3) / 2;
993  if (b_cap * WORD_BITS < b->size() + addition) {
994  dtype n_cap = 1 + (b->size() + addition) / WORD_BITS;
995  n_cap += n_cap % 2;
996  n_cap =
997  n_cap * WORD_BITS <= leaf_size ? n_cap : leaf_size / WORD_BITS;
998  b = alloc->reallocate_leaf(b, b_cap, n_cap);
999  children_[idx + 1] = b;
1000  }
1001  b->transfer_prepend(a, addition);
1002  if (idx == 0) {
1003  child_sizes_.set(0, a->size());
1004  [[unlikely]] child_sums_.set(0, a->p_sum());
1005  } else {
1006  child_sizes_.set(idx, child_sizes_.get(idx - 1) + a->size());
1007  child_sums_.set(idx, child_sums_.get(idx - 1) + a->p_sum());
1008  }
1009  }
1010 
1025  template <class allocator>
1026  void merge_leaves(leaf_type* a, leaf_type* b, uint8_t idx,
1027  allocator* alloc) {
1028  dtype a_cap = a->capacity();
1029  if (a_cap * WORD_BITS < a->size() + b->size()) {
1030  dtype n_cap = 1 + (a->size() + b->size()) / WORD_BITS;
1031  n_cap += n_cap % 2;
1032  n_cap =
1033  n_cap * WORD_BITS <= leaf_size ? n_cap : leaf_size / WORD_BITS;
1034  a = alloc->reallocate_leaf(a, a_cap, n_cap);
1035  }
1036  a->append_all(b);
1037  alloc->deallocate_leaf(b);
1038  for (uint8_t i = idx; i < child_count_ - 1; i++) {
1039  children_[i] = children_[i + 1];
1040  }
1041  children_[idx] = a;
1044  child_count_--;
1045  }
1046 
1060  template <class allocator>
1061  bool leaf_remove(dtype index, allocator* alloc) {
1062  uint8_t child_index = child_sizes_.find(index + 1);
1063  leaf_type* child = reinterpret_cast<leaf_type*>(children_[child_index]);
1064  if (child->size() <= leaf_size / 3) {
1065  if (child_index == 0) {
1066  leaf_type* sibling = reinterpret_cast<leaf_type*>(children_[1]);
1067  if (sibling->size() > leaf_size * 5 / 9) {
1068  rebalance_leaves_right(child, sibling, alloc);
1069  } else {
1070  merge_leaves(child, sibling, 0, alloc);
1071  }
1072  [[unlikely]] ((void)0);
1073  } else {
1074  leaf_type* sibling =
1075  reinterpret_cast<leaf_type*>(children_[child_index - 1]);
1076  if (sibling->size() > leaf_size * 5 / 9) {
1077  rebalance_leaves_left(sibling, child, child_index - 1,
1078  alloc);
1079  } else {
1080  merge_leaves(sibling, child, child_index - 1, alloc);
1081  }
1082  }
1083  child_index = child_sizes_.find(index);
1084  [[unlikely]] child =
1085  reinterpret_cast<leaf_type*>(children_[child_index]);
1086  }
1087  if (child_index != 0) {
1088  [[likely]] index -= child_sizes_.get(child_index - 1);
1089  }
1090  bool value = child->remove(index);
1091  child_sizes_.increment(child_index, child_count_, -1);
1092  child_sums_.increment(child_index, child_count_, -int(value));
1093  return value;
1094  }
1095 
1112  void rebalance_nodes_right(node* a, node* b, uint8_t idx) {
1113  a->transfer_append(b, (b->child_count() - branches / 3) / 2);
1114  child_sizes_.set(idx, a->size());
1115  [[unlikely]] child_sums_.set(idx, a->p_sum());
1116  }
1117 
1132  void rebalance_nodes_left(node* a, node* b, uint8_t idx) {
1133  b->transfer_prepend(a, (a->child_count() - branches / 3) / 2);
1134  if (idx == 0) {
1135  child_sizes_.set(0, a->size());
1136  [[unlikely]] child_sums_.set(0, a->p_sum());
1137  } else {
1138  child_sizes_.set(idx, child_sizes_.get(idx - 1) + a->size());
1139  child_sums_.set(idx, child_sums_.get(idx - 1) + a->p_sum());
1140  }
1141  }
1142 
1158  template <class allocator>
1159  void merge_nodes(node* a, node* b, uint8_t idx, allocator* alloc) {
1160  a->append_all(b);
1161  alloc->deallocate_node(b);
1162  for (uint8_t i = idx; i < child_count_ - 1; i++) {
1163  children_[i] = children_[i + 1];
1164  }
1165  children_[idx] = a;
1168  child_count_--;
1169  }
1170 
1184  template <class allocator>
1185  bool node_remove(dtype index, allocator* alloc) {
1186  uint8_t child_index = child_sizes_.find(index + 1);
1187  node* child = reinterpret_cast<node*>(children_[child_index]);
1188  if (child->child_count_ <= branches / 3) {
1189  if (child_index == 0) {
1190  node* sibling = reinterpret_cast<node*>(children_[1]);
1191  if (sibling->child_count_ > branches * 5 / 9) {
1192  rebalance_nodes_right(child, sibling, 0);
1193  } else {
1194  merge_nodes(child, sibling, 0, alloc);
1195  }
1196  [[unlikely]] ((void)0);
1197  } else {
1198  node* sibling =
1199  reinterpret_cast<node*>(children_[child_index - 1]);
1200  if (sibling->child_count_ > branches * 5 / 9) {
1201  rebalance_nodes_left(sibling, child, child_index - 1);
1202  } else {
1203  merge_nodes(sibling, child, child_index - 1, alloc);
1204  }
1205  }
1206  child_index = child_sizes_.find(index + 1);
1207  [[unlikely]] child =
1208  reinterpret_cast<node*>(children_[child_index]);
1209  }
1210  if (child_index != 0) {
1211  [[likely]] index -= child_sizes_.get(child_index - 1);
1212  }
1213  bool value = child->remove(index, alloc);
1214  child_sizes_.increment(child_index, child_count_, -1);
1215  child_sums_.increment(child_index, child_count_, -int(value));
1216  return value;
1217  }
1218 };
1219 } // namespace bv
1220 #endif
bv::branchless_scan::set
void set(uint8_t index, dtype value)
Set the value at index to value.
Definition: branch_selection.hpp:67
bv::node::rebalance_nodes_right
void rebalance_nodes_right(node *a, node *b, uint8_t idx)
Transfer elements from the "right" node to the "left" node.
Definition: node.hpp:1112
bv::node::node_remove
bool node_remove(dtype index, allocator *alloc)
Removal operation when children are internal nodes.
Definition: node.hpp:1185
bv::node::set
int set(dtype index, bool v)
Set the value of the logical indexth element to v.
Definition: node.hpp:174
bv::node::deallocate
void deallocate(allocator *alloc)
Recursively deallocates all children.
Definition: node.hpp:258
bv::branchless_scan
Class providing support for cumulative sums.
Definition: branch_selection.hpp:27
bv::node::child_sizes_
branching child_sizes_
Cumulative child sizes and (~0) >> 1 for non-existing children.
Definition: node.hpp:79
bv::node::merge_nodes
void merge_nodes(node *a, node *b, uint8_t idx, allocator *alloc)
Transfer all from the "right" node to the "left" node.
Definition: node.hpp:1159
bv::node::transfer_append
void transfer_append(node *other, uint8_t elems)
Moves the fist "elems" elements from "other" to the end of "this".
Definition: node.hpp:425
bv::node::child_count_
uint8_t child_count_
Number of active children.
Definition: node.hpp:75
bv::node::clear_first
void clear_first(uint8_t elems)
Remove the first "elems" elements form this node.
Definition: node.hpp:406
bv::leaf::p_sum
uint32_t p_sum() const
Getter for p_sum_.
Definition: leaf.hpp:122
bv::node::child_sums_
branching child_sums_
Cumulative child sums and (~0) >> 1 for non-existint children.
Definition: node.hpp:84
bv::node::rank
dtype rank(dtype index) const
Counts number of one bits up to the indexth logical element.
Definition: node.hpp:202
bv::node::rebalance_leaves_left
void rebalance_leaves_left(leaf_type *a, leaf_type *b, uint8_t idx, allocator *alloc)
Transfer elements from the "left" leaf to the "right" leaf.
Definition: node.hpp:989
bv::branchless_scan::get
dtype get(uint8_t index) const
Get the cumulative sum at index.
Definition: branch_selection.hpp:54
bv::node::has_leaves
bool has_leaves() const
Get the type of children for the node.
Definition: node.hpp:140
bv::node::p_sum
dtype p_sum() const
Logical number of 1-bits stored in the subtree.
Definition: node.hpp:323
bv::node::child_sizes
branching * child_sizes()
Get pointer to the cumulative child sizes.
Definition: node.hpp:298
bv::node::children_
void * children_[branches]
pointers to leaf_type or node children.
Definition: node.hpp:85
bv::bv
simple_bv< 8, 16384, 64 > bv
Default dynamic bit vector type.
Definition: bv.hpp:94
bv::node::insert
void insert(dtype index, bool value, allocator *alloc)
Insert "value" at "index".
Definition: node.hpp:369
bv::node::children
void ** children()
Get pointer to the children of this node.
Definition: node.hpp:289
bv::branchless_scan::insert
void insert(uint8_t index, uint8_t array_size, dtype a_value, dtype b_value)
Inserts a new value into the cumulative sums.
Definition: branch_selection.hpp:106
bv::node::clear_last
void clear_last(uint8_t elems)
Remove the last "elems" elemets from the node.
Definition: node.hpp:449
bv::node::append_child
void append_child(child *new_child)
Add child to the subtree.
Definition: node.hpp:337
bv::leaf
Simple flat dynamic bit vector for use as a leaf for a dynamic b-tree bit vector.
Definition: leaf.hpp:47
bv::node::leaf_remove
bool leaf_remove(dtype index, allocator *alloc)
Removal used when the children are leaves.
Definition: node.hpp:1061
bv::node::node_insert
void node_insert(dtype index, bool value, allocator *alloc)
Insertion operation if the children are internal nodes.
Definition: node.hpp:919
bv::node::child
void * child(uint8_t i)
Get ith child of the node.
Definition: node.hpp:353
bv::node::rebalance_leaf
void rebalance_leaf(uint8_t index, leaf_type *leaf, allocator *alloc)
Ensure that there is space for insertion in the child leaves.
Definition: node.hpp:672
bv::node::print
void print(bool internal_only) const
Outputs this subtree as json.
Definition: node.hpp:606
bv::branchless_scan::append
void append(uint8_t n_elems, uint8_t array_size, branchless_scan *other)
Adds elements from other to the end of this
Definition: branch_selection.hpp:179
bv::node::has_leaves
void has_leaves(bool leaves)
Set whether the children of the node are leaves or internal nodes.
Definition: node.hpp:131
bv::branchless_scan::remove
void remove(uint8_t index, uint8_t array_size)
Removes a value from the cumulative sums.
Definition: branch_selection.hpp:127
bv::branchless_scan::clear_first
void clear_first(uint8_t n, uint8_t array_size)
Removes the first n elements from the structure.
Definition: branch_selection.hpp:144
bv::node::append_all
void append_all(node *other)
Copies all children from "other" to the end of this node.
Definition: node.hpp:490
bv::branchless_scan::clear_last
void clear_last(uint8_t n, uint8_t array_size)
Removes the last n elements from the structure.
Definition: branch_selection.hpp:162
bv::leaf::size
uint32_t size() const
Getter for size_.
Definition: leaf.hpp:124
bv::node::size
dtype size() const
Logical number of elements stored in the subtree.
Definition: node.hpp:314
bv::node::remove
bool remove(dtype index, allocator *alloc)
Remove the indexth element.
Definition: node.hpp:389
bv::node::meta_data_
uint8_t meta_data_
Bit indicating whether the nodes children are laves or nodes.
Definition: node.hpp:71
bv::node::rebalance_node
void rebalance_node(uint8_t index, allocator *alloc)
Ensure that there is space for insertion in the child nodes.
Definition: node.hpp:842
bv::node::rebalance_nodes_left
void rebalance_nodes_left(node *a, node *b, uint8_t idx)
Transfer elements from the "lef" node to the "right" node.
Definition: node.hpp:1132
bv::node::select
dtype select(dtype count) const
Calculates the index of the counttu 1-bit.
Definition: node.hpp:230
bv::node::validate
uint64_t validate() const
Check that the subtree structure is internally consistent.
Definition: node.hpp:563
bv::branchless_scan::prepend
void prepend(uint8_t n_elems, uint8_t array_size, uint8_t o_size, branchless_scan *other)
Adds elements form other to the start of this
Definition: branch_selection.hpp:214
bv::node
Internal node for use with bit vector b-tree structures.
Definition: node.hpp:65
bv::node::node
node()
Constructor.
Definition: node.hpp:99
bv::node::at
bool at(dtype index) const
Access the value of the indexth element of the logical structure.
Definition: node.hpp:150
bv::node::leaf_insert
void leaf_insert(dtype index, bool value, allocator *alloc)
Insertion operation if the children are leaves.
Definition: node.hpp:796
bv::node::rebalance_leaves_right
void rebalance_leaves_right(leaf_type *a, leaf_type *b, allocator *alloc)
Transfer elements from the "right" leaf to the "left" leaf.
Definition: node.hpp:959
bv::node::bits_size
uint64_t bits_size() const
Size of the subtree in "allocated" bits.
Definition: node.hpp:511
bv::node::transfer_prepend
void transfer_prepend(node *other, uint8_t elems)
Moves the last "elems" elements from "other" to the start of "this".
Definition: node.hpp:466
bv::branchless_scan::find
uint8_t find(dtype q) const
Find the lowest child index s.t. the cumulative sum at the index is at least q.
Definition: branch_selection.hpp:256
bv::node::child_count
uint8_t child_count() const
Get the number of children of this node.
Definition: node.hpp:280
bv::node::child_sums
branching * child_sums()
Get pointer to the cumulative child sums.
Definition: node.hpp:307
bv::node::merge_leaves
void merge_leaves(leaf_type *a, leaf_type *b, uint8_t idx, allocator *alloc)
Merge the right leaf into the left leaf.
Definition: node.hpp:1026
bv::branchless_scan::increment
void increment(uint8_t from, uint8_t array_size, T change)
Increments all values in range.
Definition: branch_selection.hpp:85