38#include <pixie/bits.h>
69template <
bool Permuted = true>
77 using Payload = std::array<std::uint64_t, 32>;
92 if (n > 2048 || (n + 63) / 64 > words.size()) {
93 throw std::invalid_argument(
"PermutedBitBlock: input size");
95 std::copy_n(words.begin(), (n + 63) / 64, bits_.begin());
102 std::size_t
size() const noexcept {
return n_; }
107 bool empty() const noexcept {
return n_ == 0; }
117 throw std::out_of_range(
"PermutedBitBlock: index");
119 const auto p = physical((h_ + i) % n_);
120 return (bits_[p / 64] >> (p % 64)) & 1;
130 copy(0, result, 0, n_);
146 std::size_t distance)
noexcept {
147 assert(left <= right && right <= n_);
148 const auto length = right - left;
149 if (length == 0 || (distance %= length) == 0) {
153 h_ = (h_ + distance) % n_;
156 if constexpr (Permuted) {
157 if (h_ == 0 && ((left | right | distance) % 128 == 0)) {
158 const auto shift = left / 128 * 4;
159 const auto width = length / 128 * 4;
160 const auto cut = distance / 128 * 4;
162 const auto mask = (std::uint64_t{1} << width) - 1;
163 const auto slice = (map_ >> shift) & mask;
164 const auto rotated = (slice >> cut) | (slice << (width - cut));
165 map_ = (map_ & ~(mask << shift)) | ((rotated & mask) << shift);
170 copy(left + distance, scratch, 0, length - distance);
171 copy(left, scratch, length - distance, distance);
172 const auto position = (h_ + left) % n_;
173 if constexpr (Permuted) {
174 if (map_ != identity) {
176 std::size_t source = 0;
177 while (source != length) {
178 const auto run = std::min({length - source, n_ - p, 128 - p % 128});
179 copy_packed_bits(scratch.data(), source, bits_.data(), physical(p),
182 p = (p + run == n_) ? 0 : p + run;
187 const auto prefix = std::min(length, n_ - position);
188 copy_packed_bits(scratch.data(), 0, bits_.data(), position, prefix);
189 copy_packed_bits(scratch.data(), prefix, bits_.data(), 0, length - prefix);
202 return sizeof(*this) -
sizeof(bits_);
215 assert(
this != &rhs);
216 const auto total = n_ + rhs.n_;
217 assert(left_size <=
capacity && left_size <= total &&
220 auto gather = [&](std::size_t start,
Payload& out, std::size_t count) {
221 const auto first = start < n_ ? std::min(count, n_ - start) : 0;
222 copy(start, out, 0, first);
223 rhs.copy(start + first - std::min(start + first, n_), out, first,
226 gather(0, a, left_size);
227 gather(left_size, b, total - left_size);
231 rhs.n_ = total - left_size;
233 if constexpr (Permuted) {
234 map_ = rhs.map_ = identity;
239 static constexpr std::uint64_t identity = 0xfedcba9876543210ULL;
242 std::size_t physical(std::size_t p)
const noexcept {
243 if constexpr (Permuted) {
244 return ((map_ >> (p / 128 * 4)) & 15) * 128 + p % 128;
249 void copy(std::size_t offset,
252 std::size_t count)
const noexcept {
256 auto p = (h_ + offset) % n_;
258 auto run = std::min(count, n_ - p);
259 if constexpr (Permuted) {
260 if (map_ != identity) {
261 run = std::min(run, 128 - p % 128);
264 copy_packed_bits(bits_.data(), physical(p), out.data(), dest, run);
267 p = (p + run == n_) ? 0 : p + run;
274 [[no_unique_address]] std::conditional_t<Permuted, std::uint64_t, NoMap>
275 map_ = []()
noexcept {
276 if constexpr (Permuted) {
void redistribute(PermutedBitBlock &rhs, std::size_t left_size) noexcept
Repartition two distinct blocks without allocation or exceptions.
Definition permuted_bit_block.h:214
bool operator[](std::size_t i) const
Read a zero-based logical bit by value.
Definition permuted_bit_block.h:115
bool value_type
Logical element type returned by value.
Definition permuted_bit_block.h:73
std::size_t metadata_bytes() const noexcept
Query object metadata size; there is no dynamic allocation.
Definition permuted_bit_block.h:201
static constexpr std::size_t capacity
Maximum logical element count.
Definition permuted_bit_block.h:75
std::size_t size() const noexcept
Query the logical length.
Definition permuted_bit_block.h:102
Payload flatten() const noexcept
Copy logical bits to linear words without mutating the block.
Definition permuted_bit_block.h:128
bool empty() const noexcept
Query whether the block is empty.
Definition permuted_bit_block.h:107
PermutedBitBlock() noexcept=default
Construct empty; all storage is initialized.
std::array< std::uint64_t, 32 > Payload
Fixed owning payload, in least-significant-bit-first word order.
Definition permuted_bit_block.h:77
void rotate_left(std::size_t left, std::size_t right, std::size_t distance) noexcept
Rotate [left, right) left by distance modulo its length.
Definition permuted_bit_block.h:144
std::size_t payload_capacity_bytes() const noexcept
Query fixed payload capacity.
Definition permuted_bit_block.h:196