This documentation is automatically generated by competitive-verifier/competitive-verifier
// competitive-verifier: STANDALONE
#include <cmath>
#include <iostream>
#include <iterator>
#include <random>
#include <ranges>
#include <utility>
#include <vector>
#include "tools/assert_that.hpp"
#include "tools/persistent_array.hpp"
int main() {
std::cin.tie(nullptr);
std::ios_base::sync_with_stdio(false);
{
const tools::persistent_array<int> a;
assert_that(a.empty());
assert_that(a.size() == 0);
}
assert_that(tools::persistent_array<int>::fully_released());
{
const tools::persistent_array<int> a{0};
assert_that(!a.empty());
assert_that(a.size() == 1);
assert_that(a[0] == 0);
}
assert_that(tools::persistent_array<int>::fully_released());
{
const tools::persistent_array<int> a{0, 1};
assert_that(!a.empty());
assert_that(a.size() == 2);
assert_that(a[0] == 0);
assert_that(a[1] == 1);
}
assert_that(tools::persistent_array<int>::fully_released());
{
const tools::persistent_array<int> a{0, 1, 2};
assert_that(!a.empty());
assert_that(a.size() == 3);
assert_that(a[0] == 0);
assert_that(a[1] == 1);
assert_that(a[2] == 2);
}
assert_that(tools::persistent_array<int>::fully_released());
for (int n = 0; n <= 40; ++n) {
{
const tools::persistent_array<int> a(n);
for (int i = 0; i < n; ++i) {
assert_that(a[i] == 0);
}
assert_that(static_cast<std::vector<int>>(a) == std::vector<int>(n, 0));
}
assert_that(tools::persistent_array<int>::fully_released());
{
const tools::persistent_array<int> a(n, -1234);
for (int i = 0; i < n; ++i) {
assert_that(a[i] == -1234);
}
assert_that(static_cast<std::vector<int>>(a) == std::vector<int>(n, -1234));
}
assert_that(tools::persistent_array<int>::fully_released());
{
const tools::persistent_array<int> a(std::views::iota(0, n));
for (int i = 0; i < n; ++i) {
assert_that(a[i] == i);
}
assert_that(static_cast<std::vector<int>>(a) == (std::views::iota(0, n) | std::ranges::to<std::vector>()));
}
assert_that(tools::persistent_array<int>::fully_released());
{
std::vector<tools::persistent_array<int>> arrays(n + 1);
arrays[0] = tools::persistent_array<int>(n, -1234);
for (int i = 1; i <= n; ++i) {
arrays[i] = arrays[i - 1].set(i - 1, i - 1);
}
for (int i = 0; i <= n; ++i) {
assert_that(static_cast<std::vector<int>>(arrays[i]) == (std::views::iota(0, n) | std::views::transform([&](const auto j) { return j < i ? j : -1234; }) | std::ranges::to<std::vector>()));
}
}
assert_that(tools::persistent_array<int>::fully_released());
{
std::vector<tools::persistent_array<int>> arrays(n + 1);
arrays[0] = tools::persistent_array<int>(std::views::repeat(-1234, n));
for (int i = 1; i <= n; ++i) {
arrays[i] = arrays[i - 1].set(i - 1, i - 1);
}
for (int i = 0; i <= n; ++i) {
assert_that(static_cast<std::vector<int>>(arrays[i]) == (std::views::iota(0, n) | std::views::transform([&](const auto j) { return j < i ? j : -1234; }) | std::ranges::to<std::vector>()));
}
}
assert_that(tools::persistent_array<int>::fully_released());
{
std::vector<tools::persistent_array<int>> arrays(2 * n + 1);
for (int i = 1; i <= n; ++i) {
arrays[i] = arrays[i - 1].push_back(i - 1);
}
for (int i = n + 1; i <= 2 * n; ++i) {
arrays[i] = arrays[i - 1].pop_back();
}
for (int i = 0; i <= 2 * n; ++i) {
assert_that(static_cast<std::vector<int>>(arrays[i]) == (std::views::iota(0, n - std::abs(n - i)) | std::ranges::to<std::vector>()));
}
}
assert_that(tools::persistent_array<int>::fully_released());
}
{
const tools::persistent_array<int> a{3, 1, 4, 1, 5};
auto b = a;
const auto c = b;
b = b.set(2, -1);
assert_that(static_cast<std::vector<int>>(a) == std::vector<int>{3, 1, 4, 1, 5});
assert_that(static_cast<std::vector<int>>(b) == std::vector<int>{3, 1, -1, 1, 5});
assert_that(static_cast<std::vector<int>>(c) == std::vector<int>{3, 1, 4, 1, 5});
}
assert_that(tools::persistent_array<int>::fully_released());
{
tools::persistent_array<int> a{3, 1, 4, 1, 5};
tools::persistent_array<int> b(std::move(a));
assert_that(a.empty());
assert_that(static_cast<std::vector<int>>(b) == std::vector<int>{3, 1, 4, 1, 5});
const auto c = std::move(b);
assert_that(a.empty());
assert_that(b.empty());
assert_that(static_cast<std::vector<int>>(c) == std::vector<int>{3, 1, 4, 1, 5});
}
assert_that(tools::persistent_array<int>::fully_released());
{
std::random_device seed_gen;
std::mt19937 engine(seed_gen());
std::uniform_int_distribution<int> t_dist(0, 3);
std::uniform_int_distribution<int> x_dist(-1000000000, 1000000000);
tools::persistent_array<int> a;
std::vector<tools::persistent_array<int>> a_history;
std::vector<int> naive;
std::vector<std::vector<int>> naive_history;
for (int q = 1; q <= 1000000; ++q) {
const int t = a.empty() ? 0 : t_dist(engine);
if (t <= 1) {
const auto x = x_dist(engine);
a = a.push_back(x);
naive.push_back(x);
} else if (t == 2) {
a = a.pop_back();
naive.pop_back();
} else {
const auto i = std::uniform_int_distribution<int>(0, a.size() - 1)(engine);
const auto x = x_dist(engine);
a = a.set(i, x);
naive[i] = x;
}
if (q % 100000 == 0) {
a_history.push_back(a);
naive_history.push_back(naive);
}
}
for (int q = 0; q < std::ssize(a_history); ++q) {
assert_that(static_cast<std::vector<int>>(a_history[q]) == naive_history[q]);
}
}
assert_that(tools::persistent_array<int>::fully_released());
return 0;
}
#line 1 "tests/persistent_array.test.cpp"
// competitive-verifier: STANDALONE
#include <cmath>
#include <iostream>
#include <iterator>
#include <random>
#include <ranges>
#include <utility>
#include <vector>
#line 1 "tools/assert_that.hpp"
#line 5 "tools/assert_that.hpp"
#include <cstdlib>
#define assert_that_impl(cond, file, line, func) do {\
if (!cond) {\
std::cerr << file << ':' << line << ": " << func << ": Assertion `" << #cond << "' failed." << '\n';\
std::exit(EXIT_FAILURE);\
}\
} while (false)
#define assert_that(...) assert_that_impl((__VA_ARGS__), __FILE__, __LINE__, __func__)
#line 1 "tools/persistent_array.hpp"
#include <algorithm>
#include <array>
#include <cassert>
#include <concepts>
#include <functional>
#include <initializer_list>
#line 11 "tools/persistent_array.hpp"
#include <memory>
#line 13 "tools/persistent_array.hpp"
#include <stack>
#line 1 "tools/ceil_log2.hpp"
#line 1 "tools/bit_width.hpp"
#include <bit>
#line 6 "tools/bit_width.hpp"
#include <type_traits>
#line 1 "tools/is_signed.hpp"
#line 5 "tools/is_signed.hpp"
namespace tools {
template <typename T>
struct is_signed : std::is_signed<T> {};
template <typename T>
inline constexpr bool is_signed_v = tools::is_signed<T>::value;
}
#line 1 "tools/is_unsigned.hpp"
#line 5 "tools/is_unsigned.hpp"
namespace tools {
template <typename T>
struct is_unsigned : std::is_unsigned<T> {};
template <typename T>
inline constexpr bool is_unsigned_v = tools::is_unsigned<T>::value;
}
#line 1 "tools/make_unsigned.hpp"
#line 5 "tools/make_unsigned.hpp"
namespace tools {
template <typename T>
struct make_unsigned : std::make_unsigned<T> {};
template <typename T>
using make_unsigned_t = typename tools::make_unsigned<T>::type;
}
#line 1 "tools/non_bool_integral.hpp"
#line 1 "tools/integral.hpp"
#line 1 "tools/is_integral.hpp"
#line 5 "tools/is_integral.hpp"
namespace tools {
template <typename T>
struct is_integral : std::is_integral<T> {};
template <typename T>
inline constexpr bool is_integral_v = tools::is_integral<T>::value;
}
#line 5 "tools/integral.hpp"
namespace tools {
template <typename T>
concept integral = tools::is_integral_v<T>;
}
#line 7 "tools/non_bool_integral.hpp"
namespace tools {
template <typename T>
concept non_bool_integral = tools::integral<T> && !std::same_as<std::remove_cv_t<T>, bool>;
}
#line 12 "tools/bit_width.hpp"
namespace tools {
namespace detail::bit_width {
template <tools::non_bool_integral T>
struct impl {
constexpr int operator()(const T x) const noexcept(noexcept(impl<tools::make_unsigned_t<T>>{}(x))) requires tools::is_signed_v<T> {
assert(x >= 0);
return impl<tools::make_unsigned_t<T>>{}(x);
}
constexpr int operator()(const T x) const noexcept(noexcept(std::bit_width(x))) requires tools::is_unsigned_v<T> {
return std::bit_width(x);
}
};
}
template <typename T>
constexpr decltype(auto) bit_width(T&& x) noexcept(noexcept(tools::detail::bit_width::impl<std::remove_cvref_t<T>>{}(std::forward<T>(x)))) {
return tools::detail::bit_width::impl<std::remove_cvref_t<T>>{}(std::forward<T>(x));
}
}
#line 6 "tools/ceil_log2.hpp"
namespace tools {
template <typename T>
constexpr T ceil_log2(T x) noexcept {
assert(x > 0);
return tools::bit_width(x - 1);
}
}
#line 1 "tools/countr_zero.hpp"
#line 7 "tools/countr_zero.hpp"
#include <limits>
#line 14 "tools/countr_zero.hpp"
namespace tools {
namespace detail::countr_zero {
template <tools::non_bool_integral T>
struct impl {
constexpr int operator()(const T x) const noexcept(noexcept(impl<tools::make_unsigned_t<T>>{}(x))) requires tools::is_signed_v<T> {
assert(x >= 0);
return std::min(impl<tools::make_unsigned_t<T>>{}(x), std::numeric_limits<T>::digits);
}
constexpr int operator()(const T x) const noexcept(noexcept(std::countr_zero(x))) requires tools::is_unsigned_v<T> {
return std::countr_zero(x);
}
};
}
template <typename T>
constexpr decltype(auto) countr_zero(T&& x) noexcept(noexcept(tools::detail::countr_zero::impl<std::remove_cvref_t<T>>{}(std::forward<T>(x)))) {
return tools::detail::countr_zero::impl<std::remove_cvref_t<T>>{}(std::forward<T>(x));
}
}
#line 1 "tools/fix.hpp"
#line 6 "tools/fix.hpp"
namespace tools {
template <typename F>
struct fix : F {
template <typename G>
fix(G&& g) : F({std::forward<G>(g)}) {
}
template <typename... Args>
decltype(auto) operator()(Args&&... args) const {
return F::operator()(*this, std::forward<Args>(args)...);
}
};
template <typename F>
fix(F&&) -> fix<std::decay_t<F>>;
}
#line 1 "tools/floor_log2.hpp"
#line 6 "tools/floor_log2.hpp"
namespace tools {
template <typename T>
constexpr T floor_log2(T x) noexcept {
assert(x > 0);
return tools::bit_width(x) - 1;
}
}
#line 1 "tools/pow2.hpp"
#line 7 "tools/pow2.hpp"
namespace tools {
template <tools::integral T>
constexpr T pow2(const T x) noexcept {
assert(0 <= x && x < std::numeric_limits<T>::digits);
return T(1) << x;
}
}
#line 21 "tools/persistent_array.hpp"
namespace tools {
template <typename T>
class persistent_array {
struct inner_node {
std::array<int, 2> children;
int refcnt;
};
struct leaf_node {
T data;
int refcnt;
};
static inline std::vector<inner_node> s_inner_nodes;
static inline std::vector<int> s_free_inner_ids;
static inline std::vector<leaf_node> s_leaf_nodes;
static inline std::vector<int> s_free_leaf_ids;
static int malloc_inner() {
if (s_free_inner_ids.empty()) {
s_inner_nodes.emplace_back();
return s_inner_nodes.size() - 1;
} else {
const auto id = s_free_inner_ids.back();
s_free_inner_ids.pop_back();
return id;
}
}
static void free_inner(const int id) {
assert(0 <= id && id < std::ssize(s_inner_nodes));
assert(s_inner_nodes[id].refcnt == 0);
s_free_inner_ids.push_back(id);
}
static int malloc_leaf() {
if (s_free_leaf_ids.empty()) {
s_leaf_nodes.emplace_back();
return s_leaf_nodes.size() - 1;
} else {
const auto id = s_free_leaf_ids.back();
s_free_leaf_ids.pop_back();
return id;
}
}
static void free_leaf(const int id) {
assert(0 <= id && id < std::ssize(s_leaf_nodes));
assert(s_leaf_nodes[id].refcnt == 0);
s_free_leaf_ids.push_back(id);
}
static void increment_refcnt(int id) {
if (id >= 0) {
assert(0 <= id && id < std::ssize(s_inner_nodes));
++s_inner_nodes[id].refcnt;
} else {
id = ~id;
assert(0 <= id && id < std::ssize(s_leaf_nodes));
++s_leaf_nodes[id].refcnt;
}
}
static void increment_refcnt(const std::array<int, 2>& children) {
increment_refcnt(children[0]);
increment_refcnt(children[1]);
}
int m_size;
int m_root;
void wipe() const {
if (!this->empty()) {
tools::fix([&](auto&& dfs, int id) -> void {
if (id >= 0) {
assert(0 <= id && id < std::ssize(s_inner_nodes));
assert(s_inner_nodes[id].refcnt > 0);
--s_inner_nodes[id].refcnt;
if (s_inner_nodes[id].refcnt == 0) {
dfs(s_inner_nodes[id].children[0]);
dfs(s_inner_nodes[id].children[1]);
free_inner(id);
}
} else {
id = ~id;
assert(0 <= id && id < std::ssize(s_leaf_nodes));
assert(s_leaf_nodes[id].refcnt > 0);
--s_leaf_nodes[id].refcnt;
if (s_leaf_nodes[id].refcnt == 0) {
free_leaf(id);
}
}
})(this->m_root);
}
}
public:
// For testing purposes.
static bool fully_released() {
std::vector<bool> released_inner(s_inner_nodes.size(), false);
for (const auto id : s_free_inner_ids) {
assert(!released_inner[id]);
released_inner[id] = true;
}
std::vector<bool> released_leaf(s_leaf_nodes.size(), false);
for (const auto id : s_free_leaf_ids) {
assert(!released_leaf[id]);
released_leaf[id] = true;
}
return std::ranges::all_of(released_inner, std::identity{}) && std::ranges::all_of(released_leaf, std::identity{});
}
persistent_array() : m_size(0) {
}
persistent_array(const int n) : persistent_array(n, T{}) {
}
template <typename U>
requires std::assignable_from<T&, U>
persistent_array(const int n, U&& x) {
assert(n >= 0);
if (n == 0) {
this->m_size = 0;
return;
}
const auto floor_log2_n = tools::floor_log2(n);
const auto ceil_log2_n = tools::ceil_log2(n);
const auto v2_n = tools::countr_zero(n);
std::vector<int> perfect_binary_trees(floor_log2_n + 1);
perfect_binary_trees[0] = ~malloc_leaf();
s_leaf_nodes[~perfect_binary_trees[0]].data = std::forward<U>(x);
s_leaf_nodes[~perfect_binary_trees[0]].refcnt = 0;
for (int h = 1; h < std::ssize(perfect_binary_trees); ++h) {
perfect_binary_trees[h] = malloc_inner();
s_inner_nodes[perfect_binary_trees[h]].children = {perfect_binary_trees[h - 1], perfect_binary_trees[h - 1]};
increment_refcnt(s_inner_nodes[perfect_binary_trees[h]].children);
s_inner_nodes[perfect_binary_trees[h]].refcnt = 0;
}
auto right_id = perfect_binary_trees[v2_n];
for (auto h = v2_n + 2; h <= ceil_log2_n; ++h) {
if ((n >> (h - 1)) & 1) {
const auto id = malloc_inner();
s_inner_nodes[id].children = {perfect_binary_trees[h - 1], right_id};
increment_refcnt(s_inner_nodes[id].children);
s_inner_nodes[id].refcnt = 0;
right_id = id;
}
}
this->m_size = n;
this->m_root = right_id;
increment_refcnt(this->m_root);
}
template <std::ranges::input_range R>
requires (!std::ranges::random_access_range<R> && std::assignable_from<T&, std::ranges::range_reference_t<R>>)
persistent_array(R&& v) : persistent_array(std::forward<R>(v) | std::ranges::to<std::vector>()) {
}
template <std::ranges::random_access_range R>
requires std::assignable_from<T&, std::ranges::range_reference_t<R>>
persistent_array(R&& v) {
this->m_size = std::ranges::distance(v);
if (!this->empty()) {
this->m_root = tools::fix([&](auto&& dfs, const int l, const int r, const int h) -> int {
assert(0 <= l && l < r && r <= this->m_size);
assert(h >= 0);
assert(r - l <= tools::pow2(h));
if (r - l == 1) {
const auto id = malloc_leaf();
s_leaf_nodes[id].data = std::ranges::begin(v)[l];
s_leaf_nodes[id].refcnt = 0;
return ~id;
}
const auto m = l + tools::pow2(h - 1);
if (this->m_size <= m) {
return dfs(l, r, h - 1);
}
const auto id = malloc_inner();
const auto left_id = dfs(l, m, h - 1);
const auto right_id = dfs(m, r, h - 1);
s_inner_nodes[id].children = {left_id, right_id};
increment_refcnt(s_inner_nodes[id].children);
s_inner_nodes[id].refcnt = 0;
return id;
})(0, this->m_size, tools::ceil_log2(this->m_size));
increment_refcnt(this->m_root);
}
}
persistent_array(std::initializer_list<T> il) : persistent_array(std::views::all(il)) {
}
persistent_array(const tools::persistent_array<T>& other) : m_size(other.m_size) {
if (!this->empty()) {
this->m_root = other.m_root;
increment_refcnt(this->m_root);
}
}
persistent_array(tools::persistent_array<T>&& other) noexcept : m_size(other.m_size), m_root(other.m_root) {
other.m_size = 0;
}
~persistent_array() {
this->wipe();
}
tools::persistent_array<T>& operator=(const tools::persistent_array<T>& other) {
if (this != std::addressof(other)) {
this->wipe();
this->m_size = other.m_size;
if (!this->empty()) {
this->m_root = other.m_root;
increment_refcnt(this->m_root);
}
}
return *this;
}
tools::persistent_array<T>& operator=(tools::persistent_array<T>&& other) noexcept {
if (this != std::addressof(other)) {
this->wipe();
this->m_size = other.m_size;
this->m_root = other.m_root;
other.m_size = 0;
}
return *this;
}
bool empty() const {
return this->m_size == 0;
}
int size() const {
return this->m_size;
}
const T& operator[](const int i) const {
assert(0 <= i && i < this->m_size);
auto id = this->m_root;
int l = 0;
int r = this->m_size;
int h = tools::ceil_log2(this->m_size);
while (id >= 0) {
assert(0 <= l && l + 2 <= r && r <= this->m_size);
assert(h >= 1);
assert(r - l <= tools::pow2(h));
const auto m = l + tools::pow2(h - 1);
if (m < this->m_size) {
if (i < m) {
r = m;
id = s_inner_nodes[id].children[0];
} else {
l = m;
id = s_inner_nodes[id].children[1];
}
}
--h;
}
id = ~id;
return s_leaf_nodes[id].data;
}
template <typename U>
requires std::assignable_from<T&, U>
tools::persistent_array<T> set(const int i, U&& x) const {
assert(0 <= i && i < this->m_size);
tools::persistent_array<T> res;
res.m_size = this->m_size;
res.m_root = tools::fix([&](auto&& dfs, const int id, const int l, const int r, const int h) -> int {
assert(0 <= l && l < r && r <= this->m_size);
assert(h >= 0);
assert(r - l <= tools::pow2(h));
if (id < 0) {
assert(r - l == 1);
const auto new_id = malloc_leaf();
s_leaf_nodes[new_id].data = std::forward<U>(x);
s_leaf_nodes[new_id].refcnt = 0;
return ~new_id;
}
assert(r - l >= 2);
assert(h >= 1);
const auto m = l + tools::pow2(h - 1);
if (this->m_size <= m) {
return dfs(id, l, r, h - 1);
}
const auto new_id = malloc_inner();
int left_id, right_id;
if (i < m) {
left_id = dfs(s_inner_nodes[id].children[0], l, m, h - 1);
right_id = s_inner_nodes[id].children[1];
} else {
left_id = s_inner_nodes[id].children[0];
right_id = dfs(s_inner_nodes[id].children[1], m, r, h - 1);
}
s_inner_nodes[new_id].children = {left_id, right_id};
increment_refcnt(s_inner_nodes[new_id].children);
s_inner_nodes[new_id].refcnt = 0;
return new_id;
})(this->m_root, 0, this->m_size, tools::ceil_log2(this->m_size));
increment_refcnt(res.m_root);
return res;
}
template <typename U>
requires std::assignable_from<T&, U>
tools::persistent_array<T> push_back(U&& x) const {
tools::persistent_array<T> res;
res.m_size = this->m_size + 1;
if (this->empty()) {
const auto new_id = malloc_leaf();
s_leaf_nodes[new_id].data = std::forward<U>(x);
s_leaf_nodes[new_id].refcnt = 0;
res.m_root = ~new_id;
increment_refcnt(res.m_root);
} else {
res.m_root = tools::fix([&](auto&& dfs, const int id, const int l, const int h) -> int {
assert(0 <= l && l < this->m_size);
assert(h >= 0);
assert(this->m_size - l <= tools::pow2(h));
if (this->m_size - l == tools::pow2(h)) {
const auto new_leaf_id = malloc_leaf();
s_leaf_nodes[new_leaf_id].data = std::forward<U>(x);
s_leaf_nodes[new_leaf_id].refcnt = 0;
const auto new_inner_id = malloc_inner();
s_inner_nodes[new_inner_id].children = {id, ~new_leaf_id};
increment_refcnt(s_inner_nodes[new_inner_id].children);
s_inner_nodes[new_inner_id].refcnt = 0;
return new_inner_id;
}
assert(h >= 1);
const auto m = l + tools::pow2(h - 1);
if (this->m_size <= m) {
return dfs(id, l, h - 1);
}
assert(this->m_size - l >= 2);
const auto new_id = malloc_inner();
const auto right_id = dfs(s_inner_nodes[id].children[1], m, h - 1);
s_inner_nodes[new_id].children = {s_inner_nodes[id].children[0], right_id};
increment_refcnt(s_inner_nodes[new_id].children);
s_inner_nodes[new_id].refcnt = 0;
return new_id;
})(this->m_root, 0, tools::ceil_log2(this->m_size));
increment_refcnt(res.m_root);
}
return res;
}
tools::persistent_array<T> pop_back() const {
assert(!this->empty());
tools::persistent_array<T> res;
res.m_size = this->m_size - 1;
if (!res.empty()) {
res.m_root = tools::fix([&](auto&& dfs, const int id, const int l, const int h) -> int {
assert(0 <= l && l + 2 <= this->m_size);
assert(h >= 1);
assert(this->m_size - l <= tools::pow2(h));
if (this->m_size - l == tools::pow2(h - 1) + 1) {
return s_inner_nodes[id].children[0];
}
assert(h >= 2);
const auto m = l + tools::pow2(h - 1);
if (this->m_size <= m) {
return dfs(id, l, h - 1);
}
assert(this->m_size - l >= 3);
const auto new_id = malloc_inner();
const auto right_id = dfs(s_inner_nodes[id].children[1], m, h - 1);
s_inner_nodes[new_id].children = {s_inner_nodes[id].children[0], right_id};
increment_refcnt(s_inner_nodes[new_id].children);
s_inner_nodes[new_id].refcnt = 0;
return new_id;
})(this->m_root, 0, tools::ceil_log2(this->m_size));
increment_refcnt(res.m_root);
}
return res;
}
explicit operator std::vector<T>() const {
std::vector<T> res(this->m_size);
if (!this->empty()) {
tools::fix([&](auto&& dfs, const int id, const int l, const int r, const int h) -> void {
assert(0 <= l && l < r && r <= this->m_size);
assert(h >= 0);
assert(r - l <= tools::pow2(h));
if (id < 0) {
assert(r - l == 1);
res[l] = s_leaf_nodes[~id].data;
return;
}
assert(r - l >= 2);
assert(h >= 1);
const auto m = l + tools::pow2(h - 1);
if (this->m_size <= m) {
dfs(id, l, r, h - 1);
return;
}
dfs(s_inner_nodes[id].children[0], l, m, h - 1);
dfs(s_inner_nodes[id].children[1], m, r, h - 1);
})(this->m_root, 0, this->m_size, tools::ceil_log2(this->m_size));
}
return res;
}
};
}
#line 12 "tests/persistent_array.test.cpp"
int main() {
std::cin.tie(nullptr);
std::ios_base::sync_with_stdio(false);
{
const tools::persistent_array<int> a;
assert_that(a.empty());
assert_that(a.size() == 0);
}
assert_that(tools::persistent_array<int>::fully_released());
{
const tools::persistent_array<int> a{0};
assert_that(!a.empty());
assert_that(a.size() == 1);
assert_that(a[0] == 0);
}
assert_that(tools::persistent_array<int>::fully_released());
{
const tools::persistent_array<int> a{0, 1};
assert_that(!a.empty());
assert_that(a.size() == 2);
assert_that(a[0] == 0);
assert_that(a[1] == 1);
}
assert_that(tools::persistent_array<int>::fully_released());
{
const tools::persistent_array<int> a{0, 1, 2};
assert_that(!a.empty());
assert_that(a.size() == 3);
assert_that(a[0] == 0);
assert_that(a[1] == 1);
assert_that(a[2] == 2);
}
assert_that(tools::persistent_array<int>::fully_released());
for (int n = 0; n <= 40; ++n) {
{
const tools::persistent_array<int> a(n);
for (int i = 0; i < n; ++i) {
assert_that(a[i] == 0);
}
assert_that(static_cast<std::vector<int>>(a) == std::vector<int>(n, 0));
}
assert_that(tools::persistent_array<int>::fully_released());
{
const tools::persistent_array<int> a(n, -1234);
for (int i = 0; i < n; ++i) {
assert_that(a[i] == -1234);
}
assert_that(static_cast<std::vector<int>>(a) == std::vector<int>(n, -1234));
}
assert_that(tools::persistent_array<int>::fully_released());
{
const tools::persistent_array<int> a(std::views::iota(0, n));
for (int i = 0; i < n; ++i) {
assert_that(a[i] == i);
}
assert_that(static_cast<std::vector<int>>(a) == (std::views::iota(0, n) | std::ranges::to<std::vector>()));
}
assert_that(tools::persistent_array<int>::fully_released());
{
std::vector<tools::persistent_array<int>> arrays(n + 1);
arrays[0] = tools::persistent_array<int>(n, -1234);
for (int i = 1; i <= n; ++i) {
arrays[i] = arrays[i - 1].set(i - 1, i - 1);
}
for (int i = 0; i <= n; ++i) {
assert_that(static_cast<std::vector<int>>(arrays[i]) == (std::views::iota(0, n) | std::views::transform([&](const auto j) { return j < i ? j : -1234; }) | std::ranges::to<std::vector>()));
}
}
assert_that(tools::persistent_array<int>::fully_released());
{
std::vector<tools::persistent_array<int>> arrays(n + 1);
arrays[0] = tools::persistent_array<int>(std::views::repeat(-1234, n));
for (int i = 1; i <= n; ++i) {
arrays[i] = arrays[i - 1].set(i - 1, i - 1);
}
for (int i = 0; i <= n; ++i) {
assert_that(static_cast<std::vector<int>>(arrays[i]) == (std::views::iota(0, n) | std::views::transform([&](const auto j) { return j < i ? j : -1234; }) | std::ranges::to<std::vector>()));
}
}
assert_that(tools::persistent_array<int>::fully_released());
{
std::vector<tools::persistent_array<int>> arrays(2 * n + 1);
for (int i = 1; i <= n; ++i) {
arrays[i] = arrays[i - 1].push_back(i - 1);
}
for (int i = n + 1; i <= 2 * n; ++i) {
arrays[i] = arrays[i - 1].pop_back();
}
for (int i = 0; i <= 2 * n; ++i) {
assert_that(static_cast<std::vector<int>>(arrays[i]) == (std::views::iota(0, n - std::abs(n - i)) | std::ranges::to<std::vector>()));
}
}
assert_that(tools::persistent_array<int>::fully_released());
}
{
const tools::persistent_array<int> a{3, 1, 4, 1, 5};
auto b = a;
const auto c = b;
b = b.set(2, -1);
assert_that(static_cast<std::vector<int>>(a) == std::vector<int>{3, 1, 4, 1, 5});
assert_that(static_cast<std::vector<int>>(b) == std::vector<int>{3, 1, -1, 1, 5});
assert_that(static_cast<std::vector<int>>(c) == std::vector<int>{3, 1, 4, 1, 5});
}
assert_that(tools::persistent_array<int>::fully_released());
{
tools::persistent_array<int> a{3, 1, 4, 1, 5};
tools::persistent_array<int> b(std::move(a));
assert_that(a.empty());
assert_that(static_cast<std::vector<int>>(b) == std::vector<int>{3, 1, 4, 1, 5});
const auto c = std::move(b);
assert_that(a.empty());
assert_that(b.empty());
assert_that(static_cast<std::vector<int>>(c) == std::vector<int>{3, 1, 4, 1, 5});
}
assert_that(tools::persistent_array<int>::fully_released());
{
std::random_device seed_gen;
std::mt19937 engine(seed_gen());
std::uniform_int_distribution<int> t_dist(0, 3);
std::uniform_int_distribution<int> x_dist(-1000000000, 1000000000);
tools::persistent_array<int> a;
std::vector<tools::persistent_array<int>> a_history;
std::vector<int> naive;
std::vector<std::vector<int>> naive_history;
for (int q = 1; q <= 1000000; ++q) {
const int t = a.empty() ? 0 : t_dist(engine);
if (t <= 1) {
const auto x = x_dist(engine);
a = a.push_back(x);
naive.push_back(x);
} else if (t == 2) {
a = a.pop_back();
naive.pop_back();
} else {
const auto i = std::uniform_int_distribution<int>(0, a.size() - 1)(engine);
const auto x = x_dist(engine);
a = a.set(i, x);
naive[i] = x;
}
if (q % 100000 == 0) {
a_history.push_back(a);
naive_history.push_back(naive);
}
}
for (int q = 0; q < std::ssize(a_history); ++q) {
assert_that(static_cast<std::vector<int>>(a_history[q]) == naive_history[q]);
}
}
assert_that(tools::persistent_array<int>::fully_released());
return 0;
}