Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_persistent_vector.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 https://github.com/lrleon/Aleph-w
6
7 This file is part of Aleph-w library
8
9 Copyright (c) 2002-2026 Leandro Rabindranath Leon
10
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be included in all
19 copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 SOFTWARE.
28*/
29
46#ifndef TPL_PERSISTENT_VECTOR_H
47#define TPL_PERSISTENT_VECTOR_H
48
49#include <array>
50#include <concepts>
51#include <cstddef>
52#include <limits>
53#include <memory>
54#include <type_traits>
55#include <utility>
56
57#include <ah-errors.H>
58#include <tpl_array.H>
59
60namespace Aleph
61{
62
93template <typename T>
95{
96 static constexpr size_t branch_bits = 5;
97 static constexpr size_t branch_factor = size_t{1} << branch_bits;
98 static constexpr size_t branch_mask = branch_factor - 1;
99
100 using ValuePtr = std::shared_ptr<const T>;
101
102 struct Node
103 {
104 explicit Node(const bool leaf = true) noexcept : is_leaf(leaf) {}
105
106 bool is_leaf = true;
107 std::array<std::shared_ptr<const Node>, branch_factor> children{};
108 std::array<ValuePtr, branch_factor> values{};
109 };
110
111 using NodePtr = std::shared_ptr<const Node>;
112
114 size_t size_ = 0;
115 size_t shift_ = 0; // 0 means root is a leaf; otherwise high child shift.
116
117 [[nodiscard]] static size_t child_index(const size_t index, const size_t shift) noexcept
118 {
119 return (index >> shift) & branch_mask;
120 }
121
122 [[nodiscard]] static size_t capacity_for_shift(const size_t shift) noexcept
123 {
124 constexpr size_t digits = std::numeric_limits<size_t>::digits;
125 if (shift >= digits - branch_bits)
126 return std::numeric_limits<size_t>::max();
127 return size_t{1} << (shift + branch_bits);
128 }
129
130 [[nodiscard]] static NodePtr set_rec(const NodePtr &node,
131 const size_t shift,
132 const size_t index,
134 {
135 auto copy = node == nullptr ? std::make_shared<Node>(shift == 0)
136 : std::make_shared<Node>(*node);
137 copy->is_leaf = shift == 0;
138
139 if (shift == 0)
140 {
141 copy->values[child_index(index, 0)] = std::move(value);
142 return copy;
143 }
144
145 const size_t slot = child_index(index, shift);
146 copy->children[slot] = set_rec(copy->children[slot], shift - branch_bits,
147 index, std::move(value));
148 return copy;
149 }
150
151 [[nodiscard]] static bool leaf_has_values(const std::shared_ptr<Node> &node) noexcept
152 {
153 for (const auto &value : node->values)
154 if (value != nullptr)
155 return true;
156 return false;
157 }
158
159 [[nodiscard]] static bool internal_has_children(const std::shared_ptr<Node> &node) noexcept
160 {
161 for (const auto &child : node->children)
162 if (child != nullptr)
163 return true;
164 return false;
165 }
166
167 [[nodiscard]] static NodePtr clear_rec(const NodePtr &node,
168 const size_t shift,
169 const size_t index)
170 {
171 if (node == nullptr)
172 return nullptr;
173
174 auto copy = std::make_shared<Node>(*node);
175 copy->is_leaf = shift == 0;
176
177 if (shift == 0)
178 {
179 copy->values[child_index(index, 0)] = nullptr;
180 return leaf_has_values(copy) ? NodePtr(copy) : nullptr;
181 }
182
183 const size_t slot = child_index(index, shift);
184 copy->children[slot] = clear_rec(copy->children[slot], shift - branch_bits, index);
185 return internal_has_children(copy) ? NodePtr(copy) : nullptr;
186 }
187
188 [[nodiscard]] static const T *get_ptr(const NodePtr &root,
189 size_t shift,
190 const size_t index) noexcept
191 {
192 const Node *curr = root.get();
193 while (curr != nullptr and shift > 0)
194 {
195 curr = curr->children[child_index(index, shift)].get();
196 shift -= branch_bits;
197 }
198 if (curr == nullptr)
199 return nullptr;
200 const ValuePtr &value = curr->values[child_index(index, 0)];
201 return value == nullptr ? nullptr : value.get();
202 }
203
204 [[nodiscard]] static size_t count_values_rec(const NodePtr &node,
205 const size_t shift,
206 bool &ok) noexcept
207 {
208 if (node == nullptr)
209 return 0;
210 if (node->is_leaf != (shift == 0))
211 {
212 ok = false;
213 return 0;
214 }
215
216 size_t count = 0;
217 if (shift == 0)
218 {
219 for (const auto &child : node->children)
220 if (child != nullptr)
221 ok = false;
222 for (const auto &value : node->values)
223 if (value != nullptr)
224 ++count;
225 return count;
226 }
227
228 for (const auto &value : node->values)
229 if (value != nullptr)
230 ok = false;
231 for (const auto &child : node->children)
232 count += count_values_rec(child, shift - branch_bits, ok);
233 return count;
234 }
235
236 explicit PersistentVector(NodePtr root, const size_t n, const size_t shift) noexcept
237 : root_(std::move(root)), size_(n), shift_(shift)
238 {
239 // Empty.
240 }
241
242public:
247
253 {
254 return branch_factor;
255 }
256
261 [[nodiscard]] bool is_empty() const noexcept { return size_ == 0; }
262
267 [[nodiscard]] size_t size() const noexcept { return size_; }
268
274 [[nodiscard]] const T &get(const size_t index) const
275 {
277 << "PersistentVector::get(): index out of range";
278 const T *value = get_ptr(root_, shift_, index);
279 ah_logic_error_if(value == nullptr)
280 << "PersistentVector::get(): missing value inside vector trie";
281 return *value;
282 }
283
289 [[nodiscard]] const T &operator [] (const size_t index) const
290 {
291 return get(index);
292 }
293
301 {
302 return push_back(ValuePtr(std::make_shared<const T>(value)));
303 }
304
312 {
313 return push_back(ValuePtr(std::make_shared<const T>(std::move(value))));
314 }
315
323 [[nodiscard]] PersistentVector set(const size_t index, const T &value) const
324 {
326 << "PersistentVector::set(): index out of range";
327 return set(index, ValuePtr(std::make_shared<const T>(value)));
328 }
329
337 [[nodiscard]] PersistentVector set(const size_t index, T &&value) const
338 {
340 << "PersistentVector::set(): index out of range";
341 return set(index, ValuePtr(std::make_shared<const T>(std::move(value))));
342 }
343
350 {
352 << "PersistentVector::pop_back(): empty vector";
353
354 if (size_ == 1)
355 return PersistentVector();
356
357 const size_t new_size = size_ - 1;
359 size_t shift = shift_;
360
361 while (shift > 0 and new_size <= capacity_for_shift(shift - branch_bits))
362 {
363 root = root == nullptr ? nullptr : root->children[0];
364 shift -= branch_bits;
365 }
366
367 return PersistentVector(std::move(root), new_size, shift);
368 }
369
378 {
381 for (size_t i = 0; i < size_; ++i)
382 {
383 const T &value = get(i);
384 out.append(value);
385 }
386 return out;
387 }
388
395 {
396 if (size_ == 0)
397 return root_ == nullptr and shift_ == 0;
398 if (root_ == nullptr)
399 return false;
401 return false;
402
403 bool ok = true;
404 if (const size_t count = count_values_rec(root_, shift_, ok); not ok or count != size_)
405 return false;
406 for (size_t i = 0; i < size_; ++i)
407 if (get_ptr(root_, shift_, i) == nullptr)
408 return false;
409 return true;
410 }
411
412private:
414 {
415 ah_length_error_if(size_ == std::numeric_limits<size_t>::max())
416 << "PersistentVector::push_back(): size_t capacity exhausted";
417
418 if (root_ == nullptr)
419 return PersistentVector(set_rec(nullptr, 0, 0, std::move(value)), 1, 0);
420
422 size_t shift = shift_;
424 {
425 ah_length_error_if(shift_ >= std::numeric_limits<size_t>::digits - branch_bits)
426 << "PersistentVector::push_back(): trie depth exhausted";
427 auto grown = std::make_shared<Node>(false);
428 grown->children[0] = root_;
429 root = grown;
430 shift += branch_bits;
431 }
432
433 root = set_rec(root, shift, size_, std::move(value));
434 return PersistentVector(std::move(root), size_ + 1, shift);
435 }
436
437 [[nodiscard]] PersistentVector set(const size_t index, ValuePtr value) const
438 {
440 << "PersistentVector::set(): index out of range";
441 return PersistentVector(set_rec(root_, shift_, index, std::move(value)),
442 size_, shift_);
443 }
444};
445
446} // namespace Aleph
447
448#endif // TPL_PERSISTENT_VECTOR_H
Exception handling system with formatted messages for Aleph-w.
#define ah_length_error_if(C)
Throws std::length_error if condition holds.
Definition ah-errors.H:703
#define ah_out_of_range_error_if(C)
Throws std::out_of_range if condition holds.
Definition ah-errors.H:584
#define ah_underflow_error_if(C)
Throws std::underflow_error if condition holds.
Definition ah-errors.H:373
#define ah_logic_error_if(C)
Throws std::logic_error if condition holds.
Definition ah-errors.H:330
size_t size_t int32_t value
Definition ca-c-api.h:116
size_t size_t int32_t * out
Definition ca-c-api.h:120
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
void reserve(size_t cap)
Reserves cap cells into the array.
Definition tpl_array.H:320
Immutable vector backed by a 32-way bitmapped trie.
static constexpr size_t branch_bits
static NodePtr set_rec(const NodePtr &node, const size_t shift, const size_t index, ValuePtr value)
PersistentVector push_back(ValuePtr value) const
static bool leaf_has_values(const std::shared_ptr< Node > &node) noexcept
Array< T > to_array() const
Return all values in an Aleph array.
PersistentVector push_back(T &&value) const
Return a new version with value appended by move.
std::shared_ptr< const T > ValuePtr
PersistentVector push_back(const T &value) const
Return a new version with value appended by copy.
PersistentVector set(const size_t index, ValuePtr value) const
const T & get(const size_t index) const
Read a value by index.
static size_t count_values_rec(const NodePtr &node, const size_t shift, bool &ok) noexcept
static constexpr size_t branch_factor
static NodePtr clear_rec(const NodePtr &node, const size_t shift, const size_t index)
bool verify() const noexcept
Verify trie shape and logical prefix invariants.
static constexpr size_t branch_mask
static const T * get_ptr(const NodePtr &root, size_t shift, const size_t index) noexcept
PersistentVector() noexcept=default
Construct an empty persistent vector.
static size_t child_index(const size_t index, const size_t shift) noexcept
PersistentVector(NodePtr root, const size_t n, const size_t shift) noexcept
PersistentVector set(const size_t index, const T &value) const
Return a new version with one index replaced by copy.
static constexpr size_t branching_factor() noexcept
Return the trie branching factor.
size_t size() const noexcept
Return the number of values in this version.
bool is_empty() const noexcept
Return true when the vector has no values.
std::shared_ptr< const Node > NodePtr
static bool internal_has_children(const std::shared_ptr< Node > &node) noexcept
static size_t capacity_for_shift(const size_t shift) noexcept
const T & operator[](const size_t index) const
Read a value by index.
PersistentVector set(const size_t index, T &&value) const
Return a new version with one index replaced by move.
PersistentVector pop_back() const
Return a new version without the last value.
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4071
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Itor2 copy(Itor1 sourceBeg, const Itor1 &sourceEnd, Itor2 destBeg)
Copy elements from one range to another.
Definition ahAlgo.H:584
and
Check uniqueness with explicit hash + equality functors.
std::decay_t< typename HeadC::Item_Type > T
Definition ah-zip.H:105
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
STL namespace.
Node(const bool leaf=true) noexcept
std::array< std::shared_ptr< const Node >, branch_factor > children
std::array< ValuePtr, branch_factor > values
Dynamic array container with automatic resizing.