From 2c5e0865dc613e900dff1ad25f6991d71a6d99c6 Mon Sep 17 00:00:00 2001 From: Maksim Andrianov Date: Tue, 29 Oct 2019 16:05:57 +0300 Subject: [PATCH] [indexer] Added methods for tree node, added class Forest. --- indexer/complex/tree_node.hpp | 153 ++++++++++++++++++++++ indexer/indexer_tests/tree_node_tests.cpp | 119 +++++++++++++++++ 2 files changed, 272 insertions(+) diff --git a/indexer/complex/tree_node.hpp b/indexer/complex/tree_node.hpp index a55aca74fb..8137a44b7c 100644 --- a/indexer/complex/tree_node.hpp +++ b/indexer/complex/tree_node.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -126,4 +127,156 @@ size_t Size(types::Ptr const & node) PreOrderVisit(node, [&](auto const &) { ++size; }); return size; } + +template +decltype(auto) GetRoot(types::Ptr node) +{ + while (node->HasParent()) + node = node->GetParent(); + return node; +} + +template +decltype(auto) GetPath(types::Ptr node) +{ + types::PtrList path; + while (node) + { + path.emplace_back(node); + node = node->GetParent(); + } + return path; +} + +template +types::Ptr::type> TransformToTree( + types::Ptr const & node, Fn && fn) +{ + auto n = MakeTreeNode(fn(node->GetData())); + for (auto const & ch : node->GetChildren()) + n->AddChild(TransformToTree(ch, fn)); + return n; +} + +template +bool IsEqual(types::Ptr const & lhs, types::Ptr const & rhs) +{ + if (lhs->GetData() != rhs->GetData()) + return false; + + auto const & lhsCh = lhs->GetChildren(); + auto const & rhsCh = rhs->GetChildren(); + if (lhsCh.size() != rhsCh.size()) + return false; + + for (size_t i = 0; i < lhsCh.size(); ++i) + { + if (!IsEqual(lhsCh[i], rhsCh[i])) + return false; + } + return true; +} + +template +size_t CountIf(types::Ptr const & node, Fn && fn) +{ + size_t count = 0; + PreOrderVisit(node, [&](auto const & node) { + if (fn(node->GetData())) + ++count; + }); + return count; +} + +template +void Print(types::Ptr const & node, std::ostream & stream, + std::string prefix = "", bool isTail = true) +{ + stream << prefix; + if (isTail) + { + stream << "└───"; + prefix += " "; + } + else + { + stream << "├───"; + prefix += "│ "; + } + auto str = DebugPrint(node->GetData()); + std::replace(std::begin(str), std::end(str), '\n', '|'); + stream << str << '\n'; + auto const & children = node->GetChildren(); + size_t size = children.size(); + for (size_t i = 0; i < size; ++i) + Print(children[i], stream, prefix, i == size - 1 /* isTail */); +} + +template +std::string DebugPrint(types::Ptr const & node) +{ + std::stringstream stream; + Print(node, stream); + return stream.str(); +} + +template +class Forest +{ +public: + bool operator==(Forest const & other) const + { + auto const size = Size(); + if (size != other.Size()) + return false; + + for (size_t i = 0; i < size; ++i) + { + if (!IsEqual(m_trees[i], other.m_trees[i])) + return false; + } + return true; + } + + bool operator!=(Forest const & other) const { return !(*this == other); } + + void Append(types::Ptr const & tree) { m_trees.emplace_back(tree); } + + size_t Size() const { return m_trees.size(); } + + template + void ForEachTree(Fn && fn) const + { + base::ControlFlowWrapper wrapper(std::forward(fn)); + for (auto const & tree : m_trees) + { + if (wrapper(tree) == base::ControlFlow::Break) + return; + } + } + +private: + types::PtrList m_trees; +}; + +template +decltype(auto) FindIf(Forest const & forest, Fn && fn) +{ + types::Ptr res = nullptr; + forest.ForEachTree([&](auto const & tree) { + res = FindIf(tree, fn); + return res ? base::ControlFlow::Break : base::ControlFlow::Continue; + }); + return res; +} + +template +std::string DebugPrint(Forest const & forest) +{ + std::stringstream stream; + forest.ForEachTree([&](auto const & tree) { + stream << DebugPrint(tree) << '\n'; + }); + return stream.str(); +} } // namespace tree_node diff --git a/indexer/indexer_tests/tree_node_tests.cpp b/indexer/indexer_tests/tree_node_tests.cpp index 37051191cd..db65ec5c32 100644 --- a/indexer/indexer_tests/tree_node_tests.cpp +++ b/indexer/indexer_tests/tree_node_tests.cpp @@ -6,6 +6,7 @@ #include #include +#include #include namespace @@ -81,4 +82,122 @@ UNIT_TEST(TreeNode_GetDepth) depth = tree_node::GetDepth(tree_node::FindIf(tree, [](auto const & d) { return d == 36; })); TEST_EQUAL(depth, 3, ()); } + +UNIT_TEST(TreeNode_GetRoot) +{ + auto const tree = MakeTree(); + auto const node22 = tree_node::FindIf(tree, [](auto const & d) { return d == 22; }); + auto const root = tree_node::GetRoot(node22); + TEST(tree, ()); + TEST_EQUAL(tree->GetData(), root->GetData(), ()); +} + +UNIT_TEST(TreeNode_GetPath) +{ + auto const tree = MakeTree(); + auto const node33 = tree_node::FindIf(tree, [](auto const & d) { return d == 33; }); + auto const path = tree_node::GetPath(node33); + tree_node::types::PtrList expected = { + tree_node::FindIf(tree, [](auto const & d) { return d == 33; }), + tree_node::FindIf(tree, [](auto const & d) { return d == 21; }), + tree_node::FindIf(tree, [](auto const & d) { return d == 1; }) + }; + TEST_EQUAL(path, expected, ()); +} + +UNIT_TEST(TreeNode_IsEqual) +{ + auto tree1 = tree_node::MakeTreeNode(1); + auto node11 = tree_node::MakeTreeNode(21); + tree_node::Link(tree_node::MakeTreeNode(31), node11); + tree_node::Link(tree_node::MakeTreeNode(32), node11); + tree_node::Link(tree_node::MakeTreeNode(33), node11); + tree_node::Link(node11, tree1); + + TEST(tree_node::IsEqual(tree1, tree1), ()); + TEST(!tree_node::IsEqual(tree1, node11), ()); +} + +UNIT_TEST(TreeNode_TransformToTree) +{ + auto const tree = MakeTree(); + auto const newTree = tree_node::TransformToTree(tree, [](auto const & data) { + return std::to_string(data); + }); + + auto expected = tree_node::MakeTreeNode("1"); + auto node11 = tree_node::MakeTreeNode("21"); + tree_node::Link(tree_node::MakeTreeNode("31"), node11); + tree_node::Link(tree_node::MakeTreeNode("32"), node11); + tree_node::Link(tree_node::MakeTreeNode("33"), node11); + tree_node::Link(node11, expected); + tree_node::Link(tree_node::MakeTreeNode("34"), expected); + + auto tree2 = tree_node::MakeTreeNode("22"); + tree_node::Link(tree_node::MakeTreeNode("35"), tree2); + tree_node::Link(tree_node::MakeTreeNode("36"), tree2); + tree_node::Link(tree2, expected); + + TEST(tree_node::IsEqual(newTree, expected), ()); +} + +UNIT_TEST(TreeNode_CountIf) +{ + auto const tree = MakeTree(); + auto const count = tree_node::CountIf(tree, [](auto const & data) { return data >= 30; }); + TEST_EQUAL(count, 6, ()); +} + +UNIT_TEST(TreeNode_DebugPrint) +{ + auto const tree = MakeTree(); + LOG(LINFO, (tree_node::DebugPrint(tree))); +} + +UNIT_TEST(TreeNode_Forest) +{ + tree_node::Forest forest; + std::set> s = { + MakeTree(), + tree_node::MakeTreeNode(10) + }; + for (auto const & tree : s) + forest.Append(tree); + + TEST_EQUAL(forest.Size(), 2, ()); + forest.ForEachTree([&](auto const & tree) { + auto const count = s.erase(tree); + TEST_EQUAL(count, 1, ()); + }); + TEST(s.empty(), ()); + + auto const copy = forest; + TEST_EQUAL(copy, forest, ()); + + tree_node::Forest empty; + TEST_NOT_EQUAL(empty, forest, ()); +} + +UNIT_TEST(TreeNode_ForestFindIf) +{ + tree_node::Forest forest; + forest.Append(MakeTree()); + forest.Append(tree_node::MakeTreeNode(100)); + auto const node33 = tree_node::FindIf(forest, [](auto const & d) { return d == 33; }); + TEST(node33, ()); + TEST_EQUAL(node33->GetData(), 33, ()); + + auto const node100 = tree_node::FindIf(forest, [](auto const & d) { return d == 100; }); + TEST(node100, ()); + TEST_EQUAL(node100->GetData(), 100, ()); +} + +UNIT_TEST(TreeNode_ForestDebugPrint) +{ + tree_node::Forest forest; + forest.Append(MakeTree()); + forest.Append(tree_node::MakeTreeNode(100)); + forest.Append(tree_node::MakeTreeNode(200)); + LOG(LINFO, (tree_node::DebugPrint(forest))); +} } // namespace