#pragma once #include "mealy.hpp" /* * A splitting tree as defined in Lee & Yannakakis. The structure is also * called a derivation tree in Knuutila. Both the classical Hopcroft algorithm * and the Lee & Yannakakis algorithm produce splitting trees. */ struct splitting_tree { splitting_tree(size_t N, size_t depth); std::vector states; std::vector children; std::vector seperator; 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++; } } else { for(auto && s : node.states){ if(f(s)) node.mark++; } } } splitting_tree & lca_impl2(splitting_tree & node); 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); } 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)); } /* * The algorithm to create a splitting tree can be altered in some ways. This * struct provides options to the algorithm. There are two common setups. */ struct options { bool check_validity = true; bool cache_succesors = true; }; constexpr options lee_yannakakis_style{true, true}; constexpr options hopcroft_style{false, false}; /* * The algorithm to create a splitting tree also produces some other useful * data. This struct captures exactly that. */ struct result { result(size_t N) : root(N, 0) , successor_cache() , is_complete(true) {} // The splitting tree as described in Lee & Yannakakis splitting_tree root; // Encodes f_u : depth -> state -> state, where only the depth of u is of importance std::vector> successor_cache; // false <-> no adaptive distinguishing sequence bool is_complete; }; result create_splitting_tree(mealy const & m, options opt);