// Copyright 2019-2024, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto.  Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.

#ifndef _NVCOMPILER_MEMORY_EXECUTION_HEADER_
#define _NVCOMPILER_MEMORY_EXECUTION_HEADER_

#if !defined(_NVCOMPILER_MEMORY_HEADER_) || \
    !defined(_NVCOMPILER_EXECUTION_HEADER_)
  #error <nvhpc/memory_execution.hpp> should not be included directly. Include <memory> and <execution> instead.
#endif

#include <nvhpc/stdpar_config.hpp>

#if _NVHPC_INCLUDE_THRUST
  #define __NVCOMPILER_PROCESSING_THRUST_INCLUDES
  #include <thrust/uninitialized_copy.h>
  #include <thrust/uninitialized_fill.h>
  #undef __NVCOMPILER_PROCESSING_THRUST_INCLUDES
#endif

namespace std {

namespace __stdpar {

// _EP = execution policy
// _FIt = forward iterator
// _BDIt = bidirectional iterator
// _RIt = random access iterator
// _UF = unary operator/function
// _BF = binary operator/function

template <__back_end _BE> struct __memory_impl;

// The sequential back end.  Run the algorithm sequentially.

template <> struct __memory_impl<__back_end::__seq> {

  //========== destroy ==========

  template <class _FIt>
  static void destroy(__no_policy, _FIt __first, _FIt __last) {
    typedef typename std::remove_cv<
        typename std::iterator_traits<_FIt>::value_type>::type _T;
    for(; __first != __last; ++__first) {
      (*__first).~_T();
    }
  }

  //========== destroy_n ==========

  template <class _FIt, class _Size>
  static _FIt destroy_n(__no_policy, _FIt __first, _Size __count) {
    typedef typename std::remove_cv<
        typename std::iterator_traits<_FIt>::value_type>::type _T;
    for(; __count > 0; ++__first, --__count) {
      (*__first).~_T();
    }
    return __first;
  }

  //========== uninitialized_copy ==========

  template <class _FIt1, class _FIt2>
  static _FIt2 uninitialized_copy(__no_policy, _FIt1 __first, _FIt1 __last,
                                  _FIt2 __d_first) {
    return std::uninitialized_copy(__first, __last, __d_first);
  }

  //========== uninitialized_copy_n ==========

  template <class _FIt1, class _Size, class _FIt2>
  static _FIt2 uninitialized_copy_n(__no_policy, _FIt1 __first, _Size __count,
                                    _FIt2 __d_first) {
    return std::uninitialized_copy_n(__first, __count, __d_first);
  }

  //========== uninitialized_fill ==========

  template <class _FIt, class _T>
  static void uninitialized_fill(__no_policy, _FIt __first, _FIt __last,
                                  const _T& __value) {
    std::uninitialized_fill(__first, __last, __value);
  }

  //========== uninitialized_fill_n ==========

  template <class _FIt, class _Size, class _T>
  static _FIt uninitialized_fill_n(__no_policy, _FIt __first, _Size __count,
                                   const _T& __value) {
    return std::uninitialized_fill_n(__first, __count, __value);
  }

  //========== uninitialized_move ==========

  template <class _FIt1, class _FIt2>
  static _FIt2 uninitialized_move(__no_policy, _FIt1 __first, _FIt1 __last,
                                  _FIt2 __d_first) {
    for (; __first != __last; ++__d_first, ++__first) {
      ::new (static_cast<void*>(std::addressof(*__d_first)))
         typename std::iterator_traits<_FIt2>::value_type(std::move(*__first));
    }
    return __d_first;
  }

  //========== uninitialized_move_n ==========

  template <class _FIt1, class _Size, class _FIt2>
  static _FIt2 uninitialized_move_n(__no_policy, _FIt1 __first, _Size __count,
                                    _FIt2 __d_first) {
    for (; __count > 0; ++__d_first, ++__first, --__count) {
      ::new (static_cast<void*>(std::addressof(*__d_first)))
         typename std::iterator_traits<_FIt2>::value_type(std::move(*__first));
    }
    return __d_first;
  }

  //========== uninitialized_default_construct ==========

  template <class _FIt>
  static void uninitialized_default_construct(__no_policy, _FIt __first,
                                              _FIt __last) {
    for (; __first != __last; ++__first) {
      ::new (static_cast<void*>(std::addressof(*__first)))
          typename std::iterator_traits<_FIt>::value_type;
    }
  }

  //========== uninitialized_default_construct_n ==========

  template <class _FIt, class _Size>
  static _FIt uninitialized_default_construct_n(__no_policy, _FIt __first,
                                                _Size __count) {
    for (; __count > 0; ++__first, --__count) {
      ::new (static_cast<void*>(std::addressof(*__first)))
          typename std::iterator_traits<_FIt>::value_type;
    }
    return __first;
  }

  //========== uninitialized_value_construct ==========

  template <class _FIt>
  static void uninitialized_value_construct(__no_policy, _FIt __first,
                                            _FIt __last) {
    for (; __first != __last; ++__first) {
      ::new (static_cast<void*>(std::addressof(*__first)))
          typename std::iterator_traits<_FIt>::value_type();
    }
  }

  //========== uninitialized_value_construct_n ==========

  template <class _FIt, class _Size>
  static _FIt uninitialized_value_construct_n(__no_policy, _FIt __first,
                                              _Size __count) {
    for (; __count > 0; ++__first, --__count) {
      ::new (static_cast<void*>(std::addressof(*__first)))
          typename std::iterator_traits<_FIt>::value_type();
    }
    return __first;
  }

};

// The Thrust parallel back end.  This is used for both CPU and GPU.  The
// correct Thrust execution policy to use to choose between CPU and GPU is a
// template parameter.

template <> struct __memory_impl<__back_end::__thrust_multicore> {

#if _NVHPC_INCLUDE_THRUST

  //========== destroy ==========

  template <class _EP, class _FIt>
  _NVHPC_PARALLEL_IMPL_THRUST
  static void destroy(_EP&& __ep, _FIt __first, _FIt __last) {
    _ASSERT_RANDOM_ACCESS(_FIt);
    using _T = typename std::remove_cv<
        typename std::iterator_traits<_FIt>::value_type>::type;
    thrust::for_each((_EP&&)__ep, ::cuda::counting_iterator<_FIt>(__first),
                     ::cuda::counting_iterator<_FIt>(__last),
                     [](_FIt __it) { (*__it).~_T(); });
  }

  //========== destroy_n ==========

  template <class _EP, class _FIt, class _Size>
  _NVHPC_PARALLEL_IMPL_THRUST
  static _FIt destroy_n(_EP&& __ep, _FIt __first, _Size __count) {
    _ASSERT_RANDOM_ACCESS(_FIt);
    using _T = typename std::remove_cv<
        typename std::iterator_traits<_FIt>::value_type>::type;
    return *thrust::for_each_n((_EP&&)__ep,
                               ::cuda::counting_iterator<_FIt>(__first),
                               __count, [](_FIt __it) { (*__it).~_T(); });
  }

  //========== uninitialized_copy ==========

  template <class _EP, class _FIt1, class _FIt2>
  _NVHPC_PARALLEL_IMPL_THRUST
  static _FIt2 uninitialized_copy(_EP&& __ep, _FIt1 __first, _FIt1 __last,
                                  _FIt2 __d_first) {
    _ASSERT_RANDOM_ACCESS(_FIt1);
    _ASSERT_RANDOM_ACCESS(_FIt2);
    return thrust::uninitialized_copy((_EP&&)__ep, __first, __last, __d_first);
  }

  //========== uninitialized_copy_n ==========

  template <class _EP, class _FIt1, class _Size, class _FIt2>
  _NVHPC_PARALLEL_IMPL_THRUST
  static _FIt2 uninitialized_copy_n(_EP&& __ep, _FIt1 __first, _Size __count,
                                    _FIt2 __d_first) {
    _ASSERT_RANDOM_ACCESS(_FIt1);
    _ASSERT_RANDOM_ACCESS(_FIt2);
    return thrust::uninitialized_copy_n((_EP&&)__ep, __first, __count,
                                        __d_first);
  }

  //========== uninitialized_fill ==========

  template <class _EP, class _FIt, class _T>
  _NVHPC_PARALLEL_IMPL_THRUST
  static void uninitialized_fill(_EP&& __ep, _FIt __first, _FIt __last,
                                 const _T& __value) {
    _ASSERT_RANDOM_ACCESS(_FIt);
    thrust::uninitialized_fill((_EP&&)__ep, __first, __last, __value);
  }

  //========== uninitialized_fill_n ==========

  template <class _EP, class _FIt, class _Size, class _T>
  _NVHPC_PARALLEL_IMPL_THRUST
  static _FIt uninitialized_fill_n(_EP&& __ep, _FIt __first, _Size __count,
                                   const _T& __value) {
    _ASSERT_RANDOM_ACCESS(_FIt);
    return thrust::uninitialized_fill_n((_EP&&)__ep, __first, __count, __value);
  }

  //========== uninitialized_move ==========
  
  // No parallel implementation available.
  
  //========== uninitialized_move_n ==========

  // No parallel implementation available.

  //========== uninitialized_default_construct ==========

  template <class _EP, class _FIt>
  _NVHPC_PARALLEL_IMPL_THRUST
  static void uninitialized_default_construct(_EP&& __ep, _FIt __first,
                                              _FIt __last) {
    _ASSERT_RANDOM_ACCESS(_FIt);
    using _VT = typename std::iterator_traits<_FIt>::value_type;
    thrust::for_each((_EP&&)__ep, ::cuda::counting_iterator<_FIt>(__first),
                     ::cuda::counting_iterator<_FIt>(__last),
                     [](_FIt __it) {
                       ::new (static_cast<void*>(std::addressof(*__it))) _VT;
                     });
  } 
  
  //========== uninitialized_default_construct_n ==========

  template <class _EP, class _FIt, class _Size>
  _NVHPC_PARALLEL_IMPL_THRUST
  static _FIt uninitialized_default_construct_n(_EP&& __ep, _FIt __first,
                                                _Size __count) {
    _ASSERT_RANDOM_ACCESS(_FIt);
    using _VT = typename std::iterator_traits<_FIt>::value_type;
    return *thrust::for_each_n(
      (_EP&&)__ep, ::cuda::counting_iterator<_FIt>(__first), __count,
      [](_FIt __it) {
        ::new (static_cast<void*>(std::addressof(*__it))) _VT;
      });
  }

  //========== uninitialized_value_construct ==========

  template <class _EP, class _FIt>
  _NVHPC_PARALLEL_IMPL_THRUST
  static void uninitialized_value_construct(_EP&& __ep, _FIt __first,
                                            _FIt __last) {
    _ASSERT_RANDOM_ACCESS(_FIt);
    using _VT = typename std::iterator_traits<_FIt>::value_type;
    thrust::for_each((_EP&&)__ep, ::cuda::counting_iterator<_FIt>(__first),
                     ::cuda::counting_iterator<_FIt>(__last),
                     [](_FIt __it) {
                       ::new (static_cast<void*>(std::addressof(*__it))) _VT();
                     });
  }

  //========== uninitialized_value_construct_n ==========

  template <class _EP, class _FIt, class _Size>
  _NVHPC_PARALLEL_IMPL_THRUST
  static _FIt uninitialized_value_construct_n(_EP&& __ep, _FIt __first,
                                              _Size __count) {
    _ASSERT_RANDOM_ACCESS(_FIt);
    using _VT = typename std::iterator_traits<_FIt>::value_type;
    return *thrust::for_each_n(
      (_EP&&)__ep, ::cuda::counting_iterator<_FIt>(__first), __count,
      [](_FIt __it) {
        ::new (static_cast<void*>(std::addressof(*__it))) _VT();
      });
  }
#endif // _NVHPC_INCLUDE_THRUST
};

template <>
struct __memory_impl<__back_end::__thrust_gpu>
    : __memory_impl<__back_end::__thrust_multicore> {
};

// The __gpu_multicore back end is empty because it is treated specially by the
// dispatch framework.
template <> struct __memory_impl<__back_end::__gpu_multicore> { };

template <> struct __memory_impl<__back_end::__openacc> {

#if __NVCOMPILER_STDPAR_OPENACC_GPU || _NVHPC_STDPAR_GPU

  // No <memory> algorithms implemented in OpenACC yet.

#endif // __NVCOMPILER_STDPAR_OPENACC_GPU || _NVHPC_STDPAR_GPU
};

// Each overload of each parallel algorithm has a helper class that packages up
// the arguments so they can be passed around within the dispatch framework.

#define _NVHPC_MEMORY_CALL_IS_VALID(call) \
  _NVHPC_CALL_IS_VALID(__memory_impl, call)

//========== destroy ==========

template <class _FIt>
struct __call_destroy {
  _FIt __first;
  _FIt __last;
  using __return_type = void;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::destroy(
        __policy_for<_BE>::__policy(), __first, __last);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      destroy(__policy_for<_BE>::__policy(), std::declval<_FIt>(),
              std::declval<_FIt>()));
};

//========== destroy_n ==========

template <class _FIt, class _Size>
struct __call_destroy_n {
  _FIt __first;
  _Size __count;
  using __return_type = _FIt;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::destroy_n(
        __policy_for<_BE>::__policy(), __first, __count);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      destroy_n(__policy_for<_BE>::__policy(), std::declval<_FIt>(),
                std::declval<_Size>()));
};

