//============================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//============================================================================
// **** DO NOT EDIT THIS FILE!!! ****
// This file is automatically generated by VariantDetail.h.in

#if !defined(VTK_M_DEVICE) || !defined(VTK_M_NAMESPACE)
#error VarianImplDetail.h must be included from VariantImpl.h
// Some defines to make my IDE happy.
#define VTK_M_DEVICE
#define VTK_M_NAMESPACE tmp
#endif

#include <vtkm/Types.h>

#include <vtkm/internal/Assume.h>

#include <vtkmstd/is_trivial.h>

#include <type_traits>



namespace vtkm
{
namespace VTK_M_NAMESPACE
{
namespace internal
{
namespace detail
{

template <typename ReturnType>
struct VariantDummyReturn
{
  VTK_M_DEVICE static inline ReturnType F() noexcept { return ReturnType{}; }
};
template <>
struct VariantDummyReturn<void>
{
  VTK_M_DEVICE static inline void F() noexcept {}
};

// clang-format off

// --------------------------------------------------------------------------------
// Helper classes to determine if all Variant types are trivial.
template <typename T0 = vtkm::internal::NullType,
          typename T1 = vtkm::internal::NullType,
          typename T2 = vtkm::internal::NullType,
          typename T3 = vtkm::internal::NullType,
          typename T4 = vtkm::internal::NullType,
          typename T5 = vtkm::internal::NullType,
          typename T6 = vtkm::internal::NullType,
          typename T7 = vtkm::internal::NullType,
          typename T8 = vtkm::internal::NullType,
          typename T9 = vtkm::internal::NullType,
          typename T10 = vtkm::internal::NullType,
          typename T11 = vtkm::internal::NullType,
          typename T12 = vtkm::internal::NullType,
          typename T13 = vtkm::internal::NullType,
          typename T14 = vtkm::internal::NullType,
          typename T15 = vtkm::internal::NullType,
          typename T16 = vtkm::internal::NullType,
          typename T17 = vtkm::internal::NullType,
          typename T18 = vtkm::internal::NullType,
          typename T19 = vtkm::internal::NullType,
          typename... Ts>
struct AllTriviallyCopyable;

template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19>
struct AllTriviallyCopyable<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>
  : std::integral_constant<bool, (vtkmstd::is_trivially_copyable<T0>::value &&
                                  vtkmstd::is_trivially_copyable<T1>::value &&
                                  vtkmstd::is_trivially_copyable<T2>::value &&
                                  vtkmstd::is_trivially_copyable<T3>::value &&
                                  vtkmstd::is_trivially_copyable<T4>::value &&
                                  vtkmstd::is_trivially_copyable<T5>::value &&
                                  vtkmstd::is_trivially_copyable<T6>::value &&
                                  vtkmstd::is_trivially_copyable<T7>::value &&
                                  vtkmstd::is_trivially_copyable<T8>::value &&
                                  vtkmstd::is_trivially_copyable<T9>::value &&
                                  vtkmstd::is_trivially_copyable<T10>::value &&
                                  vtkmstd::is_trivially_copyable<T11>::value &&
                                  vtkmstd::is_trivially_copyable<T12>::value &&
                                  vtkmstd::is_trivially_copyable<T13>::value &&
                                  vtkmstd::is_trivially_copyable<T14>::value &&
                                  vtkmstd::is_trivially_copyable<T15>::value &&
                                  vtkmstd::is_trivially_copyable<T16>::value &&
                                  vtkmstd::is_trivially_copyable<T17>::value &&
                                  vtkmstd::is_trivially_copyable<T18>::value &&
                                  vtkmstd::is_trivially_copyable<T19>::value)> { };

template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename... Ts>
struct AllTriviallyCopyable<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, Ts...>
  : std::integral_constant<bool, (
      AllTriviallyCopyable<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::value &&
      AllTriviallyCopyable<T20, Ts...>::value)> {  };

template <typename T0 = vtkm::internal::NullType,
          typename T1 = vtkm::internal::NullType,
          typename T2 = vtkm::internal::NullType,
          typename T3 = vtkm::internal::NullType,
          typename T4 = vtkm::internal::NullType,
          typename T5 = vtkm::internal::NullType,
          typename T6 = vtkm::internal::NullType,
          typename T7 = vtkm::internal::NullType,
          typename T8 = vtkm::internal::NullType,
          typename T9 = vtkm::internal::NullType,
          typename T10 = vtkm::internal::NullType,
          typename T11 = vtkm::internal::NullType,
          typename T12 = vtkm::internal::NullType,
          typename T13 = vtkm::internal::NullType,
          typename T14 = vtkm::internal::NullType,
          typename T15 = vtkm::internal::NullType,
          typename T16 = vtkm::internal::NullType,
          typename T17 = vtkm::internal::NullType,
          typename T18 = vtkm::internal::NullType,
          typename T19 = vtkm::internal::NullType,
          typename... Ts>
struct AllTriviallyConstructible;

template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19>
struct AllTriviallyConstructible<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>
  : std::integral_constant<bool, (vtkmstd::is_trivially_constructible<T0>::value &&
                                  vtkmstd::is_trivially_constructible<T1>::value &&
                                  vtkmstd::is_trivially_constructible<T2>::value &&
                                  vtkmstd::is_trivially_constructible<T3>::value &&
                                  vtkmstd::is_trivially_constructible<T4>::value &&
                                  vtkmstd::is_trivially_constructible<T5>::value &&
                                  vtkmstd::is_trivially_constructible<T6>::value &&
                                  vtkmstd::is_trivially_constructible<T7>::value &&
                                  vtkmstd::is_trivially_constructible<T8>::value &&
                                  vtkmstd::is_trivially_constructible<T9>::value &&
                                  vtkmstd::is_trivially_constructible<T10>::value &&
                                  vtkmstd::is_trivially_constructible<T11>::value &&
                                  vtkmstd::is_trivially_constructible<T12>::value &&
                                  vtkmstd::is_trivially_constructible<T13>::value &&
                                  vtkmstd::is_trivially_constructible<T14>::value &&
                                  vtkmstd::is_trivially_constructible<T15>::value &&
                                  vtkmstd::is_trivially_constructible<T16>::value &&
                                  vtkmstd::is_trivially_constructible<T17>::value &&
                                  vtkmstd::is_trivially_constructible<T18>::value &&
                                  vtkmstd::is_trivially_constructible<T19>::value)> { };

template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename... Ts>
struct AllTriviallyConstructible<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, Ts...>
  : std::integral_constant<bool, (
      AllTriviallyConstructible<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::value &&
      AllTriviallyConstructible<T20, Ts...>::value)> {  };

template <typename T0 = vtkm::internal::NullType,
          typename T1 = vtkm::internal::NullType,
          typename T2 = vtkm::internal::NullType,
          typename T3 = vtkm::internal::NullType,
          typename T4 = vtkm::internal::NullType,
          typename T5 = vtkm::internal::NullType,
          typename T6 = vtkm::internal::NullType,
          typename T7 = vtkm::internal::NullType,
          typename T8 = vtkm::internal::NullType,
          typename T9 = vtkm::internal::NullType,
          typename T10 = vtkm::internal::NullType,
          typename T11 = vtkm::internal::NullType,
          typename T12 = vtkm::internal::NullType,
          typename T13 = vtkm::internal::NullType,
          typename T14 = vtkm::internal::NullType,
          typename T15 = vtkm::internal::NullType,
          typename T16 = vtkm::internal::NullType,
          typename T17 = vtkm::internal::NullType,
          typename T18 = vtkm::internal::NullType,
          typename T19 = vtkm::internal::NullType,
          typename... Ts>
struct AllTriviallyDestructible;

template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19>
struct AllTriviallyDestructible<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>
  : std::integral_constant<bool, (vtkmstd::is_trivially_destructible<T0>::value &&
                                  vtkmstd::is_trivially_destructible<T1>::value &&
                                  vtkmstd::is_trivially_destructible<T2>::value &&
                                  vtkmstd::is_trivially_destructible<T3>::value &&
                                  vtkmstd::is_trivially_destructible<T4>::value &&
                                  vtkmstd::is_trivially_destructible<T5>::value &&
                                  vtkmstd::is_trivially_destructible<T6>::value &&
                                  vtkmstd::is_trivially_destructible<T7>::value &&
                                  vtkmstd::is_trivially_destructible<T8>::value &&
                                  vtkmstd::is_trivially_destructible<T9>::value &&
                                  vtkmstd::is_trivially_destructible<T10>::value &&
                                  vtkmstd::is_trivially_destructible<T11>::value &&
                                  vtkmstd::is_trivially_destructible<T12>::value &&
                                  vtkmstd::is_trivially_destructible<T13>::value &&
                                  vtkmstd::is_trivially_destructible<T14>::value &&
                                  vtkmstd::is_trivially_destructible<T15>::value &&
                                  vtkmstd::is_trivially_destructible<T16>::value &&
                                  vtkmstd::is_trivially_destructible<T17>::value &&
                                  vtkmstd::is_trivially_destructible<T18>::value &&
                                  vtkmstd::is_trivially_destructible<T19>::value)> { };

template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename... Ts>
struct AllTriviallyDestructible<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, Ts...>
  : std::integral_constant<bool, (
      AllTriviallyDestructible<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::value &&
      AllTriviallyDestructible<T20, Ts...>::value)> {  };

// --------------------------------------------------------------------------------
// Union type used inside of Variant
//
// You may be asking yourself, why not just use an std::aligned_union rather than a real union
// type? That was our first implementation, but the problem is that the std::aligned_union
// reference needs to be recast to the actual type. Typically you would do that with
// reinterpret_cast. However, doing that leads to undefined behavior. The C++ compiler assumes that
// 2 pointers of different types point to different memory (even if it is clear that they are set
// to the same address). That means optimizers can remove code because it "knows" that data in one
// type cannot affect data in another type. (See Shafik Yaghmour's excellent writeup at
// https://gist.github.com/shafik/848ae25ee209f698763cffee272a58f8 for more details.) To safely
// change the type of an std::aligned_union, you really have to do an std::memcpy. This is
// problematic for types that cannot be trivially copied. Another problem is that we found that
// device compilers do not optimize the memcpy as well as most CPU compilers. Likely, memcpy is
// used much less frequently on GPU devices.
//
// Part of the trickiness of the union implementation is trying to preserve when the type is
// trivially constructible and copyable. The trick is that if members of the union are not trivial,
// then the default constructors are deleted. To get around that, a non-default constructor is
// added, which we can use to construct the union for non-trivial types. Working with types with
// non-trivial destructors are particularly tricky. Again, if any member of the union has a
// non-trivial destructor, the destructor is deleted. Unlike a constructor, you cannot just say to
// use a different destructor. Thus, we have to define our own destructor for the union.
// Technically, the destructor here does not do anything, but the actual destruction should be
// handled by the Variant class that contains this VariantUnion. We actually need two separate
// implementations of our union, one that defines a destructor and one that use the default
// destructor. If you define your own destructor, you can lose the trivial constructor and trivial
// copy properties.
//

// TD = trivially deconstructible
template <typename T0 = vtkm::internal::NullType,
          typename T1 = vtkm::internal::NullType,
          typename T2 = vtkm::internal::NullType,
          typename T3 = vtkm::internal::NullType,
          typename T4 = vtkm::internal::NullType,
          typename T5 = vtkm::internal::NullType,
          typename T6 = vtkm::internal::NullType,
          typename T7 = vtkm::internal::NullType,
          typename T8 = vtkm::internal::NullType,
          typename T9 = vtkm::internal::NullType,
          typename T10 = vtkm::internal::NullType,
          typename T11 = vtkm::internal::NullType,
          typename T12 = vtkm::internal::NullType,
          typename T13 = vtkm::internal::NullType,
          typename T14 = vtkm::internal::NullType,
          typename T15 = vtkm::internal::NullType,
          typename T16 = vtkm::internal::NullType,
          typename T17 = vtkm::internal::NullType,
          typename T18 = vtkm::internal::NullType,
          typename T19 = vtkm::internal::NullType,
          typename... Ts>
union VariantUnionTD;

// NTD = non-trivially deconstructible
template <typename T0 = vtkm::internal::NullType,
          typename T1 = vtkm::internal::NullType,
          typename T2 = vtkm::internal::NullType,
          typename T3 = vtkm::internal::NullType,
          typename T4 = vtkm::internal::NullType,
          typename T5 = vtkm::internal::NullType,
          typename T6 = vtkm::internal::NullType,
          typename T7 = vtkm::internal::NullType,
          typename T8 = vtkm::internal::NullType,
          typename T9 = vtkm::internal::NullType,
          typename T10 = vtkm::internal::NullType,
          typename T11 = vtkm::internal::NullType,
          typename T12 = vtkm::internal::NullType,
          typename T13 = vtkm::internal::NullType,
          typename T14 = vtkm::internal::NullType,
          typename T15 = vtkm::internal::NullType,
          typename T16 = vtkm::internal::NullType,
          typename T17 = vtkm::internal::NullType,
          typename T18 = vtkm::internal::NullType,
          typename T19 = vtkm::internal::NullType,
          typename... Ts>
union VariantUnionNTD;

template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19>
union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>
{
  T0 V0;
  T1 V1;
  T2 V2;
  T3 V3;
  T4 V4;
  T5 V5;
  T6 V6;
  T7 V7;
  T8 V8;
  T9 V9;
  T10 V10;
  T11 V11;
  T12 V12;
  T13 V13;
  T14 V14;
  T15 V15;
  T16 V16;
  T17 V17;
  T18 V18;
  T19 V19;
  vtkm::internal::NullType Remaining;

  VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
  VariantUnionTD() = default;
};

template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19>
union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>
{
  T0 V0;
  T1 V1;
  T2 V2;
  T3 V3;
  T4 V4;
  T5 V5;
  T6 V6;
  T7 V7;
  T8 V8;
  T9 V9;
  T10 V10;
  T11 V11;
  T12 V12;
  T13 V13;
  T14 V14;
  T15 V15;
  T16 V16;
  T17 V17;
  T18 V18;
  T19 V19;
  vtkm::internal::NullType Remaining;

  VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
  VariantUnionNTD() = default;
  VTK_M_DEVICE ~VariantUnionNTD() { }
};

template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename... Ts>
union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, Ts...>
{
  T0 V0;
  T1 V1;
  T2 V2;
  T3 V3;
  T4 V4;
  T5 V5;
  T6 V6;
  T7 V7;
  T8 V8;
  T9 V9;
  T10 V10;
  T11 V11;
  T12 V12;
  T13 V13;
  T14 V14;
  T15 V15;
  T16 V16;
  T17 V17;
  T18 V18;
  T19 V19;
  VariantUnionTD<T20, Ts...> Remaining;

  VTK_M_DEVICE VariantUnionTD(vtkm::internal::NullType) { }
  VariantUnionTD() = default;
};

template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10, typename T11, typename T12, typename T13, typename T14, typename T15, typename T16, typename T17, typename T18, typename T19, typename T20, typename... Ts>
union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, Ts...>
{
  T0 V0;
  T1 V1;
  T2 V2;
  T3 V3;
  T4 V4;
  T5 V5;
  T6 V6;
  T7 V7;
  T8 V8;
  T9 V9;
  T10 V10;
  T11 V11;
  T12 V12;
  T13 V13;
  T14 V14;
  T15 V15;
  T16 V16;
  T17 V17;
  T18 V18;
  T19 V19;
  VariantUnionNTD<T20, Ts...> Remaining;

