// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// SPDX-FileCopyrightText: Copyright Contributors to the Kokkos project

#include <Kokkos_Macros.hpp>
#ifdef KOKKOS_ENABLE_EXPERIMENTAL_CXX20_MODULES
import kokkos.core;
#else
#include <Kokkos_Core.hpp>
#endif
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <type_traits>

namespace {

struct TestMDRangePolicyCTAD {
  template <typename... Ts>
  static void maybe_unused(Ts&&...) {}

  struct SomeExecutionSpace {
    using execution_space = SomeExecutionSpace;
    using index_type      = int;
  };
  static_assert(Kokkos::is_execution_space_v<SomeExecutionSpace>);

  struct ImplicitlyConvertibleToDefaultExecutionSpace {
    [[maybe_unused]] operator Kokkos::DefaultExecutionSpace() const {
      return Kokkos::DefaultExecutionSpace();
    }
  };
  static_assert(!Kokkos::is_execution_space_v<
                ImplicitlyConvertibleToDefaultExecutionSpace>);

  [[maybe_unused]] static inline Kokkos::DefaultExecutionSpace des;
  [[maybe_unused]] static inline ImplicitlyConvertibleToDefaultExecutionSpace
      notEs;
  [[maybe_unused]] static inline SomeExecutionSpace ses;

  [[maybe_unused]] static inline int t[5];
  [[maybe_unused]] static inline int64_t tt[5];
  [[maybe_unused]] static inline int rank1_t[1];
  [[maybe_unused]] static inline int64_t rank1_tt[1];
  [[maybe_unused]] static inline Kokkos::Array<int64_t, 3> a;
  [[maybe_unused]] static inline Kokkos::Array<int64_t, 2> aa;
  [[maybe_unused]] static inline Kokkos::Array<int64_t, 1> rank1_a;
  [[maybe_unused]] static inline int64_t i64;

  // Workaround for nvc++ (CUDA-11.7-NVHPC) ignoring [[maybe_unused]] on
  // ImplicitlyConvertibleToDefaultExecutionSpace::operator
  // Kokkos::DefaultExecutionSpace() const
  [[maybe_unused]] static inline Kokkos::DefaultExecutionSpace notEsToDes =
      notEs;

  // Workaround for HIP-ROCm-5.2 "declared but never referenced"
  TestMDRangePolicyCTAD() {
    maybe_unused(des, notEs, ses, t, tt, rank1_t, rank1_tt, a, aa, rank1_a,
                 notEsToDes, i64);
  }

  // MDRangePolicy with C array parameters

  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<std::size(t)>>,
                     decltype(Kokkos::MDRangePolicy(t, t))>);
  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<std::size(t)>>,
                     decltype(Kokkos::MDRangePolicy(t, t, tt))>);
  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<std::size(t)>>,
                     decltype(Kokkos::MDRangePolicy(des, t, tt))>);
  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<std::size(t)>>,
                     decltype(Kokkos::MDRangePolicy(notEs, t, t))>);

  static_assert(
      std::is_same_v<
          Kokkos::MDRangePolicy<SomeExecutionSpace, Kokkos::Rank<std::size(t)>>,
          decltype(Kokkos::MDRangePolicy(ses, t, t))>);

  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<1>>,
                     decltype(Kokkos::MDRangePolicy(rank1_t, rank1_t))>);
  static_assert(std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<1>>,
                               decltype(Kokkos::MDRangePolicy(rank1_t, rank1_t,
                                                              rank1_tt))>);

  // MDRangePolicy with Kokkos::initializer_list parameters

  static_assert(std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<6>>,
                               decltype(Kokkos::MDRangePolicy(
                                   {1, 2, 3, 4, 5, 6}, {1, 2, 3, 4, 5, 6}))>);

  static_assert(std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<6>>,
                               decltype(Kokkos::MDRangePolicy(
                                   {1, 2, 3, 4, 5, 6}, {1, 2, 3, 4, 5, 6},
                                   {i64, i64, i64, i64, i64, i64}))>);

  static_assert(std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<6>>,
                               decltype(Kokkos::MDRangePolicy(
                                   des, {1, 2, 3, 4, 5, 6},
                                   {i64, i64, i64, i64, i64, i64}))>);

  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<6>>,
                     decltype(Kokkos::MDRangePolicy(notEs, {1, 2, 3, 4, 5, 6},
                                                    {1, 2, 3, 4, 5, 6}))>);

  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<SomeExecutionSpace, Kokkos::Rank<6>>,
                     decltype(Kokkos::MDRangePolicy(ses, {1, 2, 3, 4, 5, 6},
                                                    {1, 2, 3, 4, 5, 6}))>);

  // MDRangePolicy with Kokkos::Array parameters

  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<std::size(a)>>,
                     decltype(Kokkos::MDRangePolicy(a, a))>);
  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<std::size(a)>>,
                     decltype(Kokkos::MDRangePolicy(a, a, aa))>);
  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<std::size(a)>>,
                     decltype(Kokkos::MDRangePolicy(des, a, a))>);
  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<std::size(a)>>,
                     decltype(Kokkos::MDRangePolicy(notEs, a, a))>);
  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<std::size(a)>>,
                     decltype(Kokkos::MDRangePolicy(des, a, a, aa))>);
  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<std::size(a)>>,
                     decltype(Kokkos::MDRangePolicy(notEs, a, a, aa))>);

  static_assert(
      std::is_same_v<
          Kokkos::MDRangePolicy<SomeExecutionSpace, Kokkos::Rank<std::size(a)>>,
          decltype(Kokkos::MDRangePolicy(ses, a, a))>);
  static_assert(
      std::is_same_v<
          Kokkos::MDRangePolicy<SomeExecutionSpace, Kokkos::Rank<std::size(a)>>,
          decltype(Kokkos::MDRangePolicy(ses, a, a, aa))>);

  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<Kokkos::Rank<1>>,
                     decltype(Kokkos::MDRangePolicy(rank1_a, rank1_a))>);
  static_assert(
      std::is_same_v<Kokkos::MDRangePolicy<SomeExecutionSpace, Kokkos::Rank<1>>,
                     decltype(Kokkos::MDRangePolicy(ses, rank1_a, rank1_a))>);
};

}  // namespace