//========== uninitialized_copy ==========

template <class _FIt1, class _FIt2>
struct __call_uninitialized_copy {
  _FIt1 __first;
  _FIt1 __last;
  _FIt2 __d_first;
  using __return_type = _FIt2;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::uninitialized_copy(
        __policy_for<_BE>::__policy(), __first, __last, __d_first);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      uninitialized_copy(__policy_for<_BE>::__policy(), std::declval<_FIt1>(),
                         std::declval<_FIt1>(), std::declval<_FIt2>()));
};

//========== uninitialized_copy_n ==========

template <class _FIt1, class _Size, class _FIt2>
struct __call_uninitialized_copy_n {
  _FIt1 __first;
  _Size __count;
  _FIt2 __d_first;
  using __return_type = _FIt2;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::uninitialized_copy_n(
        __policy_for<_BE>::__policy(), __first, __count, __d_first);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      uninitialized_copy_n(__policy_for<_BE>::__policy(), std::declval<_FIt1>(),
                           std::declval<_Size>(), std::declval<_FIt2>()));
};

//========== uninitialized_fill ==========

template <class _FIt, class _T>
struct __call_uninitialized_fill {
  _FIt __first;
  _FIt __last;
  const _T& __value;
  using __return_type = void;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::uninitialized_fill(
        __policy_for<_BE>::__policy(), __first, __last, __value);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      uninitialized_fill(__policy_for<_BE>::__policy(), std::declval<_FIt>(),
                         std::declval<_FIt>(), std::declval<const _T&>()));
};

