// -*- C++ -*-
//===------------------------------ span ---------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright (c) 2023-24 NVIDIA CORPORATION & AFFILIATES.
//
//===---------------------------------------------------------------------===//

#ifndef _LIBCUDACXX_SPAN
#define _LIBCUDACXX_SPAN

/*
    span synopsis

namespace std {

// constants
inline constexpr size_t dynamic_extent = numeric_limits<size_t>::max();

// [views.span], class template span
template <class ElementType, size_t Extent = dynamic_extent>
    class span;

template<class ElementType, size_t Extent>
  inline constexpr bool ranges::enable_view<span<ElementType, Extent>> = true;

template<class ElementType, size_t Extent>
    inline constexpr bool ranges::enable_borrowed_range<span<ElementType, Extent>> = true;

// [span.objectrep], views of object representation
template <class ElementType, size_t Extent>
    span<const byte, ((Extent == dynamic_extent) ? dynamic_extent :
        (sizeof(ElementType) * Extent))> as_bytes(span<ElementType, Extent> s) noexcept;

template <class ElementType, size_t Extent>
    span<      byte, ((Extent == dynamic_extent) ? dynamic_extent :
        (sizeof(ElementType) * Extent))> as_writable_bytes(span<ElementType, Extent> s) noexcept;


template <class ElementType, size_t Extent = dynamic_extent>
class span {
public:
    // constants and types
    using element_type = ElementType;
    using value_type = remove_cv_t<ElementType>;
    using size_type = size_t;
    using difference_type = ptrdiff_t;
    using pointer = element_type*;
    using const_pointer = const element_type*;
    using reference = element_type&;
    using const_reference = const element_type&;
    using iterator = implementation-defined;
    using reverse_iterator = std::reverse_iterator<iterator>;
    static constexpr size_type extent = Extent;

    // [span.cons], span constructors, copy, assignment, and destructor
    constexpr span() noexcept;
    template <class It>
    constexpr explicit(Extent != dynamic_extent) span(It first, size_type count);
    template <class It, class End>
    constexpr explicit(Extent != dynamic_extent) span(It first, End last);
    template <size_t N>
        constexpr span(type_identity_t<element_type> (&arr)[N]) noexcept;
    template <size_t N>
        constexpr span(array<value_type, N>& arr) noexcept;
    template <size_t N>
        constexpr span(const array<value_type, N>& arr) noexcept;
    template<class R>
      constexpr explicit(Extent != dynamic_extent) span(R&& r);
    constexpr span(const span& other) noexcept = default;
    template <class OtherElementType, size_t OtherExtent>
        constexpr explicit(Extent != dynamic_extent) span(const span<OtherElementType, OtherExtent>& s) noexcept;
    ~span() noexcept = default;
    constexpr span& operator=(const span& other) noexcept = default;

    // [span.sub], span subviews
    template <size_t Count>
        constexpr span<element_type, Count> first() const;
    template <size_t Count>
        constexpr span<element_type, Count> last() const;
    template <size_t Offset, size_t Count = dynamic_extent>
        constexpr span<element_type, see below> subspan() const;

    constexpr span<element_type, dynamic_extent> first(size_type count) const;
    constexpr span<element_type, dynamic_extent> last(size_type count) const;
    constexpr span<element_type, dynamic_extent> subspan(size_type offset, size_type count = dynamic_extent) const;

    // [span.obs], span observers
    constexpr size_type size() const noexcept;
    constexpr size_type size_bytes() const noexcept;
    [[nodiscard]] constexpr bool empty() const noexcept;

    // [span.elem], span element access
    constexpr reference operator[](size_type idx) const;
    constexpr reference front() const;
    constexpr reference back() const;
    constexpr pointer data() const noexcept;

    // [span.iterators], span iterator support
    constexpr iterator begin() const noexcept;
    constexpr iterator end() const noexcept;
    constexpr reverse_iterator rbegin() const noexcept;
    constexpr reverse_iterator rend() const noexcept;

private:
    pointer data_;    // exposition only
    size_type size_;  // exposition only
};

template<class It, class EndOrSize>
    span(It, EndOrSize) -> span<remove_reference_t<iter_reference_t<_It>>>;

template<class T, size_t N>
    span(T (&)[N]) -> span<T, N>;

template<class T, size_t N>
    span(array<T, N>&) -> span<T, N>;

template<class T, size_t N>
    span(const array<T, N>&) -> span<const T, N>;

template<class R>
    span(R&&) -> span<remove_reference_t<ranges::range_reference_t<R>>>;

} // namespace std

*/

#include <cuda/std/detail/__config>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
#  pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
#  pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
#  pragma system_header
#endif // no system header

