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
28using namespace std;
29using namespace gsl;
30
31namespace
32{
33struct BaseClass
34{
35};
36struct DerivedClass : BaseClass
37{
38};
39}
40
41TEST_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
51TEST_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
69TEST_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
269TEST_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
295TEST_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
327TEST_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
443TEST_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
540TEST_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
572void 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
597TEST_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
614TEST_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
628void 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
654TEST_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
668TEST_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
700TEST_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