  VTK_M_DEVICE VariantUnionNTD(vtkm::internal::NullType) { }
  VariantUnionNTD() = default;
  VTK_M_DEVICE ~VariantUnionNTD() { }
};

template <bool TrivialConstructor, typename... Ts>
struct VariantUnionFinder;

template <typename... Ts>
struct VariantUnionFinder<true, Ts...>
{
  using type = VariantUnionTD<Ts...>;
};
template <typename... Ts>
struct VariantUnionFinder<false, Ts...>
{
  using type = VariantUnionNTD<Ts...>;
};

template <typename... Ts>
using VariantUnion =
  typename VariantUnionFinder<AllTriviallyDestructible<Ts...>::value, Ts...>::type;

// --------------------------------------------------------------------------------
// Methods to get values out of the variant union
template <vtkm::IdComponent I, typename UnionType>
struct VariantUnionGetImpl;

template <typename UnionType>
struct VariantUnionGetImpl<0, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V0);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V0;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V0;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<1, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V1);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V1;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V1;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<2, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V2);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V2;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V2;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<3, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V3);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V3;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V3;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<4, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V4);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V4;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V4;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<5, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V5);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V5;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V5;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<6, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V6);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V6;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V6;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<7, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V7);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V7;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V7;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<8, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V8);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V8;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V8;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<9, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V9);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V9;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V9;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<10, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V10);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V10;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V10;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<11, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V11);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V11;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V11;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<12, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V12);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V12;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V12;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<13, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V13);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V13;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V13;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<14, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V14);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V14;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V14;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<15, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V15);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V15;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V15;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<16, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V16);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V16;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V16;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<17, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V17);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V17;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V17;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<18, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V18);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V18;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V18;
  }
};