//========== uninitialized_fill_n ==========

template <class _FIt, class _Size, class _T>
struct __call_uninitialized_fill_n {
  _FIt __first;
  _Size __count;
  const _T& __value;
  using __return_type = _FIt;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::uninitialized_fill_n(
        __policy_for<_BE>::__policy(), __first, __count, __value);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      uninitialized_fill_n(__policy_for<_BE>::__policy(), std::declval<_FIt>(),
                           std::declval<_Size>(), std::declval<const _T&>()));
};

//========== uninitialized_move ==========

template <class _FIt1, class _FIt2>
struct __call_uninitialized_move {
  _FIt1 __first;
  _FIt1 __last;
  _FIt2 __d_first;
  using __return_type = _FIt2;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::uninitialized_move(
        __policy_for<_BE>::__policy(), __first, __last, __d_first);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      uninitialized_move(__policy_for<_BE>::__policy(), std::declval<_FIt1>(),
                         std::declval<_FIt1>(), std::declval<_FIt2>()));
};

//========== uninitialized_move_n ==========

template <class _FIt1, class _Size, class _FIt2>
struct __call_uninitialized_move_n {
  _FIt1 __first;
  _Size __count;
  _FIt2 __d_first;
  using __return_type = _FIt2;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::uninitialized_move_n(
        __policy_for<_BE>::__policy(), __first, __count, __d_first);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      uninitialized_move_n(__policy_for<_BE>::__policy(), std::declval<_FIt1>(),
                           std::declval<_Size>(), std::declval<_FIt2>()));
};

