Skip to content

Commit

Permalink
Add cartesian_product for_each tests, including one that validate…
Browse files Browse the repository at this point in the history
…s that a

tuple of elements not a tuple of cursors is passed to the `for_each` operation.
  • Loading branch information
brycelelbach committed Jul 31, 2023
1 parent 070d607 commit be018dd
Showing 1 changed file with 121 additions and 59 deletions.
180 changes: 121 additions & 59 deletions test/test_cartesian_product.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ namespace {

constexpr bool test_cartesian_product()
{
// 2D `cartesian_product` with lvalue references.
{
std::array arr1{100, 200};
std::array arr2{1.0f, 2.0f};
std::array arr2{true, false};

auto cart = flux::cartesian_product(flux::mut_ref(arr1), flux::mut_ref(arr2));

Expand All @@ -42,32 +43,42 @@ constexpr bool test_cartesian_product()
static_assert(flux::bounded_sequence<C const>);
static_assert(flux::sized_sequence<C const>);

static_assert(std::same_as<flux::element_t<C>, std::tuple<int&, float&>>);
static_assert(std::same_as<flux::value_t<C>, std::tuple<int, float>>);
static_assert(std::same_as<flux::rvalue_element_t<C>, std::tuple<int&&, float&&>>);
static_assert(std::same_as<flux::element_t<C>, std::tuple<int&, bool&>>);
static_assert(std::same_as<flux::value_t<C>, std::tuple<int, bool>>);
static_assert(std::same_as<flux::rvalue_element_t<C>, std::tuple<int&&, bool&&>>);

static_assert(std::same_as<flux::element_t<C const>, std::tuple<int&, float&>>);
static_assert(std::same_as<flux::value_t<C const>, std::tuple<int, float>>);
static_assert(std::same_as<flux::rvalue_element_t<C const>, std::tuple<int&&, float&&>>);
static_assert(std::same_as<flux::element_t<C const>, std::tuple<int&, bool&>>);
static_assert(std::same_as<flux::value_t<C const>, std::tuple<int, bool>>);
static_assert(std::same_as<flux::rvalue_element_t<C const>, std::tuple<int&&, bool&&>>);

STATIC_CHECK(flux::size(cart) == 2 * 2);

STATIC_CHECK(check_equal(cart, {
std::tuple{100, 1.0f}, std::tuple{100, 2.0f},
std::tuple{200, 1.0f}, std::tuple{200, 2.0f} }));
std::tuple{100, true}, std::tuple{100, false},
std::tuple{200, true}, std::tuple{200, false} }));

STATIC_CHECK(flux::distance(cart, cart.first(), cart.last()) == 2 * 2);

{
auto cur = flux::next(cart, cart.first(), 2);
STATIC_CHECK(cart[cur] == std::tuple{200, 1.0f});
STATIC_CHECK(cart[cur] == std::tuple{200, true});
flux::inc(cart, cur, -2);
STATIC_CHECK(cart[cur] == std::tuple{100, 1.0f});
STATIC_CHECK(cart[cur] == std::tuple{100, true});
}

int sum_i = 0;
int sum_j = 0;
cart.for_each(flux::unpack([&] (int i, bool j) {
sum_i += i;
sum_j += j;
}));
STATIC_CHECK(sum_i == 2 * (100 + 200));
STATIC_CHECK(sum_j == 2);
}

// 2D `cartesian_product` with rvalue references and temporaries.
{
auto cart = flux::cartesian_product(std::array{100, 200}, std::array{1.0f, 2.0f});
auto cart = flux::cartesian_product(std::array{100, 200}, std::array{true, false});

using C = decltype(cart);

Expand All @@ -87,34 +98,44 @@ constexpr bool test_cartesian_product()
static_assert(flux::bounded_sequence<C const>);
static_assert(flux::sized_sequence<C const>);

static_assert(std::same_as<flux::element_t<C>, std::tuple<int&, float&>>);
static_assert(std::same_as<flux::value_t<C>, std::tuple<int, float>>);
static_assert(std::same_as<flux::rvalue_element_t<C>, std::tuple<int&&, float&&>>);
static_assert(std::same_as<flux::element_t<C>, std::tuple<int&, bool&>>);
static_assert(std::same_as<flux::value_t<C>, std::tuple<int, bool>>);
static_assert(std::same_as<flux::rvalue_element_t<C>, std::tuple<int&&, bool&&>>);

static_assert(std::same_as<flux::element_t<C const>, std::tuple<int const&, float const&>>);
static_assert(std::same_as<flux::value_t<C const>, std::tuple<int, float>>);
static_assert(std::same_as<flux::rvalue_element_t<C const>, std::tuple<int const&&, float const&&>>);
static_assert(std::same_as<flux::element_t<C const>, std::tuple<int const&, bool const&>>);
static_assert(std::same_as<flux::value_t<C const>, std::tuple<int, bool>>);
static_assert(std::same_as<flux::rvalue_element_t<C const>, std::tuple<int const&&, bool const&&>>);

STATIC_CHECK(flux::size(cart) == 2 * 2);

STATIC_CHECK(check_equal(cart, {
std::tuple{100, 1.0f}, std::tuple{100, 2.0f},
std::tuple{200, 1.0f}, std::tuple{200, 2.0f} }));
std::tuple{100, true}, std::tuple{100, false},
std::tuple{200, true}, std::tuple{200, false} }));