#include <cuda/std/__concepts/convertible_to.h>
#include <cuda/std/__concepts/equality_comparable.h>
#include <cuda/std/__fwd/array.h>
#include <cuda/std/__fwd/span.h>
#include <cuda/std/__fwd/string.h>
#include <cuda/std/__iterator/concepts.h>
#include <cuda/std/__iterator/distance.h>
#include <cuda/std/__iterator/iterator_traits.h>
#include <cuda/std/__iterator/reverse_iterator.h>
#include <cuda/std/__iterator/wrap_iter.h>
#include <cuda/std/__memory/pointer_traits.h>
#include <cuda/std/__ranges/concepts.h>
#include <cuda/std/__ranges/data.h>
#include <cuda/std/__ranges/enable_borrowed_range.h>
#include <cuda/std/__ranges/enable_view.h>
#include <cuda/std/__ranges/size.h>
#include <cuda/std/__type_traits/enable_if.h>
#include <cuda/std/__type_traits/integral_constant.h>
#include <cuda/std/__type_traits/is_array.h>
#include <cuda/std/__type_traits/is_const.h>
#include <cuda/std/__type_traits/is_convertible.h>
#include <cuda/std/__type_traits/is_integral.h>
#include <cuda/std/__type_traits/is_same.h>
#include <cuda/std/__type_traits/remove_const.h>
#include <cuda/std/__type_traits/remove_cv.h>
#include <cuda/std/__type_traits/remove_cvref.h>
#include <cuda/std/__type_traits/remove_pointer.h>
#include <cuda/std/__type_traits/remove_reference.h>
#include <cuda/std/__type_traits/type_identity.h>
#include <cuda/std/__type_traits/void_t.h>
#include <cuda/std/__utility/declval.h>
#include <cuda/std/array>
#include <cuda/std/cstddef> // for ptrdiff_t
#include <cuda/std/detail/libcxx/include/__assert> // all public C++ headers provide the assertion handler

// standard-mandated includes
#include <cuda/std/version>

// [iterator.range]
#include <cuda/std/__iterator/access.h>
#include <cuda/std/__iterator/data.h>
#include <cuda/std/__iterator/empty.h>
#include <cuda/std/__iterator/reverse_access.h>
#include <cuda/std/__iterator/size.h>

_LIBCUDACXX_BEGIN_NAMESPACE_STD

#if _CCCL_STD_VER >= 2014

template <class _Tp>
_LIBCUDACXX_INLINE_VAR constexpr bool __is_std_array = false;

template <class _Tp, size_t _Sz>
_LIBCUDACXX_INLINE_VAR constexpr bool __is_std_array<array<_Tp, _Sz>> = true;

template <class _Tp>
_LIBCUDACXX_INLINE_VAR constexpr bool __is_std_span = false;

template <class _Tp, size_t _Extent>
_LIBCUDACXX_INLINE_VAR constexpr bool __is_std_span<span<_Tp, _Extent>> = true;

template <class _From, class _To>
_LIBCUDACXX_CONCEPT __span_array_convertible = _CCCL_TRAIT(is_convertible, _From (*)[], _To (*)[]);

#  if _CCCL_STD_VER >= 2017 && !defined(_CCCL_COMPILER_MSVC_2017)
template <class _Range, class _ElementType>
_LIBCUDACXX_CONCEPT_FRAGMENT(
  __span_compatible_range_,
  requires()(
    requires(_CUDA_VRANGES::contiguous_range<_Range>),
    requires(_CUDA_VRANGES::sized_range<_Range>),
    requires((_CUDA_VRANGES::borrowed_range<_Range> || _CCCL_TRAIT(is_const, _ElementType))),
    requires((!_CCCL_TRAIT(is_array, remove_cvref_t<_Range>))),
    requires((!__is_std_span<remove_cvref_t<_Range>> && !__is_std_array<remove_cvref_t<_Range>>) ),
    requires(_CCCL_TRAIT(
      is_convertible, remove_reference_t<_CUDA_VRANGES::range_reference_t<_Range>> (*)[], _ElementType (*)[]))));

template <class _Range, class _ElementType>
_LIBCUDACXX_CONCEPT __span_compatible_range = _LIBCUDACXX_FRAGMENT(__span_compatible_range_, _Range, _ElementType);

#    if _CCCL_STD_VER >= 2020
template <class _It, class _Tp>
_LIBCUDACXX_CONCEPT __span_compatible_iterator =
  contiguous_iterator<_It> && __span_array_convertible<remove_reference_t<iter_reference_t<_It>>, _Tp>;

template <class _Sentinel, class _It>
_LIBCUDACXX_CONCEPT __span_compatible_sentinel_for =
  sized_sentinel_for<_Sentinel, _It> && !_CCCL_TRAIT(is_convertible, _Sentinel, size_t);
