31 #include "../support/common.hpp"
32 #include "../util/Vector.hpp"
55 static const int max_ones_per_inventory = 8192;
60 int log2_ones_per_inventory, log2_ones_per_sub16, log2_ones_per_sub64, log2_longwords_per_subinventory, ones_per_inventory, ones_per_sub16, ones_per_sub64, longwords_per_subinventory,
61 longwords_per_inventory, ones_per_inventory_mask, ones_per_sub16_mask, ones_per_sub64_mask;
63 uint64_t num_words, inventory_size, exact_spill_size, num_ones;
75 SimpleSelect(
const uint64_t *
const bits,
const uint64_t num_bits,
const int max_log2_longwords_per_subinventory) : bits(bits) {
76 num_words = (num_bits + 63) / 64;
80 for (uint64_t i = 0; i < num_words; i++) c += __builtin_popcountll(bits[i]);
83 assert(c <= num_bits);
85 ones_per_inventory = num_bits == 0 ? 0 : (c * max_ones_per_inventory + num_bits - 1) / num_bits;
87 log2_ones_per_inventory = max(0,
lambda_safe(ones_per_inventory));
88 ones_per_inventory = 1ULL << log2_ones_per_inventory;
89 ones_per_inventory_mask = ones_per_inventory - 1;
90 inventory_size = (c + ones_per_inventory - 1) / ones_per_inventory;
93 printf(
"Number of ones: %" PRId64
" Number of ones per inventory item: %d\n", c, ones_per_inventory);
96 log2_longwords_per_subinventory = min(max_log2_longwords_per_subinventory, max(0, log2_ones_per_inventory - 2));
97 longwords_per_subinventory = 1 << log2_longwords_per_subinventory;
98 longwords_per_inventory = longwords_per_subinventory + 1;
99 log2_ones_per_sub64 = max(0, log2_ones_per_inventory - log2_longwords_per_subinventory);
100 log2_ones_per_sub16 = max(0, log2_ones_per_sub64 - 2);
101 ones_per_sub64 = 1ULL << log2_ones_per_sub64;
102 ones_per_sub16 = 1ULL << log2_ones_per_sub16;
103 ones_per_sub64_mask = ones_per_sub64 - 1;
104 ones_per_sub16_mask = ones_per_sub16 - 1;
107 printf(
"Longwords per subinventory: %d Ones per sub 64: %d sub 16: %d\n", longwords_per_subinventory, ones_per_sub64, ones_per_sub16);
110 inventory.
size(inventory_size * longwords_per_inventory + 1);
111 const int64_t *end_of_inventory = &inventory + inventory_size * longwords_per_inventory + 1;
116 for (uint64_t i = 0; i < num_words; i++)
117 for (
int j = 0; j < 64; j++) {
118 if (i * 64 + j >= num_bits)
break;
119 if (bits[i] & 1ULL << j) {
120 if ((d & ones_per_inventory_mask) == 0) inventory[(d >> log2_ones_per_inventory) * longwords_per_inventory] = i * 64 + j;
126 inventory[inventory_size * longwords_per_inventory] = num_bits;
129 printf(
"Inventory entries filled: %" PRId64
"\n", inventory_size + 1);
132 if (ones_per_inventory > 1) {
135 uint64_t spilled = 0, exact = 0, start, span, inventory_index;
137 for (uint64_t i = 0; i < num_words; i++)
139 for (
int j = 0; j < 64; j++) {
140 if (i * 64 + j >= num_bits)
break;
141 if (bits[i] & 1ULL << j) {
142 if ((d & ones_per_inventory_mask) == 0) {
143 inventory_index = d >> log2_ones_per_inventory;
144 start = inventory[inventory_index * longwords_per_inventory];
145 span = inventory[(inventory_index + 1) * longwords_per_inventory] - start;
146 ones = min(c - d, (uint64_t)ones_per_inventory);
148 assert(start + span == num_bits || ones == ones_per_inventory);
151 if (span >= (1 << 16)) {
153 if (ones_per_sub64 > 1) spilled += ones;
161 printf(
"Spilled entries: %" PRId64
" exact: %" PRId64
"\n", spilled, exact);
164 exact_spill_size = spilled;
165 exact_spill.
size(exact_spill_size);
173 for (uint64_t i = 0; i < num_words; i++)
174 for (
int j = 0; j < 64; j++) {
175 if (i * 64 + j >= num_bits)
break;
176 if (bits[i] & 1ULL << j) {
177 if ((d & ones_per_inventory_mask) == 0) {
178 inventory_index = d >> log2_ones_per_inventory;
179 start = inventory[inventory_index * longwords_per_inventory];
180 span = inventory[(inventory_index + 1) * longwords_per_inventory] - start;
181 p64 = &inventory[inventory_index * longwords_per_inventory + 1];
182 p16 = (uint16_t *)p64;
186 if (span < (1 << 16)) {
187 assert(i * 64 + j - start <= (1 << 16));
188 if ((d & ones_per_sub16_mask) == 0) {
189 assert(offset < longwords_per_subinventory * 4);
190 assert(p16 + offset < (uint16_t *)end_of_inventory);
191 p16[offset++] = i * 64 + j - start;
194 if (ones_per_sub64 == 1) {
195 assert(p64 + offset < end_of_inventory);
196 p64[offset++] = i * 64 + j;
198 assert(p64 < end_of_inventory);
199 if ((d & ones_per_inventory_mask) == 0) {
200 inventory[inventory_index * longwords_per_inventory] |= 1ULL << 63;
203 assert(spilled < exact_spill_size);
204 exact_spill[spilled++] = i * 64 + j;
228 printf(
"Selecting %" PRId64
"\n...", rank);
231 const uint64_t inventory_index = rank >> log2_ones_per_inventory;
232 const int64_t *inventory_start = &inventory + (inventory_index << log2_longwords_per_subinventory) + inventory_index;
233 assert(inventory_index <= inventory_size);
235 const int64_t inventory_rank = *inventory_start;
236 const int subrank = rank & ones_per_inventory_mask;
238 printf(
"Rank: %" PRId64
" inventory index: %" PRId64
" inventory rank: %" PRId64
" subrank: %d\n", rank, inventory_index, inventory_rank, subrank);
242 if (subrank == 0) puts(
"Exact hit (no subrank); returning inventory");
244 if (subrank == 0)
return inventory_rank & ~(1ULL << 63);
249 if (inventory_rank >= 0) {
250 start = inventory_rank + ((uint16_t *)(inventory_start + 1))[subrank >> log2_ones_per_sub16];
251 residual = subrank & ones_per_sub16_mask;
253 if (ones_per_sub64 == 1)
return *(inventory_start + 1 + subrank);
254 assert(*(inventory_start + 1) + subrank < (int64_t)exact_spill_size);
255 return exact_spill[*(inventory_start + 1) + subrank];
259 printf(
"Differential; start: %" PRId64
" residual: %d\n", start, residual);
260 if (residual == 0) puts(
"No residual; returning start");
263 if (residual == 0)
return start;
265 uint64_t word_index = start / 64;
266 uint64_t word = bits[word_index] & -1ULL << start % 64;
269 const int bit_count = __builtin_popcountll(word);
270 if (residual < bit_count)
break;
271 word = bits[++word_index];
272 residual -= bit_count;
275 return word_index * 64 +
select64(word, residual);
279 size_t bitCount()
const {
return inventory.
bitCount() -
sizeof(inventory) * 8 + exact_spill.
bitCount() -
sizeof(exact_spill) * 8 +
sizeof(*
this) * 8; }