30 namespace rank_groups_detail {
31 template <
unsigned Bits,
unsigned Word,
unsigned Plane>
33 std::uint64_t mask = 0;
35 if ((Word * 64 +
bit) % Bits == Plane) mask |= std::uint64_t{1} <<
bit;
42 word_view words,
unsigned count)
noexcept {
43 static_assert(Bits == 2 || Bits == 3 || Bits == 5);
44 constexpr auto masks = [] {
45 std::array<std::array<std::uint64_t, Bits>, Bits> result{};
46 for (
unsigned word = 0; word < Bits; ++word)
48 result[word][(word * 64 +
bit) % Bits] |= std::uint64_t{1} <<
bit;
52 std::uint64_t pairs = 0;
53 auto bits = count * Bits;
54 for (
unsigned word = 0; word * 64 < bits; ++word) {
55 auto value = words[word];
56 auto remaining = bits - word * 64;
57 if (remaining < 64) value &= (std::uint64_t{1} << remaining) - 1;
58 if constexpr (Bits == 2) {
59 value = (value & 0x3333333333333333ull) + ((value >> 2) & 0x3333333333333333ull);
60 pairs += (value & 0x0f0f0f0f0f0f0f0full) + ((value >> 4) & 0x0f0f0f0f0f0f0f0full);
62 for (
unsigned plane = 0; plane < Bits; ++plane)
63 result +=
unsigned(std::popcount(value & masks[word % Bits][plane])) << plane;
66 if constexpr (Bits == 2) {
69 pairs = (pairs & 0x00ff00ff00ff00ffull) + ((pairs >> 8) & 0x00ff00ff00ff00ffull);
70 return unsigned((pairs * 0x0001000100010001ull) >> 48);
76 std::uint64_t
const * words,
unsigned count)
noexcept {
77 return prefix_portable<Bits>(
word_view(std::span(words, ((count * Bits + 63) >> 6))), count);
80 template <
unsigned Bits, simd::architecture Arch,
unsigned Vector,
unsigned Plane = 0>
81 simd_inline
auto weighted_bits(simd::vec<std::uint8_t, 16, Arch> value)
noexcept {
82 using B = simd::vec<std::uint8_t, 16, Arch>;
83 using W = simd::vec<std::uint64_t, 2, Arch>;
84 if constexpr (Bits == 2) {
85 auto pairs = (value & B(0x33)) + (value.template right<2>() & B(0x33));
86 return (pairs & B(15)) + pairs.template right<4>();
88 auto mask = W(plane_mask<Bits, 2 * Vector, Plane>(), plane_mask<Bits, 2 * Vector + 1, Plane>());
89 auto population = popcount(value & reinterpret_bits<std::uint8_t>(mask)).template left<Plane>();
90 if constexpr (Plane + 1 == Bits)
return population;
91 else return population + weighted_bits<Bits, Arch, Vector, Plane + 1>(value);
95 template <
unsigned Bits, simd::architecture Arch,
unsigned Vector = 0>
96 simd_inline
auto prefix_vectors(std::byte
const * bytes,
unsigned bits)
noexcept {
97 using B = simd::vec<std::uint8_t, 16, Arch>;
98 using W = simd::vec<std::uint64_t, 2, Arch>;
99 auto positions = W(std::uint64_t(2 * Vector), std::uint64_t(2 * Vector + 1));
100 auto boundary = W(bits >> 6);
101 auto mask = select(positions < boundary, W(~std::uint64_t{0}),
102 select(positions == boundary, W((std::uint64_t{1} << (bits & 63)) - 1), W(0)));
103 auto value = B::loadu(
reinterpret_cast<std::uint8_t
const *
>(bytes) + 16 * Vector) &
104 reinterpret_bits<std::uint8_t>(mask);
107 auto counts = pairwise_add_widened(weighted_bits<Bits, Arch, Vector>(value));
108 if constexpr (Vector + 1 == Bits)
return counts;
109 else return counts + prefix_vectors<Bits, Arch, Vector + 1>(bytes, bits);
112 template <
unsigned Bits, simd::architecture Arch = simd::scalar>
113 simd_inline
unsigned prefix(std::uint64_t
const * words,
unsigned count)
noexcept {
114 if constexpr (std::same_as<Arch, simd::neon> && std::endian::native == std::endian::little)
115 return unsigned(reduce_add_widened(prefix_vectors<Bits, Arch>(
116 reinterpret_cast<std::byte
const *
>(words), count * Bits)));
117 else return prefix_portable<Bits>(words, count);
128 static_assert(K >= 3 && K < std::numeric_limits<std::uint64_t>::max() && std::has_single_bit(K + 1),
129 "group size must be 2^n-1 and at least three");
131 static constexpr unsigned class_bits = unsigned(std::countr_zero(K + 1));
134 std::span<std::uint64_t const> checkpoints,
135 std::uint64_t virtual_count)
138 template <
class Words>
requires std::is_same_v<Words, word_view>
140 std::uint64_t virtual_count)
143 if (groups > (std::numeric_limits<std::uint64_t>::max() - 63) /
class_bits)
144 error_detail::raise<std::overflow_error>(
"rank groups packed size");
146 if (classes.size() != ((bits + 63) >> 6) ||
147 checkpoints.size() != ((groups + 127) >> 7))
148 error_detail::raise<std::invalid_argument>(
"invalid rank groups spans");
153 template <simd::architecture Arch = simd::scalar>
159 error_detail::raise<std::invalid_argument>(
"invalid rank groups final population");
167 std::uint64_t
class_at(std::uint64_t group)
const {
168 if (group >=
group_count()) error_detail::raise<std::out_of_range>(
"rank groups class");
172 template <simd::architecture Arch = simd::scalar>
173 std::uint64_t
rank(std::uint64_t group)
const {
174 if (group >=
group_count()) error_detail::raise<std::out_of_range>(
"rank groups boundary");
177 if constexpr (K == 3 || K == 7 || K == 31) {
178 auto count = unsigned(group & 127);
183 if constexpr (K != 3 && std::same_as<Arch, simd::neon> && std::endian::native == std::endian::little)
185 return add_prefix(result, reduce_add_widened(rank_groups_detail::prefix_vectors<class_bits, Arch>(
189 for (
auto i = group & ~std::uint64_t{127}; i < group; ++i) result =
add_prefix(result,
read_class(i), limit);
194 static std::uint64_t
add_prefix(std::uint64_t checkpoint, std::uint64_t prefix, std::uint64_t limit) {
195 if (checkpoint > limit || prefix > limit - checkpoint)
196 error_detail::raise<std::invalid_argument>(
"invalid rank groups checkpoint or prefix");
197 return checkpoint + prefix;
199 std::uint64_t
read_class(std::uint64_t group)
const noexcept {
201 auto word =
bit >> 6;
202 unsigned shift = unsigned(
bit & 63);
203 auto value =
classes_[word] >> shift;
217 std::span<std::uint64_t const> checkpoints,
218 std::uint64_t virtual_count)
219 : view_(classes, checkpoints, virtual_count) {}
221 template <
class Words>
requires std::is_same_v<Words, word_view>
223 std::uint64_t virtual_count)
224 : view_(classes, checkpoints, virtual_count) {}
229 std::uint64_t
size() const noexcept {
return view_.size(); }
230 template <simd::architecture Arch = simd::scalar>
231 std::uint64_t
count()
const {
return view_.template count<Arch>(); }
232 std::uint64_t
group_count() const noexcept {
return view_.group_count(); }
233 std::uint64_t
class_at(std::uint64_t group)
const {
return view_.class_at(group); }
234 template <simd::architecture Arch = simd::scalar>
235 std::uint64_t
rank(std::uint64_t group)
const {
return view_.template rank<Arch>(group); }
242 static_assert(K >= 3 && K < std::numeric_limits<std::uint64_t>::max() && std::has_single_bit(K + 1),
243 "group size must be 2^n-1 and at least three");
245 static constexpr unsigned class_bits = unsigned(std::countr_zero(K + 1));
248 auto groups = count / K + (count % K != 0);
249 if (source.size() != groups) error_detail::raise<std::invalid_argument>(
"rank groups class length");
250 if (groups > (std::numeric_limits<std::uint64_t>::max() - 63) /
class_bits)
251 error_detail::raise<std::overflow_error>(
"rank groups packed size");
255 result.
classes.resize((bits + 63) >> 6);
256 std::uint64_t total = 0;
257 for (std::uint64_t i = 0; i < groups; ++i) {
258 auto limit = i + 1 == groups && count % K ? count % K : K;
259 auto value = source[i];
260 if (value > limit) error_detail::raise<std::invalid_argument>(
"rank groups population");
261 if ((i & 127) == 0) result.
checkpoints.push_back(total);
263 unsigned shift = unsigned(
bit & 63);
291 : data_(std::move(other.data_)), groups_(other.groups_), population_(other.population_),
292 partial_(other.partial_), finished_(std::exchange(other.finished_,
true)) {}
294 if (
this != &other) {
295 data_ = std::move(other.data_); groups_ = other.groups_; population_ = other.population_;
296 partial_ = other.partial_; finished_ = std::exchange(other.finished_,
true);
301 std::uint64_t
size() const noexcept {
return data_.virtual_count; }
303 bool finished() const noexcept {
return finished_; }
305 void append(std::uint64_t population, std::uint64_t width = K) {
307 constexpr auto maximum = std::numeric_limits<std::uint64_t>::max();
308 if (partial_ || !width || width > K || population > width)
309 error_detail::raise<std::invalid_argument>(
"invalid incremental rank group");
310 if (width > maximum - data_.virtual_count || groups_ >= (maximum - 63) /
class_bits)
311 error_detail::raise<std::length_error>(
"incremental rank groups are too large");
313 auto words = (bits + 63) >> 6;
314 auto checkpoint = (groups_ & 127) == 0;
315 reserve(data_.classes, words);
316 if (checkpoint) reserve(data_.checkpoints, (groups_ >> 7) + 1);
318 data_.classes.resize(
static_cast<std::size_t
>(words), 0);
319 if (checkpoint) data_.checkpoints.push_back(population_);
321 auto shift = unsigned(
bit & 63);
322 data_.classes[
bit >> 6] |= population << shift;
323 if (shift +
class_bits > 64) data_.classes[(
bit >> 6) + 1] |= population >> (64 - shift);
324 data_.virtual_count += width;
325 population_ += population;
327 partial_ = width != K;
333 return std::move(data_);
338 std::uint64_t groups_ = 0, population_ = 0;
339 bool partial_ =
false, finished_ =
false;
342 if (finished_) error_detail::raise<std::logic_error>(
"incremental rank groups are finished");
344 static void reserve(std::vector<std::uint64_t> & data, std::uint64_t count) {
345 if (count <= data.capacity())
return;
346 if (count > data.max_size()) error_detail::raise<std::length_error>(
"incremental rank groups are too large");
347 auto capacity = data.capacity();
348 auto grown = capacity > (data.max_size() >> 1) ? data.max_size() : capacity << 1;
349 data.reserve(std::max(
static_cast<std::size_t
>(count), grown));
Outlines exceptional check failures while preserving their types and messages.
constexpr std::uint64_t plane_mask() noexcept
Definition rank_groups.h:32
unsigned prefix(std::uint64_t const *words, unsigned count) noexcept
Definition rank_groups.h:113
unsigned prefix_portable(word_view words, unsigned count) noexcept
Definition rank_groups.h:41
auto weighted_bits(simd::vec< std::uint8_t, 16, Arch > value) noexcept
Definition rank_groups.h:81
auto prefix_vectors(std::byte const *bytes, unsigned bits) noexcept
Definition rank_groups.h:96
Definition active_engine.h:18
Declares Everett's rank15 support.
Definition rank_groups.h:283
std::uint64_t group_count() const noexcept
Definition rank_groups.h:302
static void reserve(std::vector< std::uint64_t > &data, std::uint64_t count)
Definition rank_groups.h:344
rank_groups_builder()=default
rank_groups< K > data_
Definition rank_groups.h:337
rank_groups_builder & operator=(rank_groups_builder const &)=delete
rank_groups_builder & operator=(rank_groups_builder &&other) noexcept
Definition rank_groups.h:293
rank_groups< K > finish()
Definition rank_groups.h:330
rank_groups_builder(rank_groups_builder &&other) noexcept
Definition rank_groups.h:290
rank_groups_builder(rank_groups_builder const &)=delete
void require_active() const
Definition rank_groups.h:341
bool finished() const noexcept
Definition rank_groups.h:303
void append(std::uint64_t population, std::uint64_t width=K)
Definition rank_groups.h:305
std::uint64_t size() const noexcept
Definition rank_groups.h:301
rank15_view view_
Definition rank_groups.h:238
std::uint64_t size() const noexcept
Definition rank_groups.h:229
rank_groups_view(std::span< std::uint64_t const > classes, std::span< std::uint64_t const > checkpoints, std::uint64_t virtual_count)
Definition rank_groups.h:216
std::uint64_t class_at(std::uint64_t group) const
Definition rank_groups.h:233
word_view checkpoint_words() const noexcept
Definition rank_groups.h:227
std::uint64_t count() const
Definition rank_groups.h:231
rank_groups_view(Words classes, Words checkpoints, std::uint64_t virtual_count)
Definition rank_groups.h:222
word_view class_words() const noexcept
Definition rank_groups.h:226
std::uint64_t group_count() const noexcept
Definition rank_groups.h:232
std::uint64_t rank(std::uint64_t group) const
Definition rank_groups.h:235
Definition rank_groups.h:127
static std::uint64_t add_prefix(std::uint64_t checkpoint, std::uint64_t prefix, std::uint64_t limit)
Definition rank_groups.h:194
word_view checkpoint_words() const noexcept
Definition rank_groups.h:166
rank_groups_view(Words classes, Words checkpoints, std::uint64_t virtual_count)
Definition rank_groups.h:139
static constexpr unsigned class_bits
Definition rank_groups.h:131
std::uint64_t group_count() const noexcept
Definition rank_groups.h:162
std::uint64_t class_at(std::uint64_t group) const
Definition rank_groups.h:167
std::uint64_t read_class(std::uint64_t group) const noexcept
Definition rank_groups.h:199
rank_groups_view(std::span< std::uint64_t const > classes, std::span< std::uint64_t const > checkpoints, std::uint64_t virtual_count)
Definition rank_groups.h:133
static constexpr std::uint64_t group_size
Definition rank_groups.h:130
word_view classes_
Definition rank_groups.h:207
std::uint64_t virtual_count_
Definition rank_groups.h:209
word_view checkpoints_
Definition rank_groups.h:208
std::uint64_t rank(std::uint64_t group) const
Definition rank_groups.h:173
std::uint64_t count() const
Definition rank_groups.h:154
std::uint64_t size() const noexcept
Definition rank_groups.h:151
word_view class_words() const noexcept
Definition rank_groups.h:165
Definition rank_groups.h:241
static rank_groups build(std::span< std::uint64_t const > source, std::uint64_t count)
Definition rank_groups.h:247
rank_groups_view< K > view() const &&=delete
static constexpr std::uint64_t group_size
Definition rank_groups.h:244
rank_groups_view< K > view() const &
Definition rank_groups.h:271
std::uint64_t virtual_count
Definition rank_groups.h:276
std::vector< std::uint64_t > checkpoints
Definition rank_groups.h:275
std::vector< std::uint64_t > classes
Definition rank_groups.h:274
static constexpr unsigned class_bits
Definition rank_groups.h:245
Definition word_view.h:31
word_view subspan(std::size_t offset, std::size_t count=std::dynamic_extent) const
Definition word_view.h:45
std::size_t size() const noexcept
Definition word_view.h:41
std::span< std::byte const > bytes() const noexcept
Definition word_view.h:43