diff --git a/c++/itertools/itertools.hpp b/c++/itertools/itertools.hpp index 66448bb..a916f9d 100644 --- a/c++/itertools/itertools.hpp +++ b/c++/itertools/itertools.hpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace itertools { @@ -199,6 +200,45 @@ namespace itertools { decltype(auto) dereference() const { return tuple_map_impl(std::index_sequence_for{}); } }; + /********************* Product Iterator with homegenous type but varying number of arg ********************/ + + // same logic as before, but at runtime + template + struct prod_iter_vec : iterator_facade, std::vector::value_type>> { + + std::vector its_begin, its_end; + std::vector its = its_begin; + + prod_iter_vec(std::vector its_begin, std::vector its_end) : its_begin(std::move(its_begin)), its_end(std::move(its_end)) {} + + void increment() { + for (int N = 0; N < its.size() - 1; ++N) { + ++its[N]; + if (its[N] != its_end[N]) return; + its[N] = its_begin[N]; + } + ++its[its.size() - 1]; + } + + bool equal(prod_iter_vec const &other) const { return (its == other.its); } + + template + bool equal(sentinel_t const &s) const { + return (s.it == its.back()); + } + + template + bool operator==(sentinel_t const &s) const { + return equal(s); + } + + std::vector dereference() const { + std::vector r(its.size()); + for (int i = 0; i < its.size(); ++i) r[i] = *its[i]; + return r; + } + }; + /********************* Stride Iterator ********************/ template @@ -331,6 +371,39 @@ namespace itertools { // --------------------------------------------- + template + struct multiplied_vec { + std::vector tu; // T can be a ref. + + using iterator = prod_iter_vec()))>; + using const_iterator = prod_iter_vec()))>; + + multiplied_vec(std::vector const &ranges) : tu{ranges} {} + + iterator begin() noexcept { + std::vector _b(tu.size()), _e(tu.size()); + std::transform(tu.begin(), tu.end(), _b.begin(), [](auto &&x) { return std::begin(x); }); + std::transform(tu.begin(), tu.end(), _e.begin(), [](auto &&x) { return std::end(x); }); + return iterator{_b, _e}; + } + + const_iterator cbegin() noexcept { + std::vector _b(tu.size()), _e(tu.size()); + std::transform(tu.begin(), tu.end(), _b.begin(), [](auto &&x) { return std::cbegin(x); }); + std::transform(tu.begin(), tu.end(), _e.begin(), [](auto &&x) { return std::cend(x); }); + return const_iterator{_b, _e}; + } + + auto end() noexcept { return make_sentinel(std::end(tu.back())); } + auto cend() const noexcept { return make_sentinel(std::cend(tu.back())); } + auto end() const noexcept { return cend(); } + }; + + template + multiplied_vec(T &&) -> multiplied_vec>; + + // --------------------------------------------- + template struct sliced { T x; @@ -458,6 +531,16 @@ namespace itertools { return {std::forward(ranges)...}; } + /** + * Lazy-product of multiple ranges. Same as product, but with an uniform type, + * but a number of ranges known at run time. + * + */ + template + details::multiplied_vec product_vec(std::vector const &ranges) { + return {ranges}; + } + /** * Lazy-slice a range. * This function returns itself a slice of the initial range diff --git a/test/c++/itertools.cpp b/test/c++/itertools.cpp index f91ace5..b0de337 100644 --- a/test/c++/itertools.cpp +++ b/test/c++/itertools.cpp @@ -105,6 +105,27 @@ TEST(Itertools, Product) { EXPECT_EQ(V4, std::vector(4, 1 * 2 * 3 * 4)); } +TEST(Itertools, ProductVec) { + + std::vector V1{0, 1, 2}; + std::vector V2{0, 1, 2}; + std::vector> W{V1, V2}; + + std::vector> res, check{{0, 0}, {1, 0}, {2, 0}, {0, 1}, {1, 1}, {2, 1}, {0, 2}, {1, 2}, {2, 2}}; + for (auto vec : product_vec(W)) { + res.push_back(vec); + std::cout << "[" << vec[0] << "," << vec[1] << "]\n"; + } + + EXPECT_EQ(res.size(), check.size()); + for (int i : range(res.size())) { + EXPECT_EQ(res[i].size(), check[i].size()); + for (int j : range(res[i].size())) { EXPECT_EQ(res[i][j], check[i][j]); } + } + + // make a real check +} + TEST(Itertools, Slice) { for (long N : range(1, 6)) {