16#include <simd/integer.h>
36 namespace rank_detail {
42 for (
unsigned word = 0; word < (bits >> 6); ++word)
43 result +=
unsigned(std::popcount(words[word]));
45 result += unsigned(std::popcount(words[bits >> 6] & ((std::uint64_t{1} << (bits & 63)) - 1)));
51 template <simd::architecture Arch,
unsigned Vector>
52 simd_inline
auto prefix_vector(std::uint64_t
const * words,
unsigned bits)
noexcept {
53 constexpr auto bytes = backend_detail::register_bytes<Arch>;
54 using W = simd::vec<std::uint64_t, bytes / 8, Arch>;
55 constexpr auto positions = [=] {
56 std::array<std::uint64_t, bytes / 8> result{};
57 for (
unsigned i = 0; i < result.size(); ++i) result[i] = Vector * result.size() + i;
60 auto boundary = W(bits >> 6);
61 auto mask = select(W(positions) < boundary, W(~std::uint64_t{0}),
62 select(W(positions) == boundary, W((std::uint64_t{1} << (bits & 63)) - 1), W(0)));
63 return popcount(reinterpret_bits<std::uint8_t>(W::loadu(words + Vector * (bytes / 8)) & mask));
66 template <simd::architecture Arch,
unsigned Vector = 0>
67 simd_inline
auto prefix_vectors(std::uint64_t
const * words,
unsigned bits)
noexcept {
68 auto counts = prefix_vector<Arch, Vector>(words, bits);
69 if constexpr ((Vector + 1) * backend_detail::register_bytes<Arch> == 64)
return counts;
70 else return counts + prefix_vectors<Arch, Vector + 1>(words, bits);
74 template <simd::architecture Arch = simd::scalar>
75 simd_inline
unsigned prefix512(std::uint64_t
const * words,
unsigned bits)
noexcept {
76 if constexpr (std::same_as<Arch, simd::scalar>)
return prefix512_portable(words, bits);
77 else return unsigned(reduce_add_widened(prefix_vectors<Arch>(words, bits)));
82 for (
unsigned i = 0; i < 8; ++i) total +=
unsigned(std::popcount(words[i]));
86 template <simd::architecture Arch,
unsigned Vector = 0>
88 constexpr auto width = backend_detail::register_bytes<Arch>;
89 using V = simd::vec<std::uint8_t, width, Arch>;
90 auto counts = popcount(V::loadu(bytes + Vector * width));
91 if constexpr ((Vector + 1) * width == 64)
return counts;
92 else return counts + population_vectors<Arch, Vector + 1>(bytes);
95 template <simd::architecture Arch = simd::scalar>
96 simd_inline
unsigned popcount512(std::uint64_t
const * words)
noexcept {
98 else return unsigned(reduce_add_widened(population_vectors<Arch>(
99 reinterpret_cast<std::uint8_t
const *
>(words))));
104 constexpr unsigned run_prefix(std::uint32_t packed,
unsigned run)
noexcept {
105 std::uint64_t selected = packed & ((std::uint64_t{1} << (11 * run)) - 1);
106 return unsigned(((selected * 0x400801ull) >> 22) & 2047u);
113 return (block & ((std::uint64_t{1} << 21) - 1)) == 0;
115 std::uint32_t
before(std::uint64_t block, std::uint64_t total) {
117 if (total < epoch_base || total - epoch_base > std::numeric_limits<std::uint32_t>::max())
118 throw std::overflow_error(
"rank relative count");
133 std::span<rank_block const> blocks,
134 std::span<std::uint64_t const> supers,
135 std::uint64_t bit_count)
138 if (bit_count > std::numeric_limits<std::uint64_t>::max() - 0xffffffffu ||
139 words.size() != ((bit_count + 63) >> 6) ||
140 blocks.size() != ((bit_count + 2047) >> 11) ||
141 supers.size() != ((bit_count + 0xffffffffu) >> 32))
142 throw std::invalid_argument(
"invalid rank spans");
147 template <simd::architecture Arch = simd::scalar>
151 return rank<Arch>(last) + ((
words_[last >> 6] >> (last & 63)) & 1);
155 template <simd::architecture Arch = simd::scalar>
156 std::uint64_t
rank(std::uint64_t position)
const {
157 if (position >=
bit_count_)
throw std::out_of_range(
"rank position");
158 auto block =
blocks_[position >> 11];
159 unsigned run = unsigned((position >> 9) & 3);
160 std::uint64_t result =
supers_[position >> 32] + block.before;
162 auto word = (position >> 9) << 3;
163 auto bits = unsigned(position & 511);
164 if (!bits)
return result;
165 if constexpr (!std::same_as<Arch, simd::scalar>)
166 if (
words_.size() - word >= 8)
167 return result + rank_detail::prefix512<Arch>(
words_.data() + word, bits);
179 template <simd::architecture Arch = simd::scalar>
181 if (bits > std::numeric_limits<std::uint64_t>::max() - 0xffffffffu ||
182 source.size() != ((bits + 63) >> 6))
183 throw std::invalid_argument(
"rank source length");
186 result.
words.assign(source.begin(), source.end());
187 if (bits & 63) result.
words.back() &= (std::uint64_t{1} << (bits & 63)) - 1;
188 result.
blocks.resize((bits + 2047) >> 11);
190 std::uint64_t total = 0;
191 auto begin_block = [&](std::uint64_t block) ->
rank_block & {
192 auto & entry = result.
blocks[block];
193 entry.before = cursor.
before(block, total);
197 auto full_blocks = bits >> 11;
198 for (std::uint64_t block = 0; block < full_blocks; ++block) {
199 auto & entry = begin_block(block);
200 auto words = result.
words.data() + block * 32;
201 auto a = rank_detail::popcount512<Arch>(
words);
202 auto b = rank_detail::popcount512<Arch>(
words + 8);
203 auto c = rank_detail::popcount512<Arch>(
words + 16);
204 auto d = rank_detail::popcount512<Arch>(
words + 24);
205 entry.runs = a | (b << 11) | (c << 22);
206 total += a + b + c + d;
208 if (full_blocks != result.
blocks.size()) {
209 auto & entry = begin_block(full_blocks);
210 unsigned counts[4]{};
211 auto first = full_blocks * 32;
214 for (
auto word = first; word < result.
words.size(); ++word)
215 counts[(word - first) >> 3] += unsigned(std::popcount(result.
words[word]));
216 entry.runs = counts[0] | (counts[1] << 11) | (counts[2] << 22);
217 total += counts[0] + counts[1] + counts[2] + counts[3];
Shares explicit SIMD architecture traits with Everett's kernels.
unsigned prefix512_portable(std::uint64_t const *words, unsigned bits) noexcept
Definition rank.h:40
unsigned popcount512_portable(std::uint64_t const *words) noexcept
Definition rank.h:80
auto prefix_vectors(std::uint64_t const *words, unsigned bits) noexcept
Definition rank.h:67
constexpr unsigned run_prefix(std::uint32_t packed, unsigned run) noexcept
Definition rank.h:104
auto prefix_vector(std::uint64_t const *words, unsigned bits) noexcept
Definition rank.h:52
unsigned popcount512(std::uint64_t const *words) noexcept
Definition rank.h:96
unsigned prefix512(std::uint64_t const *words, unsigned bits) noexcept
Definition rank.h:75
auto population_vectors(std::uint8_t const *bytes) noexcept
Definition rank.h:87
Definition active_engine.h:18
std::uint32_t runs
Definition rank.h:32
std::uint32_t before
Definition rank.h:31
std::uint32_t before(std::uint64_t block, std::uint64_t total)
Definition rank.h:115
static constexpr bool starts_epoch(std::uint64_t block) noexcept
Definition rank.h:112
std::uint64_t epoch_base
Definition rank.h:121
static rank_index build(std::span< std::uint64_t const > source, std::uint64_t bits)
Definition rank.h:180
std::vector< rank_block > blocks
Definition rank.h:229
std::vector< std::uint64_t > supers
Definition rank.h:230
rank_view view() const &
Definition rank.h:222
rank_view view() const &&=delete
std::vector< std::uint64_t > words
Definition rank.h:228
std::uint64_t bit_count
Definition rank.h:231
std::uint64_t rank(std::uint64_t position) const
Definition rank.h:156
std::span< rank_block const > blocks_
Definition rank.h:173
rank_view(std::span< std::uint64_t const > words, std::span< rank_block const > blocks, std::span< std::uint64_t const > supers, std::uint64_t bit_count)
Definition rank.h:132
std::uint64_t count() const
Definition rank.h:148
std::span< std::uint64_t const > words_
Definition rank.h:172
std::uint64_t size() const noexcept
Definition rank.h:145
std::span< std::uint64_t const > supers_
Definition rank.h:174
std::uint64_t bit_count_
Definition rank.h:175