13#if defined(__AVX512VPOPCNTDQ__) && defined(__AVX512F__) && \
15#define PIXIE_AVX512_SUPPORT
18#if defined(__BMI__) && defined(__BMI2__)
19#define PIXIE_BMI_SUPPORT
23#define PIXIE_AVX2_SUPPORT
27static inline const __m256i lookup_popcount_4 = _mm256_setr_epi8(
40static inline const __m256i mask_first_half = _mm256_setr_epi8(
41 0xFF, 0xFF, 0xFF, 0xFF,
42 0xFF, 0xFF, 0xFF, 0xFF,
43 0xFF, 0xFF, 0xFF, 0xFF,
44 0xFF, 0xFF, 0xFF, 0xFF,
54static inline constexpr int8_t excess_nibble_min_offset[16] = {
55 4, 4, 4, 4, 2, 2, 1, 1, 3, 3, 1, 1, 2, 2, 1, 1};
57#if defined(__SSSE3__) && defined(__SSE4_1__)
58#define PIXIE_SSE41_SUPPORT
60static inline const __m128i excess_lut_delta_sse = _mm_setr_epi8(
65static inline const __m128i excess_lut_pos0_sse = _mm_setr_epi8(
70static inline const __m128i excess_lut_pos1_sse = _mm_setr_epi8(
75static inline const __m128i excess_lut_pos2_sse = _mm_setr_epi8(
80static inline const __m128i excess_lut_min_sse = _mm_setr_epi8(
85static inline const __m128i excess_lut_nibble_index_sse = _mm_setr_epi8(
90static inline const __m128i excess_lut_low_nibble_index_sse = _mm_setr_epi8(
95static inline const __m128i excess_lut_high_nibble_index_sse = _mm_setr_epi8(
100static inline const __m128i excess_lut_nibble_mask_sse = _mm_set1_epi8(0x0F);
104#ifdef PIXIE_SSE41_SUPPORT
105static inline __m128i excess_nibbles_64_sse(
const uint64_t* s)
noexcept {
106 const __m128i word_vec = _mm_loadl_epi64(
reinterpret_cast<const __m128i*
>(s));
107 const __m128i lo_nibbles =
108 _mm_and_si128(word_vec, excess_lut_nibble_mask_sse);
109 const __m128i hi_nibbles =
110 _mm_and_si128(_mm_srli_epi16(word_vec, 4), excess_lut_nibble_mask_sse);
111 return _mm_unpacklo_epi8(lo_nibbles, hi_nibbles);
114static inline __m128i excess_prefix_sum_16x_i8(__m128i v)
noexcept {
116 __m128i t = _mm_slli_si128(x, 1);
117 x = _mm_add_epi8(x, t);
118 t = _mm_slli_si128(x, 2);
119 x = _mm_add_epi8(x, t);
120 t = _mm_slli_si128(x, 4);
121 x = _mm_add_epi8(x, t);
122 t = _mm_slli_si128(x, 8);
123 return _mm_add_epi8(x, t);
126static inline int excess_horizontal_min_i8(__m128i v)
noexcept {
127 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 8));
128 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 4));
129 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 2));
130 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 1));
131 return static_cast<int>(
static_cast<int8_t
>(_mm_extract_epi8(v, 0)));
149static inline uint32_t rmm_btree_match_mask_i16x16(
const int16_t* prefix_before,
150 const int16_t* min_excess,
151 const int16_t* max_excess,
153 bool include_zero_boundary) {
154#ifdef PIXIE_AVX2_SUPPORT
155 const __m256i vtarget = _mm256_set1_epi16(target);
156 const __m256i vprefix =
157 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(prefix_before));
159 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(min_excess));
161 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(max_excess));
163 const __m256i lower = _mm256_adds_epi16(vprefix, vmin);
164 const __m256i upper = _mm256_adds_epi16(vprefix, vmax);
165 const __m256i ge_lower = _mm256_or_si256(_mm256_cmpgt_epi16(vtarget, lower),
166 _mm256_cmpeq_epi16(vtarget, lower));
167 const __m256i le_upper = _mm256_or_si256(_mm256_cmpgt_epi16(upper, vtarget),
168 _mm256_cmpeq_epi16(upper, vtarget));
169 __m256i matched = _mm256_and_si256(ge_lower, le_upper);
170 if (include_zero_boundary) {
171 matched = _mm256_or_si256(matched, _mm256_cmpeq_epi16(vtarget, vprefix));
174 const uint32_t byte_mask =
175 static_cast<uint32_t
>(_mm256_movemask_epi8(matched));
177 for (
size_t lane = 0; lane < 16; ++lane) {
178 const uint32_t lane_mask = 0x3u << (lane * 2);
179 if ((byte_mask & lane_mask) == lane_mask) {
180 result |= uint32_t{1} << lane;
186 for (
size_t lane = 0; lane < 16; ++lane) {
187 const int lower = prefix_before[lane] + min_excess[lane];
188 const int upper = prefix_before[lane] + max_excess[lane];
189 const bool found = (lower <= target && target <= upper) ||
190 (include_zero_boundary && target == prefix_before[lane]);
192 result |= uint32_t{1} << lane;
213static inline uint32_t rmm_btree_match_mask_i64x4(
const int64_t* prefix_before,
214 const int64_t* min_excess,
215 const int64_t* max_excess,
217 bool include_zero_boundary) {
218#ifdef PIXIE_AVX2_SUPPORT
219 const __m256i vtarget = _mm256_set1_epi64x(target);
220 const __m256i vprefix =
221 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(prefix_before));
223 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(min_excess));
225 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(max_excess));
227 const __m256i relative = _mm256_sub_epi64(vtarget, vprefix);
228 const __m256i ge_min = _mm256_or_si256(_mm256_cmpgt_epi64(relative, vmin),
229 _mm256_cmpeq_epi64(relative, vmin));
230 const __m256i le_max = _mm256_or_si256(_mm256_cmpgt_epi64(vmax, relative),
231 _mm256_cmpeq_epi64(vmax, relative));
232 __m256i matched = _mm256_and_si256(ge_min, le_max);
233 if (include_zero_boundary) {
234 matched = _mm256_or_si256(matched, _mm256_cmpeq_epi64(vtarget, vprefix));
237 const uint32_t byte_mask =
238 static_cast<uint32_t
>(_mm256_movemask_epi8(matched));
240 for (
size_t lane = 0; lane < 4; ++lane) {
241 const uint32_t lane_mask = 0xffu << (lane * 8);
242 if ((byte_mask & lane_mask) == lane_mask) {
243 result |= uint32_t{1} << lane;
249 for (
size_t lane = 0; lane < 4; ++lane) {
250 const int64_t relative = target - prefix_before[lane];
252 (min_excess[lane] <= relative && relative <= max_excess[lane]) ||
253 (include_zero_boundary && relative == 0);
255 result |= uint32_t{1} << lane;
269static inline uint64_t first_bits_mask(
size_t num) {
270 return num >= 64 ? UINT64_MAX : ((1llu << num) - 1);
293static inline uint64_t rank_512(
const uint64_t* x, uint64_t count) {
294#ifdef PIXIE_AVX512_SUPPORT
296 __m512i a = _mm512_maskz_set1_epi64((1ull << ((count >> 6))) - 1,
297 std::numeric_limits<uint64_t>::max());
298 __m512i b = _mm512_maskz_set1_epi64((1ull << ((count >> 6) + 1)) - 1,
299 std::numeric_limits<uint64_t>::max());
300 __m512i mask = _mm512_shldv_epi64(a, b, _mm512_set1_epi64(count % 64));
302 __m512i res = _mm512_loadu_epi64(x);
303 res = _mm512_and_epi64(res, mask);
304 __m512i cnt = _mm512_popcnt_epi64(res);
305 return _mm512_reduce_add_epi64(cnt);
309 uint64_t last_uint = count < 512 ? count >> 6 : 8;
311 uint64_t pop_val = 0;
313 for (
int i = 0; i < last_uint; i++) {
314 pop_val += std::popcount(x[i]);
317 pop_val += count < 512
318 ? std::popcount(x[last_uint] & first_bits_mask(count & 63))
329static inline uint64_t select_64(uint64_t x, uint64_t rank) {
330#ifdef PIXIE_BMI_SUPPORT
331 return _tzcnt_u64(_pdep_u64(1ull << rank, x));
337 return std::countr_zero(x);
358static inline uint64_t select_512(
const uint64_t* x, uint64_t rank) {
359#ifdef PIXIE_AVX512_SUPPORT
361 __m512i res = _mm512_loadu_epi64(x);
362 __m512i counts = _mm512_popcnt_epi64(res);
363 __m512i prefix = counts;
365 const __m512i idx_shift1 = _mm512_set_epi64(6, 5, 4, 3, 2, 1, 0, 0);
366 const __m512i idx_shift2 = _mm512_set_epi64(5, 4, 3, 2, 1, 0, 0, 0);
367 const __m512i idx_shift4 = _mm512_set_epi64(3, 2, 1, 0, 0, 0, 0, 0);
369 __m512i tmp = _mm512_maskz_permutexvar_epi64(0xFE, idx_shift1, prefix);
370 prefix = _mm512_add_epi64(prefix, tmp);
371 tmp = _mm512_maskz_permutexvar_epi64(0xFC, idx_shift2, prefix);
372 prefix = _mm512_add_epi64(prefix, tmp);
373 tmp = _mm512_maskz_permutexvar_epi64(0xF0, idx_shift4, prefix);
374 prefix = _mm512_add_epi64(prefix, tmp);
376 __mmask8 mask = _mm512_cmpgt_epu64_mask(prefix, _mm512_set1_epi64(rank));
377 uint32_t i = _tzcnt_u32(
static_cast<uint32_t
>(mask));
380 __m512i idx_prev = _mm512_set1_epi64(
static_cast<int64_t
>(i - 1));
381 __m512i prev_vec = _mm512_permutexvar_epi64(idx_prev, prefix);
382 prev =
static_cast<uint64_t
>(
383 _mm_cvtsi128_si64(_mm512_castsi512_si128(prev_vec)));
385 return i * 64 + select_64(x[i], rank - prev);
390 int popcount = std::popcount(x[0]);
391 while (i < 7 && popcount <= rank) {
393 popcount = std::popcount(x[++i]);
395 return i * 64 + select_64(x[i], rank);
400#ifdef PIXIE_AVX512_SUPPORT
401static inline uint64_t select0_512_from_inverted_words(
const uint64_t* x,
404 __m512i counts = _mm512_popcnt_epi64(res);
405 __m512i prefix = counts;
407 const __m512i idx_shift1 = _mm512_set_epi64(6, 5, 4, 3, 2, 1, 0, 0);
408 const __m512i idx_shift2 = _mm512_set_epi64(5, 4, 3, 2, 1, 0, 0, 0);
409 const __m512i idx_shift4 = _mm512_set_epi64(3, 2, 1, 0, 0, 0, 0, 0);
411 __m512i tmp = _mm512_maskz_permutexvar_epi64(0xFE, idx_shift1, prefix);
412 prefix = _mm512_add_epi64(prefix, tmp);
413 tmp = _mm512_maskz_permutexvar_epi64(0xFC, idx_shift2, prefix);
414 prefix = _mm512_add_epi64(prefix, tmp);
415 tmp = _mm512_maskz_permutexvar_epi64(0xF0, idx_shift4, prefix);
416 prefix = _mm512_add_epi64(prefix, tmp);
418 __mmask8 mask = _mm512_cmpgt_epu64_mask(prefix, _mm512_set1_epi64(rank0));
419 uint32_t i = _tzcnt_u32(
static_cast<uint32_t
>(mask));
422 __m512i idx_prev = _mm512_set1_epi64(
static_cast<int64_t
>(i - 1));
423 __m512i prev_vec = _mm512_permutexvar_epi64(idx_prev, prefix);
424 prev =
static_cast<uint64_t
>(
425 _mm_cvtsi128_si64(_mm512_castsi512_si128(prev_vec)));
427 return i * 64 + select_64(~x[i], rank0 - prev);
435static inline uint64_t select0_512(
const uint64_t* x, uint64_t rank0) {
436#ifdef PIXIE_AVX512_SUPPORT
438 __m512i res = _mm512_loadu_epi64(x);
439 res = _mm512_ternarylogic_epi64(res, res, res, 0x55);
440 return select0_512_from_inverted_words(x, rank0, res);
445 int popcount = std::popcount(~x[0]);
446 while (i < 7 && popcount <= rank0) {
448 popcount = std::popcount(~x[++i]);
450 return i * 64 + select_64(~x[i], rank0);
459static inline uint16_t lower_bound_4x64(
const uint64_t* x, uint64_t y) {
460#ifdef PIXIE_AVX512_SUPPORT
462 auto y_4 = _mm256_set1_epi64x(y);
463 auto reg_256 = _mm256_loadu_epi64(x);
464 auto cmp = _mm256_cmpge_epu64_mask(reg_256, y_4);
466 return _tzcnt_u16(cmp);
469#ifdef PIXIE_AVX2_SUPPORT
471 auto y_4 = _mm256_set1_epi64x(y);
472 __m256i reg_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
474 const __m256i offset = _mm256_set1_epi64x(0x8000000000000000ULL);
475 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
476 __m256i y_offset = _mm256_xor_si256(y_4, offset);
477 auto mask = _mm256_movemask_epi8(_mm256_cmpgt_epi64(
478 x_offset, _mm256_sub_epi64(y_offset, _mm256_set1_epi64x(1))));
480 return _tzcnt_u32(mask) >> 3;
484 for (uint16_t i = 0; i < 4; ++i) {
508static inline uint16_t lower_bound_delta_4x64(
const uint64_t* x,
510 const uint64_t* delta_array,
511 uint64_t delta_scalar) {
512#ifdef PIXIE_AVX512_SUPPORT
514 const __m256i dlt_256 = _mm256_loadu_epi64(delta_array);
515 auto x_256 = _mm256_loadu_epi64(x);
516 auto dlt_4 = _mm256_set1_epi64x(delta_scalar);
517 auto y_4 = _mm256_set1_epi64x(y);
519 auto tmp = _mm256_add_epi64(dlt_4, dlt_256);
520 auto reg_256 = _mm256_sub_epi64(tmp, x_256);
521 auto cmp = _mm256_cmpge_epu64_mask(reg_256, y_4);
523 return _tzcnt_u16(cmp);
526#ifdef PIXIE_AVX2_SUPPORT
528 const __m256i dlt_256 =
529 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(delta_array));
530 auto x_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
531 auto dlt_4 = _mm256_set1_epi64x(delta_scalar);
532 auto y_4 = _mm256_set1_epi64x(y);
534 auto tmp = _mm256_add_epi64(dlt_4, dlt_256);
535 auto reg_256 = _mm256_sub_epi64(tmp, x_256);
537 const __m256i offset = _mm256_set1_epi64x(0x8000000000000000ULL);
538 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
539 __m256i y_offset = _mm256_xor_si256(y_4, offset);
540 auto mask = _mm256_movemask_epi8(_mm256_cmpgt_epi64(
541 x_offset, _mm256_sub_epi64(y_offset, _mm256_set1_epi64x(1))));
543 return _tzcnt_u32(mask) >> 3;
547 for (uint16_t i = 0; i < 4; ++i) {
548 if (delta_array[i] + delta_scalar - x[i] >= y) {
562static inline uint16_t lower_bound_8x64(
const uint64_t* x, uint64_t y) {
563#ifdef PIXIE_AVX512_SUPPORT
565 auto y_8 = _mm512_set1_epi64(y);
566 auto reg_512 = _mm512_loadu_epi64(x);
567 auto cmp = _mm512_cmpge_epu64_mask(reg_512, y_8);
569 return _tzcnt_u16(cmp);
572#ifdef PIXIE_AVX2_SUPPORT
574 uint16_t len = lower_bound_4x64(x, y);
580 return len + lower_bound_4x64(x + 4, y);
584 for (uint16_t i = 0; i < 8; ++i) {
608static inline uint16_t lower_bound_delta_8x64(
const uint64_t* x,
610 const uint64_t* delta_array,
611 uint64_t delta_scalar) {
612#ifdef PIXIE_AVX512_SUPPORT
614 const __m512i dlt_512 = _mm512_loadu_epi64(delta_array);
615 auto x_512 = _mm512_loadu_epi64(x);
616 auto dlt_8 = _mm512_set1_epi64(delta_scalar);
617 auto y_8 = _mm512_set1_epi64(y);
619 auto tmp = _mm512_add_epi64(dlt_8, dlt_512);
620 auto reg_512 = _mm512_sub_epi64(tmp, x_512);
621 auto cmp = _mm512_cmpge_epu64_mask(reg_512, y_8);
623 return _tzcnt_u16(cmp);
626#ifdef PIXIE_AVX2_SUPPORT
628 uint16_t len = lower_bound_delta_4x64(x, y, delta_array, delta_scalar);
634 return len + lower_bound_delta_4x64(x + 4, y, delta_array + 4, delta_scalar);
638 for (uint16_t i = 0; i < 8; ++i) {
639 if (delta_array[i] + delta_scalar - x[i] >= y) {
653static inline uint16_t lower_bound_32x16(
const uint16_t* x, uint16_t y) {
654#ifdef PIXIE_AVX512_SUPPORT
656 auto y_32 = _mm512_set1_epi16(y);
657 auto reg_512 = _mm512_loadu_epi16(x);
658 auto cmp = _mm512_cmplt_epu16_mask(reg_512, y_32);
659 return std::popcount(cmp);
662#ifdef PIXIE_AVX2_SUPPORT
664 auto y_16 = _mm256_set1_epi16(y);
665 __m256i reg_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
667 const __m256i offset = _mm256_set1_epi16(0x8000);
668 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
669 __m256i y_offset = _mm256_xor_si256(y_16, offset);
670 uint32_t mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
672 uint16_t count = std::popcount(mask) >> 1;
674 reg_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x + 16));
676 x_offset = _mm256_xor_si256(reg_256, offset);
677 mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
679 return count + (std::popcount(mask) >> 1);
684 for (uint16_t i = 0; i < 32; ++i) {
708static inline uint16_t lower_bound_delta_32x16(
const uint16_t* x,
710 const uint16_t* delta_array,
711 uint16_t delta_scalar) {
712#ifdef PIXIE_AVX512_SUPPORT
714 const __m512i dlt_512 = _mm512_loadu_epi64(delta_array);
715 auto x_512 = _mm512_loadu_epi64(x);
716 auto dlt_32 = _mm512_set1_epi16(delta_scalar);
717 auto y_32 = _mm512_set1_epi16(y);
719 auto tmp = _mm512_add_epi16(dlt_32, dlt_512);
720 auto reg_512 = _mm512_sub_epi16(tmp, x_512);
721 auto cmp = _mm512_cmplt_epu16_mask(reg_512, y_32);
722 return std::popcount(cmp);
725#ifdef PIXIE_AVX2_SUPPORT
728 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(delta_array));
729 auto x_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
730 auto dlt_16 = _mm256_set1_epi16(delta_scalar);
731 auto y_16 = _mm256_set1_epi16(y);
733 auto tmp = _mm256_add_epi16(dlt_16, dlt_256);
734 auto reg_256 = _mm256_sub_epi16(tmp, x_256);
736 const __m256i offset = _mm256_set1_epi16(0x8000);
737 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
738 __m256i y_offset = _mm256_xor_si256(y_16, offset);
739 uint32_t mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
741 uint16_t count = std::popcount(mask) >> 1;
744 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(delta_array + 16));
745 x_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x + 16));
747 tmp = _mm256_add_epi16(dlt_16, dlt_256);
748 reg_256 = _mm256_sub_epi16(tmp, x_256);
750 x_offset = _mm256_xor_si256(reg_256, offset);
751 mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
753 return count + (std::popcount(mask) >> 1);
758 for (uint16_t i = 0; i < 32; ++i) {
759 if (delta_array[i] + delta_scalar - x[i] < y) {
780static inline void popcount_64x4(
const uint8_t* x, uint8_t* result) {
781#ifdef PIXIE_AVX512_SUPPORT
782 __m256i data = _mm256_loadu_si256((__m256i
const*)x);
785 const __m256i low_bits_mask = _mm256_set1_epi8(0x0F);
788 __m256i low_bits = _mm256_and_si256(data, low_bits_mask);
789 __m256i low_count = _mm256_shuffle_epi8(lookup_popcount_4, low_bits);
792 __m256i high_bits = _mm256_srli_epi16(data, 4);
793 high_bits = _mm256_and_si256(high_bits, low_bits_mask);
794 __m256i high_count = _mm256_shuffle_epi8(lookup_popcount_4, high_bits);
798 _mm256_or_si256(low_count, _mm256_slli_epi16(high_count, 4));
799 _mm256_storeu_epi8(result, result_vec);
802 for (
size_t i = 0; i < 32; i++) {
804 uint8_t a = x[i] & 0x0F;
805 uint8_t low_count = std::popcount(a);
807 a = (x[i] >> 4) & 0x0F;
808 uint8_t high_count = std::popcount(a);
811 result[i] = low_count | (high_count << 4);
827static inline void popcount_32x8(
const uint8_t* x, uint8_t* result) {
828#ifdef PIXIE_AVX512_SUPPORT
830 __m256i data = _mm256_loadu_si256((__m256i
const*)x);
831 auto popcount_8 = _mm256_popcnt_epi8(data);
832 _mm256_storeu_si256((__m256i*)result, popcount_8);
834#ifdef PIXIE_AVX2_SUPPORT
836 __m256i data = _mm256_loadu_si256((__m256i
const*)x);
839 const __m256i low_bits_mask = _mm256_set1_epi8(0x0F);
842 __m256i low_bits = _mm256_and_si256(data, low_bits_mask);
843 __m256i low_count = _mm256_shuffle_epi8(lookup_popcount_4, low_bits);
846 __m256i high_bits = _mm256_srli_epi16(data, 4);
847 high_bits = _mm256_and_si256(high_bits, low_bits_mask);
848 __m256i high_count = _mm256_shuffle_epi8(lookup_popcount_4, high_bits);
850 __m256i result_vec = _mm256_add_epi8(low_count, high_count);
851 _mm256_storeu_si256((__m256i*)result, result_vec);
854 for (
size_t i = 0; i < 32; i++) {
855 result[i] = std::popcount(x[i]);
861#ifdef PIXIE_AVX2_SUPPORT
864static inline const __m256i excess_lut_delta = _mm256_setr_epi8(
875static inline const __m256i excess_lut_pos0 = _mm256_setr_epi8(
885static inline const __m256i excess_lut_pos1 = _mm256_setr_epi8(
895static inline const __m256i excess_lut_pos2 = _mm256_setr_epi8(
904static inline const __m256i excess_lut_min = _mm256_setr_epi8(
913static inline constexpr int8_t excess_lut_min_offset[16] = {
914 4, 4, 4, 4, 2, 2, 1, 1, 3, 3, 1, 1, 2, 2, 1, 1};
915static inline const __m256i excess_lut_pack_multiplier =
916 _mm256_set1_epi16(0x1001);
917static inline const __m256i excess_lut_bit0 = _mm256_set1_epi8(1);
918static inline const __m256i excess_lut_bit1 = _mm256_set1_epi8(2);
919static inline const __m256i excess_lut_bit2 = _mm256_set1_epi8(4);
920static inline const __m256i excess_lut_bit3 = _mm256_set1_epi8(8);
921static inline const __m256i excess_lut_nibble_index = _mm256_setr_epi8(
930static inline const __m128i excess_lut_nibble_mask = _mm_set1_epi8(0x0F);
933static inline __m256i excess_nibbles_128_avx2(
const uint64_t* s)
noexcept {
934 __m128i word_vec = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(s));
935 __m128i lo_nibbles = _mm_and_si128(word_vec, excess_lut_nibble_mask);
937 _mm_and_si128(_mm_srli_epi16(word_vec, 4), excess_lut_nibble_mask);
939 __m128i unpack_lo = _mm_unpacklo_epi8(lo_nibbles, hi_nibbles);
940 __m128i unpack_hi = _mm_unpackhi_epi8(lo_nibbles, hi_nibbles);
942 return _mm256_inserti128_si256(_mm256_castsi128_si256(unpack_lo), unpack_hi,
946static inline __m256i excess_bit_masks_16x_i16() noexcept {
947 return _mm256_setr_epi16(0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020,
948 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800,
949 0x1000, 0x2000, 0x4000,
950 static_cast<int16_t
>(0x8000));
953static inline __m256i excess_prefix_sum_16x_i16(__m256i v)
noexcept {
955 __m256i t = _mm256_slli_si256(x, 2);
956 x = _mm256_add_epi16(x, t);
957 t = _mm256_slli_si256(x, 4);
958 x = _mm256_add_epi16(x, t);
959 t = _mm256_slli_si256(x, 8);
960 x = _mm256_add_epi16(x, t);
962 __m128i lo = _mm256_extracti128_si256(x, 0);
963 __m128i hi = _mm256_extracti128_si256(x, 1);
964 const int16_t carry =
static_cast<int16_t
>(_mm_extract_epi16(lo, 7));
965 hi = _mm_add_epi16(hi, _mm_set1_epi16(carry));
967 __m256i out = _mm256_castsi128_si256(lo);
968 return _mm256_inserti128_si256(out, hi, 1);
996constexpr int8_t excess_byte_delta_value(uint8_t x) {
997 return static_cast<int8_t
>(2 * std::popcount(x) - 8);
1000constexpr int8_t excess_byte_min_prefix_value(uint8_t x) {
1003 for (
int bit = 0; bit < 8; ++bit) {
1004 cur += ((x >> bit) & 1u) != 0 ? 1 : -1;
1005 if (bit == 0 || cur < best) {
1009 return static_cast<int8_t
>(best);
1012constexpr int8_t excess_byte_min_prefix_offset_value(uint8_t x) {
1015 int best_offset = 1;
1016 for (
int bit = 0; bit < 8; ++bit) {
1017 cur += ((x >> bit) & 1u) != 0 ? 1 : -1;
1018 if (bit == 0 || cur < best) {
1020 best_offset = bit + 1;
1023 return static_cast<int8_t
>(best_offset);
1026constexpr int8_t excess_nibble_min_prefix_offset_value(uint8_t x,
int bits) {
1029 int best_offset = 1;
1030 for (
int bit = 0; bit < bits; ++bit) {
1031 cur += ((x >> bit) & 1u) != 0 ? 1 : -1;
1032 if (bit == 0 || cur < best) {
1034 best_offset = bit + 1;
1037 return static_cast<int8_t
>(best_offset);
1040template <
typename Fn>
1041constexpr std::array<int8_t, 256> excess_make_byte_lut(Fn fn) {
1042 std::array<int8_t, 256> out{};
1043 for (
size_t i = 0; i < out.size(); ++i) {
1044 out[i] = fn(
static_cast<uint8_t
>(i));
1049static inline constexpr std::array<int8_t, 256> excess_byte_delta_lut =
1050 excess_make_byte_lut([](uint8_t x) {
return excess_byte_delta_value(x); });
1051static inline constexpr std::array<int8_t, 256> excess_byte_min_lut =
1052 excess_make_byte_lut(
1053 [](uint8_t x) {
return excess_byte_min_prefix_value(x); });
1054static inline constexpr std::array<int8_t, 256> excess_byte_min_offset_lut =
1055 excess_make_byte_lut(
1056 [](uint8_t x) {
return excess_byte_min_prefix_offset_value(x); });
1057static inline constexpr std::array<std::array<int8_t, 16>, 4>
1058 excess_partial_nibble_min_offset_lut = [] {
1059 std::array<std::array<int8_t, 16>, 4> out{};
1060 for (
size_t width = 1; width < out.size(); ++width) {
1061 for (
size_t nibble = 0; nibble < out[width].size(); ++nibble) {
1062 out[width][nibble] = excess_nibble_min_prefix_offset_value(
1063 static_cast<uint8_t
>(nibble),
static_cast<int>(width));
1076constexpr uint8_t excess_byte_record_lows_mask(uint8_t
byte,
int threshold) {
1079 for (
int bit = 0; bit < 8; ++bit) {
1080 cur += ((
byte >> bit) & 1u) ? 1 : -1;
1081 if (cur < threshold) {
1082 mask |=
static_cast<uint8_t
>(1u << bit);
1096static inline constexpr std::array<std::array<uint8_t, 8>, 256>
1097 excess_byte_record_lows_lut = [] {
1098 std::array<std::array<uint8_t, 8>, 256> out{};
1099 for (
size_t byte = 0;
byte < 256; ++byte) {
1100 for (
int g = 0; g < 8; ++g) {
1102 excess_byte_record_lows_mask(
static_cast<uint8_t
>(
byte), -g);
1121static inline int excess_positions_128(
const uint64_t* s,
1123 uint64_t* out)
noexcept {
1124 out[0] = out[1] = 0;
1125 const int block_delta = 2 * (std::popcount(s[0]) + std::popcount(s[1])) - 128;
1127 if (target_x < -128 || target_x > 128) {
1131#ifdef PIXIE_AVX2_SUPPORT
1132 const __m256i vdelta = excess_lut_delta;
1133 const __m256i vpos0 = excess_lut_pos0;
1134 const __m256i vpos1 = excess_lut_pos1;
1135 const __m256i vpos2 = excess_lut_pos2;
1136 const __m256i vmult = excess_lut_pack_multiplier;
1137 const __m256i vbit0 = excess_lut_bit0;
1138 const __m256i vbit1 = excess_lut_bit1;
1139 const __m256i vbit2 = excess_lut_bit2;
1140 const __m256i vbit3 = excess_lut_bit3;
1142 const int d = 2 * target_x - block_delta;
1143 if (d < -128 || d > 128) {
1147 __m256i nibbles = excess_nibbles_128_avx2(s);
1149 __m256i ps = _mm256_shuffle_epi8(vdelta, nibbles);
1150 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 1));
1151 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 2));
1152 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 4));
1153 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 8));
1155 __m128i ps_lo = _mm256_castsi256_si128(ps);
1156 __m128i ps_hi = _mm256_extracti128_si256(ps, 1);
1157 __m128i carry = _mm_set1_epi8((int8_t)_mm_extract_epi8(ps_lo, 15));
1158 ps_hi = _mm_add_epi8(ps_hi, carry);
1159 ps = _mm256_inserti128_si256(_mm256_castsi128_si256(ps_lo), ps_hi, 1);
1161 __m256i b = _mm256_permute2x128_si256(ps, ps, 0x08);
1162 __m256i excl_ps = _mm256_alignr_epi8(ps, b, 15);
1164 __m256i vtgt = _mm256_set1_epi8((int8_t)target_x);
1165 __m256i t = _mm256_sub_epi8(vtgt, excl_ps);
1167 __m256i cmp0 = _mm256_cmpeq_epi8(_mm256_shuffle_epi8(vpos0, nibbles), t);
1168 __m256i cmp1 = _mm256_cmpeq_epi8(_mm256_shuffle_epi8(vpos1, nibbles), t);
1169 __m256i cmp2 = _mm256_cmpeq_epi8(_mm256_shuffle_epi8(vpos2, nibbles), t);
1170 __m256i cmp3 = _mm256_cmpeq_epi8(ps, vtgt);
1172 __m256i bit0 = _mm256_and_si256(cmp0, vbit0);
1173 __m256i bit1 = _mm256_and_si256(cmp1, vbit1);
1174 __m256i bit2 = _mm256_and_si256(cmp2, vbit2);
1175 __m256i bit3 = _mm256_and_si256(cmp3, vbit3);
1177 __m256i total_match =
1178 _mm256_or_si256(_mm256_or_si256(bit0, bit1), _mm256_or_si256(bit2, bit3));
1180 __m256i res = _mm256_maddubs_epi16(total_match, vmult);
1181 __m128i res_lo = _mm256_castsi256_si128(res);
1182 __m128i res_hi = _mm256_extracti128_si256(res, 1);
1183 __m128i packed = _mm_packus_epi16(res_lo, res_hi);
1185 _mm_storeu_si128((__m128i*)out, packed);
1188 for (
size_t i = 0; i < 128; ++i) {
1189 const uint64_t w = s[i >> 6];
1190 const int bit = int((w >> (i & 63)) & 1ull);
1191 cur += bit ? +1 : -1;
1192 if (cur == target_x) {
1193 out[i >> 6] |= (uint64_t{1} << (i & 63));
1209static inline int prefix_excess_128(
const uint64_t* s,
1210 size_t end_offset)
noexcept {
1211 end_offset = end_offset > 128 ? 128 : end_offset;
1212 if (end_offset == 0) {
1215 if (end_offset <= 64) {
1216 const int ones =
static_cast<int>(std::popcount(
1217 s[0] & first_bits_mask(
static_cast<uint32_t
>(end_offset))));
1218 return 2 * ones -
static_cast<int>(end_offset);
1220 const int ones =
static_cast<int>(
1221 std::popcount(s[0]) +
1222 std::popcount(s[1] &
1223 first_bits_mask(
static_cast<uint32_t
>(end_offset - 64))));
1224 return 2 * ones -
static_cast<int>(end_offset);
1236static inline int prefix_excess_64(
const uint64_t* s,
1237 size_t end_offset)
noexcept {
1238 end_offset = end_offset > 64 ? 64 : end_offset;
1239 if (end_offset == 0) {
1242 const int ones =
static_cast<int>(
1243 std::popcount(s[0] & first_bits_mask(
static_cast<uint32_t
>(end_offset))));
1244 return 2 * ones -
static_cast<int>(end_offset);
1247static inline ExcessResult excess_min_128_byte_lut_short(
1250 size_t right)
noexcept {
1251 int best = prefix_excess_128(s, left);
1252 size_t best_offset = left;
1253 if (left == right) {
1254 return {best, best_offset};
1259 for (; bit < right && (bit & 7u) != 0; ++bit) {
1260 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1261 const size_t offset = bit + 1;
1262 if (current < best) {
1264 best_offset = offset;
1268 for (; bit + 8 <= right; bit += 8) {
1269 const uint8_t
byte =
1270 static_cast<uint8_t
>((s[bit >> 6] >> (bit & 63)) & 0xFFu);
1271 const int candidate = current + excess_byte_min_lut[byte];
1272 if (candidate < best) {
1274 best_offset = bit +
static_cast<size_t>(excess_byte_min_offset_lut[byte]);
1276 current += excess_byte_delta_lut[byte];
1279 for (; bit < right; ++bit) {
1280 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1281 const size_t offset = bit + 1;
1282 if (current < best) {
1284 best_offset = offset;
1288 return {best, best_offset};
1305static inline ExcessResult excess_min_64(
const uint64_t* s,
1307 size_t right)
noexcept {
1311 left = std::min<size_t>(left, 64);
1312 right = std::min<size_t>(right, 64);
1314 int best = prefix_excess_64(s, left);
1315 size_t best_offset = left;
1316 if (left == right) {
1317 return {best, best_offset};
1320#ifdef PIXIE_SSE41_SUPPORT
1323 for (; bit < right && (bit & 3u) != 0; ++bit) {
1324 current += ((s[0] >> bit) & 1ull) != 0 ? 1 : -1;
1325 const size_t offset = bit + 1;
1326 if (current < best) {
1328 best_offset = offset;
1332 const size_t first_full_nibble = bit >> 2;
1333 const size_t last_full_nibble = right >> 2;
1334 const size_t right_partial_width = bit < right ? (right & 3u) : 0;
1335 const size_t end_nibble =
1336 last_full_nibble + (right_partial_width == 0 ? 0 : 1);
1337 if (first_full_nibble < end_nibble) {
1338 const __m128i nibbles = excess_nibbles_64_sse(s);
1340 __m128i ps = _mm_shuffle_epi8(excess_lut_delta_sse, nibbles);
1341 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 1));
1342 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 2));
1343 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 4));
1344 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 8));
1346 const __m128i excl_ps = _mm_slli_si128(ps, 1);
1347 __m128i local_min = _mm_shuffle_epi8(excess_lut_min_sse, nibbles);
1348 if (right_partial_width != 0) {
1349 __m128i partial_min = _mm_shuffle_epi8(excess_lut_pos0_sse, nibbles);
1350 if (right_partial_width >= 2) {
1351 partial_min = _mm_min_epi8(
1352 partial_min, _mm_shuffle_epi8(excess_lut_pos1_sse, nibbles));
1354 if (right_partial_width >= 3) {
1355 partial_min = _mm_min_epi8(
1356 partial_min, _mm_shuffle_epi8(excess_lut_pos2_sse, nibbles));
1358 local_min = _mm_blendv_epi8(
1359 local_min, partial_min,
1360 _mm_cmpeq_epi8(excess_lut_nibble_index_sse,
1361 _mm_set1_epi8(
static_cast<int8_t
>(last_full_nibble))));
1363 const __m128i partial_candidates = _mm_add_epi8(excl_ps, local_min);
1365 const __m128i idx = excess_lut_nibble_index_sse;
1366 const int first_minus_one_value =
static_cast<int>(first_full_nibble) - 1;
1367 const __m128i first_minus_one =
1368 _mm_set1_epi8(
static_cast<int8_t
>(first_minus_one_value));
1369 const __m128i last = _mm_set1_epi8(
static_cast<int8_t
>(end_nibble));
1370 const __m128i active = _mm_and_si128(_mm_cmpgt_epi8(idx, first_minus_one),
1371 _mm_cmpgt_epi8(last, idx));
1372 const __m128i masked_candidates =
1373 _mm_blendv_epi8(_mm_set1_epi8(127), partial_candidates, active);
1375 __m128i min128 = masked_candidates;
1376 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 8));
1377 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 4));
1378 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 2));
1379 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 1));
1381 const int candidate_min =
1382 static_cast<int>(
static_cast<int8_t
>(_mm_extract_epi8(min128, 0)));
1383 if (candidate_min < best) {
1384 const __m128i equal_min = _mm_cmpeq_epi8(
1385 masked_candidates, _mm_set1_epi8(
static_cast<int8_t
>(candidate_min)));
1386 const uint32_t equal_mask =
1387 static_cast<uint32_t
>(_mm_movemask_epi8(equal_min));
1388 const uint32_t nibble_index = std::countr_zero(equal_mask);
1389 const uint8_t nibble =
1390 static_cast<uint8_t
>((s[0] >> (nibble_index * 4u)) & 0xFu);
1391 best = candidate_min;
1392 if (right_partial_width != 0 && nibble_index == last_full_nibble) {
1395 size_t local_offset = 1;
1396 for (
size_t i = 0; i < right_partial_width; ++i) {
1397 local += ((nibble >> i) & 1u) != 0 ? 1 : -1;
1398 if (i == 0 || local < local_best) {
1400 local_offset = i + 1;
1403 best_offset =
static_cast<size_t>(nibble_index) * 4u + local_offset;
1405 best_offset =
static_cast<size_t>(nibble_index) * 4u +
1406 static_cast<size_t>(excess_nibble_min_offset[nibble]);
1410 bit = end_nibble * 4;
1413 for (; bit < right; ++bit) {
1414 current += ((s[0] >> bit) & 1ull) != 0 ? 1 : -1;
1415 const size_t offset = bit + 1;
1416 if (current < best) {
1418 best_offset = offset;
1423 for (
size_t bit = left; bit < right; ++bit) {
1424 current += ((s[0] >> bit) & 1ull) != 0 ? 1 : -1;
1425 const size_t offset = bit + 1;
1426 if (current < best) {
1428 best_offset = offset;
1433 return {best, best_offset};
1442static inline ExcessResult excess_min_128(
const uint64_t* s,
1444 size_t right)
noexcept {
1448 left = std::min<size_t>(left, 128);
1449 right = std::min<size_t>(right, 128);
1451 if (right - left <= 32 && (left & 7u) == 0 && (right & 7u) == 0)
1453 return excess_min_128_byte_lut_short(s, left, right);
1456 int best = prefix_excess_128(s, left);
1457 size_t best_offset = left;
1458 if (left == right) {
1459 return {best, best_offset};
1462#ifdef PIXIE_AVX2_SUPPORT
1465 for (; bit < right && (bit & 3u) != 0; ++bit) {
1466 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1467 const size_t offset = bit + 1;
1468 if (current < best) {
1470 best_offset = offset;
1474 const size_t first_nibble = bit >> 2;
1475 const size_t last_full_nibble = right >> 2;
1476 const size_t right_partial_width = bit < right ? (right & 3u) : 0;
1477 const size_t end_nibble =
1478 last_full_nibble + (right_partial_width == 0 ? 0 : 1);
1479 if (first_nibble < end_nibble) {
1480 const __m128i bytes = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(s));
1481 const __m128i lo_nibbles = _mm_and_si128(bytes, excess_lut_nibble_mask_sse);
1482 const __m128i hi_nibbles =
1483 _mm_and_si128(_mm_srli_epi16(bytes, 4), excess_lut_nibble_mask_sse);
1484 const __m128i lo_delta = _mm_shuffle_epi8(excess_lut_delta_sse, lo_nibbles);
1485 const __m128i hi_delta = _mm_shuffle_epi8(excess_lut_delta_sse, hi_nibbles);
1486 const __m128i byte_delta = _mm_add_epi8(lo_delta, hi_delta);
1487 const __m128i byte_prefix = excess_prefix_sum_16x_i8(byte_delta);
1488 const __m128i byte_prefix_before = _mm_slli_si128(byte_prefix, 1);
1490 __m128i lo_local_min = _mm_shuffle_epi8(excess_lut_min_sse, lo_nibbles);
1491 __m128i hi_local_min = _mm_shuffle_epi8(excess_lut_min_sse, hi_nibbles);
1493 const __m128i byte_index = excess_lut_nibble_index_sse;
1494 if (right_partial_width != 0) {
1495 const bool partial_is_high = (last_full_nibble & 1u) != 0;
1496 const size_t partial_byte = last_full_nibble >> 1;
1497 const __m128i partial_source = partial_is_high ? hi_nibbles : lo_nibbles;
1498 __m128i partial_min =
1499 _mm_shuffle_epi8(excess_lut_pos0_sse, partial_source);
1500 if (right_partial_width >= 2) {
1501 partial_min = _mm_min_epi8(
1502 partial_min, _mm_shuffle_epi8(excess_lut_pos1_sse, partial_source));
1504 if (right_partial_width >= 3) {
1505 partial_min = _mm_min_epi8(
1506 partial_min, _mm_shuffle_epi8(excess_lut_pos2_sse, partial_source));
1508 const __m128i partial_lane = _mm_cmpeq_epi8(
1509 byte_index, _mm_set1_epi8(
static_cast<int8_t
>(partial_byte)));
1510 if (partial_is_high) {
1511 hi_local_min = _mm_blendv_epi8(hi_local_min, partial_min, partial_lane);
1513 lo_local_min = _mm_blendv_epi8(lo_local_min, partial_min, partial_lane);
1517 const __m128i lo_candidates =
1518 _mm_add_epi8(byte_prefix_before, lo_local_min);
1519 const __m128i hi_candidates =
1520 _mm_add_epi8(_mm_add_epi8(byte_prefix_before, lo_delta), hi_local_min);
1522 __m128i masked_lo = lo_candidates;
1523 __m128i masked_hi = hi_candidates;
1524 if (first_nibble != 0 || end_nibble != 32) {
1525 const __m128i first_minus_one = _mm_set1_epi8(
1526 static_cast<int8_t
>(
static_cast<int>(first_nibble) - 1));
1527 const __m128i last = _mm_set1_epi8(
static_cast<int8_t
>(end_nibble));
1528 const __m128i lo_active = _mm_and_si128(
1529 _mm_cmpgt_epi8(excess_lut_low_nibble_index_sse, first_minus_one),
1530 _mm_cmpgt_epi8(last, excess_lut_low_nibble_index_sse));
1531 const __m128i hi_active = _mm_and_si128(
1532 _mm_cmpgt_epi8(excess_lut_high_nibble_index_sse, first_minus_one),
1533 _mm_cmpgt_epi8(last, excess_lut_high_nibble_index_sse));
1534 masked_lo = _mm_blendv_epi8(_mm_set1_epi8(127), lo_candidates, lo_active);
1535 masked_hi = _mm_blendv_epi8(_mm_set1_epi8(127), hi_candidates, hi_active);
1538 const int candidate_min =
1539 excess_horizontal_min_i8(_mm_min_epi8(masked_lo, masked_hi));
1540 if (candidate_min < best) {
1541 const __m128i min_vec = _mm_set1_epi8(
static_cast<int8_t
>(candidate_min));
1542 const uint32_t lo_equal_mask =
static_cast<uint32_t
>(
1543 _mm_movemask_epi8(_mm_cmpeq_epi8(masked_lo, min_vec)));
1544 const uint32_t hi_equal_mask =
static_cast<uint32_t
>(
1545 _mm_movemask_epi8(_mm_cmpeq_epi8(masked_hi, min_vec)));
1546 const uint32_t lo_nibble_index =
1549 :
static_cast<uint32_t
>(std::countr_zero(lo_equal_mask)) * 2u;
1550 const uint32_t hi_nibble_index =
1553 :
static_cast<uint32_t
>(std::countr_zero(hi_equal_mask)) * 2u +
1555 const uint32_t nibble_index = std::min(lo_nibble_index, hi_nibble_index);
1556 const uint32_t byte_offset = nibble_index >> 1u;
1557 const uint64_t byte_word = s[byte_offset >> 3u];
1558 const uint8_t
byte =
static_cast<uint8_t
>(
1559 (byte_word >> ((byte_offset & 7u) * 8u)) & 0xFFu);
1560 const uint8_t nibble = (nibble_index & 1u) == 0
1561 ?
static_cast<uint8_t
>(
byte & 0xFu)
1562 :
static_cast<uint8_t
>((
byte >> 4u) & 0xFu);
1563 const size_t local_offset =
1564 right_partial_width != 0 && nibble_index == last_full_nibble
1565 ?
static_cast<size_t>(
1566 excess_partial_nibble_min_offset_lut[right_partial_width]
1568 : static_cast<size_t>(excess_lut_min_offset[nibble]);
1569 best = candidate_min;
1570 best_offset =
static_cast<size_t>(nibble_index) * 4u + local_offset;
1575 for (
size_t bit = 0; bit < right; ++bit) {
1576 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1577 const size_t offset = bit + 1;
1578 if (offset >= left && current < best) {
1580 best_offset = offset;
1585 return {best, best_offset};
1602 const uint64_t* suffix_s,
1604 const uint64_t* prefix_s,
1605 size_t prefix_right)
noexcept {
1606 suffix_left = std::min<size_t>(suffix_left, 64);
1607 prefix_right = std::min<size_t>(prefix_right, 64);
1608 return {excess_min_64(suffix_s, suffix_left, 63),
1609 excess_min_64(prefix_s, 0, prefix_right)};
1631 const uint64_t* suffix_s,
1633 const uint64_t* prefix_s,
1634 size_t prefix_right)
noexcept {
1635 suffix_left = std::min<size_t>(suffix_left, 128);
1636 prefix_right = std::min<size_t>(prefix_right, 128);
1637 if (suffix_left <= prefix_right || suffix_left > 127 || prefix_right > 127) {
1638 return {excess_min_128(suffix_s, suffix_left, 127),
1639 excess_min_128(prefix_s, 0, prefix_right)};
1642#ifdef PIXIE_AVX2_SUPPORT
1645 int suffix_best = prefix_excess_128(suffix_s, suffix_left);
1647 size_t suffix_bit = suffix_left;
1648 int suffix_current = suffix_best;
1649 for (; suffix_bit < 127 && (suffix_bit & 3u) != 0; ++suffix_bit) {
1651 ((suffix_s[suffix_bit >> 6] >> (suffix_bit & 63)) & 1ull) != 0 ? 1 : -1;
1652 const size_t offset = suffix_bit + 1;
1653 if (suffix_current < suffix.min_excess) {
1654 suffix = {suffix_current, offset};
1658 const size_t prefix_last_nibble = prefix_right >> 2;
1659 const size_t prefix_partial_width = prefix_right & 3u;
1660 const size_t prefix_end_nibble =
1661 prefix_last_nibble + (prefix_partial_width == 0 ? 0 : 1);
1662 const size_t suffix_first_nibble = suffix_bit < 127 ? suffix_bit >> 2 : 32;
1663 const int prefix_artificial_delta =
1664 prefix_excess_128(prefix_s, prefix_end_nibble * 4);
1666 const __m256i idx = excess_lut_nibble_index;
1667 const __m256i prefix_active = _mm256_cmpgt_epi8(
1668 _mm256_set1_epi8(
static_cast<int8_t
>(prefix_end_nibble)), idx);
1669 const __m256i suffix_active = _mm256_cmpgt_epi8(
1670 idx, _mm256_set1_epi8(
static_cast<int8_t
>(suffix_first_nibble) - 1));
1672 const __m256i prefix_nibbles = excess_nibbles_128_avx2(prefix_s);
1673 const __m256i suffix_nibbles = excess_nibbles_128_avx2(suffix_s);
1675 _mm256_blendv_epi8(_mm256_set1_epi8(3), prefix_nibbles, prefix_active);
1676 nibbles = _mm256_blendv_epi8(nibbles, suffix_nibbles, suffix_active);
1678 __m256i ps = _mm256_shuffle_epi8(excess_lut_delta, nibbles);
1679 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 1));
1680 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 2));
1681 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 4));
1682 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 8));
1684 __m128i ps_lo = _mm256_castsi256_si128(ps);
1685 __m128i ps_hi = _mm256_extracti128_si256(ps, 1);
1687 _mm_set1_epi8(
static_cast<int8_t
>(_mm_extract_epi8(ps_lo, 15)));
1688 ps_hi = _mm_add_epi8(ps_hi, carry);
1689 ps = _mm256_inserti128_si256(_mm256_castsi128_si256(ps_lo), ps_hi, 1);
1691 __m256i b = _mm256_permute2x128_si256(ps, ps, 0x08);
1692 const __m256i excl_ps = _mm256_alignr_epi8(ps, b, 15);
1694 __m256i local_min = _mm256_shuffle_epi8(excess_lut_min, nibbles);
1695 if (prefix_partial_width != 0) {
1696 __m256i partial_min = _mm256_shuffle_epi8(excess_lut_pos0, nibbles);
1697 if (prefix_partial_width >= 2) {
1698 partial_min = _mm256_min_epi8(
1699 partial_min, _mm256_shuffle_epi8(excess_lut_pos1, nibbles));
1701 if (prefix_partial_width >= 3) {
1702 partial_min = _mm256_min_epi8(
1703 partial_min, _mm256_shuffle_epi8(excess_lut_pos2, nibbles));
1705 local_min = _mm256_blendv_epi8(
1706 local_min, partial_min,
1708 idx, _mm256_set1_epi8(
static_cast<int8_t
>(prefix_last_nibble))));
1710 __m256i suffix_partial_min = _mm256_min_epi8(
1711 _mm256_shuffle_epi8(excess_lut_pos0, nibbles),
1712 _mm256_min_epi8(_mm256_shuffle_epi8(excess_lut_pos1, nibbles),
1713 _mm256_shuffle_epi8(excess_lut_pos2, nibbles)));
1714 const __m256i suffix_tail_active = _mm256_and_si256(
1715 suffix_active, _mm256_cmpeq_epi8(idx, _mm256_set1_epi8(31)));
1717 _mm256_blendv_epi8(local_min, suffix_partial_min, suffix_tail_active);
1719 const __m256i base_candidates = _mm256_add_epi8(excl_ps, local_min);
1720 const __m256i sentinel = _mm256_set1_epi8(127);
1722 const __m256i prefix_candidates =
1723 _mm256_blendv_epi8(sentinel, base_candidates, prefix_active);
1724 const __m256i suffix_candidates = _mm256_blendv_epi8(
1726 _mm256_add_epi8(base_candidates,
1727 _mm256_set1_epi8(
static_cast<int8_t
>(
1728 suffix_current - prefix_artificial_delta))),
1731 auto reduce_min = [](__m256i values) {
1732 __m128i min128 = _mm_min_epi8(_mm256_castsi256_si128(values),
1733 _mm256_extracti128_si256(values, 1));
1734 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 8));
1735 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 4));
1736 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 2));
1737 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 1));
1738 return static_cast<int>(
static_cast<int8_t
>(_mm_extract_epi8(min128, 0)));
1741 auto local_offset = [](uint8_t nibble,
size_t width_value) {
1742 if (width_value == 0 || width_value == 4) {
1743 return static_cast<size_t>(excess_lut_min_offset[nibble]);
1747 size_t best_offset = 1;
1748 for (
size_t i = 0; i < width_value; ++i) {
1749 current += ((nibble >> i) & 1u) != 0 ? 1 : -1;
1750 if (i == 0 || current < best) {
1752 best_offset = i + 1;
1758 const int prefix_min = reduce_min(prefix_candidates);
1759 if (prefix_min < prefix.min_excess) {
1760 const uint32_t mask =
static_cast<uint32_t
>(_mm256_movemask_epi8(
1761 _mm256_cmpeq_epi8(prefix_candidates,
1762 _mm256_set1_epi8(
static_cast<int8_t
>(prefix_min)))));
1763 const uint32_t prefix_lane = std::countr_zero(mask);
1764 const uint64_t word = prefix_s[prefix_lane >> 4];
1765 const uint8_t nibble =
1766 static_cast<uint8_t
>((word >> ((prefix_lane & 15u) * 4u)) & 0xFu);
1767 prefix.min_excess = prefix_min;
1768 const size_t width =
1769 prefix_partial_width != 0 && prefix_lane == prefix_last_nibble
1770 ? prefix_partial_width
1773 static_cast<size_t>(prefix_lane) * 4u + local_offset(nibble, width);
1776 const int suffix_min = reduce_min(suffix_candidates);
1777 if (suffix_min < suffix.min_excess) {
1778 const uint32_t mask =
static_cast<uint32_t
>(_mm256_movemask_epi8(
1779 _mm256_cmpeq_epi8(suffix_candidates,
1780 _mm256_set1_epi8(
static_cast<int8_t
>(suffix_min)))));
1781 const uint32_t suffix_lane = std::countr_zero(mask);
1782 const uint64_t word = suffix_s[suffix_lane >> 4];
1783 const uint8_t nibble =
1784 static_cast<uint8_t
>((word >> ((suffix_lane & 15u) * 4u)) & 0xFu);
1785 suffix.min_excess = suffix_min;
1786 const size_t width = suffix_lane == 31 ? 3 : 4;
1788 static_cast<size_t>(suffix_lane) * 4u + local_offset(nibble, width);
1791 return {suffix, prefix};
1793 return {excess_min_128(suffix_s, suffix_left, 127),
1794 excess_min_128(prefix_s, 0, prefix_right)};
1813static inline size_t forward_search_128(
const uint64_t* s,
1815 size_t start_offset,
1816 int* block_excess =
nullptr) noexcept {
1818 const int delta = excess_positions_128(s, target_x, out);
1819 if (block_excess !=
nullptr) {
1820 *block_excess = delta;
1822 if (start_offset >= 128) {
1826 const size_t first_word = start_offset >> 6;
1827 const size_t first_bit = start_offset & 63;
1828 for (
size_t word = first_word; word < 2; ++word) {
1829 uint64_t mask = out[word];
1830 if (word == first_word && first_bit != 0) {
1831 mask &= ~first_bits_mask(first_bit);
1834 return word * 64 + std::countr_zero(mask);
1857static inline size_t backward_search_128(
const uint64_t* s,
1860 int* block_excess =
nullptr) noexcept {
1862 const int delta = excess_positions_128(s, target_x, out);
1863 if (block_excess !=
nullptr) {
1864 *block_excess = delta;
1866 if (end_offset == 0) {
1870 const size_t max_prefix_length = end_offset - 1;
1871 if (max_prefix_length > 0) {
1872 const size_t last_bit_index = max_prefix_length - 1;
1873 size_t word = last_bit_index >> 6;
1874 const size_t bit_in_word = last_bit_index & 63;
1875 uint64_t mask = out[word] & first_bits_mask(bit_in_word + 1);
1878 return word * 64 + (63 - std::countl_zero(mask)) + 1;
1887 return target_x == 0 ? 0 : 128;
1902static inline void excess_positions_512(
const uint64_t* s,
1904 uint64_t* out)
noexcept {
1905 if (target_x < -512 || target_x > 512) {
1906 out[0] = out[1] = out[2] = out[3] = 0;
1907 out[4] = out[5] = out[6] = out[7] = 0;
1911 for (
int k = 0; k < 4; ++k) {
1912 target_x -= excess_positions_128(s + 2 * k, target_x, out + 2 * k);
1929static inline void excess_record_lows_128(
const uint64_t* s,
1930 uint64_t* out)
noexcept {
1931 out[0] = out[1] = 0;
1934 for (
size_t i = 0; i < 128; ++i) {
1935 const uint64_t w = s[i >> 6];
1936 const int bit =
static_cast<int>((w >> (i & 63)) & 1ull);
1937 cur += bit ? +1 : -1;
1940 out[i >> 6] |= (uint64_t{1} << (i & 63));
1953static inline void excess_record_lows_128_byte_lut(
const uint64_t* s,
1954 uint64_t* out)
noexcept {
1955 out[0] = out[1] = 0;
1958 for (
size_t byte_idx = 0; byte_idx < 16; ++byte_idx) {
1959 const size_t bit_base = byte_idx * 8;
1960 const uint8_t
byte =
1961 static_cast<uint8_t
>((s[bit_base >> 6] >> (bit_base & 63)) & 0xFFu);
1962 const int byte_min = excess_byte_min_lut[byte];
1963 if (cur + byte_min < best) {
1964 for (
size_t i = 0; i < 8; ++i) {
1965 const int bit =
static_cast<int>((
byte >> i) & 1u);
1966 cur += bit ? +1 : -1;
1969 const size_t pos = bit_base + i;
1970 out[pos >> 6] |= (uint64_t{1} << (pos & 63));
1974 cur += excess_byte_delta_lut[byte];
1987static inline void excess_record_lows_128_lut(
const uint64_t* s,
1988 uint64_t* out)
noexcept {
1989 out[0] = out[1] = 0;
1992 for (
size_t byte_idx = 0; byte_idx < 16; ++byte_idx) {
1993 const size_t bit_base = byte_idx * 8;
1994 const uint8_t
byte =
1995 static_cast<uint8_t
>((s[bit_base >> 6] >> (bit_base & 63)) & 0xFFu);
1996 const int gap = cur - best;
1997 const int idx = gap > 7 ? 7 : (gap < 0 ? 0 : gap);
1998 const uint8_t mask =
1999 excess_byte_record_lows_lut[byte][
static_cast<size_t>(idx)];
2004 uint8_t local_mask = 0;
2005 for (
int bit = 0; bit < 8; ++bit) {
2006 local += ((
byte >> bit) & 1u) ? 1 : -1;
2007 if (local < local_best) {
2009 local_mask |=
static_cast<uint8_t
>(1u << bit);
2013 uint8_t out_mask = 0;
2015 for (
int bit = 0; bit < 8; ++bit) {
2016 local += ((
byte >> bit) & 1u) ? 1 : -1;
2017 if (cur + local < best) {
2018 out_mask |=
static_cast<uint8_t
>(1u << bit);
2022 if (out_mask != 0) {
2023 const uint64_t word =
static_cast<uint64_t
>(out_mask)
2025 out[bit_base >> 6] |= word;
2028 cur += excess_byte_delta_lut[byte];
2032#ifdef PIXIE_AVX2_SUPPORT
2040static inline void excess_record_lows_128_avx2(
const uint64_t* s,
2041 uint64_t* out)
noexcept {
2042 out[0] = out[1] = 0;
2046 const __m256i masks = excess_bit_masks_16x_i16();
2047 const __m256i zero = _mm256_setzero_si256();
2048 const __m256i pos = _mm256_set1_epi16(1);
2049 const __m256i neg = _mm256_set1_epi16(-1);
2051 for (
size_t chunk = 0; chunk < 8; ++chunk) {
2052 const size_t chunk_bit = chunk * 16;
2053 const uint16_t bits =
2055 ?
static_cast<uint16_t
>((s[0] >> (chunk * 16)) & 0xFFFFu)
2056 : static_cast<uint16_t>((s[1] >> ((chunk - 4) * 16)) & 0xFFFFu);
2058 const __m256i vb = _mm256_set1_epi16(
static_cast<int16_t
>(bits));
2059 const __m256i m = _mm256_and_si256(vb, masks);
2060 const __m256i is_zero = _mm256_cmpeq_epi16(m, zero);
2061 const __m256i steps = _mm256_blendv_epi8(pos, neg, is_zero);
2062 const __m256i pref_rel = excess_prefix_sum_16x_i16(steps);
2063 const __m256i pref_abs = _mm256_add_epi16(
2064 pref_rel, _mm256_set1_epi16(
static_cast<int16_t
>(cur)));
2066 alignas(32) int16_t vals[16];
2067 _mm256_store_si256(
reinterpret_cast<__m256i*
>(vals), pref_abs);
2069 for (
size_t lane = 0; lane < 16; ++lane) {
2070 const int val = vals[lane];
2073 const size_t pos_idx = chunk_bit + lane;
2074 out[pos_idx >> 6] |= (uint64_t{1} << (pos_idx & 63));
2078 cur += 2 *
static_cast<int>(std::popcount(bits)) - 16;
2091static inline void excess_record_lows_128_nibble_lut(
const uint64_t* s,
2092 uint64_t* out)
noexcept {
2093 out[0] = out[1] = 0;
2097 const __m256i vdelta = excess_lut_delta;
2098 const __m256i vmin = excess_lut_min;
2100 __m256i nibbles = excess_nibbles_128_avx2(s);
2103 __m256i ps = _mm256_shuffle_epi8(vdelta, nibbles);
2104 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 1));
2105 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 2));
2106 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 4));
2107 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 8));
2109 __m128i ps_lo = _mm256_castsi256_si128(ps);
2110 __m128i ps_hi = _mm256_extracti128_si256(ps, 1);
2112 _mm_set1_epi8(
static_cast<int8_t
>(_mm_extract_epi8(ps_lo, 15)));
2113 ps_hi = _mm_add_epi8(ps_hi, carry);
2114 ps = _mm256_inserti128_si256(_mm256_castsi128_si256(ps_lo), ps_hi, 1);
2117 __m256i b = _mm256_permute2x128_si256(ps, ps, 0x08);
2118 __m256i excl_ps = _mm256_alignr_epi8(ps, b, 15);
2121 __m256i local_min = _mm256_shuffle_epi8(vmin, nibbles);
2123 alignas(32) int8_t excl[32];
2124 _mm256_store_si256(
reinterpret_cast<__m256i*
>(excl), excl_ps);
2126 alignas(32) int8_t nibble_min[32];
2127 _mm256_store_si256(
reinterpret_cast<__m256i*
>(nibble_min), local_min);
2129 alignas(32) int8_t nibble_vals[32];
2130 _mm256_store_si256(
reinterpret_cast<__m256i*
>(nibble_vals), nibbles);
2132 for (
int n = 0; n < 32; ++n) {
2133 const int nibble_base = cur + excl[n];
2134 const int nibble_best = nibble_base + nibble_min[n];
2135 if (nibble_best < best) {
2137 const uint8_t nibble =
static_cast<uint8_t
>(nibble_vals[n]);
2139 for (
int bit = 0; bit < 4; ++bit) {
2140 local += ((nibble >> bit) & 1u) ? 1 : -1;
2141 const int val = nibble_base + local;
2144 const size_t pos =
static_cast<size_t>(n) * 4 + bit;
2145 out[pos >> 6] |= (uint64_t{1} << (pos & 63));
2165static inline void excess_record_lows_512(
const uint64_t* s,
2166 uint64_t* out)
noexcept {
2167 out[0] = out[1] = out[2] = out[3] = 0;
2168 out[4] = out[5] = out[6] = out[7] = 0;
2171 for (
int k = 0; k < 4; ++k) {
2172 const uint64_t* block = s + 2 * k;
2173 uint64_t* block_out = out + 2 * k;
2174 for (
size_t i = 0; i < 128; ++i) {
2175 const uint64_t w = block[i >> 6];
2176 const int bit =
static_cast<int>((w >> (i & 63)) & 1ull);
2177 global += bit ? +1 : -1;
2178 if (global < best) {
2180 block_out[i >> 6] |= (uint64_t{1} << (i & 63));
2197static inline void rank_32x8(
const uint8_t* x, uint8_t* result) {
2198#ifdef PIXIE_AVX512_SUPPORT
2200 popcount_32x8(x, result);
2201 __m256i prefix_sums = _mm256_loadu_si256((__m256i
const*)result);
2202 const __m256i zero = _mm256_setzero_si256();
2204 prefix_sums = _mm256_add_epi8(prefix_sums,
2205 _mm256_alignr_epi8(prefix_sums, zero, 16 - 1));
2206 prefix_sums = _mm256_add_epi8(prefix_sums,
2207 _mm256_alignr_epi8(prefix_sums, zero, 16 - 2));
2208 prefix_sums = _mm256_add_epi8(prefix_sums,
2209 _mm256_alignr_epi8(prefix_sums, zero, 16 - 4));
2210 prefix_sums = _mm256_add_epi8(prefix_sums,
2211 _mm256_alignr_epi8(prefix_sums, zero, 16 - 8));
2215 __m128i low_lane = _mm256_extracti128_si256(prefix_sums, 0);
2216 __m128i high_lane = _mm256_extracti128_si256(prefix_sums, 1);
2217 auto last_val_low = _mm_extract_epi8(low_lane, 15);
2218 __m128i add_to_high = _mm_set1_epi8(last_val_low);
2219 high_lane = _mm_add_epi8(high_lane, add_to_high);
2220 prefix_sums = _mm256_set_m128i(high_lane, low_lane);
2221 _mm256_storeu_epi8(result, prefix_sums);
2224 result[0] = std::popcount(x[0]);
2225 for (
size_t i = 1; i < 32; ++i) {
2226 result[i] = std::popcount(x[i]) + result[i - 1];
Pair of boundary minimum results for adjacent BP query blocks.
Definition bits.h:991
Minimum prefix excess in a 128-bit bitstring range.
Definition bits.h:980