Home | History | Annotate | Download | only in tests
      1 ///////////////////////////////////////////////////////////////////////////////
      2 //
      3 // Copyright (c) 2015 Microsoft Corporation. All rights reserved.
      4 //
      5 // This code is licensed under the MIT License (MIT).
      6 //
      7 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      8 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      9 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     10 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     11 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     12 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     13 // THE SOFTWARE.
     14 //
     15 ///////////////////////////////////////////////////////////////////////////////
     16 
     17 #include <catch/catch.hpp>
     18 
     19 #include <gsl/multi_span>
     20 
     21 #include <iostream>
     22 #include <list>
     23 #include <map>
     24 #include <memory>
     25 #include <string>
     26 #include <vector>
     27 
     28 using namespace std;
     29 using namespace gsl;
     30 
     31 namespace
     32 {
     33 struct BaseClass
     34 {
     35 };
     36 struct DerivedClass : BaseClass
     37 {
     38 };
     39 }
     40 
     41 TEST_CASE("span_section_test")
     42 {
     43     int a[30][4][5];
     44 
     45     const auto av = as_multi_span(a);
     46     const auto sub = av.section({15, 0, 0}, gsl::index<3>{2, 2, 2});
     47     const auto subsub = sub.section({1, 0, 0}, gsl::index<3>{1, 1, 1});
     48     (void) subsub;
     49 }
     50 
     51 TEST_CASE("span_section")
     52 {
     53     std::vector<int> data(5 * 10);
     54     std::iota(begin(data), end(data), 0);
     55     const multi_span<int, 5, 10> av = as_multi_span(multi_span<int>{data}, dim<5>(), dim<10>());
     56 
     57     const strided_span<int, 2> av_section_1 = av.section({1, 2}, {3, 4});
     58     CHECK((av_section_1[{0, 0}] == 12));
     59     CHECK((av_section_1[{0, 1}] == 13));
     60     CHECK((av_section_1[{1, 0}] == 22));
     61     CHECK((av_section_1[{2, 3}] == 35));
     62 
     63     const strided_span<int, 2> av_section_2 = av_section_1.section({1, 2}, {2, 2});
     64     CHECK((av_section_2[{0, 0}] == 24));
     65     CHECK((av_section_2[{0, 1}] == 25));
     66     CHECK((av_section_2[{1, 0}] == 34));
     67 }
     68 
     69 TEST_CASE("strided_span_constructors")
     70 {
     71     // Check stride constructor
     72     {
     73         int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
     74         const int carr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
     75 
     76         strided_span<int, 1> sav1{arr, {{9}, {1}}}; // T -> T
     77         CHECK(sav1.bounds().index_bounds() == index<1>{9});
     78         CHECK(sav1.bounds().stride() == 1);
     79         CHECK((sav1[0] == 1 && sav1[8] == 9));
     80 
     81         strided_span<const int, 1> sav2{carr, {{4}, {2}}}; // const T -> const T
     82         CHECK(sav2.bounds().index_bounds() == index<1>{4});
     83         CHECK(sav2.bounds().strides() == index<1>{2});
     84         CHECK((sav2[0] == 1 && sav2[3] == 7));
     85 
     86         strided_span<int, 2> sav3{arr, {{2, 2}, {6, 2}}}; // T -> const T
     87         CHECK((sav3.bounds().index_bounds() == index<2>{2, 2}));
     88         CHECK((sav3.bounds().strides() == index<2>{6, 2}));
     89         CHECK((sav3[{0, 0}] == 1 && sav3[{0, 1}] == 3 && sav3[{1, 0}] == 7));
     90     }
     91 
     92     // Check multi_span constructor
     93     {
     94         int arr[] = {1, 2};
     95 
     96         // From non-cv-qualified source
     97         {
     98             const multi_span<int> src = arr;
     99 
    100             strided_span<int, 1> sav{src, {2, 1}};
    101             CHECK(sav.bounds().index_bounds() == index<1>{2});
    102             CHECK(sav.bounds().strides() == index<1>{1});
    103             CHECK(sav[1] == 2);
    104 
    105 #if _MSC_VER > 1800
    106             // strided_span<const int, 1> sav_c{ {src}, {2, 1} };
    107             strided_span<const int, 1> sav_c{multi_span<const int>{src},
    108                                              strided_bounds<1>{2, 1}};
    109 #else
    110             strided_span<const int, 1> sav_c{multi_span<const int>{src},
    111                                              strided_bounds<1>{2, 1}};
    112 #endif
    113             CHECK(sav_c.bounds().index_bounds() == index<1>{2});
    114             CHECK(sav_c.bounds().strides() == index<1>{1});
    115             CHECK(sav_c[1] == 2);
    116 
    117 #if _MSC_VER > 1800
    118             strided_span<volatile int, 1> sav_v{src, {2, 1}};
    119 #else
    120             strided_span<volatile int, 1> sav_v{multi_span<volatile int>{src},
    121                                                 strided_bounds<1>{2, 1}};
    122 #endif
    123             CHECK(sav_v.bounds().index_bounds() == index<1>{2});
    124             CHECK(sav_v.bounds().strides() == index<1>{1});
    125             CHECK(sav_v[1] == 2);
    126 
    127 #if _MSC_VER > 1800
    128             strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
    129 #else
    130             strided_span<const volatile int, 1> sav_cv{multi_span<const volatile int>{src},
    131                                                        strided_bounds<1>{2, 1}};
    132 #endif
    133             CHECK(sav_cv.bounds().index_bounds() == index<1>{2});
    134             CHECK(sav_cv.bounds().strides() == index<1>{1});
    135             CHECK(sav_cv[1] == 2);
    136         }
    137 
    138         // From const-qualified source
    139         {
    140             const multi_span<const int> src{arr};
    141 
    142             strided_span<const int, 1> sav_c{src, {2, 1}};
    143             CHECK(sav_c.bounds().index_bounds() == index<1>{2});
    144             CHECK(sav_c.bounds().strides() == index<1>{1});
    145             CHECK(sav_c[1] == 2);
    146 
    147 #if _MSC_VER > 1800
    148             strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
    149 #else
    150             strided_span<const volatile int, 1> sav_cv{multi_span<const volatile int>{src},
    151                                                        strided_bounds<1>{2, 1}};
    152 #endif
    153 
    154             CHECK(sav_cv.bounds().index_bounds() == index<1>{2});
    155             CHECK(sav_cv.bounds().strides() == index<1>{1});
    156             CHECK(sav_cv[1] == 2);
    157         }
    158 
    159         // From volatile-qualified source
    160         {
    161             const multi_span<volatile int> src{arr};
    162 
    163             strided_span<volatile int, 1> sav_v{src, {2, 1}};
    164             CHECK(sav_v.bounds().index_bounds() == index<1>{2});
    165             CHECK(sav_v.bounds().strides() == index<1>{1});
    166             CHECK(sav_v[1] == 2);
    167 
    168 #if _MSC_VER > 1800
    169             strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
    170 #else
    171             strided_span<const volatile int, 1> sav_cv{multi_span<const volatile int>{src},
    172                                                        strided_bounds<1>{2, 1}};
    173 #endif
    174             CHECK(sav_cv.bounds().index_bounds() == index<1>{2});
    175             CHECK(sav_cv.bounds().strides() == index<1>{1});
    176             CHECK(sav_cv[1] == 2);
    177         }
    178 
    179         // From cv-qualified source
    180         {
    181             const multi_span<const volatile int> src{arr};
    182 
    183             strided_span<const volatile int, 1> sav_cv{src, {2, 1}};
    184             CHECK(sav_cv.bounds().index_bounds() == index<1>{2});
    185             CHECK(sav_cv.bounds().strides() == index<1>{1});
    186             CHECK(sav_cv[1] == 2);
    187         }
    188     }
    189 
    190     // Check const-casting constructor
    191     {
    192         int arr[2] = {4, 5};
    193 
    194         const multi_span<int, 2> av(arr, 2);
    195         multi_span<const int, 2> av2{av};
    196         CHECK(av2[1] == 5);
    197 
    198         static_assert(
    199             std::is_convertible<const multi_span<int, 2>, multi_span<const int, 2>>::value,
    200             "ctor is not implicit!");
    201 
    202         const strided_span<int, 1> src{arr, {2, 1}};
    203         strided_span<const int, 1> sav{src};
    204         CHECK(sav.bounds().index_bounds() == index<1>{2});
    205         CHECK(sav.bounds().stride() == 1);
    206         CHECK(sav[1] == 5);
    207 
    208         static_assert(
    209             std::is_convertible<const strided_span<int, 1>, strided_span<const int, 1>>::value,
    210             "ctor is not implicit!");
    211     }
    212 
    213     // Check copy constructor
    214     {
    215         int arr1[2] = {3, 4};
    216         const strided_span<int, 1> src1{arr1, {2, 1}};
    217         strided_span<int, 1> sav1{src1};
    218 
    219         CHECK(sav1.bounds().index_bounds() == index<1>{2});
    220         CHECK(sav1.bounds().stride() == 1);
    221         CHECK(sav1[0] == 3);
    222 
    223         int arr2[6] = {1, 2, 3, 4, 5, 6};
    224         const strided_span<const int, 2> src2{arr2, {{3, 2}, {2, 1}}};
    225         strided_span<const int, 2> sav2{src2};
    226         CHECK((sav2.bounds().index_bounds() == index<2>{3, 2}));
    227         CHECK((sav2.bounds().strides() == index<2>{2, 1}));
    228         CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
    229     }
    230 
    231     // Check const-casting assignment operator
    232     {
    233         int arr1[2] = {1, 2};
    234         int arr2[6] = {3, 4, 5, 6, 7, 8};
    235 
    236         const strided_span<int, 1> src{arr1, {{2}, {1}}};
    237         strided_span<const int, 1> sav{arr2, {{3}, {2}}};
    238         strided_span<const int, 1>& sav_ref = (sav = src);
    239         CHECK(sav.bounds().index_bounds() == index<1>{2});
    240         CHECK(sav.bounds().strides() == index<1>{1});
    241         CHECK(sav[0] == 1);
    242         CHECK(&sav_ref == &sav);
    243     }
    244 
    245     // Check copy assignment operator
    246     {
    247         int arr1[2] = {3, 4};
    248         int arr1b[1] = {0};
    249         const strided_span<int, 1> src1{arr1, {2, 1}};
    250         strided_span<int, 1> sav1{arr1b, {1, 1}};
    251         strided_span<int, 1>& sav1_ref = (sav1 = src1);
    252         CHECK(sav1.bounds().index_bounds() == index<1>{2});
    253         CHECK(sav1.bounds().strides() == index<1>{1});
    254         CHECK(sav1[0] == 3);
    255         CHECK(&sav1_ref == &sav1);
    256 
    257         const int arr2[6] = {1, 2, 3, 4, 5, 6};
    258         const int arr2b[1] = {0};
    259         const strided_span<const int, 2> src2{arr2, {{3, 2}, {2, 1}}};
    260         strided_span<const int, 2> sav2{arr2b, {{1, 1}, {1, 1}}};
    261         strided_span<const int, 2>& sav2_ref = (sav2 = src2);
    262         CHECK((sav2.bounds().index_bounds() == index<2>{3, 2}));
    263         CHECK((sav2.bounds().strides() == index<2>{2, 1}));
    264         CHECK((sav2[{0, 0}] == 1 && sav2[{2, 0}] == 5));
    265         CHECK(&sav2_ref == &sav2);
    266     }
    267 }
    268 
    269 TEST_CASE("strided_span_slice")
    270 {
    271     std::vector<int> data(5 * 10);
    272     std::iota(begin(data), end(data), 0);
    273     const multi_span<int, 5, 10> src =
    274         as_multi_span(multi_span<int>{data}, dim<5>(), dim<10>());
    275 
    276     const strided_span<int, 2> sav{src, {{5, 10}, {10, 1}}};
    277 #ifdef CONFIRM_COMPILATION_ERRORS
    278     const strided_span<const int, 2> csav{{src}, {{5, 10}, {10, 1}}};
    279 #endif
    280     const strided_span<const int, 2> csav{multi_span<const int, 5, 10>{src},
    281                                           {{5, 10}, {10, 1}}};
    282 
    283     strided_span<int, 1> sav_sl = sav[2];
    284     CHECK(sav_sl[0] == 20);
    285     CHECK(sav_sl[9] == 29);
    286 
    287     strided_span<const int, 1> csav_sl = sav[3];
    288     CHECK(csav_sl[0] == 30);
    289     CHECK(csav_sl[9] == 39);
    290 
    291     CHECK(sav[4][0] == 40);
    292     CHECK(sav[4][9] == 49);
    293 }
    294 
    295 TEST_CASE("strided_span_column_major")
    296 {
    297     // strided_span may be used to accomodate more peculiar
    298     // use cases, such as column-major multidimensional array
    299     // (aka. "FORTRAN" layout).
    300 
    301     int cm_array[3 * 5] = {1, 4, 7, 10, 13, 2, 5, 8, 11, 14, 3, 6, 9, 12, 15};
    302     strided_span<int, 2> cm_sav{cm_array, {{5, 3}, {1, 5}}};
    303 
    304     // Accessing elements
    305     CHECK((cm_sav[{0, 0}] == 1));
    306     CHECK((cm_sav[{0, 1}] == 2));
    307     CHECK((cm_sav[{1, 0}] == 4));
    308     CHECK((cm_sav[{4, 2}] == 15));
    309 
    310     // Slice
    311     strided_span<int, 1> cm_sl = cm_sav[3];
    312 
    313     CHECK(cm_sl[0] == 10);
    314     CHECK(cm_sl[1] == 11);
    315     CHECK(cm_sl[2] == 12);
    316 
    317     // Section
    318     strided_span<int, 2> cm_sec = cm_sav.section({2, 1}, {3, 2});
    319 
    320     CHECK((cm_sec.bounds().index_bounds() == index<2>{3, 2}));
    321     CHECK((cm_sec[{0, 0}] == 8));
    322     CHECK((cm_sec[{0, 1}] == 9));
    323     CHECK((cm_sec[{1, 0}] == 11));
    324     CHECK((cm_sec[{2, 1}] == 15));
    325 }
    326 
    327 TEST_CASE("strided_span_bounds")
    328 {
    329     int arr[] = {0, 1, 2, 3};
    330     multi_span<int> av(arr);
    331 
    332     {
    333         // incorrect sections
    334 
    335         CHECK_THROWS_AS(av.section(0, 0)[0], fail_fast);
    336         CHECK_THROWS_AS(av.section(1, 0)[0], fail_fast);
    337         CHECK_THROWS_AS(av.section(1, 1)[1], fail_fast);
    338 
    339         CHECK_THROWS_AS(av.section(2, 5), fail_fast);
    340         CHECK_THROWS_AS(av.section(5, 2), fail_fast);
    341         CHECK_THROWS_AS(av.section(5, 0), fail_fast);
    342         CHECK_THROWS_AS(av.section(0, 5), fail_fast);
    343         CHECK_THROWS_AS(av.section(5, 5), fail_fast);
    344     }
    345 
    346     {
    347         // zero stride
    348         strided_span<int, 1> sav{av, {{4}, {}}};
    349         CHECK(sav[0] == 0);
    350         CHECK(sav[3] == 0);
    351         CHECK_THROWS_AS(sav[4], fail_fast);
    352     }
    353 
    354     {
    355         // zero extent
    356         strided_span<int, 1> sav{av, {{}, {1}}};
    357         CHECK_THROWS_AS(sav[0], fail_fast);
    358     }
    359 
    360     {
    361         // zero extent and stride
    362         strided_span<int, 1> sav{av, {{}, {}}};
    363         CHECK_THROWS_AS(sav[0], fail_fast);
    364     }
    365 
    366     {
    367         // strided array ctor with matching strided bounds
    368         strided_span<int, 1> sav{arr, {4, 1}};
    369         CHECK(sav.bounds().index_bounds() == index<1>{4});
    370         CHECK(sav[3] == 3);
    371         CHECK_THROWS_AS(sav[4], fail_fast);
    372     }
    373 
    374     {
    375         // strided array ctor with smaller strided bounds
    376         strided_span<int, 1> sav{arr, {2, 1}};
    377         CHECK(sav.bounds().index_bounds() == index<1>{2});
    378         CHECK(sav[1] == 1);
    379         CHECK_THROWS_AS(sav[2], fail_fast);
    380     }
    381 
    382     {
    383         // strided array ctor with fitting irregular bounds
    384         strided_span<int, 1> sav{arr, {2, 3}};
    385         CHECK(sav.bounds().index_bounds() == index<1>{2});
    386         CHECK(sav[0] == 0);
    387         CHECK(sav[1] == 3);
    388         CHECK_THROWS_AS(sav[2], fail_fast);
    389     }
    390 
    391     {
    392         // bounds cross data boundaries - from static arrays
    393         CHECK_THROWS_AS((strided_span<int, 1>{arr, {3, 2}}), fail_fast);
    394         CHECK_THROWS_AS((strided_span<int, 1>{arr, {3, 3}}), fail_fast);
    395         CHECK_THROWS_AS((strided_span<int, 1>{arr, {4, 5}}), fail_fast);
    396         CHECK_THROWS_AS((strided_span<int, 1>{arr, {5, 1}}), fail_fast);
    397         CHECK_THROWS_AS((strided_span<int, 1>{arr, {5, 5}}), fail_fast);
    398     }
    399 
    400     {
    401         // bounds cross data boundaries - from array view
    402         CHECK_THROWS_AS((strided_span<int, 1>{av, {3, 2}}), fail_fast);
    403         CHECK_THROWS_AS((strided_span<int, 1>{av, {3, 3}}), fail_fast);
    404         CHECK_THROWS_AS((strided_span<int, 1>{av, {4, 5}}), fail_fast);
    405         CHECK_THROWS_AS((strided_span<int, 1>{av, {5, 1}}), fail_fast);
    406         CHECK_THROWS_AS((strided_span<int, 1>{av, {5, 5}}), fail_fast);
    407     }
    408 
    409     {
    410         // bounds cross data boundaries - from dynamic arrays
    411         CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {3, 2}}), fail_fast);
    412         CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {3, 3}}), fail_fast);
    413         CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {4, 5}}), fail_fast);
    414         CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {5, 1}}), fail_fast);
    415         CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 4, {5, 5}}), fail_fast);
    416         CHECK_THROWS_AS((strided_span<int, 1>{av.data(), 2, {2, 2}}), fail_fast);
    417     }
    418 
    419 #ifdef CONFIRM_COMPILATION_ERRORS
    420     {
    421         strided_span<int, 1> sav0{av.data(), {3, 2}};
    422         strided_span<int, 1> sav1{arr, {1}};
    423         strided_span<int, 1> sav2{arr, {1, 1, 1}};
    424         strided_span<int, 1> sav3{av, {1}};
    425         strided_span<int, 1> sav4{av, {1, 1, 1}};
    426         strided_span<int, 2> sav5{av.as_multi_span(dim<2>(), dim<2>()), {1}};
    427         strided_span<int, 2> sav6{av.as_multi_span(dim<2>(), dim<2>()), {1, 1, 1}};
    428         strided_span<int, 2> sav7{av.as_multi_span(dim<2>(), dim<2>()),
    429                                   {{1, 1}, {1, 1}, {1, 1}}};
    430 
    431         index<1> index{0, 1};
    432         strided_span<int, 1> sav8{arr, {1, {1, 1}}};
    433         strided_span<int, 1> sav9{arr, {{1, 1}, {1, 1}}};
    434         strided_span<int, 1> sav10{av, {1, {1, 1}}};
    435         strided_span<int, 1> sav11{av, {{1, 1}, {1, 1}}};
    436         strided_span<int, 2> sav12{av.as_multi_span(dim<2>(), dim<2>()), {{1}, {1}}};
    437         strided_span<int, 2> sav13{av.as_multi_span(dim<2>(), dim<2>()), {{1}, {1, 1, 1}}};
    438         strided_span<int, 2> sav14{av.as_multi_span(dim<2>(), dim<2>()), {{1, 1, 1}, {1}}};
    439     }
    440 #endif
    441 }
    442 
    443 TEST_CASE("strided_span_type_conversion")
    444 {
    445     int arr[] = {0, 1, 2, 3};
    446     multi_span<int> av(arr);
    447 
    448     {
    449         strided_span<int, 1> sav{av.data(), av.size(), {av.size() / 2, 2}};
    450 #ifdef CONFIRM_COMPILATION_ERRORS
    451         strided_span<long, 1> lsav1 = sav.as_strided_span<long, 1>();
    452 #endif
    453     }
    454     {
    455         strided_span<int, 1> sav{av, {av.size() / 2, 2}};
    456 #ifdef CONFIRM_COMPILATION_ERRORS
    457         strided_span<long, 1> lsav1 = sav.as_strided_span<long, 1>();
    458 #endif
    459     }
    460 
    461     multi_span<const byte, dynamic_range> bytes = as_bytes(av);
    462 
    463     // retype strided array with regular strides - from raw data
    464     {
    465         strided_bounds<2> bounds{{2, bytes.size() / 4}, {bytes.size() / 2, 1}};
    466         strided_span<const byte, 2> sav2{bytes.data(), bytes.size(), bounds};
    467         strided_span<const int, 2> sav3 = sav2.as_strided_span<const int>();
    468         CHECK(sav3[0][0] == 0);
    469         CHECK(sav3[1][0] == 2);
    470         CHECK_THROWS_AS(sav3[1][1], fail_fast);
    471         CHECK_THROWS_AS(sav3[0][1], fail_fast);
    472     }
    473 
    474     // retype strided array with regular strides - from multi_span
    475     {
    476         strided_bounds<2> bounds{{2, bytes.size() / 4}, {bytes.size() / 2, 1}};
    477         multi_span<const byte, 2, dynamic_range> bytes2 =
    478             as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
    479         strided_span<const byte, 2> sav2{bytes2, bounds};
    480         strided_span<int, 2> sav3 = sav2.as_strided_span<int>();
    481         CHECK(sav3[0][0] == 0);
    482         CHECK(sav3[1][0] == 2);
    483         CHECK_THROWS_AS(sav3[1][1], fail_fast);
    484         CHECK_THROWS_AS(sav3[0][1], fail_fast);
    485     }
    486 
    487     // retype strided array with not enough elements - last dimension of the array is too small
    488     {
    489         strided_bounds<2> bounds{{4, 2}, {4, 1}};
    490         multi_span<const byte, 2, dynamic_range> bytes2 =
    491             as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
    492         strided_span<const byte, 2> sav2{bytes2, bounds};
    493         CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
    494     }
    495 
    496     // retype strided array with not enough elements - strides are too small
    497     {
    498         strided_bounds<2> bounds{{4, 2}, {2, 1}};
    499         multi_span<const byte, 2, dynamic_range> bytes2 =
    500             as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
    501         strided_span<const byte, 2> sav2{bytes2, bounds};
    502         CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
    503     }
    504 
    505     // retype strided array with not enough elements - last dimension does not divide by the new
    506     // typesize
    507     {
    508         strided_bounds<2> bounds{{2, 6}, {4, 1}};
    509         multi_span<const byte, 2, dynamic_range> bytes2 =
    510             as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
    511         strided_span<const byte, 2> sav2{bytes2, bounds};
    512         CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
    513     }
    514 
    515     // retype strided array with not enough elements - strides does not divide by the new
    516     // typesize
    517     {
    518         strided_bounds<2> bounds{{2, 1}, {6, 1}};
    519         multi_span<const byte, 2, dynamic_range> bytes2 =
    520             as_multi_span(bytes, dim<2>(), dim(bytes.size() / 2));
    521         strided_span<const byte, 2> sav2{bytes2, bounds};
    522         CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
    523     }
    524 
    525     // retype strided array with irregular strides - from raw data
    526     {
    527         strided_bounds<1> bounds{bytes.size() / 2, 2};
    528         strided_span<const byte, 1> sav2{bytes.data(), bytes.size(), bounds};
    529         CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
    530     }
    531 
    532     // retype strided array with irregular strides - from multi_span
    533     {
    534         strided_bounds<1> bounds{bytes.size() / 2, 2};
    535         strided_span<const byte, 1> sav2{bytes, bounds};
    536         CHECK_THROWS_AS(sav2.as_strided_span<int>(), fail_fast);
    537     }
    538 }
    539 
    540 TEST_CASE("empty_strided_spans")
    541 {
    542     {
    543         multi_span<int, 0> empty_av(nullptr);
    544         strided_span<int, 1> empty_sav{empty_av, {0, 1}};
    545 
    546         CHECK(empty_sav.bounds().index_bounds() == index<1>{0});
    547         CHECK_THROWS_AS(empty_sav[0], fail_fast);
    548         CHECK_THROWS_AS(empty_sav.begin()[0], fail_fast);
    549         CHECK_THROWS_AS(empty_sav.cbegin()[0], fail_fast);
    550 
    551         for (const auto& v : empty_sav) {
    552             (void) v;
    553             CHECK(false);
    554         }
    555     }
    556 
    557     {
    558         strided_span<int, 1> empty_sav{nullptr, 0, {0, 1}};
    559 
    560         CHECK(empty_sav.bounds().index_bounds() == index<1>{0});
    561         CHECK_THROWS_AS(empty_sav[0], fail_fast);
    562         CHECK_THROWS_AS(empty_sav.begin()[0], fail_fast);
    563         CHECK_THROWS_AS(empty_sav.cbegin()[0], fail_fast);
    564 
    565         for (const auto& v : empty_sav) {
    566             (void) v;
    567             CHECK(false);
    568         }
    569     }
    570 }
    571 
    572 void iterate_every_other_element(multi_span<int, dynamic_range> av)
    573 {
    574     // pick every other element
    575 
    576     auto length = av.size() / 2;
    577 #if _MSC_VER > 1800
    578     auto bounds = strided_bounds<1>({length}, {2});
    579 #else
    580     auto bounds = strided_bounds<1>(index<1>{length}, index<1>{2});
    581 #endif
    582     strided_span<int, 1> strided(&av.data()[1], av.size() - 1, bounds);
    583 
    584     CHECK(strided.size() == length);
    585     CHECK(strided.bounds().index_bounds()[0] == length);
    586     for (auto i = 0; i < strided.size(); ++i) {
    587         CHECK(strided[i] == av[2 * i + 1]);
    588     }
    589 
    590     int idx = 0;
    591     for (auto num : strided) {
    592         CHECK(num == av[2 * idx + 1]);
    593         idx++;
    594     }
    595 }
    596 
    597 TEST_CASE("strided_span_section_iteration")
    598 {
    599     int arr[8] = {4, 0, 5, 1, 6, 2, 7, 3};
    600 
    601     // static bounds
    602     {
    603         multi_span<int, 8> av(arr, 8);
    604         iterate_every_other_element(av);
    605     }
    606 
    607     // dynamic bounds
    608     {
    609         multi_span<int, dynamic_range> av(arr, 8);
    610         iterate_every_other_element(av);
    611     }
    612 }
    613 
    614 TEST_CASE("dynamic_strided_span_section_iteration")
    615 {
    616     auto arr = new int[8];
    617     for (int i = 0; i < 4; ++i) {
    618         arr[2 * i] = 4 + i;
    619         arr[2 * i + 1] = i;
    620     }
    621 
    622     auto av = as_multi_span(arr, 8);
    623     iterate_every_other_element(av);
    624 
    625     delete[] arr;
    626 }
    627 
    628 void iterate_second_slice(multi_span<int, dynamic_range, dynamic_range, dynamic_range> av)
    629 {
    630     const int expected[6] = {2, 3, 10, 11, 18, 19};
    631     auto section = av.section({0, 1, 0}, {3, 1, 2});
    632 
    633     for (auto i = 0; i < section.extent<0>(); ++i) {
    634         for (auto j = 0; j < section.extent<1>(); ++j)
    635             for (auto k = 0; k < section.extent<2>(); ++k) {
    636                 auto idx = index<3>{i, j, k}; // avoid braces in the CHECK macro
    637                 CHECK(section[idx] == expected[2 * i + 2 * j + k]);
    638             }
    639     }
    640 
    641     for (auto i = 0; i < section.extent<0>(); ++i) {
    642         for (auto j = 0; j < section.extent<1>(); ++j)
    643             for (auto k = 0; k < section.extent<2>(); ++k)
    644                 CHECK(section[i][j][k] == expected[2 * i + 2 * j + k]);
    645     }
    646 
    647     int i = 0;
    648     for (const auto num : section) {
    649         CHECK(num == expected[i]);
    650         i++;
    651     }
    652 }
    653 
    654 TEST_CASE("strided_span_section_iteration_3d")
    655 {
    656     int arr[3][4][2]{};
    657     for (auto i = 0; i < 3; ++i) {
    658         for (auto j = 0; j < 4; ++j)
    659             for (auto k = 0; k < 2; ++k) arr[i][j][k] = 8 * i + 2 * j + k;
    660     }
    661 
    662     {
    663         multi_span<int, 3, 4, 2> av = arr;
    664         iterate_second_slice(av);
    665     }
    666 }
    667 
    668 TEST_CASE("dynamic_strided_span_section_iteration_3d")
    669 {
    670     const auto height = 12, width = 2;
    671     const auto size = height * width;
    672 
    673     auto arr = new int[static_cast<std::size_t>(size)];
    674     for (auto i = 0; i < size; ++i) {
    675         arr[i] = i;
    676     }
    677 
    678     {
    679         auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<4>(), dim<2>());
    680         iterate_second_slice(av);
    681     }
    682 
    683     {
    684         auto av = as_multi_span(as_multi_span(arr, 24), dim(3), dim<4>(), dim<2>());
    685         iterate_second_slice(av);
    686     }
    687 
    688     {
    689         auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim(4), dim<2>());
    690         iterate_second_slice(av);
    691     }
    692 
    693     {
    694         auto av = as_multi_span(as_multi_span(arr, 24), dim<3>(), dim<4>(), dim(2));
    695         iterate_second_slice(av);
    696     }
    697     delete[] arr;
    698 }
    699 
    700 TEST_CASE("strided_span_conversion")
    701 {
    702     // get an multi_span of 'c' values from the list of X's
    703 
    704     struct X
    705     {
    706         int a;
    707         int b;
    708         int c;
    709     };
    710 
    711     X arr[4] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11}};
    712 
    713     int s = sizeof(int) / sizeof(byte);
    714     auto d2 = 3 * s;
    715     auto d1 = narrow_cast<int>(sizeof(int)) * 12 / d2;
    716 
    717     // convert to 4x12 array of bytes
    718     auto av = as_multi_span(as_bytes(as_multi_span(arr, 4)), dim(d1), dim(d2));
    719 
    720     CHECK(av.bounds().index_bounds()[0] == 4);
    721     CHECK(av.bounds().index_bounds()[1] == 12);
    722 
    723     // get the last 4 columns
    724     auto section = av.section({0, 2 * s}, {4, s}); // { { arr[0].c[0], arr[0].c[1], arr[0].c[2],
    725                                                    // arr[0].c[3] } , { arr[1].c[0], ... } , ...
    726                                                    // }
    727 
    728     // convert to array 4x1 array of integers
    729     auto cs = section.as_strided_span<int>(); // { { arr[0].c }, {arr[1].c } , ... }
    730 
    731     CHECK(cs.bounds().index_bounds()[0] == 4);
    732     CHECK(cs.bounds().index_bounds()[1] == 1);
    733 
    734     // transpose to 1x4 array
    735     strided_bounds<2> reverse_bounds{
    736         {cs.bounds().index_bounds()[1], cs.bounds().index_bounds()[0]},
    737         {cs.bounds().strides()[1], cs.bounds().strides()[0]}};
    738 
    739     strided_span<int, 2> transposed{cs.data(), cs.bounds().total_size(), reverse_bounds};
    740 
    741     // slice to get a one-dimensional array of c's
    742     strided_span<int, 1> result = transposed[0];
    743 
    744     CHECK(result.bounds().index_bounds()[0] == 4);
    745     CHECK_THROWS_AS(result.bounds().index_bounds()[1], fail_fast);
    746 
    747     int i = 0;
    748     for (auto& num : result) {
    749         CHECK(num == arr[i].c);
    750         i++;
    751     }
    752 }
    753