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