From a9e3fdfd1c7b694a7bc5cf1164812a97388d515b Mon Sep 17 00:00:00 2001 From: Joshua Moerman Date: Fri, 10 Jul 2015 09:31:39 +0200 Subject: [PATCH] Simplified the lca implementation (now without state) --- CMakeLists.txt | 2 +- lib/separating_family.cpp | 29 ++++++----------- lib/splitting_tree.cpp | 8 ----- lib/splitting_tree.hpp | 66 ++++++++++++++++++++++++++++----------- src/CMakeLists.txt | 12 +++---- 5 files changed, 63 insertions(+), 54 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18fd895..64247ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ project(Yannakakis) cmake_minimum_required(VERSION 2.8) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") find_package(Boost REQUIRED COMPONENTS iostreams program_options filesystem system serialization) include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) diff --git a/lib/separating_family.cpp b/lib/separating_family.cpp index 0c1f845..7fd5950 100644 --- a/lib/separating_family.cpp +++ b/lib/separating_family.cpp @@ -42,29 +42,18 @@ separating_family create_separating_family(const adaptive_distinguishing_sequenc const auto s = p.second; states[s] = true; } - const auto root - = lca(separating_sequences, [&states](state z) -> bool { return states[z]; }); + const auto roots + = multi_lca(separating_sequences, [&states](state z) -> bool { return states[z]; }); - vector stack_of_words; - const function recursor = [&](splitting_tree const & n) { - if (n.children.empty()) { - for (state s : n.states) { - if (states[s]) { - for (auto const & w : stack_of_words) { - suffixes[s].insert(w); - } - } + // NOTE: this is slightly less efficient than doing the same thing inline in lca(...) + // but I was to lazy to write a dfs again + for (const splitting_tree & n : roots) { + for (state s : n.states) { + if (states[s]) { + suffixes[s].insert(n.separator); } - } else { - if (n.mark > 1) stack_of_words.push_back(n.separator); - for (auto const & c : n.children) { - recursor(c); - } - if (n.mark > 1) stack_of_words.pop_back(); } - }; - - recursor(root); + } // Finalize the suffixes for (auto && p : node.CI) { diff --git a/lib/splitting_tree.cpp b/lib/splitting_tree.cpp index c3e48d8..657e547 100644 --- a/lib/splitting_tree.cpp +++ b/lib/splitting_tree.cpp @@ -15,14 +15,6 @@ splitting_tree::splitting_tree(size_t N, size_t d) : states(N), depth(d) { iota(begin(states), end(states), 0); } -splitting_tree & lca_impl2(splitting_tree & node) { - if (node.mark > 1) return node; - for (auto && c : node.children) { - if (c.mark > 0) return lca_impl2(c); - } - return node; // this is a leaf -} - result create_splitting_tree(const mealy & g, options opt) { const auto N = g.graph_size; const auto P = g.input_size; diff --git a/lib/splitting_tree.hpp b/lib/splitting_tree.hpp index f7aee1a..e34e21d 100644 --- a/lib/splitting_tree.hpp +++ b/lib/splitting_tree.hpp @@ -12,36 +12,66 @@ struct splitting_tree { std::vector children; word separator; size_t depth = 0; - mutable int mark = 0; // used for some algorithms... }; -template void lca_impl1(splitting_tree const & node, Fun && f) { - node.mark = 0; - if (!node.children.empty()) { - for (auto && c : node.children) { - lca_impl1(c, f); - if (c.mark) node.mark++; +/// \brief the generic lca implementation. +/// It uses \p store to store the relevant nodes (in some bottom up order), the last store is the +/// actual lowest common ancestor (but the other might be relevant as well). The function \p f is +/// the predicate on the states (returns true for the states we want to compute the lca of). +template +size_t lca_impl(splitting_tree const & node, Fun && f, Store && store) { + static_assert(std::is_same::value, "f should return a bool"); + if (node.children.empty()) { + // if it is a leaf, we search for the states + // if it contains a state, return this leaf + for (auto s : node.states) { + if (f(s)) { + store(node); + return 1; + } } + // did not contain the leaf => nullptr + return 0; } else { - for (auto && s : node.states) { - if (f(s)) node.mark++; + // otherwise, check our children. If there is a single one giving a node + // we return this (it's the lca), if more children return a non-nil + // node, then we are the lca + size_t count = 0; + for (auto & c : node.children) { + auto inner_count = lca_impl(c, f, store); + if (inner_count > 0) count++; } - } -} -splitting_tree & lca_impl2(splitting_tree & node); + if (count >= 2) { + store(node); + } + + return count; + } + throw std::logic_error("unreachable code"); +} /// \brief Find the lowest common ancestor of elements on which \p f returns true. template splitting_tree & lca(splitting_tree & root, Fun && f) { - static_assert(std::is_same::value, "f should return a bool"); - lca_impl1(root, f); - return lca_impl2(root); + splitting_tree const * store = nullptr; + lca_impl(root, f, [&store](splitting_tree const & node) { store = &node; }); + return const_cast(*store); // NOTE: this const_cast is safe } template const splitting_tree & lca(const splitting_tree & root, Fun && f) { - static_assert(std::is_same::value, "f should return a bool"); - lca_impl1(root, f); - return lca_impl2(const_cast(root)); + splitting_tree const * store = nullptr; + lca_impl(root, f, [&store](splitting_tree const & node) { store = &node; }); + return *store; +} + +/// \brief Find "all" lca's of elements on which \p f returns true. +/// This can be used to collect all the separating sequences for the subset of states. +template +std::vector> multi_lca(const splitting_tree & root, + Fun && f) { + std::vector> ret; + lca_impl(root, f, [&ret](splitting_tree const & node) { ret.emplace_back(node); }); + return ret; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8e6c5f2..919828e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,11 +3,9 @@ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) file(GLOB sources "*.cpp") -#foreach(source ${sources}) -# get_filename_component(exec ${source} NAME_WE) -# add_executable(${exec} ${source}) -# target_link_libraries(${exec} common ${libs}) -#endforeach() +foreach(source ${sources}) + get_filename_component(exec ${source} NAME_WE) + add_executable(${exec} ${source}) + target_link_libraries(${exec} common ${libs}) +endforeach() -add_executable(main main.cpp) -target_link_libraries(main common ${libs})