Sux
DoubleEF.hpp
Go to the documentation of this file.
1 /*
2  * Sux: Succinct data structures
3  *
4  * Copyright (C) 2019-2020 Emmanuel Esposito and Sebastiano Vigna
5  *
6  * This library is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by the Free
8  * Software Foundation; either version 3 of the License, or (at your option)
9  * any later version.
10  *
11  * This library is free software; you can redistribute it and/or modify it under
12  * the terms of the GNU General Public License as published by the Free Software
13  * Foundation; either version 3, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
17  * PARTICULAR PURPOSE. See the GNU General Public License for more details.
18  *
19  * Under Section 7 of GPL version 3, you are granted additional permissions
20  * described in the GCC Runtime Library Exception, version 3.1, as published by
21  * the Free Software Foundation.
22  *
23  * You should have received a copy of the GNU General Public License and a copy of
24  * the GCC Runtime Library Exception along with this program; see the files
25  * COPYING3 and COPYING.RUNTIME respectively. If not, see
26  * <http://www.gnu.org/licenses/>.
27  */
28 
29 #pragma once
30 
31 #include "../support/common.hpp"
32 #include "../util/Vector.hpp"
33 #include <cstdint>
34 #include <cstring>
35 #include <iostream>
36 #include <limits>
37 #include <vector>
38 
39 #ifndef LOG2Q
40 #define LOG2Q 8
41 #endif
42 
43 namespace sux::function {
44 
45 using namespace sux;
46 using namespace sux::util;
47 
54 template <util::AllocType AT = util::AllocType::MALLOC> class DoubleEF {
55  private:
56  static constexpr uint64_t log2q = LOG2Q;
57  static constexpr uint64_t q = 1 << log2q;
58  static constexpr uint64_t q_mask = q - 1;
59  static constexpr uint64_t super_q = 1 << 14;
60  static constexpr uint64_t super_q_mask = super_q - 1;
61  static constexpr uint64_t q_per_super_q = super_q / q;
62  static constexpr uint64_t super_q_size = 1 + q_per_super_q / 4;
63  Vector<uint64_t, AT> lower_bits, upper_bits_position, upper_bits_cum_keys, jump;
64  uint64_t lower_bits_mask_cum_keys, lower_bits_mask_position;
65 
66  uint64_t num_buckets, u_cum_keys, u_position;
67  uint64_t l_position, l_cum_keys;
68  int64_t cum_keys_min_delta, min_diff;
69  uint64_t bits_per_key_fixed_point;
70 
71  __inline static void set(util::Vector<uint64_t, AT> &bits, const uint64_t pos) { bits[pos / 64] |= 1ULL << pos % 64; }
72 
73  __inline static void set_bits(util::Vector<uint64_t, AT> &bits, const uint64_t start, const int width, const uint64_t value) {
74  const uint64_t mask = ((UINT64_C(1) << width) - 1) << start % 8;
75  uint64_t t;
76  memcpy(&t, (uint8_t *)&bits + start / 8, 8);
77  t = (t & ~mask) | value << start % 8;
78  memcpy((uint8_t *)&bits + start / 8, &t, 8);
79  }
80 
81  __inline size_t lower_bits_size_words() const { return ((num_buckets + 1) * (l_cum_keys + l_position) + 63) / 64 + 1; }
82 
83  __inline size_t cum_keys_size_words() const { return (num_buckets + 1 + (u_cum_keys >> l_cum_keys) + 63) / 64; }
84 
85  __inline size_t position_size_words() const { return (num_buckets + 1 + (u_position >> l_position) + 63) / 64; }
86 
87  __inline size_t jump_size_words() const {
88  size_t size = (num_buckets / super_q) * super_q_size * 2; // Whole blocks
89  if (num_buckets % super_q != 0) size += (1 + ((num_buckets % super_q + q - 1) / q + 3) / 4) * 2; // Partial block
90  return size;
91  }
92 
93  friend std::ostream &operator<<(std::ostream &os, const DoubleEF<AT> &ef) {
94  os.write((char *)&ef.num_buckets, sizeof(ef.num_buckets));
95  os.write((char *)&ef.u_cum_keys, sizeof(ef.u_cum_keys));
96  os.write((char *)&ef.u_position, sizeof(ef.u_position));
97  os.write((char *)&ef.cum_keys_min_delta, sizeof(ef.cum_keys_min_delta));
98  os.write((char *)&ef.min_diff, sizeof(ef.min_diff));
99  os.write((char *)&ef.bits_per_key_fixed_point, sizeof(ef.bits_per_key_fixed_point));
100 
101  os << ef.lower_bits;
102  os << ef.upper_bits_cum_keys;
103  os << ef.upper_bits_position;
104  os << ef.jump;
105  return os;
106  }
107 
108  friend std::istream &operator>>(std::istream &is, DoubleEF<AT> &ef) {
109  is.read((char *)&ef.num_buckets, sizeof(ef.num_buckets));
110  is.read((char *)&ef.u_cum_keys, sizeof(ef.u_cum_keys));
111  is.read((char *)&ef.u_position, sizeof(ef.u_position));
112  is.read((char *)&ef.cum_keys_min_delta, sizeof(ef.cum_keys_min_delta));
113  is.read((char *)&ef.min_diff, sizeof(ef.min_diff));
114  is.read((char *)&ef.bits_per_key_fixed_point, sizeof(ef.bits_per_key_fixed_point));
115 
116  ef.l_position = ef.u_position / (ef.num_buckets + 1) == 0 ? 0 : lambda(ef.u_position / (ef.num_buckets + 1));
117  ef.l_cum_keys = ef.u_cum_keys / (ef.num_buckets + 1) == 0 ? 0 : lambda(ef.u_cum_keys / (ef.num_buckets + 1));
118  assert(ef.l_cum_keys * 2 + ef.l_position <= 56);
119 
120  ef.lower_bits_mask_cum_keys = (UINT64_C(1) << ef.l_cum_keys) - 1;
121  ef.lower_bits_mask_position = (UINT64_C(1) << ef.l_position) - 1;
122 
123  is >> ef.lower_bits;
124  is >> ef.upper_bits_cum_keys;
125  is >> ef.upper_bits_position;
126  is >> ef.jump;
127  return is;
128  }
129 
130  public:
131  DoubleEF() {}
132 
133  DoubleEF(const std::vector<uint64_t> &cum_keys, const std::vector<uint64_t> &position) {
134  assert(cum_keys.size() == position.size());
135  num_buckets = cum_keys.size() - 1;
136 
137  bits_per_key_fixed_point = (uint64_t(1) << 20) * (position[num_buckets] / (double)cum_keys[num_buckets]);
138 
139  min_diff = std::numeric_limits<int64_t>::max() / 2;
140  cum_keys_min_delta = std::numeric_limits<int64_t>::max() / 2;
141  int64_t prev_bucket_bits = 0;
142  for (size_t i = 1; i <= num_buckets; ++i) {
143  const int64_t nkeys_delta = cum_keys[i] - cum_keys[i - 1];
144  cum_keys_min_delta = min(cum_keys_min_delta, nkeys_delta);
145  const int64_t bucket_bits = int64_t(position[i]) - int64_t(bits_per_key_fixed_point * cum_keys[i] >> 20);
146  min_diff = min(min_diff, bucket_bits - prev_bucket_bits);
147  prev_bucket_bits = bucket_bits;
148  }
149 
150  u_position = int64_t(position[num_buckets]) - int64_t(bits_per_key_fixed_point * cum_keys[num_buckets] >> 20) - int64_t(num_buckets * min_diff) + 1;
151  l_position = u_position / (num_buckets + 1) == 0 ? 0 : lambda(u_position / (num_buckets + 1));
152  u_cum_keys = cum_keys[num_buckets] - num_buckets * cum_keys_min_delta + 1;
153  l_cum_keys = u_cum_keys / (num_buckets + 1) == 0 ? 0 : lambda(u_cum_keys / (num_buckets + 1));
154  assert(l_cum_keys * 2 + l_position <= 56); // To be able to perform a single unaligned read
155 
156 #ifdef MORESTATS
157  printf("Elias-Fano l (cumulative): %d\n", l_cum_keys);
158  printf("Elias-Fano l (positions): %d\n", l_position);
159  printf("Elias-Fano u (cumulative): %lld\n", u_cum_keys);
160  printf("Elias-Fano u (positions): %lld\n", u_position);
161 #endif
162 
163  lower_bits_mask_cum_keys = (UINT64_C(1) << l_cum_keys) - 1;
164  lower_bits_mask_position = (UINT64_C(1) << l_position) - 1;
165 
166  const uint64_t words_lower_bits = lower_bits_size_words();
167  lower_bits.size(words_lower_bits);
168  const uint64_t words_cum_keys = cum_keys_size_words();
169  upper_bits_cum_keys.size(words_cum_keys);
170  const uint64_t words_position = position_size_words();
171  upper_bits_position.size(words_position);
172 
173  for (uint64_t i = 0, cum_delta = 0, bit_delta = 0; i <= num_buckets; i++, cum_delta += cum_keys_min_delta, bit_delta += min_diff) {
174  if (l_cum_keys != 0) set_bits(lower_bits, i * (l_cum_keys + l_position), l_cum_keys, (cum_keys[i] - cum_delta) & lower_bits_mask_cum_keys);
175  set(upper_bits_cum_keys, ((cum_keys[i] - cum_delta) >> l_cum_keys) + i);
176 
177  const auto pval = int64_t(position[i]) - int64_t(bits_per_key_fixed_point * cum_keys[i] >> 20);
178  if (l_position != 0) set_bits(lower_bits, i * (l_cum_keys + l_position) + l_cum_keys, l_position, (pval - bit_delta) & lower_bits_mask_position);
179  set(upper_bits_position, ((pval - bit_delta) >> l_position) + i);
180  }
181 
182  const uint64_t jump_words = jump_size_words();
183  jump.size(jump_words);
184 
185  for (uint64_t i = 0, c = 0, last_super_q = 0; i < words_cum_keys; i++) {
186  for (int b = 0; b < 64; b++) {
187  if (upper_bits_cum_keys[i] & UINT64_C(1) << b) {
188  if ((c & super_q_mask) == 0) jump[(c / super_q) * (super_q_size * 2)] = last_super_q = i * 64 + b;
189  if ((c & q_mask) == 0) {
190  const uint64_t offset = i * 64 + b - last_super_q;
191  if (offset >= (1 << 16)) abort();
192  ((uint16_t *)(&jump + (c / super_q) * (super_q_size * 2) + 2))[2 * ((c % super_q) / q)] = offset;
193  }
194  c++;
195  }
196  }
197  }
198 
199  for (uint64_t i = 0, c = 0, last_super_q = 0; i < words_position; i++) {
200  for (int b = 0; b < 64; b++) {
201  if (upper_bits_position[i] & UINT64_C(1) << b) {
202  if ((c & super_q_mask) == 0) jump[(c / super_q) * (super_q_size * 2) + 1] = last_super_q = i * 64 + b;
203  if ((c & q_mask) == 0) {
204  const uint64_t offset = i * 64 + b - last_super_q;
205  if (offset >= (1 << 16)) abort();
206  ((uint16_t *)(&jump + (c / super_q) * (super_q_size * 2) + 2))[2 * ((c % super_q) / q) + 1] = offset;
207  }
208  c++;
209  }
210  }
211  }
212 
213 #ifndef NDEBUG
214  for (uint64_t i = 0; i < num_buckets; i++) {
215  uint64_t x, x2, y;
216 
217  get(i, x, x2, y);
218  assert(x == cum_keys[i]);
219  assert(x2 == cum_keys[i + 1]);
220  assert(y == position[i]);
221 
222  get(i, x, y);
223  assert(x == cum_keys[i]);
224  assert(y == position[i]);
225  }
226 #endif
227  }
228 
229  void get(const uint64_t i, uint64_t &cum_keys, uint64_t &cum_keys_next, uint64_t &position) {
230  const uint64_t pos_lower = i * (l_cum_keys + l_position);
231  uint64_t lower;
232  memcpy(&lower, (uint8_t *)&lower_bits + pos_lower / 8, 8);
233  lower >>= pos_lower % 8;
234 
235  const uint64_t jump_super_q = (i / super_q) * super_q_size * 2;
236  const uint64_t jump_inside_super_q = (i % super_q) / q;
237  const uint64_t jump_cum_keys = jump[jump_super_q] + ((uint16_t *)(&jump + jump_super_q + 2))[2 * jump_inside_super_q];
238  const uint64_t jump_position = jump[jump_super_q + 1] + ((uint16_t *)(&jump + jump_super_q + 2))[2 * jump_inside_super_q + 1];
239 
240  uint64_t curr_word_cum_keys = jump_cum_keys / 64;
241  uint64_t curr_word_position = jump_position / 64;
242  uint64_t window_cum_keys = upper_bits_cum_keys[curr_word_cum_keys] & UINT64_C(-1) << jump_cum_keys % 64;
243  uint64_t window_position = upper_bits_position[curr_word_position] & UINT64_C(-1) << jump_position % 64;
244  uint64_t delta_cum_keys = i & q_mask;
245  uint64_t delta_position = i & q_mask;
246 
247  for (uint64_t bit_count; (bit_count = nu(window_cum_keys)) <= delta_cum_keys; delta_cum_keys -= bit_count) window_cum_keys = upper_bits_cum_keys[++curr_word_cum_keys];
248  for (uint64_t bit_count; (bit_count = nu(window_position)) <= delta_position; delta_position -= bit_count) window_position = upper_bits_position[++curr_word_position];
249 
250  const uint64_t select_cum_keys = select64(window_cum_keys, delta_cum_keys);
251  const int64_t cum_delta = i * cum_keys_min_delta;
252  cum_keys = ((curr_word_cum_keys * 64 + select_cum_keys - i) << l_cum_keys | (lower & lower_bits_mask_cum_keys)) + cum_delta;
253 
254  lower >>= l_cum_keys;
255  const int64_t bit_delta = i * min_diff;
256  position = ((curr_word_position * 64 + select64(window_position, delta_position) - i) << l_position | (lower & lower_bits_mask_position)) + bit_delta +
257  int64_t(bits_per_key_fixed_point * cum_keys >> 20);
258 
259  window_cum_keys &= (-1ULL << select_cum_keys) << 1;
260  while (window_cum_keys == 0) window_cum_keys = upper_bits_cum_keys[++curr_word_cum_keys];
261 
262  lower >>= l_position;
263  cum_keys_next = ((curr_word_cum_keys * 64 + rho(window_cum_keys) - i - 1) << l_cum_keys | (lower & lower_bits_mask_cum_keys)) + cum_delta + cum_keys_min_delta;
264  }
265 
266  void get(const uint64_t i, uint64_t &cum_keys, uint64_t &position) {
267  const uint64_t pos_lower = i * (l_cum_keys + l_position);
268  uint64_t lower;
269  memcpy(&lower, (uint8_t *)&lower_bits + pos_lower / 8, 8);
270  lower >>= pos_lower % 8;
271 
272  const uint64_t jump_super_q = (i / super_q) * super_q_size * 2;
273  const uint64_t jump_inside_super_q = (i % super_q) / q;
274  const uint64_t jump_cum_keys = jump[jump_super_q] + ((uint16_t *)(&jump + jump_super_q + 2))[2 * jump_inside_super_q];
275  const uint64_t jump_position = jump[jump_super_q + 1] + ((uint16_t *)(&jump + jump_super_q + 2))[2 * jump_inside_super_q + 1];
276 
277  uint64_t curr_word_cum_keys = jump_cum_keys / 64;
278  uint64_t curr_word_position = jump_position / 64;
279  uint64_t window_cum_keys = upper_bits_cum_keys[curr_word_cum_keys] & UINT64_C(-1) << jump_cum_keys % 64;
280  uint64_t window_position = upper_bits_position[curr_word_position] & UINT64_C(-1) << jump_position % 64;
281  uint64_t delta_cum_keys = i & q_mask;
282  uint64_t delta_position = i & q_mask;
283 
284  for (uint64_t bit_count; (bit_count = nu(window_cum_keys)) <= delta_cum_keys; delta_cum_keys -= bit_count) window_cum_keys = upper_bits_cum_keys[++curr_word_cum_keys];
285  for (uint64_t bit_count; (bit_count = nu(window_position)) <= delta_position; delta_position -= bit_count) window_position = upper_bits_position[++curr_word_position];
286 
287  const uint64_t select_cum_keys = select64(window_cum_keys, delta_cum_keys);
288  const size_t cum_delta = i * cum_keys_min_delta;
289  cum_keys = ((curr_word_cum_keys * 64 + select_cum_keys - i) << l_cum_keys | (lower & lower_bits_mask_cum_keys)) + cum_delta;
290 
291  lower >>= l_cum_keys;
292  const int64_t bit_delta = i * min_diff;
293  position = ((curr_word_position * 64 + select64(window_position, delta_position) - i) << l_position | (lower & lower_bits_mask_position)) + bit_delta +
294  int64_t(bits_per_key_fixed_point * cum_keys >> 20);
295  }
296 
297  uint64_t bitCountCumKeys() { return (num_buckets + 1) * l_cum_keys + num_buckets + 1 + (u_cum_keys >> l_cum_keys) + jump_size_words() / 2; }
298 
299  uint64_t bitCountPosition() { return (num_buckets + 1) * l_position + num_buckets + 1 + (u_position >> l_position) + jump_size_words() / 2; }
300 };
301 
302 } // namespace sux::function
LOG2Q
#define LOG2Q
Definition: DoubleEF.hpp:40
sux::rho
int rho(uint64_t word)
Definition: common.hpp:168
sux::util
Definition: Expandable.hpp:37
sux::function::DoubleEF::get
void get(const uint64_t i, uint64_t &cum_keys, uint64_t &cum_keys_next, uint64_t &position)
Definition: DoubleEF.hpp:229
sux::util::Vector::size
void size(size_t size)
Definition: Vector.hpp:178
sux
Definition: DynamicBitVector.hpp:31
sux::function::DoubleEF
Definition: DoubleEF.hpp:54
sux::function::DoubleEF::DoubleEF
DoubleEF(const std::vector< uint64_t > &cum_keys, const std::vector< uint64_t > &position)
Definition: DoubleEF.hpp:133
sux::function
Definition: DoubleEF.hpp:43
sux::function::DoubleEF::get
void get(const uint64_t i, uint64_t &cum_keys, uint64_t &position)
Definition: DoubleEF.hpp:266
sux::function::DoubleEF::bitCountCumKeys
uint64_t bitCountCumKeys()
Definition: DoubleEF.hpp:297
sux::lambda
int lambda(uint64_t word)
Definition: common.hpp:178
sux::function::DoubleEF::operator>>
friend std::istream & operator>>(std::istream &is, DoubleEF< AT > &ef)
Definition: DoubleEF.hpp:108
sux::nu
int nu(uint64_t word)
Definition: common.hpp:339
sux::function::DoubleEF::bitCountPosition
uint64_t bitCountPosition()
Definition: DoubleEF.hpp:299
sux::function::DoubleEF::DoubleEF
DoubleEF()
Definition: DoubleEF.hpp:131
sux::function::DoubleEF::operator<<
friend std::ostream & operator<<(std::ostream &os, const DoubleEF< AT > &ef)
Definition: DoubleEF.hpp:93
sux::util::Vector< uint64_t, AT >
sux::select64
uint64_t select64(uint64_t x, uint64_t k)
Definition: common.hpp:372