Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
persistent_hash_map_test.cc
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
35#include <gtest/gtest.h>
36
37#include <string>
38#include <unordered_map>
39#include <vector>
40#include <random>
41
43
44using namespace Aleph;
45
46namespace
47{
48
49size_t bad_hash(const std::string &)
50{
51 return 0;
52}
53
54char lower_ascii(const char c)
55{
56 return c >= 'A' and c <= 'Z' ? static_cast<char>(c - 'A' + 'a') : c;
57}
58
59size_t case_hash(const std::string &s)
60{
61 size_t h = 1469598103934665603ULL;
62 for (const char c : s)
63 {
64 h ^= static_cast<unsigned char>(lower_ascii(c));
65 h *= 1099511628211ULL;
66 }
67 return h;
68}
69
70struct CaseEqual
71{
72 bool operator () (const std::string &a, const std::string &b) const noexcept
73 {
74 if (a.size() != b.size())
75 return false;
76 for (size_t i = 0; i < a.size(); ++i)
77 if (lower_ascii(a[i]) != lower_ascii(b[i]))
78 return false;
79 return true;
80 }
81};
82
83struct MoveTracked
84{
85 int value = 0;
86 bool moved_from = false;
87
88 explicit MoveTracked(const int v = 0) noexcept
89 : value(v)
90 {
91 // Empty.
92 }
93
94 MoveTracked(const MoveTracked &rhs) noexcept
95 : value(rhs.value)
96 {
97 // Empty.
98 }
99
100 MoveTracked(MoveTracked &&rhs) noexcept
101 : value(rhs.value)
102 {
103 rhs.moved_from = true;
104 }
105
106 MoveTracked &operator = (const MoveTracked &rhs) noexcept
107 {
108 value = rhs.value;
109 moved_from = false;
110 return *this;
111 }
112
113 MoveTracked &operator = (MoveTracked &&rhs) noexcept
114 {
115 value = rhs.value;
116 moved_from = false;
117 rhs.moved_from = true;
118 return *this;
119 }
120};
121
122} // namespace
123
125{
127 EXPECT_TRUE(map.is_empty());
128 EXPECT_EQ(map.size(), 0);
129 EXPECT_EQ(map.keys().size(), 0);
130 EXPECT_EQ(map.items().size(), 0);
131 EXPECT_TRUE(map.verify());
132}
133
135{
137 auto m2 = m1.insert("foo", 42);
138
139 EXPECT_EQ(m1.size(), 0);
140 EXPECT_FALSE(m1.contains("foo"));
141
142 EXPECT_EQ(m2.size(), 1);
143 EXPECT_TRUE(m2.contains("foo"));
144 ASSERT_NE(m2.find("foo"), nullptr);
145 EXPECT_EQ(*m2.find("foo"), 42);
146
147 auto m3 = m2.insert("bar", 99);
148 EXPECT_EQ(m2.size(), 1);
149 EXPECT_EQ(m3.size(), 2);
150 EXPECT_TRUE(m3.contains("foo"));
151 EXPECT_TRUE(m3.contains("bar"));
152 ASSERT_NE(m3.find("bar"), nullptr);
153 EXPECT_EQ(*m3.find("bar"), 99);
154
155 EXPECT_TRUE(m3.verify());
156}
157
159{
161 auto m2 = m1.insert("foo", 42);
162 auto duplicate = m2.insert("foo", 99);
163
164 EXPECT_EQ(duplicate.size(), 1);
165 ASSERT_NE(duplicate.find("foo"), nullptr);
166 EXPECT_EQ(*duplicate.find("foo"), 42);
167
168 auto updated = m2.insert_or_assign("foo", 99);
169 EXPECT_EQ(updated.size(), 1);
170 ASSERT_NE(updated.find("foo"), nullptr);
171 EXPECT_EQ(*updated.find("foo"), 99);
172
173 ASSERT_NE(m2.find("foo"), nullptr);
174 EXPECT_EQ(*m2.find("foo"), 42);
175 EXPECT_TRUE(updated.verify());
176}
177
179{
181 auto m2 = m1.insert("foo", MoveTracked{42});
182
183 MoveTracked replacement{99};
184 auto duplicate = m2.insert("foo", std::move(replacement));
185
186 EXPECT_FALSE(replacement.moved_from);
187 EXPECT_EQ(duplicate.size(), 1);
188 ASSERT_NE(duplicate.find("foo"), nullptr);
189 EXPECT_EQ(duplicate.find("foo")->value, 42);
190}
191
193{
195 map = map.insert("a", 1).insert("b", 2).insert("c", 3);
196
197 auto keys = map.keys();
198 auto items = map.items();
199 EXPECT_EQ(keys.size(), 3);
200 EXPECT_EQ(items.size(), 3);
201
202 std::unordered_map<std::string, int> seen;
203 for (size_t i = 0; i < items.size(); ++i)
204 seen.emplace(items[i].first, items[i].second);
205
206 EXPECT_EQ(seen.size(), 3);
207 EXPECT_EQ(seen["a"], 1);
208 EXPECT_EQ(seen["b"], 2);
209 EXPECT_EQ(seen["c"], 3);
210}
211
213{
215 auto m2 = m1.insert("a", 1).insert("b", 2).insert("c", 3);
216
217 EXPECT_EQ(m2.size(), 3);
218 auto missing = m2.erase("missing");
219 EXPECT_EQ(missing.size(), 3);
220 EXPECT_TRUE(missing.verify());
221
222 auto m3 = m2.erase("b");
223
224 EXPECT_EQ(m2.size(), 3);
225 EXPECT_TRUE(m2.contains("b"));
226
227 EXPECT_EQ(m3.size(), 2);
228 EXPECT_FALSE(m3.contains("b"));
229 EXPECT_TRUE(m3.contains("a"));
230 EXPECT_TRUE(m3.contains("c"));
231
232 auto m4 = m3.erase("a").erase("c");
233 EXPECT_TRUE(m4.is_empty());
234 EXPECT_TRUE(m4.verify());
235}
236
238{
240 std::vector<PersistentHashMap<std::string, int>> versions;
241 versions.push_back(map);
242
243 for (int i = 0; i < 100; ++i)
244 {
245 map = map.insert(std::to_string(i), i * 10);
246 versions.push_back(map);
247 EXPECT_TRUE(map.verify());
248 }
249
250 EXPECT_EQ(map.size(), 100);
251 for (int i = 0; i < 100; ++i)
252 {
253 ASSERT_NE(map.find(std::to_string(i)), nullptr);
254 EXPECT_EQ(*map.find(std::to_string(i)), i * 10);
255 }
256
257 auto keys = map.keys();
258 auto items = map.items();
259 EXPECT_EQ(keys.size(), 100u);
260 EXPECT_EQ(items.size(), 100u);
261
262 std::unordered_map<std::string, bool> exported_keys;
263 for (size_t i = 0; i < keys.size(); ++i)
264 exported_keys.emplace(keys[i], true);
265
266 std::unordered_map<std::string, int> exported_items;
267 for (size_t i = 0; i < items.size(); ++i)
268 exported_items.emplace(items[i].first, items[i].second);
269
270 EXPECT_EQ(exported_keys.size(), 100u);
271 EXPECT_EQ(exported_items.size(), 100u);
272 for (int i = 0; i < 100; ++i)
273 {
274 const std::string key = std::to_string(i);
275 EXPECT_NE(exported_keys.find(key), exported_keys.end());
276 auto item = exported_items.find(key);
277 ASSERT_NE(item, exported_items.end());
278 EXPECT_EQ(item->second, i * 10);
279 }
280
281 for (int i = 0; i <= 100; ++i)
282 EXPECT_EQ(versions[i].size(), i);
283
284 for (int i = 0; i < 100; ++i)
285 {
286 map = map.erase(std::to_string(i));
287 EXPECT_TRUE(map.verify());
288 }
289 EXPECT_TRUE(map.is_empty());
290}
291
293{
295 auto base = map.insert("a", 1).insert("b", 2);
296
297 auto duplicate = base.insert("a", 99);
298 EXPECT_EQ(duplicate.size(), 2);
299 ASSERT_NE(duplicate.find("a"), nullptr);
300 EXPECT_EQ(*duplicate.find("a"), 1);
301
302 auto updated = base.insert_or_assign("a", 99);
303 EXPECT_EQ(updated.size(), 2);
304 ASSERT_NE(updated.find("a"), nullptr);
305 ASSERT_NE(updated.find("b"), nullptr);
306 EXPECT_EQ(*updated.find("a"), 99);
307 EXPECT_EQ(*updated.find("b"), 2);
308 EXPECT_TRUE(updated.verify());
309
310 ASSERT_NE(base.find("a"), nullptr);
311 EXPECT_EQ(*base.find("a"), 1);
312}
313
315{
317 auto m1 = map.insert("Aleph", 1);
318 auto duplicate = m1.insert("aleph", 2);
319 auto updated = m1.insert_or_assign("aleph", 2);
320
321 EXPECT_EQ(duplicate.size(), 1);
322 ASSERT_NE(duplicate.find("ALEPH"), nullptr);
323 EXPECT_EQ(*duplicate.find("ALEPH"), 1);
324
325 EXPECT_EQ(updated.size(), 1);
326 ASSERT_NE(updated.find("ALEPH"), nullptr);
327 EXPECT_EQ(*updated.find("ALEPH"), 2);
328 EXPECT_TRUE(updated.verify());
329}
330
332{
333 std::mt19937 rng(42);
334 std::uniform_int_distribution<int> dist_op(0, 2);
335 std::uniform_int_distribution<int> dist_key(0, 500);
336
338 std::unordered_map<int, std::string> stdmap;
339 std::vector<PersistentHashMap<int, std::string>> versions;
340
341 versions.push_back(pmap);
342
343 for (int i = 0; i < 5000; ++i)
344 {
345 const int op = dist_op(rng);
346 const int key = dist_key(rng);
347
348 if (op == 0)
349 {
350 const std::string val = "insert" + std::to_string(key) + "_" + std::to_string(i);
351 pmap = pmap.insert(key, val);
352 stdmap.emplace(key, val);
353 }
354 else if (op == 1)
355 {
356 const std::string val = "assign" + std::to_string(key) + "_" + std::to_string(i);
357 pmap = pmap.insert_or_assign(key, val);
358 stdmap[key] = val;
359 }
360 else
361 {
362 pmap = pmap.erase(key);
363 stdmap.erase(key);
364 }
365
366 EXPECT_EQ(pmap.size(), stdmap.size());
367 EXPECT_TRUE(pmap.verify());
368
369 if (i % 100 == 0)
370 versions.push_back(pmap);
371 }
372
373 for (const auto &[k, v] : stdmap)
374 {
375 EXPECT_TRUE(pmap.contains(k));
376 ASSERT_NE(pmap.find(k), nullptr);
377 EXPECT_EQ(*pmap.find(k), v);
378 }
379
380 for (const auto &v_map : versions)
381 EXPECT_TRUE(v_map.verify());
382}
long double h
Definition btreepic.C:154
size_t size_t int32_t value
Definition ca-c-api.h:116
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:365
Immutable unordered map backed by a Hash Array Mapped Trie (HAMT).
bool is_empty() const noexcept
Return true when this version has no bindings.
PersistentHashMap insert_or_assign(const Key &key, const T &value) const
Return a new version with key bound to value.
bool verify() const
Verify HAMT routing, collision, uniqueness and size invariants.
Array< std::pair< Key, T > > items() const
Return all key/value bindings in unspecified order.
PersistentHashMap insert(const Key &key, const T &value) const
Return a new version with key inserted if absent.
Array< Key > keys() const
Return all keys in unspecified order.
PersistentHashMap erase(const Key &key) const
Return a new version without key.
const T * find(const Key &key) const
Find a mapped value.
size_t size() const noexcept
Return the number of bindings stored in this version.
#define TEST(name)
static mt19937 rng
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
size_t size(Node *root) noexcept
and
Check uniqueness with explicit hash + equality functors.
int keys[]
static int * k
Immutable path-copying hash map backed by a HAMT.