diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ee35c7..8a9512f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,7 @@ add_executable(conflict_set_test ConflictSet.cpp ConflictSet.h) target_compile_definitions(conflict_set_test PRIVATE ENABLE_TESTS) # keep asserts for test target_compile_options(conflict_set_test PRIVATE -UNDEBUG) +target_compile_options(conflict_set_test PRIVATE -Wall -Wextra -Wpedantic -Wunreachable-code) include(CTest) add_test(NAME conflict_set_test COMMAND conflict_set_test) \ No newline at end of file diff --git a/ConflictSet.cpp b/ConflictSet.cpp index 0246588..d6b08b8 100644 --- a/ConflictSet.cpp +++ b/ConflictSet.cpp @@ -86,6 +86,77 @@ void destroyNode(Node *node) { assert(node->child[1] == nullptr); free(node); } + +// Return a pointer to the node whose key immediately follows `n`'s key (if +// `dir` is false, precedes). Return nullptr if none exists. +[[maybe_unused]] Node *next(Node *n, bool dir) { + // Traverse left spine of right child (when moving right, i.e. dir = true) + if (n->child[dir]) { + n = n->child[dir]; + while (n->child[!dir]) { + n = n->child[!dir]; + } + } else { + // Search upward for a node such that we're the left child (when moving + // right, i.e. dir = true) + while (n->parent && n == n->parent->child[dir]) { + n = n->parent; + } + n = n->parent; + } + return n; +} + +// Return a pointer to the node whose key is greatest among keys in the tree +// rooted at `n` (if dir = false, least). Return nullptr if none exists (i.e. +// `n` is null). +[[maybe_unused]] Node *extrema(Node *n, bool dir) { + if (n == nullptr) { + return nullptr; + } + while (n->child[dir] != nullptr) { + n = n->child[dir]; + } + return n; +} + +[[maybe_unused]] void debugPrintDot(FILE *file, Node *node) { + + struct DebugDotPrinter { + + explicit DebugDotPrinter(FILE *file) : file(file) {} + + void print(Node *node) { + for (int i = 0; i < 2; ++i) { + if (node->child[0] != nullptr) { + fprintf(file, " _%.*s -> _%.*s;\n", node->len, + (const char *)(node + 1), node->child[0]->len, + (const char *)(node->child[0] + 1)); + print(node->child[0]); + } else { + fprintf(file, " _%.*s -> null%d;\n", node->len, + (const char *)(node + 1), id); + fprintf(file, " null%d [shape=point];\n", id); + ++id; + } + } + } + int id = 0; + FILE *file; + }; + + fprintf(file, "digraph TreeSet {\n"); + fprintf(file, " node [fontname=\"Scientifica\"];\n"); + for (auto iter = extrema(node, false); iter != nullptr; + iter = next(iter, true)) { + fprintf(file, " _%.*s;\n", node->len, (const char *)(node + 1)); + } + if (node != nullptr) { + DebugDotPrinter{file}.print(node); + } + fprintf(file, "}\n"); +} + } // namespace struct ConflictSet::Impl { @@ -151,5 +222,8 @@ ConflictSet &ConflictSet::operator=(ConflictSet &&other) noexcept { } #ifdef ENABLE_TESTS -int main(void) { ConflictSet cs{0}; } +int main(void) { + ConflictSet::Impl cs{0}; + debugPrintDot(stdout, cs.root); +} #endif \ No newline at end of file diff --git a/ConflictSet.h b/ConflictSet.h index 2863d0d..3ea5c31 100644 --- a/ConflictSet.h +++ b/ConflictSet.h @@ -55,7 +55,9 @@ struct ConflictSet { ConflictSet(const ConflictSet &) = delete; ConflictSet &operator=(const ConflictSet &) = delete; -private: + /// @private struct Impl; + +private: Impl *impl; }; \ No newline at end of file