35 using node_index_t = size_t;
36 static constexpr node_index_t npos = std::numeric_limits<node_index_t>::max();
37 static constexpr std::array<std::uint8_t, 8> kSerializationMagic = {
38 'P',
'X',
'W',
'A',
'V',
'E',
'T',
'\0'};
39 static constexpr std::uint32_t kSerializationVersion = 5;
40 static constexpr std::size_t kSerializationHeaderBytes = 24;
42 struct PreWaveletNode {
43 node_index_t parent = npos;
44 node_index_t left_child = npos;
45 node_index_t right_child = npos;
48 explicit PreWaveletNode(std::size_t middle) : middle(middle) {}
60 node_index_t parent, left_child, right_child;
62 Storage bit_vector_data;
65 WaveletNode() =
default;
67 WaveletNode(
const WaveletNode& node)
68 : parent(node.parent),
69 left_child(node.left_child),
70 right_child(node.right_child),
72 bit_vector_data(node.bit_vector_data),
74 if constexpr (std::same_as<Storage, AlignedStorage>) {
82 WaveletNode& operator=(
const WaveletNode& node) {
84 WaveletNode copy(node);
85 *
this = std::move(copy);
90 WaveletNode(WaveletNode&&)
noexcept =
default;
91 WaveletNode& operator=(WaveletNode&&)
noexcept =
default;
93 WaveletNode(PreWaveletNode&& node)
94 requires(std::same_as<Storage, AlignedStorage>)
95 : parent(node.parent),
96 left_child(node.left_child),
97 right_child(node.right_child),
99 const std::size_t bit_count = node.stream.size_bits();
100 const std::vector<std::uint64_t> words = node.stream.take_words();
101 bit_vector_data =
AlignedStorage(std::span<const std::uint64_t>(words));
112 bit_vector_data.serialize(writer);
124 result.bit_vector_data = Storage::deserialize(reader);
126 reader, result.bit_vector_data.as_words64(), validation);
131 size_t alphabet_size_ = 0;
132 size_t data_size_ = 0;
133 node_index_t root_ = npos;
134 std::vector<WaveletNode> nodes_;
135 std::vector<node_index_t> leaves_;
136 std::vector<size_t> permutation_, inverse_permutation_;
138 void validate_deserialized_topology(
142 std::ranges::any_of(leaves_,
143 [](node_index_t leaf) {
return leaf != npos; })) {
144 throw std::invalid_argument(
145 "Invalid serialized empty wavelet-tree leaves");
149 if (nodes_[root_].parent != npos) {
150 throw std::invalid_argument(
"Serialized wavelet-tree root has a parent");
153 std::vector<std::uint8_t> incoming_edges(nodes_.size());
154 for (node_index_t parent = 0; parent < nodes_.size(); ++parent) {
155 const WaveletNode& node = nodes_[parent];
156 for (
const node_index_t child : {node.left_child, node.right_child}) {
160 if (nodes_[child].parent != parent) {
161 throw std::invalid_argument(
162 "Serialized wavelet-tree parent/child links disagree");
164 if (incoming_edges[child] != 0) {
165 throw std::invalid_argument(
166 "Serialized wavelet-tree node has multiple parents");
168 ++incoming_edges[child];
172 for (node_index_t node = 0; node < nodes_.size(); ++node) {
173 const std::size_t expected_edges = node == root_ ? 0 : 1;
174 if (incoming_edges[node] != expected_edges) {
175 throw std::invalid_argument(
176 "Serialized wavelet-tree node is detached from its parent");
182 std::size_t symbol_begin;
183 std::size_t symbol_end;
185 std::vector<bool> reached(nodes_.size());
186 std::vector<PendingNode> pending = {{root_, 0, alphabet_size_}};
187 while (!pending.empty()) {
188 const PendingNode current = pending.back();
190 const node_index_t node = current.node;
191 reached[node] =
true;
192 const WaveletNode& metadata = nodes_[node];
193 if (metadata.middle <= current.symbol_begin ||
194 metadata.middle >= current.symbol_end) {
195 throw std::invalid_argument(
196 "Serialized wavelet-tree split is outside its symbol range");
199 const std::size_t one_count =
201 ? metadata.data.
rank(metadata.data.
size())
203 const std::size_t zero_count =
205 ? metadata.data.
size() - one_count
207 const auto validate_branch = [&](node_index_t child,
208 std::size_t symbol_begin,
209 std::size_t symbol_end,
210 std::size_t expected_size) {
213 nodes_[child].data.size() != expected_size) {
214 throw std::invalid_argument(
215 "Serialized wavelet-tree child has the wrong length");
217 pending.push_back({child, symbol_begin, symbol_end});
221 for (std::size_t symbol = symbol_begin; symbol < symbol_end;
223 if (leaves_[symbol] != node) {
224 throw std::invalid_argument(
225 "Serialized wavelet-tree leaf map disagrees with topology");
230 validate_branch(metadata.left_child, current.symbol_begin,
231 metadata.middle, zero_count);
232 validate_branch(metadata.right_child, metadata.middle, current.symbol_end,
235 if (std::ranges::find(reached,
false) != reached.end()) {
236 throw std::invalid_argument(
237 "Serialized wavelet-tree contains unreachable nodes");
256 template <
typename F>
257 node_index_t build_node(
size_t begin,
261 std::span<const size_t> prefix_sum,
262 std::vector<PreWaveletNode>& nodes)
263 requires(std::same_as<Storage, AlignedStorage>)
265 if (end - begin == 1) {
266 leaves_[begin] = parent;
269 if (prefix_sum[end] == prefix_sum[begin]) {
270 for (
size_t symbol = begin; symbol < end; symbol++) {
271 leaves_[symbol] = parent;
276 node_index_t result = nodes.size();
277 size_t middle = get_middle(result);
278 middle = begin + (middle == npos ? (end - begin) / 2 : middle);
280 nodes.emplace_back(middle);
281 nodes[result].stream.reserve_bits(prefix_sum[end] - prefix_sum[begin]);
282 nodes[result].parent = parent;
283 nodes[result].left_child =
284 build_node(begin, middle, result, get_middle, prefix_sum, nodes);
285 nodes[result].right_child =
286 build_node(middle, end, result, get_middle, prefix_sum, nodes);
306 void copy_segment_content(node_index_t node,
309 std::span<Symbol> dst,
310 std::span<Symbol> tmp)
const {
314 const size_t rank = nodes_[node].data.rank(begin), rank0 = begin -
rank;
315 const size_t right = nodes_[node].data.rank(end) -
rank,
316 left = (end - begin) - right;
318 if (nodes_[node].left_child == npos) {
320 tmp.begin(),
static_cast<long long>(left),
321 static_cast<Symbol
>(inverse_permutation_[nodes_[node].middle - 1]));
323 copy_segment_content(nodes_[node].left_child, rank0, rank0 + left,
324 tmp.subspan(0, left), dst.subspan(0, left));
326 if (nodes_[node].right_child == npos) {
327 std::fill(tmp.begin() +
static_cast<long long>(left), tmp.end(),
328 static_cast<Symbol
>(inverse_permutation_[nodes_[node].middle]));
330 copy_segment_content(nodes_[node].right_child,
rank,
rank + right,
331 tmp.subspan(left, right), dst.subspan(left, right));
334 size_t j = 0, k = left;
335 const auto& bit_vector = nodes_[node].bit_vector_data.as_words64();
336 for (
size_t i = begin; i < end; i++) {
337 if ((bit_vector[i / 64] >> (i % 64)) & 1) {
338 dst[i - begin] = tmp[k++];
340 dst[i - begin] = tmp[j++];
345 static void validate_alphabet_size(std::size_t alphabet_size) {
346 if (alphabet_size != 0 &&
348 static_cast<std::size_t
>(std::numeric_limits<Symbol>::max())) {
349 throw std::invalid_argument(
350 "Wavelet-tree alphabet does not fit its symbol type");
354 static std::size_t checked_symbol_index(Symbol symbol,
355 std::size_t alphabet_size) {
356 const std::size_t index =
static_cast<std::size_t
>(symbol);
357 if (index >= alphabet_size) {
358 throw std::invalid_argument(
359 "Wavelet-tree symbol is outside the alphabet");
364 template <
class ForEachSymbol>
365 void build_from_counts(std::size_t alphabet_size,
366 std::span<const std::size_t> symbol_counts,
367 ForEachSymbol&& for_each_symbol,
369 requires(std::same_as<Storage, AlignedStorage>)
371 validate_alphabet_size(alphabet_size);
372 if (symbol_counts.size() != alphabet_size) {
373 throw std::invalid_argument(
374 "Wavelet-tree symbol counts must match the alphabet size");
376 alphabet_size_ = alphabet_size;
377 for (
const std::size_t count : symbol_counts) {
378 if (count > std::numeric_limits<std::size_t>::max() - data_size_) {
379 throw std::length_error(
"Wavelet-tree input is too large");
383 leaves_.assign(alphabet_size_, npos);
385 std::vector<PreWaveletNode> nodes;
386 std::vector<std::size_t> nodes_structure;
387 if (alphabet_size_ != 0) {
388 nodes.reserve(alphabet_size_);
389 nodes_structure.reserve(alphabet_size_);
391 if (build_type == WaveletTreeBuildType::Standard) {
392 permutation_.resize(alphabet_size_);
393 inverse_permutation_.resize(alphabet_size_);
394 std::iota(permutation_.begin(), permutation_.end(), 0);
395 std::iota(inverse_permutation_.begin(), inverse_permutation_.end(), 0);
396 nodes_structure.resize(alphabet_size_, npos);
403 std::vector<HuffmanNode> huffman_nodes(alphabet_size_, {0, 0, 0});
404 for (std::size_t symbol = 0; symbol < alphabet_size_; ++symbol) {
405 huffman_nodes[symbol].size = symbol_counts[symbol];
408 using QueueElement = std::pair<std::size_t, std::size_t>;
409 std::priority_queue<QueueElement, std::vector<QueueElement>,
412 for (std::size_t symbol = 0; symbol < alphabet_size_; ++symbol) {
413 queue.emplace(huffman_nodes[symbol].
size, symbol);
415 while (queue.size() >= 2) {
416 const std::size_t right = queue.top().second;
418 const std::size_t left = queue.top().second;
420 huffman_nodes.push_back(
421 {huffman_nodes[left].size + huffman_nodes[right].size, left + 1,
423 queue.emplace(huffman_nodes.back().size, huffman_nodes.size() - 1);
426 std::function<std::size_t(std::size_t)> enumerate =
427 [&](std::size_t index) -> std::size_t {
428 const auto& [
size, left, right] = huffman_nodes[index];
429 if (left == 0 || right == 0) {
430 permutation_[index] = inverse_permutation_.size();
431 inverse_permutation_.push_back(index);
434 const std::size_t node = nodes_structure.size();
435 std::size_t subtree = 0;
437 nodes_structure.push_back(0);
439 subtree += enumerate(left - 1);
441 nodes_structure[node] = subtree;
443 subtree += enumerate(right - 1);
447 permutation_.resize(alphabet_size_);
448 inverse_permutation_.reserve(alphabet_size_);
449 enumerate(huffman_nodes.size() - 1);
452 std::vector<std::size_t> prefix_sum(alphabet_size_ + 1);
453 for (std::size_t symbol = 0; symbol < alphabet_size_; ++symbol) {
454 prefix_sum[permutation_[symbol] + 1] = symbol_counts[symbol];
456 std::partial_sum(prefix_sum.begin(), prefix_sum.end(),
459 0, alphabet_size_, npos,
460 [&](node_index_t node) {
return nodes_structure[node]; }, prefix_sum,
464 std::vector<std::size_t> actual_counts(alphabet_size_);
465 for_each_symbol([&](Symbol symbol) {
466 const std::size_t original = checked_symbol_index(symbol, alphabet_size_);
467 if (actual_counts[original] == std::numeric_limits<std::size_t>::max()) {
468 throw std::length_error(
"Wavelet-tree symbol count is too large");
470 ++actual_counts[original];
471 const std::size_t permuted = permutation_[original];
472 for (node_index_t current = root_; current != npos;) {
473 auto& node = nodes[current];
474 const bool go_right = permuted >= node.middle;
475 node.stream.write_bit(go_right);
476 current = go_right ? node.right_child : node.left_child;
479 if (!std::ranges::equal(actual_counts, symbol_counts)) {
480 throw std::invalid_argument(
481 "Wavelet-tree emitted symbols do not match their counts");
484 nodes_.reserve(nodes.size());
485 for (
auto& node : nodes) {
486 nodes_.emplace_back(std::move(node));
490 WaveletTreeIndex() =
default;
493 using symbol_type = Symbol;
503 std::size_t alphabet_size,
504 std::span<const Symbol> data,
506 requires(std::same_as<Storage, AlignedStorage>)
508 validate_alphabet_size(alphabet_size);
509 std::vector<std::size_t> counts(alphabet_size);
510 for (
const Symbol symbol : data) {
511 ++counts[checked_symbol_index(symbol, alphabet_size)];
514 alphabet_size, counts,
516 for (
const Symbol symbol : data) {
535 template <
class ForEachSymbol>
537 std::size_t alphabet_size,
538 std::span<const std::size_t> symbol_counts,
539 ForEachSymbol&& for_each_symbol,
541 requires(std::same_as<Storage, AlignedStorage>)
543 build_from_counts(alphabet_size, symbol_counts,
544 std::forward<ForEachSymbol>(for_each_symbol), build_type);
556 std::size_t symbol_index =
static_cast<std::size_t
>(symbol);
557 if (symbol_index >= alphabet_size_) [[unlikely]] {
560 symbol_index = permutation_[symbol_index];
561 for (node_index_t current = root_; current != npos;) {
562 const WaveletNode& node = nodes_[current];
563 if (symbol_index < node.middle) {
564 pos = node.data.
rank0(pos);
565 current = node.left_child;
567 pos = node.data.
rank(pos);
568 current = node.right_child;
583 std::size_t symbol_index =
static_cast<std::size_t
>(symbol);
584 if (symbol_index >= alphabet_size_ || data_size_ == 0) [[unlikely]] {
587 symbol_index = permutation_[symbol_index];
588 node_index_t current = leaves_[symbol_index];
589 for (; current != npos; current = nodes_[current].parent) {
590 const WaveletNode& node = nodes_[current];
591 if (symbol_index < node.middle) {
615 if (alphabet_size_ == 0 || data_size_ == 0 || begin >= end) [[unlikely]] {
618 const std::size_t length = end - begin;
619 if (root_ == npos) [[unlikely]] {
620 return std::vector<Symbol>(
621 length,
static_cast<Symbol
>(inverse_permutation_.front()));
623 if (length > std::vector<Symbol>().max_size() / 2) {
624 throw std::length_error(
"Wavelet-tree segment is too large");
626 std::vector<Symbol> result(2 * length);
627 copy_segment_content(root_, begin, end, std::span(result).first(length),
628 std::span(result).subspan(length));
629 result.resize(length);
646 if (writer.
size_bytes() %
alignof(std::uint64_t) != 0) {
647 throw std::invalid_argument(
648 "Wavelet-tree serialization requires an aligned writer offset");
650 const std::size_t artifact_begin = writer.
size_bytes();
651 detail::write_magic(writer, kSerializationMagic);
653 writer.
write_u32(std::numeric_limits<Symbol>::digits);
660 for (
const WaveletNode& node : nodes_) {
661 node.serialize(writer);
663 for (
const node_index_t leaf : leaves_) {
666 for (
const size_t idx : permutation_) {
670 const std::size_t unpadded_size = writer.
size_bytes() - artifact_begin;
672 (
sizeof(std::uint64_t) - unpadded_size %
sizeof(std::uint64_t)) %
673 sizeof(std::uint64_t));
675 artifact_size_position,
676 static_cast<std::uint64_t
>(writer.
size_bytes() - artifact_begin));
699 requires(std::same_as<Storage, AlignedStorage> ||
700 std::same_as<Storage, ReadOnlyStorageView>)
703 if constexpr (std::same_as<Storage, ReadOnlyStorageView>) {
704 if (
reinterpret_cast<std::uintptr_t
>(candidate.
remaining_bytes().data()) %
705 alignof(std::uint64_t) !=
707 throw std::invalid_argument(
708 "Serialized wavelet-tree artifact is not word aligned");
711 const std::size_t available_size = candidate.
remaining();
712 detail::require_magic(candidate, kSerializationMagic);
713 if (candidate.
read_u32() != kSerializationVersion ||
714 candidate.
read_u32() != std::numeric_limits<Symbol>::digits) {
715 throw std::invalid_argument(
716 "Incompatible serialized wavelet-tree artifact");
718 const std::size_t artifact_size = detail::checked_artifact_size(
719 candidate.
read_u64(), kSerializationHeaderBytes, available_size);
721 candidate.
read_subreader(artifact_size - kSerializationHeaderBytes);
723 WaveletTreeIndex result;
724 result.alphabet_size_ = payload.
read_size();
725 result.validate_alphabet_size(result.alphabet_size_);
728 const std::size_t node_count = payload.
read_size();
729 const std::vector<WaveletNode> empty_nodes;
730 if (node_count > empty_nodes.max_size()) {
731 throw std::length_error(
732 "Serialized wavelet-tree node count is too large");
734 constexpr std::size_t kMinimumNodeBytes =
735 4 *
sizeof(std::uint64_t) +
sizeof(std::uint64_t);
736 if (node_count > payload.
remaining() / kMinimumNodeBytes) {
740 result.nodes_.resize(node_count);
741 for (
auto& node : result.nodes_) {
742 node = WaveletNode::deserialize(payload, validation);
744 const std::vector<node_index_t> empty_indices;
745 if (result.alphabet_size_ > empty_indices.max_size() ||
746 result.alphabet_size_ >
747 payload.
remaining() / (2 *
sizeof(std::uint64_t))) {
748 throw std::length_error(
"Serialized wavelet-tree alphabet is too large");
750 result.leaves_.resize(result.alphabet_size_);
751 for (node_index_t& leaf : result.leaves_) {
754 result.permutation_.resize(result.alphabet_size_);
755 for (
size_t& index : result.permutation_) {
758 result.inverse_permutation_.resize(result.alphabet_size_);
759 std::vector<bool> seen(result.alphabet_size_);
760 for (
size_t i = 0; i < result.alphabet_size_; i++) {
761 if (result.permutation_[i] >= result.alphabet_size_ ||
762 seen[result.permutation_[i]]) {
763 throw std::invalid_argument(
764 "Invalid serialized wavelet-tree permutation");
766 seen[result.permutation_[i]] =
true;
767 result.inverse_permutation_[result.permutation_[i]] = i;
769 const auto valid_node_index = [&result](node_index_t index) {
770 return index == npos || index < result.nodes_.size();
772 if (!valid_node_index(result.root_) ||
773 (result.nodes_.empty() != (result.root_ == npos))) {
774 throw std::invalid_argument(
"Invalid serialized wavelet-tree root");
776 for (
const node_index_t leaf : result.leaves_) {
777 if (!valid_node_index(leaf)) {
778 throw std::invalid_argument(
"Invalid serialized wavelet-tree leaf");
781 for (
const WaveletNode& node : result.nodes_) {
782 if (!valid_node_index(node.parent) ||
783 !valid_node_index(node.left_child) ||
784 !valid_node_index(node.right_child) ||
785 node.data.
size() > node.bit_vector_data.size_bits() ||
786 node.middle == 0 || node.middle >= result.alphabet_size_) {
787 throw std::invalid_argument(
"Invalid serialized wavelet-tree node");
790 result.validate_deserialized_topology(validation);
791 if (result.root_ != npos &&
792 result.nodes_[result.root_].data.size() != result.data_size_) {
793 throw std::invalid_argument(
794 "Serialized wavelet-tree root has the wrong length");