master
  1// -*- C++ -*-
  2//===----------------------------------------------------------------------===//
  3//
  4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  5// See https://llvm.org/LICENSE.txt for license information.
  6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  7//
  8//                        Kokkos v. 4.0
  9//       Copyright (2022) National Technology & Engineering
 10//               Solutions of Sandia, LLC (NTESS).
 11//
 12// Under the terms of Contract DE-NA0003525 with NTESS,
 13// the U.S. Government retains certain rights in this software.
 14//
 15//===---------------------------------------------------------------------===//
 16
 17#ifndef _LIBCPP___MDSPAN_EXTENTS_H
 18#define _LIBCPP___MDSPAN_EXTENTS_H
 19
 20#include <__assert>
 21#include <__config>
 22
 23#include <__concepts/arithmetic.h>
 24#include <__type_traits/common_type.h>
 25#include <__type_traits/integer_traits.h>
 26#include <__type_traits/is_convertible.h>
 27#include <__type_traits/is_nothrow_constructible.h>
 28#include <__type_traits/make_unsigned.h>
 29#include <__utility/integer_sequence.h>
 30#include <__utility/unreachable.h>
 31#include <array>
 32#include <concepts>
 33#include <limits>
 34#include <span>
 35
 36#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 37#  pragma GCC system_header
 38#endif
 39
 40_LIBCPP_PUSH_MACROS
 41#include <__undef_macros>
 42
 43_LIBCPP_BEGIN_NAMESPACE_STD
 44
 45#if _LIBCPP_STD_VER >= 23
 46
 47namespace __mdspan_detail {
 48
 49// ------------------------------------------------------------------
 50// ------------ __static_array --------------------------------------
 51// ------------------------------------------------------------------
 52// array like class which provides an array of static values with get
 53template <class _Tp, _Tp... _Values>
 54struct __static_array {
 55  static constexpr array<_Tp, sizeof...(_Values)> __array = {_Values...};
 56
 57public:
 58  _LIBCPP_HIDE_FROM_ABI static constexpr size_t __size() { return sizeof...(_Values); }
 59  _LIBCPP_HIDE_FROM_ABI static constexpr _Tp __get(size_t __index) noexcept { return __array[__index]; }
 60
 61  template <size_t _Index>
 62  _LIBCPP_HIDE_FROM_ABI static constexpr _Tp __get() {
 63    return __get(_Index);
 64  }
 65};
 66
 67// ------------------------------------------------------------------
 68// ------------ __possibly_empty_array  -----------------------------
 69// ------------------------------------------------------------------
 70
 71// array like class which provides get function and operator [], and
 72// has a specialization for the size 0 case.
 73// This is needed to make the __maybe_static_array be truly empty, for
 74// all static values.
 75
 76template <class _Tp, size_t _Size>
 77struct __possibly_empty_array {
 78  _Tp __vals_[_Size];
 79  _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator[](size_t __index) { return __vals_[__index]; }
 80  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator[](size_t __index) const { return __vals_[__index]; }
 81};
 82
 83template <class _Tp>
 84struct __possibly_empty_array<_Tp, 0> {
 85  _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator[](size_t) { __libcpp_unreachable(); }
 86  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator[](size_t) const { __libcpp_unreachable(); }
 87};
 88
 89// ------------------------------------------------------------------
 90// ------------ static_partial_sums ---------------------------------
 91// ------------------------------------------------------------------
 92
 93// Provides a compile time partial sum one can index into
 94
 95template <size_t... _Values>
 96struct __static_partial_sums {
 97  _LIBCPP_HIDE_FROM_ABI static constexpr array<size_t, sizeof...(_Values)> __static_partial_sums_impl() {
 98    array<size_t, sizeof...(_Values)> __values{_Values...};
 99    array<size_t, sizeof...(_Values)> __partial_sums{{}};
100    size_t __running_sum = 0;
101    for (int __i = 0; __i != sizeof...(_Values); ++__i) {
102      __partial_sums[__i] = __running_sum;
103      __running_sum += __values[__i];
104    }
105    return __partial_sums;
106  }
107  static constexpr array<size_t, sizeof...(_Values)> __result{__static_partial_sums_impl()};
108
109  _LIBCPP_HIDE_FROM_ABI static constexpr size_t __get(size_t __index) { return __result[__index]; }
110};
111
112// ------------------------------------------------------------------
113// ------------ __maybe_static_array --------------------------------
114// ------------------------------------------------------------------
115
116// array like class which has a mix of static and runtime values but
117// only stores the runtime values.
118// The type of the static and the runtime values can be different.
119// The position of a dynamic value is indicated through a tag value.
120template <class _TDynamic, class _TStatic, _TStatic _DynTag, _TStatic... _Values>
121struct __maybe_static_array {
122  static_assert(is_convertible<_TStatic, _TDynamic>::value,
123                "__maybe_static_array: _TStatic must be convertible to _TDynamic");
124  static_assert(is_convertible<_TDynamic, _TStatic>::value,
125                "__maybe_static_array: _TDynamic must be convertible to _TStatic");
126
127private:
128  // Static values member
129  static constexpr size_t __size_         = sizeof...(_Values);
130  static constexpr size_t __size_dynamic_ = ((_Values == _DynTag) + ... + 0);
131  using _StaticValues _LIBCPP_NODEBUG     = __static_array<_TStatic, _Values...>;
132  using _DynamicValues _LIBCPP_NODEBUG    = __possibly_empty_array<_TDynamic, __size_dynamic_>;
133
134  // Dynamic values member
135  _LIBCPP_NO_UNIQUE_ADDRESS _DynamicValues __dyn_vals_;
136
137  // static mapping of indices to the position in the dynamic values array
138  using _DynamicIdxMap _LIBCPP_NODEBUG = __static_partial_sums<static_cast<size_t>(_Values == _DynTag)...>;
139
140  template <size_t... _Indices>
141  _LIBCPP_HIDE_FROM_ABI static constexpr _DynamicValues __zeros(index_sequence<_Indices...>) noexcept {
142    return _DynamicValues{((void)_Indices, 0)...};
143  }
144
145public:
146  _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array() noexcept
147      : __dyn_vals_{__zeros(make_index_sequence<__size_dynamic_>())} {}
148
149  // constructors from dynamic values only -- this covers the case for rank() == 0
150  template <class... _DynVals>
151    requires(sizeof...(_DynVals) == __size_dynamic_)
152  _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(_DynVals... __vals)
153      : __dyn_vals_{static_cast<_TDynamic>(__vals)...} {}
154
155  template <class _Tp, size_t _Size >
156    requires(_Size == __size_dynamic_)
157  _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array([[maybe_unused]] const span<_Tp, _Size>& __vals) {
158    if constexpr (_Size > 0) {
159      for (size_t __i = 0; __i < _Size; __i++)
160        __dyn_vals_[__i] = static_cast<_TDynamic>(__vals[__i]);
161    }
162  }
163
164  // constructors from all values -- here rank will be greater than 0
165  template <class... _DynVals>
166    requires(sizeof...(_DynVals) != __size_dynamic_)
167  _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(_DynVals... __vals) {
168    static_assert(sizeof...(_DynVals) == __size_, "Invalid number of values.");
169    _TDynamic __values[__size_] = {static_cast<_TDynamic>(__vals)...};
170    for (size_t __i = 0; __i < __size_; __i++) {
171      _TStatic __static_val = _StaticValues::__get(__i);
172      if (__static_val == _DynTag) {
173        __dyn_vals_[_DynamicIdxMap::__get(__i)] = __values[__i];
174      } else
175        // Not catching this could lead to out of bounds errors later
176        // e.g. using my_mdspan_t = mdspan<int, extents<int, 10>>; my_mdspan_t = m(new int[5], 5);
177        // Right-hand-side construction looks ok with allocation and size matching,
178        // but since (potentially elsewhere defined) my_mdspan_t has static size m now thinks its range is 10 not 5
179        _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
180            __values[__i] == static_cast<_TDynamic>(__static_val),
181            "extents construction: mismatch of provided arguments with static extents.");
182    }
183  }
184
185  template <class _Tp, size_t _Size>
186    requires(_Size != __size_dynamic_)
187  _LIBCPP_HIDE_FROM_ABI constexpr __maybe_static_array(const span<_Tp, _Size>& __vals) {
188    static_assert(_Size == __size_ || __size_ == dynamic_extent);
189    for (size_t __i = 0; __i < __size_; __i++) {
190      _TStatic __static_val = _StaticValues::__get(__i);
191      if (__static_val == _DynTag) {
192        __dyn_vals_[_DynamicIdxMap::__get(__i)] = static_cast<_TDynamic>(__vals[__i]);
193      } else
194        // Not catching this could lead to out of bounds errors later
195        // e.g. using my_mdspan_t = mdspan<int, extents<int, 10>>; my_mdspan_t = m(new int[N], span<int,1>(&N));
196        // Right-hand-side construction looks ok with allocation and size matching,
197        // but since (potentially elsewhere defined) my_mdspan_t has static size m now thinks its range is 10 not N
198        _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
199            static_cast<_TDynamic>(__vals[__i]) == static_cast<_TDynamic>(__static_val),
200            "extents construction: mismatch of provided arguments with static extents.");
201    }
202  }
203
204  // access functions
205  _LIBCPP_HIDE_FROM_ABI static constexpr _TStatic __static_value(size_t __i) noexcept {
206    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __size_, "extents access: index must be less than rank");
207    return _StaticValues::__get(__i);
208  }
209
210  _LIBCPP_HIDE_FROM_ABI constexpr _TDynamic __value(size_t __i) const {
211    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __size_, "extents access: index must be less than rank");
212    _TStatic __static_val = _StaticValues::__get(__i);
213    return __static_val == _DynTag ? __dyn_vals_[_DynamicIdxMap::__get(__i)] : static_cast<_TDynamic>(__static_val);
214  }
215  _LIBCPP_HIDE_FROM_ABI constexpr _TDynamic operator[](size_t __i) const {
216    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__i < __size_, "extents access: index must be less than rank");
217    return __value(__i);
218  }
219
220  // observers
221  _LIBCPP_HIDE_FROM_ABI static constexpr size_t __size() { return __size_; }
222  _LIBCPP_HIDE_FROM_ABI static constexpr size_t __size_dynamic() { return __size_dynamic_; }
223};
224
225// Function to check whether a value is representable as another type
226// value must be a positive integer otherwise returns false
227// if _From is not an integral, we just check positivity
228template <integral _To, class _From>
229  requires(integral<_From>)
230_LIBCPP_HIDE_FROM_ABI constexpr bool __is_representable_as(_From __value) {
231  using _To_u   = make_unsigned_t<_To>;
232  using _From_u = make_unsigned_t<_From>;
233  if constexpr (is_signed_v<_From>) {
234    if (__value < 0)
235      return false;
236  }
237  if constexpr (static_cast<_To_u>(numeric_limits<_To>::max()) >= static_cast<_From_u>(numeric_limits<_From>::max())) {
238    return true;
239  } else {
240    return static_cast<_To_u>(numeric_limits<_To>::max()) >= static_cast<_From_u>(__value);
241  }
242}
243
244template <integral _To, class _From>
245  requires(!integral<_From>)
246_LIBCPP_HIDE_FROM_ABI constexpr bool __is_representable_as(_From __value) {
247  if constexpr (is_signed_v<_To>) {
248    if (static_cast<_To>(__value) < 0)
249      return false;
250  }
251  return true;
252}
253
254template <integral _To, class... _From>
255_LIBCPP_HIDE_FROM_ABI constexpr bool __are_representable_as(_From... __values) {
256  return (__mdspan_detail::__is_representable_as<_To>(__values) && ... && true);
257}
258
259template <integral _To, class _From, size_t _Size>
260_LIBCPP_HIDE_FROM_ABI constexpr bool __are_representable_as(span<_From, _Size> __values) {
261  for (size_t __i = 0; __i < _Size; __i++)
262    if (!__mdspan_detail::__is_representable_as<_To>(__values[__i]))
263      return false;
264  return true;
265}
266
267} // namespace __mdspan_detail
268
269// ------------------------------------------------------------------
270// ------------ extents ---------------------------------------------
271// ------------------------------------------------------------------
272
273// Class to describe the extents of a multi dimensional array.
274// Used by mdspan, mdarray and layout mappings.
275// See ISO C++ standard [mdspan.extents]
276
277template <class _IndexType, size_t... _Extents>
278class extents {
279public:
280  // typedefs for integral types used
281  using index_type = _IndexType;
282  using size_type  = make_unsigned_t<index_type>;
283  using rank_type  = size_t;
284
285  static_assert(__signed_or_unsigned_integer<index_type>,
286                "extents::index_type must be a signed or unsigned integer type");
287  static_assert(((__mdspan_detail::__is_representable_as<index_type>(_Extents) || (_Extents == dynamic_extent)) && ...),
288                "extents ctor: arguments must be representable as index_type and nonnegative");
289
290private:
291  static constexpr rank_type __rank_         = sizeof...(_Extents);
292  static constexpr rank_type __rank_dynamic_ = ((_Extents == dynamic_extent) + ... + 0);
293
294  // internal storage type using __maybe_static_array
295  using _Values _LIBCPP_NODEBUG =
296      __mdspan_detail::__maybe_static_array<_IndexType, size_t, dynamic_extent, _Extents...>;
297  [[no_unique_address]] _Values __vals_;
298
299public:
300  // [mdspan.extents.obs], observers of multidimensional index space
301  _LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank() noexcept { return __rank_; }
302  _LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank_dynamic() noexcept { return __rank_dynamic_; }
303
304  _LIBCPP_HIDE_FROM_ABI constexpr index_type extent(rank_type __r) const noexcept { return __vals_.__value(__r); }
305  _LIBCPP_HIDE_FROM_ABI static constexpr size_t static_extent(rank_type __r) noexcept {
306    return _Values::__static_value(__r);
307  }
308
309  // [mdspan.extents.cons], constructors
310  _LIBCPP_HIDE_FROM_ABI constexpr extents() noexcept = default;
311
312  // Construction from just dynamic or all values.
313  // Precondition check is deferred to __maybe_static_array constructor
314  template <class... _OtherIndexTypes>
315    requires((is_convertible_v<_OtherIndexTypes, index_type> && ...) &&
316             (is_nothrow_constructible_v<index_type, _OtherIndexTypes> && ...) &&
317             (sizeof...(_OtherIndexTypes) == __rank_ || sizeof...(_OtherIndexTypes) == __rank_dynamic_))
318  _LIBCPP_HIDE_FROM_ABI constexpr explicit extents(_OtherIndexTypes... __dynvals) noexcept
319      : __vals_(static_cast<index_type>(__dynvals)...) {
320    // Not catching this could lead to out of bounds errors later
321    // e.g. mdspan m(ptr, dextents<char, 1>(200u)); leads to an extent of -56 on m
322    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(__dynvals...),
323                                        "extents ctor: arguments must be representable as index_type and nonnegative");
324  }
325
326  template <class _OtherIndexType, size_t _Size>
327    requires(is_convertible_v<const _OtherIndexType&, index_type> &&
328             is_nothrow_constructible_v<index_type, const _OtherIndexType&> &&
329             (_Size == __rank_ || _Size == __rank_dynamic_))
330  explicit(_Size != __rank_dynamic_)
331      _LIBCPP_HIDE_FROM_ABI constexpr extents(const array<_OtherIndexType, _Size>& __exts) noexcept
332      : __vals_(span(__exts)) {
333    // Not catching this could lead to out of bounds errors later
334    // e.g. mdspan m(ptr, dextents<char, 1>(array<unsigned,1>(200))); leads to an extent of -56 on m
335    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(span(__exts)),
336                                        "extents ctor: arguments must be representable as index_type and nonnegative");
337  }
338
339  template <class _OtherIndexType, size_t _Size>
340    requires(is_convertible_v<const _OtherIndexType&, index_type> &&
341             is_nothrow_constructible_v<index_type, const _OtherIndexType&> &&
342             (_Size == __rank_ || _Size == __rank_dynamic_))
343  explicit(_Size != __rank_dynamic_)
344      _LIBCPP_HIDE_FROM_ABI constexpr extents(const span<_OtherIndexType, _Size>& __exts) noexcept
345      : __vals_(__exts) {
346    // Not catching this could lead to out of bounds errors later
347    // e.g. array a{200u}; mdspan<int, dextents<char,1>> m(ptr, extents(span<unsigned,1>(a))); leads to an extent of -56
348    // on m
349    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(__exts),
350                                        "extents ctor: arguments must be representable as index_type and nonnegative");
351  }
352
353private:
354  // Function to construct extents storage from other extents.
355  template <size_t _DynCount, size_t _Idx, class _OtherExtents, class... _DynamicValues>
356    requires(_Idx < __rank_)
357  _LIBCPP_HIDE_FROM_ABI constexpr _Values __construct_vals_from_extents(
358      integral_constant<size_t, _DynCount>,
359      integral_constant<size_t, _Idx>,
360      const _OtherExtents& __exts,
361      _DynamicValues... __dynamic_values) noexcept {
362    if constexpr (static_extent(_Idx) == dynamic_extent)
363      return __construct_vals_from_extents(
364          integral_constant<size_t, _DynCount + 1>(),
365          integral_constant<size_t, _Idx + 1>(),
366          __exts,
367          __dynamic_values...,
368          __exts.extent(_Idx));
369    else
370      return __construct_vals_from_extents(
371          integral_constant<size_t, _DynCount>(), integral_constant<size_t, _Idx + 1>(), __exts, __dynamic_values...);
372  }
373
374  template <size_t _DynCount, size_t _Idx, class _OtherExtents, class... _DynamicValues>
375    requires((_Idx == __rank_) && (_DynCount == __rank_dynamic_))
376  _LIBCPP_HIDE_FROM_ABI constexpr _Values __construct_vals_from_extents(
377      integral_constant<size_t, _DynCount>,
378      integral_constant<size_t, _Idx>,
379      const _OtherExtents&,
380      _DynamicValues... __dynamic_values) noexcept {
381    return _Values{static_cast<index_type>(__dynamic_values)...};
382  }
383
384public:
385  // Converting constructor from other extents specializations
386  template <class _OtherIndexType, size_t... _OtherExtents>
387    requires((sizeof...(_OtherExtents) == sizeof...(_Extents)) &&
388             ((_OtherExtents == dynamic_extent || _Extents == dynamic_extent || _OtherExtents == _Extents) && ...))
389  explicit((((_Extents != dynamic_extent) && (_OtherExtents == dynamic_extent)) || ...) ||
390           (static_cast<make_unsigned_t<index_type>>(numeric_limits<index_type>::max()) <
391            static_cast<make_unsigned_t<_OtherIndexType>>(numeric_limits<_OtherIndexType>::max())))
392      _LIBCPP_HIDE_FROM_ABI constexpr extents(const extents<_OtherIndexType, _OtherExtents...>& __other) noexcept
393      : __vals_(
394            __construct_vals_from_extents(integral_constant<size_t, 0>(), integral_constant<size_t, 0>(), __other)) {
395    if constexpr (rank() > 0) {
396      for (size_t __r = 0; __r < rank(); __r++) {
397        if constexpr (static_cast<make_unsigned_t<index_type>>(numeric_limits<index_type>::max()) <
398                      static_cast<make_unsigned_t<_OtherIndexType>>(numeric_limits<_OtherIndexType>::max())) {
399          // Not catching this could lead to out of bounds errors later
400          // e.g. dextents<char,1>> e(dextents<unsigned,1>(200)) leads to an extent of -56 on e
401          _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
402              __mdspan_detail::__is_representable_as<index_type>(__other.extent(__r)),
403              "extents ctor: arguments must be representable as index_type and nonnegative");
404        }
405        // Not catching this could lead to out of bounds errors later
406        // e.g. mdspan<int, extents<int, 10>> m = mdspan<int, dextents<int, 1>>(new int[5], 5);
407        // Right-hand-side construction was ok, but m now thinks its range is 10 not 5
408        _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
409            (_Values::__static_value(__r) == dynamic_extent) ||
410                (static_cast<index_type>(__other.extent(__r)) == static_cast<index_type>(_Values::__static_value(__r))),
411            "extents construction: mismatch of provided arguments with static extents.");
412      }
413    }
414  }
415
416  // Comparison operator
417  template <class _OtherIndexType, size_t... _OtherExtents>
418  _LIBCPP_HIDE_FROM_ABI friend constexpr bool
419  operator==(const extents& __lhs, const extents<_OtherIndexType, _OtherExtents...>& __rhs) noexcept {
420    if constexpr (rank() != sizeof...(_OtherExtents)) {
421      return false;
422    } else {
423      for (rank_type __r = 0; __r < __rank_; __r++) {
424        // avoid warning when comparing signed and unsigner integers and pick the wider of two types
425        using _CommonType = common_type_t<index_type, _OtherIndexType>;
426        if (static_cast<_CommonType>(__lhs.extent(__r)) != static_cast<_CommonType>(__rhs.extent(__r))) {
427          return false;
428        }
429      }
430    }
431    return true;
432  }
433};
434
435// Recursive helper classes to implement dextents alias for extents
436namespace __mdspan_detail {
437
438template <class _IndexType, size_t _Rank, class _Extents = extents<_IndexType>>
439struct __make_dextents;
440
441template <class _IndexType, size_t _Rank, size_t... _ExtentsPack>
442struct __make_dextents< _IndexType, _Rank, extents<_IndexType, _ExtentsPack...>> {
443  using type _LIBCPP_NODEBUG =
444      typename __make_dextents< _IndexType, _Rank - 1, extents<_IndexType, dynamic_extent, _ExtentsPack...>>::type;
445};
446
447template <class _IndexType, size_t... _ExtentsPack>
448struct __make_dextents< _IndexType, 0, extents<_IndexType, _ExtentsPack...>> {
449  using type _LIBCPP_NODEBUG = extents<_IndexType, _ExtentsPack...>;
450};
451
452} // namespace __mdspan_detail
453
454// [mdspan.extents.dextents], alias template
455template <class _IndexType, size_t _Rank>
456using dextents = typename __mdspan_detail::__make_dextents<_IndexType, _Rank>::type;
457
458#  if _LIBCPP_STD_VER >= 26
459// [mdspan.extents.dims], alias template `dims`
460template <size_t _Rank, class _IndexType = size_t>
461using dims = dextents<_IndexType, _Rank>;
462#  endif
463
464// Deduction guide for extents
465#  if _LIBCPP_STD_VER >= 26
466template <class... _IndexTypes>
467  requires(is_convertible_v<_IndexTypes, size_t> && ...)
468explicit extents(_IndexTypes...) -> extents<size_t, __maybe_static_ext<_IndexTypes>...>;
469#  else
470template <class... _IndexTypes>
471  requires(is_convertible_v<_IndexTypes, size_t> && ...)
472explicit extents(_IndexTypes...) -> extents<size_t, size_t(((void)sizeof(_IndexTypes), dynamic_extent))...>;
473#  endif
474
475namespace __mdspan_detail {
476
477// Helper type traits for identifying a class as extents.
478template <class _Tp>
479struct __is_extents : false_type {};
480
481template <class _IndexType, size_t... _ExtentsPack>
482struct __is_extents<extents<_IndexType, _ExtentsPack...>> : true_type {};
483
484template <class _Tp>
485inline constexpr bool __is_extents_v = __is_extents<_Tp>::value;
486
487// Function to check whether a set of indices are a multidimensional
488// index into extents. This is a word of power in the C++ standard
489// requiring that the indices are larger than 0 and smaller than
490// the respective extents.
491
492template <integral _IndexType, class _From>
493  requires(integral<_From>)
494_LIBCPP_HIDE_FROM_ABI constexpr bool __is_index_in_extent(_IndexType __extent, _From __value) {
495  if constexpr (is_signed_v<_From>) {
496    if (__value < 0)
497      return false;
498  }
499  using _Tp = common_type_t<_IndexType, _From>;
500  return static_cast<_Tp>(__value) < static_cast<_Tp>(__extent);
501}
502
503template <integral _IndexType, class _From>
504  requires(!integral<_From>)
505_LIBCPP_HIDE_FROM_ABI constexpr bool __is_index_in_extent(_IndexType __extent, _From __value) {
506  if constexpr (is_signed_v<_IndexType>) {
507    if (static_cast<_IndexType>(__value) < 0)
508      return false;
509  }
510  return static_cast<_IndexType>(__value) < __extent;
511}
512
513template <size_t... _Idxs, class _Extents, class... _From>
514_LIBCPP_HIDE_FROM_ABI constexpr bool
515__is_multidimensional_index_in_impl(index_sequence<_Idxs...>, const _Extents& __ext, _From... __values) {
516  return (__mdspan_detail::__is_index_in_extent(__ext.extent(_Idxs), __values) && ...);
517}
518
519template <class _Extents, class... _From>
520_LIBCPP_HIDE_FROM_ABI constexpr bool __is_multidimensional_index_in(const _Extents& __ext, _From... __values) {
521  return __mdspan_detail::__is_multidimensional_index_in_impl(
522      make_index_sequence<_Extents::rank()>(), __ext, __values...);
523}
524
525} // namespace __mdspan_detail
526
527#endif // _LIBCPP_STD_VER >= 23
528
529_LIBCPP_END_NAMESPACE_STD
530
531_LIBCPP_POP_MACROS
532
533#endif // _LIBCPP___MDSPAN_EXTENTS_H