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#pragma once
18
19#ifndef GSL_MULTI_SPAN_H
20#define GSL_MULTI_SPAN_H
21
22#include <gsl/gsl_assert>
23#include <gsl/gsl_byte>
24#include <gsl/gsl_util>
25
26#include <algorithm>
27#include <array>
28#include <cassert>
29#include <cstddef>
30#include <cstdint>
31#include <functional>
32#include <iterator>
33#include <limits>
34#include <new>
35#include <numeric>
36#include <stdexcept>
37#include <type_traits>
38#include <utility>
39
40#ifdef _MSC_VER
41
42// turn off some warnings that are noisy about our Expects statements
43#pragma warning(push)
44#pragma warning(disable : 4127) // conditional expression is constant
45#pragma warning(disable : 4702) // unreachable code
46
47#if _MSC_VER < 1910
48#pragma push_macro("constexpr")
49#define constexpr /*constexpr*/
50
51#endif                          // _MSC_VER < 1910
52#endif                          // _MSC_VER
53
54#ifdef GSL_THROW_ON_CONTRACT_VIOLATION
55#define GSL_NOEXCEPT /*noexcept*/
56#else
57#define GSL_NOEXCEPT noexcept
58#endif // GSL_THROW_ON_CONTRACT_VIOLATION
59
60namespace gsl
61{
62
63/*
64** begin definitions of index and bounds
65*/
66namespace details
67{
68    template <typename SizeType>
69    struct SizeTypeTraits
70    {
71        static const SizeType max_value = std::numeric_limits<SizeType>::max();
72    };
73
74    template <typename... Ts>
75    class are_integral : public std::integral_constant<bool, true>
76    {
77    };
78
79    template <typename T, typename... Ts>
80    class are_integral<T, Ts...>
81        : public std::integral_constant<bool,
82                                        std::is_integral<T>::value && are_integral<Ts...>::value>
83    {
84    };
85}
86
87template <std::size_t Rank>
88class index final
89{
90    static_assert(Rank > 0, "Rank must be greater than 0!");
91
92    template <std::size_t OtherRank>
93    friend class index;
94
95public:
96    static const std::size_t rank = Rank;
97    using value_type = std::ptrdiff_t;
98    using size_type = value_type;
99    using reference = std::add_lvalue_reference_t<value_type>;
100    using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>;
101
102    constexpr index() GSL_NOEXCEPT {}
103
104    constexpr index(const value_type (&values)[Rank]) GSL_NOEXCEPT
105    {
106        std::copy(values, values + Rank, elems);
107    }
108
109    template <typename... Ts, typename = std::enable_if_t<(sizeof...(Ts) == Rank) &&
110                                                          details::are_integral<Ts...>::value>>
111    constexpr index(Ts... ds) GSL_NOEXCEPT : elems{narrow_cast<value_type>(ds)...}
112    {
113    }
114
115    constexpr index(const index& other) GSL_NOEXCEPT = default;
116
117    constexpr index& operator=(const index& rhs) GSL_NOEXCEPT = default;
118
119    // Preconditions: component_idx < rank
120    constexpr reference operator[](std::size_t component_idx)
121    {
122        Expects(component_idx < Rank); // Component index must be less than rank
123        return elems[component_idx];
124    }
125
126    // Preconditions: component_idx < rank
127    constexpr const_reference operator[](std::size_t component_idx) const GSL_NOEXCEPT
128    {
129        Expects(component_idx < Rank); // Component index must be less than rank
130        return elems[component_idx];
131    }
132
133    constexpr bool operator==(const index& rhs) const GSL_NOEXCEPT
134    {
135        return std::equal(elems, elems + rank, rhs.elems);
136    }
137
138    constexpr bool operator!=(const index& rhs) const GSL_NOEXCEPT { return !(this == rhs); }
139
140    constexpr index operator+() const GSL_NOEXCEPT { return *this; }
141
142    constexpr index operator-() const GSL_NOEXCEPT
143    {
144        index ret = *this;
145        std::transform(ret, ret + rank, ret, std::negate<value_type>{});
146        return ret;
147    }
148
149    constexpr index operator+(const index& rhs) const GSL_NOEXCEPT
150    {
151        index ret = *this;
152        ret += rhs;
153        return ret;
154    }
155
156    constexpr index operator-(const index& rhs) const GSL_NOEXCEPT
157    {
158        index ret = *this;
159        ret -= rhs;
160        return ret;
161    }
162
163    constexpr index& operator+=(const index& rhs) GSL_NOEXCEPT
164    {
165        std::transform(elems, elems + rank, rhs.elems, elems, std::plus<value_type>{});
166        return *this;
167    }
168
169    constexpr index& operator-=(const index& rhs) GSL_NOEXCEPT
170    {
171        std::transform(elems, elems + rank, rhs.elems, elems, std::minus<value_type>{});
172        return *this;
173    }
174
175    constexpr index operator*(value_type v) const GSL_NOEXCEPT
176    {
177        index ret = *this;
178        ret *= v;
179        return ret;
180    }
181
182    constexpr index operator/(value_type v) const GSL_NOEXCEPT
183    {
184        index ret = *this;
185        ret /= v;
186        return ret;
187    }
188
189    friend constexpr index operator*(value_type v, const index& rhs) GSL_NOEXCEPT
190    {
191        return rhs * v;
192    }
193
194    constexpr index& operator*=(value_type v) GSL_NOEXCEPT
195    {
196        std::transform(elems, elems + rank, elems,
197                       [v](value_type x) { return std::multiplies<value_type>{}(x, v); });
198        return *this;
199    }
200
201    constexpr index& operator/=(value_type v) GSL_NOEXCEPT
202    {
203        std::transform(elems, elems + rank, elems,
204                       [v](value_type x) { return std::divides<value_type>{}(x, v); });
205        return *this;
206    }
207
208private:
209    value_type elems[Rank] = {};
210};
211
212#if !defined(_MSC_VER) || _MSC_VER >= 1910
213
214struct static_bounds_dynamic_range_t
215{
216    template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
217    constexpr operator T() const GSL_NOEXCEPT
218    {
219        return narrow_cast<T>(-1);
220    }
221};
222
223constexpr bool operator==(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT
224{
225    return true;
226}
227
228constexpr bool operator!=(static_bounds_dynamic_range_t, static_bounds_dynamic_range_t) GSL_NOEXCEPT
229{
230    return false;
231}
232
233template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
234constexpr bool operator==(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT
235{
236    return narrow_cast<T>(-1) == other;
237}
238
239template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
240constexpr bool operator==(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT
241{
242    return right == left;
243}
244
245template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
246constexpr bool operator!=(static_bounds_dynamic_range_t, T other) GSL_NOEXCEPT
247{
248    return narrow_cast<T>(-1) != other;
249}
250
251template <typename T, typename Dummy = std::enable_if_t<std::is_integral<T>::value>>
252constexpr bool operator!=(T left, static_bounds_dynamic_range_t right) GSL_NOEXCEPT
253{
254    return right != left;
255}
256
257constexpr static_bounds_dynamic_range_t dynamic_range{};
258#else
259const std::ptrdiff_t dynamic_range = -1;
260#endif
261
262struct generalized_mapping_tag
263{
264};
265struct contiguous_mapping_tag : generalized_mapping_tag
266{
267};
268
269namespace details
270{
271
272    template <std::ptrdiff_t Left, std::ptrdiff_t Right>
273    struct LessThan
274    {
275        static const bool value = Left < Right;
276    };
277
278    template <std::ptrdiff_t... Ranges>
279    struct BoundsRanges
280    {
281        using size_type = std::ptrdiff_t;
282        static const size_type Depth = 0;
283        static const size_type DynamicNum = 0;
284        static const size_type CurrentRange = 1;
285        static const size_type TotalSize = 1;
286
287        // TODO : following signature is for work around VS bug
288        template <typename OtherRange>
289        BoundsRanges(const OtherRange&, bool /* firstLevel */)
290        {
291        }
292
293        BoundsRanges(const std::ptrdiff_t* const) {}
294        BoundsRanges() = default;
295
296        template <typename T, std::size_t Dim>
297        void serialize(T&) const
298        {
299        }
300
301        template <typename T, std::size_t Dim>
302        size_type linearize(const T&) const
303        {
304            return 0;
305        }
306
307        template <typename T, std::size_t Dim>
308        size_type contains(const T&) const
309        {
310            return -1;
311        }
312
313        size_type elementNum(std::size_t) const GSL_NOEXCEPT { return 0; }
314
315        size_type totalSize() const GSL_NOEXCEPT { return TotalSize; }
316
317        bool operator==(const BoundsRanges&) const GSL_NOEXCEPT { return true; }
318    };
319
320    template <std::ptrdiff_t... RestRanges>
321    struct BoundsRanges<dynamic_range, RestRanges...> : BoundsRanges<RestRanges...>
322    {
323        using Base = BoundsRanges<RestRanges...>;
324        using size_type = std::ptrdiff_t;
325        static const std::size_t Depth = Base::Depth + 1;
326        static const std::size_t DynamicNum = Base::DynamicNum + 1;
327        static const size_type CurrentRange = dynamic_range;
328        static const size_type TotalSize = dynamic_range;
329
330    private:
331        size_type m_bound;
332
333    public:
334        BoundsRanges(const std::ptrdiff_t* const arr)
335            : Base(arr + 1), m_bound(*arr * this->Base::totalSize())
336        {
337            Expects(0 <= *arr);
338        }
339
340        BoundsRanges() : m_bound(0) {}
341
342        template <std::ptrdiff_t OtherRange, std::ptrdiff_t... RestOtherRanges>
343        BoundsRanges(const BoundsRanges<OtherRange, RestOtherRanges...>& other,
344                     bool /* firstLevel */ = true)
345            : Base(static_cast<const BoundsRanges<RestOtherRanges...>&>(other), false)
346            , m_bound(other.totalSize())
347        {
348        }
349
350        template <typename T, std::size_t Dim = 0>
351        void serialize(T& arr) const
352        {
353            arr[Dim] = elementNum();
354            this->Base::template serialize<T, Dim + 1>(arr);
355        }
356
357        template <typename T, std::size_t Dim = 0>
358        size_type linearize(const T& arr) const
359        {
360            const size_type index = this->Base::totalSize() * arr[Dim];
361            Expects(index < m_bound);
362            return index + this->Base::template linearize<T, Dim + 1>(arr);
363        }
364
365        template <typename T, std::size_t Dim = 0>
366        size_type contains(const T& arr) const
367        {
368            const ptrdiff_t last = this->Base::template contains<T, Dim + 1>(arr);
369            if (last == -1) return -1;
370            const ptrdiff_t cur = this->Base::totalSize() * arr[Dim];
371            return cur < m_bound ? cur + last : -1;
372        }
373
374        size_type totalSize() const GSL_NOEXCEPT { return m_bound; }
375
376        size_type elementNum() const GSL_NOEXCEPT { return totalSize() / this->Base::totalSize(); }
377
378        size_type elementNum(std::size_t dim) const GSL_NOEXCEPT
379        {
380            if (dim > 0)
381                return this->Base::elementNum(dim - 1);
382            else
383                return elementNum();
384        }
385
386        bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT
387        {
388            return m_bound == rhs.m_bound &&
389                   static_cast<const Base&>(*this) == static_cast<const Base&>(rhs);
390        }
391    };
392
393    template <std::ptrdiff_t CurRange, std::ptrdiff_t... RestRanges>
394    struct BoundsRanges<CurRange, RestRanges...> : BoundsRanges<RestRanges...>
395    {
396        using Base = BoundsRanges<RestRanges...>;
397        using size_type = std::ptrdiff_t;
398        static const std::size_t Depth = Base::Depth + 1;
399        static const std::size_t DynamicNum = Base::DynamicNum;
400        static const size_type CurrentRange = CurRange;
401        static const size_type TotalSize =
402            Base::TotalSize == dynamic_range ? dynamic_range : CurrentRange * Base::TotalSize;
403
404        BoundsRanges(const std::ptrdiff_t* const arr) : Base(arr) {}
405        BoundsRanges() = default;
406
407        template <std::ptrdiff_t OtherRange, std::ptrdiff_t... RestOtherRanges>
408        BoundsRanges(const BoundsRanges<OtherRange, RestOtherRanges...>& other,
409                     bool firstLevel = true)
410            : Base(static_cast<const BoundsRanges<RestOtherRanges...>&>(other), false)
411        {
412            (void) firstLevel;
413        }
414
415        template <typename T, std::size_t Dim = 0>
416        void serialize(T& arr) const
417        {
418            arr[Dim] = elementNum();
419            this->Base::template serialize<T, Dim + 1>(arr);
420        }
421
422        template <typename T, std::size_t Dim = 0>
423        size_type linearize(const T& arr) const
424        {
425            Expects(arr[Dim] >= 0 && arr[Dim] < CurrentRange); // Index is out of range
426            return this->Base::totalSize() * arr[Dim] +
427                   this->Base::template linearize<T, Dim + 1>(arr);
428        }
429
430        template <typename T, std::size_t Dim = 0>
431        size_type contains(const T& arr) const
432        {
433            if (arr[Dim] >= CurrentRange) return -1;
434            const size_type last = this->Base::template contains<T, Dim + 1>(arr);
435            if (last == -1) return -1;
436            return this->Base::totalSize() * arr[Dim] + last;
437        }
438
439        size_type totalSize() const GSL_NOEXCEPT { return CurrentRange * this->Base::totalSize(); }
440
441        size_type elementNum() const GSL_NOEXCEPT { return CurrentRange; }
442
443        size_type elementNum(std::size_t dim) const GSL_NOEXCEPT
444        {
445            if (dim > 0)
446                return this->Base::elementNum(dim - 1);
447            else
448                return elementNum();
449        }
450
451        bool operator==(const BoundsRanges& rhs) const GSL_NOEXCEPT
452        {
453            return static_cast<const Base&>(*this) == static_cast<const Base&>(rhs);
454        }
455    };
456
457    template <typename SourceType, typename TargetType>
458    struct BoundsRangeConvertible
459        : public std::integral_constant<bool, (SourceType::TotalSize >= TargetType::TotalSize ||
460                                               TargetType::TotalSize == dynamic_range ||
461                                               SourceType::TotalSize == dynamic_range ||
462                                               TargetType::TotalSize == 0)>
463    {
464    };
465
466    template <typename TypeChain>
467    struct TypeListIndexer
468    {
469        const TypeChain& obj_;
470        TypeListIndexer(const TypeChain& obj) : obj_(obj) {}
471
472        template <std::size_t N>
473        const TypeChain& getObj(std::true_type)
474        {
475            return obj_;
476        }
477
478        template <std::size_t N, typename MyChain = TypeChain,
479                  typename MyBase = typename MyChain::Base>
480        auto getObj(std::false_type)
481            -> decltype(TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>())
482        {
483            return TypeListIndexer<MyBase>(static_cast<const MyBase&>(obj_)).template get<N>();
484        }
485
486        template <std::size_t N>
487        auto get() -> decltype(getObj<N - 1>(std::integral_constant<bool, N == 0>()))
488        {
489            return getObj<N - 1>(std::integral_constant<bool, N == 0>());
490        }
491    };
492
493    template <typename TypeChain>
494    TypeListIndexer<TypeChain> createTypeListIndexer(const TypeChain& obj)
495    {
496        return TypeListIndexer<TypeChain>(obj);
497    }
498
499    template <std::size_t Rank, bool Enabled = (Rank > 1),
500              typename Ret = std::enable_if_t<Enabled, index<Rank - 1>>>
501    inline constexpr Ret shift_left(const index<Rank>& other) GSL_NOEXCEPT
502    {
503        Ret ret{};
504        for (std::size_t i = 0; i < Rank - 1; ++i) {
505            ret[i] = other[i + 1];
506        }
507        return ret;
508    }
509}
510
511template <typename IndexType>
512class bounds_iterator;
513
514template <std::ptrdiff_t... Ranges>
515class static_bounds
516{
517public:
518    static_bounds(const details::BoundsRanges<Ranges...>&) {}
519};
520
521template <std::ptrdiff_t FirstRange, std::ptrdiff_t... RestRanges>
522class static_bounds<FirstRange, RestRanges...>
523{
524    using MyRanges = details::BoundsRanges<FirstRange, RestRanges...>;
525
526    MyRanges m_ranges;
527    constexpr static_bounds(const MyRanges& range) : m_ranges(range) {}
528
529    template <std::ptrdiff_t... OtherRanges>
530    friend class static_bounds;
531
532public:
533    static const std::size_t rank = MyRanges::Depth;
534    static const std::size_t dynamic_rank = MyRanges::DynamicNum;
535    static const std::ptrdiff_t static_size = MyRanges::TotalSize;
536
537    using size_type = std::ptrdiff_t;
538    using index_type = index<rank>;
539    using const_index_type = std::add_const_t<index_type>;
540    using iterator = bounds_iterator<const_index_type>;
541    using const_iterator = bounds_iterator<const_index_type>;
542    using difference_type = std::ptrdiff_t;
543    using sliced_type = static_bounds<RestRanges...>;
544    using mapping_type = contiguous_mapping_tag;
545
546    constexpr static_bounds(const static_bounds&) = default;
547
548    template <typename SourceType, typename TargetType, std::size_t Rank>
549    struct BoundsRangeConvertible2;
550
551    template <std::size_t Rank, typename SourceType, typename TargetType,
552              typename Ret = BoundsRangeConvertible2<typename SourceType::Base,
553                                                     typename TargetType::Base, Rank>>
554    static auto helpBoundsRangeConvertible(SourceType, TargetType, std::true_type) -> Ret;
555
556    template <std::size_t Rank, typename SourceType, typename TargetType>
557    static auto helpBoundsRangeConvertible(SourceType, TargetType, ...) -> std::false_type;
558
559    template <typename SourceType, typename TargetType, std::size_t Rank>
560    struct BoundsRangeConvertible2
561        : decltype(helpBoundsRangeConvertible<Rank - 1>(
562              SourceType(), TargetType(),
563              std::integral_constant<bool,
564                                     SourceType::Depth == TargetType::Depth &&
565                                         (SourceType::CurrentRange == TargetType::CurrentRange ||
566                                          TargetType::CurrentRange == dynamic_range ||
567                                          SourceType::CurrentRange == dynamic_range)>()))
568    {
569    };
570
571    template <typename SourceType, typename TargetType>
572    struct BoundsRangeConvertible2<SourceType, TargetType, 0> : std::true_type
573    {
574    };
575
576    template <typename SourceType, typename TargetType, std::ptrdiff_t Rank = TargetType::Depth>
577    struct BoundsRangeConvertible
578        : decltype(helpBoundsRangeConvertible<Rank - 1>(
579              SourceType(), TargetType(),
580              std::integral_constant<bool,
581                                     SourceType::Depth == TargetType::Depth &&
582                                         (!details::LessThan<SourceType::CurrentRange,
583                                                             TargetType::CurrentRange>::value ||
584                                          TargetType::CurrentRange == dynamic_range ||
585                                          SourceType::CurrentRange == dynamic_range)>()))
586    {
587    };
588
589    template <typename SourceType, typename TargetType>
590    struct BoundsRangeConvertible<SourceType, TargetType, 0> : std::true_type
591    {
592    };
593
594    template <std::ptrdiff_t... Ranges,
595              typename = std::enable_if_t<details::BoundsRangeConvertible<
596                  details::BoundsRanges<Ranges...>,
597                  details::BoundsRanges<FirstRange, RestRanges...>>::value>>
598    constexpr static_bounds(const static_bounds<Ranges...>& other) : m_ranges(other.m_ranges)
599    {
600        Expects((MyRanges::DynamicNum == 0 && details::BoundsRanges<Ranges...>::DynamicNum == 0) ||
601                MyRanges::DynamicNum > 0 || other.m_ranges.totalSize() >= m_ranges.totalSize());
602    }
603
604    constexpr static_bounds(std::initializer_list<size_type> il)
605        : m_ranges(static_cast<const std::ptrdiff_t*>(il.begin()))
606    {
607        // Size of the initializer list must match the rank of the array
608        Expects((MyRanges::DynamicNum == 0 && il.size() == 1 && *il.begin() == static_size) ||
609                MyRanges::DynamicNum == il.size());
610        // Size of the range must be less than the max element of the size type
611        Expects(m_ranges.totalSize() <= PTRDIFF_MAX);
612    }
613
614    constexpr static_bounds() = default;
615
616    constexpr sliced_type slice() const GSL_NOEXCEPT
617    {
618        return sliced_type{static_cast<const details::BoundsRanges<RestRanges...>&>(m_ranges)};
619    }
620
621    constexpr size_type stride() const GSL_NOEXCEPT { return rank > 1 ? slice().size() : 1; }
622
623    constexpr size_type size() const GSL_NOEXCEPT { return m_ranges.totalSize(); }
624
625    constexpr size_type total_size() const GSL_NOEXCEPT { return m_ranges.totalSize(); }
626
627    constexpr size_type linearize(const index_type& idx) const { return m_ranges.linearize(idx); }
628
629    constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT
630    {
631        return m_ranges.contains(idx) != -1;
632    }
633
634    constexpr size_type operator[](std::size_t index) const GSL_NOEXCEPT
635    {
636        return m_ranges.elementNum(index);
637    }
638
639    template <std::size_t Dim = 0>
640    constexpr size_type extent() const GSL_NOEXCEPT
641    {
642        static_assert(Dim < rank,
643                      "dimension should be less than rank (dimension count starts from 0)");
644        return details::createTypeListIndexer(m_ranges).template get<Dim>().elementNum();
645    }
646
647    template <typename IntType>
648    constexpr size_type extent(IntType dim) const GSL_NOEXCEPT
649    {
650        static_assert(std::is_integral<IntType>::value,
651                      "Dimension parameter must be supplied as an integral type.");
652        auto real_dim = narrow_cast<std::size_t>(dim);
653        Expects(real_dim < rank);
654
655        return m_ranges.elementNum(real_dim);
656    }
657
658    constexpr index_type index_bounds() const GSL_NOEXCEPT
659    {
660        size_type extents[rank] = {};
661        m_ranges.serialize(extents);
662        return {extents};
663    }
664
665    template <std::ptrdiff_t... Ranges>
666    constexpr bool operator==(const static_bounds<Ranges...>& rhs) const GSL_NOEXCEPT
667    {
668        return this->size() == rhs.size();
669    }
670
671    template <std::ptrdiff_t... Ranges>
672    constexpr bool operator!=(const static_bounds<Ranges...>& rhs) const GSL_NOEXCEPT
673    {
674        return !(*this == rhs);
675    }
676
677    constexpr const_iterator begin() const GSL_NOEXCEPT
678    {
679        return const_iterator(*this, index_type{});
680    }
681
682    constexpr const_iterator end() const GSL_NOEXCEPT
683    {
684        return const_iterator(*this, this->index_bounds());
685    }
686};
687
688template <std::size_t Rank>
689class strided_bounds
690{
691    template <std::size_t OtherRank>
692    friend class strided_bounds;
693
694public:
695    static const std::size_t rank = Rank;
696    using value_type = std::ptrdiff_t;
697    using reference = std::add_lvalue_reference_t<value_type>;
698    using const_reference = std::add_const_t<reference>;
699    using size_type = value_type;
700    using difference_type = value_type;
701    using index_type = index<rank>;
702    using const_index_type = std::add_const_t<index_type>;
703    using iterator = bounds_iterator<const_index_type>;
704    using const_iterator = bounds_iterator<const_index_type>;
705    static const value_type dynamic_rank = rank;
706    static const value_type static_size = dynamic_range;
707    using sliced_type = std::conditional_t<rank != 0, strided_bounds<rank - 1>, void>;
708    using mapping_type = generalized_mapping_tag;
709
710    constexpr strided_bounds(const strided_bounds&) GSL_NOEXCEPT = default;
711
712    constexpr strided_bounds& operator=(const strided_bounds&) GSL_NOEXCEPT = default;
713
714    constexpr strided_bounds(const value_type (&values)[rank], index_type strides)
715        : m_extents(values), m_strides(std::move(strides))
716    {
717    }
718
719    constexpr strided_bounds(const index_type& extents, const index_type& strides) GSL_NOEXCEPT
720        : m_extents(extents),
721          m_strides(strides)
722    {
723    }
724
725    constexpr index_type strides() const GSL_NOEXCEPT { return m_strides; }
726
727    constexpr size_type total_size() const GSL_NOEXCEPT
728    {
729        size_type ret = 0;
730        for (std::size_t i = 0; i < rank; ++i) {
731            ret += (m_extents[i] - 1) * m_strides[i];
732        }
733        return ret + 1;
734    }
735
736    constexpr size_type size() const GSL_NOEXCEPT
737    {
738        size_type ret = 1;
739        for (std::size_t i = 0; i < rank; ++i) {
740            ret *= m_extents[i];
741        }
742        return ret;
743    }
744
745    constexpr bool contains(const index_type& idx) const GSL_NOEXCEPT
746    {
747        for (std::size_t i = 0; i < rank; ++i) {
748            if (idx[i] < 0 || idx[i] >= m_extents[i]) return false;
749        }
750        return true;
751    }
752
753    constexpr size_type linearize(const index_type& idx) const GSL_NOEXCEPT
754    {
755        size_type ret = 0;
756        for (std::size_t i = 0; i < rank; i++) {
757            Expects(idx[i] < m_extents[i]); // index is out of bounds of the array
758            ret += idx[i] * m_strides[i];
759        }
760        return ret;
761    }
762
763    constexpr size_type stride() const GSL_NOEXCEPT { return m_strides[0]; }
764
765    template <bool Enabled = (rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
766    constexpr sliced_type slice() const
767    {
768        return {details::shift_left(m_extents), details::shift_left(m_strides)};
769    }
770
771    template <std::size_t Dim = 0>
772    constexpr size_type extent() const GSL_NOEXCEPT
773    {
774        static_assert(Dim < Rank,
775                      "dimension should be less than rank (dimension count starts from 0)");
776        return m_extents[Dim];
777    }
778
779    constexpr index_type index_bounds() const GSL_NOEXCEPT { return m_extents; }
780    constexpr const_iterator begin() const GSL_NOEXCEPT
781    {
782        return const_iterator{*this, index_type{}};
783    }
784
785    constexpr const_iterator end() const GSL_NOEXCEPT
786    {
787        return const_iterator{*this, index_bounds()};
788    }
789
790private:
791    index_type m_extents;
792    index_type m_strides;
793};
794
795template <typename T>
796struct is_bounds : std::integral_constant<bool, false>
797{
798};
799template <std::ptrdiff_t... Ranges>
800struct is_bounds<static_bounds<Ranges...>> : std::integral_constant<bool, true>
801{
802};
803template <std::size_t Rank>
804struct is_bounds<strided_bounds<Rank>> : std::integral_constant<bool, true>
805{
806};
807
808template <typename IndexType>
809class bounds_iterator : public std::iterator<std::random_access_iterator_tag, IndexType>
810{
811private:
812    using Base = std::iterator<std::random_access_iterator_tag, IndexType>;
813
814public:
815    static const std::size_t rank = IndexType::rank;
816    using typename Base::reference;
817    using typename Base::pointer;
818    using typename Base::difference_type;
819    using typename Base::value_type;
820    using index_type = value_type;
821    using index_size_type = typename IndexType::value_type;
822    template <typename Bounds>
823    explicit bounds_iterator(const Bounds& bnd, value_type curr) GSL_NOEXCEPT
824        : boundary_(bnd.index_bounds()),
825          curr_(std::move(curr))
826    {
827        static_assert(is_bounds<Bounds>::value, "Bounds type must be provided");
828    }
829
830    constexpr reference operator*() const GSL_NOEXCEPT { return curr_; }
831
832    constexpr pointer operator->() const GSL_NOEXCEPT { return &curr_; }
833
834    constexpr bounds_iterator& operator++() GSL_NOEXCEPT
835    {
836        for (std::size_t i = rank; i-- > 0;) {
837            if (curr_[i] < boundary_[i] - 1) {
838                curr_[i]++;
839                return *this;
840            }
841            curr_[i] = 0;
842        }
843        // If we're here we've wrapped over - set to past-the-end.
844        curr_ = boundary_;
845        return *this;
846    }
847
848    constexpr bounds_iterator operator++(int) GSL_NOEXCEPT
849    {
850        auto ret = *this;
851        ++(*this);
852        return ret;
853    }
854
855    constexpr bounds_iterator& operator--() GSL_NOEXCEPT
856    {
857        if (!less(curr_, boundary_)) {
858            // if at the past-the-end, set to last element
859            for (std::size_t i = 0; i < rank; ++i) {
860                curr_[i] = boundary_[i] - 1;
861            }
862            return *this;
863        }
864        for (std::size_t i = rank; i-- > 0;) {
865            if (curr_[i] >= 1) {
866                curr_[i]--;
867                return *this;
868            }
869            curr_[i] = boundary_[i] - 1;
870        }
871        // If we're here the preconditions were violated
872        // "pre: there exists s such that r == ++s"
873        Expects(false);
874        return *this;
875    }
876
877    constexpr bounds_iterator operator--(int) GSL_NOEXCEPT
878    {
879        auto ret = *this;
880        --(*this);
881        return ret;
882    }
883
884    constexpr bounds_iterator operator+(difference_type n) const GSL_NOEXCEPT
885    {
886        bounds_iterator ret{*this};
887        return ret += n;
888    }
889
890    constexpr bounds_iterator& operator+=(difference_type n) GSL_NOEXCEPT
891    {
892        auto linear_idx = linearize(curr_) + n;
893        std::remove_const_t<value_type> stride = 0;
894        stride[rank - 1] = 1;
895        for (std::size_t i = rank - 1; i-- > 0;) {
896            stride[i] = stride[i + 1] * boundary_[i + 1];
897        }
898        for (std::size_t i = 0; i < rank; ++i) {
899            curr_[i] = linear_idx / stride[i];
900            linear_idx = linear_idx % stride[i];
901        }
902        // index is out of bounds of the array
903        Expects(!less(curr_, index_type{}) && !less(boundary_, curr_));
904        return *this;
905    }
906
907    constexpr bounds_iterator operator-(difference_type n) const GSL_NOEXCEPT
908    {
909        bounds_iterator ret{*this};
910        return ret -= n;
911    }
912
913    constexpr bounds_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; }
914
915    constexpr difference_type operator-(const bounds_iterator& rhs) const GSL_NOEXCEPT
916    {
917        return linearize(curr_) - linearize(rhs.curr_);
918    }
919
920    constexpr value_type operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); }
921
922    constexpr bool operator==(const bounds_iterator& rhs) const GSL_NOEXCEPT
923    {
924        return curr_ == rhs.curr_;
925    }
926
927    constexpr bool operator!=(const bounds_iterator& rhs) const GSL_NOEXCEPT
928    {
929        return !(*this == rhs);
930    }
931
932    constexpr bool operator<(const bounds_iterator& rhs) const GSL_NOEXCEPT
933    {
934        return less(curr_, rhs.curr_);
935    }
936
937    constexpr bool operator<=(const bounds_iterator& rhs) const GSL_NOEXCEPT
938    {
939        return !(rhs < *this);
940    }
941
942    constexpr bool operator>(const bounds_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; }
943
944    constexpr bool operator>=(const bounds_iterator& rhs) const GSL_NOEXCEPT
945    {
946        return !(rhs > *this);
947    }
948
949    void swap(bounds_iterator& rhs) GSL_NOEXCEPT
950    {
951        std::swap(boundary_, rhs.boundary_);
952        std::swap(curr_, rhs.curr_);
953    }
954
955private:
956    constexpr bool less(index_type& one, index_type& other) const GSL_NOEXCEPT
957    {
958        for (std::size_t i = 0; i < rank; ++i) {
959            if (one[i] < other[i]) return true;
960        }
961        return false;
962    }
963
964    constexpr index_size_type linearize(const value_type& idx) const GSL_NOEXCEPT
965    {
966        // TODO: Smarter impl.
967        // Check if past-the-end
968        index_size_type multiplier = 1;
969        index_size_type res = 0;
970        if (!less(idx, boundary_)) {
971            res = 1;
972            for (std::size_t i = rank; i-- > 0;) {
973                res += (idx[i] - 1) * multiplier;
974                multiplier *= boundary_[i];
975            }
976        }
977        else
978        {
979            for (std::size_t i = rank; i-- > 0;) {
980                res += idx[i] * multiplier;
981                multiplier *= boundary_[i];
982            }
983        }
984        return res;
985    }
986
987    value_type boundary_;
988    std::remove_const_t<value_type> curr_;
989};
990
991template <typename IndexType>
992bounds_iterator<IndexType> operator+(typename bounds_iterator<IndexType>::difference_type n,
993                                     const bounds_iterator<IndexType>& rhs) GSL_NOEXCEPT
994{
995    return rhs + n;
996}
997
998namespace details
999{
1000    template <typename Bounds>
1001    inline constexpr std::enable_if_t<
1002        std::is_same<typename Bounds::mapping_type, generalized_mapping_tag>::value,
1003        typename Bounds::index_type>
1004    make_stride(const Bounds& bnd) GSL_NOEXCEPT
1005    {
1006        return bnd.strides();
1007    }
1008
1009    // Make a stride vector from bounds, assuming contiguous memory.
1010    template <typename Bounds>
1011    inline constexpr std::enable_if_t<
1012        std::is_same<typename Bounds::mapping_type, contiguous_mapping_tag>::value,
1013        typename Bounds::index_type>
1014    make_stride(const Bounds& bnd) GSL_NOEXCEPT
1015    {
1016        auto extents = bnd.index_bounds();
1017        typename Bounds::size_type stride[Bounds::rank] = {};
1018
1019        stride[Bounds::rank - 1] = 1;
1020        for (std::size_t i = 1; i < Bounds::rank; ++i) {
1021            stride[Bounds::rank - i - 1] = stride[Bounds::rank - i] * extents[Bounds::rank - i];
1022        }
1023        return {stride};
1024    }
1025
1026    template <typename BoundsSrc, typename BoundsDest>
1027    void verifyBoundsReshape(const BoundsSrc& src, const BoundsDest& dest)
1028    {
1029        static_assert(is_bounds<BoundsSrc>::value && is_bounds<BoundsDest>::value,
1030                      "The src type and dest type must be bounds");
1031        static_assert(std::is_same<typename BoundsSrc::mapping_type, contiguous_mapping_tag>::value,
1032                      "The source type must be a contiguous bounds");
1033        static_assert(BoundsDest::static_size == dynamic_range ||
1034                          BoundsSrc::static_size == dynamic_range ||
1035                          BoundsDest::static_size == BoundsSrc::static_size,
1036                      "The source bounds must have same size as dest bounds");
1037        Expects(src.size() == dest.size());
1038    }
1039
1040} // namespace details
1041
1042template <typename Span>
1043class contiguous_span_iterator;
1044template <typename Span>
1045class general_span_iterator;
1046
1047template <std::ptrdiff_t DimSize = dynamic_range>
1048struct dim_t
1049{
1050    static const std::ptrdiff_t value = DimSize;
1051};
1052template <>
1053struct dim_t<dynamic_range>
1054{
1055    static const std::ptrdiff_t value = dynamic_range;
1056    const std::ptrdiff_t dvalue;
1057    dim_t(std::ptrdiff_t size) : dvalue(size) {}
1058};
1059
1060template <std::ptrdiff_t N, class = std::enable_if_t<(N >= 0)>>
1061inline constexpr dim_t<N> dim() GSL_NOEXCEPT
1062{
1063    return dim_t<N>();
1064}
1065
1066template <std::ptrdiff_t N = dynamic_range, class = std::enable_if_t<N == dynamic_range>>
1067inline constexpr dim_t<N> dim(std::ptrdiff_t n) GSL_NOEXCEPT
1068{
1069    return dim_t<>(n);
1070}
1071
1072template <typename ValueType, std::ptrdiff_t FirstDimension = dynamic_range,
1073          std::ptrdiff_t... RestDimensions>
1074class multi_span;
1075
1076template <typename ValueType, std::size_t Rank>
1077class strided_span;
1078
1079namespace details
1080{
1081    template <typename T, typename = std::true_type>
1082    struct SpanTypeTraits
1083    {
1084        using value_type = T;
1085        using size_type = std::size_t;
1086    };
1087
1088    template <typename Traits>
1089    struct SpanTypeTraits<Traits, typename std::is_reference<typename Traits::span_traits&>::type>
1090    {
1091        using value_type = typename Traits::span_traits::value_type;
1092        using size_type = typename Traits::span_traits::size_type;
1093    };
1094
1095    template <typename T, std::ptrdiff_t... Ranks>
1096    struct SpanArrayTraits
1097    {
1098        using type = multi_span<T, Ranks...>;
1099        using value_type = T;
1100        using bounds_type = static_bounds<Ranks...>;
1101        using pointer = T*;
1102        using reference = T&;
1103    };
1104    template <typename T, std::ptrdiff_t N, std::ptrdiff_t... Ranks>
1105    struct SpanArrayTraits<T[N], Ranks...> : SpanArrayTraits<T, Ranks..., N>
1106    {
1107    };
1108
1109    template <typename BoundsType>
1110    BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::true_type) // dynamic size
1111    {
1112        Expects(totalSize >= 0 && totalSize <= PTRDIFF_MAX);
1113        return BoundsType{totalSize};
1114    }
1115    template <typename BoundsType>
1116    BoundsType newBoundsHelperImpl(std::ptrdiff_t totalSize, std::false_type) // static size
1117    {
1118        Expects(BoundsType::static_size <= totalSize);
1119        return {};
1120    }
1121    template <typename BoundsType>
1122    BoundsType newBoundsHelper(std::ptrdiff_t totalSize)
1123    {
1124        static_assert(BoundsType::dynamic_rank <= 1, "dynamic rank must less or equal to 1");
1125        return newBoundsHelperImpl<BoundsType>(
1126            totalSize, std::integral_constant<bool, BoundsType::dynamic_rank == 1>());
1127    }
1128
1129    struct Sep
1130    {
1131    };
1132
1133    template <typename T, typename... Args>
1134    T static_as_multi_span_helper(Sep, Args... args)
1135    {
1136        return T{narrow_cast<typename T::size_type>(args)...};
1137    }
1138    template <typename T, typename Arg, typename... Args>
1139    std::enable_if_t<
1140        !std::is_same<Arg, dim_t<dynamic_range>>::value && !std::is_same<Arg, Sep>::value, T>
1141    static_as_multi_span_helper(Arg, Args... args)
1142    {
1143        return static_as_multi_span_helper<T>(args...);
1144    }
1145    template <typename T, typename... Args>
1146    T static_as_multi_span_helper(dim_t<dynamic_range> val, Args... args)
1147    {
1148        return static_as_multi_span_helper<T>(args..., val.dvalue);
1149    }
1150
1151    template <typename... Dimensions>
1152    struct static_as_multi_span_static_bounds_helper
1153    {
1154        using type = static_bounds<(Dimensions::value)...>;
1155    };
1156
1157    template <typename T>
1158    struct is_multi_span_oracle : std::false_type
1159    {
1160    };
1161
1162    template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
1163    struct is_multi_span_oracle<multi_span<ValueType, FirstDimension, RestDimensions...>>
1164        : std::true_type
1165    {
1166    };
1167
1168    template <typename ValueType, std::ptrdiff_t Rank>
1169    struct is_multi_span_oracle<strided_span<ValueType, Rank>> : std::true_type
1170    {
1171    };
1172
1173    template <typename T>
1174    struct is_multi_span : is_multi_span_oracle<std::remove_cv_t<T>>
1175    {
1176    };
1177}
1178
1179template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
1180class multi_span
1181{
1182    // TODO do we still need this?
1183    template <typename ValueType2, std::ptrdiff_t FirstDimension2,
1184              std::ptrdiff_t... RestDimensions2>
1185    friend class multi_span;
1186
1187public:
1188    using bounds_type = static_bounds<FirstDimension, RestDimensions...>;
1189    static const std::size_t Rank = bounds_type::rank;
1190    using size_type = typename bounds_type::size_type;
1191    using index_type = typename bounds_type::index_type;
1192    using value_type = ValueType;
1193    using const_value_type = std::add_const_t<value_type>;
1194    using pointer = std::add_pointer_t<value_type>;
1195    using reference = std::add_lvalue_reference_t<value_type>;
1196    using iterator = contiguous_span_iterator<multi_span>;
1197    using const_span = multi_span<const_value_type, FirstDimension, RestDimensions...>;
1198    using const_iterator = contiguous_span_iterator<const_span>;
1199    using reverse_iterator = std::reverse_iterator<iterator>;
1200    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
1201    using sliced_type =
1202        std::conditional_t<Rank == 1, value_type, multi_span<value_type, RestDimensions...>>;
1203
1204private:
1205    pointer data_;
1206    bounds_type bounds_;
1207
1208    friend iterator;
1209    friend const_iterator;
1210
1211public:
1212    // default constructor - same as constructing from nullptr_t
1213    constexpr multi_span() GSL_NOEXCEPT : multi_span(nullptr, bounds_type{})
1214    {
1215        static_assert(bounds_type::dynamic_rank != 0 ||
1216                          (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
1217                      "Default construction of multi_span<T> only possible "
1218                      "for dynamic or fixed, zero-length spans.");
1219    }
1220
1221    // construct from nullptr - get an empty multi_span
1222    constexpr multi_span(std::nullptr_t) GSL_NOEXCEPT : multi_span(nullptr, bounds_type{})
1223    {
1224        static_assert(bounds_type::dynamic_rank != 0 ||
1225                          (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
1226                      "nullptr_t construction of multi_span<T> only possible "
1227                      "for dynamic or fixed, zero-length spans.");
1228    }
1229
1230    // construct from nullptr with size of 0 (helps with template function calls)
1231    template <class IntType, typename = std::enable_if_t<std::is_integral<IntType>::value>>
1232    constexpr multi_span(std::nullptr_t, IntType size) GSL_NOEXCEPT
1233        : multi_span(nullptr, bounds_type{})
1234    {
1235        static_assert(bounds_type::dynamic_rank != 0 ||
1236                          (bounds_type::dynamic_rank == 0 && bounds_type::static_size == 0),
1237                      "nullptr_t construction of multi_span<T> only possible "
1238                      "for dynamic or fixed, zero-length spans.");
1239        Expects(size == 0);
1240    }
1241
1242    // construct from a single element
1243    constexpr multi_span(reference data) GSL_NOEXCEPT : multi_span(&data, bounds_type{1})
1244    {
1245        static_assert(bounds_type::dynamic_rank > 0 || bounds_type::static_size == 0 ||
1246                          bounds_type::static_size == 1,
1247                      "Construction from a single element only possible "
1248                      "for dynamic or fixed spans of length 0 or 1.");
1249    }
1250
1251    // prevent constructing from temporaries for single-elements
1252    constexpr multi_span(value_type&&) = delete;
1253
1254    // construct from pointer + length
1255    constexpr multi_span(pointer ptr, size_type size) GSL_NOEXCEPT
1256        : multi_span(ptr, bounds_type{size})
1257    {
1258    }
1259
1260    // construct from pointer + length - multidimensional
1261    constexpr multi_span(pointer data, bounds_type bounds) GSL_NOEXCEPT : data_(data),
1262                                                                          bounds_(std::move(bounds))
1263    {
1264        Expects((bounds_.size() > 0 && data != nullptr) || bounds_.size() == 0);
1265    }
1266
1267    // construct from begin,end pointer pair
1268    template <typename Ptr,
1269              typename = std::enable_if_t<std::is_convertible<Ptr, pointer>::value &&
1270                                          details::LessThan<bounds_type::dynamic_rank, 2>::value>>
1271    constexpr multi_span(pointer begin, Ptr end)
1272        : multi_span(begin,
1273                     details::newBoundsHelper<bounds_type>(static_cast<pointer>(end) - begin))
1274    {
1275        Expects(begin != nullptr && end != nullptr && begin <= static_cast<pointer>(end));
1276    }
1277
1278    // construct from n-dimensions static array
1279    template <typename T, std::size_t N, typename Helper = details::SpanArrayTraits<T, N>>
1280    constexpr multi_span(T (&arr)[N])
1281        : multi_span(reinterpret_cast<pointer>(arr), bounds_type{typename Helper::bounds_type{}})
1282    {
1283        static_assert(std::is_convertible<typename Helper::value_type(*)[], value_type(*)[]>::value,
1284                      "Cannot convert from source type to target multi_span type.");
1285        static_assert(std::is_convertible<typename Helper::bounds_type, bounds_type>::value,
1286                      "Cannot construct a multi_span from an array with fewer elements.");
1287    }
1288
1289    // construct from n-dimensions dynamic array (e.g. new int[m][4])
1290    // (precedence will be lower than the 1-dimension pointer)
1291    template <typename T, typename Helper = details::SpanArrayTraits<T, dynamic_range>>
1292    constexpr multi_span(T* const& data, size_type size)
1293        : multi_span(reinterpret_cast<pointer>(data), typename Helper::bounds_type{size})
1294    {
1295        static_assert(std::is_convertible<typename Helper::value_type(*)[], value_type(*)[]>::value,
1296                      "Cannot convert from source type to target multi_span type.");
1297    }
1298
1299    // construct from std::array
1300    template <typename T, std::size_t N>
1301    constexpr multi_span(std::array<T, N>& arr)
1302        : multi_span(arr.data(), bounds_type{static_bounds<N>{}})
1303    {
1304        static_assert(
1305            std::is_convertible<T(*)[], typename std::remove_const_t<value_type>(*)[]>::value,
1306            "Cannot convert from source type to target multi_span type.");
1307        static_assert(std::is_convertible<static_bounds<N>, bounds_type>::value,
1308                      "You cannot construct a multi_span from a std::array of smaller size.");
1309    }
1310
1311    // construct from const std::array
1312    template <typename T, std::size_t N>
1313    constexpr multi_span(const std::array<T, N>& arr)
1314        : multi_span(arr.data(), bounds_type{static_bounds<N>{}})
1315    {
1316        static_assert(std::is_convertible<T(*)[], typename std::remove_const_t<value_type>(*)[]>::value,
1317                      "Cannot convert from source type to target multi_span type.");
1318        static_assert(std::is_convertible<static_bounds<N>, bounds_type>::value,
1319                      "You cannot construct a multi_span from a std::array of smaller size.");
1320    }
1321
1322    // prevent constructing from temporary std::array
1323    template <typename T, std::size_t N>
1324    constexpr multi_span(std::array<T, N>&& arr) = delete;
1325
1326    // construct from containers
1327    // future: could use contiguous_iterator_traits to identify only contiguous containers
1328    // type-requirements: container must have .size(), operator[] which are value_type compatible
1329    template <typename Cont, typename DataType = typename Cont::value_type,
1330              typename = std::enable_if_t<
1331                  !details::is_multi_span<Cont>::value &&
1332                  std::is_convertible<DataType (*)[], value_type (*)[]>::value &&
1333                  std::is_same<std::decay_t<decltype(std::declval<Cont>().size(),
1334                                                     *std::declval<Cont>().data())>,
1335                               DataType>::value>>
1336    constexpr multi_span(Cont& cont)
1337        : multi_span(static_cast<pointer>(cont.data()),
1338                     details::newBoundsHelper<bounds_type>(narrow_cast<size_type>(cont.size())))
1339    {
1340    }
1341
1342    // prevent constructing from temporary containers
1343    template <typename Cont, typename DataType = typename Cont::value_type,
1344              typename = std::enable_if_t<
1345                  !details::is_multi_span<Cont>::value &&
1346                  std::is_convertible<DataType (*)[], value_type (*)[]>::value &&
1347                  std::is_same<std::decay_t<decltype(std::declval<Cont>().size(),
1348                                                     *std::declval<Cont>().data())>,
1349                               DataType>::value>>
1350    explicit constexpr multi_span(Cont&& cont) = delete;
1351
1352    // construct from a convertible multi_span
1353    template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
1354              typename OtherBounds = static_bounds<OtherDimensions...>,
1355              typename = std::enable_if_t<std::is_convertible<OtherValueType, ValueType>::value &&
1356                                          std::is_convertible<OtherBounds, bounds_type>::value>>
1357    constexpr multi_span(multi_span<OtherValueType, OtherDimensions...> other) GSL_NOEXCEPT
1358        : data_(other.data_),
1359          bounds_(other.bounds_)
1360    {
1361    }
1362
1363    // trivial copy and move
1364    constexpr multi_span(const multi_span&) = default;
1365    constexpr multi_span(multi_span&&) = default;
1366
1367    // trivial assignment
1368    constexpr multi_span& operator=(const multi_span&) = default;
1369    constexpr multi_span& operator=(multi_span&&) = default;
1370
1371    // first() - extract the first Count elements into a new multi_span
1372    template <std::ptrdiff_t Count>
1373    constexpr multi_span<ValueType, Count> first() const GSL_NOEXCEPT
1374    {
1375        static_assert(Count >= 0, "Count must be >= 0.");
1376        static_assert(bounds_type::static_size == dynamic_range ||
1377                          Count <= bounds_type::static_size,
1378                      "Count is out of bounds.");
1379
1380        Expects(bounds_type::static_size != dynamic_range || Count <= this->size());
1381        return {this->data(), Count};
1382    }
1383
1384    // first() - extract the first count elements into a new multi_span
1385    constexpr multi_span<ValueType, dynamic_range> first(size_type count) const GSL_NOEXCEPT
1386    {
1387        Expects(count >= 0 && count <= this->size());
1388        return {this->data(), count};
1389    }
1390
1391    // last() - extract the last Count elements into a new multi_span
1392    template <std::ptrdiff_t Count>
1393    constexpr multi_span<ValueType, Count> last() const GSL_NOEXCEPT
1394    {
1395        static_assert(Count >= 0, "Count must be >= 0.");
1396        static_assert(bounds_type::static_size == dynamic_range ||
1397                          Count <= bounds_type::static_size,
1398                      "Count is out of bounds.");
1399
1400        Expects(bounds_type::static_size != dynamic_range || Count <= this->size());
1401        return {this->data() + this->size() - Count, Count};
1402    }
1403
1404    // last() - extract the last count elements into a new multi_span
1405    constexpr multi_span<ValueType, dynamic_range> last(size_type count) const GSL_NOEXCEPT
1406    {
1407        Expects(count >= 0 && count <= this->size());
1408        return {this->data() + this->size() - count, count};
1409    }
1410
1411    // subspan() - create a subview of Count elements starting at Offset
1412    template <std::ptrdiff_t Offset, std::ptrdiff_t Count>
1413    constexpr multi_span<ValueType, Count> subspan() const GSL_NOEXCEPT
1414    {
1415        static_assert(Count >= 0, "Count must be >= 0.");
1416        static_assert(Offset >= 0, "Offset must be >= 0.");
1417        static_assert(bounds_type::static_size == dynamic_range ||
1418                          ((Offset <= bounds_type::static_size) &&
1419                           Count <= bounds_type::static_size - Offset),
1420                      "You must describe a sub-range within bounds of the multi_span.");
1421
1422        Expects(bounds_type::static_size != dynamic_range ||
1423                (Offset <= this->size() && Count <= this->size() - Offset));
1424        return {this->data() + Offset, Count};
1425    }
1426
1427    // subspan() - create a subview of count elements starting at offset
1428    // supplying dynamic_range for count will consume all available elements from offset
1429    constexpr multi_span<ValueType, dynamic_range>
1430    subspan(size_type offset, size_type count = dynamic_range) const GSL_NOEXCEPT
1431    {
1432        Expects((offset >= 0 && offset <= this->size()) &&
1433                (count == dynamic_range || (count <= this->size() - offset)));
1434        return {this->data() + offset, count == dynamic_range ? this->length() - offset : count};
1435    }
1436
1437    // section - creates a non-contiguous, strided multi_span from a contiguous one
1438    constexpr strided_span<ValueType, Rank> section(index_type origin,
1439                                                    index_type extents) const GSL_NOEXCEPT
1440    {
1441        size_type size = this->bounds().total_size() - this->bounds().linearize(origin);
1442        return {&this->operator[](origin), size,
1443                strided_bounds<Rank>{extents, details::make_stride(bounds())}};
1444    }
1445
1446    // length of the multi_span in elements
1447    constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); }
1448
1449    // length of the multi_span in elements
1450    constexpr size_type length() const GSL_NOEXCEPT { return this->size(); }
1451
1452    // length of the multi_span in bytes
1453    constexpr size_type size_bytes() const GSL_NOEXCEPT
1454    {
1455        return narrow_cast<size_type>(sizeof(value_type)) * this->size();
1456    }
1457
1458    // length of the multi_span in bytes
1459    constexpr size_type length_bytes() const GSL_NOEXCEPT { return this->size_bytes(); }
1460
1461    constexpr bool empty() const GSL_NOEXCEPT { return this->size() == 0; }
1462
1463    static constexpr std::size_t rank() { return Rank; }
1464
1465    template <std::size_t Dim = 0>
1466    constexpr size_type extent() const GSL_NOEXCEPT
1467    {
1468        static_assert(Dim < Rank,
1469                      "Dimension should be less than rank (dimension count starts from 0).");
1470        return bounds_.template extent<Dim>();
1471    }
1472
1473    template <typename IntType>
1474    constexpr size_type extent(IntType dim) const GSL_NOEXCEPT
1475    {
1476        return bounds_.extent(dim);
1477    }
1478
1479    constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; }
1480
1481    constexpr pointer data() const GSL_NOEXCEPT { return data_; }
1482
1483    template <typename FirstIndex>
1484    constexpr reference operator()(FirstIndex index)
1485    {
1486        return this->operator[](narrow_cast<std::ptrdiff_t>(index));
1487    }
1488
1489    template <typename FirstIndex, typename... OtherIndices>
1490    constexpr reference operator()(FirstIndex index, OtherIndices... indices)
1491    {
1492        index_type idx = {narrow_cast<std::ptrdiff_t>(index),
1493                          narrow_cast<std::ptrdiff_t>(indices)...};
1494        return this->operator[](idx);
1495    }
1496
1497    constexpr reference operator[](const index_type& idx) const GSL_NOEXCEPT
1498    {
1499        return data_[bounds_.linearize(idx)];
1500    }
1501
1502    template <bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
1503    constexpr Ret operator[](size_type idx) const GSL_NOEXCEPT
1504    {
1505        Expects(idx >= 0 && idx < bounds_.size()); // index is out of bounds of the array
1506        const size_type ridx = idx * bounds_.stride();
1507
1508        // index is out of bounds of the underlying data
1509        Expects(ridx < bounds_.total_size());
1510        return Ret{data_ + ridx, bounds_.slice()};
1511    }
1512
1513    constexpr iterator begin() const GSL_NOEXCEPT { return iterator{this, true}; }
1514
1515    constexpr iterator end() const GSL_NOEXCEPT { return iterator{this, false}; }
1516
1517    constexpr const_iterator cbegin() const GSL_NOEXCEPT
1518    {
1519        return const_iterator{reinterpret_cast<const const_span*>(this), true};
1520    }
1521
1522    constexpr const_iterator cend() const GSL_NOEXCEPT
1523    {
1524        return const_iterator{reinterpret_cast<const const_span*>(this), false};
1525    }
1526
1527    constexpr reverse_iterator rbegin() const GSL_NOEXCEPT { return reverse_iterator{end()}; }
1528
1529    constexpr reverse_iterator rend() const GSL_NOEXCEPT { return reverse_iterator{begin()}; }
1530
1531    constexpr const_reverse_iterator crbegin() const GSL_NOEXCEPT
1532    {
1533        return const_reverse_iterator{cend()};
1534    }
1535
1536    constexpr const_reverse_iterator crend() const GSL_NOEXCEPT
1537    {
1538        return const_reverse_iterator{cbegin()};
1539    }
1540
1541    template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
1542              typename Dummy = std::enable_if_t<std::is_same<
1543                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1544    constexpr bool
1545    operator==(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
1546    {
1547        return bounds_.size() == other.bounds_.size() &&
1548               (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin()));
1549    }
1550
1551    template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
1552              typename Dummy = std::enable_if_t<std::is_same<
1553                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1554    constexpr bool
1555    operator!=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
1556    {
1557        return !(*this == other);
1558    }
1559
1560    template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
1561              typename Dummy = std::enable_if_t<std::is_same<
1562                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1563    constexpr bool
1564    operator<(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
1565    {
1566        return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end());
1567    }
1568
1569    template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
1570              typename Dummy = std::enable_if_t<std::is_same<
1571                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1572    constexpr bool
1573    operator<=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
1574    {
1575        return !(other < *this);
1576    }
1577
1578    template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
1579              typename Dummy = std::enable_if_t<std::is_same<
1580                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1581    constexpr bool
1582    operator>(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
1583    {
1584        return (other < *this);
1585    }
1586
1587    template <typename OtherValueType, std::ptrdiff_t... OtherDimensions,
1588              typename Dummy = std::enable_if_t<std::is_same<
1589                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1590    constexpr bool
1591    operator>=(const multi_span<OtherValueType, OtherDimensions...>& other) const GSL_NOEXCEPT
1592    {
1593        return !(*this < other);
1594    }
1595};
1596
1597//
1598// Free functions for manipulating spans
1599//
1600
1601// reshape a multi_span into a different dimensionality
1602// DimCount and Enabled here are workarounds for a bug in MSVC 2015
1603template <typename SpanType, typename... Dimensions2, std::size_t DimCount = sizeof...(Dimensions2),
1604          bool Enabled = (DimCount > 0), typename = std::enable_if_t<Enabled>>
1605inline constexpr auto as_multi_span(SpanType s, Dimensions2... dims)
1606    -> multi_span<typename SpanType::value_type, Dimensions2::value...>
1607{
1608    static_assert(details::is_multi_span<SpanType>::value,
1609                  "Variadic as_multi_span() is for reshaping existing spans.");
1610    using BoundsType =
1611        typename multi_span<typename SpanType::value_type, (Dimensions2::value)...>::bounds_type;
1612    auto tobounds = details::static_as_multi_span_helper<BoundsType>(dims..., details::Sep{});
1613    details::verifyBoundsReshape(s.bounds(), tobounds);
1614    return {s.data(), tobounds};
1615}
1616
1617// convert a multi_span<T> to a multi_span<const byte>
1618template <typename U, std::ptrdiff_t... Dimensions>
1619multi_span<const byte, dynamic_range> as_bytes(multi_span<U, Dimensions...> s) GSL_NOEXCEPT
1620{
1621    static_assert(std::is_trivial<std::decay_t<U>>::value,
1622                  "The value_type of multi_span must be a trivial type.");
1623    return {reinterpret_cast<const byte*>(s.data()), s.size_bytes()};
1624}
1625
1626// convert a multi_span<T> to a multi_span<byte> (a writeable byte multi_span)
1627// this is not currently a portable function that can be relied upon to work
1628// on all implementations. It should be considered an experimental extension
1629// to the standard GSL interface.
1630template <typename U, std::ptrdiff_t... Dimensions>
1631multi_span<byte> as_writeable_bytes(multi_span<U, Dimensions...> s) GSL_NOEXCEPT
1632{
1633    static_assert(std::is_trivial<std::decay_t<U>>::value,
1634                  "The value_type of multi_span must be a trivial type.");
1635    return {reinterpret_cast<byte*>(s.data()), s.size_bytes()};
1636}
1637
1638// convert a multi_span<const byte> to a multi_span<const T>
1639// this is not currently a portable function that can be relied upon to work
1640// on all implementations. It should be considered an experimental extension
1641// to the standard GSL interface.
1642template <typename U, std::ptrdiff_t... Dimensions>
1643inline constexpr auto
1644as_multi_span(multi_span<const byte, Dimensions...> s) GSL_NOEXCEPT -> multi_span<
1645    const U, static_cast<std::ptrdiff_t>(
1646                 multi_span<const byte, Dimensions...>::bounds_type::static_size != dynamic_range
1647                     ? (static_cast<std::size_t>(
1648                            multi_span<const byte, Dimensions...>::bounds_type::static_size) /
1649                        sizeof(U))
1650                     : dynamic_range)>
1651{
1652    using ConstByteSpan = multi_span<const byte, Dimensions...>;
1653    static_assert(
1654        std::is_trivial<std::decay_t<U>>::value &&
1655            (ConstByteSpan::bounds_type::static_size == dynamic_range ||
1656             ConstByteSpan::bounds_type::static_size % narrow_cast<std::ptrdiff_t>(sizeof(U)) == 0),
1657        "Target type must be a trivial type and its size must match the byte array size");
1658
1659    Expects((s.size_bytes() % narrow_cast<std::ptrdiff_t>(sizeof(U))) == 0 &&
1660            (s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))) < PTRDIFF_MAX);
1661    return {reinterpret_cast<const U*>(s.data()),
1662            s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))};
1663}
1664
1665// convert a multi_span<byte> to a multi_span<T>
1666// this is not currently a portable function that can be relied upon to work
1667// on all implementations. It should be considered an experimental extension
1668// to the standard GSL interface.
1669template <typename U, std::ptrdiff_t... Dimensions>
1670inline constexpr auto as_multi_span(multi_span<byte, Dimensions...> s) GSL_NOEXCEPT
1671    -> multi_span<U, narrow_cast<std::ptrdiff_t>(
1672                         multi_span<byte, Dimensions...>::bounds_type::static_size != dynamic_range
1673                             ? static_cast<std::size_t>(
1674                                   multi_span<byte, Dimensions...>::bounds_type::static_size) /
1675                                   sizeof(U)
1676                             : dynamic_range)>
1677{
1678    using ByteSpan = multi_span<byte, Dimensions...>;
1679    static_assert(
1680        std::is_trivial<std::decay_t<U>>::value &&
1681            (ByteSpan::bounds_type::static_size == dynamic_range ||
1682             ByteSpan::bounds_type::static_size % static_cast<std::size_t>(sizeof(U)) == 0),
1683        "Target type must be a trivial type and its size must match the byte array size");
1684
1685    Expects((s.size_bytes() % sizeof(U)) == 0);
1686    return {reinterpret_cast<U*>(s.data()),
1687            s.size_bytes() / narrow_cast<std::ptrdiff_t>(sizeof(U))};
1688}
1689
1690template <typename T, std::ptrdiff_t... Dimensions>
1691inline constexpr auto as_multi_span(T* const& ptr, dim_t<Dimensions>... args)
1692    -> multi_span<std::remove_all_extents_t<T>, Dimensions...>
1693{
1694    return {reinterpret_cast<std::remove_all_extents_t<T>*>(ptr),
1695            details::static_as_multi_span_helper<static_bounds<Dimensions...>>(args...,
1696                                                                               details::Sep{})};
1697}
1698
1699template <typename T>
1700inline constexpr auto as_multi_span(T* arr, std::ptrdiff_t len) ->
1701    typename details::SpanArrayTraits<T, dynamic_range>::type
1702{
1703    return {reinterpret_cast<std::remove_all_extents_t<T>*>(arr), len};
1704}
1705
1706template <typename T, std::size_t N>
1707inline constexpr auto as_multi_span(T (&arr)[N]) -> typename details::SpanArrayTraits<T, N>::type
1708{
1709    return {arr};
1710}
1711
1712template <typename T, std::size_t N>
1713inline constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>& arr)
1714{
1715    return {arr};
1716}
1717
1718template <typename T, std::size_t N>
1719inline constexpr multi_span<const T, N> as_multi_span(const std::array<T, N>&&) = delete;
1720
1721template <typename T, std::size_t N>
1722inline constexpr multi_span<T, N> as_multi_span(std::array<T, N>& arr)
1723{
1724    return {arr};
1725}
1726
1727template <typename T>
1728inline constexpr multi_span<T, dynamic_range> as_multi_span(T* begin, T* end)
1729{
1730    return {begin, end};
1731}
1732
1733template <typename Cont>
1734inline constexpr auto as_multi_span(Cont& arr) -> std::enable_if_t<
1735    !details::is_multi_span<std::decay_t<Cont>>::value,
1736    multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>>
1737{
1738    Expects(arr.size() < PTRDIFF_MAX);
1739    return {arr.data(), narrow_cast<std::ptrdiff_t>(arr.size())};
1740}
1741
1742template <typename Cont>
1743inline constexpr auto as_multi_span(Cont&& arr) -> std::enable_if_t<
1744    !details::is_multi_span<std::decay_t<Cont>>::value,
1745    multi_span<std::remove_reference_t<decltype(arr.size(), *arr.data())>, dynamic_range>> = delete;
1746
1747// from basic_string which doesn't have nonconst .data() member like other contiguous containers
1748template <typename CharT, typename Traits, typename Allocator>
1749inline constexpr auto as_multi_span(std::basic_string<CharT, Traits, Allocator>& str)
1750    -> multi_span<CharT, dynamic_range>
1751{
1752    Expects(str.size() < PTRDIFF_MAX);
1753    return {&str[0], narrow_cast<std::ptrdiff_t>(str.size())};
1754}
1755
1756// strided_span is an extension that is not strictly part of the GSL at this time.
1757// It is kept here while the multidimensional interface is still being defined.
1758template <typename ValueType, std::size_t Rank>
1759class strided_span
1760{
1761public:
1762    using bounds_type = strided_bounds<Rank>;
1763    using size_type = typename bounds_type::size_type;
1764    using index_type = typename bounds_type::index_type;
1765    using value_type = ValueType;
1766    using const_value_type = std::add_const_t<value_type>;
1767    using pointer = std::add_pointer_t<value_type>;
1768    using reference = std::add_lvalue_reference_t<value_type>;
1769    using iterator = general_span_iterator<strided_span>;
1770    using const_strided_span = strided_span<const_value_type, Rank>;
1771    using const_iterator = general_span_iterator<const_strided_span>;
1772    using reverse_iterator = std::reverse_iterator<iterator>;
1773    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
1774    using sliced_type =
1775        std::conditional_t<Rank == 1, value_type, strided_span<value_type, Rank - 1>>;
1776
1777private:
1778    pointer data_;
1779    bounds_type bounds_;
1780
1781    friend iterator;
1782    friend const_iterator;
1783    template <typename OtherValueType, std::size_t OtherRank>
1784    friend class strided_span;
1785
1786public:
1787    // from raw data
1788    constexpr strided_span(pointer ptr, size_type size, bounds_type bounds)
1789        : data_(ptr), bounds_(std::move(bounds))
1790    {
1791        Expects((bounds_.size() > 0 && ptr != nullptr) || bounds_.size() == 0);
1792        // Bounds cross data boundaries
1793        Expects(this->bounds().total_size() <= size);
1794        (void) size;
1795    }
1796
1797    // from static array of size N
1798    template <size_type N>
1799    constexpr strided_span(value_type (&values)[N], bounds_type bounds)
1800        : strided_span(values, N, std::move(bounds))
1801    {
1802    }
1803
1804    // from array view
1805    template <typename OtherValueType, std::ptrdiff_t... Dimensions,
1806              bool Enabled1 = (sizeof...(Dimensions) == Rank),
1807              bool Enabled2 = std::is_convertible<OtherValueType*, ValueType*>::value,
1808              typename Dummy = std::enable_if_t<Enabled1 && Enabled2>>
1809    constexpr strided_span(multi_span<OtherValueType, Dimensions...> av, bounds_type bounds)
1810        : strided_span(av.data(), av.bounds().total_size(), std::move(bounds))
1811    {
1812    }
1813
1814    // convertible
1815    template <typename OtherValueType, typename Dummy = std::enable_if_t<std::is_convertible<
1816                                           OtherValueType (*)[], value_type (*)[]>::value>>
1817    constexpr strided_span(const strided_span<OtherValueType, Rank>& other)
1818        : data_(other.data_), bounds_(other.bounds_)
1819    {
1820    }
1821
1822    // convert from bytes
1823    template <typename OtherValueType>
1824    constexpr strided_span<
1825        typename std::enable_if<std::is_same<value_type, const byte>::value, OtherValueType>::type,
1826        Rank>
1827    as_strided_span() const
1828    {
1829        static_assert((sizeof(OtherValueType) >= sizeof(value_type)) &&
1830                          (sizeof(OtherValueType) % sizeof(value_type) == 0),
1831                      "OtherValueType should have a size to contain a multiple of ValueTypes");
1832        auto d = narrow_cast<size_type>(sizeof(OtherValueType) / sizeof(value_type));
1833
1834        size_type size = this->bounds().total_size() / d;
1835        return {const_cast<OtherValueType*>(reinterpret_cast<const OtherValueType*>(this->data())),
1836                size,
1837                bounds_type{resize_extent(this->bounds().index_bounds(), d),
1838                            resize_stride(this->bounds().strides(), d)}};
1839    }
1840
1841    constexpr strided_span section(index_type origin, index_type extents) const
1842    {
1843        size_type size = this->bounds().total_size() - this->bounds().linearize(origin);
1844        return {&this->operator[](origin), size,
1845                bounds_type{extents, details::make_stride(bounds())}};
1846    }
1847
1848    constexpr reference operator[](const index_type& idx) const
1849    {
1850        return data_[bounds_.linearize(idx)];
1851    }
1852
1853    template <bool Enabled = (Rank > 1), typename Ret = std::enable_if_t<Enabled, sliced_type>>
1854    constexpr Ret operator[](size_type idx) const
1855    {
1856        Expects(idx < bounds_.size()); // index is out of bounds of the array
1857        const size_type ridx = idx * bounds_.stride();
1858
1859        // index is out of bounds of the underlying data
1860        Expects(ridx < bounds_.total_size());
1861        return {data_ + ridx, bounds_.slice().total_size(), bounds_.slice()};
1862    }
1863
1864    constexpr bounds_type bounds() const GSL_NOEXCEPT { return bounds_; }
1865
1866    template <std::size_t Dim = 0>
1867    constexpr size_type extent() const GSL_NOEXCEPT
1868    {
1869        static_assert(Dim < Rank,
1870                      "dimension should be less than Rank (dimension count starts from 0)");
1871        return bounds_.template extent<Dim>();
1872    }
1873
1874    constexpr size_type size() const GSL_NOEXCEPT { return bounds_.size(); }
1875
1876    constexpr pointer data() const GSL_NOEXCEPT { return data_; }
1877
1878    constexpr explicit operator bool() const GSL_NOEXCEPT { return data_ != nullptr; }
1879
1880    constexpr iterator begin() const { return iterator{this, true}; }
1881
1882    constexpr iterator end() const { return iterator{this, false}; }
1883
1884    constexpr const_iterator cbegin() const
1885    {
1886        return const_iterator{reinterpret_cast<const const_strided_span*>(this), true};
1887    }
1888
1889    constexpr const_iterator cend() const
1890    {
1891        return const_iterator{reinterpret_cast<const const_strided_span*>(this), false};
1892    }
1893
1894    constexpr reverse_iterator rbegin() const { return reverse_iterator{end()}; }
1895
1896    constexpr reverse_iterator rend() const { return reverse_iterator{begin()}; }
1897
1898    constexpr const_reverse_iterator crbegin() const { return const_reverse_iterator{cend()}; }
1899
1900    constexpr const_reverse_iterator crend() const { return const_reverse_iterator{cbegin()}; }
1901
1902    template <typename OtherValueType, std::ptrdiff_t OtherRank,
1903              typename Dummy = std::enable_if_t<std::is_same<
1904                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1905    constexpr bool
1906    operator==(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
1907    {
1908        return bounds_.size() == other.bounds_.size() &&
1909               (data_ == other.data_ || std::equal(this->begin(), this->end(), other.begin()));
1910    }
1911
1912    template <typename OtherValueType, std::ptrdiff_t OtherRank,
1913              typename Dummy = std::enable_if_t<std::is_same<
1914                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1915    constexpr bool
1916    operator!=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
1917    {
1918        return !(*this == other);
1919    }
1920
1921    template <typename OtherValueType, std::ptrdiff_t OtherRank,
1922              typename Dummy = std::enable_if_t<std::is_same<
1923                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1924    constexpr bool
1925    operator<(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
1926    {
1927        return std::lexicographical_compare(this->begin(), this->end(), other.begin(), other.end());
1928    }
1929
1930    template <typename OtherValueType, std::ptrdiff_t OtherRank,
1931              typename Dummy = std::enable_if_t<std::is_same<
1932                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1933    constexpr bool
1934    operator<=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
1935    {
1936        return !(other < *this);
1937    }
1938
1939    template <typename OtherValueType, std::ptrdiff_t OtherRank,
1940              typename Dummy = std::enable_if_t<std::is_same<
1941                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1942    constexpr bool
1943    operator>(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
1944    {
1945        return (other < *this);
1946    }
1947
1948    template <typename OtherValueType, std::ptrdiff_t OtherRank,
1949              typename Dummy = std::enable_if_t<std::is_same<
1950                  std::remove_cv_t<value_type>, std::remove_cv_t<OtherValueType>>::value>>
1951    constexpr bool
1952    operator>=(const strided_span<OtherValueType, OtherRank>& other) const GSL_NOEXCEPT
1953    {
1954        return !(*this < other);
1955    }
1956
1957private:
1958    static index_type resize_extent(const index_type& extent, std::ptrdiff_t d)
1959    {
1960        // The last dimension of the array needs to contain a multiple of new type elements
1961        Expects(extent[Rank - 1] >= d && (extent[Rank - 1] % d == 0));
1962
1963        index_type ret = extent;
1964        ret[Rank - 1] /= d;
1965
1966        return ret;
1967    }
1968
1969    template <bool Enabled = (Rank == 1), typename Dummy = std::enable_if_t<Enabled>>
1970    static index_type resize_stride(const index_type& strides, std::ptrdiff_t, void* = nullptr)
1971    {
1972        // Only strided arrays with regular strides can be resized
1973        Expects(strides[Rank - 1] == 1);
1974
1975        return strides;
1976    }
1977
1978    template <bool Enabled = (Rank > 1), typename Dummy = std::enable_if_t<Enabled>>
1979    static index_type resize_stride(const index_type& strides, std::ptrdiff_t d)
1980    {
1981        // Only strided arrays with regular strides can be resized
1982        Expects(strides[Rank - 1] == 1);
1983        // The strides must have contiguous chunks of
1984        // memory that can contain a multiple of new type elements
1985        Expects(strides[Rank - 2] >= d && (strides[Rank - 2] % d == 0));
1986
1987        for (std::size_t i = Rank - 1; i > 0; --i) {
1988            // Only strided arrays with regular strides can be resized
1989            Expects((strides[i - 1] >= strides[i]) && (strides[i - 1] % strides[i] == 0));
1990        }
1991
1992        index_type ret = strides / d;
1993        ret[Rank - 1] = 1;
1994
1995        return ret;
1996    }
1997};
1998
1999template <class Span>
2000class contiguous_span_iterator
2001    : public std::iterator<std::random_access_iterator_tag, typename Span::value_type>
2002{
2003    using Base = std::iterator<std::random_access_iterator_tag, typename Span::value_type>;
2004
2005public:
2006    using typename Base::reference;
2007    using typename Base::pointer;
2008    using typename Base::difference_type;
2009
2010private:
2011    template <typename ValueType, std::ptrdiff_t FirstDimension, std::ptrdiff_t... RestDimensions>
2012    friend class multi_span;
2013
2014    pointer data_;
2015    const Span* m_validator;
2016    void validateThis() const
2017    {
2018        // iterator is out of range of the array
2019        Expects(data_ >= m_validator->data_ && data_ < m_validator->data_ + m_validator->size());
2020    }
2021    contiguous_span_iterator(const Span* container, bool isbegin)
2022        : data_(isbegin ? container->data_ : container->data_ + container->size())
2023        , m_validator(container)
2024    {
2025    }
2026
2027public:
2028    reference operator*() const GSL_NOEXCEPT
2029    {
2030        validateThis();
2031        return *data_;
2032    }
2033    pointer operator->() const GSL_NOEXCEPT
2034    {
2035        validateThis();
2036        return data_;
2037    }
2038    contiguous_span_iterator& operator++() GSL_NOEXCEPT
2039    {
2040        ++data_;
2041        return *this;
2042    }
2043    contiguous_span_iterator operator++(int) GSL_NOEXCEPT
2044    {
2045        auto ret = *this;
2046        ++(*this);
2047        return ret;
2048    }
2049    contiguous_span_iterator& operator--() GSL_NOEXCEPT
2050    {
2051        --data_;
2052        return *this;
2053    }
2054    contiguous_span_iterator operator--(int) GSL_NOEXCEPT
2055    {
2056        auto ret = *this;
2057        --(*this);
2058        return ret;
2059    }
2060    contiguous_span_iterator operator+(difference_type n) const GSL_NOEXCEPT
2061    {
2062        contiguous_span_iterator ret{*this};
2063        return ret += n;
2064    }
2065    contiguous_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT
2066    {
2067        data_ += n;
2068        return *this;
2069    }
2070    contiguous_span_iterator operator-(difference_type n) const GSL_NOEXCEPT
2071    {
2072        contiguous_span_iterator ret{*this};
2073        return ret -= n;
2074    }
2075    contiguous_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; }
2076    difference_type operator-(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
2077    {
2078        Expects(m_validator == rhs.m_validator);
2079        return data_ - rhs.data_;
2080    }
2081    reference operator[](difference_type n) const GSL_NOEXCEPT { return *(*this + n); }
2082    bool operator==(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
2083    {
2084        Expects(m_validator == rhs.m_validator);
2085        return data_ == rhs.data_;
2086    }
2087    bool operator!=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
2088    {
2089        return !(*this == rhs);
2090    }
2091    bool operator<(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
2092    {
2093        Expects(m_validator == rhs.m_validator);
2094        return data_ < rhs.data_;
2095    }
2096    bool operator<=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
2097    {
2098        return !(rhs < *this);
2099    }
2100    bool operator>(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; }
2101    bool operator>=(const contiguous_span_iterator& rhs) const GSL_NOEXCEPT
2102    {
2103        return !(rhs > *this);
2104    }
2105    void swap(contiguous_span_iterator& rhs) GSL_NOEXCEPT
2106    {
2107        std::swap(data_, rhs.data_);
2108        std::swap(m_validator, rhs.m_validator);
2109    }
2110};
2111
2112template <typename Span>
2113contiguous_span_iterator<Span> operator+(typename contiguous_span_iterator<Span>::difference_type n,
2114                                         const contiguous_span_iterator<Span>& rhs) GSL_NOEXCEPT
2115{
2116    return rhs + n;
2117}
2118
2119template <typename Span>
2120class general_span_iterator
2121    : public std::iterator<std::random_access_iterator_tag, typename Span::value_type>
2122{
2123    using Base = std::iterator<std::random_access_iterator_tag, typename Span::value_type>;
2124
2125public:
2126    using typename Base::reference;
2127    using typename Base::pointer;
2128    using typename Base::difference_type;
2129    using typename Base::value_type;
2130
2131private:
2132    template <typename ValueType, std::size_t Rank>
2133    friend class strided_span;
2134
2135    const Span* m_container;
2136    typename Span::bounds_type::iterator m_itr;
2137    general_span_iterator(const Span* container, bool isbegin)
2138        : m_container(container)
2139        , m_itr(isbegin ? m_container->bounds().begin() : m_container->bounds().end())
2140    {
2141    }
2142
2143public:
2144    reference operator*() GSL_NOEXCEPT { return (*m_container)[*m_itr]; }
2145    pointer operator->() GSL_NOEXCEPT { return &(*m_container)[*m_itr]; }
2146    general_span_iterator& operator++() GSL_NOEXCEPT
2147    {
2148        ++m_itr;
2149        return *this;
2150    }
2151    general_span_iterator operator++(int) GSL_NOEXCEPT
2152    {
2153        auto ret = *this;
2154        ++(*this);
2155        return ret;
2156    }
2157    general_span_iterator& operator--() GSL_NOEXCEPT
2158    {
2159        --m_itr;
2160        return *this;
2161    }
2162    general_span_iterator operator--(int) GSL_NOEXCEPT
2163    {
2164        auto ret = *this;
2165        --(*this);
2166        return ret;
2167    }
2168    general_span_iterator operator+(difference_type n) const GSL_NOEXCEPT
2169    {
2170        general_span_iterator ret{*this};
2171        return ret += n;
2172    }
2173    general_span_iterator& operator+=(difference_type n) GSL_NOEXCEPT
2174    {
2175        m_itr += n;
2176        return *this;
2177    }
2178    general_span_iterator operator-(difference_type n) const GSL_NOEXCEPT
2179    {
2180        general_span_iterator ret{*this};
2181        return ret -= n;
2182    }
2183    general_span_iterator& operator-=(difference_type n) GSL_NOEXCEPT { return *this += -n; }
2184    difference_type operator-(const general_span_iterator& rhs) const GSL_NOEXCEPT
2185    {
2186        Expects(m_container == rhs.m_container);
2187        return m_itr - rhs.m_itr;
2188    }
2189    value_type operator[](difference_type n) const GSL_NOEXCEPT { return (*m_container)[m_itr[n]]; }
2190
2191    bool operator==(const general_span_iterator& rhs) const GSL_NOEXCEPT
2192    {
2193        Expects(m_container == rhs.m_container);
2194        return m_itr == rhs.m_itr;
2195    }
2196    bool operator!=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(*this == rhs); }
2197    bool operator<(const general_span_iterator& rhs) const GSL_NOEXCEPT
2198    {
2199        Expects(m_container == rhs.m_container);
2200        return m_itr < rhs.m_itr;
2201    }
2202    bool operator<=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs < *this); }
2203    bool operator>(const general_span_iterator& rhs) const GSL_NOEXCEPT { return rhs < *this; }
2204    bool operator>=(const general_span_iterator& rhs) const GSL_NOEXCEPT { return !(rhs > *this); }
2205    void swap(general_span_iterator& rhs) GSL_NOEXCEPT
2206    {
2207        std::swap(m_itr, rhs.m_itr);
2208        std::swap(m_container, rhs.m_container);
2209    }
2210};
2211
2212template <typename Span>
2213general_span_iterator<Span> operator+(typename general_span_iterator<Span>::difference_type n,
2214                                      const general_span_iterator<Span>& rhs) GSL_NOEXCEPT
2215{
2216    return rhs + n;
2217}
2218
2219} // namespace gsl
2220
2221#undef GSL_NOEXCEPT
2222
2223#ifdef _MSC_VER
2224#if _MSC_VER < 1910
2225
2226#undef constexpr
2227#pragma pop_macro("constexpr")
2228#endif // _MSC_VER < 1910
2229
2230#pragma warning(pop)
2231
2232#endif // _MSC_VER
2233
2234#endif // GSL_MULTI_SPAN_H
2235