//========== uninitialized_default_construct ==========

template <class _FIt>
struct __call_uninitialized_default_construct {
  _FIt __first;
  _FIt __last;
  using __return_type = void;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::uninitialized_default_construct(
        __policy_for<_BE>::__policy(), __first, __last);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      uninitialized_default_construct(__policy_for<_BE>::__policy(),
                                      std::declval<_FIt>(),
                                      std::declval<_FIt>()));
};

//========== uninitialized_default_construct_n ==========

template <class _FIt, class _Size>
struct __call_uninitialized_default_construct_n {
  _FIt __first;
  _Size __count;
  using __return_type = _FIt;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::uninitialized_default_construct_n(
        __policy_for<_BE>::__policy(), __first, __count);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      uninitialized_default_construct_n(__policy_for<_BE>::__policy(),
                                        std::declval<_FIt>(),
                                        std::declval<_Size>()));
};

//========== uninitialized_value_construct ==========

template <class _FIt>
struct __call_uninitialized_value_construct {
  _FIt __first;
  _FIt __last;
  using __return_type = void;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::uninitialized_value_construct(
        __policy_for<_BE>::__policy(), __first, __last);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      uninitialized_value_construct(__policy_for<_BE>::__policy(),
                                    std::declval<_FIt>(),
                                    std::declval<_FIt>()));
};

//========== uninitialized_value_construct_n ==========

