/*************************************************************************** * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht * * Copyright (c) QuantStack * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ #include "xtensor/containers/xarray.hpp" #include "xtensor/containers/xtensor.hpp" #include "test_common.hpp" #include "test_common_macros.hpp" namespace xt { template class xiterator_test : public ::testing::Test { public: using result_type = R; }; using std::size_t; template void test_increment(const R& result, const S& shape) { using size_type = typename R::size_type; using vector_type = typename R::vector_type; vector_type data = result.storage(); xarray_adaptor a(data, result.shape(), result.strides()); auto offset = shape.size() - a.dimension(); auto broadcasting_stride = std::accumulate( shape.cbegin(), shape.cbegin() + offset, size_type(1), std::multiplies() ); size_type nb_inc = (L == layout_type::row_major ? shape.back() * shape[shape.size() - 2] + 1 : broadcasting_stride * (result.shape().front() * result.shape()[1] + 1)); int expected = a(1, 0, 1); auto iter = a.template begin(shape); auto iter2 = a.template begin(shape); for (size_type i = 0; i < nb_inc; ++i) { ++iter; iter2++; } CHECK_MESSAGE(*iter == expected, "preincrement operator doesn't give expected result"); CHECK_MESSAGE(*iter2 == expected, "postincrement operator doesn't give expected result"); } template void test_random_increment(const R& result, const S& shape) { using difference_type = typename R::difference_type; using vector_type = typename R::vector_type; vector_type data = result.storage(); xarray_adaptor a(data, result.shape(), result.strides()); auto offset = shape.size() - a.dimension(); auto broadcasting_stride = std::accumulate( shape.cbegin(), shape.cbegin() + offset, difference_type(1), std::multiplies() ); auto nb_inc = L == layout_type::row_major ? difference_type(shape.back() * shape[shape.size() - 2] + 1) : broadcasting_stride * difference_type(result.shape().front() * result.shape()[1] + 1); int expected = a(1, 0, 1); auto iter = a.template begin(); auto iter2 = a.template begin(); iter += nb_inc; auto iter3 = iter2 + nb_inc; CHECK_MESSAGE(*iter == expected, "preincrement operator doesn't give expected result"); CHECK_MESSAGE(*iter3 == expected, "postincrement operator doesn't give expected result"); CHECK_MESSAGE(iter2[nb_inc] == expected, "postincrement operator doesn't give expected result"); } template void test_end(const R& result, const S& shape) { using size_type = typename R::size_type; using vector_type = typename R::vector_type; vector_type data = result.storage(); xarray_adaptor a(data, result.shape(), result.strides()); size_type size = compute_size(shape); auto iter = a.template begin(shape); auto last = a.template end(shape); for (size_type i = 0; i < size; ++i) { ++iter; } CHECK_MESSAGE(iter == last, "iterator doesn't reach the end"); EXPECT_FALSE(iter < last); } template void test_decrement(const R& result, const S& shape) { using size_type = typename R::size_type; using vector_type = typename R::vector_type; vector_type data = result.storage(); xarray_adaptor a(data, result.shape(), result.strides()); auto offset = shape.size() - a.dimension(); auto broadcasting_stride = std::accumulate( shape.cbegin(), shape.cbegin() + offset, size_type(1), std::multiplies() ); size_type nb_inc = (L == layout_type::row_major ? shape.back() * shape[shape.size() - 2] + 1 : broadcasting_stride * (result.shape().front() * result.shape()[1] + 1)); int expected = a(1, 1, 2); auto iter = a.template rbegin(shape); auto iter2 = a.template rbegin(shape); for (size_type i = 0; i < nb_inc; ++i) { ++iter; iter2++; } CHECK_MESSAGE(*iter == expected, "predecrement operator doesn't give expected result"); CHECK_MESSAGE(*iter2 == expected, "postdecrement operator doesn't give expected result"); } template void test_random_decrement(const R& result, const S& shape) { using difference_type = typename R::difference_type; using vector_type = typename R::vector_type; vector_type data = result.storage(); xarray_adaptor a(data, result.shape(), result.strides()); auto offset = shape.size() - a.dimension(); auto broadcasting_stride = std::accumulate( shape.cbegin(), shape.cbegin() + offset, difference_type(1), std::multiplies() ); auto nb_inc = L == layout_type::row_major ? difference_type(shape.back() * shape[shape.size() - 2] + 1) : broadcasting_stride * difference_type(result.shape().front() * result.shape()[1] + 1); int expected = a(1, 1, 2); auto iter = a.template rbegin(shape); auto iter2 = a.template rbegin(shape); iter += nb_inc; auto iter3 = iter2 + nb_inc; CHECK_MESSAGE(*iter == expected, "predecrement operator doesn't give expected result"); CHECK_MESSAGE(*iter3 == expected, "postdecrement operator doesn't give expected result"); } template void test_rend(const R& result, const S& shape) { using size_type = typename R::size_type; using vector_type = typename R::vector_type; vector_type data = result.storage(); xarray_adaptor a(data, result.shape(), result.strides()); size_type size = compute_size(shape); auto iter = a.template rbegin(shape); auto last = a.template rend(shape); CHECK_MESSAGE(*iter == data.back(), "dereferencing rbegin does not result in last element"); CHECK_MESSAGE(*last == data.front(), "dereferencing rend does not result in first element"); for (size_type i = 0; i < size; ++i) { ++iter; } CHECK_MESSAGE(iter == last, "reverse iterator doesn't reach the end"); } #define XITERATOR_TEST_TYPES \ row_major_result<>, column_major_result<>, central_major_result<>, unit_shape_result<> TEST_SUITE("xiterator_test") { TEST_CASE_TEMPLATE("increment", TypeParam, XITERATOR_TEST_TYPES) { using TestFixture = xiterator_test; typename TestFixture::result_type rm; SUBCASE("same shape - row_major iterator") { test_increment(rm, rm.shape()); } SUBCASE("broadcasting shape - row_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_increment(rm, rm.shape()); } SUBCASE("same shape - column_major iterator") { test_increment(rm, rm.shape()); } SUBCASE("broadcasting shape - column_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_increment(rm, rm.shape()); } } TEST_CASE_TEMPLATE("random_increment", TypeParam, XITERATOR_TEST_TYPES) { using TestFixture = xiterator_test; typename TestFixture::result_type rm; SUBCASE("same shape - row_major iterator") { test_random_increment(rm, rm.shape()); } SUBCASE("broadcasting shape - row_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_random_increment(rm, rm.shape()); } SUBCASE("same shape - column_major iterator") { test_random_increment(rm, rm.shape()); } SUBCASE("broadcasting shape - column_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_random_increment(rm, rm.shape()); } } TEST_CASE_TEMPLATE("end", TypeParam, XITERATOR_TEST_TYPES) { using TestFixture = xiterator_test; typename TestFixture::result_type rm; SUBCASE("same shape - row_major iterator") { test_end(rm, rm.shape()); } SUBCASE("broadcasting shape - row_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_end(rm, sh); } SUBCASE("same shape - column_major iterator") { test_end(rm, rm.shape()); } SUBCASE("broadcasting shape - column_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_end(rm, sh); } } TEST_CASE_TEMPLATE("decrement", TypeParam, XITERATOR_TEST_TYPES) { using TestFixture = xiterator_test; typename TestFixture::result_type rm; SUBCASE("same shape - row_major iterator") { test_decrement(rm, rm.shape()); } SUBCASE("broadcasting shape - row_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_decrement(rm, sh); } SUBCASE("same shape - column_major iterator") { test_decrement(rm, rm.shape()); } SUBCASE("broadcasting shape - column_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_decrement(rm, sh); } } TEST_CASE_TEMPLATE("random_decrement", TypeParam, XITERATOR_TEST_TYPES) { using TestFixture = xiterator_test; typename TestFixture::result_type rm; SUBCASE("same shape - row_major iterator") { test_random_decrement(rm, rm.shape()); } SUBCASE("broadcasting shape - row_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_random_decrement(rm, sh); } SUBCASE("same shape - column_major iterator") { test_random_decrement(rm, rm.shape()); } SUBCASE("broadcasting shape - column_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_random_decrement(rm, sh); } } TEST_CASE_TEMPLATE("reverse_end", TypeParam, XITERATOR_TEST_TYPES) { using TestFixture = xiterator_test; typename TestFixture::result_type rm; SUBCASE("same shape - row_major iterator") { test_rend(rm, rm.shape()); } SUBCASE("broadcasting shape - row_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_rend(rm, sh); } SUBCASE("same shape - column_major iterator") { test_rend(rm, rm.shape()); } SUBCASE("broadcasting shape - column_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_rend(rm, sh); } } } template void test_minus(const R& result, const S& shape) { using size_type = typename R::size_type; using difference_type = typename R::difference_type; using vector_type = typename R::vector_type; vector_type data = result.storage(); xarray_adaptor a(data, result.shape(), result.strides()); size_type size = shape.size(); difference_type nb_inc = difference_type( L == layout_type::row_major ? shape.back() * shape[size - 2] + shape.back() + 2 : shape[size - 3] * shape[size - 2] * 2 + shape[size - 3] + 1 ); difference_type nb_inc2 = difference_type( L == layout_type::row_major ? shape.back() * shape[size - 2] * 2 + 3 : shape[size - 3] * shape[size - 2] * 3 + 2 ); ptrdiff_t expected = ptrdiff_t(nb_inc2 - nb_inc); auto iter = a.template begin() + nb_inc; auto iter2 = a.template begin() + nb_inc2; CHECK_MESSAGE(iter2 - iter == expected, "operator- doesn't give expected result"); auto riter = a.template rbegin() + nb_inc; auto riter2 = a.template rbegin() + nb_inc2; CHECK_MESSAGE(riter2 - riter == expected, "operator- doesn't give expected result"); auto diff = a.template end() - a.template begin(); CHECK_EQ(size_type(diff), a.size()); auto rdiff = a.template rend() - a.template rbegin(); CHECK_EQ(size_type(rdiff), a.size()); } TEST(xiterator, row_major_minus) { row_major_result<> rm; SUBCASE("same shape - row_major iterator") { test_minus(rm, rm.shape()); } SUBCASE("broadcasting shape - row_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_minus(rm, rm.shape()); } } TEST(xiterator, column_major_minus) { column_major_result<> rm; SUBCASE("same shape - column_major iterator") { test_minus(rm, rm.shape()); } SUBCASE("broadcasting shape - column_major iterator") { layout_result<>::shape_type sh = rm.shape(); sh.insert(sh.begin(), 2); sh.insert(sh.begin(), 4); test_minus(rm, rm.shape()); } } TEST(xiterator, broadcast) { EXPECT_TRUE(broadcastable(std::vector({1, 2, 1}), std::vector({3, 2, 1}))); EXPECT_TRUE(broadcastable(std::vector({1, 1}), std::vector({3, 2, 1}))); EXPECT_TRUE(broadcastable(std::vector({1, 1}), std::vector({2, 2, 1}))); EXPECT_FALSE(broadcastable(std::vector({2, 2, 1}), std::vector({3, 2, 1}))); } TEST(xiterator, pointer) { xarray m{{3, 4}, {6, 5}}; constexpr layout_type l = xarray::static_layout == layout_type::column_major ? layout_type::row_major : layout_type::column_major; auto it = m.begin(); EXPECT_EQ(*(it.operator->()), 3); } TEST(xiterator, cross_layout) { xarray a = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; xarray b = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}; // Performs an element-wise comparison via iterators and ensures the default traversal // of a container is consistent for any layout. bool res = (a == b); EXPECT_TRUE(res); } TEST(xiterator, comparisons) { row_major_result<> rm; xarray arr(rm.shape()); auto rm_iter = arr.template cbegin(); auto rm_iter2 = rm_iter + 2; EXPECT_TRUE(rm_iter != rm_iter2); EXPECT_FALSE(rm_iter == rm_iter2); EXPECT_TRUE(rm_iter < rm_iter2); EXPECT_TRUE(rm_iter <= rm_iter2); EXPECT_FALSE(rm_iter > rm_iter2); EXPECT_FALSE(rm_iter >= rm_iter2); auto cm_iter = arr.template cbegin(); auto cm_iter2 = cm_iter + 2; EXPECT_TRUE(cm_iter != cm_iter2); EXPECT_FALSE(cm_iter == cm_iter2); EXPECT_TRUE(cm_iter < cm_iter2); EXPECT_TRUE(cm_iter <= cm_iter2); EXPECT_FALSE(cm_iter > cm_iter2); EXPECT_FALSE(cm_iter >= cm_iter2); } TEST(xiterator, assign) { row_major_result<> rm; using vector_type = row_major_result<>::vector_type; xarray_adaptor a(rm.storage(), rm.shape(), rm.strides()); SUBCASE("row_major iterator") { xarray dst(a.shape(), 1); std::copy( a.cbegin(), a.cend(), dst.begin() ); EXPECT_EQ(a, dst); } SUBCASE("column_major iterator") { xarray dst(a.shape(), 1); std::copy( a.cbegin(), a.cend(), dst.begin() ); EXPECT_EQ(a, dst); } } TEST(xiterator, revert_assign) { row_major_result<> rm; using vector_type = row_major_result<>::vector_type; xarray_adaptor a(rm.storage(), rm.shape(), rm.strides()); SUBCASE("row_major iterator") { xarray dst(a.shape(), 1); std::copy( a.crbegin(), a.crend(), dst.rbegin() ); EXPECT_EQ(a, dst); } SUBCASE("column_major iterator") { xarray dst(a.shape(), 1); std::copy( a.crbegin(), a.crend(), dst.rbegin() ); EXPECT_EQ(a, dst); } } TEST(xiterator, reverse_corner_cases) { xtensor a = {{{13, 16}, {13, 16}, {13, 16}}}; constexpr auto cm = layout_type::column_major; constexpr auto rm = layout_type::row_major; { std::vector exp_iter = {16, 16, 16, 13, 13, 13}; auto e_iter = exp_iter.begin(); for (auto it = a.template rbegin(); it != a.template rend(); ++it) { EXPECT_EQ(*e_iter, *it); ++e_iter; } EXPECT_TRUE(e_iter == exp_iter.end()); } { std::vector exp_iter = {16, 13, 16, 13, 16, 13}; auto e_iter = exp_iter.begin(); for (auto it = a.template rbegin(); it != a.template rend(); ++it) { EXPECT_EQ(*e_iter, *it); ++e_iter; } EXPECT_TRUE(e_iter == exp_iter.end()); } { std::vector exp_iter = {13, 16, 13, 16, 13, 16}; auto e_iter = exp_iter.begin(); for (auto it = a.template begin(); it != a.template end(); ++it) { EXPECT_EQ(*e_iter, *it); ++e_iter; } EXPECT_TRUE(e_iter == exp_iter.end()); } { std::vector exp_iter = {13, 13, 13, 16, 16, 16}; auto e_iter = exp_iter.begin(); for (auto it = a.template begin(); it != a.template end(); ++it) { EXPECT_EQ(*e_iter, *it); ++e_iter; } } xtensor b = {{{1}, {1}}, {{2}, {2}}, {{3}, {3}}}; { std::vector exp_iter = {3, 2, 1, 3, 2, 1}; auto e_iter = exp_iter.begin(); for (auto it = b.template rbegin(); it != b.template rend(); ++it) { EXPECT_EQ(*e_iter, *it); ++e_iter; } EXPECT_TRUE(e_iter == exp_iter.end()); } { std::vector exp_iter = {3, 3, 2, 2, 1, 1}; auto e_iter = exp_iter.begin(); for (auto it = b.template rbegin(); it != b.template rend(); ++it) { EXPECT_EQ(*e_iter, *it); ++e_iter; } EXPECT_TRUE(e_iter == exp_iter.end()); } { std::vector exp_iter = {1, 1, 2, 2, 3, 3}; auto e_iter = exp_iter.begin(); for (auto it = b.template begin(); it != b.template end(); ++it) { EXPECT_EQ(*e_iter, *it); ++e_iter; } EXPECT_TRUE(e_iter == exp_iter.end()); } { std::vector exp_iter = {1, 2, 3, 1, 2, 3}; auto e_iter = exp_iter.begin(); for (auto it = b.template begin(); it != b.template end(); ++it) { EXPECT_EQ(*e_iter, *it); ++e_iter; } EXPECT_TRUE(e_iter == exp_iter.end()); } } }