3#if defined(__AVX512VPOPCNTDQ__) && defined(__AVX512F__) && \
5#define PIXIE_AVX512_SUPPORT
8#if defined(__BMI2__) && !defined(PIXIE_DISABLE_BMI2)
9#define PIXIE_BMI2_SUPPORT
13#define PIXIE_AVX2_SUPPORT
16#if defined(__SSSE3__) && defined(__SSE4_1__)
17#define PIXIE_SSE41_SUPPORT
20#if defined(__AVX512F__) || defined(PIXIE_BMI2_SUPPORT) || \
21 defined(PIXIE_AVX2_SUPPORT) || defined(PIXIE_SSE41_SUPPORT)
33#ifdef PIXIE_AVX2_SUPPORT
37static inline const __m256i lookup_popcount_4 = _mm256_setr_epi8(
50static inline const __m256i mask_first_half = _mm256_setr_epi8(
51 0xFF, 0xFF, 0xFF, 0xFF,
52 0xFF, 0xFF, 0xFF, 0xFF,
53 0xFF, 0xFF, 0xFF, 0xFF,
54 0xFF, 0xFF, 0xFF, 0xFF,
64static inline constexpr int8_t excess_nibble_min_offset[16] = {
65 4, 4, 4, 4, 2, 2, 1, 1, 3, 3, 1, 1, 2, 2, 1, 1};
67#ifdef PIXIE_SSE41_SUPPORT
69static inline const __m128i excess_lut_delta_sse = _mm_setr_epi8(
74static inline const __m128i excess_lut_pos0_sse = _mm_setr_epi8(
79static inline const __m128i excess_lut_pos1_sse = _mm_setr_epi8(
84static inline const __m128i excess_lut_pos2_sse = _mm_setr_epi8(
89static inline const __m128i excess_lut_min_sse = _mm_setr_epi8(
94static inline const __m128i excess_lut_nibble_index_sse = _mm_setr_epi8(
99static inline const __m128i excess_lut_low_nibble_index_sse = _mm_setr_epi8(
104static inline const __m128i excess_lut_high_nibble_index_sse = _mm_setr_epi8(
109static inline const __m128i excess_lut_nibble_mask_sse = _mm_set1_epi8(0x0F);
113#ifdef PIXIE_SSE41_SUPPORT
114static inline __m128i excess_nibbles_64_sse(
const uint64_t* s)
noexcept {
115 const __m128i word_vec = _mm_loadl_epi64(
reinterpret_cast<const __m128i*
>(s));
116 const __m128i lo_nibbles =
117 _mm_and_si128(word_vec, excess_lut_nibble_mask_sse);
118 const __m128i hi_nibbles =
119 _mm_and_si128(_mm_srli_epi16(word_vec, 4), excess_lut_nibble_mask_sse);
120 return _mm_unpacklo_epi8(lo_nibbles, hi_nibbles);
123static inline __m128i excess_prefix_sum_16x_i8(__m128i v)
noexcept {
125 __m128i t = _mm_slli_si128(x, 1);
126 x = _mm_add_epi8(x, t);
127 t = _mm_slli_si128(x, 2);
128 x = _mm_add_epi8(x, t);
129 t = _mm_slli_si128(x, 4);
130 x = _mm_add_epi8(x, t);
131 t = _mm_slli_si128(x, 8);
132 return _mm_add_epi8(x, t);
135static inline int excess_horizontal_min_i8(__m128i v)
noexcept {
136 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 8));
137 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 4));
138 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 2));
139 v = _mm_min_epi8(v, _mm_alignr_epi8(v, v, 1));
140 return static_cast<int>(
static_cast<int8_t
>(_mm_extract_epi8(v, 0)));
158static inline uint32_t rmm_btree_match_mask_i16x16(
const int16_t* prefix_before,
159 const int16_t* min_excess,
160 const int16_t* max_excess,
162 bool include_zero_boundary) {
163#ifdef PIXIE_AVX2_SUPPORT
164 const __m256i vtarget = _mm256_set1_epi16(target);
165 const __m256i vprefix =
166 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(prefix_before));
168 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(min_excess));
170 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(max_excess));
172 const __m256i lower = _mm256_adds_epi16(vprefix, vmin);
173 const __m256i upper = _mm256_adds_epi16(vprefix, vmax);
174 const __m256i ge_lower = _mm256_or_si256(_mm256_cmpgt_epi16(vtarget, lower),
175 _mm256_cmpeq_epi16(vtarget, lower));
176 const __m256i le_upper = _mm256_or_si256(_mm256_cmpgt_epi16(upper, vtarget),
177 _mm256_cmpeq_epi16(upper, vtarget));
178 __m256i matched = _mm256_and_si256(ge_lower, le_upper);
179 if (include_zero_boundary) {
180 matched = _mm256_or_si256(matched, _mm256_cmpeq_epi16(vtarget, vprefix));
183 const uint32_t byte_mask =
184 static_cast<uint32_t
>(_mm256_movemask_epi8(matched));
186 for (
size_t lane = 0; lane < 16; ++lane) {
187 const uint32_t lane_mask = 0x3u << (lane * 2);
188 if ((byte_mask & lane_mask) == lane_mask) {
189 result |= uint32_t{1} << lane;
195 for (
size_t lane = 0; lane < 16; ++lane) {
196 const int lower = prefix_before[lane] + min_excess[lane];
197 const int upper = prefix_before[lane] + max_excess[lane];
198 const bool found = (lower <= target && target <= upper) ||
199 (include_zero_boundary && target == prefix_before[lane]);
201 result |= uint32_t{1} << lane;
222static inline uint32_t rmm_btree_match_mask_i64x4(
const int64_t* prefix_before,
223 const int64_t* min_excess,
224 const int64_t* max_excess,
226 bool include_zero_boundary) {
227#ifdef PIXIE_AVX2_SUPPORT
228 const __m256i vtarget = _mm256_set1_epi64x(target);
229 const __m256i vprefix =
230 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(prefix_before));
232 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(min_excess));
234 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(max_excess));
236 const __m256i relative = _mm256_sub_epi64(vtarget, vprefix);
237 const __m256i ge_min = _mm256_or_si256(_mm256_cmpgt_epi64(relative, vmin),
238 _mm256_cmpeq_epi64(relative, vmin));
239 const __m256i le_max = _mm256_or_si256(_mm256_cmpgt_epi64(vmax, relative),
240 _mm256_cmpeq_epi64(vmax, relative));
241 __m256i matched = _mm256_and_si256(ge_min, le_max);
242 if (include_zero_boundary) {
243 matched = _mm256_or_si256(matched, _mm256_cmpeq_epi64(vtarget, vprefix));
246 const uint32_t byte_mask =
247 static_cast<uint32_t
>(_mm256_movemask_epi8(matched));
249 for (
size_t lane = 0; lane < 4; ++lane) {
250 const uint32_t lane_mask = 0xffu << (lane * 8);
251 if ((byte_mask & lane_mask) == lane_mask) {
252 result |= uint32_t{1} << lane;
258 for (
size_t lane = 0; lane < 4; ++lane) {
259 const int64_t relative = target - prefix_before[lane];
261 (min_excess[lane] <= relative && relative <= max_excess[lane]) ||
262 (include_zero_boundary && relative == 0);
264 result |= uint32_t{1} << lane;
278static inline uint64_t first_bits_mask(
size_t num) {
279 return num >= 64 ? UINT64_MAX : ((1llu << num) - 1);
302inline void copy_packed_bits(
const uint64_t* source,
304 uint64_t* destination,
305 size_t destination_bit,
306 size_t count)
noexcept {
308 const auto copy_boundary = [&](
size_t width) {
309 const auto shift = source_bit % 64;
310 uint64_t value = source[source_bit / 64] >> shift;
311 if (width > 64 - shift) {
312 value |= source[source_bit / 64 + 1] << (64 - shift);
314 const auto offset = destination_bit % 64;
315 const auto mask = first_bits_mask(width) << offset;
316 auto& word = destination[destination_bit / 64];
317 word = (word & ~mask) | ((value << offset) & mask);
319 destination_bit += width;
325 if (destination_bit % 64 != 0) {
326 copy_boundary(std::min(count, 64 - destination_bit % 64));
328 const auto shift = source_bit % 64;
329 const auto* input = source + source_bit / 64;
330 auto* output = destination + destination_bit / 64;
331 auto words = count / 64;
333 std::copy_n(input, words, output);
335#if defined(__AVX512F__)
336#if defined(__AVX512VBMI2__)
337 const auto shifts = _mm512_set1_epi64(shift);
339 const auto low_shift = _mm_cvtsi64_si128(shift);
340 const auto high_shift = _mm_cvtsi64_si128(64 - shift);
342 for (; words >= 8; words -= 8, input += 8, output += 8) {
343 const auto low = _mm512_loadu_si512(input);
344 const auto high = _mm512_loadu_si512(input + 1);
345#if defined(__AVX512VBMI2__)
346 _mm512_storeu_si512(output, _mm512_shrdv_epi64(low, high, shifts));
348 _mm512_storeu_si512(output,
349 _mm512_or_si512(_mm512_srl_epi64(low, low_shift),
350 _mm512_sll_epi64(high, high_shift)));
353#elif defined(PIXIE_AVX2_SUPPORT)
354 const auto low_shift = _mm_cvtsi64_si128(shift);
355 const auto high_shift = _mm_cvtsi64_si128(64 - shift);
356 for (; words >= 4; words -= 4, input += 4, output += 4) {
358 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(input));
360 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(input + 1));
361 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(output),
362 _mm256_or_si256(_mm256_srl_epi64(low, low_shift),
363 _mm256_sll_epi64(high, high_shift)));
366 for (
size_t i = 0; i < words; ++i) {
367 output[i] = (input[i] >> shift) | (input[i + 1] << (64 - shift));
370 source_bit += count / 64 * 64;
371 destination_bit += count / 64 * 64;
374 copy_boundary(count);
400static inline uint64_t rank_512(
const uint64_t* x, uint64_t count) {
401#if defined(PIXIE_AVX512_SUPPORT) && defined(__AVX512VBMI2__)
403 __m512i a = _mm512_maskz_set1_epi64((1ull << ((count >> 6))) - 1,
404 std::numeric_limits<uint64_t>::max());
405 __m512i b = _mm512_maskz_set1_epi64((1ull << ((count >> 6) + 1)) - 1,
406 std::numeric_limits<uint64_t>::max());
407 __m512i mask = _mm512_shldv_epi64(a, b, _mm512_set1_epi64(count % 64));
409 __m512i res = _mm512_loadu_epi64(x);
410 res = _mm512_and_epi64(res, mask);
411 __m512i cnt = _mm512_popcnt_epi64(res);
412 return _mm512_reduce_add_epi64(cnt);
416 size_t last_uint = count < 512 ? count >> 6 : 8;
418 uint64_t pop_val = 0;
420 for (
size_t i = 0; i < last_uint; i++) {
421 pop_val += std::popcount(x[i]);
424 pop_val += count < 512
425 ? std::popcount(x[last_uint] & first_bits_mask(count & 63))
432struct PixieSelectByteLut {
433 uint8_t popcounts[256];
434 uint8_t select[256][8];
436 constexpr PixieSelectByteLut() : popcounts{}, select{} {
437 for (
int byte = 0;
byte < 256; ++byte) {
438 for (
int rank = 0; rank < 8; ++rank) {
439 select[byte][rank] = 8;
443 for (
int bit = 0; bit < 8; ++bit) {
444 if (((
byte >> bit) & 1) != 0) {
445 select[byte][count++] =
static_cast<uint8_t
>(bit);
448 popcounts[byte] =
static_cast<uint8_t
>(count);
455static inline uint64_t select_64_no_bmi2(uint64_t x, uint64_t rank) {
458 uint64_t count = std::popcount(
static_cast<uint32_t
>(x));
465 count = std::popcount(
static_cast<uint16_t
>(x));
472 const auto low_byte =
static_cast<uint8_t
>(x);
473 count = pixie_select_byte_lut.popcounts[low_byte];
480 return offset + pixie_select_byte_lut.select[
static_cast<uint8_t
>(x)][rank];
492static inline uint64_t select_64(uint64_t x, uint64_t rank) {
493#ifdef PIXIE_BMI2_SUPPORT
494 return std::countr_zero(_pdep_u64(1ull << rank, x));
496 return select_64_no_bmi2(x, rank);
500template <
bool Invert>
501static inline uint64_t select_512_word_count(uint64_t word) {
502 if constexpr (Invert) {
503 return std::popcount(~word);
505 return std::popcount(word);
509template <
bool Invert>
510static inline uint64_t select_512_selected_word(uint64_t word) {
511 if constexpr (Invert) {
518template <
bool Invert>
519static inline uint64_t select_512_scalar_impl(
const uint64_t* x,
523 if constexpr (Invert) {
524 count = std::popcount(~x[0]);
526 count = std::popcount(x[0]);
528 while (word < 7 && count <= rank) {
531 if constexpr (Invert) {
532 count = std::popcount(~x[word]);
534 count = std::popcount(x[word]);
538 if constexpr (Invert) {
539 return word * 64 + select_64(~x[word], rank);
541 return word * 64 + select_64(x[word], rank);
545#ifdef PIXIE_AVX2_SUPPORT
546template <
bool Invert>
547static inline void select_512_avx2_counts(
const uint64_t* x, uint64_t* counts) {
548 const __m256i low_mask = _mm256_set1_epi8(0x0F);
549 const __m256i zero = _mm256_setzero_si256();
550 const __m256i sixty_four = _mm256_set1_epi64x(64);
552 for (
int half = 0; half < 2; ++half) {
553 const __m256i words =
554 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x + 4 * half));
556 const __m256i low_nibbles = _mm256_and_si256(words, low_mask);
557 const __m256i high_nibbles =
558 _mm256_and_si256(_mm256_srli_epi16(words, 4), low_mask);
559 const __m256i byte_counts =
560 _mm256_add_epi8(_mm256_shuffle_epi8(lookup_popcount_4, low_nibbles),
561 _mm256_shuffle_epi8(lookup_popcount_4, high_nibbles));
562 __m256i word_counts = _mm256_sad_epu8(byte_counts, zero);
563 if constexpr (Invert) {
564 word_counts = _mm256_sub_epi64(sixty_four, word_counts);
566 _mm256_store_si256(
reinterpret_cast<__m256i*
>(counts + 4 * half),
571template <
bool Invert>
572static inline uint64_t select_512_avx2_impl(
const uint64_t* x, uint64_t rank) {
573 alignas(32) uint64_t counts[8];
574 select_512_avx2_counts<Invert>(x, counts);
576 for (
size_t i = 0; i < 8; ++i) {
577 if (rank < counts[i]) {
578 return i * 64 + select_64(select_512_selected_word<Invert>(x[i]), rank);
586#ifdef PIXIE_AVX512_SUPPORT
587static inline __m512i select_512_avx512_prefix_sum_u64(__m512i prefix) {
588 const __m512i idx_shift1 = _mm512_set_epi64(6, 5, 4, 3, 2, 1, 0, 0);
589 const __m512i idx_shift2 = _mm512_set_epi64(5, 4, 3, 2, 1, 0, 0, 0);
590 const __m512i idx_shift4 = _mm512_set_epi64(3, 2, 1, 0, 0, 0, 0, 0);
592 __m512i tmp = _mm512_maskz_permutexvar_epi64(0xFE, idx_shift1, prefix);
593 prefix = _mm512_add_epi64(prefix, tmp);
594 tmp = _mm512_maskz_permutexvar_epi64(0xFC, idx_shift2, prefix);
595 prefix = _mm512_add_epi64(prefix, tmp);
596 tmp = _mm512_maskz_permutexvar_epi64(0xF0, idx_shift4, prefix);
597 return _mm512_add_epi64(prefix, tmp);
600static inline uint64_t select_512_avx512_previous_prefix(__m512i prefix,
605 const __m512i idx_previous =
606 _mm512_set1_epi64(
static_cast<int64_t
>(lane - 1));
607 const __m512i previous_vec = _mm512_permutexvar_epi64(idx_previous, prefix);
608 return static_cast<uint64_t
>(
609 _mm_cvtsi128_si64(_mm512_castsi512_si128(previous_vec)));
612template <
bool Invert>
613static inline uint64_t select_512_avx512_impl(
const uint64_t* x,
615 const __m512i words = _mm512_loadu_epi64(x);
616 __m512i prefix = _mm512_popcnt_epi64(words);
617 if constexpr (Invert) {
618 prefix = _mm512_sub_epi64(_mm512_set1_epi64(64), prefix);
620 prefix = select_512_avx512_prefix_sum_u64(prefix);
622 const __mmask8 mask = _mm512_cmpgt_epu64_mask(
623 prefix, _mm512_set1_epi64(
static_cast<int64_t
>(rank)));
624 const uint32_t lane = _tzcnt_u32(
static_cast<uint32_t
>(mask));
625 const uint64_t previous = select_512_avx512_previous_prefix(prefix, lane);
627 select_64(select_512_selected_word<Invert>(x[lane]), rank - previous);
643static inline uint64_t select_512(
const uint64_t* x, uint64_t rank) {
644#ifdef PIXIE_AVX512_SUPPORT
645 return select_512_avx512_impl<false>(x, rank);
650 return select_512_scalar_impl<false>(x, rank);
664static inline uint64_t select0_512(
const uint64_t* x, uint64_t rank0) {
665#ifdef PIXIE_AVX512_SUPPORT
666 return select_512_avx512_impl<true>(x, rank0);
670 return select_512_scalar_impl<true>(x, rank0);
678static inline uint16_t lower_bound_4x64(
const uint64_t* x, uint64_t y) {
679#ifdef PIXIE_AVX512_SUPPORT
681 auto y_4 = _mm256_set1_epi64x(y);
682 auto reg_256 = _mm256_loadu_epi64(x);
683 auto cmp = _mm256_cmpge_epu64_mask(reg_256, y_4);
685 return _tzcnt_u16(cmp);
688#ifdef PIXIE_AVX2_SUPPORT
690 auto y_4 = _mm256_set1_epi64x(y);
691 __m256i reg_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
693 const __m256i offset = _mm256_set1_epi64x(0x8000000000000000ULL);
694 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
695 __m256i y_offset = _mm256_xor_si256(y_4, offset);
696 auto mask = _mm256_movemask_epi8(_mm256_cmpgt_epi64(
697 x_offset, _mm256_sub_epi64(y_offset, _mm256_set1_epi64x(1))));
699 return _tzcnt_u32(mask) >> 3;
703 for (uint16_t i = 0; i < 4; ++i) {
727static inline uint16_t lower_bound_delta_4x64(
const uint64_t* x,
729 const uint64_t* delta_array,
730 uint64_t delta_scalar) {
731#ifdef PIXIE_AVX512_SUPPORT
733 const __m256i dlt_256 = _mm256_loadu_epi64(delta_array);
734 auto x_256 = _mm256_loadu_epi64(x);
735 auto dlt_4 = _mm256_set1_epi64x(delta_scalar);
736 auto y_4 = _mm256_set1_epi64x(y);
738 auto tmp = _mm256_add_epi64(dlt_4, dlt_256);
739 auto reg_256 = _mm256_sub_epi64(tmp, x_256);
740 auto cmp = _mm256_cmpge_epu64_mask(reg_256, y_4);
742 return _tzcnt_u16(cmp);
745#ifdef PIXIE_AVX2_SUPPORT
747 const __m256i dlt_256 =
748 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(delta_array));
749 auto x_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
750 auto dlt_4 = _mm256_set1_epi64x(delta_scalar);
751 auto y_4 = _mm256_set1_epi64x(y);
753 auto tmp = _mm256_add_epi64(dlt_4, dlt_256);
754 auto reg_256 = _mm256_sub_epi64(tmp, x_256);
756 const __m256i offset = _mm256_set1_epi64x(0x8000000000000000ULL);
757 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
758 __m256i y_offset = _mm256_xor_si256(y_4, offset);
759 auto mask = _mm256_movemask_epi8(_mm256_cmpgt_epi64(
760 x_offset, _mm256_sub_epi64(y_offset, _mm256_set1_epi64x(1))));
762 return _tzcnt_u32(mask) >> 3;
766 for (uint16_t i = 0; i < 4; ++i) {
767 if (delta_array[i] + delta_scalar - x[i] >= y) {
781static inline uint16_t lower_bound_8x64(
const uint64_t* x, uint64_t y) {
782#ifdef PIXIE_AVX512_SUPPORT
784 auto y_8 = _mm512_set1_epi64(y);
785 auto reg_512 = _mm512_loadu_epi64(x);
786 auto cmp = _mm512_cmpge_epu64_mask(reg_512, y_8);
788 return _tzcnt_u16(cmp);
791#ifdef PIXIE_AVX2_SUPPORT
793 uint16_t len = lower_bound_4x64(x, y);
799 return len + lower_bound_4x64(x + 4, y);
803 for (uint16_t i = 0; i < 8; ++i) {
827static inline uint16_t lower_bound_delta_8x64(
const uint64_t* x,
829 const uint64_t* delta_array,
830 uint64_t delta_scalar) {
831#ifdef PIXIE_AVX512_SUPPORT
833 const __m512i dlt_512 = _mm512_loadu_epi64(delta_array);
834 auto x_512 = _mm512_loadu_epi64(x);
835 auto dlt_8 = _mm512_set1_epi64(delta_scalar);
836 auto y_8 = _mm512_set1_epi64(y);
838 auto tmp = _mm512_add_epi64(dlt_8, dlt_512);
839 auto reg_512 = _mm512_sub_epi64(tmp, x_512);
840 auto cmp = _mm512_cmpge_epu64_mask(reg_512, y_8);
842 return _tzcnt_u16(cmp);
845#ifdef PIXIE_AVX2_SUPPORT
847 uint16_t len = lower_bound_delta_4x64(x, y, delta_array, delta_scalar);
853 return len + lower_bound_delta_4x64(x + 4, y, delta_array + 4, delta_scalar);
857 for (uint16_t i = 0; i < 8; ++i) {
858 if (delta_array[i] + delta_scalar - x[i] >= y) {
872static inline uint16_t lower_bound_32x16(
const uint16_t* x, uint16_t y) {
873#ifdef PIXIE_AVX512_SUPPORT
875 auto y_32 = _mm512_set1_epi16(y);
876 auto reg_512 = _mm512_loadu_epi16(x);
877 auto cmp = _mm512_cmplt_epu16_mask(reg_512, y_32);
878 return std::popcount(cmp);
881#ifdef PIXIE_AVX2_SUPPORT
883 auto y_16 = _mm256_set1_epi16(y);
884 __m256i reg_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
886 const __m256i offset = _mm256_set1_epi16(0x8000);
887 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
888 __m256i y_offset = _mm256_xor_si256(y_16, offset);
889 uint32_t mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
891 uint16_t count = std::popcount(mask) >> 1;
893 reg_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x + 16));
895 x_offset = _mm256_xor_si256(reg_256, offset);
896 mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
898 return count + (std::popcount(mask) >> 1);
903 for (uint16_t i = 0; i < 32; ++i) {
927static inline uint16_t lower_bound_delta_32x16(
const uint16_t* x,
929 const uint16_t* delta_array,
930 uint16_t delta_scalar) {
931#ifdef PIXIE_AVX512_SUPPORT
933 const __m512i dlt_512 = _mm512_loadu_epi64(delta_array);
934 auto x_512 = _mm512_loadu_epi64(x);
935 auto dlt_32 = _mm512_set1_epi16(delta_scalar);
936 auto y_32 = _mm512_set1_epi16(y);
938 auto tmp = _mm512_add_epi16(dlt_32, dlt_512);
939 auto reg_512 = _mm512_sub_epi16(tmp, x_512);
940 auto cmp = _mm512_cmplt_epu16_mask(reg_512, y_32);
941 return std::popcount(cmp);
944#ifdef PIXIE_AVX2_SUPPORT
947 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(delta_array));
948 auto x_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x));
949 auto dlt_16 = _mm256_set1_epi16(delta_scalar);
950 auto y_16 = _mm256_set1_epi16(y);
952 auto tmp = _mm256_add_epi16(dlt_16, dlt_256);
953 auto reg_256 = _mm256_sub_epi16(tmp, x_256);
955 const __m256i offset = _mm256_set1_epi16(0x8000);
956 __m256i x_offset = _mm256_xor_si256(reg_256, offset);
957 __m256i y_offset = _mm256_xor_si256(y_16, offset);
958 uint32_t mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
960 uint16_t count = std::popcount(mask) >> 1;
963 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(delta_array + 16));
964 x_256 = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(x + 16));
966 tmp = _mm256_add_epi16(dlt_16, dlt_256);
967 reg_256 = _mm256_sub_epi16(tmp, x_256);
969 x_offset = _mm256_xor_si256(reg_256, offset);
970 mask = _mm256_movemask_epi8(_mm256_cmpgt_epi16(y_offset, x_offset));
972 return count + (std::popcount(mask) >> 1);
977 for (uint16_t i = 0; i < 32; ++i) {
978 if (delta_array[i] + delta_scalar - x[i] < y) {
999static inline void popcount_64x4(
const uint8_t* x, uint8_t* result) {
1000#ifdef PIXIE_AVX512_SUPPORT
1001 __m256i data = _mm256_loadu_si256((__m256i
const*)x);
1004 const __m256i low_bits_mask = _mm256_set1_epi8(0x0F);
1007 __m256i low_bits = _mm256_and_si256(data, low_bits_mask);
1008 __m256i low_count = _mm256_shuffle_epi8(lookup_popcount_4, low_bits);
1011 __m256i high_bits = _mm256_srli_epi16(data, 4);
1012 high_bits = _mm256_and_si256(high_bits, low_bits_mask);
1013 __m256i high_count = _mm256_shuffle_epi8(lookup_popcount_4, high_bits);
1016 __m256i result_vec =
1017 _mm256_or_si256(low_count, _mm256_slli_epi16(high_count, 4));
1018 _mm256_storeu_epi8(result, result_vec);
1021 for (
size_t i = 0; i < 32; i++) {
1023 uint8_t a = x[i] & 0x0F;
1024 uint8_t low_count = std::popcount(a);
1026 a = (x[i] >> 4) & 0x0F;
1027 uint8_t high_count = std::popcount(a);
1030 result[i] = low_count | (high_count << 4);
1046static inline void popcount_32x8(
const uint8_t* x, uint8_t* result) {
1047#ifdef PIXIE_AVX512_SUPPORT
1049 __m256i data = _mm256_loadu_si256((__m256i
const*)x);
1050 auto popcount_8 = _mm256_popcnt_epi8(data);
1051 _mm256_storeu_si256((__m256i*)result, popcount_8);
1053#ifdef PIXIE_AVX2_SUPPORT
1055 __m256i data = _mm256_loadu_si256((__m256i
const*)x);
1058 const __m256i low_bits_mask = _mm256_set1_epi8(0x0F);
1061 __m256i low_bits = _mm256_and_si256(data, low_bits_mask);
1062 __m256i low_count = _mm256_shuffle_epi8(lookup_popcount_4, low_bits);
1065 __m256i high_bits = _mm256_srli_epi16(data, 4);
1066 high_bits = _mm256_and_si256(high_bits, low_bits_mask);
1067 __m256i high_count = _mm256_shuffle_epi8(lookup_popcount_4, high_bits);
1069 __m256i result_vec = _mm256_add_epi8(low_count, high_count);
1070 _mm256_storeu_si256((__m256i*)result, result_vec);
1073 for (
size_t i = 0; i < 32; i++) {
1074 result[i] = std::popcount(x[i]);
1080#ifdef PIXIE_AVX2_SUPPORT
1083static inline const __m256i excess_lut_delta = _mm256_setr_epi8(
1094static inline const __m256i excess_lut_pos0 = _mm256_setr_epi8(
1104static inline const __m256i excess_lut_pos1 = _mm256_setr_epi8(
1114static inline const __m256i excess_lut_pos2 = _mm256_setr_epi8(
1123static inline const __m256i excess_lut_min = _mm256_setr_epi8(
1132static inline constexpr int8_t excess_lut_min_offset[16] = {
1133 4, 4, 4, 4, 2, 2, 1, 1, 3, 3, 1, 1, 2, 2, 1, 1};
1134static inline const __m256i excess_lut_pack_multiplier =
1135 _mm256_set1_epi16(0x1001);
1136static inline const __m256i excess_lut_bit0 = _mm256_set1_epi8(1);
1137static inline const __m256i excess_lut_bit1 = _mm256_set1_epi8(2);
1138static inline const __m256i excess_lut_bit2 = _mm256_set1_epi8(4);
1139static inline const __m256i excess_lut_bit3 = _mm256_set1_epi8(8);
1140static inline const __m256i excess_lut_nibble_index = _mm256_setr_epi8(
1149static inline const __m128i excess_lut_nibble_mask = _mm_set1_epi8(0x0F);
1152static inline __m256i excess_nibbles_128_avx2(
const uint64_t* s)
noexcept {
1153 __m128i word_vec = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(s));
1154 __m128i lo_nibbles = _mm_and_si128(word_vec, excess_lut_nibble_mask);
1155 __m128i hi_nibbles =
1156 _mm_and_si128(_mm_srli_epi16(word_vec, 4), excess_lut_nibble_mask);
1158 __m128i unpack_lo = _mm_unpacklo_epi8(lo_nibbles, hi_nibbles);
1159 __m128i unpack_hi = _mm_unpackhi_epi8(lo_nibbles, hi_nibbles);
1161 return _mm256_inserti128_si256(_mm256_castsi128_si256(unpack_lo), unpack_hi,
1165static inline __m256i excess_bit_masks_16x_i16() noexcept {
1166 return _mm256_setr_epi16(0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020,
1167 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800,
1168 0x1000, 0x2000, 0x4000,
1169 static_cast<int16_t
>(0x8000));
1172static inline __m256i excess_prefix_sum_16x_i16(__m256i v)
noexcept {
1174 __m256i t = _mm256_slli_si256(x, 2);
1175 x = _mm256_add_epi16(x, t);
1176 t = _mm256_slli_si256(x, 4);
1177 x = _mm256_add_epi16(x, t);
1178 t = _mm256_slli_si256(x, 8);
1179 x = _mm256_add_epi16(x, t);
1181 __m128i lo = _mm256_extracti128_si256(x, 0);
1182 __m128i hi = _mm256_extracti128_si256(x, 1);
1183 const int16_t carry =
static_cast<int16_t
>(_mm_extract_epi16(lo, 7));
1184 hi = _mm_add_epi16(hi, _mm_set1_epi16(carry));
1186 __m256i out = _mm256_castsi128_si256(lo);
1187 return _mm256_inserti128_si256(out, hi, 1);
1201 size_t offset = 128;
1215constexpr int8_t excess_byte_delta_value(uint8_t x) {
1216 return static_cast<int8_t
>(2 * std::popcount(x) - 8);
1219constexpr int8_t excess_byte_min_prefix_value(uint8_t x) {
1222 for (
int bit = 0; bit < 8; ++bit) {
1223 cur += ((x >> bit) & 1u) != 0 ? 1 : -1;
1224 if (bit == 0 || cur < best) {
1228 return static_cast<int8_t
>(best);
1231constexpr int8_t excess_byte_min_prefix_offset_value(uint8_t x) {
1234 int best_offset = 1;
1235 for (
int bit = 0; bit < 8; ++bit) {
1236 cur += ((x >> bit) & 1u) != 0 ? 1 : -1;
1237 if (bit == 0 || cur < best) {
1239 best_offset = bit + 1;
1242 return static_cast<int8_t
>(best_offset);
1245constexpr int8_t excess_nibble_min_prefix_offset_value(uint8_t x,
int bits) {
1248 int best_offset = 1;
1249 for (
int bit = 0; bit < bits; ++bit) {
1250 cur += ((x >> bit) & 1u) != 0 ? 1 : -1;
1251 if (bit == 0 || cur < best) {
1253 best_offset = bit + 1;
1256 return static_cast<int8_t
>(best_offset);
1259template <
typename Fn>
1260constexpr std::array<int8_t, 256> excess_make_byte_lut(Fn fn) {
1261 std::array<int8_t, 256> out{};
1262 for (
size_t i = 0; i < out.size(); ++i) {
1263 out[i] = fn(
static_cast<uint8_t
>(i));
1268static inline constexpr std::array<int8_t, 256> excess_byte_delta_lut =
1269 excess_make_byte_lut([](uint8_t x) {
return excess_byte_delta_value(x); });
1270static inline constexpr std::array<int8_t, 256> excess_byte_min_lut =
1271 excess_make_byte_lut(
1272 [](uint8_t x) {
return excess_byte_min_prefix_value(x); });
1273static inline constexpr std::array<int8_t, 256> excess_byte_min_offset_lut =
1274 excess_make_byte_lut(
1275 [](uint8_t x) {
return excess_byte_min_prefix_offset_value(x); });
1276static inline constexpr std::array<std::array<int8_t, 16>, 4>
1277 excess_partial_nibble_min_offset_lut = [] {
1278 std::array<std::array<int8_t, 16>, 4> out{};
1279 for (
size_t width = 1; width < out.size(); ++width) {
1280 for (
size_t nibble = 0; nibble < out[width].size(); ++nibble) {
1281 out[width][nibble] = excess_nibble_min_prefix_offset_value(
1282 static_cast<uint8_t
>(nibble),
static_cast<int>(width));
1295constexpr uint8_t excess_byte_record_lows_mask(uint8_t
byte,
int threshold) {
1298 for (
int bit = 0; bit < 8; ++bit) {
1299 cur += ((
byte >> bit) & 1u) ? 1 : -1;
1300 if (cur < threshold) {
1301 mask |=
static_cast<uint8_t
>(1u << bit);
1315static inline constexpr std::array<std::array<uint8_t, 8>, 256>
1316 excess_byte_record_lows_lut = [] {
1317 std::array<std::array<uint8_t, 8>, 256> out{};
1318 for (
size_t byte = 0;
byte < 256; ++byte) {
1319 for (
int g = 0; g < 8; ++g) {
1321 excess_byte_record_lows_mask(
static_cast<uint8_t
>(
byte), -g);
1340static inline int excess_positions_128(
const uint64_t* s,
1342 uint64_t* out)
noexcept {
1343 out[0] = out[1] = 0;
1344 const int block_delta = 2 * (std::popcount(s[0]) + std::popcount(s[1])) - 128;
1346 if (target_x < -128 || target_x > 128) {
1350#ifdef PIXIE_AVX2_SUPPORT
1351 const __m256i vdelta = excess_lut_delta;
1352 const __m256i vpos0 = excess_lut_pos0;
1353 const __m256i vpos1 = excess_lut_pos1;
1354 const __m256i vpos2 = excess_lut_pos2;
1355 const __m256i vmult = excess_lut_pack_multiplier;
1356 const __m256i vbit0 = excess_lut_bit0;
1357 const __m256i vbit1 = excess_lut_bit1;
1358 const __m256i vbit2 = excess_lut_bit2;
1359 const __m256i vbit3 = excess_lut_bit3;
1361 const int d = 2 * target_x - block_delta;
1362 if (d < -128 || d > 128) {
1366 __m256i nibbles = excess_nibbles_128_avx2(s);
1368 __m256i ps = _mm256_shuffle_epi8(vdelta, nibbles);
1369 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 1));
1370 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 2));
1371 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 4));
1372 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 8));
1374 __m128i ps_lo = _mm256_castsi256_si128(ps);
1375 __m128i ps_hi = _mm256_extracti128_si256(ps, 1);
1376 __m128i carry = _mm_set1_epi8((int8_t)_mm_extract_epi8(ps_lo, 15));
1377 ps_hi = _mm_add_epi8(ps_hi, carry);
1378 ps = _mm256_inserti128_si256(_mm256_castsi128_si256(ps_lo), ps_hi, 1);
1380 __m256i b = _mm256_permute2x128_si256(ps, ps, 0x08);
1381 __m256i excl_ps = _mm256_alignr_epi8(ps, b, 15);
1383 __m256i vtgt = _mm256_set1_epi8((int8_t)target_x);
1384 __m256i t = _mm256_sub_epi8(vtgt, excl_ps);
1386 __m256i cmp0 = _mm256_cmpeq_epi8(_mm256_shuffle_epi8(vpos0, nibbles), t);
1387 __m256i cmp1 = _mm256_cmpeq_epi8(_mm256_shuffle_epi8(vpos1, nibbles), t);
1388 __m256i cmp2 = _mm256_cmpeq_epi8(_mm256_shuffle_epi8(vpos2, nibbles), t);
1389 __m256i cmp3 = _mm256_cmpeq_epi8(ps, vtgt);
1391 __m256i bit0 = _mm256_and_si256(cmp0, vbit0);
1392 __m256i bit1 = _mm256_and_si256(cmp1, vbit1);
1393 __m256i bit2 = _mm256_and_si256(cmp2, vbit2);
1394 __m256i bit3 = _mm256_and_si256(cmp3, vbit3);
1396 __m256i total_match =
1397 _mm256_or_si256(_mm256_or_si256(bit0, bit1), _mm256_or_si256(bit2, bit3));
1399 __m256i res = _mm256_maddubs_epi16(total_match, vmult);
1400 __m128i res_lo = _mm256_castsi256_si128(res);
1401 __m128i res_hi = _mm256_extracti128_si256(res, 1);
1402 __m128i packed = _mm_packus_epi16(res_lo, res_hi);
1404 _mm_storeu_si128((__m128i*)out, packed);
1407 for (
size_t i = 0; i < 128; ++i) {
1408 const uint64_t w = s[i >> 6];
1409 const int bit = int((w >> (i & 63)) & 1ull);
1410 cur += bit ? +1 : -1;
1411 if (cur == target_x) {
1412 out[i >> 6] |= (uint64_t{1} << (i & 63));
1428static inline int prefix_excess_128(
const uint64_t* s,
1429 size_t end_offset)
noexcept {
1430 end_offset = end_offset > 128 ? 128 : end_offset;
1431 if (end_offset == 0) {
1434 if (end_offset <= 64) {
1435 const int ones =
static_cast<int>(std::popcount(
1436 s[0] & first_bits_mask(
static_cast<uint32_t
>(end_offset))));
1437 return 2 * ones -
static_cast<int>(end_offset);
1439 const int ones =
static_cast<int>(
1440 std::popcount(s[0]) +
1441 std::popcount(s[1] &
1442 first_bits_mask(
static_cast<uint32_t
>(end_offset - 64))));
1443 return 2 * ones -
static_cast<int>(end_offset);
1455static inline int prefix_excess_64(
const uint64_t* s,
1456 size_t end_offset)
noexcept {
1457 end_offset = end_offset > 64 ? 64 : end_offset;
1458 if (end_offset == 0) {
1461 const int ones =
static_cast<int>(
1462 std::popcount(s[0] & first_bits_mask(
static_cast<uint32_t
>(end_offset))));
1463 return 2 * ones -
static_cast<int>(end_offset);
1466static inline ExcessResult excess_min_128_byte_lut_short(
1469 size_t right)
noexcept {
1470 int best = prefix_excess_128(s, left);
1471 size_t best_offset = left;
1472 if (left == right) {
1473 return {best, best_offset};
1478 for (; bit < right && (bit & 7u) != 0; ++bit) {
1479 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1480 const size_t offset = bit + 1;
1481 if (current < best) {
1483 best_offset = offset;
1487 for (; bit + 8 <= right; bit += 8) {
1488 const uint8_t
byte =
1489 static_cast<uint8_t
>((s[bit >> 6] >> (bit & 63)) & 0xFFu);
1490 const int candidate = current + excess_byte_min_lut[byte];
1491 if (candidate < best) {
1493 best_offset = bit +
static_cast<size_t>(excess_byte_min_offset_lut[byte]);
1495 current += excess_byte_delta_lut[byte];
1498 for (; bit < right; ++bit) {
1499 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1500 const size_t offset = bit + 1;
1501 if (current < best) {
1503 best_offset = offset;
1507 return {best, best_offset};
1524static inline ExcessResult excess_min_64(
const uint64_t* s,
1526 size_t right)
noexcept {
1530 left = std::min<size_t>(left, 64);
1531 right = std::min<size_t>(right, 64);
1533 int best = prefix_excess_64(s, left);
1534 size_t best_offset = left;
1535 if (left == right) {
1536 return {best, best_offset};
1539#ifdef PIXIE_SSE41_SUPPORT
1542 for (; bit < right && (bit & 3u) != 0; ++bit) {
1543 current += ((s[0] >> bit) & 1ull) != 0 ? 1 : -1;
1544 const size_t offset = bit + 1;
1545 if (current < best) {
1547 best_offset = offset;
1551 const size_t first_full_nibble = bit >> 2;
1552 const size_t last_full_nibble = right >> 2;
1553 const size_t right_partial_width = bit < right ? (right & 3u) : 0;
1554 const size_t end_nibble =
1555 last_full_nibble + (right_partial_width == 0 ? 0 : 1);
1556 if (first_full_nibble < end_nibble) {
1557 const __m128i nibbles = excess_nibbles_64_sse(s);
1559 __m128i ps = _mm_shuffle_epi8(excess_lut_delta_sse, nibbles);
1560 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 1));
1561 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 2));
1562 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 4));
1563 ps = _mm_add_epi8(ps, _mm_slli_si128(ps, 8));
1565 const __m128i excl_ps = _mm_slli_si128(ps, 1);
1566 __m128i local_min = _mm_shuffle_epi8(excess_lut_min_sse, nibbles);
1567 if (right_partial_width != 0) {
1568 __m128i partial_min = _mm_shuffle_epi8(excess_lut_pos0_sse, nibbles);
1569 if (right_partial_width >= 2) {
1570 partial_min = _mm_min_epi8(
1571 partial_min, _mm_shuffle_epi8(excess_lut_pos1_sse, nibbles));
1573 if (right_partial_width >= 3) {
1574 partial_min = _mm_min_epi8(
1575 partial_min, _mm_shuffle_epi8(excess_lut_pos2_sse, nibbles));
1577 local_min = _mm_blendv_epi8(
1578 local_min, partial_min,
1579 _mm_cmpeq_epi8(excess_lut_nibble_index_sse,
1580 _mm_set1_epi8(
static_cast<int8_t
>(last_full_nibble))));
1582 const __m128i partial_candidates = _mm_add_epi8(excl_ps, local_min);
1584 const __m128i idx = excess_lut_nibble_index_sse;
1585 const int first_minus_one_value =
static_cast<int>(first_full_nibble) - 1;
1586 const __m128i first_minus_one =
1587 _mm_set1_epi8(
static_cast<int8_t
>(first_minus_one_value));
1588 const __m128i last = _mm_set1_epi8(
static_cast<int8_t
>(end_nibble));
1589 const __m128i active = _mm_and_si128(_mm_cmpgt_epi8(idx, first_minus_one),
1590 _mm_cmpgt_epi8(last, idx));
1591 const __m128i masked_candidates =
1592 _mm_blendv_epi8(_mm_set1_epi8(127), partial_candidates, active);
1594 __m128i min128 = masked_candidates;
1595 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 8));
1596 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 4));
1597 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 2));
1598 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 1));
1600 const int candidate_min =
1601 static_cast<int>(
static_cast<int8_t
>(_mm_extract_epi8(min128, 0)));
1602 if (candidate_min < best) {
1603 const __m128i equal_min = _mm_cmpeq_epi8(
1604 masked_candidates, _mm_set1_epi8(
static_cast<int8_t
>(candidate_min)));
1605 const uint32_t equal_mask =
1606 static_cast<uint32_t
>(_mm_movemask_epi8(equal_min));
1607 const uint32_t nibble_index = std::countr_zero(equal_mask);
1608 const uint8_t nibble =
1609 static_cast<uint8_t
>((s[0] >> (nibble_index * 4u)) & 0xFu);
1610 best = candidate_min;
1611 if (right_partial_width != 0 && nibble_index == last_full_nibble) {
1614 size_t local_offset = 1;
1615 for (
size_t i = 0; i < right_partial_width; ++i) {
1616 local += ((nibble >> i) & 1u) != 0 ? 1 : -1;
1617 if (i == 0 || local < local_best) {
1619 local_offset = i + 1;
1622 best_offset =
static_cast<size_t>(nibble_index) * 4u + local_offset;
1624 best_offset =
static_cast<size_t>(nibble_index) * 4u +
1625 static_cast<size_t>(excess_nibble_min_offset[nibble]);
1629 bit = end_nibble * 4;
1632 for (; bit < right; ++bit) {
1633 current += ((s[0] >> bit) & 1ull) != 0 ? 1 : -1;
1634 const size_t offset = bit + 1;
1635 if (current < best) {
1637 best_offset = offset;
1642 for (
size_t bit = left; bit < right; ++bit) {
1643 current += ((s[0] >> bit) & 1ull) != 0 ? 1 : -1;
1644 const size_t offset = bit + 1;
1645 if (current < best) {
1647 best_offset = offset;
1652 return {best, best_offset};
1661static inline ExcessResult excess_min_128(
const uint64_t* s,
1663 size_t right)
noexcept {
1667 left = std::min<size_t>(left, 128);
1668 right = std::min<size_t>(right, 128);
1670 if (right - left <= 32 && (left & 7u) == 0 && (right & 7u) == 0)
1672 return excess_min_128_byte_lut_short(s, left, right);
1675 int best = prefix_excess_128(s, left);
1676 size_t best_offset = left;
1677 if (left == right) {
1678 return {best, best_offset};
1681#ifdef PIXIE_AVX2_SUPPORT
1684 for (; bit < right && (bit & 3u) != 0; ++bit) {
1685 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1686 const size_t offset = bit + 1;
1687 if (current < best) {
1689 best_offset = offset;
1693 const size_t first_nibble = bit >> 2;
1694 const size_t last_full_nibble = right >> 2;
1695 const size_t right_partial_width = bit < right ? (right & 3u) : 0;
1696 const size_t end_nibble =
1697 last_full_nibble + (right_partial_width == 0 ? 0 : 1);
1698 if (first_nibble < end_nibble) {
1699 const __m128i bytes = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(s));
1700 const __m128i lo_nibbles = _mm_and_si128(bytes, excess_lut_nibble_mask_sse);
1701 const __m128i hi_nibbles =
1702 _mm_and_si128(_mm_srli_epi16(bytes, 4), excess_lut_nibble_mask_sse);
1703 const __m128i lo_delta = _mm_shuffle_epi8(excess_lut_delta_sse, lo_nibbles);
1704 const __m128i hi_delta = _mm_shuffle_epi8(excess_lut_delta_sse, hi_nibbles);
1705 const __m128i byte_delta = _mm_add_epi8(lo_delta, hi_delta);
1706 const __m128i byte_prefix = excess_prefix_sum_16x_i8(byte_delta);
1707 const __m128i byte_prefix_before = _mm_slli_si128(byte_prefix, 1);
1709 __m128i lo_local_min = _mm_shuffle_epi8(excess_lut_min_sse, lo_nibbles);
1710 __m128i hi_local_min = _mm_shuffle_epi8(excess_lut_min_sse, hi_nibbles);
1712 const __m128i byte_index = excess_lut_nibble_index_sse;
1713 if (right_partial_width != 0) {
1714 const bool partial_is_high = (last_full_nibble & 1u) != 0;
1715 const size_t partial_byte = last_full_nibble >> 1;
1716 const __m128i partial_source = partial_is_high ? hi_nibbles : lo_nibbles;
1717 __m128i partial_min =
1718 _mm_shuffle_epi8(excess_lut_pos0_sse, partial_source);
1719 if (right_partial_width >= 2) {
1720 partial_min = _mm_min_epi8(
1721 partial_min, _mm_shuffle_epi8(excess_lut_pos1_sse, partial_source));
1723 if (right_partial_width >= 3) {
1724 partial_min = _mm_min_epi8(
1725 partial_min, _mm_shuffle_epi8(excess_lut_pos2_sse, partial_source));
1727 const __m128i partial_lane = _mm_cmpeq_epi8(
1728 byte_index, _mm_set1_epi8(
static_cast<int8_t
>(partial_byte)));
1729 if (partial_is_high) {
1730 hi_local_min = _mm_blendv_epi8(hi_local_min, partial_min, partial_lane);
1732 lo_local_min = _mm_blendv_epi8(lo_local_min, partial_min, partial_lane);
1736 const __m128i lo_candidates =
1737 _mm_add_epi8(byte_prefix_before, lo_local_min);
1738 const __m128i hi_candidates =
1739 _mm_add_epi8(_mm_add_epi8(byte_prefix_before, lo_delta), hi_local_min);
1741 __m128i masked_lo = lo_candidates;
1742 __m128i masked_hi = hi_candidates;
1743 if (first_nibble != 0 || end_nibble != 32) {
1744 const __m128i first_minus_one = _mm_set1_epi8(
1745 static_cast<int8_t
>(
static_cast<int>(first_nibble) - 1));
1746 const __m128i last = _mm_set1_epi8(
static_cast<int8_t
>(end_nibble));
1747 const __m128i lo_active = _mm_and_si128(
1748 _mm_cmpgt_epi8(excess_lut_low_nibble_index_sse, first_minus_one),
1749 _mm_cmpgt_epi8(last, excess_lut_low_nibble_index_sse));
1750 const __m128i hi_active = _mm_and_si128(
1751 _mm_cmpgt_epi8(excess_lut_high_nibble_index_sse, first_minus_one),
1752 _mm_cmpgt_epi8(last, excess_lut_high_nibble_index_sse));
1753 masked_lo = _mm_blendv_epi8(_mm_set1_epi8(127), lo_candidates, lo_active);
1754 masked_hi = _mm_blendv_epi8(_mm_set1_epi8(127), hi_candidates, hi_active);
1757 const int candidate_min =
1758 excess_horizontal_min_i8(_mm_min_epi8(masked_lo, masked_hi));
1759 if (candidate_min < best) {
1760 const __m128i min_vec = _mm_set1_epi8(
static_cast<int8_t
>(candidate_min));
1761 const uint32_t lo_equal_mask =
static_cast<uint32_t
>(
1762 _mm_movemask_epi8(_mm_cmpeq_epi8(masked_lo, min_vec)));
1763 const uint32_t hi_equal_mask =
static_cast<uint32_t
>(
1764 _mm_movemask_epi8(_mm_cmpeq_epi8(masked_hi, min_vec)));
1765 const uint32_t lo_nibble_index =
1768 :
static_cast<uint32_t
>(std::countr_zero(lo_equal_mask)) * 2u;
1769 const uint32_t hi_nibble_index =
1772 :
static_cast<uint32_t
>(std::countr_zero(hi_equal_mask)) * 2u +
1774 const uint32_t nibble_index = std::min(lo_nibble_index, hi_nibble_index);
1775 const uint32_t byte_offset = nibble_index >> 1u;
1776 const uint64_t byte_word = s[byte_offset >> 3u];
1777 const uint8_t
byte =
static_cast<uint8_t
>(
1778 (byte_word >> ((byte_offset & 7u) * 8u)) & 0xFFu);
1779 const uint8_t nibble = (nibble_index & 1u) == 0
1780 ?
static_cast<uint8_t
>(
byte & 0xFu)
1781 :
static_cast<uint8_t
>((
byte >> 4u) & 0xFu);
1782 const size_t local_offset =
1783 right_partial_width != 0 && nibble_index == last_full_nibble
1784 ?
static_cast<size_t>(
1785 excess_partial_nibble_min_offset_lut[right_partial_width]
1787 : static_cast<size_t>(excess_lut_min_offset[nibble]);
1788 best = candidate_min;
1789 best_offset =
static_cast<size_t>(nibble_index) * 4u + local_offset;
1794 for (
size_t bit = 0; bit < right; ++bit) {
1795 current += ((s[bit >> 6] >> (bit & 63)) & 1ull) != 0 ? 1 : -1;
1796 const size_t offset = bit + 1;
1797 if (offset >= left && current < best) {
1799 best_offset = offset;
1804 return {best, best_offset};
1821 const uint64_t* suffix_s,
1823 const uint64_t* prefix_s,
1824 size_t prefix_right)
noexcept {
1825 suffix_left = std::min<size_t>(suffix_left, 64);
1826 prefix_right = std::min<size_t>(prefix_right, 64);
1827 return {excess_min_64(suffix_s, suffix_left, 63),
1828 excess_min_64(prefix_s, 0, prefix_right)};
1850 const uint64_t* suffix_s,
1852 const uint64_t* prefix_s,
1853 size_t prefix_right)
noexcept {
1854 suffix_left = std::min<size_t>(suffix_left, 128);
1855 prefix_right = std::min<size_t>(prefix_right, 128);
1856 if (suffix_left <= prefix_right || suffix_left > 127 || prefix_right > 127) {
1857 return {excess_min_128(suffix_s, suffix_left, 127),
1858 excess_min_128(prefix_s, 0, prefix_right)};
1861#ifdef PIXIE_AVX2_SUPPORT
1864 int suffix_best = prefix_excess_128(suffix_s, suffix_left);
1866 size_t suffix_bit = suffix_left;
1867 int suffix_current = suffix_best;
1868 for (; suffix_bit < 127 && (suffix_bit & 3u) != 0; ++suffix_bit) {
1870 ((suffix_s[suffix_bit >> 6] >> (suffix_bit & 63)) & 1ull) != 0 ? 1 : -1;
1871 const size_t offset = suffix_bit + 1;
1872 if (suffix_current < suffix.min_excess) {
1873 suffix = {suffix_current, offset};
1877 const size_t prefix_last_nibble = prefix_right >> 2;
1878 const size_t prefix_partial_width = prefix_right & 3u;
1879 const size_t prefix_end_nibble =
1880 prefix_last_nibble + (prefix_partial_width == 0 ? 0 : 1);
1881 const size_t suffix_first_nibble = suffix_bit < 127 ? suffix_bit >> 2 : 32;
1882 const int prefix_artificial_delta =
1883 prefix_excess_128(prefix_s, prefix_end_nibble * 4);
1885 const __m256i idx = excess_lut_nibble_index;
1886 const __m256i prefix_active = _mm256_cmpgt_epi8(
1887 _mm256_set1_epi8(
static_cast<int8_t
>(prefix_end_nibble)), idx);
1888 const __m256i suffix_active = _mm256_cmpgt_epi8(
1889 idx, _mm256_set1_epi8(
static_cast<int8_t
>(suffix_first_nibble) - 1));
1891 const __m256i prefix_nibbles = excess_nibbles_128_avx2(prefix_s);
1892 const __m256i suffix_nibbles = excess_nibbles_128_avx2(suffix_s);
1894 _mm256_blendv_epi8(_mm256_set1_epi8(3), prefix_nibbles, prefix_active);
1895 nibbles = _mm256_blendv_epi8(nibbles, suffix_nibbles, suffix_active);
1897 __m256i ps = _mm256_shuffle_epi8(excess_lut_delta, nibbles);
1898 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 1));
1899 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 2));
1900 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 4));
1901 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 8));
1903 __m128i ps_lo = _mm256_castsi256_si128(ps);
1904 __m128i ps_hi = _mm256_extracti128_si256(ps, 1);
1906 _mm_set1_epi8(
static_cast<int8_t
>(_mm_extract_epi8(ps_lo, 15)));
1907 ps_hi = _mm_add_epi8(ps_hi, carry);
1908 ps = _mm256_inserti128_si256(_mm256_castsi128_si256(ps_lo), ps_hi, 1);
1910 __m256i b = _mm256_permute2x128_si256(ps, ps, 0x08);
1911 const __m256i excl_ps = _mm256_alignr_epi8(ps, b, 15);
1913 __m256i local_min = _mm256_shuffle_epi8(excess_lut_min, nibbles);
1914 if (prefix_partial_width != 0) {
1915 __m256i partial_min = _mm256_shuffle_epi8(excess_lut_pos0, nibbles);
1916 if (prefix_partial_width >= 2) {
1917 partial_min = _mm256_min_epi8(
1918 partial_min, _mm256_shuffle_epi8(excess_lut_pos1, nibbles));
1920 if (prefix_partial_width >= 3) {
1921 partial_min = _mm256_min_epi8(
1922 partial_min, _mm256_shuffle_epi8(excess_lut_pos2, nibbles));
1924 local_min = _mm256_blendv_epi8(
1925 local_min, partial_min,
1927 idx, _mm256_set1_epi8(
static_cast<int8_t
>(prefix_last_nibble))));
1929 __m256i suffix_partial_min = _mm256_min_epi8(
1930 _mm256_shuffle_epi8(excess_lut_pos0, nibbles),
1931 _mm256_min_epi8(_mm256_shuffle_epi8(excess_lut_pos1, nibbles),
1932 _mm256_shuffle_epi8(excess_lut_pos2, nibbles)));
1933 const __m256i suffix_tail_active = _mm256_and_si256(
1934 suffix_active, _mm256_cmpeq_epi8(idx, _mm256_set1_epi8(31)));
1936 _mm256_blendv_epi8(local_min, suffix_partial_min, suffix_tail_active);
1938 const __m256i base_candidates = _mm256_add_epi8(excl_ps, local_min);
1939 const __m256i sentinel = _mm256_set1_epi8(127);
1941 const __m256i prefix_candidates =
1942 _mm256_blendv_epi8(sentinel, base_candidates, prefix_active);
1943 const __m256i suffix_candidates = _mm256_blendv_epi8(
1945 _mm256_add_epi8(base_candidates,
1946 _mm256_set1_epi8(
static_cast<int8_t
>(
1947 suffix_current - prefix_artificial_delta))),
1950 auto reduce_min = [](__m256i values) {
1951 __m128i min128 = _mm_min_epi8(_mm256_castsi256_si128(values),
1952 _mm256_extracti128_si256(values, 1));
1953 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 8));
1954 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 4));
1955 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 2));
1956 min128 = _mm_min_epi8(min128, _mm_alignr_epi8(min128, min128, 1));
1957 return static_cast<int>(
static_cast<int8_t
>(_mm_extract_epi8(min128, 0)));
1960 auto local_offset = [](uint8_t nibble,
size_t width_value) {
1961 if (width_value == 0 || width_value == 4) {
1962 return static_cast<size_t>(excess_lut_min_offset[nibble]);
1966 size_t best_offset = 1;
1967 for (
size_t i = 0; i < width_value; ++i) {
1968 current += ((nibble >> i) & 1u) != 0 ? 1 : -1;
1969 if (i == 0 || current < best) {
1971 best_offset = i + 1;
1977 const int prefix_min = reduce_min(prefix_candidates);
1978 if (prefix_min < prefix.min_excess) {
1979 const uint32_t mask =
static_cast<uint32_t
>(_mm256_movemask_epi8(
1980 _mm256_cmpeq_epi8(prefix_candidates,
1981 _mm256_set1_epi8(
static_cast<int8_t
>(prefix_min)))));
1982 const uint32_t prefix_lane = std::countr_zero(mask);
1983 const uint64_t word = prefix_s[prefix_lane >> 4];
1984 const uint8_t nibble =
1985 static_cast<uint8_t
>((word >> ((prefix_lane & 15u) * 4u)) & 0xFu);
1986 prefix.min_excess = prefix_min;
1987 const size_t width =
1988 prefix_partial_width != 0 && prefix_lane == prefix_last_nibble
1989 ? prefix_partial_width
1992 static_cast<size_t>(prefix_lane) * 4u + local_offset(nibble, width);
1995 const int suffix_min = reduce_min(suffix_candidates);
1996 if (suffix_min < suffix.min_excess) {
1997 const uint32_t mask =
static_cast<uint32_t
>(_mm256_movemask_epi8(
1998 _mm256_cmpeq_epi8(suffix_candidates,
1999 _mm256_set1_epi8(
static_cast<int8_t
>(suffix_min)))));
2000 const uint32_t suffix_lane = std::countr_zero(mask);
2001 const uint64_t word = suffix_s[suffix_lane >> 4];
2002 const uint8_t nibble =
2003 static_cast<uint8_t
>((word >> ((suffix_lane & 15u) * 4u)) & 0xFu);
2004 suffix.min_excess = suffix_min;
2005 const size_t width = suffix_lane == 31 ? 3 : 4;
2007 static_cast<size_t>(suffix_lane) * 4u + local_offset(nibble, width);
2010 return {suffix, prefix};
2012 return {excess_min_128(suffix_s, suffix_left, 127),
2013 excess_min_128(prefix_s, 0, prefix_right)};
2032static inline size_t forward_search_128(
const uint64_t* s,
2034 size_t start_offset,
2035 int* block_excess =
nullptr) noexcept {
2037 const int delta = excess_positions_128(s, target_x, out);
2038 if (block_excess !=
nullptr) {
2039 *block_excess = delta;
2041 if (start_offset >= 128) {
2045 const size_t first_word = start_offset >> 6;
2046 const size_t first_bit = start_offset & 63;
2047 for (
size_t word = first_word; word < 2; ++word) {
2048 uint64_t mask = out[word];
2049 if (word == first_word && first_bit != 0) {
2050 mask &= ~first_bits_mask(first_bit);
2053 return word * 64 + std::countr_zero(mask);
2076static inline size_t backward_search_128(
const uint64_t* s,
2079 int* block_excess =
nullptr) noexcept {
2081 const int delta = excess_positions_128(s, target_x, out);
2082 if (block_excess !=
nullptr) {
2083 *block_excess = delta;
2085 if (end_offset == 0) {
2089 const size_t max_prefix_length = end_offset - 1;
2090 if (max_prefix_length > 0) {
2091 const size_t last_bit_index = max_prefix_length - 1;
2092 size_t word = last_bit_index >> 6;
2093 const size_t bit_in_word = last_bit_index & 63;
2094 uint64_t mask = out[word] & first_bits_mask(bit_in_word + 1);
2097 return word * 64 + (63 - std::countl_zero(mask)) + 1;
2106 return target_x == 0 ? 0 : 128;
2121static inline void excess_positions_512(
const uint64_t* s,
2123 uint64_t* out)
noexcept {
2124 if (target_x < -512 || target_x > 512) {
2125 out[0] = out[1] = out[2] = out[3] = 0;
2126 out[4] = out[5] = out[6] = out[7] = 0;
2130 for (
int k = 0; k < 4; ++k) {
2131 target_x -= excess_positions_128(s + 2 * k, target_x, out + 2 * k);
2148static inline void excess_record_lows_128(
const uint64_t* s,
2149 uint64_t* out)
noexcept {
2150 out[0] = out[1] = 0;
2153 for (
size_t i = 0; i < 128; ++i) {
2154 const uint64_t w = s[i >> 6];
2155 const int bit =
static_cast<int>((w >> (i & 63)) & 1ull);
2156 cur += bit ? +1 : -1;
2159 out[i >> 6] |= (uint64_t{1} << (i & 63));
2172static inline void excess_record_lows_128_byte_lut(
const uint64_t* s,
2173 uint64_t* out)
noexcept {
2174 out[0] = out[1] = 0;
2177 for (
size_t byte_idx = 0; byte_idx < 16; ++byte_idx) {
2178 const size_t bit_base = byte_idx * 8;
2179 const uint8_t
byte =
2180 static_cast<uint8_t
>((s[bit_base >> 6] >> (bit_base & 63)) & 0xFFu);
2181 const int byte_min = excess_byte_min_lut[byte];
2182 if (cur + byte_min < best) {
2183 for (
size_t i = 0; i < 8; ++i) {
2184 const int bit =
static_cast<int>((
byte >> i) & 1u);
2185 cur += bit ? +1 : -1;
2188 const size_t pos = bit_base + i;
2189 out[pos >> 6] |= (uint64_t{1} << (pos & 63));
2193 cur += excess_byte_delta_lut[byte];
2206static inline void excess_record_lows_128_lut(
const uint64_t* s,
2207 uint64_t* out)
noexcept {
2208 out[0] = out[1] = 0;
2211 for (
size_t byte_idx = 0; byte_idx < 16; ++byte_idx) {
2212 const size_t bit_base = byte_idx * 8;
2213 const uint8_t
byte =
2214 static_cast<uint8_t
>((s[bit_base >> 6] >> (bit_base & 63)) & 0xFFu);
2215 const int gap = cur - best;
2216 const int idx = gap > 7 ? 7 : (gap < 0 ? 0 : gap);
2217 const uint8_t mask =
2218 excess_byte_record_lows_lut[byte][
static_cast<size_t>(idx)];
2223 uint8_t local_mask = 0;
2224 for (
int bit = 0; bit < 8; ++bit) {
2225 local += ((
byte >> bit) & 1u) ? 1 : -1;
2226 if (local < local_best) {
2228 local_mask |=
static_cast<uint8_t
>(1u << bit);
2232 uint8_t out_mask = 0;
2234 for (
int bit = 0; bit < 8; ++bit) {
2235 local += ((
byte >> bit) & 1u) ? 1 : -1;
2236 if (cur + local < best) {
2237 out_mask |=
static_cast<uint8_t
>(1u << bit);
2241 if (out_mask != 0) {
2242 const uint64_t word =
static_cast<uint64_t
>(out_mask)
2244 out[bit_base >> 6] |= word;
2247 cur += excess_byte_delta_lut[byte];
2251#ifdef PIXIE_AVX2_SUPPORT
2259static inline void excess_record_lows_128_avx2(
const uint64_t* s,
2260 uint64_t* out)
noexcept {
2261 out[0] = out[1] = 0;
2265 const __m256i masks = excess_bit_masks_16x_i16();
2266 const __m256i zero = _mm256_setzero_si256();
2267 const __m256i pos = _mm256_set1_epi16(1);
2268 const __m256i neg = _mm256_set1_epi16(-1);
2270 for (
size_t chunk = 0; chunk < 8; ++chunk) {
2271 const size_t chunk_bit = chunk * 16;
2272 const uint16_t bits =
2274 ?
static_cast<uint16_t
>((s[0] >> (chunk * 16)) & 0xFFFFu)
2275 : static_cast<uint16_t>((s[1] >> ((chunk - 4) * 16)) & 0xFFFFu);
2277 const __m256i vb = _mm256_set1_epi16(
static_cast<int16_t
>(bits));
2278 const __m256i m = _mm256_and_si256(vb, masks);
2279 const __m256i is_zero = _mm256_cmpeq_epi16(m, zero);
2280 const __m256i steps = _mm256_blendv_epi8(pos, neg, is_zero);
2281 const __m256i pref_rel = excess_prefix_sum_16x_i16(steps);
2282 const __m256i pref_abs = _mm256_add_epi16(
2283 pref_rel, _mm256_set1_epi16(
static_cast<int16_t
>(cur)));
2285 alignas(32) int16_t vals[16];
2286 _mm256_store_si256(
reinterpret_cast<__m256i*
>(vals), pref_abs);
2288 for (
size_t lane = 0; lane < 16; ++lane) {
2289 const int val = vals[lane];
2292 const size_t pos_idx = chunk_bit + lane;
2293 out[pos_idx >> 6] |= (uint64_t{1} << (pos_idx & 63));
2297 cur += 2 *
static_cast<int>(std::popcount(bits)) - 16;
2310static inline void excess_record_lows_128_nibble_lut(
const uint64_t* s,
2311 uint64_t* out)
noexcept {
2312 out[0] = out[1] = 0;
2316 const __m256i vdelta = excess_lut_delta;
2317 const __m256i vmin = excess_lut_min;
2319 __m256i nibbles = excess_nibbles_128_avx2(s);
2322 __m256i ps = _mm256_shuffle_epi8(vdelta, nibbles);
2323 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 1));
2324 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 2));
2325 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 4));
2326 ps = _mm256_add_epi8(ps, _mm256_slli_si256(ps, 8));
2328 __m128i ps_lo = _mm256_castsi256_si128(ps);
2329 __m128i ps_hi = _mm256_extracti128_si256(ps, 1);
2331 _mm_set1_epi8(
static_cast<int8_t
>(_mm_extract_epi8(ps_lo, 15)));
2332 ps_hi = _mm_add_epi8(ps_hi, carry);
2333 ps = _mm256_inserti128_si256(_mm256_castsi128_si256(ps_lo), ps_hi, 1);
2336 __m256i b = _mm256_permute2x128_si256(ps, ps, 0x08);
2337 __m256i excl_ps = _mm256_alignr_epi8(ps, b, 15);
2340 __m256i local_min = _mm256_shuffle_epi8(vmin, nibbles);
2342 alignas(32) int8_t excl[32];
2343 _mm256_store_si256(
reinterpret_cast<__m256i*
>(excl), excl_ps);
2345 alignas(32) int8_t nibble_min[32];
2346 _mm256_store_si256(
reinterpret_cast<__m256i*
>(nibble_min), local_min);
2348 alignas(32) int8_t nibble_vals[32];
2349 _mm256_store_si256(
reinterpret_cast<__m256i*
>(nibble_vals), nibbles);
2351 for (
int n = 0; n < 32; ++n) {
2352 const int nibble_base = cur + excl[n];
2353 const int nibble_best = nibble_base + nibble_min[n];
2354 if (nibble_best < best) {
2356 const uint8_t nibble =
static_cast<uint8_t
>(nibble_vals[n]);
2358 for (
int bit = 0; bit < 4; ++bit) {
2359 local += ((nibble >> bit) & 1u) ? 1 : -1;
2360 const int val = nibble_base + local;
2363 const size_t pos =
static_cast<size_t>(n) * 4 + bit;
2364 out[pos >> 6] |= (uint64_t{1} << (pos & 63));
2384static inline void excess_record_lows_512(
const uint64_t* s,
2385 uint64_t* out)
noexcept {
2386 out[0] = out[1] = out[2] = out[3] = 0;
2387 out[4] = out[5] = out[6] = out[7] = 0;
2390 for (
int k = 0; k < 4; ++k) {
2391 const uint64_t* block = s + 2 * k;
2392 uint64_t* block_out = out + 2 * k;
2393 for (
size_t i = 0; i < 128; ++i) {
2394 const uint64_t w = block[i >> 6];
2395 const int bit =
static_cast<int>((w >> (i & 63)) & 1ull);
2396 global += bit ? +1 : -1;
2397 if (global < best) {
2399 block_out[i >> 6] |= (uint64_t{1} << (i & 63));
2416static inline void rank_32x8(
const uint8_t* x, uint8_t* result) {
2417#ifdef PIXIE_AVX512_SUPPORT
2419 popcount_32x8(x, result);
2420 __m256i prefix_sums = _mm256_loadu_si256((__m256i
const*)result);
2421 const __m256i zero = _mm256_setzero_si256();
2423 prefix_sums = _mm256_add_epi8(prefix_sums,
2424 _mm256_alignr_epi8(prefix_sums, zero, 16 - 1));
2425 prefix_sums = _mm256_add_epi8(prefix_sums,
2426 _mm256_alignr_epi8(prefix_sums, zero, 16 - 2));
2427 prefix_sums = _mm256_add_epi8(prefix_sums,
2428 _mm256_alignr_epi8(prefix_sums, zero, 16 - 4));
2429 prefix_sums = _mm256_add_epi8(prefix_sums,
2430 _mm256_alignr_epi8(prefix_sums, zero, 16 - 8));
2434 __m128i low_lane = _mm256_extracti128_si256(prefix_sums, 0);
2435 __m128i high_lane = _mm256_extracti128_si256(prefix_sums, 1);
2436 auto last_val_low = _mm_extract_epi8(low_lane, 15);
2437 __m128i add_to_high = _mm_set1_epi8(last_val_low);
2438 high_lane = _mm_add_epi8(high_lane, add_to_high);
2439 prefix_sums = _mm256_set_m128i(high_lane, low_lane);
2440 _mm256_storeu_epi8(result, prefix_sums);
2443 result[0] = std::popcount(x[0]);
2444 for (
size_t i = 1; i < 32; ++i) {
2445 result[i] = std::popcount(x[i]) + result[i - 1];
Pair of boundary minimum results for adjacent BP query blocks.
Definition bits.h:1210
Minimum prefix excess in a 128-bit bitstring range.
Definition bits.h:1199