template <typename UnionType>
struct VariantUnionGetImpl<19, UnionType>
{
  using ReturnType = decltype(std::declval<UnionType>().V19);
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return storage.V19;
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return storage.V19;
  }
};


template <vtkm::IdComponent I, typename UnionType>
struct VariantUnionGetImpl
{
  VTKM_STATIC_ASSERT(I >= 20);
  using RecursiveGet = VariantUnionGetImpl<I - 20, decltype(std::declval<UnionType&>().Remaining)>;
  using ReturnType = typename RecursiveGet::ReturnType;
  VTK_M_DEVICE static ReturnType& Get(UnionType& storage) noexcept
  {
    return RecursiveGet::Get(storage.Remaining);
  }
  VTK_M_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
  {
    return RecursiveGet::Get(storage.Remaining);
  }
};

template <vtkm::IdComponent I, typename UnionType>
VTK_M_DEVICE auto VariantUnionGet(UnionType& storage) noexcept
  -> decltype(VariantUnionGetImpl<I, typename std::decay<UnionType>::type>::Get(storage))&
{
  return VariantUnionGetImpl<I, typename std::decay<UnionType>::type>::Get(storage);
}

//clang-format on

// --------------------------------------------------------------------------------
// Internal implementation of CastAndCall for Variant
template <typename ReturnType,
          typename Functor,
          typename CastMember,
          typename... Args>
