13#if defined(__AVX512VPOPCNTDQ__) && defined(__AVX512F__) && \
15#define PIXIE_AVX512_SUPPORT
18#if defined(__BMI2__) && !defined(PIXIE_DISABLE_BMI2)
19#define PIXIE_BMI2_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 size_t last_uint = count < 512 ? count >> 6 : 8;
311 uint64_t pop_val = 0;
313 for (
size_t 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))
325struct PixieSelectByteLut {
326 uint8_t popcounts[256];
327 uint8_t select[256][8];
329 constexpr PixieSelectByteLut() : popcounts{}, select{} {
330 for (
int byte = 0;
byte < 256; ++byte) {
331 for (
int rank = 0; rank < 8; ++rank) {
332 select[byte][rank] = 8;
336 for (
int bit = 0; bit < 8; ++bit) {
337 if (((
byte >> bit) & 1) != 0) {
338 select[byte][count++] =
static_cast<uint8_t
>(bit);
341 popcounts[byte] =
static_cast<uint8_t
>(count);
348static inline uint64_t select_64_no_bmi2(uint64_t x, uint64_t rank) {
351 uint64_t count = std::popcount(
static_cast<uint32_t
>(x));
358 count = std::popcount(
static_cast<uint16_t
>(x));
365 const auto low_byte =
static_cast<uint8_t
>(x);
366 count = pixie_select_byte_lut.popcounts[low_byte];
373 return offset + pixie_select_byte_lut.select[
static_cast<uint8_t
>(x)][rank];
385static inline uint64_t select_64(uint64_t x, uint64_t rank) {
386#ifdef PIXIE_BMI2_SUPPORT
387 return std::countr_zero(_pdep_u64(1ull << rank, x));
389 return select_64_no_bmi2(x, rank);
393template <
bool Invert>
394static inline uint64_t select_512_word_count(uint64_t word) {
395 if constexpr (Invert) {
396 return std::popcount(~word);
398 return std::popcount(word);
402template <
bool Invert>
403static inline uint64_t select_512_selected_word(uint64_t word) {
404 if constexpr (Invert) {
411template <
bool Invert>
412static inline uint64_t select_512_scalar_impl(
const uint64_t* x,
416 if constexpr (Invert) {
417 count = std::popcount(~x[0]);
419 count = std::popcount(x[0]);
421 while (word < 7 && count <= rank) {
424 if constexpr (Invert) {
425 count = std::popcount(~x[word]);
427 count = std::popcount(x[word]);
431 if constexpr (Invert) {
432 return word * 64 + select_64(~x[word], rank);
434 return word * 64 + select_64(x[word], rank);
438#ifdef PIXIE_AVX2_SUPPORT
439template <
bool Invert>
440static inline void select_512_avx2_counts(
const uint64_t* x, uint64_t* counts) {
441 const __m256i low_mask = _mm256_set1_epi8(0x0F);
442 const __m256i zero = _mm256_setzero_si256();
443 const __m256i sixty_four = _mm256_set1_epi64x(64);
445 for (
int half = 0; half < 2; ++half) {
446 const __m256i words =
447 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x + 4 * half));
449 const __m256i low_nibbles = _mm256_and_si256(words, low_mask);
450 const __m256i high_nibbles =
451 _mm256_and_si256(_mm256_srli_epi16(words, 4), low_mask);
452 const __m256i byte_counts =
453 _mm256_add_epi8(_mm256_shuffle_epi8(lookup_popcount_4, low_nibbles),
454 _mm256_shuffle_epi8(lookup_popcount_4, high_nibbles));
455 __m256i word_counts = _mm256_sad_epu8(byte_counts, zero);
456 if constexpr (Invert) {
457 word_counts = _mm256_sub_epi64(sixty_four, word_counts);
459 _mm256_store_si256(
reinterpret_cast<__m256i*
>(counts + 4 * half),
464template <
bool Invert>
465static inline uint64_t select_512_avx2_impl(
const uint64_t* x, uint64_t rank) {
466 alignas(32) uint64_t counts[8];
467 select_512_avx2_counts<Invert>(x, counts);
469 for (
size_t i = 0; i < 8; ++i) {
470 if (rank < counts[i]) {
471 return i * 64 + select_64(select_512_selected_word<Invert>(x[i]), rank);
479#ifdef PIXIE_AVX512_SUPPORT
480static inline __m512i select_512_avx512_prefix_sum_u64(__m512i prefix) {
481 const __m512i idx_shift1 = _mm512_set_epi64(6, 5, 4, 3, 2, 1, 0, 0);
482 const __m512i idx_shift2 = _mm512_set_epi64(5, 4, 3, 2, 1, 0, 0, 0);
483 const __m512i idx_shift4 = _mm512_set_epi64(3, 2, 1, 0, 0, 0, 0, 0);
485 __m512i tmp = _mm512_maskz_permutexvar_epi64(0xFE, idx_shift1, prefix);
486 prefix = _mm512_add_epi64(prefix, tmp);
487 tmp = _mm512_maskz_permutexvar_epi64(0xFC, idx_shift2, prefix);
488 prefix = _mm512_add_epi64(prefix, tmp);
489 tmp = _mm512_maskz_permutexvar_epi64(0xF0, idx_shift4, prefix);
490 return _mm512_add_epi64(prefix, tmp);
493static inline uint64_t select_512_avx512_previous_prefix(__m512i prefix,
498 const __m512i idx_previous =
499 _mm512_set1_epi64(
static_cast<int64_t
>(lane - 1));
500 const __m512i previous_vec = _mm512_permutexvar_epi64(idx_previous, prefix);
501 return static_cast<uint64_t
>(
502 _mm_cvtsi128_si64(_mm512_castsi512_si128(previous_vec)));
505template <
bool Invert>
506static inline uint64_t select_512_avx512_impl(
const uint64_t* x,
508 const __m512i words = _mm512_loadu_epi64(x);
509 __m512i prefix = _mm512_popcnt_epi64(words);
510 if constexpr (Invert) {
511 prefix = _mm512_sub_epi64(_mm512_set1_epi64(64), prefix);
513 prefix = select_512_avx512_prefix_sum_u64(prefix);
515 const __mmask8 mask = _mm512_cmpgt_epu64_mask(
516 prefix, _mm512_set1_epi64(
static_cast<int64_t
>(rank)));
517 const uint32_t lane = _tzcnt_u32(
static_cast<uint32_t
>(mask));
518 const uint64_t previous = select_512_avx512_previous_prefix(prefix, lane);
520 select_64(select_512_selected_word<Invert>(x[lane]), rank - previous);
536static inline uint64_t select_512(
const uint64_t* x, uint64_t rank) {
537#ifdef PIXIE_AVX512_SUPPORT
538 return select_512_avx512_impl<false>(x, rank);
543 return select_512_scalar_impl<false>(x, rank);
557static inline uint64_t select0_512(
const uint64_t* x, uint64_t rank0) {
558#ifdef PIXIE_AVX512_SUPPORT
559 return select_512_avx512_impl<true>(x, rank0);
563 return select_512_scalar_impl<true>(x, rank0);
571static inline uint16_t lower_bound_4x64(
const uint64_t* x, uint64_t y) {
572#ifdef PIXIE_AVX512_SUPPORT
574 auto y_4 = _mm256_set1_epi64x(y);
575 auto reg_256 = _mm256_loadu_epi64(x);
576 auto cmp = _mm256_cmpge_epu64_mask(reg_256, y_4);
578 return _tzcnt_u16(cmp);
581#ifdef PIXIE_AVX2_SUPPORT
583 auto y_4 = _mm256_set1_epi64x(y);
584 __m256i reg_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
586 const __m256i offset = _mm256_set1_epi64x(0x8000000000000000ULL);
587 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
588 __m256i y_offset = _mm256_xor_si256(y_4, offset);
589 auto mask = _mm256_movemask_epi8(_mm256_cmpgt_epi64(
590 x_offset, _mm256_sub_epi64(y_offset, _mm256_set1_epi64x(1))));
592 return _tzcnt_u32(mask) >> 3;
596 for (uint16_t i = 0; i < 4; ++i) {
620static inline uint16_t lower_bound_delta_4x64(
const uint64_t* x,
622 const uint64_t* delta_array,
623 uint64_t delta_scalar) {
624#ifdef PIXIE_AVX512_SUPPORT
626 const __m256i dlt_256 = _mm256_loadu_epi64(delta_array);
627 auto x_256 = _mm256_loadu_epi64(x);
628 auto dlt_4 = _mm256_set1_epi64x(delta_scalar);
629 auto y_4 = _mm256_set1_epi64x(y);
631 auto tmp = _mm256_add_epi64(dlt_4, dlt_256);
632 auto reg_256 = _mm256_sub_epi64(tmp, x_256);
633 auto cmp = _mm256_cmpge_epu64_mask(reg_256, y_4);
635 return _tzcnt_u16(cmp);
638#ifdef PIXIE_AVX2_SUPPORT
640 const __m256i dlt_256 =
641 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(delta_array));
642 auto x_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
643 auto dlt_4 = _mm256_set1_epi64x(delta_scalar);
644 auto y_4 = _mm256_set1_epi64x(y);
646 auto tmp = _mm256_add_epi64(dlt_4, dlt_256);
647 auto reg_256 = _mm256_sub_epi64(tmp, x_256);
649 const __m256i offset = _mm256_set1_epi64x(0x8000000000000000ULL);
650 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
651 __m256i y_offset = _mm256_xor_si256(y_4, offset);
652 auto mask = _mm256_movemask_epi8(_mm256_cmpgt_epi64(
653 x_offset, _mm256_sub_epi64(y_offset, _mm256_set1_epi64x(1))));
655 return _tzcnt_u32(mask) >> 3;
659 for (uint16_t i = 0; i < 4; ++i) {
660 if (delta_array[i] + delta_scalar - x[i] >= y) {
674static inline uint16_t lower_bound_8x64(
const uint64_t* x, uint64_t y) {
675#ifdef PIXIE_AVX512_SUPPORT
677 auto y_8 = _mm512_set1_epi64(y);
678 auto reg_512 = _mm512_loadu_epi64(x);
679 auto cmp = _mm512_cmpge_epu64_mask(reg_512, y_8);
681 return _tzcnt_u16(cmp);
684#ifdef PIXIE_AVX2_SUPPORT
686 uint16_t len = lower_bound_4x64(x, y);
692 return len + lower_bound_4x64(x + 4, y);
696 for (uint16_t i = 0; i < 8; ++i) {
720static inline uint16_t lower_bound_delta_8x64(
const uint64_t* x,
722 const uint64_t* delta_array,
723 uint64_t delta_scalar) {
724#ifdef PIXIE_AVX512_SUPPORT
726 const __m512i dlt_512 = _mm512_loadu_epi64(delta_array);
727 auto x_512 = _mm512_loadu_epi64(x);
728 auto dlt_8 = _mm512_set1_epi64(delta_scalar);
729 auto y_8 = _mm512_set1_epi64(y);
731 auto tmp = _mm512_add_epi64(dlt_8, dlt_512);
732 auto reg_512 = _mm512_sub_epi64(tmp, x_512);
733 auto cmp = _mm512_cmpge_epu64_mask(reg_512, y_8);
735 return _tzcnt_u16(cmp);
738#ifdef PIXIE_AVX2_SUPPORT
740 uint16_t len = lower_bound_delta_4x64(x, y, delta_array, delta_scalar);
746 return len + lower_bound_delta_4x64(x + 4, y, delta_array + 4, delta_scalar);
750 for (uint16_t i = 0; i < 8; ++i) {
751 if (delta_array[i] + delta_scalar - x[i] >= y) {
765static inline uint16_t lower_bound_32x16(
const uint16_t* x, uint16_t y) {
766#ifdef PIXIE_AVX512_SUPPORT
768 auto y_32 = _mm512_set1_epi16(y);
769 auto reg_512 = _mm512_loadu_epi16(x);
770 auto cmp = _mm512_cmplt_epu16_mask(reg_512, y_32);
771 return std::popcount(cmp);
774#ifdef PIXIE_AVX2_SUPPORT
776 auto y_16 = _mm256_set1_epi16(y);
777 __m256i reg_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
779 const __m256i offset = _mm256_set1_epi16(0x8000);
780 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
781 __m256i y_offset = _mm256_xor_si256(y_16, offset);
782 uint32_t mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
784 uint16_t count = std::popcount(mask) >> 1;
786 reg_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x + 16));
788 x_offset = _mm256_xor_si256(reg_256, offset);
789 mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
791 return count + (std::popcount(mask) >> 1);
796 for (uint16_t i = 0; i < 32; ++i) {
820static inline uint16_t lower_bound_delta_32x16(
const uint16_t* x,
822 const uint16_t* delta_array,
823 uint16_t delta_scalar) {
824#ifdef PIXIE_AVX512_SUPPORT
826 const __m512i dlt_512 = _mm512_loadu_epi64(delta_array);
827 auto x_512 = _mm512_loadu_epi64(x);
828 auto dlt_32 = _mm512_set1_epi16(delta_scalar);
829 auto y_32 = _mm512_set1_epi16(y);
831 auto tmp = _mm512_add_epi16(dlt_32, dlt_512);
832 auto reg_512 = _mm512_sub_epi16(tmp, x_512);
833 auto cmp = _mm512_cmplt_epu16_mask(reg_512, y_32);
834 return std::popcount(cmp);
837#ifdef PIXIE_AVX2_SUPPORT
840 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(delta_array));
841 auto x_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
842 auto dlt_16 = _mm256_set1_epi16(delta_scalar);
843 auto y_16 = _mm256_set1_epi16(y);
845 auto tmp = _mm256_add_epi16(dlt_16, dlt_256);
846 auto reg_256 = _mm256_sub_epi16(tmp, x_256);
848 const __m256i offset = _mm256_set1_epi16(0x8000);
849 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
850 __m256i y_offset = _mm256_xor_si256(y_16, offset);
851 uint32_t mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
853 uint16_t count = std::popcount(mask) >> 1;
856 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(delta_array + 16));
857 x_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x + 16));
859 tmp = _mm256_add_epi16(dlt_16, dlt_256);
860 reg_256 = _mm256_sub_epi16(tmp, x_256);
862 x_offset = _mm256_xor_si256(reg_256, offset);
863 mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
865 return count + (std::popcount(mask) >> 1);
870 for (uint16_t i = 0; i < 32; ++i) {
871 if (delta_array[i] + delta_scalar - x[i] < y) {
892static inline void popcount_64x4(
const uint8_t* x, uint8_t* result) {
893#ifdef PIXIE_AVX512_SUPPORT
894 __m256i data = _mm256_loadu_si256((__m256i
const*)x);
897 const __m256i low_bits_mask = _mm256_set1_epi8(0x0F);
900 __m256i low_bits = _mm256_and_si256(data, low_bits_mask);
901 __m256i low_count = _mm256_shuffle_epi8(lookup_popcount_4, low_bits);
904 __m256i high_bits = _mm256_srli_epi16(data, 4);
905 high_bits = _mm256_and_si256(high_bits, low_bits_mask);
906 __m256i high_count = _mm256_shuffle_epi8(lookup_popcount_4, high_bits);
910 _mm256_or_si256(low_count, _mm256_slli_epi16(high_count, 4));
911 _mm256_storeu_epi8(result, result_vec);
914 for (
size_t i = 0; i < 32; i++) {
916 uint8_t a = x[i] & 0x0F;
917 uint8_t low_count = std::popcount(a);
919 a = (x[i] >> 4) & 0x0F;
920 uint8_t high_count = std::popcount(a);
923 result[i] = low_count | (high_count << 4);
939static inline void popcount_32x8(
const uint8_t* x, uint8_t* result) {
940#ifdef PIXIE_AVX512_SUPPORT
942 __m256i data = _mm256_loadu_si256((__m256i
const*)x);
943 auto popcount_8 = _mm256_popcnt_epi8(data);
944 _mm256_storeu_si256((__m256i*)result, popcount_8);
946#ifdef PIXIE_AVX2_SUPPORT
948 __m256i data = _mm256_loadu_si256((__m256i
const*)x);
951 const __m256i low_bits_mask = _mm256_set1_epi8(0x0F);
954 __m256i low_bits = _mm256_and_si256(data, low_bits_mask);
955 __m256i low_count = _mm256_shuffle_epi8(lookup_popcount_4, low_bits);
958 __m256i high_bits = _mm256_srli_epi16(data, 4);
959 high_bits = _mm256_and_si256(high_bits, low_bits_mask);
960 __m256i high_count = _mm256_shuffle_epi8(lookup_popcount_4, high_bits);
962 __m256i result_vec = _mm256_add_epi8(low_count, high_count);
963 _mm256_storeu_si256((__m256i*)result, result_vec);
966 for (
size_t i = 0; i < 32; i++) {
967 result[i] = std::popcount(x[i]);
973#ifdef PIXIE_AVX2_SUPPORT
976static inline const __m256i excess_lut_delta = _mm256_setr_epi8(
987static inline const __m256i excess_lut_pos0 = _mm256_setr_epi8(
997static inline const __m256i excess_lut_pos1 = _mm256_setr_epi8(
1007static inline const __m256i excess_lut_pos2 = _mm256_setr_epi8(
1016static inline const __m256i excess_lut_min = _mm256_setr_epi8(
1025static inline constexpr int8_t excess_lut_min_offset[16] = {
1026 4, 4, 4, 4, 2, 2, 1, 1, 3, 3, 1, 1, 2, 2, 1, 1};
1027static inline const __m256i excess_lut_pack_multiplier =
1028 _mm256_set1_epi16(0x1001);
1029static inline const __m256i excess_lut_bit0 = _mm256_set1_epi8(1);
1030static inline const __m256i excess_lut_bit1 = _mm256_set1_epi8(2);
1031static inline const __m256i excess_lut_bit2 = _mm256_set1_epi8(4);
1032static inline const __m256i excess_lut_bit3 = _mm256_set1_epi8(8);
1033static inline const __m256i excess_lut_nibble_index = _mm256_setr_epi8(
1042static inline const __m128i excess_lut_nibble_mask = _mm_set1_epi8(0x0F);
1045static inline __m256i excess_nibbles_128_avx2(
const uint64_t* s)
noexcept {
1046 __m128i word_vec = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(s));
1047 __m128i lo_nibbles = _mm_and_si128(word_vec, excess_lut_nibble_mask);
1048 __m128i hi_nibbles =
1049 _mm_and_si128(_mm_srli_epi16(word_vec, 4), excess_lut_nibble_mask);
1051 __m128i unpack_lo = _mm_unpacklo_epi8(lo_nibbles, hi_nibbles);
1052 __m128i unpack_hi = _mm_unpackhi_epi8(lo_nibbles, hi_nibbles);
1054 return _mm256_inserti128_si256(_mm256_castsi128_si256(unpack_lo), unpack_hi,
1058static inline __m256i excess_bit_masks_16x_i16() noexcept {
1059 return _mm256_setr_epi16(0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020,
1060 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800,
1061 0x1000, 0x2000, 0x4000,
1062 static_cast<int16_t
>(0x8000));
1065static inline __m256i excess_prefix_sum_16x_i16(__m256i v)
noexcept {
1067 __m256i t = _mm256_slli_si256(x, 2);
1068 x = _mm256_add_epi16(x, t);
1069 t = _mm256_slli_si256(x, 4);
1070 x = _mm256_add_epi16(x, t);
1071 t = _mm256_slli_si256(x, 8);
1072 x = _mm256_add_epi16(x, t);
1074 __m128i lo = _mm256_extracti128_si256(x, 0);
1075 __m128i hi = _mm256_extracti128_si256(x, 1);
1076 const int16_t carry =
static_cast<int16_t
>(_mm_extract_epi16(lo, 7));
1077 hi = _mm_add_epi16(hi, _mm_set1_epi16(carry));
1079 __m256i out = _mm256_castsi128_si256(lo);
1080 return _mm256_inserti128_si256(out, hi, 1);
1094 size_t offset = 128;
1108constexpr int8_t excess_byte_delta_value(uint8_t x) {
1109 return static_cast<int8_t
>(2 * std::popcount(x) - 8);
1112constexpr int8_t excess_byte_min_prefix_value(uint8_t x) {
1115 for (
int bit = 0; bit < 8; ++bit) {
1116 cur += ((x >> bit) & 1u) != 0 ? 1 : -1;
1117 if (bit == 0 || cur < best) {
1121 return static_cast<int8_t
>(best);
1124constexpr int8_t excess_byte_min_prefix_offset_value(uint8_t x) {
1127 int best_offset = 1;
1128 for (
int bit = 0; bit < 8; ++bit) {
1129 cur += ((x >> bit) & 1u) != 0 ? 1 : -1;
1130 if (bit == 0 || cur < best) {
1132 best_offset = bit + 1;
1135 return static_cast<int8_t
>(best_offset);
1138constexpr int8_t excess_nibble_min_prefix_offset_value(uint8_t x,
int bits) {
1141 int best_offset = 1;
1142 for (
int bit = 0; bit < bits; ++bit) {
1143 cur += ((x >> bit) & 1u) != 0 ? 1 : -1;
1144 if (bit == 0 || cur < best) {
1146 best_offset = bit + 1;
1149 return static_cast<int8_t
>(best_offset);
1152template <
typename Fn>
1153constexpr std::array<int8_t, 256> excess_make_byte_lut(Fn fn) {
1154 std::array<int8_t, 256> out{};
1155 for (
size_t i = 0; i < out.size(); ++i) {
1156 out[i] = fn(
static_cast<uint8_t
>(i));
1161static inline constexpr std::array<int8_t, 256> excess_byte_delta_lut =
1162 excess_make_byte_lut([](uint8_t x) {
return excess_byte_delta_value(x); });
1163static inline constexpr std::array<int8_t, 256> excess_byte_min_lut =
1164 excess_make_byte_lut(
1165 [](uint8_t x) {
return excess_byte_min_prefix_value(x); });
1166static inline constexpr std::array<int8_t, 256> excess_byte_min_offset_lut =
1167 excess_make_byte_lut(
1168 [](uint8_t x) {
return excess_byte_min_prefix_offset_value(x); });
1169static inline constexpr std::array<std::array<int8_t, 16>, 4>
1170 excess_partial_nibble_min_offset_lut = [] {
1171 std::array<std::array<int8_t, 16>, 4> out{};
1172 for (
size_t width = 1; width < out.size(); ++width) {
1173 for (
size_t nibble = 0; nibble < out[width].size(); ++nibble) {
1174 out[width][nibble] = excess_nibble_min_prefix_offset_value(
1175 static_cast<uint8_t
>(nibble),
static_cast<int>(width));
1188constexpr uint8_t excess_byte_record_lows_mask(uint8_t
byte,
int threshold) {
1191 for (
int bit = 0; bit < 8; ++bit) {
1192 cur += ((
byte >> bit) & 1u) ? 1 : -1;
1193 if (cur < threshold) {
1194 mask |=
static_cast<uint8_t
>(1u << bit);
1208static inline constexpr std::array<std::array<uint8_t, 8>, 256>
1209 excess_byte_record_lows_lut = [] {
1210 std::array<std::array<uint8_t, 8>, 256> out{};
1211 for (
size_t byte = 0;
byte < 256; ++byte) {
1212 for (
int g = 0; g < 8; ++g) {
1214 excess_byte_record_lows_mask(
static_cast<uint8_t
>(
byte), -g);
1233static inline int excess_positions_128(
const uint64_t* s,
1235 uint64_t* out)
noexcept {
1236 out[0] = out[1] = 0;
1237 const int block_delta = 2 * (std::popcount(s[0]) + std::popcount(s[1])) - 128;
1239 if (target_x < -128 || target_x > 128) {
1243#ifdef PIXIE_AVX2_SUPPORT
1244 const __m256i vdelta = excess_lut_delta;
1245 const __m256i vpos0 = excess_lut_pos0;
1246 const __m256i vpos1 = excess_lut_pos1;
1247 const __m256i vpos2 = excess_lut_pos2;
1248 const __m256i vmult = excess_lut_pack_multiplier;
1249 const __m256i vbit0 = excess_lut_bit0;
1250 const __m256i vbit1 = excess_lut_bit1;
1251 const __m256i vbit2 = excess_lut_bit2;
1252 const __m256i vbit3 = excess_lut_bit3;
1254 const int d = 2 * target_x - block_delta;
1255 if (d < -128 || d > 128) {
1259 __m256i nibbles = excess_nibbles_128_avx2(s);
1261 __m256i ps = _mm256_shuffle_epi8(vdelta, nibbles);
1262 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 1));
1263 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 2));
1264 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 4));
1265 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 8));
1267 __m128i ps_lo = _mm256_castsi256_si128(ps);
1268 __m128i ps_hi = _mm256_extracti128_si256(ps, 1);
1269 __m128i carry = _mm_set1_epi8((int8_t)_mm_extract_epi8(ps_lo, 15));
1270 ps_hi = _mm_add_epi8(ps_hi, carry);
1271 ps = _mm256_inserti128_si256(_mm256_castsi128_si256(ps_lo), ps_hi, 1);
1273 __m256i b = _mm256_permute2x128_si256(ps, ps, 0x08);
1274 __m256i excl_ps = _mm256_alignr_epi8(ps, b, 15);
1276 __m256i vtgt = _mm256_set1_epi8((int8_t)target_x);
1277 __m256i t = _mm256_sub_epi8(vtgt, excl_ps);
1279 __m256i cmp0 = _mm256_cmpeq_epi8(_mm256_shuffle_epi8(vpos0, nibbles), t);
1280 __m256i cmp1 = _mm256_cmpeq_epi8(_mm256_shuffle_epi8(vpos1, nibbles), t);
1281 __m256i cmp2 = _mm256_cmpeq_epi8(_mm256_shuffle_epi8(vpos2, nibbles), t);
1282 __m256i cmp3 = _mm256_cmpeq_epi8(ps, vtgt);
1284 __m256i bit0 = _mm256_and_si256(cmp0, vbit0);
1285 __m256i bit1 = _mm256_and_si256(cmp1, vbit1);
1286 __m256i bit2 = _mm256_and_si256(cmp2, vbit2);
1287 __m256i bit3 = _mm256_and_si256(cmp3, vbit3);
1289 __m256i total_match =
1290 _mm256_or_si256(_mm256_or_si256(bit0, bit1), _mm256_or_si256(bit2, bit3));
1292 __m256i res = _mm256_maddubs_epi16(total_match, vmult);
1293 __m128i res_lo = _mm256_castsi256_si128(res);
1294 __m128i res_hi = _mm256_extracti128_si256(res, 1);
1295 __m128i packed = _mm_packus_epi16(res_lo, res_hi);
1297 _mm_storeu_si128((__m128i*)out, packed);
1300 for (
size_t i = 0; i < 128; ++i) {
1301 const uint64_t w = s[i >> 6];
1302 const int bit = int((w >> (i & 63)) & 1ull);
1303 cur += bit ? +1 : -1;
1304 if (cur == target_x) {
1305 out[i >> 6] |= (uint64_t{1} << (i & 63));
1321static inline int prefix_excess_128(
const uint64_t* s,
1322 size_t end_offset)
noexcept {
1323 end_offset = end_offset > 128 ? 128 : end_offset;
1324 if (end_offset == 0) {
1327 if (end_offset <= 64) {
1328 const int ones =
static_cast<int>(std::popcount(
1329 s[0] & first_bits_mask(
static_cast<uint32_t
>(end_offset))));
1330 return 2 * ones -
static_cast<int>(end_offset);
1332 const int ones =
static_cast<int>(
1333 std::popcount(s[0]) +
1334 std::popcount(s[1] &
1335 first_bits_mask(
static_cast<uint32_t
>(end_offset - 64))));
1336 return 2 * ones -
static_cast<int>(end_offset);
1348static inline int prefix_excess_64(
const uint64_t* s,
1349 size_t end_offset)
noexcept {
1350 end_offset = end_offset > 64 ? 64 : end_offset;
1351 if (end_offset == 0) {
1354 const int ones =
static_cast<int>(
1355 std::popcount(s[0] & first_bits_mask(
static_cast<uint32_t
>(end_offset))));
1356 return 2 * ones -
static_cast<int>(end_offset);
1359static inline ExcessResult excess_min_128_byte_lut_short(
1362 size_t right)
noexcept {
1363 int best = prefix_excess_128(s, left);
1364 size_t best_offset = left;
1365 if (left == right) {
1366 return {best, best_offset};
1371 for (; bit < right && (bit & 7u) != 0; ++bit) {
1372 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1373 const size_t offset = bit + 1;
1374 if (current < best) {
1376 best_offset = offset;
1380 for (; bit + 8 <= right; bit += 8) {
1381 const uint8_t
byte =
1382 static_cast<uint8_t
>((s[bit >> 6] >> (bit & 63)) & 0xFFu);
1383 const int candidate = current + excess_byte_min_lut[byte];
1384 if (candidate < best) {
1386 best_offset = bit +
static_cast<size_t>(excess_byte_min_offset_lut[byte]);
1388 current += excess_byte_delta_lut[byte];
1391 for (; bit < right; ++bit) {
1392 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1393 const size_t offset = bit + 1;
1394 if (current < best) {
1396 best_offset = offset;
1400 return {best, best_offset};
1417static inline ExcessResult excess_min_64(
const uint64_t* s,
1419 size_t right)
noexcept {
1423 left = std::min<size_t>(left, 64);
1424 right = std::min<size_t>(right, 64);
1426 int best = prefix_excess_64(s, left);
1427 size_t best_offset = left;
1428 if (left == right) {
1429 return {best, best_offset};
1432#ifdef PIXIE_SSE41_SUPPORT
1435 for (; bit < right && (bit & 3u) != 0; ++bit) {
1436 current += ((s[0] >> bit) & 1ull) != 0 ? 1 : -1;
1437 const size_t offset = bit + 1;
1438 if (current < best) {
1440 best_offset = offset;
1444 const size_t first_full_nibble = bit >> 2;
1445 const size_t last_full_nibble = right >> 2;
1446 const size_t right_partial_width = bit < right ? (right & 3u) : 0;
1447 const size_t end_nibble =
1448 last_full_nibble + (right_partial_width == 0 ? 0 : 1);
1449 if (first_full_nibble < end_nibble) {
1450 const __m128i nibbles = excess_nibbles_64_sse(s);
1452 __m128i ps = _mm_shuffle_epi8(excess_lut_delta_sse, nibbles);
1453 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 1));
1454 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 2));
1455 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 4));
1456 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 8));
1458 const __m128i excl_ps = _mm_slli_si128(ps, 1);
1459 __m128i local_min = _mm_shuffle_epi8(excess_lut_min_sse, nibbles);
1460 if (right_partial_width != 0) {
1461 __m128i partial_min = _mm_shuffle_epi8(excess_lut_pos0_sse, nibbles);
1462 if (right_partial_width >= 2) {
1463 partial_min = _mm_min_epi8(
1464 partial_min, _mm_shuffle_epi8(excess_lut_pos1_sse, nibbles));
1466 if (right_partial_width >= 3) {
1467 partial_min = _mm_min_epi8(
1468 partial_min, _mm_shuffle_epi8(excess_lut_pos2_sse, nibbles));
1470 local_min = _mm_blendv_epi8(
1471 local_min, partial_min,
1472 _mm_cmpeq_epi8(excess_lut_nibble_index_sse,
1473 _mm_set1_epi8(
static_cast<int8_t
>(last_full_nibble))));
1475 const __m128i partial_candidates = _mm_add_epi8(excl_ps, local_min);
1477 const __m128i idx = excess_lut_nibble_index_sse;
1478 const int first_minus_one_value =
static_cast<int>(first_full_nibble) - 1;
1479 const __m128i first_minus_one =
1480 _mm_set1_epi8(
static_cast<int8_t
>(first_minus_one_value));
1481 const __m128i last = _mm_set1_epi8(
static_cast<int8_t
>(end_nibble));
1482 const __m128i active = _mm_and_si128(_mm_cmpgt_epi8(idx, first_minus_one),
1483 _mm_cmpgt_epi8(last, idx));
1484 const __m128i masked_candidates =
1485 _mm_blendv_epi8(_mm_set1_epi8(127), partial_candidates, active);
1487 __m128i min128 = masked_candidates;
1488 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 8));
1489 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 4));
1490 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 2));
1491 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 1));
1493 const int candidate_min =
1494 static_cast<int>(
static_cast<int8_t
>(_mm_extract_epi8(min128, 0)));
1495 if (candidate_min < best) {
1496 const __m128i equal_min = _mm_cmpeq_epi8(
1497 masked_candidates, _mm_set1_epi8(
static_cast<int8_t
>(candidate_min)));
1498 const uint32_t equal_mask =
1499 static_cast<uint32_t
>(_mm_movemask_epi8(equal_min));
1500 const uint32_t nibble_index = std::countr_zero(equal_mask);
1501 const uint8_t nibble =
1502 static_cast<uint8_t
>((s[0] >> (nibble_index * 4u)) & 0xFu);
1503 best = candidate_min;
1504 if (right_partial_width != 0 && nibble_index == last_full_nibble) {
1507 size_t local_offset = 1;
1508 for (
size_t i = 0; i < right_partial_width; ++i) {
1509 local += ((nibble >> i) & 1u) != 0 ? 1 : -1;
1510 if (i == 0 || local < local_best) {
1512 local_offset = i + 1;
1515 best_offset =
static_cast<size_t>(nibble_index) * 4u + local_offset;
1517 best_offset =
static_cast<size_t>(nibble_index) * 4u +
1518 static_cast<size_t>(excess_nibble_min_offset[nibble]);
1522 bit = end_nibble * 4;
1525 for (; bit < right; ++bit) {
1526 current += ((s[0] >> bit) & 1ull) != 0 ? 1 : -1;
1527 const size_t offset = bit + 1;
1528 if (current < best) {
1530 best_offset = offset;
1535 for (
size_t bit = left; bit < right; ++bit) {
1536 current += ((s[0] >> bit) & 1ull) != 0 ? 1 : -1;
1537 const size_t offset = bit + 1;
1538 if (current < best) {
1540 best_offset = offset;
1545 return {best, best_offset};
1554static inline ExcessResult excess_min_128(
const uint64_t* s,
1556 size_t right)
noexcept {
1560 left = std::min<size_t>(left, 128);
1561 right = std::min<size_t>(right, 128);
1563 if (right - left <= 32 && (left & 7u) == 0 && (right & 7u) == 0)
1565 return excess_min_128_byte_lut_short(s, left, right);
1568 int best = prefix_excess_128(s, left);
1569 size_t best_offset = left;
1570 if (left == right) {
1571 return {best, best_offset};
1574#ifdef PIXIE_AVX2_SUPPORT
1577 for (; bit < right && (bit & 3u) != 0; ++bit) {
1578 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1579 const size_t offset = bit + 1;
1580 if (current < best) {
1582 best_offset = offset;
1586 const size_t first_nibble = bit >> 2;
1587 const size_t last_full_nibble = right >> 2;
1588 const size_t right_partial_width = bit < right ? (right & 3u) : 0;
1589 const size_t end_nibble =
1590 last_full_nibble + (right_partial_width == 0 ? 0 : 1);
1591 if (first_nibble < end_nibble) {
1592 const __m128i bytes = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(s));
1593 const __m128i lo_nibbles = _mm_and_si128(bytes, excess_lut_nibble_mask_sse);
1594 const __m128i hi_nibbles =
1595 _mm_and_si128(_mm_srli_epi16(bytes, 4), excess_lut_nibble_mask_sse);
1596 const __m128i lo_delta = _mm_shuffle_epi8(excess_lut_delta_sse, lo_nibbles);
1597 const __m128i hi_delta = _mm_shuffle_epi8(excess_lut_delta_sse, hi_nibbles);
1598 const __m128i byte_delta = _mm_add_epi8(lo_delta, hi_delta);
1599 const __m128i byte_prefix = excess_prefix_sum_16x_i8(byte_delta);
1600 const __m128i byte_prefix_before = _mm_slli_si128(byte_prefix, 1);
1602 __m128i lo_local_min = _mm_shuffle_epi8(excess_lut_min_sse, lo_nibbles);
1603 __m128i hi_local_min = _mm_shuffle_epi8(excess_lut_min_sse, hi_nibbles);
1605 const __m128i byte_index = excess_lut_nibble_index_sse;
1606 if (right_partial_width != 0) {
1607 const bool partial_is_high = (last_full_nibble & 1u) != 0;
1608 const size_t partial_byte = last_full_nibble >> 1;
1609 const __m128i partial_source = partial_is_high ? hi_nibbles : lo_nibbles;
1610 __m128i partial_min =
1611 _mm_shuffle_epi8(excess_lut_pos0_sse, partial_source);
1612 if (right_partial_width >= 2) {
1613 partial_min = _mm_min_epi8(
1614 partial_min, _mm_shuffle_epi8(excess_lut_pos1_sse, partial_source));
1616 if (right_partial_width >= 3) {
1617 partial_min = _mm_min_epi8(
1618 partial_min, _mm_shuffle_epi8(excess_lut_pos2_sse, partial_source));
1620 const __m128i partial_lane = _mm_cmpeq_epi8(
1621 byte_index, _mm_set1_epi8(
static_cast<int8_t
>(partial_byte)));
1622 if (partial_is_high) {
1623 hi_local_min = _mm_blendv_epi8(hi_local_min, partial_min, partial_lane);
1625 lo_local_min = _mm_blendv_epi8(lo_local_min, partial_min, partial_lane);
1629 const __m128i lo_candidates =
1630 _mm_add_epi8(byte_prefix_before, lo_local_min);
1631 const __m128i hi_candidates =
1632 _mm_add_epi8(_mm_add_epi8(byte_prefix_before, lo_delta), hi_local_min);
1634 __m128i masked_lo = lo_candidates;
1635 __m128i masked_hi = hi_candidates;
1636 if (first_nibble != 0 || end_nibble != 32) {
1637 const __m128i first_minus_one = _mm_set1_epi8(
1638 static_cast<int8_t
>(
static_cast<int>(first_nibble) - 1));
1639 const __m128i last = _mm_set1_epi8(
static_cast<int8_t
>(end_nibble));
1640 const __m128i lo_active = _mm_and_si128(
1641 _mm_cmpgt_epi8(excess_lut_low_nibble_index_sse, first_minus_one),
1642 _mm_cmpgt_epi8(last, excess_lut_low_nibble_index_sse));
1643 const __m128i hi_active = _mm_and_si128(
1644 _mm_cmpgt_epi8(excess_lut_high_nibble_index_sse, first_minus_one),
1645 _mm_cmpgt_epi8(last, excess_lut_high_nibble_index_sse));
1646 masked_lo = _mm_blendv_epi8(_mm_set1_epi8(127), lo_candidates, lo_active);
1647 masked_hi = _mm_blendv_epi8(_mm_set1_epi8(127), hi_candidates, hi_active);
1650 const int candidate_min =
1651 excess_horizontal_min_i8(_mm_min_epi8(masked_lo, masked_hi));
1652 if (candidate_min < best) {
1653 const __m128i min_vec = _mm_set1_epi8(
static_cast<int8_t
>(candidate_min));
1654 const uint32_t lo_equal_mask =
static_cast<uint32_t
>(
1655 _mm_movemask_epi8(_mm_cmpeq_epi8(masked_lo, min_vec)));
1656 const uint32_t hi_equal_mask =
static_cast<uint32_t
>(
1657 _mm_movemask_epi8(_mm_cmpeq_epi8(masked_hi, min_vec)));
1658 const uint32_t lo_nibble_index =
1661 :
static_cast<uint32_t
>(std::countr_zero(lo_equal_mask)) * 2u;
1662 const uint32_t hi_nibble_index =
1665 :
static_cast<uint32_t
>(std::countr_zero(hi_equal_mask)) * 2u +
1667 const uint32_t nibble_index = std::min(lo_nibble_index, hi_nibble_index);
1668 const uint32_t byte_offset = nibble_index >> 1u;
1669 const uint64_t byte_word = s[byte_offset >> 3u];
1670 const uint8_t
byte =
static_cast<uint8_t
>(
1671 (byte_word >> ((byte_offset & 7u) * 8u)) & 0xFFu);
1672 const uint8_t nibble = (nibble_index & 1u) == 0
1673 ?
static_cast<uint8_t
>(
byte & 0xFu)
1674 :
static_cast<uint8_t
>((
byte >> 4u) & 0xFu);
1675 const size_t local_offset =
1676 right_partial_width != 0 && nibble_index == last_full_nibble
1677 ?
static_cast<size_t>(
1678 excess_partial_nibble_min_offset_lut[right_partial_width]
1680 : static_cast<size_t>(excess_lut_min_offset[nibble]);
1681 best = candidate_min;
1682 best_offset =
static_cast<size_t>(nibble_index) * 4u + local_offset;
1687 for (
size_t bit = 0; bit < right; ++bit) {
1688 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1689 const size_t offset = bit + 1;
1690 if (offset >= left && current < best) {
1692 best_offset = offset;
1697 return {best, best_offset};
1714 const uint64_t* suffix_s,
1716 const uint64_t* prefix_s,
1717 size_t prefix_right)
noexcept {
1718 suffix_left = std::min<size_t>(suffix_left, 64);
1719 prefix_right = std::min<size_t>(prefix_right, 64);
1720 return {excess_min_64(suffix_s, suffix_left, 63),
1721 excess_min_64(prefix_s, 0, prefix_right)};
1743 const uint64_t* suffix_s,
1745 const uint64_t* prefix_s,
1746 size_t prefix_right)
noexcept {
1747 suffix_left = std::min<size_t>(suffix_left, 128);
1748 prefix_right = std::min<size_t>(prefix_right, 128);
1749 if (suffix_left <= prefix_right || suffix_left > 127 || prefix_right > 127) {
1750 return {excess_min_128(suffix_s, suffix_left, 127),
1751 excess_min_128(prefix_s, 0, prefix_right)};
1754#ifdef PIXIE_AVX2_SUPPORT
1757 int suffix_best = prefix_excess_128(suffix_s, suffix_left);
1759 size_t suffix_bit = suffix_left;
1760 int suffix_current = suffix_best;
1761 for (; suffix_bit < 127 && (suffix_bit & 3u) != 0; ++suffix_bit) {
1763 ((suffix_s[suffix_bit >> 6] >> (suffix_bit & 63)) & 1ull) != 0 ? 1 : -1;
1764 const size_t offset = suffix_bit + 1;
1765 if (suffix_current < suffix.min_excess) {
1766 suffix = {suffix_current, offset};
1770 const size_t prefix_last_nibble = prefix_right >> 2;
1771 const size_t prefix_partial_width = prefix_right & 3u;
1772 const size_t prefix_end_nibble =
1773 prefix_last_nibble + (prefix_partial_width == 0 ? 0 : 1);
1774 const size_t suffix_first_nibble = suffix_bit < 127 ? suffix_bit >> 2 : 32;
1775 const int prefix_artificial_delta =
1776 prefix_excess_128(prefix_s, prefix_end_nibble * 4);
1778 const __m256i idx = excess_lut_nibble_index;
1779 const __m256i prefix_active = _mm256_cmpgt_epi8(
1780 _mm256_set1_epi8(
static_cast<int8_t
>(prefix_end_nibble)), idx);
1781 const __m256i suffix_active = _mm256_cmpgt_epi8(
1782 idx, _mm256_set1_epi8(
static_cast<int8_t
>(suffix_first_nibble) - 1));
1784 const __m256i prefix_nibbles = excess_nibbles_128_avx2(prefix_s);
1785 const __m256i suffix_nibbles = excess_nibbles_128_avx2(suffix_s);
1787 _mm256_blendv_epi8(_mm256_set1_epi8(3), prefix_nibbles, prefix_active);
1788 nibbles = _mm256_blendv_epi8(nibbles, suffix_nibbles, suffix_active);
1790 __m256i ps = _mm256_shuffle_epi8(excess_lut_delta, nibbles);
1791 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 1));
1792 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 2));
1793 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 4));
1794 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 8));
1796 __m128i ps_lo = _mm256_castsi256_si128(ps);
1797 __m128i ps_hi = _mm256_extracti128_si256(ps, 1);
1799 _mm_set1_epi8(
static_cast<int8_t
>(_mm_extract_epi8(ps_lo, 15)));
1800 ps_hi = _mm_add_epi8(ps_hi, carry);
1801 ps = _mm256_inserti128_si256(_mm256_castsi128_si256(ps_lo), ps_hi, 1);
1803 __m256i b = _mm256_permute2x128_si256(ps, ps, 0x08);
1804 const __m256i excl_ps = _mm256_alignr_epi8(ps, b, 15);
1806 __m256i local_min = _mm256_shuffle_epi8(excess_lut_min, nibbles);
1807 if (prefix_partial_width != 0) {
1808 __m256i partial_min = _mm256_shuffle_epi8(excess_lut_pos0, nibbles);
1809 if (prefix_partial_width >= 2) {
1810 partial_min = _mm256_min_epi8(
1811 partial_min, _mm256_shuffle_epi8(excess_lut_pos1, nibbles));
1813 if (prefix_partial_width >= 3) {
1814 partial_min = _mm256_min_epi8(
1815 partial_min, _mm256_shuffle_epi8(excess_lut_pos2, nibbles));
1817 local_min = _mm256_blendv_epi8(
1818 local_min, partial_min,
1820 idx, _mm256_set1_epi8(
static_cast<int8_t
>(prefix_last_nibble))));
1822 __m256i suffix_partial_min = _mm256_min_epi8(
1823 _mm256_shuffle_epi8(excess_lut_pos0, nibbles),
1824 _mm256_min_epi8(_mm256_shuffle_epi8(excess_lut_pos1, nibbles),
1825 _mm256_shuffle_epi8(excess_lut_pos2, nibbles)));
1826 const __m256i suffix_tail_active = _mm256_and_si256(
1827 suffix_active, _mm256_cmpeq_epi8(idx, _mm256_set1_epi8(31)));
1829 _mm256_blendv_epi8(local_min, suffix_partial_min, suffix_tail_active);
1831 const __m256i base_candidates = _mm256_add_epi8(excl_ps, local_min);
1832 const __m256i sentinel = _mm256_set1_epi8(127);
1834 const __m256i prefix_candidates =
1835 _mm256_blendv_epi8(sentinel, base_candidates, prefix_active);
1836 const __m256i suffix_candidates = _mm256_blendv_epi8(
1838 _mm256_add_epi8(base_candidates,
1839 _mm256_set1_epi8(
static_cast<int8_t
>(
1840 suffix_current - prefix_artificial_delta))),
1843 auto reduce_min = [](__m256i values) {
1844 __m128i min128 = _mm_min_epi8(_mm256_castsi256_si128(values),
1845 _mm256_extracti128_si256(values, 1));
1846 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 8));
1847 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 4));
1848 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 2));
1849 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 1));
1850 return static_cast<int>(
static_cast<int8_t
>(_mm_extract_epi8(min128, 0)));
1853 auto local_offset = [](uint8_t nibble,
size_t width_value) {
1854 if (width_value == 0 || width_value == 4) {
1855 return static_cast<size_t>(excess_lut_min_offset[nibble]);
1859 size_t best_offset = 1;
1860 for (
size_t i = 0; i < width_value; ++i) {
1861 current += ((nibble >> i) & 1u) != 0 ? 1 : -1;
1862 if (i == 0 || current < best) {
1864 best_offset = i + 1;
1870 const int prefix_min = reduce_min(prefix_candidates);
1871 if (prefix_min < prefix.min_excess) {
1872 const uint32_t mask =
static_cast<uint32_t
>(_mm256_movemask_epi8(
1873 _mm256_cmpeq_epi8(prefix_candidates,
1874 _mm256_set1_epi8(
static_cast<int8_t
>(prefix_min)))));
1875 const uint32_t prefix_lane = std::countr_zero(mask);
1876 const uint64_t word = prefix_s[prefix_lane >> 4];
1877 const uint8_t nibble =
1878 static_cast<uint8_t
>((word >> ((prefix_lane & 15u) * 4u)) & 0xFu);
1879 prefix.min_excess = prefix_min;
1880 const size_t width =
1881 prefix_partial_width != 0 && prefix_lane == prefix_last_nibble
1882 ? prefix_partial_width
1885 static_cast<size_t>(prefix_lane) * 4u + local_offset(nibble, width);
1888 const int suffix_min = reduce_min(suffix_candidates);
1889 if (suffix_min < suffix.min_excess) {
1890 const uint32_t mask =
static_cast<uint32_t
>(_mm256_movemask_epi8(
1891 _mm256_cmpeq_epi8(suffix_candidates,
1892 _mm256_set1_epi8(
static_cast<int8_t
>(suffix_min)))));
1893 const uint32_t suffix_lane = std::countr_zero(mask);
1894 const uint64_t word = suffix_s[suffix_lane >> 4];
1895 const uint8_t nibble =
1896 static_cast<uint8_t
>((word >> ((suffix_lane & 15u) * 4u)) & 0xFu);
1897 suffix.min_excess = suffix_min;
1898 const size_t width = suffix_lane == 31 ? 3 : 4;
1900 static_cast<size_t>(suffix_lane) * 4u + local_offset(nibble, width);
1903 return {suffix, prefix};
1905 return {excess_min_128(suffix_s, suffix_left, 127),
1906 excess_min_128(prefix_s, 0, prefix_right)};
1925static inline size_t forward_search_128(
const uint64_t* s,
1927 size_t start_offset,
1928 int* block_excess =
nullptr) noexcept {
1930 const int delta = excess_positions_128(s, target_x, out);
1931 if (block_excess !=
nullptr) {
1932 *block_excess = delta;
1934 if (start_offset >= 128) {
1938 const size_t first_word = start_offset >> 6;
1939 const size_t first_bit = start_offset & 63;
1940 for (
size_t word = first_word; word < 2; ++word) {
1941 uint64_t mask = out[word];
1942 if (word == first_word && first_bit != 0) {
1943 mask &= ~first_bits_mask(first_bit);
1946 return word * 64 + std::countr_zero(mask);
1969static inline size_t backward_search_128(
const uint64_t* s,
1972 int* block_excess =
nullptr) noexcept {
1974 const int delta = excess_positions_128(s, target_x, out);
1975 if (block_excess !=
nullptr) {
1976 *block_excess = delta;
1978 if (end_offset == 0) {
1982 const size_t max_prefix_length = end_offset - 1;
1983 if (max_prefix_length > 0) {
1984 const size_t last_bit_index = max_prefix_length - 1;
1985 size_t word = last_bit_index >> 6;
1986 const size_t bit_in_word = last_bit_index & 63;
1987 uint64_t mask = out[word] & first_bits_mask(bit_in_word + 1);
1990 return word * 64 + (63 - std::countl_zero(mask)) + 1;
1999 return target_x == 0 ? 0 : 128;
2014static inline void excess_positions_512(
const uint64_t* s,
2016 uint64_t* out)
noexcept {
2017 if (target_x < -512 || target_x > 512) {
2018 out[0] = out[1] = out[2] = out[3] = 0;
2019 out[4] = out[5] = out[6] = out[7] = 0;
2023 for (
int k = 0; k < 4; ++k) {
2024 target_x -= excess_positions_128(s + 2 * k, target_x, out + 2 * k);
2041static inline void excess_record_lows_128(
const uint64_t* s,
2042 uint64_t* out)
noexcept {
2043 out[0] = out[1] = 0;
2046 for (
size_t i = 0; i < 128; ++i) {
2047 const uint64_t w = s[i >> 6];
2048 const int bit =
static_cast<int>((w >> (i & 63)) & 1ull);
2049 cur += bit ? +1 : -1;
2052 out[i >> 6] |= (uint64_t{1} << (i & 63));
2065static inline void excess_record_lows_128_byte_lut(
const uint64_t* s,
2066 uint64_t* out)
noexcept {
2067 out[0] = out[1] = 0;
2070 for (
size_t byte_idx = 0; byte_idx < 16; ++byte_idx) {
2071 const size_t bit_base = byte_idx * 8;
2072 const uint8_t
byte =
2073 static_cast<uint8_t
>((s[bit_base >> 6] >> (bit_base & 63)) & 0xFFu);
2074 const int byte_min = excess_byte_min_lut[byte];
2075 if (cur + byte_min < best) {
2076 for (
size_t i = 0; i < 8; ++i) {
2077 const int bit =
static_cast<int>((
byte >> i) & 1u);
2078 cur += bit ? +1 : -1;
2081 const size_t pos = bit_base + i;
2082 out[pos >> 6] |= (uint64_t{1} << (pos & 63));
2086 cur += excess_byte_delta_lut[byte];
2099static inline void excess_record_lows_128_lut(
const uint64_t* s,
2100 uint64_t* out)
noexcept {
2101 out[0] = out[1] = 0;
2104 for (
size_t byte_idx = 0; byte_idx < 16; ++byte_idx) {
2105 const size_t bit_base = byte_idx * 8;
2106 const uint8_t
byte =
2107 static_cast<uint8_t
>((s[bit_base >> 6] >> (bit_base & 63)) & 0xFFu);
2108 const int gap = cur - best;
2109 const int idx = gap > 7 ? 7 : (gap < 0 ? 0 : gap);
2110 const uint8_t mask =
2111 excess_byte_record_lows_lut[byte][
static_cast<size_t>(idx)];
2116 uint8_t local_mask = 0;
2117 for (
int bit = 0; bit < 8; ++bit) {
2118 local += ((
byte >> bit) & 1u) ? 1 : -1;
2119 if (local < local_best) {
2121 local_mask |=
static_cast<uint8_t
>(1u << bit);
2125 uint8_t out_mask = 0;
2127 for (
int bit = 0; bit < 8; ++bit) {
2128 local += ((
byte >> bit) & 1u) ? 1 : -1;
2129 if (cur + local < best) {
2130 out_mask |=
static_cast<uint8_t
>(1u << bit);
2134 if (out_mask != 0) {
2135 const uint64_t word =
static_cast<uint64_t
>(out_mask)
2137 out[bit_base >> 6] |= word;
2140 cur += excess_byte_delta_lut[byte];
2144#ifdef PIXIE_AVX2_SUPPORT
2152static inline void excess_record_lows_128_avx2(
const uint64_t* s,
2153 uint64_t* out)
noexcept {
2154 out[0] = out[1] = 0;
2158 const __m256i masks = excess_bit_masks_16x_i16();
2159 const __m256i zero = _mm256_setzero_si256();
2160 const __m256i pos = _mm256_set1_epi16(1);
2161 const __m256i neg = _mm256_set1_epi16(-1);
2163 for (
size_t chunk = 0; chunk < 8; ++chunk) {
2164 const size_t chunk_bit = chunk * 16;
2165 const uint16_t bits =
2167 ?
static_cast<uint16_t
>((s[0] >> (chunk * 16)) & 0xFFFFu)
2168 : static_cast<uint16_t>((s[1] >> ((chunk - 4) * 16)) & 0xFFFFu);
2170 const __m256i vb = _mm256_set1_epi16(
static_cast<int16_t
>(bits));
2171 const __m256i m = _mm256_and_si256(vb, masks);
2172 const __m256i is_zero = _mm256_cmpeq_epi16(m, zero);
2173 const __m256i steps = _mm256_blendv_epi8(pos, neg, is_zero);
2174 const __m256i pref_rel = excess_prefix_sum_16x_i16(steps);
2175 const __m256i pref_abs = _mm256_add_epi16(
2176 pref_rel, _mm256_set1_epi16(
static_cast<int16_t
>(cur)));
2178 alignas(32) int16_t vals[16];
2179 _mm256_store_si256(
reinterpret_cast<__m256i*
>(vals), pref_abs);
2181 for (
size_t lane = 0; lane < 16; ++lane) {
2182 const int val = vals[lane];
2185 const size_t pos_idx = chunk_bit + lane;
2186 out[pos_idx >> 6] |= (uint64_t{1} << (pos_idx & 63));
2190 cur += 2 *
static_cast<int>(std::popcount(bits)) - 16;
2203static inline void excess_record_lows_128_nibble_lut(
const uint64_t* s,
2204 uint64_t* out)
noexcept {
2205 out[0] = out[1] = 0;
2209 const __m256i vdelta = excess_lut_delta;
2210 const __m256i vmin = excess_lut_min;
2212 __m256i nibbles = excess_nibbles_128_avx2(s);
2215 __m256i ps = _mm256_shuffle_epi8(vdelta, nibbles);
2216 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 1));
2217 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 2));
2218 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 4));
2219 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 8));
2221 __m128i ps_lo = _mm256_castsi256_si128(ps);
2222 __m128i ps_hi = _mm256_extracti128_si256(ps, 1);
2224 _mm_set1_epi8(
static_cast<int8_t
>(_mm_extract_epi8(ps_lo, 15)));
2225 ps_hi = _mm_add_epi8(ps_hi, carry);
2226 ps = _mm256_inserti128_si256(_mm256_castsi128_si256(ps_lo), ps_hi, 1);
2229 __m256i b = _mm256_permute2x128_si256(ps, ps, 0x08);
2230 __m256i excl_ps = _mm256_alignr_epi8(ps, b, 15);
2233 __m256i local_min = _mm256_shuffle_epi8(vmin, nibbles);
2235 alignas(32) int8_t excl[32];
2236 _mm256_store_si256(
reinterpret_cast<__m256i*
>(excl), excl_ps);
2238 alignas(32) int8_t nibble_min[32];
2239 _mm256_store_si256(
reinterpret_cast<__m256i*
>(nibble_min), local_min);
2241 alignas(32) int8_t nibble_vals[32];
2242 _mm256_store_si256(
reinterpret_cast<__m256i*
>(nibble_vals), nibbles);
2244 for (
int n = 0; n < 32; ++n) {
2245 const int nibble_base = cur + excl[n];
2246 const int nibble_best = nibble_base + nibble_min[n];
2247 if (nibble_best < best) {
2249 const uint8_t nibble =
static_cast<uint8_t
>(nibble_vals[n]);
2251 for (
int bit = 0; bit < 4; ++bit) {
2252 local += ((nibble >> bit) & 1u) ? 1 : -1;
2253 const int val = nibble_base + local;
2256 const size_t pos =
static_cast<size_t>(n) * 4 + bit;
2257 out[pos >> 6] |= (uint64_t{1} << (pos & 63));
2277static inline void excess_record_lows_512(
const uint64_t* s,
2278 uint64_t* out)
noexcept {
2279 out[0] = out[1] = out[2] = out[3] = 0;
2280 out[4] = out[5] = out[6] = out[7] = 0;
2283 for (
int k = 0; k < 4; ++k) {
2284 const uint64_t* block = s + 2 * k;
2285 uint64_t* block_out = out + 2 * k;
2286 for (
size_t i = 0; i < 128; ++i) {
2287 const uint64_t w = block[i >> 6];
2288 const int bit =
static_cast<int>((w >> (i & 63)) & 1ull);
2289 global += bit ? +1 : -1;
2290 if (global < best) {
2292 block_out[i >> 6] |= (uint64_t{1} << (i & 63));
2309static inline void rank_32x8(
const uint8_t* x, uint8_t* result) {
2310#ifdef PIXIE_AVX512_SUPPORT
2312 popcount_32x8(x, result);
2313 __m256i prefix_sums = _mm256_loadu_si256((__m256i
const*)result);
2314 const __m256i zero = _mm256_setzero_si256();
2316 prefix_sums = _mm256_add_epi8(prefix_sums,
2317 _mm256_alignr_epi8(prefix_sums, zero, 16 - 1));
2318 prefix_sums = _mm256_add_epi8(prefix_sums,
2319 _mm256_alignr_epi8(prefix_sums, zero, 16 - 2));
2320 prefix_sums = _mm256_add_epi8(prefix_sums,
2321 _mm256_alignr_epi8(prefix_sums, zero, 16 - 4));
2322 prefix_sums = _mm256_add_epi8(prefix_sums,
2323 _mm256_alignr_epi8(prefix_sums, zero, 16 - 8));
2327 __m128i low_lane = _mm256_extracti128_si256(prefix_sums, 0);
2328 __m128i high_lane = _mm256_extracti128_si256(prefix_sums, 1);
2329 auto last_val_low = _mm_extract_epi8(low_lane, 15);
2330 __m128i add_to_high = _mm_set1_epi8(last_val_low);
2331 high_lane = _mm_add_epi8(high_lane, add_to_high);
2332 prefix_sums = _mm256_set_m128i(high_lane, low_lane);
2333 _mm256_storeu_epi8(result, prefix_sums);
2336 result[0] = std::popcount(x[0]);
2337 for (
size_t i = 1; i < 32; ++i) {
2338 result[i] = std::popcount(x[i]) + result[i - 1];
Pair of boundary minimum results for adjacent BP query blocks.
Definition bits.h:1103
Minimum prefix excess in a 128-bit bitstring range.
Definition bits.h:1092