STATIC_CHECK(flux::distance(cart, cart.first(), cart.last()) == 2 * 2);

{
auto cur = flux::next(cart, cart.first(), 2);
STATIC_CHECK(cart[cur] == std::tuple{200, 1.0f});
STATIC_CHECK(cart[cur] == std::tuple{200, true});
flux::inc(cart, cur, -2);
STATIC_CHECK(cart[cur] == std::tuple{100, 1.0f});
STATIC_CHECK(cart[cur] == std::tuple{100, true});
}

int sum_i = 0;
int sum_j = 0;
cart.for_each(flux::unpack([&] (int i, bool j) {
sum_i += i;
sum_j += j;
}));
STATIC_CHECK(sum_i == 2 * (100 + 200));
STATIC_CHECK(sum_j == 2);
}

// 3D `cartesian_product`.
{
std::array arr1{100, 200};
std::array arr2{1.0f, 2.0f, 3.0f, 4.0f};
std::array arr3{0ULL, 2ULL, 4ULL};
std::array arr2{true, false, true, false};
std::array arr3{1ULL, 2ULL, 4ULL};

auto cart = flux::cartesian_product(flux::mut_ref(arr1), flux::mut_ref(arr2), flux::mut_ref(arr3));

Expand All @@ -136,53 +157,66 @@ constexpr bool test_cartesian_product()
static_assert(flux::bounded_sequence<C const>);
static_assert(flux::sized_sequence<C const>);

static_assert(std::same_as<flux::element_t<C>, std::tuple<int&, float&, unsigned long long&>>);
static_assert(std::same_as<flux::value_t<C>, std::tuple<int, float, unsigned long long>>);
static_assert(std::same_as<flux::rvalue_element_t<C>, std::tuple<int&&, float&&, unsigned long long&&>>);
static_assert(std::same_as<flux::element_t<C>, std::tuple<int&, bool&, unsigned long long&>>);
static_assert(std::same_as<flux::value_t<C>, std::tuple<int, bool, unsigned long long>>);
static_assert(std::same_as<flux::rvalue_element_t<C>, std::tuple<int&&, bool&&, unsigned long long&&>>);

static_assert(std::same_as<flux::element_t<C const>, std::tuple<int&, float&, unsigned long long&>>);
static_assert(std::same_as<flux::value_t<C const>, std::tuple<int, float, unsigned long long>>);
static_assert(std::same_as<flux::rvalue_element_t<C const>, std::tuple<int&&, float&&, unsigned long long&&>>);
static_assert(std::same_as<flux::element_t<C const>, std::tuple<int&, bool&, unsigned long long&>>);
static_assert(std::same_as<flux::value_t<C const>, std::tuple<int, bool, unsigned long long>>);
static_assert(std::same_as<flux::rvalue_element_t<C const>, std::tuple<int&&, bool&&, unsigned long long&&>>);

STATIC_CHECK(flux::size(cart) == 2 * 4 * 3);

STATIC_CHECK(check_equal(cart, {
std::tuple{100, 1.0f, 0ULL},
std::tuple{100, 1.0f, 2ULL},
std::tuple{100, 1.0f, 4ULL},
std::tuple{100, 2.0f, 0ULL},
std::tuple{100, 2.0f, 2ULL},
std::tuple{100, 2.0f, 4ULL},
std::tuple{100, 3.0f, 0ULL},
std::tuple{100, 3.0f, 2ULL},
std::tuple{100, 3.0f, 4ULL},
std::tuple{100, 4.0f, 0ULL},
std::tuple{100, 4.0f, 2ULL},
std::tuple{100, 4.0f, 4ULL},
std::tuple{200, 1.0f, 0ULL},
std::tuple{200, 1.0f, 2ULL},
std::tuple{200, 1.0f, 4ULL},
std::tuple{200, 2.0f, 0ULL},
std::tuple{200, 2.0f, 2ULL},
std::tuple{200, 2.0f, 4ULL},
std::tuple{200, 3.0f, 0ULL},
std::tuple{200, 3.0f, 2ULL},
std::tuple{200, 3.0f, 4ULL},
std::tuple{200, 4.0f, 0ULL},
std::tuple{200, 4.0f, 2ULL},
std::tuple{200, 4.0f, 4ULL}
std::tuple{100, true, 1ULL},
std::tuple{100, true, 2ULL},
std::tuple{100, true, 4ULL},
std::tuple{100, false, 1ULL},
std::tuple{100, false, 2ULL},
std::tuple{100, false, 4ULL},
std::tuple{100, true, 1ULL},
std::tuple{100, true, 2ULL},
std::tuple{100, true, 4ULL},
std::tuple{100, false, 1ULL},
std::tuple{100, false, 2ULL},
std::tuple{100, false, 4ULL},
std::tuple{200, true, 1ULL},
std::tuple{200, true, 2ULL},
std::tuple{200, true, 4ULL},
std::tuple{200, false, 1ULL},
std::tuple{200, false, 2ULL},
std::tuple{200, false, 4ULL},
std::tuple{200, true, 1ULL},
std::tuple{200, true, 2ULL},
std::tuple{200, true, 4ULL},
std::tuple{200, false, 1ULL},
std::tuple{200, false, 2ULL},
std::tuple{200, false, 4ULL}
}));

STATIC_CHECK(flux::distance(cart, cart.first(), cart.last()) == 2 * 4 * 3);

{
auto cur = flux::next(cart, cart.first(), 3);
STATIC_CHECK(cart[cur] == std::tuple{100, 2.0f, 0ULL});
STATIC_CHECK(cart[cur] == std::tuple{100, false, 1ULL});
flux::inc(cart, cur, -3);
STATIC_CHECK(cart[cur] == std::tuple{100, 1.0f, 0ULL});
STATIC_CHECK(cart[cur] == std::tuple{100, true, 1ULL});
}

int sum_i = 0;
int sum_j = 0;
unsigned long long sum_k = 0;
cart.for_each(flux::unpack([&] (int i, bool j, unsigned long long k) {
sum_i += i;
sum_j += j;
sum_k += k;
}));
STATIC_CHECK(sum_i == 12 * (100 + 200));
STATIC_CHECK(sum_j == 12);
STATIC_CHECK(sum_k == 8ULL * (1ULL + 2ULL + 4ULL));
}

