3#include <pixie/permutations/detail/packed_bit_block.h>
15namespace pixie::permutations::detail {
30 std::size_t StorageBits = 2048,
31 std::size_t Width = std::numeric_limits<T>::digits,
32 class BitStorage = PackedBitBlock<StorageBits>>
33class PackedValueBlock {
34 static_assert(std::is_integral_v<T> && std::is_unsigned_v<T> &&
35 std::numeric_limits<T>::digits <= 64);
36 static_assert(Width > 0 && Width <= std::numeric_limits<T>::digits);
37 using Bits = BitStorage;
44 static constexpr std::size_t capacity = Bits::capacity / Width;
47 static constexpr std::size_t width = Width;
49 static constexpr std::size_t payload_offset_bytes =
50 Bits::payload_offset_bytes;
53 static constexpr std::size_t payload_alignment = Bits::payload_alignment;
54 static_assert(capacity > 0);
57 PackedValueBlock() noexcept {}
65 explicit PackedValueBlock(std::span<const T> values) {
66 if (values.size() > capacity) {
67 throw std::invalid_argument(
"PackedValueBlock: input size");
69 typename Bits::Payload words;
70 if constexpr (Width != 64) {
73 for (std::size_t i = 0; i < values.size(); ++i) {
74 if (
static_cast<std::uint64_t
>(values[i]) > field_max) {
75 throw std::invalid_argument(
"PackedValueBlock: value width");
77 if constexpr (Width == 64) {
80 encode(words, i, values[i]);
83 bits_ = Bits(words, values.size() * Width);
88 std::size_t size() const noexcept {
return bits_.size() / Width; }
92 bool empty() const noexcept {
return bits_.empty(); }
100 T operator[](std::size_t i)
const {
102 throw std::out_of_range(
"PackedValueBlock: index");
104 return read_unchecked(i);
110 T read_unchecked(std::size_t i)
const noexcept {
112 if constexpr (Width == 1) {
113 return static_cast<T
>(bits_[i]);
114 }
else if constexpr (Width == 64) {
115 return static_cast<T
>(bits_.read_normalized_word(i * 64));
117 return static_cast<T
>(bits_.read_bits(i * Width, Width));
125 void set_at(std::size_t i, T value)
noexcept {
126 assert(i < size() &&
static_cast<std::uint64_t
>(value) <= field_max);
127 if constexpr (Width == 64) {
128 bits_.write_aligned_word(i * Width, value);
130 bits_.write_bits(i * Width, Width, value);
141 void rotate_left(std::size_t left,
143 std::size_t distance)
noexcept {
144 assert(left <= right && right <= size());
145 if constexpr (Width == 1) {
146 bits_.rotate_left(left, right, distance);
147 }
else if (left != right) {
148 bits_.rotate_left(left * Width, right * Width,
149 (distance % (right - left)) * Width);
150 if constexpr (Width == 64) {
164 void insert_at(std::size_t offset, T value)
noexcept {
165 assert(offset <= size() && size() < capacity);
166 typename Bits::Payload words{};
167 const auto n = size();
168 if constexpr (Width == 64) {
169 for (std::size_t i = 0; i < n; ++i) {
170 words[i] = bits_.read_aligned_word(i * 64);
172 std::memmove(words.data() + offset + 1, words.data() + offset,
173 (n - offset) *
sizeof(std::uint64_t));
174 words[offset] =
static_cast<std::uint64_t
>(value);
176 for (std::size_t i = 0; i < offset; ++i) {
177 encode(words, i, bits_.read_bits(i * Width, Width));
179 encode(words, offset, value);
180 for (std::size_t i = offset; i < n; ++i) {
181 encode(words, i + 1, bits_.read_bits(i * Width, Width));
184 bits_ = Bits(words, (n + 1) * Width);
194 void redistribute(PackedValueBlock& rhs, std::size_t left_size)
noexcept {
195 assert(
this != &rhs && left_size <= capacity &&
196 left_size <= size() + rhs.size() &&
197 size() + rhs.size() - left_size <= capacity);
198 bits_.redistribute(rhs.bits_, left_size * Width);
207 void add_bias(std::uint64_t bias)
noexcept {
208 typename Bits::Payload words{};
209 const auto n = size();
210 for (std::size_t i = 0; i < n; ++i) {
211 const auto value = bits_.read_bits(i * Width, Width);
212 assert(bias <= field_max && value <= field_max - bias);
213 encode(words, i, value + bias);
215 bits_ = Bits(words, n * Width);
220 std::size_t payload_capacity_bytes() const noexcept {
221 return bits_.payload_capacity_bytes();
226 std::size_t metadata_bytes() const noexcept {
return bits_.metadata_bytes(); }
229 static constexpr std::uint64_t field_max =
230 std::numeric_limits<std::uint64_t>::max() >> (64 - Width);
231 static void encode(
typename Bits::Payload& words,
233 std::uint64_t value)
noexcept {
234 const auto bit = i * Width;
235 const auto shift = bit % 64;
236 words[bit / 64] |= value << shift;
237 if (Width > 64 - shift) {
238 words[bit / 64 + 1] |= value >> (64 - shift);