diff --git a/docs/reference/adaptors.rst b/docs/reference/adaptors.rst index 9319bc41..c4c09ac8 100644 --- a/docs/reference/adaptors.rst +++ b/docs/reference/adaptors.rst @@ -212,6 +212,129 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o * - :concept:`const_iterable_sequence` - :var:`Seq` is bounded and const-iterable (passthrough) +``cartesian_power`` +^^^^^^^^^^^^^^^^^^^ + +.. function:: + template \ + requires (N >= 0) \ + auto cartesian_power(multipass_sequence auto seq) -> multipass_sequence auto; + + Returns a sequence object giving the :var:`N` th `Cartesian power `_ of the elements of :var:`seq`. It is equivalent to :var:`N` nested ``for`` loops each iterating over :var:`seq`. + + The element type of the returned sequence is an :var:`N`-tuple of the elements of :var:`seq`. The number of elements is :math:`S^N`, where :math:`S` is :expr:`count(seq)`. + + Equivalent to :expr:`cartesian_product(seq, seq, ...)`, but stores only a single copy of :var:`seq`. + + :tparam N: The cartesian power + + :param seq: A multipass sequence + + :returns: A multipass sequence yielding :var:`N`-tuples of all combinations of elements of :var:`seq` + + :example: + + .. literalinclude:: ../../example/docs/cartesian_power.cpp + :language: cpp + :dedent: + :lines: 15-25 + + :models: + + .. list-table:: + :align: left + :header-rows: 1 + + * - Concept + - When + * - :concept:`multipass_sequence` + - Always + * - :concept:`bidirectional_sequence` + - :var:`seq` is bidirectional + * - :concept:`random_access_sequence` + - :var:`seq` is random-access + * - :concept:`contiguous_sequence` + - Never + * - :concept:`bounded_sequence` + - :var:`seq` is bounded + * - :concept:`sized_sequence` + - :var:`seq` is sized + * - :concept:`infinite_sequence` + - Never + * - :concept:`read_only_sequence` + - :var:`seq` is read-only + * - :concept:`const_iterable_sequence` + - :var:`seq` is const-iterable + + :see also: + + * `std::views::cartesian_product `_ (C++23) + * :func:`cartesian_power_map` + * :func:`cartesian_product` + + +``cartesian_power_map`` +^^^^^^^^^^^^^^^^^^^^^^^ + +.. function:: + template \ + requires (N >= 0) \ + auto cartesian_power_map(multipass_sequence auto seq, auto func) -> multipass_sequence auto; + + Returns a sequence adaptor which applies the :var:`N`-ary function :var:`func` to the :var:`N` th `Cartesian power `_ of the elements of :var:`seq`. + + The total number of elements in the returned sequence is :math:`S^N`, where :math:`S` is :expr:`count(seq)`. + + Equivalent to :expr:`cartesian_power(seq, unpack(func))`, but avoids forming an intermediate tuple. + + :tparam N: The cartesian power + + :param seq: A multipass sequence + + :param func: An :var:`N`-ary function callable with the cartesian product of the elements of :var:`seq` + + :returns: A multipass sequence whose elements are the result of applying :var:`func` to :var:`N` elements of :var:`seq` + + :example: + + .. literalinclude:: ../../example/docs/cartesian_power_map.cpp + :language: cpp + :dedent: + :lines: 17-25 + + :models: + + .. list-table:: + :align: left + :header-rows: 1 + + * - Concept + - When + * - :concept:`multipass_sequence` + - Always + * - :concept:`bidirectional_sequence` + - :var:`seq` is bidirectional + * - :concept:`random_access_sequence` + - :var:`seq` is random-access + * - :concept:`contiguous_sequence` + - Never + * - :concept:`bounded_sequence` + - :var:`seq` is bounded + * - :concept:`sized_sequence` + - :var:`seq` is sized + * - :concept:`infinite_sequence` + - Never + * - :concept:`read_only_sequence` + - :var:`seq` is read-only + * - :concept:`const_iterable_sequence` + - :var:`seq` is const-iterable and :var:`func` is const-invocable + + :see also: + + * `std::views::cartesian_product `_ (C++23) + * :func:`cartesian_power` + * :func:`cartesian_product_map` + ``cartesian_product`` ^^^^^^^^^^^^^^^^^^^^^ @@ -222,6 +345,13 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o The element type of the returned sequence is a tuple of the element types of the input sequences. + :example: + + .. literalinclude:: ../../example/docs/cartesian_product.cpp + :language: cpp + :dedent: + :lines: 17-43 + :models: .. list-table:: @@ -249,6 +379,12 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o * - :concept:`const_iterable_sequence` - All passed-in sequences are const-iterable + :see also: + + * `std::views::cartesian_product `_ (C++23) + * :func:`cartesian_power` + * :func:`cartesian_product_map` + ``cartesian_product_map`` ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -257,10 +393,17 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o requires std::regular_invocable, element_t...> \ auto cartesian_product_with(Func func, Seq0 seq0, Seqs... seqs) -> sequence auto; - Given ``N`` input sequences and an ``N``-ary function :var:`func`, applies :var:`func` to the cartesian product of the elements of the input sequences. + Given ``N`` sequences and an ``N``-ary function :var:`func`, applies :var:`func` to the cartesian product of the elements of the input sequences. Equivalent to :expr:`map(cartesian_product(seq0, seqs...), unpack(func))`, but avoids forming an intermediate tuple. + :example: + + .. literalinclude:: ../../example/docs/cartesian_product_map.cpp + :language: cpp + :dedent: + :lines: 17-29 + :models: .. list-table:: @@ -288,6 +431,12 @@ You can pass a reference to a sequence into an adaptor using :func:`flux::ref` o * - :concept:`const_iterable_sequence` - All passed-in sequences are const-iterable and :var:`func` is const-invocable + :see also: + + * `std::views::cartesian_product `_ (C++23) + * :func:`cartesian_power_map` + * :func:`cartesian_product` + ``chain`` ^^^^^^^^^ diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 248db317..b4b93eee 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -23,6 +23,10 @@ add_example(example-docs-adjacent docs/adjacent.cpp) add_example(example-docs-adjacent-filter docs/adjacent_filter.cpp) add_example(example-docs-all docs/all.cpp) add_example(example-docs-any docs/any.cpp) +add_example(example-docs-cartesian-power docs/cartesian_power.cpp) +add_example(example-docs-cartesian-power-map docs/cartesian_power_map.cpp) +add_example(example-docs-cartesian-product docs/cartesian_product.cpp) +add_example(example-docs-cartesian-product-map docs/cartesian_product_map.cpp) add_example(example-docs-compare docs/compare.cpp) add_example(example-docs-contains docs/contains.cpp) add_example(example-docs-count docs/count.cpp) diff --git a/example/docs/cartesian_power.cpp b/example/docs/cartesian_power.cpp new file mode 100644 index 00000000..d4d234a0 --- /dev/null +++ b/example/docs/cartesian_power.cpp @@ -0,0 +1,26 @@ + +// Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include + +#include "assert.hpp" +#include +#include + +int main() +{ + std::string_view str = "ab"; + std::vector strings; + + // cartesian_power<3> gives us 3-tuples which we can destructure + // The total number of elements in this case is size(str)^3 = 8 + for (auto [x, y, z] : flux::cartesian_power<3>(str)) { + strings.push_back(std::string{x, y, z}); + } + + assert((strings == std::vector{"aaa", "aab", "aba", "abb", + "baa", "bab", "bba", "bbb"})); +} \ No newline at end of file diff --git a/example/docs/cartesian_power_map.cpp b/example/docs/cartesian_power_map.cpp new file mode 100644 index 00000000..7829f74d --- /dev/null +++ b/example/docs/cartesian_power_map.cpp @@ -0,0 +1,26 @@ + +// Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include + +#include "assert.hpp" +#include +#include + +using namespace std::string_view_literals; + +int main() +{ + std::array nums{1, 2, 3}; + + // cartesian_power_map takes a sequence and a callable of N arguments + // Here we are using N=2 and the binary function object std::plus + auto sums = flux::cartesian_power_map<2>(nums, std::plus{}).to>(); + + assert((sums == std::vector{1 + 1, 1 + 2, 1 + 3, + 2 + 1, 2 + 2, 2 + 3, + 3 + 1, 3 + 2, 3 + 3})); +} \ No newline at end of file diff --git a/example/docs/cartesian_product.cpp b/example/docs/cartesian_product.cpp new file mode 100644 index 00000000..23ba7e04 --- /dev/null +++ b/example/docs/cartesian_product.cpp @@ -0,0 +1,44 @@ + +// Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include + +#include "assert.hpp" +#include +#include + +using namespace std::string_view_literals; + +int main() +{ + std::string_view str = "abc"; + std::array nums = {1, 2, 3}; + + // flux::cartesian_product(str, nums) yields all combinations of elements from + // the two sequences as a tuple + auto pairs = flux::cartesian_product(str, nums).to(); + + using P = std::tuple; + + assert((pairs == std::vector{P{'a', 1}, P{'a', 2}, P{'a', 3}, + P{'b', 1}, P{'b', 2}, P{'b', 3}, + P{'c', 1}, P{'c', 2}, P{'c', 3}})); + + // cartesian_product can take an arbitrary number of sequence arguments + // The number of elements is the product of the sizes of the input sequences + // It is bidirectional and random-access if all the input sequences + // satisfy these concepts + auto seq = flux::cartesian_product("xy"sv, + std::array{1.0, 2.0}, + std::vector{111, 222}).reverse(); + + using T = std::tuple; + + assert(flux::equal(seq, std::vector{{'y', 2.0, 222}, {'y', 2.0, 111}, + {'y', 1.0, 222}, {'y', 1.0, 111}, + {'x', 2.0, 222}, {'x', 2.0, 111}, + {'x', 1.0, 222}, {'x', 1.0, 111}})); +} \ No newline at end of file diff --git a/example/docs/cartesian_product_map.cpp b/example/docs/cartesian_product_map.cpp new file mode 100644 index 00000000..c5889724 --- /dev/null +++ b/example/docs/cartesian_product_map.cpp @@ -0,0 +1,30 @@ + +// Copyright (c) 2024 Tristan Brindle (tcbrindle at gmail dot com) +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +#include + +#include "assert.hpp" +#include +#include + +int main() +{ + std::array big = {10000L, 20000L}; + std::array medium = {100.0f, 200.0f}; + std::array small = {1, 2}; + + auto add3 = [](long a, float b, int c) { return double(a) + b + c; }; + + // Note that because cartesian_product_map takes a variadic number of + // sequences, the function argument goes first + auto vec = flux::cartesian_product_map(add3, big, medium, small) + .to(); + + assert((vec == std::vector{10101.0, 10102.0, + 10201.0, 10202.0, + 20101.0, 20102.0, + 20201.0, 20202.0})); +} \ No newline at end of file diff --git a/include/flux/op/cartesian_power.hpp b/include/flux/op/cartesian_power.hpp index 510cb7ac..6cff5adc 100644 --- a/include/flux/op/cartesian_power.hpp +++ b/include/flux/op/cartesian_power.hpp @@ -62,8 +62,9 @@ struct cartesian_power_fn { } // end namespace detail FLUX_EXPORT -template -inline constexpr auto cartesian_power = detail::cartesian_power_fn{}; +template + requires (N >= 0) +inline constexpr auto cartesian_power = detail::cartesian_power_fn{}; } // end namespace flux