Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <array> 7 : #include <iterator> 8 : #include <tuple> 9 : #include <utility> 10 : #include <vector> 11 : 12 : // This implementation is from: 13 : // https://stackoverflow.com/questions/16686942/how-to-iterate-over-two-stl-like-containers-cartesian-product 14 : 15 : namespace detail { 16 : 17 : // the lambda is fully bound with one element from each of the ranges 18 : template <class Op> 19 : void insert_tuples(Op op) { 20 : // evaluating the lambda will insert the currently bound tuple 21 : op(); 22 : } 23 : 24 : // "peel off" the first range from the remaining tuple of ranges 25 : template <class Op, class InputIterator1, class... InputIterator2> 26 : void insert_tuples(Op op, std::pair<InputIterator1, InputIterator1> head, 27 : std::pair<InputIterator2, InputIterator2>... tail) { 28 : // "peel off" the elements from the first of the remaining ranges 29 : // NOTE: the recursion will effectively generate the multiple nested for-loops 30 : for (auto it = head.first; it != head.second; ++it) { 31 : // bind the first free variable in the lambda, and 32 : // keep one free variable for each of the remaining ranges 33 : detail::insert_tuples( 34 : [&op, &it](InputIterator2... elems) { op(it, elems...); }, tail...); 35 : } 36 : } 37 : 38 : } // namespace detail 39 : 40 : /*! 41 : * \ingroup UtilitiesGroup 42 : * \brief Fill the `result` iterator with the Cartesian product of a sequence of 43 : * iterators 44 : * 45 : * The `result` will hold all possible combinations of the input iterators. 46 : * The last dimension varies fastest. 47 : */ 48 : template <class OutputIterator, class... InputIterator> 49 1 : void cartesian_product(OutputIterator result, 50 : std::pair<InputIterator, InputIterator>... dimensions) { 51 : detail::insert_tuples( 52 : [&result](InputIterator... elems) { 53 : *result++ = std::make_tuple(*elems...); 54 : }, 55 : dimensions...); 56 : } 57 : 58 : /*! 59 : * \ingroup UtilitiesGroup 60 : * \brief The Cartesian product of a sequence of arrays 61 : * 62 : * Returns a `std::array` with all possible combinations of the input arrays. 63 : * The last dimension varies fastest. 64 : * 65 : * \example 66 : * Here's an example using this function to replace a nested for loop: 67 : * \snippet Test_Wedge3D.cpp cartesian_product_loop 68 : */ 69 : template <typename... Ts, size_t... Lens> 70 1 : std::array<std::tuple<Ts...>, (... * Lens)> cartesian_product( 71 : const std::array<Ts, Lens>&... dimensions) { 72 : std::array<std::tuple<Ts...>, (... * Lens)> result{}; 73 : cartesian_product(result.begin(), 74 : std::make_pair(dimensions.begin(), dimensions.end())...); 75 : return result; 76 : } 77 : 78 : /*! 79 : * \ingroup UtilitiesGroup 80 : * \brief The Cartesian product of several containers 81 : * 82 : * Returns a `std::vector` with all possible combinations of the values of the 83 : * input containers. The value of the last container varies fastest. 84 : */ 85 : template <typename... Containers> 86 1 : std::vector<std::tuple<typename Containers::value_type...>> cartesian_product( 87 : const Containers&... containers) { 88 : std::vector<std::tuple<typename Containers::value_type...>> result{}; 89 : cartesian_product( 90 : std::back_inserter(result), 91 : std::make_pair(std::begin(containers), std::end(containers))...); 92 : return result; 93 : }