#    else // ^^^ C++20 ^^^ / vvv C++17 vvv
template <class _It, class _Tp>
_LIBCUDACXX_CONCEPT_FRAGMENT(
  __span_compatible_iterator_,
  requires()(requires(contiguous_iterator<_It>),
             requires(__span_array_convertible<remove_reference_t<iter_reference_t<_It>>, _Tp>)));

template <class _It, class _Tp>
_LIBCUDACXX_CONCEPT __span_compatible_iterator = _LIBCUDACXX_FRAGMENT(__span_compatible_iterator_, _It, _Tp);

template <class _Sentinel, class _It>
_LIBCUDACXX_CONCEPT_FRAGMENT(
  __span_compatible_sentinel_for_,
  requires()(requires(sized_sentinel_for<_Sentinel, _It>), requires(!_CCCL_TRAIT(is_convertible, _Sentinel, size_t))));

template <class _Sentinel, class _It>
_LIBCUDACXX_CONCEPT __span_compatible_sentinel_for =
  _LIBCUDACXX_FRAGMENT(__span_compatible_sentinel_for_, _Sentinel, _It);
#    endif // _CCCL_STD_VER <= 2017
#  else // _CCCL_STD_VER >= 2017 && !_CCCL_COMPILER_MSVC_2017

template <class _Tp, class _ElementType, class = void>
_LIBCUDACXX_INLINE_VAR constexpr bool __is_span_compatible_container = false;

template <class _Tp, class _ElementType>
_LIBCUDACXX_INLINE_VAR constexpr bool __is_span_compatible_container<
  _Tp,
  _ElementType,
  void_t<
    // is not a specialization of span
    enable_if_t<!__is_std_span<_Tp>, nullptr_t>,
    // is not a specialization of array
    enable_if_t<!__is_std_array<_Tp>, nullptr_t>,
    // is_array_v<Container> is false,
    enable_if_t<!_CCCL_TRAIT(is_array, _Tp), nullptr_t>,
    // data(cont) and size(cont) are well formed
    decltype(data(declval<_Tp>())),
    decltype(size(declval<_Tp>())),
    // remove_pointer_t<decltype(data(cont))>(*)[] is convertible to ElementType(*)[]
    enable_if_t<is_convertible<remove_pointer_t<decltype(data(declval<_Tp&>()))> (*)[], _ElementType (*)[]>::value,
                nullptr_t>>> = true;
#  endif // _CCCL_STD_VER <= 2014 || _CCCL_COMPILER_MSVC_2017

#  if _CCCL_STD_VER >= 2020

template <class _Tp>
concept __integral_constant_like =
  is_integral_v<decltype(_Tp::value)> //
  && !is_same_v<bool, remove_const_t<decltype(_Tp::value)>> //
  && convertible_to<_Tp, decltype(_Tp::value)> //
  && equality_comparable_with<_Tp, decltype(_Tp::value)> //
  && bool_constant<_Tp() == _Tp::value>::value
  && bool_constant<static_cast<decltype(_Tp::value)>(_Tp()) == _Tp::value>::value;

#  else // ^^^ _CCCL_STD_VER >= 2020 ^^^ / vvv _CCCL_STD_VER <= 2017 vvv

template <class _Tp>
_LIBCUDACXX_CONCEPT_FRAGMENT(
  __integral_constant_like_,
  requires()( //
    requires(_CCCL_TRAIT(is_integral, decltype(_Tp::value))),
    requires(!_CCCL_TRAIT(is_same, bool, remove_const_t<decltype(_Tp::value)>)),
    requires(convertible_to<_Tp, decltype(_Tp::value)>),
    requires(equality_comparable_with<_Tp, decltype(_Tp::value)>),
    (integral_constant<bool, _Tp() == _Tp::value>::value),
    (integral_constant<bool, static_cast<decltype(_Tp::value)>(_Tp()) == _Tp::value>::value) //
    ));
template <class _Tp>
_LIBCUDACXX_CONCEPT __integral_constant_like = _LIBCUDACXX_FRAGMENT(__integral_constant_like_, _Tp);
#  endif // _CCCL_STD_VER <= 2017

template <class _Tp, bool = __integral_constant_like<_Tp>>
_LIBCUDACXX_INLINE_VAR constexpr size_t __maybe_static_ext = dynamic_extent;

template <class _Tp>
_LIBCUDACXX_INLINE_VAR constexpr size_t __maybe_static_ext<_Tp, true> = {_Tp::value};