template <class _FIt, class _Size>
struct __call_uninitialized_value_construct_n {
  _FIt __first;
  _Size __count;
  using __return_type = _FIt;
  template <__back_end _BE>
  _NVHPC_PARALLEL_FRAMEWORK_IMPL __return_type __call() const {
    return __memory_impl<_BE>::uninitialized_value_construct_n(
        __policy_for<_BE>::__policy(), __first, __count);
  }
  _NVHPC_MEMORY_CALL_IS_VALID(
      uninitialized_value_construct_n(__policy_for<_BE>::__policy(),
                                      std::declval<_FIt>(),
                                      std::declval<_Size>()));
};

} // namespace __stdpar

// The definitions of the standard parallel algorithms.

//========== destroy ==========

template <class _EP, class _FIt>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, void>
destroy(_EP&&, _FIt __first, _FIt __last) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_destroy<_FIt>{__first, __last});
  } catch (...) { std::terminate(); }
}

//========== destroy_n ==========

template <class _EP, class _FIt, class _Size>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, _FIt>
destroy_n(_EP&&, _FIt __first, _Size __count) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_destroy_n<_FIt, _Size>{__first, __count});
  } catch (...) { std::terminate(); }
}

//========== uninitialized_copy ==========

template <class _EP, class _FIt1, class _FIt2>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, _FIt2>
uninitialized_copy(_EP&&, _FIt1 __first, _FIt1 __last, _FIt2 __d_first) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_uninitialized_copy<_FIt1, _FIt2>{
          __first, __last, __d_first});
  } catch (...) { std::terminate(); }
}

//========== uninitialized_copy_n ==========

template <class _EP, class _FIt1, class _Size, class _FIt2>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, _FIt2>
uninitialized_copy_n(_EP&&, _FIt1 __first, _Size __count, _FIt2 __d_first) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_uninitialized_copy_n<_FIt1, _Size, _FIt2>{
          __first, __count, __d_first});
  } catch (...) { std::terminate(); }
}

//========== uninitialized_fill ==========

template <class _EP, class _FIt, class _T>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, void>
uninitialized_fill(_EP&&, _FIt __first, _FIt __last, const _T& __value) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_uninitialized_fill<_FIt, _T>{
          __first, __last, __value});
  } catch (...) { std::terminate(); }
}

//========== uninitialized_fill_n ==========

template <class _EP, class _FIt, class _Size, class _T>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, _FIt>
uninitialized_fill_n(_EP&&, _FIt __first, _Size __count, const _T& __value) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_uninitialized_fill_n<_FIt, _Size, _T>{
          __first, __count, __value});
  } catch (...) { std::terminate(); }
}

//========== uninitialized_move ==========

template <class _EP, class _FIt1, class _FIt2>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, _FIt2>
uninitialized_move(_EP&&, _FIt1 __first, _FIt1 __last, _FIt2 __d_first) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_uninitialized_move<_FIt1, _FIt2>{
          __first, __last, __d_first});
  } catch (...) { std::terminate(); }
}

//========== uninitialized_move_n ==========

template <class _EP, class _FIt1, class _Size, class _FIt2>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, _FIt2>
uninitialized_move_n(_EP&&, _FIt1 __first, _Size __count, _FIt2 __d_first) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_uninitialized_move_n<_FIt1, _Size, _FIt2>{
          __first, __count, __d_first});
  } catch (...) { std::terminate(); }
}

//========== uninitialized_default_construct ==========

template <class _EP, class _FIt>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, void>
uninitialized_default_construct(_EP&&, _FIt __first, _FIt __last) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_uninitialized_default_construct<_FIt>{
          __first, __last});
  } catch (...) { std::terminate(); }
}

//========== uninitialized_default_construct_n ==========

template <class _EP, class _FIt, class _Size>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, _FIt>
uninitialized_default_construct_n(_EP&&, _FIt __first, _Size __count) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_uninitialized_default_construct_n<_FIt, _Size>{
          __first, __count});
  } catch (...) { std::terminate(); }
} 

//========== uninitialized_value_construct ==========

template <class _EP, class _FIt>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, void>
uninitialized_value_construct(_EP&&, _FIt __first, _FIt __last) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_uninitialized_value_construct<_FIt>{__first, __last});
  } catch (...) { std::terminate(); }
}

//========== uninitialized_value_construct_n ==========

template <class _EP, class _FIt, class _Size>
_NVHPC_PARALLEL_ALGORITHM
__stdpar::__enable_if_EP<_EP, _FIt>
uninitialized_value_construct_n(_EP&&, _FIt __first, _Size __count) {
  try {
    _NVHPC_STDPAR_NVTX_RANGE
    return __stdpar::__dispatch<_EP>(
        __stdpar::__call_uninitialized_value_construct_n<_FIt, _Size>{
          __first, __count});
  } catch (...) { std::terminate(); }
}

} // namespace std

#endif