// `cartesian_product` of `iota`/`ints`.
{
auto cart = flux::cartesian_product(flux::ints(0, 4), flux::ints(0, 2), flux::ints(0, 3));

Expand Down Expand Up @@ -268,11 +302,40 @@ constexpr bool test_cartesian_product()
STATIC_CHECK(flux::next(cart, flux::next(cart, cart.first(), 11), -4) == std::tuple{1, 0, 1});
STATIC_CHECK(flux::next(cart, flux::next(cart, cart.first(), 11), -5) == std::tuple{1, 0, 0});
}

flux::distance_t sum_i = 0;
flux::distance_t sum_j = 0;
flux::distance_t sum_k = 0;
cart.for_each(flux::unpack([&] (flux::distance_t i, flux::distance_t j, flux::distance_t k) {
sum_i += i;
sum_j += j;
sum_k += k;
}));
constexpr auto triangular_number = [] (auto n) { return (n * (n + 1)) / 2; };
STATIC_CHECK(sum_i == triangular_number(4 - 1) * 2 * 3);
STATIC_CHECK(sum_j == 4 * triangular_number(2 - 1) * 3);
STATIC_CHECK(sum_k == 4 * 2 * triangular_number(3 - 1));
}

// TODO: Product with a zero-sized sequence works and produces an empty sequence
// `cartesian_product` `for_each` element type.
{
struct T {};

auto cart = flux::cartesian_product(std::array{100, 200}, std::array{T{}, T{}});

// Test unpack()
int sum_i = 0;
int count_j = 0;
cart.for_each(flux::unpack([&] (int i, T j) {
sum_i += i;
count_j += 1;
}));
STATIC_CHECK(sum_i == 2 * (100 + 200));
STATIC_CHECK(count_j == 4);
}

// `cartesian_product` with a zero-sized sequence produces an empty sequence.

// `cartesian_product` with `unpack`.
{
int vals[3][3] = {};

Expand All @@ -286,7 +349,6 @@ constexpr bool test_cartesian_product()
STATIC_CHECK(vals[i][j] == 100);
}
}

}

return true;
Expand Down

0 comments on commit be018dd

Please sign in to comment.