xref: /freebsd-src/contrib/llvm-project/libcxx/include/__mdspan/layout_stride.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
15f757f3fSDimitry Andric // -*- C++ -*-
25f757f3fSDimitry Andric //===----------------------------------------------------------------------===//
35f757f3fSDimitry Andric //
45f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
55f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
65f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
75f757f3fSDimitry Andric //
85f757f3fSDimitry Andric //                        Kokkos v. 4.0
95f757f3fSDimitry Andric //       Copyright (2022) National Technology & Engineering
105f757f3fSDimitry Andric //               Solutions of Sandia, LLC (NTESS).
115f757f3fSDimitry Andric //
125f757f3fSDimitry Andric // Under the terms of Contract DE-NA0003525 with NTESS,
135f757f3fSDimitry Andric // the U.S. Government retains certain rights in this software.
145f757f3fSDimitry Andric //
155f757f3fSDimitry Andric //===---------------------------------------------------------------------===//
165f757f3fSDimitry Andric 
175f757f3fSDimitry Andric #ifndef _LIBCPP___MDSPAN_LAYOUT_STRIDE_H
185f757f3fSDimitry Andric #define _LIBCPP___MDSPAN_LAYOUT_STRIDE_H
195f757f3fSDimitry Andric 
205f757f3fSDimitry Andric #include <__assert>
215f757f3fSDimitry Andric #include <__config>
225f757f3fSDimitry Andric #include <__fwd/mdspan.h>
235f757f3fSDimitry Andric #include <__mdspan/extents.h>
245f757f3fSDimitry Andric #include <__type_traits/is_constructible.h>
255f757f3fSDimitry Andric #include <__type_traits/is_convertible.h>
265f757f3fSDimitry Andric #include <__type_traits/is_nothrow_constructible.h>
275f757f3fSDimitry Andric #include <__utility/as_const.h>
285f757f3fSDimitry Andric #include <__utility/integer_sequence.h>
295f757f3fSDimitry Andric #include <__utility/swap.h>
305f757f3fSDimitry Andric #include <array>
315f757f3fSDimitry Andric #include <cinttypes>
325f757f3fSDimitry Andric #include <cstddef>
335f757f3fSDimitry Andric #include <limits>
345f757f3fSDimitry Andric 
355f757f3fSDimitry Andric #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
365f757f3fSDimitry Andric #  pragma GCC system_header
375f757f3fSDimitry Andric #endif
385f757f3fSDimitry Andric 
395f757f3fSDimitry Andric _LIBCPP_PUSH_MACROS
405f757f3fSDimitry Andric #include <__undef_macros>
415f757f3fSDimitry Andric 
425f757f3fSDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD
435f757f3fSDimitry Andric 
445f757f3fSDimitry Andric #if _LIBCPP_STD_VER >= 23
455f757f3fSDimitry Andric 
465f757f3fSDimitry Andric namespace __mdspan_detail {
475f757f3fSDimitry Andric template <class _Layout, class _Mapping>
485f757f3fSDimitry Andric constexpr bool __is_mapping_of =
495f757f3fSDimitry Andric     is_same_v<typename _Layout::template mapping<typename _Mapping::extents_type>, _Mapping>;
505f757f3fSDimitry Andric 
515f757f3fSDimitry Andric template <class _Mapping>
525f757f3fSDimitry Andric concept __layout_mapping_alike = requires {
535f757f3fSDimitry Andric   requires __is_mapping_of<typename _Mapping::layout_type, _Mapping>;
545f757f3fSDimitry Andric   requires __is_extents_v<typename _Mapping::extents_type>;
555f757f3fSDimitry Andric   { _Mapping::is_always_strided() } -> same_as<bool>;
565f757f3fSDimitry Andric   { _Mapping::is_always_exhaustive() } -> same_as<bool>;
575f757f3fSDimitry Andric   { _Mapping::is_always_unique() } -> same_as<bool>;
585f757f3fSDimitry Andric   bool_constant<_Mapping::is_always_strided()>::value;
595f757f3fSDimitry Andric   bool_constant<_Mapping::is_always_exhaustive()>::value;
605f757f3fSDimitry Andric   bool_constant<_Mapping::is_always_unique()>::value;
615f757f3fSDimitry Andric };
625f757f3fSDimitry Andric } // namespace __mdspan_detail
635f757f3fSDimitry Andric 
645f757f3fSDimitry Andric template <class _Extents>
655f757f3fSDimitry Andric class layout_stride::mapping {
665f757f3fSDimitry Andric public:
675f757f3fSDimitry Andric   static_assert(__mdspan_detail::__is_extents<_Extents>::value,
685f757f3fSDimitry Andric                 "layout_stride::mapping template argument must be a specialization of extents.");
695f757f3fSDimitry Andric 
705f757f3fSDimitry Andric   using extents_type = _Extents;
715f757f3fSDimitry Andric   using index_type   = typename extents_type::index_type;
725f757f3fSDimitry Andric   using size_type    = typename extents_type::size_type;
735f757f3fSDimitry Andric   using rank_type    = typename extents_type::rank_type;
745f757f3fSDimitry Andric   using layout_type  = layout_stride;
755f757f3fSDimitry Andric 
765f757f3fSDimitry Andric private:
775f757f3fSDimitry Andric   static constexpr rank_type __rank_ = extents_type::rank();
785f757f3fSDimitry Andric 
795f757f3fSDimitry Andric   // Used for default construction check and mandates
805f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI static constexpr bool __required_span_size_is_representable(const extents_type& __ext) {
815f757f3fSDimitry Andric     if constexpr (__rank_ == 0)
825f757f3fSDimitry Andric       return true;
835f757f3fSDimitry Andric 
845f757f3fSDimitry Andric     index_type __prod = __ext.extent(0);
855f757f3fSDimitry Andric     for (rank_type __r = 1; __r < __rank_; __r++) {
865f757f3fSDimitry Andric       bool __overflowed = __builtin_mul_overflow(__prod, __ext.extent(__r), &__prod);
875f757f3fSDimitry Andric       if (__overflowed)
885f757f3fSDimitry Andric         return false;
895f757f3fSDimitry Andric     }
905f757f3fSDimitry Andric     return true;
915f757f3fSDimitry Andric   }
925f757f3fSDimitry Andric 
935f757f3fSDimitry Andric   template <class _OtherIndexType>
945f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI static constexpr bool
955f757f3fSDimitry Andric   __required_span_size_is_representable(const extents_type& __ext, span<_OtherIndexType, __rank_> __strides) {
965f757f3fSDimitry Andric     if constexpr (__rank_ == 0)
975f757f3fSDimitry Andric       return true;
985f757f3fSDimitry Andric 
995f757f3fSDimitry Andric     index_type __size = 1;
1005f757f3fSDimitry Andric     for (rank_type __r = 0; __r < __rank_; __r++) {
1015f757f3fSDimitry Andric       // We can only check correct conversion of _OtherIndexType if it is an integral
1025f757f3fSDimitry Andric       if constexpr (is_integral_v<_OtherIndexType>) {
1035f757f3fSDimitry Andric         using _CommonType = common_type_t<index_type, _OtherIndexType>;
1045f757f3fSDimitry Andric         if (static_cast<_CommonType>(__strides[__r]) > static_cast<_CommonType>(numeric_limits<index_type>::max()))
1055f757f3fSDimitry Andric           return false;
1065f757f3fSDimitry Andric       }
1075f757f3fSDimitry Andric       if (__ext.extent(__r) == static_cast<index_type>(0))
1085f757f3fSDimitry Andric         return true;
1095f757f3fSDimitry Andric       index_type __prod     = (__ext.extent(__r) - 1);
1105f757f3fSDimitry Andric       bool __overflowed_mul = __builtin_mul_overflow(__prod, static_cast<index_type>(__strides[__r]), &__prod);
1115f757f3fSDimitry Andric       if (__overflowed_mul)
1125f757f3fSDimitry Andric         return false;
1135f757f3fSDimitry Andric       bool __overflowed_add = __builtin_add_overflow(__size, __prod, &__size);
1145f757f3fSDimitry Andric       if (__overflowed_add)
1155f757f3fSDimitry Andric         return false;
1165f757f3fSDimitry Andric     }
1175f757f3fSDimitry Andric     return true;
1185f757f3fSDimitry Andric   }
1195f757f3fSDimitry Andric 
1205f757f3fSDimitry Andric   // compute offset of a strided layout mapping
1215f757f3fSDimitry Andric   template <class _StridedMapping>
1225f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI static constexpr index_type __offset(const _StridedMapping& __mapping) {
1235f757f3fSDimitry Andric     if constexpr (_StridedMapping::extents_type::rank() == 0) {
1245f757f3fSDimitry Andric       return static_cast<index_type>(__mapping());
1255f757f3fSDimitry Andric     } else if (__mapping.required_span_size() == static_cast<typename _StridedMapping::index_type>(0)) {
1265f757f3fSDimitry Andric       return static_cast<index_type>(0);
1275f757f3fSDimitry Andric     } else {
1285f757f3fSDimitry Andric       return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
1295f757f3fSDimitry Andric         return static_cast<index_type>(__mapping((_Pos ? 0 : 0)...));
1305f757f3fSDimitry Andric       }(make_index_sequence<__rank_>());
1315f757f3fSDimitry Andric     }
1325f757f3fSDimitry Andric   }
1335f757f3fSDimitry Andric 
1345f757f3fSDimitry Andric   // compute the permutation for sorting the stride array
1355f757f3fSDimitry Andric   // we never actually sort the stride array
1365f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr void __bubble_sort_by_strides(array<rank_type, __rank_>& __permute) const {
1375f757f3fSDimitry Andric     for (rank_type __i = __rank_ - 1; __i > 0; __i--) {
1385f757f3fSDimitry Andric       for (rank_type __r = 0; __r < __i; __r++) {
1395f757f3fSDimitry Andric         if (__strides_[__permute[__r]] > __strides_[__permute[__r + 1]]) {
1405f757f3fSDimitry Andric           swap(__permute[__r], __permute[__r + 1]);
1415f757f3fSDimitry Andric         } else {
1425f757f3fSDimitry Andric           // if two strides are the same then one of the associated extents must be 1 or 0
1435f757f3fSDimitry Andric           // both could be, but you can't have one larger than 1 come first
1445f757f3fSDimitry Andric           if ((__strides_[__permute[__r]] == __strides_[__permute[__r + 1]]) &&
1455f757f3fSDimitry Andric               (__extents_.extent(__permute[__r]) > static_cast<index_type>(1)))
1465f757f3fSDimitry Andric             swap(__permute[__r], __permute[__r + 1]);
1475f757f3fSDimitry Andric         }
1485f757f3fSDimitry Andric       }
1495f757f3fSDimitry Andric     }
1505f757f3fSDimitry Andric   }
1515f757f3fSDimitry Andric 
152*0fca6ea1SDimitry Andric   static_assert(extents_type::rank_dynamic() > 0 || __required_span_size_is_representable(extents_type()),
1535f757f3fSDimitry Andric                 "layout_stride::mapping product of static extents must be representable as index_type.");
1545f757f3fSDimitry Andric 
1555f757f3fSDimitry Andric public:
1565f757f3fSDimitry Andric   // [mdspan.layout.stride.cons], constructors
1575f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr mapping() noexcept : __extents_(extents_type()) {
1585f757f3fSDimitry Andric     // Note the nominal precondition is covered by above static assert since
1595f757f3fSDimitry Andric     // if rank_dynamic is != 0 required_span_size is zero for default construction
1605f757f3fSDimitry Andric     if constexpr (__rank_ > 0) {
1615f757f3fSDimitry Andric       index_type __stride = 1;
1625f757f3fSDimitry Andric       for (rank_type __r = __rank_ - 1; __r > static_cast<rank_type>(0); __r--) {
1635f757f3fSDimitry Andric         __strides_[__r] = __stride;
1645f757f3fSDimitry Andric         __stride *= __extents_.extent(__r);
1655f757f3fSDimitry Andric       }
1665f757f3fSDimitry Andric       __strides_[0] = __stride;
1675f757f3fSDimitry Andric     }
1685f757f3fSDimitry Andric   }
1695f757f3fSDimitry Andric 
1705f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr mapping(const mapping&) noexcept = default;
1715f757f3fSDimitry Andric 
1725f757f3fSDimitry Andric   template <class _OtherIndexType>
1735f757f3fSDimitry Andric     requires(is_convertible_v<const _OtherIndexType&, index_type> &&
1745f757f3fSDimitry Andric              is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
1755f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext, span<_OtherIndexType, __rank_> __strides) noexcept
1765f757f3fSDimitry Andric       : __extents_(__ext), __strides_([&]<size_t... _Pos>(index_sequence<_Pos...>) {
1775f757f3fSDimitry Andric           return __mdspan_detail::__possibly_empty_array<index_type, __rank_>{
1785f757f3fSDimitry Andric               static_cast<index_type>(std::as_const(__strides[_Pos]))...};
1795f757f3fSDimitry Andric         }(make_index_sequence<__rank_>())) {
1805f757f3fSDimitry Andric     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
1815f757f3fSDimitry Andric         ([&]<size_t... _Pos>(index_sequence<_Pos...>) {
1825f757f3fSDimitry Andric           // For integrals we can do a pre-conversion check, for other types not
1835f757f3fSDimitry Andric           if constexpr (is_integral_v<_OtherIndexType>) {
1845f757f3fSDimitry Andric             return ((__strides[_Pos] > static_cast<_OtherIndexType>(0)) && ... && true);
1855f757f3fSDimitry Andric           } else {
1865f757f3fSDimitry Andric             return ((static_cast<index_type>(__strides[_Pos]) > static_cast<index_type>(0)) && ... && true);
1875f757f3fSDimitry Andric           }
1885f757f3fSDimitry Andric         }(make_index_sequence<__rank_>())),
1895f757f3fSDimitry Andric         "layout_stride::mapping ctor: all strides must be greater than 0");
1905f757f3fSDimitry Andric     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
1915f757f3fSDimitry Andric         __required_span_size_is_representable(__ext, __strides),
1925f757f3fSDimitry Andric         "layout_stride::mapping ctor: required span size is not representable as index_type.");
1935f757f3fSDimitry Andric     if constexpr (__rank_ > 1) {
1945f757f3fSDimitry Andric       _LIBCPP_ASSERT_UNCATEGORIZED(
1955f757f3fSDimitry Andric           ([&]<size_t... _Pos>(index_sequence<_Pos...>) {
1965f757f3fSDimitry Andric             // basically sort the dimensions based on strides and extents, sorting is represented in permute array
1975f757f3fSDimitry Andric             array<rank_type, __rank_> __permute{_Pos...};
1985f757f3fSDimitry Andric             __bubble_sort_by_strides(__permute);
1995f757f3fSDimitry Andric 
2005f757f3fSDimitry Andric             // check that this permutations represents a growing set
2015f757f3fSDimitry Andric             for (rank_type __i = 1; __i < __rank_; __i++)
2025f757f3fSDimitry Andric               if (static_cast<index_type>(__strides[__permute[__i]]) <
2035f757f3fSDimitry Andric                   static_cast<index_type>(__strides[__permute[__i - 1]]) * __extents_.extent(__permute[__i - 1]))
2045f757f3fSDimitry Andric                 return false;
2055f757f3fSDimitry Andric             return true;
2065f757f3fSDimitry Andric           }(make_index_sequence<__rank_>())),
2075f757f3fSDimitry Andric           "layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
2085f757f3fSDimitry Andric     }
2095f757f3fSDimitry Andric   }
2105f757f3fSDimitry Andric 
2115f757f3fSDimitry Andric   template <class _OtherIndexType>
2125f757f3fSDimitry Andric     requires(is_convertible_v<const _OtherIndexType&, index_type> &&
2135f757f3fSDimitry Andric              is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
2145f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext,
2155f757f3fSDimitry Andric                                           const array<_OtherIndexType, __rank_>& __strides) noexcept
2165f757f3fSDimitry Andric       : mapping(__ext, span(__strides)) {}
2175f757f3fSDimitry Andric 
2185f757f3fSDimitry Andric   template <class _StridedLayoutMapping>
2195f757f3fSDimitry Andric     requires(__mdspan_detail::__layout_mapping_alike<_StridedLayoutMapping> &&
2205f757f3fSDimitry Andric              is_constructible_v<extents_type, typename _StridedLayoutMapping::extents_type> &&
2215f757f3fSDimitry Andric              _StridedLayoutMapping::is_always_unique() && _StridedLayoutMapping::is_always_strided())
2225f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr explicit(
2235f757f3fSDimitry Andric       !(is_convertible_v<typename _StridedLayoutMapping::extents_type, extents_type> &&
2245f757f3fSDimitry Andric         (__mdspan_detail::__is_mapping_of<layout_left, _StridedLayoutMapping> ||
2255f757f3fSDimitry Andric          __mdspan_detail::__is_mapping_of<layout_right, _StridedLayoutMapping> ||
2265f757f3fSDimitry Andric          __mdspan_detail::__is_mapping_of<layout_stride, _StridedLayoutMapping>)))
2275f757f3fSDimitry Andric       mapping(const _StridedLayoutMapping& __other) noexcept
2285f757f3fSDimitry Andric       : __extents_(__other.extents()), __strides_([&]<size_t... _Pos>(index_sequence<_Pos...>) {
2295f757f3fSDimitry Andric           // stride() only compiles for rank > 0
2305f757f3fSDimitry Andric           if constexpr (__rank_ > 0) {
2315f757f3fSDimitry Andric             return __mdspan_detail::__possibly_empty_array<index_type, __rank_>{
2325f757f3fSDimitry Andric                 static_cast<index_type>(__other.stride(_Pos))...};
2335f757f3fSDimitry Andric           } else {
2345f757f3fSDimitry Andric             return __mdspan_detail::__possibly_empty_array<index_type, 0>{};
2355f757f3fSDimitry Andric           }
2365f757f3fSDimitry Andric         }(make_index_sequence<__rank_>())) {
2375f757f3fSDimitry Andric     // stride() only compiles for rank > 0
2385f757f3fSDimitry Andric     if constexpr (__rank_ > 0) {
2395f757f3fSDimitry Andric       _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
2405f757f3fSDimitry Andric           ([&]<size_t... _Pos>(index_sequence<_Pos...>) {
2415f757f3fSDimitry Andric             return ((static_cast<index_type>(__other.stride(_Pos)) > static_cast<index_type>(0)) && ... && true);
2425f757f3fSDimitry Andric           }(make_index_sequence<__rank_>())),
2435f757f3fSDimitry Andric           "layout_stride::mapping converting ctor: all strides must be greater than 0");
2445f757f3fSDimitry Andric     }
2455f757f3fSDimitry Andric     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
2465f757f3fSDimitry Andric         __mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
2475f757f3fSDimitry Andric         "layout_stride::mapping converting ctor: other.required_span_size() must be representable as index_type.");
2485f757f3fSDimitry Andric     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(static_cast<index_type>(0) == __offset(__other),
2495f757f3fSDimitry Andric                                         "layout_stride::mapping converting ctor: base offset of mapping must be zero.");
2505f757f3fSDimitry Andric   }
2515f757f3fSDimitry Andric 
2525f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr mapping& operator=(const mapping&) noexcept = default;
2535f757f3fSDimitry Andric 
2545f757f3fSDimitry Andric   // [mdspan.layout.stride.obs], observers
2555f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr const extents_type& extents() const noexcept { return __extents_; }
2565f757f3fSDimitry Andric 
2575f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr array<index_type, __rank_> strides() const noexcept {
2585f757f3fSDimitry Andric     return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
2595f757f3fSDimitry Andric       return array<index_type, __rank_>{__strides_[_Pos]...};
2605f757f3fSDimitry Andric     }(make_index_sequence<__rank_>());
2615f757f3fSDimitry Andric   }
2625f757f3fSDimitry Andric 
2635f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr index_type required_span_size() const noexcept {
2645f757f3fSDimitry Andric     if constexpr (__rank_ == 0) {
2655f757f3fSDimitry Andric       return static_cast<index_type>(1);
2665f757f3fSDimitry Andric     } else {
2675f757f3fSDimitry Andric       return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
2685f757f3fSDimitry Andric         if ((__extents_.extent(_Pos) * ... * 1) == 0)
2695f757f3fSDimitry Andric           return static_cast<index_type>(0);
2705f757f3fSDimitry Andric         else
2715f757f3fSDimitry Andric           return static_cast<index_type>(
2725f757f3fSDimitry Andric               static_cast<index_type>(1) +
2735f757f3fSDimitry Andric               (((__extents_.extent(_Pos) - static_cast<index_type>(1)) * __strides_[_Pos]) + ... +
2745f757f3fSDimitry Andric                static_cast<index_type>(0)));
2755f757f3fSDimitry Andric       }(make_index_sequence<__rank_>());
2765f757f3fSDimitry Andric     }
2775f757f3fSDimitry Andric   }
2785f757f3fSDimitry Andric 
2795f757f3fSDimitry Andric   template <class... _Indices>
2805f757f3fSDimitry Andric     requires((sizeof...(_Indices) == __rank_) && (is_convertible_v<_Indices, index_type> && ...) &&
2815f757f3fSDimitry Andric              (is_nothrow_constructible_v<index_type, _Indices> && ...))
2825f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr index_type operator()(_Indices... __idx) const noexcept {
2835f757f3fSDimitry Andric     // Mappings are generally meant to be used for accessing allocations and are meant to guarantee to never
2845f757f3fSDimitry Andric     // return a value exceeding required_span_size(), which is used to know how large an allocation one needs
2855f757f3fSDimitry Andric     // Thus, this is a canonical point in multi-dimensional data structures to make invalid element access checks
2865f757f3fSDimitry Andric     // However, mdspan does check this on its own, so for now we avoid double checking in hardened mode
2875f757f3fSDimitry Andric     _LIBCPP_ASSERT_UNCATEGORIZED(__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...),
2885f757f3fSDimitry Andric                                  "layout_stride::mapping: out of bounds indexing");
2895f757f3fSDimitry Andric     return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
2905f757f3fSDimitry Andric       return ((static_cast<index_type>(__idx) * __strides_[_Pos]) + ... + index_type(0));
2915f757f3fSDimitry Andric     }(make_index_sequence<sizeof...(_Indices)>());
2925f757f3fSDimitry Andric   }
2935f757f3fSDimitry Andric 
2945f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() noexcept { return true; }
2955f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept { return false; }
2965f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() noexcept { return true; }
2975f757f3fSDimitry Andric 
2985f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI static constexpr bool is_unique() noexcept { return true; }
2995f757f3fSDimitry Andric   // The answer of this function is fairly complex in the case where one or more
3005f757f3fSDimitry Andric   // extents are zero.
3015f757f3fSDimitry Andric   // Technically it is meaningless to query is_exhaustive() in that case, but unfortunately
3025f757f3fSDimitry Andric   // the way the standard defines this function, we can't give a simple true or false then.
3035f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr bool is_exhaustive() const noexcept {
3045f757f3fSDimitry Andric     if constexpr (__rank_ == 0)
3055f757f3fSDimitry Andric       return true;
3065f757f3fSDimitry Andric     else {
3075f757f3fSDimitry Andric       index_type __span_size = required_span_size();
3085f757f3fSDimitry Andric       if (__span_size == static_cast<index_type>(0)) {
3095f757f3fSDimitry Andric         if constexpr (__rank_ == 1)
3105f757f3fSDimitry Andric           return __strides_[0] == 1;
3115f757f3fSDimitry Andric         else {
3125f757f3fSDimitry Andric           rank_type __r_largest = 0;
3135f757f3fSDimitry Andric           for (rank_type __r = 1; __r < __rank_; __r++)
3145f757f3fSDimitry Andric             if (__strides_[__r] > __strides_[__r_largest])
3155f757f3fSDimitry Andric               __r_largest = __r;
3165f757f3fSDimitry Andric           for (rank_type __r = 0; __r < __rank_; __r++)
3175f757f3fSDimitry Andric             if (__extents_.extent(__r) == 0 && __r != __r_largest)
3185f757f3fSDimitry Andric               return false;
3195f757f3fSDimitry Andric           return true;
3205f757f3fSDimitry Andric         }
3215f757f3fSDimitry Andric       } else {
3225f757f3fSDimitry Andric         return required_span_size() == [&]<size_t... _Pos>(index_sequence<_Pos...>) {
3235f757f3fSDimitry Andric           return (__extents_.extent(_Pos) * ... * static_cast<index_type>(1));
3245f757f3fSDimitry Andric         }(make_index_sequence<__rank_>());
3255f757f3fSDimitry Andric       }
3265f757f3fSDimitry Andric     }
3275f757f3fSDimitry Andric   }
3285f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI static constexpr bool is_strided() noexcept { return true; }
3295f757f3fSDimitry Andric 
3305f757f3fSDimitry Andric   // according to the standard layout_stride does not have a constraint on stride(r) for rank>0
3315f757f3fSDimitry Andric   // it still has the precondition though
3325f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI constexpr index_type stride(rank_type __r) const noexcept {
3335f757f3fSDimitry Andric     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__r < __rank_, "layout_stride::mapping::stride(): invalid rank index");
3345f757f3fSDimitry Andric     return __strides_[__r];
3355f757f3fSDimitry Andric   }
3365f757f3fSDimitry Andric 
3375f757f3fSDimitry Andric   template <class _OtherMapping>
3385f757f3fSDimitry Andric     requires(__mdspan_detail::__layout_mapping_alike<_OtherMapping> &&
3395f757f3fSDimitry Andric              (_OtherMapping::extents_type::rank() == __rank_) && _OtherMapping::is_always_strided())
3405f757f3fSDimitry Andric   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const mapping& __lhs, const _OtherMapping& __rhs) noexcept {
3415f757f3fSDimitry Andric     if (__offset(__rhs))
3425f757f3fSDimitry Andric       return false;
3435f757f3fSDimitry Andric     if constexpr (__rank_ == 0)
3445f757f3fSDimitry Andric       return true;
3455f757f3fSDimitry Andric     else {
3465f757f3fSDimitry Andric       return __lhs.extents() == __rhs.extents() && [&]<size_t... _Pos>(index_sequence<_Pos...>) {
3475f757f3fSDimitry Andric         // avoid warning when comparing signed and unsigner integers and pick the wider of two types
3485f757f3fSDimitry Andric         using _CommonType = common_type_t<index_type, typename _OtherMapping::index_type>;
3495f757f3fSDimitry Andric         return ((static_cast<_CommonType>(__lhs.stride(_Pos)) == static_cast<_CommonType>(__rhs.stride(_Pos))) && ... &&
3505f757f3fSDimitry Andric                 true);
3515f757f3fSDimitry Andric       }(make_index_sequence<__rank_>());
3525f757f3fSDimitry Andric     }
3535f757f3fSDimitry Andric   }
3545f757f3fSDimitry Andric 
3555f757f3fSDimitry Andric private:
3565f757f3fSDimitry Andric   _LIBCPP_NO_UNIQUE_ADDRESS extents_type __extents_{};
3575f757f3fSDimitry Andric   _LIBCPP_NO_UNIQUE_ADDRESS __mdspan_detail::__possibly_empty_array<index_type, __rank_> __strides_{};
3585f757f3fSDimitry Andric };
3595f757f3fSDimitry Andric 
3605f757f3fSDimitry Andric #endif // _LIBCPP_STD_VER >= 23
3615f757f3fSDimitry Andric 
3625f757f3fSDimitry Andric _LIBCPP_END_NAMESPACE_STD
3635f757f3fSDimitry Andric 
3645f757f3fSDimitry Andric _LIBCPP_POP_MACROS
3655f757f3fSDimitry Andric 
3665f757f3fSDimitry Andric #endif // _LIBCPP___MDSPAN_LAYOUT_STRIDE_H
367