template <typename _Tp, size_t _Extent>
class _LIBCUDACXX_TEMPLATE_VIS span
{
public:
  //  constants and types
  using element_type     = _Tp;
  using value_type       = remove_cv_t<_Tp>;
  using size_type        = size_t;
  using difference_type  = ptrdiff_t;
  using pointer          = _Tp*;
  using const_pointer    = const _Tp*;
  using reference        = _Tp&;
  using const_reference  = const _Tp&;
  using iterator         = __wrap_iter<pointer>;
  using reverse_iterator = _CUDA_VSTD::reverse_iterator<iterator>;

  static constexpr size_type extent = _Extent;

  // [span.cons], span constructors, copy, assignment, and destructor
  template <size_t _Sz = _Extent, enable_if_t<_Sz == 0, int> = 0>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span() noexcept
      : __data_{nullptr}
  {}

  span(const span&) noexcept            = default;
  span& operator=(const span&) noexcept = default;

#  if _CCCL_STD_VER >= 2017 && !defined(_CCCL_COMPILER_MSVC_2017)
  _LIBCUDACXX_TEMPLATE(class _It)
  _LIBCUDACXX_REQUIRES(__span_compatible_iterator<_It, element_type>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit span(_It __first, size_type __count)
      : __data_{_CUDA_VSTD::to_address(__first)}
  {
    (void) __count;
    _LIBCUDACXX_ASSERT(_Extent == __count, "size mismatch in span's constructor (iterator, len)");
  }

  _LIBCUDACXX_TEMPLATE(class _It, class _End)
  _LIBCUDACXX_REQUIRES(
    __span_compatible_iterator<_It, element_type> _LIBCUDACXX_AND __span_compatible_sentinel_for<_End, _It>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit span(_It __first, _End __last)
      : __data_{_CUDA_VSTD::to_address(__first)}
  {
    (void) __last;
    _LIBCUDACXX_ASSERT((__last - __first >= 0), "invalid range in span's constructor (iterator, sentinel)");
    _LIBCUDACXX_ASSERT(__last - __first == _Extent,
                       "invalid range in span's constructor (iterator, sentinel): last - first != extent");
  }
#  else // ^^^ C++17 ^^^ / vvv C++14 vvv
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(pointer __ptr, size_type __count)
      : __data_{__ptr}
  {
    (void) __count;
    _LIBCUDACXX_ASSERT(_Extent == __count, "size mismatch in span's constructor (ptr, len)");
  }
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(pointer __f, pointer __l)
      : __data_{__f}
  {
    (void) __l;
    _LIBCUDACXX_ASSERT(_Extent == distance(__f, __l), "size mismatch in span's constructor (ptr, ptr)");
  }
#  endif // _CCCL_STD_VER <= 2014 || _CCCL_COMPILER_MSVC_2017

#  if defined(_CCCL_COMPILER_NVRTC) || defined(_CCCL_COMPILER_MSVC_2017)
  template <size_t _Sz = _Extent, enable_if_t<_Sz != 0, int> = 0>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(type_identity_t<element_type> (&__arr)[_Sz]) noexcept
      : __data_{__arr}
  {}
#  else
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(type_identity_t<element_type> (&__arr)[_Extent]) noexcept
      : __data_{__arr}
  {}
#  endif // !_CCCL_COMPILER_NVRTC && !_CCCL_COMPILER_MSVC_2017

  _LIBCUDACXX_TEMPLATE(class _OtherElementType)
  _LIBCUDACXX_REQUIRES(__span_array_convertible<_OtherElementType, element_type>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(array<_OtherElementType, _Extent>& __arr) noexcept
      : __data_{__arr.data()}
  {}

  _LIBCUDACXX_TEMPLATE(class _OtherElementType)
  _LIBCUDACXX_REQUIRES(__span_array_convertible<const _OtherElementType, element_type>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(const array<_OtherElementType, _Extent>& __arr) noexcept
      : __data_{__arr.data()}
  {}

#  if _CCCL_STD_VER >= 2017 && !defined(_CCCL_COMPILER_MSVC_2017)
  _LIBCUDACXX_TEMPLATE(class _Range)
  _LIBCUDACXX_REQUIRES(__span_compatible_range<_Range, element_type>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit span(_Range&& __r)
      : __data_{_CUDA_VRANGES::data(__r)}
  {
    _LIBCUDACXX_ASSERT(_CUDA_VRANGES::size(__r) == _Extent, "size mismatch in span's constructor (range)");
  }
#  else // ^^^ C++17 ^^^ / vvv C++14 vvv
  _LIBCUDACXX_TEMPLATE(class _Container)
  _LIBCUDACXX_REQUIRES(__is_span_compatible_container<_Container, _Tp>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(_Container& __c) noexcept(noexcept(_CUDA_VSTD::data(__c)))
      : __data_{_CUDA_VSTD::data(__c)}
  {
    _LIBCUDACXX_ASSERT(_Extent == _CUDA::VSTD::size(__c), "size mismatch in span's constructor (other span)");
  }

  _LIBCUDACXX_TEMPLATE(class _Container)
  _LIBCUDACXX_REQUIRES(__is_span_compatible_container<_Container, _Tp>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(const _Container& __c) noexcept(noexcept(_CUDA_VSTD::data(__c)))
      : __data_{_CUDA_VSTD::data(__c)}
  {
    _LIBCUDACXX_ASSERT(_Extent == _CUDA::VSTD::size(__c), "size mismatch in span's constructor (other span)");
  }
#  endif // _CCCL_STD_VER <= 2014 || _CCCL_COMPILER_MSVC_2017

  _LIBCUDACXX_TEMPLATE(class _OtherElementType, size_t _Extent2 = _Extent)
  _LIBCUDACXX_REQUIRES((_Extent2 != dynamic_extent)
                         _LIBCUDACXX_AND __span_array_convertible<_OtherElementType, element_type>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(const span<_OtherElementType, _Extent2>& __other) noexcept
      : __data_{__other.data()}
  {}

  _LIBCUDACXX_TEMPLATE(class _OtherElementType)
  _LIBCUDACXX_REQUIRES(__span_array_convertible<_OtherElementType, element_type>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr explicit span(const span<_OtherElementType, dynamic_extent>& __other) noexcept
      : __data_{__other.data()}
  {
    _LIBCUDACXX_ASSERT(_Extent == __other.size(), "size mismatch in span's constructor (other span)");
  }

  //  ~span() noexcept = default;

  template <size_t _Count>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span<element_type, _Count> first() const noexcept
  {
    static_assert(_Count <= _Extent, "span<T, N>::first<Count>(): Count out of range");
    return span<element_type, _Count>{data(), _Count};
  }

  template <size_t _Count>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span<element_type, _Count> last() const noexcept
  {
    static_assert(_Count <= _Extent, "span<T, N>::last<Count>(): Count out of range");
    return span<element_type, _Count>{data() + size() - _Count, _Count};
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr span<element_type, dynamic_extent> first(size_type __count) const noexcept
  {
    _LIBCUDACXX_ASSERT(__count <= size(), "span<T, N>::first(count): count out of range");
    return {data(), __count};
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr span<element_type, dynamic_extent> last(size_type __count) const noexcept
  {
    _LIBCUDACXX_ASSERT(__count <= size(), "span<T, N>::last(count): count out of range");
    return {data() + size() - __count, __count};
  }

  template <size_t _Offset, size_t _Count>
  using __subspan_t = span<element_type, _Count != dynamic_extent ? _Count : _Extent - _Offset>;

  template <size_t _Offset, size_t _Count = dynamic_extent>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr __subspan_t<_Offset, _Count> subspan() const noexcept
  {
    static_assert(_Offset <= _Extent, "span<T, N>::subspan<Offset, Count>(): Offset out of range");
    static_assert(_Count == dynamic_extent || _Count <= _Extent - _Offset,
                  "span<T, N>::subspan<Offset, Count>(): Offset + Count out of range");
    return __subspan_t<_Offset, _Count>{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr span<element_type, dynamic_extent>
  subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept
  {
    _LIBCUDACXX_ASSERT(__offset <= size(), "span<T, N>::subspan(offset, count): offset out of range");
    _LIBCUDACXX_ASSERT(__count <= size() || __count == dynamic_extent,
                       "span<T, N>::subspan(offset, count): count out of range");
    if (__count == dynamic_extent)
    {
      return {data() + __offset, size() - __offset};
    }
    _LIBCUDACXX_ASSERT(__count <= size() - __offset, "span<T, N>::subspan(offset, count): offset + count out of range");
    return {data() + __offset, __count};
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr size_type size() const noexcept
  {
    return _Extent;
  }
  _LIBCUDACXX_INLINE_VISIBILITY constexpr size_type size_bytes() const noexcept
  {
    return _Extent * sizeof(element_type);
  }
  _CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY constexpr bool empty() const noexcept
  {
    return _Extent == 0;
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr reference operator[](size_type __idx) const noexcept
  {
    _LIBCUDACXX_ASSERT(__idx < size(), "span<T, N>::operator[](index): index out of range");
    return __data_[__idx];
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr reference front() const noexcept
  {
    _LIBCUDACXX_ASSERT(!empty(), "span<T, N>::front() on empty span");
    return __data_[0];
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr reference back() const noexcept
  {
    _LIBCUDACXX_ASSERT(!empty(), "span<T, N>::back() on empty span");
    return __data_[size() - 1];
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr pointer data() const noexcept
  {
    return __data_;
  }

  // [span.iter], span iterator support
  _LIBCUDACXX_INLINE_VISIBILITY constexpr iterator begin() const noexcept
  {
    return iterator(data());
  }
  _LIBCUDACXX_INLINE_VISIBILITY constexpr iterator end() const noexcept
  {
    return iterator(data() + size());
  }
  _LIBCUDACXX_INLINE_VISIBILITY _CCCL_CONSTEXPR_CXX17 reverse_iterator rbegin() const noexcept
  {
    return reverse_iterator(end());
  }
  _LIBCUDACXX_INLINE_VISIBILITY _CCCL_CONSTEXPR_CXX17 reverse_iterator rend() const noexcept
  {
    return reverse_iterator(begin());
  }

  _LIBCUDACXX_INLINE_VISIBILITY span<const byte, _Extent * sizeof(element_type)> __as_bytes() const noexcept
  {
    return span<const byte, _Extent * sizeof(element_type)>{reinterpret_cast<const byte*>(data()), size_bytes()};
  }

  _LIBCUDACXX_INLINE_VISIBILITY span<byte, _Extent * sizeof(element_type)> __as_writable_bytes() const noexcept
  {
    return span<byte, _Extent * sizeof(element_type)>{reinterpret_cast<byte*>(data()), size_bytes()};
  }

private:
  pointer __data_;
};

template <typename _Tp>
class _LIBCUDACXX_TEMPLATE_VIS span<_Tp, dynamic_extent>
{
public:
  //  constants and types
  using element_type     = _Tp;
  using value_type       = remove_cv_t<_Tp>;
  using size_type        = size_t;
  using difference_type  = ptrdiff_t;
  using pointer          = _Tp*;
  using const_pointer    = const _Tp*;
  using reference        = _Tp&;
  using const_reference  = const _Tp&;
  using iterator         = __wrap_iter<pointer>;
  using reverse_iterator = _CUDA_VSTD::reverse_iterator<iterator>;

  static constexpr size_type extent = dynamic_extent;

  // [span.cons], span constructors, copy, assignment, and destructor
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span() noexcept
      : __data_{nullptr}
      , __size_{0}
  {}

  span(const span&) noexcept            = default;
  span& operator=(const span&) noexcept = default;
#  if _CCCL_STD_VER >= 2017 && !defined(_CCCL_COMPILER_MSVC_2017)
  _LIBCUDACXX_TEMPLATE(class _It)
  _LIBCUDACXX_REQUIRES(__span_compatible_iterator<_It, element_type>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(_It __first, size_type __count)
      : __data_{_CUDA_VSTD::to_address(__first)}
      , __size_{__count}
  {}

  _LIBCUDACXX_TEMPLATE(class _It, class _End)
  _LIBCUDACXX_REQUIRES(
    __span_compatible_iterator<_It, element_type> _LIBCUDACXX_AND __span_compatible_sentinel_for<_End, _It>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(_It __first, _End __last)
      : __data_(_CUDA_VSTD::to_address(__first))
      , __size_(__last - __first)
  {
    _LIBCUDACXX_ASSERT(__last - __first >= 0, "invalid range in span's constructor (iterator, sentinel)");
  }

#  else // ^^^ C++17 ^^^ / vvv C++14 vvv
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(pointer __ptr, size_type __count)
      : __data_{__ptr}
      , __size_{__count}
  {}
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(pointer __f, pointer __l)
      : __data_{__f}
      , __size_{static_cast<size_t>(__l - __f)}
  {}
#  endif // _CCCL_STD_VER <= 2014 || _CCCL_COMPILER_MSVC_2017

  template <size_t _Sz>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(type_identity_t<element_type> (&__arr)[_Sz]) noexcept
      : __data_{__arr}
      , __size_{_Sz}
  {}

  _LIBCUDACXX_TEMPLATE(class _OtherElementType, size_t _Sz)
  _LIBCUDACXX_REQUIRES(__span_array_convertible<_OtherElementType, element_type>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(array<_OtherElementType, _Sz>& __arr) noexcept
      : __data_{__arr.data()}
      , __size_{_Sz}
  {}

  _LIBCUDACXX_TEMPLATE(class _OtherElementType, size_t _Sz)
  _LIBCUDACXX_REQUIRES(__span_array_convertible<const _OtherElementType, element_type>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(const array<_OtherElementType, _Sz>& __arr) noexcept
      : __data_{__arr.data()}
      , __size_{_Sz}
  {}

#  if _CCCL_STD_VER >= 2017 && !defined(_CCCL_COMPILER_MSVC_2017)
  _LIBCUDACXX_TEMPLATE(class _Range)
  _LIBCUDACXX_REQUIRES(__span_compatible_range<_Range, element_type>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(_Range&& __r)
      : __data_(_CUDA_VRANGES::data(__r))
      , __size_{_CUDA_VRANGES::size(__r)}
  {}
#  else // ^^^ C++17 ^^^ / vvv C++14 vvv
  _LIBCUDACXX_TEMPLATE(class _Container)
  _LIBCUDACXX_REQUIRES(__is_span_compatible_container<_Container, _Tp>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(_Container& __c)
      : __data_{_CUDA_VSTD::data(__c)}
      , __size_{(size_type) _CUDA_VSTD::size(__c)}
  {}

  _LIBCUDACXX_TEMPLATE(class _Container)
  _LIBCUDACXX_REQUIRES(__is_span_compatible_container<_Container, _Tp>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(const _Container& __c)
      : __data_{_CUDA_VSTD::data(__c)}
      , __size_{(size_type) _CUDA_VSTD::size(__c)}
  {}
#  endif // _CCCL_STD_VER <= 2014 || _CCCL_COMPILER_MSVC_2017

  _LIBCUDACXX_TEMPLATE(class _OtherElementType, size_t _OtherExtent)
  _LIBCUDACXX_REQUIRES(__span_array_convertible<_OtherElementType, element_type>)
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span(const span<_OtherElementType, _OtherExtent>& __other) noexcept
      : __data_{__other.data()}
      , __size_{__other.size()}
  {}

  //    ~span() noexcept = default;

  template <size_t _Count>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span<element_type, _Count> first() const noexcept
  {
    _LIBCUDACXX_ASSERT(_Count <= size(), "span<T>::first<Count>(): Count out of range");
    return span<element_type, _Count>{data(), _Count};
  }

  template <size_t _Count>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr span<element_type, _Count> last() const noexcept
  {
    _LIBCUDACXX_ASSERT(_Count <= size(), "span<T>::last<Count>(): Count out of range");
    return span<element_type, _Count>{data() + size() - _Count, _Count};
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr span<element_type, dynamic_extent> first(size_type __count) const noexcept
  {
    _LIBCUDACXX_ASSERT(__count <= size(), "span<T>::first(count): count out of range");
    return {data(), __count};
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr span<element_type, dynamic_extent> last(size_type __count) const noexcept
  {
    _LIBCUDACXX_ASSERT(__count <= size(), "span<T>::last(count): count out of range");
    return {data() + size() - __count, __count};
  }

  template <size_t _Offset, size_t _Count>
  using __subspan_t = span<element_type, _Count>;

  template <size_t _Offset, size_t _Count = dynamic_extent>
  _LIBCUDACXX_INLINE_VISIBILITY constexpr __subspan_t<_Offset, _Count> subspan() const noexcept
  {
    _LIBCUDACXX_ASSERT(_Offset <= size(), "span<T>::subspan<Offset, Count>(): Offset out of range");
    _LIBCUDACXX_ASSERT(_Count == dynamic_extent || _Count <= size() - _Offset,
                       "span<T>::subspan<Offset, Count>(): Offset + Count out of range");
    return __subspan_t<_Offset, _Count>{data() + _Offset, _Count == dynamic_extent ? size() - _Offset : _Count};
  }

  constexpr span<element_type, dynamic_extent> _LIBCUDACXX_INLINE_VISIBILITY
  subspan(size_type __offset, size_type __count = dynamic_extent) const noexcept
  {
    _LIBCUDACXX_ASSERT(__offset <= size(), "span<T>::subspan(offset, count): offset out of range");
    _LIBCUDACXX_ASSERT(__count <= size() || __count == dynamic_extent,
                       "span<T>::subspan(offset, count): count out of range");
    if (__count == dynamic_extent)
    {
      return {data() + __offset, size() - __offset};
    }
    _LIBCUDACXX_ASSERT(__count <= size() - __offset, "span<T>::subspan(offset, count): offset + count out of range");
    return {data() + __offset, __count};
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr size_type size() const noexcept
  {
    return __size_;
  }
  _LIBCUDACXX_INLINE_VISIBILITY constexpr size_type size_bytes() const noexcept
  {
    return __size_ * sizeof(element_type);
  }
  _CCCL_NODISCARD _LIBCUDACXX_INLINE_VISIBILITY constexpr bool empty() const noexcept
  {
    return __size_ == 0;
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr reference operator[](size_type __idx) const noexcept
  {
    _LIBCUDACXX_ASSERT(__idx < size(), "span<T>::operator[](index): index out of range");
    return __data_[__idx];
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr reference front() const noexcept
  {
    _LIBCUDACXX_ASSERT(!empty(), "span<T>::front() on empty span");
    return __data_[0];
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr reference back() const noexcept
  {
    _LIBCUDACXX_ASSERT(!empty(), "span<T>::back() on empty span");
    return __data_[size() - 1];
  }

  _LIBCUDACXX_INLINE_VISIBILITY constexpr pointer data() const noexcept
  {
    return __data_;
  }

  // [span.iter], span iterator support
  _LIBCUDACXX_INLINE_VISIBILITY constexpr iterator begin() const noexcept
  {
    return iterator(data());
  }
  _LIBCUDACXX_INLINE_VISIBILITY constexpr iterator end() const noexcept
  {
    return iterator(data() + size());
  }
  _LIBCUDACXX_INLINE_VISIBILITY _CCCL_CONSTEXPR_CXX17 reverse_iterator rbegin() const noexcept
  {
    return reverse_iterator(end());
  }
  _LIBCUDACXX_INLINE_VISIBILITY _CCCL_CONSTEXPR_CXX17 reverse_iterator rend() const noexcept
  {
    return reverse_iterator(begin());
  }

  _LIBCUDACXX_INLINE_VISIBILITY span<const byte, dynamic_extent> __as_bytes() const noexcept
  {
    return {reinterpret_cast<const byte*>(data()), size_bytes()};
  }

  _LIBCUDACXX_INLINE_VISIBILITY span<byte, dynamic_extent> __as_writable_bytes() const noexcept
  {
    return {reinterpret_cast<byte*>(data()), size_bytes()};
  }

private:
  pointer __data_;
  size_type __size_;
};

//  as_bytes & as_writable_bytes
template <class _Tp, size_t _Extent>
_LIBCUDACXX_INLINE_VISIBILITY auto as_bytes(span<_Tp, _Extent> __s) noexcept
{
  return __s.__as_bytes();
}

_LIBCUDACXX_TEMPLATE(class _Tp, size_t _Extent)
_LIBCUDACXX_REQUIRES((!is_const<_Tp>::value))
_LIBCUDACXX_INLINE_VISIBILITY auto as_writable_bytes(span<_Tp, _Extent> __s) noexcept
{
  return __s.__as_writable_bytes();
}

#endif // _CCCL_STD_VER >= 2014

#if _CCCL_STD_VER >= 2017

//  Deduction guides
template <class _Tp, size_t _Sz>
_CCCL_HOST_DEVICE span(_Tp (&)[_Sz]) -> span<_Tp, _Sz>;

template <class _Tp, size_t _Sz>
_CCCL_HOST_DEVICE span(array<_Tp, _Sz>&) -> span<_Tp, _Sz>;

template <class _Tp, size_t _Sz>
_CCCL_HOST_DEVICE span(const array<_Tp, _Sz>&) -> span<const _Tp, _Sz>;

#  if defined(_CCCL_COMPILER_MSVC_2017)
template <class _Container>
_CCCL_HOST_DEVICE span(_Container&) -> span<typename _Container::value_type>;

template <class _Container>
_CCCL_HOST_DEVICE span(const _Container&) -> span<const typename _Container::value_type>;
#  else // ^^^ _CCCL_COMPILER_MSVC_2017 ^^^ / vvv !_CCCL_COMPILER_MSVC_2017 vvv

_LIBCUDACXX_TEMPLATE(class _It, class _EndOrSize)
_LIBCUDACXX_REQUIRES(contiguous_iterator<_It>)
_CCCL_HOST_DEVICE span(_It, _EndOrSize)
  -> span<remove_reference_t<iter_reference_t<_It>>, __maybe_static_ext<_EndOrSize>>;

_LIBCUDACXX_TEMPLATE(class _Range)
_LIBCUDACXX_REQUIRES(_CUDA_VRANGES::contiguous_range<_Range>)
_CCCL_HOST_DEVICE span(_Range&&) -> span<remove_reference_t<_CUDA_VRANGES::range_reference_t<_Range>>>;
#  endif // !_CCCL_COMPILER_MSVC_2017

#endif // _CCCL_STD_VER >= 2017

_LIBCUDACXX_END_NAMESPACE_STD

#if _CCCL_STD_VER >= 2017 && !defined(_CCCL_COMPILER_MSVC_2017)
_LIBCUDACXX_BEGIN_NAMESPACE_RANGES
template <class _Tp, size_t _Extent>
_LIBCUDACXX_INLINE_VAR constexpr bool enable_borrowed_range<span<_Tp, _Extent>> = true;

template <class _Tp, size_t _Extent>
_LIBCUDACXX_INLINE_VAR constexpr bool enable_view<span<_Tp, _Extent>> = true;
_LIBCUDACXX_END_NAMESPACE_RANGES
#endif // _CCCL_STD_VER >= 2017 && !_CCCL_COMPILER_MSVC_2017

#endif // _LIBCUDACXX_SPAN
