51#include <gtest/gtest.h>
72#if defined(__SANITIZE_THREAD__)
73# define ALEPH_PREFIX_TREE_TEST_UNDER_TSAN 1
74#elif defined(__has_feature)
75# if __has_feature(thread_sanitizer)
76# define ALEPH_PREFIX_TREE_TEST_UNDER_TSAN 1
79#ifndef ALEPH_PREFIX_TREE_TEST_UNDER_TSAN
80# define ALEPH_PREFIX_TREE_TEST_UNDER_TSAN 0
89#if !ALEPH_PREFIX_TREE_TEST_UNDER_TSAN
93 while (remaining >= 0)
98 remaining, remaining - 1, std::memory_order_relaxed))
108 throw std::bad_alloc();
113 void *ptr = std::malloc(
size);
115 throw std::bad_alloc();
135 class AllocationFailureScope
143 std::memory_order_relaxed);
159#if !ALEPH_PREFIX_TREE_TEST_UNDER_TSAN
161void *
operator new(std::size_t
size)
166void *
operator new[](std::size_t
size)
171void *
operator new(std::size_t
size,
const std::nothrow_t &)
noexcept
183void *
operator new[](std::size_t
size,
const std::nothrow_t &)
noexcept
195void operator delete(
void *ptr)
noexcept
200void operator delete[](
void *ptr)
noexcept
205void operator delete(
void *ptr, std::size_t)
noexcept
210void operator delete[](
void *ptr, std::size_t)
noexcept
215void operator delete(
void *ptr,
const std::nothrow_t &)
noexcept
220void operator delete[](
void *ptr,
const std::nothrow_t &)
noexcept
227using namespace Aleph;
229static_assert(std::is_same_v<decltype(std::declval<Cnode &>().search_child(
'a')),
231static_assert(std::is_same_v<decltype(std::declval<const Cnode &>().search_child(
'a')),
233static_assert(std::is_same_v<decltype(std::declval<Cnode &>().greater_child(
'a')),
235static_assert(std::is_same_v<decltype(std::declval<const Cnode &>().greater_child(
'a')),
237static_assert(std::is_same_v<decltype(std::declval<Cnode &>().search_prefix(
"a")),
238 std::tuple<Cnode *, const char *>>);
239static_assert(std::is_same_v<decltype(std::declval<const Cnode &>().search_prefix(
"a")),
240 std::tuple<const Cnode *, const char *>>);
241static_assert(std::is_same_v<decltype(std::declval<Prefix_Tree &>().root()),
243static_assert(std::is_same_v<decltype(std::declval<const Prefix_Tree &>().root()),
245static_assert(std::is_move_constructible_v<Prefix_Tree_Map<int>>);
246static_assert(std::is_copy_constructible_v<Prefix_Tree_Map<int>>);
247static_assert(std::is_move_constructible_v<Prefix_Tree_Map<std::unique_ptr<int>>>);
248static_assert(
not std::is_copy_constructible_v<Prefix_Tree_Map<std::unique_ptr<int>>>);
252 std::vector<std::string>
ret;
254 std::sort(
ret.begin(),
ret.end());
260 std::vector<std::string>
ret;
262 for (
const auto &
w : words)
264 std::sort(
ret.begin(),
ret.end());
344 auto * child =
new Cnode(
'a');
345 root->insert_child(child);
361 auto it =
kids.get_it();
442 (std::vector<std::string>{
"a",
"a!"}));
447#if ALEPH_PREFIX_TREE_TEST_UNDER_TSAN
448 GTEST_SKIP() <<
"AllocationFailureScope needs a custom global operator "
449 "new/delete, which conflicts with TSan's own at link time.";
460 root->insert_word(
"abc");
462 catch (
const std::bad_alloc &)
514 root->insert_word(
"hello");
522 root->insert_word(
"hello");
524 auto result =
root->search_word(
"hello");
531 root->insert_word(
"hello");
544 auto [node, remaining] =
root->search_prefix(
"");
552 root->insert_word(
"hello");
554 auto [node, remaining] =
root->search_prefix(
"hel");
562 root->insert_word(
"hello");
564 auto [node, remaining] =
root->search_prefix(
"helping");
572 root->insert_word(
"hello");
574 auto [node, remaining] =
root->search_prefix(
"world");
586 auto words =
root->words();
592 root->insert_word(
"hello");
594 auto words =
root->words();
596 EXPECT_EQ(std::string(words[0]),
"hello");
601 root->insert_word(
"hello");
602 root->insert_word(
"help");
603 root->insert_word(
"world");
605 auto words =
root->words();
609 std::vector<std::string> v;
610 words.for_each([&v](
const std::string&
w) { v.push_back(
w); });
611 std::sort(v.begin(), v.end());
620 root->insert_word(
"a");
621 root->insert_word(
"ab");
622 root->insert_word(
"abc");
623 root->insert_word(
"abcd");
625 auto words =
root->words();
640 (std::vector<std::string>{
"$",
"a$",
"a$b"}));
651 (std::vector<std::string>{
"",
"!"}));
671 root->insert_word(
"hello");
672 root->insert_word(
"help");
673 root->insert_word(
"world");
683 root->insert_word(
"test");
693 root->insert_word(
"");
694 root->insert_word(
"$");
695 root->insert_word(
"a!");
696 root->insert_word(
"a");
705 (std::vector<std::string>{
"",
"$",
"a",
"a!"}));
713#if ALEPH_PREFIX_TREE_TEST_UNDER_TSAN
714 GTEST_SKIP() <<
"AllocationFailureScope needs a custom global operator "
715 "new/delete, which conflicts with TSan's own at link time.";
717 root->insert_word(
"alpha");
718 root->insert_word(
"beta");
719 root->insert_word(
"$");
732 catch (
const std::bad_alloc &)
753 std::string str =
root->to_str();
760 root->insert_word(
"ab");
761 std::string str =
root->to_str();
764 EXPECT_NE(str.find(
'a'), std::string::npos);
765 EXPECT_NE(str.find(
'b'), std::string::npos);
782 for (
int i = 0; i <
N; ++i)
783 root->insert_word(
"w" + std::to_string(i));
785 for (
int i = 0; i <
N; ++i)
788 auto words =
root->words();
824 root->insert_word(
"hello");
830 root->insert_word(
"hello");
831 root->insert_word(
"help");
832 root->insert_word(
"world");
838 root->insert_word(
"a");
839 root->insert_word(
"ab");
840 root->insert_word(
"abc");
850 root->insert_word(
"hello");
851 auto words =
root->words_with_prefix(
"xyz");
857 root->insert_word(
"hello");
858 root->insert_word(
"help");
859 root->insert_word(
"helicopter");
860 root->insert_word(
"world");
862 auto words =
root->words_with_prefix(
"hel");
868 root->insert_word(
"test");
869 root->insert_word(
"testing");
870 root->insert_word(
"tester");
872 const auto words =
root->words_with_prefix(
"test");
878 root->insert_word(
"apple");
879 root->insert_word(
"application");
881 auto words =
root->words_with_prefix(
"ban");
887 root->insert_word(
"");
888 root->insert_word(
"hello");
889 root->insert_word(
"help");
890 root->insert_word(
"world");
899 auto * tree =
new Cnode(
'\0');
900 tree->insert_word(
"hi");
901 tree->insert_word(
"bye");
932 (std::vector<std::string>{
"",
"alpha",
"alphabet"}));
934 (std::vector<std::string>{
"alpha",
"alphabet"}));
974#if ALEPH_PREFIX_TREE_TEST_UNDER_TSAN
975 GTEST_SKIP() <<
"AllocationFailureScope needs a custom global operator "
976 "new/delete, which conflicts with TSan's own at link time.";
1016 copy.insert_word(
"gamma");
1083 target = std::move(source);
1101#if ALEPH_PREFIX_TREE_TEST_UNDER_TSAN
1102 GTEST_SKIP() <<
"AllocationFailureScope needs a custom global operator "
1103 "new/delete, which conflicts with TSan's own at link time.";
1122 catch (
const std::bad_alloc &)
1190 auto duplicate = std::make_unique<int>(2);
1258 (std::vector<std::string>{
"",
"app",
"apple",
"banana"}));
1260 (std::vector<std::string>{
"app",
"apple"}));
1271 copy.insert_or_assign(
"alpha",
"uno");
1272 copy.insert_or_assign(
"gamma",
"three");
1317 const std::vector<std::string>
keys =
1319 "",
"a",
"app",
"apple",
"application",
"banana",
"band",
"bandana",
1320 "car",
"carbon",
"cart",
"dog",
"door",
"dorm"
1324 std::map<std::string, int> reference;
1326 for (
size_t step = 0; step < 500; ++step)
1328 const std::string &key =
keys[(step * 7 + 3) %
keys.size()];
1329 const int value =
static_cast<int>(step);
1331 if ((step % 4) == 0)
1333 reference.emplace(key,
value).second);
1334 else if ((step % 4) == 1)
1337 reference[key] =
value;
1339 else if ((step % 4) == 2)
1344 const auto it = reference.find(key);
1354 for (
const auto &
kv : reference)
1357 for (
const auto &
kv : reference)
1366 const std::vector<std::string> words =
1368 "",
"app",
"apple",
"application",
"apt",
"banana",
"band",
1369 "bandana",
"car",
"carbon",
"cart"
1376 for (
size_t i = 0; i < words.size(); ++i)
1378 const int value =
static_cast<int>(i * 10);
1383 for (
size_t i = 0; i < words.size(); ++i)
1396 for (
const auto &
prefix : {
"",
"app",
"ban",
"car",
"z"})
1400 <<
"Prefix_Tree disagreement for prefix " <<
prefix;
1403 <<
"RadixTree disagreement for prefix " <<
prefix;
1413 ::testing::InitGoogleTest(&
argc,
argv);
size_t size_t int32_t value
Simple dynamic array with automatic resizing and functional operations.
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Low-level prefix tree node for storing character sequences.
static void clone(const Tree_Node< char > *src, Tree_Node< char > *tgt)
Clone helper - copies children from src to tgt.
void destroy() noexcept
Destroy all children of this node.
void mark_end_word()
Mark this node as the end of a word.
bool is_end_word() const noexcept
Check if this node marks the end of a word.
char symbol() const noexcept
Return the character stored in this node.
bool insert_word(const std::string &word)
Insert a word into the tree.
DynList< Cnode * > children() const
Return a list of all child nodes.
bool is_empty() const noexcept
Return true if the array is empty.
Owning prefix tree map from strings to values.
DynArray< Key > words(const size_t max_word_length=2048) const
Get all keys stored in the map.
size_t count() const noexcept
Return the number of stored key-value pairs.
bool contains(const Key &key) const noexcept
Check whether key is stored.
const Value * find(const Key &key) const noexcept
Look up key.
bool insert(const Key &key, const Value &value)
Insert key with a copied value if absent.
DynArray< Key > words_with_prefix(const Key &prefix, const size_t max_word_length=2048) const
Get all keys with a given prefix.
void insert_or_assign(const Key &key, Value value)
Insert key or overwrite its mapped value.
bool erase(const Key &key) noexcept
Remove key if present.
void clear()
Remove every key-value pair from the map.
size_t size() const noexcept
Return the number of stored key-value pairs.
bool is_empty() const noexcept
Check whether the map has no keys.
Owning prefix tree wrapper.
bool insert_word(const std::string &word)
Insert a word into the tree.
DynArray< std::string > words(const size_t max_word_length=2048) const
Get all words stored in the tree.
DynArray< std::string > words_with_prefix(const std::string &prefix, const size_t max_word_length=2048) const
Get all words with a given prefix.
Cnode * root() noexcept
Return the mutable root node.
size_t count() const noexcept
Count the words stored in the tree.
bool contains(const std::string &word) const noexcept
Check whether a word exists in the tree.
size_t size() const noexcept
Return the number of words stored in the tree.
Compressed prefix tree mapping std::basic_string<Char> keys to values of type T.
Minimal std::expected-style result type for C++20.
void for_each(Operation &operation)
Traverse all the container and performs an operation on each element.
__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)
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Main namespace for Aleph-w library functions.
size_t size(Node *root) noexcept
Itor2 copy(Itor1 sourceBeg, const Itor1 &sourceEnd, Itor2 destBeg)
Copy elements from one range to another.
static void prefix(Node *root, DynList< Node * > &acc)
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Trie (prefix tree) implementation.
static std::vector< std::string > to_sorted_vector(const DynArray< std::string > &words)
TEST_F(PrefixTreeTest, NodeConstruction)
Compressed prefix tree (Aleph::RadixTree) mapping string keys to values.