diff --git a/pysdsl/__init__.cpp b/pysdsl/__init__.cpp index 4376ce2..8f31801 100644 --- a/pysdsl/__init__.cpp +++ b/pysdsl/__init__.cpp @@ -16,6 +16,7 @@ #include "types/intvector.hpp" #include "types/suffixarray.hpp" #include "types/wavelet.hpp" +#include "types/rmq.hpp" namespace py = pybind11; @@ -43,6 +44,11 @@ PYBIND11_MODULE(pysdsl, m) auto csa_classes = add_csa(m); + auto rmq_classes_tuple = add_rmq_classes(m, iv_classes_as_params); + auto& rmq_sparse_tables = std::get<0>(rmq_classes_tuple); + auto& rmq_sada = std::get<1>(rmq_classes_tuple); + auto& rmq_sct = std::get<2>(rmq_classes_tuple); + for_each_in_tuple(iv_classes, make_inits_many_functor(iv_classes)); for_each_in_tuple(iv_classes, make_inits_many_functor(enc_classes)); for_each_in_tuple(iv_classes, diff --git a/pysdsl/docstrings.hpp b/pysdsl/docstrings.hpp index 5995f37..fa87b74 100644 --- a/pysdsl/docstrings.hpp +++ b/pysdsl/docstrings.hpp @@ -258,3 +258,18 @@ const char* doc_csa_wt( "A class for the Compressed Suffix Array (CSA) based on a Wavelet Tree " "(WT) of the Burrow Wheeler Transform of the original text." ); + +const char* doc_rmq_sada( + "A class to support range minimum or range maximum queries on a random access container " + "in constant time and 4n+o(n) bits space." +); + +const char* doc_rmq_sparse_table( + "A class to support range minimum or range maximum queries on a random access container " + "in constant time and O( n log^2(n) ) bits space." +); + +const char* doc_rmq_sct( + "A class to support range minimum or range maximum queries on a random access container " + "in constant time and 2n+o(n) bits space." +); diff --git a/pysdsl/types/rmq.hpp b/pysdsl/types/rmq.hpp new file mode 100644 index 0000000..724baac --- /dev/null +++ b/pysdsl/types/rmq.hpp @@ -0,0 +1,236 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include + +#include "operations/sizes.hpp" +#include "operations/iteration.hpp" +#include "util/tupletricks.hpp" +#include "docstrings.hpp" +#include "io.hpp" +#include "calc.hpp" + + + +namespace detail { +// adds constructors of t_rac... containers +template +typename std::enable_if::type add_rac_constructor(const PybindClass&) {} + +template +void add_rac_constructor(PybindClass& cls) +{ + cls.def(py::init([](const t_rac_head* rac) { + return typename PybindClass::type(rac); + })); + add_rac_constructor(cls); +} + +} // namespace details + + +// containers names for sparse +namespace RAC_names { + constexpr char INT_VECTOR_NAME[] = "IntVector", + INT16_VECTOR_NAME[] = "Int16Vector"; +} + + +struct add_rmq_sparse_table_functor { + py::module& m; + const char* doc; + + constexpr add_rmq_sparse_table_functor(py::module& m, const char* doc = nullptr) + : m(m), doc(doc) {} + + template + decltype(auto) operator()(std::tuple, + std::integral_constant>) + { + using Table = sdsl::rmq_support_sparse_table; + using size_type = typename Table::size_type; + + std::string name = + std::string("Range") + (t_min ? "Min" : "Max") + "QuerySparseTable_for_" + rac_name; + + auto cls = py::class_(m, name.c_str()) + .def(py::init([](const t_rac* rac) {return Table(rac);})) + .def("set_vector", &Table::set_vector, + "Sets a vector rmq is processed on.") + .def("__call__", + (size_type (Table::*)(size_type, size_type) const)& Table::operator(), + (std::string("Returns an index of the ") + (t_min ? "minimal" : "maximal") + + " value on the segment [l,r].").c_str()); + + add_sizes(cls); + add_description(cls); + + // load takes two params + // add_serialization(cls); + + cls.doc() = doc; + + std::string key = std::string(t_min ? "Min" : "Max") + "_in_" + rac_name; + + m.attr("rmq_sparse_tables").attr("__setitem__")(key, cls); + m.attr((std::string("all_range_") + (t_min ? "min" : "max") + "_classes").c_str()) + .attr("append")(cls); + + return cls; + } +}; + + +struct add_rmq_sada_functor { + py::module& m; + const char* doc; + + constexpr add_rmq_sada_functor(py::module& m, const char* doc = nullptr) + : m(m), doc(doc) {} + + + template + decltype(auto) operator()(std::tuple, + std::integral_constant>) + { + using RMQClass = typename std::conditional, + typename sdsl::range_maximum_support_sada<>::type>::type; + using size_type = typename RMQClass::size_type; + + std::string name = + std::string("Range") + (t_min ? "Min" : "Max") + "QuerySuccintSada"; + + auto cls = py::class_(m, name.c_str()) + .def(py::init()) + .def("__call__", + (size_type (RMQClass::*)(size_type, size_type) const)& RMQClass::operator(), + (std::string("Returns an index of the ") + (t_min ? "minimal" : "maximal") + + " value on the segment [l,r].").c_str());; + + detail::add_rac_constructor(cls); + + add_sizes(cls); + add_description(cls); + add_serialization(cls); + + cls.doc() = doc; + + m.attr("rmq_sada").attr("__setitem__")(t_min ? "Min" : "Max", cls); + m.attr((std::string("all_range_") + (t_min ? "min" : "max") + "_classes").c_str()) + .attr("append")(cls); + + return cls; + } +}; + + +struct add_rmq_sct_functor { + py::module& m; + const char* doc; + + constexpr add_rmq_sct_functor(py::module& m, const char* doc = nullptr) + : m(m), doc(doc) {} + + + template + decltype(auto) operator()(std::tuple, + std::integral_constant>) + { + using RMQClass = typename std::conditional, + typename sdsl::range_maximum_sct<>::type>::type; + using size_type = typename RMQClass::size_type; + + std::string name = + std::string("Range") + (t_min ? "Min" : "Max") + "QuerySuccintSct"; + + auto cls = py::class_(m, name.c_str()) + .def(py::init()) + .def("__call__", + (size_type (RMQClass::*)(size_type, size_type) const)& RMQClass::operator(), + (std::string("Returns an index of the ") + (t_min ? "minimal" : "maximal") + + " value on the segment [l,r].").c_str()); + + + detail::add_rac_constructor(cls); + + add_sizes(cls); + add_description(cls); + add_serialization(cls); + + cls.doc() = doc; + + m.attr("rmq_sct").attr("__setitem__")(t_min ? "Min" : "Max", cls); + m.attr((std::string("all_range_") + (t_min ? "min" : "max") + "_classes").c_str()) + .attr("append")(cls); + + return cls; + } +}; + + +// generalized (constants -> typenames) template for usage with GeneralSubsetFunctor +template +using general_rmq_sparse_table = + py::class_>; + +template +using general_rmq_sada = py::class_< + typename std::conditional, + typename sdsl::range_maximum_support_sada<>::type>::type>; + +template +using general_rmq_sct = py::class_< + typename std::conditional, + typename sdsl::range_maximum_sct<>::type>::type>; + + +template +static +auto make_rmq_params(const std::tuple&...>& params) { + return std::tuple, std::integral_constant>, + std::tuple, std::integral_constant>>(); +} + + +template +inline auto add_rmq_classes(py::module& m, const std::tuple&...>& params) +{ + m.attr("rmq_sparse_tables") = py::dict(); + m.attr("rmq_sada") = py::dict(); + m.attr("rmq_sct") = py::dict(); + m.attr("all_range_min_classes") = py::list(); + m.attr("all_range_max_classes") = py::list(); + + using rmq_support_sparse_table_params = std::tuple< + std::tuple, + std::integral_constant, + std::integral_constant>, + std::tuple, + std::integral_constant, + std::integral_constant>, + std::tuple, + std::integral_constant, + std::integral_constant>, + std::tuple, + std::integral_constant, + std::integral_constant> + >; + + auto rmq_sparse_tables = for_each_in_tuple(rmq_support_sparse_table_params(), + add_rmq_sparse_table_functor(m, doc_rmq_sparse_table)); + auto rmq_sada_classes = for_each_in_tuple(make_rmq_params(params), + add_rmq_sada_functor(m, doc_rmq_sada)); + auto rmq_sct_classes = for_each_in_tuple(make_rmq_params(params), + add_rmq_sct_functor(m, doc_rmq_sct)); + + return std::make_tuple(rmq_sparse_tables, rmq_sada_classes, rmq_sct_classes); +} diff --git a/pysdsl/util/tupletricks.hpp b/pysdsl/util/tupletricks.hpp index a1c030f..283d66a 100644 --- a/pysdsl/util/tupletricks.hpp +++ b/pysdsl/util/tupletricks.hpp @@ -91,4 +91,3 @@ template