VTK_M_DEVICE inline ReturnType VariantCallFunctor(
  std::false_type,
  Functor&& f,
  CastMember& value,
  Args&&... args) noexcept(noexcept(f(value, args...)))
{
  // If you get a compile error here, it probably means that you have called Variant::CastAndCall
  // with a functor that does not accept one of the types in the Variant. The functor you provide
  // must be callable with all types in the Variant, not just the one that it currently holds.
  return f(value, std::forward<Args>(args)...);
}

template <typename ReturnType,
          typename Functor,
          typename... Args>
VTK_M_DEVICE inline ReturnType VariantCallFunctor(
  std::true_type, Functor&&, vtkm::internal::NullType, Args&&...) noexcept
{
  // If we are here, it means a Variant had an inappropriate index.
  VTKM_ASSERT(false && "Internal error, bad Variant state.");
  return VariantDummyReturn<ReturnType>::F();
}

// Terminating condition in recursive template
template <typename ReturnType, typename Functor, typename... Args>
VTK_M_DEVICE inline ReturnType VariantCastAndCallImplR(
  std::true_type,
  vtkm::IdComponent vtkmNotUsed(index),
  Functor&& vtkmNotUsed(f),
  vtkm::internal::NullType,
  Args&&... vtkmNotUsed(args)) noexcept
{
  // If we are here, it means a Variant had an inappropriate index.
  VTKM_ASSERT(false && "Internal error, bad Variant state.");
  return VariantDummyReturn<ReturnType>::F();
}

template <typename ReturnType, typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE inline ReturnType VariantCastAndCallImplR(
  std::false_type,
  vtkm::IdComponent index,
  Functor&& f,
  UnionType& storage,
  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
{
  switch (index)
  {
    case 0:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V0), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V0,
        std::forward<Args>(args)...);
    case 1:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V1), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V1,
        std::forward<Args>(args)...);
    case 2:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V2), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V2,
        std::forward<Args>(args)...);
    case 3:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V3), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V3,
        std::forward<Args>(args)...);
    case 4:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V4), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V4,
        std::forward<Args>(args)...);
    case 5:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V5), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V5,
        std::forward<Args>(args)...);
    case 6:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V6), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V6,
        std::forward<Args>(args)...);
    case 7:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V7), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V7,
        std::forward<Args>(args)...);
    case 8:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V8), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V8,
        std::forward<Args>(args)...);
    case 9:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V9), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V9,
        std::forward<Args>(args)...);
    case 10:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V10), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V10,
        std::forward<Args>(args)...);
    case 11:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V11), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V11,
        std::forward<Args>(args)...);
    case 12:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V12), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V12,
        std::forward<Args>(args)...);
    case 13:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V13), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V13,
        std::forward<Args>(args)...);
    case 14:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V14), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V14,
        std::forward<Args>(args)...);
    case 15:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V15), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V15,
        std::forward<Args>(args)...);
    case 16:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V16), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V16,
        std::forward<Args>(args)...);
    case 17:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V17), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V17,
        std::forward<Args>(args)...);
    case 18:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V18), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V18,
        std::forward<Args>(args)...);
    case 19:
      return VariantCallFunctor<ReturnType>(
        typename std::is_same<decltype(storage.V19), vtkm::internal::NullType>::type{},
        std::forward<Functor>(f),
        storage.V19,
        std::forward<Args>(args)...);
    default:
      return VariantCastAndCallImplR<ReturnType>(
        typename std::is_same<decltype(storage.Remaining), vtkm::internal::NullType>::type{},
        index - 20,
        std::forward<Functor>(f),
        storage.Remaining,
        std::forward<Args>(args)...);
  }
}

template <typename Functor, typename UnionType, typename... Args>
VTK_M_DEVICE inline auto VariantCastAndCallImpl(
  vtkm::IdComponent index,
  Functor&& f,
  UnionType& storage,
  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
  -> decltype(f(storage.V0, args...))
{
  return VariantCastAndCallImplR<decltype(f(storage.V0, args...))>(
    std::false_type{}, index, std::forward<Functor>(f), storage, std::forward<Args>(args)...);
}

}
}
}
} // vtkm::VTK_M_NAMESPACE::internal::detail
