Skip to content

Commit 4aefd4b

Browse files
committed
new algorithms cartesian_product
1 parent ae3dbba commit 4aefd4b

File tree

7 files changed

+161
-0
lines changed

7 files changed

+161
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ if(BUILD_TESTING AND ${KDALGORITHMS_BUILD_TEST})
6868
src/kdalgorithms_bits/zip.h
6969
src/kdalgorithms_bits/tuple_utils.h
7070
src/kdalgorithms_bits/invoke.h
71+
src/kdalgorithms_bits/cartesian_product.h
7172

7273
tests/tst_kdalgorithms.cpp
7374
tests/tst_constraints.cpp

Documentation/algorithms.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Other
4141
- <a href="#partitioned">partitioned</a>
4242
- <a href="#multi_partitioned">multi_partitioned</a>
4343
- <a href="#zip">zip</a>
44+
- <a href="#cartesian_product">product</a>
4445

4546

4647

@@ -953,3 +954,34 @@ auto result = kdalgorithms::zip<std::deque>(v1, v2);
953954
```
954955

955956
See [boost::compine](https://www.boost.org/doc/libs/1_81_0/libs/range/doc/html/range/reference/utilities/combine.html) for similar algorithm in boost, and [std::ranges::views::zip](https://en.cppreference.com/w/cpp/ranges/zip_view) for the C++23 version.
957+
958+
959+
<a name="cartesian_product">cartesian_product</a>
960+
-----------------------------
961+
cartesian_product takes a number of containers and returns a cartesian product of the items.
962+
963+
```
964+
const std::array<char, 2> x = {'A', 'B'};
965+
const std::vector<int> y = {1, 2, 3};
966+
const std::list<std::string> z = {"α", "β", "γ", "δ"};
967+
968+
auto result = kdalgorithms::product(x, y, z);
969+
// result is:
970+
// std::vector<std::tuple<char, int, std::string>>{
971+
// {'A', 1, "α"}, {'A', 1, "β"}, {'A', 1, "γ"}, {'A', 1, "δ"},
972+
// {'A', 2, "α"}, {'A', 2, "β"}, {'A', 2, "γ"}, {'A', 2, "δ"},
973+
// {'A', 3, "α"}, {'A', 3, "β"}, {'A', 3, "γ"}, {'A', 3, "δ"},
974+
// {'B', 1, "α"}, {'B', 1, "β"}, {'B', 1, "γ"}, {'B', 1, "δ"},
975+
// {'B', 2, "α"}, {'B', 2, "β"}, {'B', 2, "γ"}, {'B', 2, "δ"},
976+
// {'B', 3, "α"}, {'B', 3, "β"}, {'B', 3, "γ"}, {'B', 3, "δ"},
977+
// };
978+
```
979+
980+
It is also possible to specify the return type:
981+
```
982+
auto result = kdalgorithms::cartesian_product<std::deque>( x, y, z );
983+
// result is:
984+
// std::deque<std::tuple<char, int, std::string>>{ ... }
985+
```
986+
987+
See [std::cartesian_product](https://en.cppreference.com/w/cpp/ranges/cartesian_product_view)

src/kdalgorithms.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#pragma once
1212

13+
#include "kdalgorithms_bits/cartesian_product.h"
1314
#include "kdalgorithms_bits/filter.h"
1415
#include "kdalgorithms_bits/find_if.h"
1516
#include "kdalgorithms_bits/generate.h"
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/****************************************************************************
2+
**
3+
** This file is part of KDAlgorithms
4+
**
5+
** SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
6+
**
7+
** SPDX-License-Identifier: MIT
8+
**
9+
****************************************************************************/
10+
11+
#pragma once
12+
13+
#include "transform.h"
14+
#include "tuple_utils.h"
15+
#include <tuple>
16+
#include <vector>
17+
18+
namespace kdalgorithms {
19+
namespace detail {
20+
template <template <typename...> class ResultContainerClass, typename Container>
21+
auto cartesian_product(Container &&arg)
22+
{
23+
return kdalgorithms::transformed<ResultContainerClass>(
24+
std::forward<Container>(arg), [](auto &&elm) { return std::make_tuple(elm); });
25+
}
26+
27+
template <template <typename...> class ResultContainerClass, typename Container,
28+
typename... REST>
29+
auto cartesian_product(Container &&container, REST... args)
30+
{
31+
auto partialResult = cartesian_product<ResultContainerClass>(args...);
32+
33+
using TupleType =
34+
detail::merge_tuple_types_t<ValueType<Container>, ValueType<decltype(partialResult)>>;
35+
ResultContainerClass<TupleType> result;
36+
for (auto &&item : container) {
37+
for (auto &&partialItem : partialResult) {
38+
result.push_back(std::tuple_cat(std::make_tuple(item), partialItem));
39+
}
40+
}
41+
return result;
42+
}
43+
} // namespace detail
44+
45+
template <template <typename...> class ResultContainerClass = std::vector, typename... ARGS>
46+
auto cartesian_product(ARGS... args)
47+
{
48+
return detail::cartesian_product<ResultContainerClass>(std::forward<ARGS>(args)...);
49+
}
50+
51+
} // namespace kdalgorithms

src/kdalgorithms_bits/transform.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#pragma once
1212

1313
#include "insert_wrapper.h"
14+
#include "read_iterator_wrapper.h"
1415
#include "reserve_helper.h"
1516
#include "shared.h"
1617
#include "to_function_object.h"

src/kdalgorithms_bits/tuple_utils.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,16 @@ namespace detail {
4040
fn);
4141
}
4242

43+
template <typename T1, typename T2>
44+
struct merge_tuple_types;
45+
46+
template <typename T, typename... TupleItems>
47+
struct merge_tuple_types<T, std::tuple<TupleItems...>>
48+
{
49+
using type = std::tuple<T, TupleItems...>;
50+
};
51+
52+
template <typename T, typename Tuple>
53+
using merge_tuple_types_t = typename merge_tuple_types<T, Tuple>::type;
4354
} // namespace detail
4455
} // namespace kdalgorithms

tests/tst_kdalgorithms.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <QTest>
1616
#include <QVector>
1717
#include <algorithm>
18+
#include <array>
1819
#include <deque>
1920
#include <forward_list>
2021
#include <iostream>
@@ -182,6 +183,7 @@ private Q_SLOTS:
182183
void multi_partitioned();
183184
void multi_partitioned_with_function_taking_a_value();
184185
void sub_range();
186+
void product();
185187
};
186188

187189
void TestAlgorithms::copy()
@@ -2929,6 +2931,68 @@ void TestAlgorithms::sub_range()
29292931
#endif
29302932
}
29312933

2934+
void TestAlgorithms::product()
2935+
{
2936+
{ // Base case
2937+
std::vector<int> v{1, 2, 3};
2938+
std::vector<std::tuple<int>> expected{{1}, {2}, {3}};
2939+
auto result = kdalgorithms::cartesian_product(v);
2940+
QCOMPARE(result, expected);
2941+
}
2942+
2943+
{ // Two lists
2944+
std::vector<int> v1{1, 2};
2945+
std::vector<int> v2{3, 4};
2946+
std::vector<std::tuple<int, int>> expected{{1, 3}, {1, 4}, {2, 3}, {2, 4}};
2947+
auto result = kdalgorithms::cartesian_product(v1, v2);
2948+
QCOMPARE(result, expected);
2949+
}
2950+
2951+
{ // Three lists
2952+
std::vector<int> v1{1, 2};
2953+
std::vector<int> v2{3, 4};
2954+
std::vector<int> v3{5, 6, 7};
2955+
std::vector<std::tuple<int, int, int>> expected{{1, 3, 5}, {1, 3, 6}, {1, 3, 7}, {1, 4, 5},
2956+
{1, 4, 6}, {1, 4, 7}, {2, 3, 5}, {2, 3, 6},
2957+
{2, 3, 7}, {2, 4, 5}, {2, 4, 6}, {2, 4, 7}};
2958+
auto result = kdalgorithms::cartesian_product(v1, v2, v3);
2959+
QCOMPARE(result, expected);
2960+
}
2961+
2962+
{ // Different types
2963+
std::vector<int> v1{1, 2};
2964+
std::vector<bool> v2{true, false};
2965+
std::vector<std::tuple<int, bool>> expected{{1, true}, {1, false}, {2, true}, {2, false}};
2966+
auto result = kdalgorithms::cartesian_product(v1, v2);
2967+
QCOMPARE(result, expected);
2968+
}
2969+
2970+
{ // Different container types
2971+
// Example from https://en.cppreference.com/w/cpp/ranges/cartesian_product_view
2972+
const std::array<char, 2> x = {'A', 'B'};
2973+
const std::vector<int> y = {1, 2, 3};
2974+
const std::list<std::string> z = {"α", "β", "γ", "δ"};
2975+
2976+
const auto expected = std::vector<std::tuple<char, int, std::string>>{
2977+
{'A', 1, "α"}, {'A', 1, "β"}, {'A', 1, "γ"}, {'A', 1, "δ"}, {'A', 2, "α"},
2978+
{'A', 2, "β"}, {'A', 2, "γ"}, {'A', 2, "δ"}, {'A', 3, "α"}, {'A', 3, "β"},
2979+
{'A', 3, "γ"}, {'A', 3, "δ"}, {'B', 1, "α"}, {'B', 1, "β"}, {'B', 1, "γ"},
2980+
{'B', 1, "δ"}, {'B', 2, "α"}, {'B', 2, "β"}, {'B', 2, "γ"}, {'B', 2, "δ"},
2981+
{'B', 3, "α"}, {'B', 3, "β"}, {'B', 3, "γ"}, {'B', 3, "δ"},
2982+
};
2983+
auto result = kdalgorithms::cartesian_product(x, y, z);
2984+
QCOMPARE(result, expected);
2985+
}
2986+
2987+
{ // Result Type given
2988+
std::vector<int> v1{1, 2};
2989+
std::list<bool> v2{true, false};
2990+
std::deque<std::tuple<int, bool>> expected{{1, true}, {1, false}, {2, true}, {2, false}};
2991+
auto result = kdalgorithms::cartesian_product<std::deque>(v1, v2);
2992+
QCOMPARE(result, expected);
2993+
}
2994+
}
2995+
29322996
QTEST_MAIN(TestAlgorithms)
29332997

29342998
#include "tst_kdalgorithms.moc"

0 commit comments

Comments
 (0)