Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
prefix_tree_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
51#include <gtest/gtest.h>
52#include <prefix-tree.H>
53#include <algorithm>
54#include <vector>
55
56using namespace Aleph;
57
58//==============================================================================
59// Test Fixture
60//==============================================================================
61
62class PrefixTreeTest : public ::testing::Test
63{
64protected:
65 Cnode * root = nullptr;
66
67 void SetUp() override
68 {
69 root = new Cnode('\0'); // Root with sentinel character
70 }
71
72 void TearDown() override
73 {
74 if (root)
75 {
76 root->destroy();
77 delete root;
78 }
79 }
80};
81
82//==============================================================================
83// Basic Node Tests
84//==============================================================================
85
87{
88 Cnode node('a');
89 EXPECT_EQ(node.symbol(), 'a');
90}
91
93{
94 Cnode node('x');
95 EXPECT_EQ(node.symbol(), 'x');
96
97 Cnode node2('$');
98 EXPECT_EQ(node2.symbol(), '$');
99}
100
102{
103 Cnode node('a');
104 EXPECT_TRUE(node.children().is_empty());
105}
106
112
114{
115 Cnode node('a');
117
118 node.mark_end_word();
119 EXPECT_TRUE(node.is_end_word());
120
121 // Clean up the '$' child created by mark_end_word
122 node.destroy();
123}
124
125//==============================================================================
126// Child Operations Tests
127//==============================================================================
128
130{
131 EXPECT_EQ(root->search_child('a'), nullptr);
132}
133
135{
136 auto * child = new Cnode('a');
137 root->insert_child(child);
138
139 EXPECT_EQ(root->search_child('a'), child);
140 EXPECT_EQ(root->search_child('b'), nullptr);
141}
142
144{
145 root->insert_child(new Cnode('c'));
146 root->insert_child(new Cnode('a'));
147 root->insert_child(new Cnode('b'));
148
149 auto kids = root->children();
150 ASSERT_EQ(kids.size(), 3);
151
152 // Verify sorted order
153 auto it = kids.get_it();
154 EXPECT_EQ(it.get_curr()->symbol(), 'a');
155 it.next();
156 EXPECT_EQ(it.get_curr()->symbol(), 'b');
157 it.next();
158 EXPECT_EQ(it.get_curr()->symbol(), 'c');
159}
160
162{
163 root->insert_child(new Cnode('a'));
164 root->insert_child(new Cnode('c'));
165 root->insert_child(new Cnode('e'));
166
167 EXPECT_EQ(root->greater_child('b')->symbol(), 'c');
168 EXPECT_EQ(root->greater_child('d')->symbol(), 'e');
169 EXPECT_EQ(root->greater_child('e'), nullptr);
170}
171
172//==============================================================================
173// Word Insertion Tests
174//==============================================================================
175
177{
178 EXPECT_TRUE(root->insert_word("hello"));
179 EXPECT_TRUE(root->contains("hello"));
180}
181
183{
184 EXPECT_TRUE(root->insert_word("hello"));
185 EXPECT_FALSE(root->insert_word("hello")); // Already exists
186}
187
189{
190 EXPECT_TRUE(root->insert_word("hello"));
191 EXPECT_TRUE(root->insert_word("help"));
192 EXPECT_TRUE(root->insert_word("world"));
193
194 EXPECT_TRUE(root->contains("hello"));
195 EXPECT_TRUE(root->contains("help"));
196 EXPECT_TRUE(root->contains("world"));
197}
198
200{
201 EXPECT_TRUE(root->insert_word("test"));
202 EXPECT_TRUE(root->insert_word("testing"));
203 EXPECT_TRUE(root->insert_word("tester"));
204
205 EXPECT_TRUE(root->contains("test"));
206 EXPECT_TRUE(root->contains("testing"));
207 EXPECT_TRUE(root->contains("tester"));
208}
209
211{
212 EXPECT_TRUE(root->insert_word(""));
213 EXPECT_TRUE(root->contains(""));
214 EXPECT_FALSE(root->insert_word("")); // Already exists
215}
216
218{
219 EXPECT_TRUE(root->insert_word("a"));
220 EXPECT_TRUE(root->contains("a"));
221}
222
224{
225 EXPECT_TRUE(root->insert_word("testing"));
226
227 EXPECT_TRUE(root->contains("testing"));
228 EXPECT_FALSE(root->contains("test"));
229 EXPECT_FALSE(root->contains("tes"));
230 EXPECT_FALSE(root->contains("te"));
231 EXPECT_FALSE(root->contains("t"));
232}
233
235{
236 EXPECT_TRUE(root->insert_word("test"));
237 EXPECT_TRUE(root->insert_word("testing"));
238
239 EXPECT_TRUE(root->contains("test"));
240 EXPECT_TRUE(root->contains("testing"));
241}
242
244{
245 EXPECT_TRUE(root->insert_word("testing"));
246 EXPECT_TRUE(root->insert_word("test"));
247
248 EXPECT_TRUE(root->contains("test"));
249 EXPECT_TRUE(root->contains("testing"));
250}
251
252//==============================================================================
253// Search Tests
254//==============================================================================
255
257{
258 root->insert_word("hello");
259
260 EXPECT_EQ(root->search_word("world"), nullptr);
261 EXPECT_EQ(root->search_word("hel"), nullptr);
262}
263
265{
266 root->insert_word("hello");
267
268 auto result = root->search_word("hello");
269 ASSERT_NE(result, nullptr);
270 EXPECT_EQ(result->symbol(), 'o');
271}
272
274{
275 root->insert_word("hello");
276
277 EXPECT_FALSE(root->contains("world"));
278 EXPECT_FALSE(root->contains("helloworld"));
279 EXPECT_FALSE(root->contains("hell"));
280}
281
282//==============================================================================
283// Prefix Search Tests
284//==============================================================================
285
287{
288 auto [node, remaining] = root->search_prefix("");
289
290 EXPECT_EQ(node, root);
291 EXPECT_STREQ(remaining, "");
292}
293
295{
296 root->insert_word("hello");
297
298 auto [node, remaining] = root->search_prefix("hel");
299
300 EXPECT_EQ(node->symbol(), 'l');
301 EXPECT_STREQ(remaining, "");
302}
303
305{
306 root->insert_word("hello");
307
308 auto [node, remaining] = root->search_prefix("helping");
309
310 EXPECT_EQ(node->symbol(), 'l');
311 EXPECT_STREQ(remaining, "ping");
312}
313
315{
316 root->insert_word("hello");
317
318 auto [node, remaining] = root->search_prefix("world");
319
320 EXPECT_EQ(node, root);
321 EXPECT_STREQ(remaining, "world");
322}
323
324//==============================================================================
325// Words Extraction Tests
326//==============================================================================
327
329{
330 auto words = root->words();
331 EXPECT_TRUE(words.is_empty());
332}
333
335{
336 root->insert_word("hello");
337
338 auto words = root->words();
339 ASSERT_EQ(words.size(), 1);
340 EXPECT_EQ(std::string(words[0]), "hello");
341}
342
344{
345 root->insert_word("hello");
346 root->insert_word("help");
347 root->insert_word("world");
348
349 auto words = root->words();
350 ASSERT_EQ(words.size(), 3);
351
352 // Convert to vector for easier checking
353 std::vector<std::string> v;
354 words.for_each([&v](const std::string& w) { v.push_back(w); });
355 std::sort(v.begin(), v.end());
356
357 EXPECT_EQ(v[0], std::string("hello"));
358 EXPECT_EQ(v[1], std::string("help"));
359 EXPECT_EQ(v[2], std::string("world"));
360}
361
363{
364 root->insert_word("a");
365 root->insert_word("ab");
366 root->insert_word("abc");
367 root->insert_word("abcd");
368
369 auto words = root->words();
370 ASSERT_EQ(words.size(), 4);
371}
372
373//==============================================================================
374// Clone Tests
375//==============================================================================
376
378{
379 Cnode * cloned = root->clone();
380
381 EXPECT_EQ(cloned->symbol(), root->symbol());
382 EXPECT_TRUE(cloned->children().is_empty());
383
384 cloned->destroy();
385 delete cloned;
386}
387
389{
390 root->insert_word("hello");
391 root->insert_word("help");
392 root->insert_word("world");
393
394 Cnode * cloned = root->clone();
395
396 // Verify cloned tree has same words
397 EXPECT_TRUE(cloned->contains("hello"));
398 EXPECT_TRUE(cloned->contains("help"));
399 EXPECT_TRUE(cloned->contains("world"));
400
401 // Verify independence - modify original
402 root->insert_word("test");
403 EXPECT_TRUE(root->contains("test"));
404 EXPECT_FALSE(cloned->contains("test"));
405
406 cloned->destroy();
407 delete cloned;
408}
409
410//==============================================================================
411// To String Tests
412//==============================================================================
413
415{
416 std::string str = root->to_str();
417 // Root has null character
418 EXPECT_FALSE(str.empty());
419}
420
422{
423 root->insert_word("ab");
424 std::string str = root->to_str();
425
426 // Should contain 'a' and 'b' somewhere
427 EXPECT_NE(str.find('a'), std::string::npos);
428 EXPECT_NE(str.find('b'), std::string::npos);
429}
430
431//==============================================================================
432// Edge Cases
433//==============================================================================
434
436{
437 std::string longWord(20, 'a'); // Keep short to avoid stack overflow in destroy_tree
438 EXPECT_TRUE(root->insert_word(longWord));
439 EXPECT_TRUE(root->contains(longWord));
440}
441
443{
444 const int N = 50; // Reduced to avoid stack issues
445 for (int i = 0; i < N; ++i)
446 root->insert_word("w" + std::to_string(i));
447
448 for (int i = 0; i < N; ++i)
449 EXPECT_TRUE(root->contains("w" + std::to_string(i)));
450
451 auto words = root->words();
452 EXPECT_EQ(words.size(), N);
453}
454
456{
457 EXPECT_TRUE(root->insert_word("hello-world"));
458 EXPECT_TRUE(root->insert_word("test_case"));
459 EXPECT_TRUE(root->insert_word("foo.bar"));
460
461 EXPECT_TRUE(root->contains("hello-world"));
462 EXPECT_TRUE(root->contains("test_case"));
463 EXPECT_TRUE(root->contains("foo.bar"));
464}
465
467{
468 EXPECT_TRUE(root->insert_word("123"));
469 EXPECT_TRUE(root->insert_word("456"));
470
471 EXPECT_TRUE(root->contains("123"));
472 EXPECT_TRUE(root->contains("456"));
473 EXPECT_FALSE(root->contains("789"));
474}
475
476//==============================================================================
477// Count Tests
478//==============================================================================
479
481{
482 EXPECT_EQ(root->count(), 0);
483}
484
486{
487 root->insert_word("hello");
488 EXPECT_EQ(root->count(), 1);
489}
490
492{
493 root->insert_word("hello");
494 root->insert_word("help");
495 root->insert_word("world");
496 EXPECT_EQ(root->count(), 3);
497}
498
500{
501 root->insert_word("a");
502 root->insert_word("ab");
503 root->insert_word("abc");
504 EXPECT_EQ(root->count(), 3);
505}
506
507//==============================================================================
508// Words With Prefix Tests
509//==============================================================================
510
512{
513 root->insert_word("hello");
514 auto words = root->words_with_prefix("xyz");
515 EXPECT_TRUE(words.is_empty());
516}
517
519{
520 root->insert_word("hello");
521 root->insert_word("help");
522 root->insert_word("helicopter");
523 root->insert_word("world");
524
525 auto words = root->words_with_prefix("hel");
526 EXPECT_EQ(words.size(), 3);
527}
528
530{
531 root->insert_word("test");
532 root->insert_word("testing");
533 root->insert_word("tester");
534
535 const auto words = root->words_with_prefix("test");
536 EXPECT_EQ(words.size(), 3); // test, testing, tester
537}
538
540{
541 root->insert_word("apple");
542 root->insert_word("application");
543
544 auto words = root->words_with_prefix("ban");
545 EXPECT_TRUE(words.is_empty());
546}
547
549{
550 // Use a separate test without the fixture to avoid double-free
551 auto * tree = new Cnode('\0');
552 tree->insert_word("hi");
553 tree->insert_word("bye");
554
555 EXPECT_TRUE(tree->contains("hi"));
556 EXPECT_TRUE(tree->contains("bye"));
557
558 // destroy() calls destroy_tree on children which deletes them
559 tree->destroy();
560 delete tree;
561 // If we get here without crashing, destroy worked
562}
563
564//==============================================================================
565// Main
566//==============================================================================
567
568int main(int argc, char **argv)
569{
570 ::testing::InitGoogleTest(&argc, argv);
571 return RUN_ALL_TESTS();
572}
int main()
long double w
Definition btreepic.C:153
Prefix tree (Trie) node for storing character sequences.
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.
DynList< Cnode * > children() const
Return a list of all child nodes.
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:190
void TearDown() override
void SetUp() override
#define TEST(name)
#define N
Definition fib.C:294
__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:4060
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
Trie (prefix tree) implementation.
TEST_F(PrefixTreeTest, NodeConstruction)