//============================================================================
//  The contents of this file are covered by the Viskores license. See
//  LICENSE.txt for details.
//
//  By contributing to this file, all contributors agree to the Developer
//  Certificate of Origin Version 1.1 (DCO 1.1) as stated in DCO.txt.
//============================================================================

//============================================================================
//  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.
//============================================================================
$# This file uses the pyexpander macro processing utility to build the
$# FunctionInterface facilities that use a variable number of arguments.
$# Information, documentation, and downloads for pyexpander can be found at:
$#
$#     http://pyexpander.sourceforge.net/
$#
$# To build the source code, execute the following (after installing
$# pyexpander, of course):
$#
$#     expander.py VariantDetail.h.in > VariantDetail.h
$#
$# Ignore the following comment. It is meant for the generated file.
// **** DO NOT EDIT THIS FILE!!! ****
// This file is automatically generated by Tuple.h.in

#ifndef viskores_Tuple_h
#define viskores_Tuple_h

#include <viskores/Types.h>

#include <viskores/internal/DecayHelpers.h>
#include <viskores/internal/IndexTag.h>

$py(max_expanded=20)\

$# Python commands used in template expansion.
$py(
def type_list(num_params, name='T'):
  if num_params < 1:
    return ''
  result = '%s0' % name
  for param in range(1, num_params):
    result += ', %s%d' % (name, param)
  return result

def typename_list(num_params, name='T'):
  if num_params < 1:
    return ''
  result = 'typename %s0' % name
  for param in range(1, num_params):
    result += ', typename %s%d' % (name, param)
  return result

def perfect_param_list(num_params, classname='A', argname='a'):
  if num_params < 1:
    return ''
  result = '%s0&& %s0' % (classname, argname)
  for param in range(1, num_params):
    result += ', %s%d&& %s%d' % (classname, param, argname, param)
  return result
)\
$#
$extend(type_list, typename_list, perfect_param_list)\

namespace viskores
{

///@{
/// \brief Viskores replacement for std::tuple
///
/// This function serves the same function as `std::tuple` and behaves similarly. However, this
/// version of `Tuple` works on devices that Viskores supports. There are also some implementation
/// details that makes compiling faster for Viskores use. We also provide some methods like `Apply`
/// and `ForEach` that are helpful for several Viskores operations.
///
template <typename... Ts>
class Tuple;

/// \brief Get the size of a tuple.
///
/// Given a `viskores::Tuple` type, becomes a `std::integral_constant` of the type.
///
template <typename TupleType>
using TupleSize = std::integral_constant<viskores::IdComponent, TupleType::Size>;

/// \brief Compatible with `std::tuple_size` for `viskores::Tuple`.
///
template <typename TupleType>
using tuple_size = std::integral_constant<std::size_t, static_cast<std::size_t>(TupleType::Size)>;

namespace detail
{

template <viskores::IdComponent Index, typename TupleType>
struct TupleElementImpl
{
  using type = decltype(TupleType::ElementTypeI(viskores::internal::IndexTag<Index>{}));
};

} // namespace detail

/// \brief Becomes the type of the given index for the given `viskores::Tuple`.
///
template <viskores::IdComponent Index, typename TupleType>
using TupleElement = typename detail::TupleElementImpl<Index, TupleType>::type;

/// \brief Compatible with `std::tuple_element` for `viskores::Tuple`.
///
template <std::size_t Index, typename TupleType>
struct tuple_element
{
  using type = TupleElement<static_cast<viskores::IdComponent>(Index), TupleType>;
};

/// \brief Compatible with `std::tuple_element_t` for `viskores::Tuple`.
///
template <std::size_t Index, typename TupleType>
using tuple_element_t = typename tuple_element<Index, TupleType>::type;

/// @brief Retrieve the object from a `viskores::Tuple` at the given index.
VISKORES_SUPPRESS_EXEC_WARNINGS
template <viskores::IdComponent Index, typename... Ts>
VISKORES_EXEC_CONT auto Get(const viskores::Tuple<Ts...>& tuple)
#ifndef VISKORES_DOXYGEN_ONLY
  // Breathe (for Sphinx) has problems parsing this declarator id.
  -> decltype(tuple.template Get<Index>())
#endif
{
  return tuple.template Get<Index>();
}

/// @brief Retrieve the object from a `viskores::Tuple` at the given index.
VISKORES_SUPPRESS_EXEC_WARNINGS
template <viskores::IdComponent Index, typename... Ts>
VISKORES_EXEC_CONT auto Get(viskores::Tuple<Ts...>& tuple)
#ifndef VISKORES_DOXYGEN_ONLY
  // Breathe (for Sphinx) has problems parsing this declarator id.
  -> decltype(tuple.template Get<Index>())
#endif
{
  return tuple.template Get<Index>();
}

/// @brief Compatible with `std::get` for `viskores::Tuple`.
VISKORES_SUPPRESS_EXEC_WARNINGS
template <std::size_t Index, typename... Ts>
VISKORES_EXEC_CONT auto get(const viskores::Tuple<Ts...>& tuple)
  -> decltype(viskores::Get<static_cast<viskores::IdComponent>(Index)>(tuple))
{
  return viskores::Get<static_cast<viskores::IdComponent>(Index)>(tuple);
}

/// @brief Compatible with `std::get` for `viskores::Tuple`.
VISKORES_SUPPRESS_EXEC_WARNINGS
template <std::size_t Index, typename... Ts>
VISKORES_EXEC_CONT auto get(viskores::Tuple<Ts...>& tuple)
  -> decltype(viskores::Get<static_cast<viskores::IdComponent>(Index)>(tuple))
{
  return viskores::Get<static_cast<viskores::IdComponent>(Index)>(tuple);
}

/// \brief Creates a new `viskores::Tuple` with the given types.
///
VISKORES_SUPPRESS_EXEC_WARNINGS
template <typename... Ts>
VISKORES_EXEC_CONT auto MakeTuple(Ts&&... args) -> viskores::Tuple<typename std::decay<Ts>::type...>
{
  return viskores::Tuple<typename std::decay<Ts>::type...>(std::forward<Ts>(args)...);
}

/// \brief Compatible with `std::make_tuple` for `viskores::Tuple`.
///
VISKORES_SUPPRESS_EXEC_WARNINGS
template <typename... Ts>
VISKORES_EXEC_CONT auto make_tuple(Ts&&... args) -> decltype(viskores::MakeTuple(std::forward<Ts>(args)...))
{
  return viskores::MakeTuple(std::forward<Ts>(args)...);
}

/// @cond NONE
namespace detail
{
struct TupleTransformFunctor
{
  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function, typename... Ts>
  VISKORES_EXEC_CONT auto operator()(Function&& f, Ts&&... args)
    -> decltype(viskores::MakeTuple(f(std::forward<Ts>(args))...))
  {
    return viskores::MakeTuple(f(std::forward<Ts>(args))...);
  }
};

struct TupleForEachFunctor
{
  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function, typename... Ts>
  VISKORES_EXEC_CONT void operator()(Function&& f, Ts&&... args)
  {
    (void)std::initializer_list<bool>{ (f(std::forward<Ts>(args)), false)... };
  }
};

} // namespace detail
/// @endcond

/// @brief Call a function with the values of a `viskores::Tuple` as arguments.
///
/// If a `viskores::Tuple<A, B, C>` is given with values `a`, `b`, and `c`, then
/// `f` will be called as `f(a, b, c)`.
///
/// Additional arguments can optionally be given to `viskores::Apply()`. These
/// arguments will be added to the _beginning_ of the arguments to the function.
///
/// The returned value of the function (if any) will be returned from `viskores::Apply()`.
template <typename... Ts, typename Function, typename... Args>
VISKORES_EXEC_CONT auto Apply(const viskores::Tuple<Ts...>& tuple, Function&& f, Args&&... args)
  -> decltype(tuple.Apply(std::forward<Function>(f), std::forward<Args>(args)...))
{
  return tuple.Apply(std::forward<Function>(f), std::forward<Args>(args)...);
}

/// @copydoc Apply
template <typename... Ts, typename Function, typename... Args>
VISKORES_EXEC_CONT auto Apply(viskores::Tuple<Ts...>& tuple, Function&& f, Args&&... args)
  -> decltype(tuple.Apply(std::forward<Function>(f), std::forward<Args>(args)...))
{
  return tuple.Apply(std::forward<Function>(f), std::forward<Args>(args)...);
}

/// @brief Call a function with each value of the given tuple.
///
/// The function calls will be done in the order of the values in the `viskores::Tuple`.
template <typename... Ts, typename Function>
VISKORES_EXEC_CONT void ForEach(const viskores::Tuple<Ts...>& tuple, Function&& f)
{
  return viskores::Apply(tuple, detail::TupleForEachFunctor{}, std::forward<Function>(f));
}

/// @copydoc ForEach
template <typename... Ts, typename Function>
VISKORES_EXEC_CONT void ForEach(viskores::Tuple<Ts...>& tuple, Function&& f)
{
  return viskores::Apply(tuple, detail::TupleForEachFunctor{}, std::forward<Function>(f));
}

///@{
/// @brief Construct a new `viskores::Tuple` by applying a function to each value.
///
/// The `viskores::Transform` function builds a new `viskores::Tuple` by calling a function
/// or functor on each of the items in the given `tuple`. The return value is placed
/// in the corresponding part of the resulting Tuple, and the type is automatically
/// created from the return type of the function.
template <typename TupleType, typename Function>
VISKORES_EXEC_CONT auto Transform(const TupleType&& tuple, Function&& f)
  -> decltype(Apply(tuple, detail::TupleTransformFunctor(), std::forward<Function>(f)))
{
  return Apply(tuple, detail::TupleTransformFunctor(), std::forward<Function>(f));
}

template <typename TupleType, typename Function>
VISKORES_EXEC_CONT auto Transform(TupleType&& tuple, Function&& f)
  -> decltype(Apply(tuple, detail::TupleTransformFunctor(), std::forward<Function>(f)))
{
  return Apply(tuple, detail::TupleTransformFunctor(), std::forward<Function>(f));
}
///@}

template <>
class Tuple<>
{
public:
  static constexpr viskores::IdComponent Size = 0;

  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function, typename... Args>
  VISKORES_EXEC_CONT auto Apply(Function&& f, Args&&... args)
    -> decltype(f(std::forward<Args>(args)...))
  {
    return f(std::forward<Args>(args)...);
  }
  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function, typename... Args>
  VISKORES_EXEC_CONT auto Apply(Function&& f, Args&&... args) const
    -> decltype(f(std::forward<Args>(args)...))
  {
    return f(std::forward<Args>(args)...);
  }

  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function>
  VISKORES_EXEC_CONT void ForEach(Function&&) const
  {
  }

  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function>
  VISKORES_EXEC_CONT viskores::Tuple<> Transform(Function&&) const
  {
    return viskores::Tuple<>{};
  }
};

// clang-format off

$for(num_params in range(1, max_expanded + 1))\
template<$typename_list(num_params)>
class Tuple<$type_list(num_params)>
{
$for(index in range(0, num_params))\
  T$(index) Value$(index);
  static T$(index) ElementTypeI(viskores::internal::IndexTag<$(index)>);
  VISKORES_EXEC_CONT viskores::internal::remove_cvref<T$(index)>& GetImpl(viskores::internal::IndexTag<$(index)>)
  {
    return this->Value$(index);
  }
  VISKORES_EXEC_CONT const viskores::internal::remove_cvref<T$(index)>& GetImpl(viskores::internal::IndexTag<$(index)>) const
  {
    return this->Value$(index);
  }

$endfor\

  // Invalid indices
  template <viskores::IdComponent Index>
  static viskores::internal::NullType ElementTypeI(viskores::internal::IndexTag<Index>);

  template <viskores::IdComponent, typename>
  friend struct detail::TupleElementImpl;

public:
  static constexpr viskores::IdComponent Size = $(num_params);
  template <viskores::IdComponent Index>
  using ElementType = viskores::TupleElement<Index, Tuple<$type_list(num_params)>>;

  Tuple() = default;
  Tuple(Tuple&&) = default;
  Tuple(const Tuple&) = default;
  ~Tuple() = default;
  Tuple& operator=(Tuple&&) = default;
  Tuple& operator=(const Tuple&) = default;

  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <$typename_list(num_params, 'A')>
  VISKORES_EXEC_CONT Tuple($perfect_param_list(num_params, 'A', 'a'))
    : Value0(std::forward<A0>(a0))
$for(index in range(1, num_params))\
    , Value$(index)(std::forward<A$(index)>(a$(index)))
$endfor\
  {
  }

  template <viskores::IdComponent Index>
  VISKORES_EXEC_CONT auto Get() -> decltype(this->GetImpl(viskores::internal::IndexTag<Index>{}))
  {
    return this->GetImpl(viskores::internal::IndexTag<Index>{});
  }

  template <viskores::IdComponent Index>
  VISKORES_EXEC_CONT auto Get() const -> decltype(this->GetImpl(viskores::internal::IndexTag<Index>{}))
  {
    return this->GetImpl(viskores::internal::IndexTag<Index>{});
  }

  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function, typename... Args>
  VISKORES_EXEC_CONT
  auto Apply(Function&& f, Args&&... args)
    -> decltype(f(std::forward<Args>(args)..., $type_list(num_params, "Value")))
  {
    return f(std::forward<Args>(args)..., $type_list(num_params, "Value"));
  }
  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function, typename... Args>
  VISKORES_EXEC_CONT
  auto Apply(Function&& f, Args&&... args) const
    -> decltype(f(std::forward<Args>(args)..., $type_list(num_params, "Value")))
  {
    return f(std::forward<Args>(args)..., $type_list(num_params, "Value"));
  }

  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function>
  VISKORES_EXEC_CONT void ForEach(Function&& f)
  {
    viskores::ForEach(*this, std::forward<Function>(f));
  }
  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function>
  VISKORES_EXEC_CONT void ForEach(Function&& f) const
  {
    viskores::ForEach(*this, std::forward<Function>(f));
  }

  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function>
  VISKORES_EXEC_CONT auto Transform(Function&& f)
    -> decltype(viskores::Transform(*this, std::forward<Function>(f)))
  {
    return viskores::Transform(*this, std::forward<Function>(f));
  }
  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function>
  VISKORES_EXEC_CONT auto Transform(Function&& f) const
    -> decltype(viskores::Transform(*this, std::forward<Function>(f)))
  {
    return viskores::Transform(*this, std::forward<Function>(f));
  }
};

$endfor\

// Fallback case for tuples with > $(max_expanded) items.
template<$typename_list(max_expanded + 1), typename... Ts>
class Tuple<$type_list(max_expanded + 1), Ts...>
{
$for(index in range(0, max_expanded))\
  T$(index) Value$(index);
  static T$(index) ElementTypeI(viskores::internal::IndexTag<$(index)>);
  VISKORES_EXEC_CONT viskores::internal::remove_cvref<T$(index)>& GetImpl(viskores::internal::IndexTag<$(index)>)
  {
    return this->Value$(index);
  }
  VISKORES_EXEC_CONT const viskores::internal::remove_cvref<T$(index)>& GetImpl(viskores::internal::IndexTag<$(index)>) const
  {
    return this->Value$(index);
  }

$endfor\

  // Implement the "extra" objects in a sub-Tuple
  using RemainingValuesType = viskores::Tuple<T$(max_expanded), Ts...>;
  RemainingValuesType RemainingValues;

  template <viskores::IdComponent Index>
  static viskores::TupleElement<Index - $(max_expanded), RemainingValuesType>
  ElementTypeI(viskores::internal::IndexTag<Index>);

  template <typename viskores::IdComponent Index>
  VISKORES_EXEC_CONT const viskores::internal::remove_cvref<viskores::TupleElement<Index - $(max_expanded), RemainingValuesType>>&
  GetImpl(viskores::internal::IndexTag<Index>) { return viskores::Get<Index - $(max_expanded)>(this->RemainingValues); }
  template <typename viskores::IdComponent Index>
  VISKORES_EXEC_CONT const viskores::internal::remove_cvref<viskores::TupleElement<Index - $(max_expanded), RemainingValuesType>>&
  GetImpl(viskores::internal::IndexTag<Index>) const { return viskores::Get<Index - $(max_expanded)>(this->RemainingValues); }

  template <viskores::IdComponent, typename>
  friend struct detail::TupleElementImpl;

public:
  static constexpr viskores::IdComponent Size =
    $(max_expanded + 1) + static_cast<viskores::IdComponent>(sizeof...(Ts));
  template <viskores::IdComponent Index>
  using ElementType = viskores::TupleElement<Index, Tuple<$type_list(max_expanded + 1), Ts...>>;

  Tuple() = default;
  Tuple(Tuple&&) = default;
  Tuple(const Tuple&) = default;
  ~Tuple() = default;
  Tuple& operator=(Tuple&&) = default;
  Tuple& operator=(const Tuple&) = default;

  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <$typename_list(max_expanded, 'A'), typename... As>
  VISKORES_EXEC_CONT Tuple($perfect_param_list(max_expanded, 'A', 'a'), As&&... remainingArgs)
    : Value0(std::forward<A0>(a0))
$for(index in range(1, max_expanded))\
    , Value$(index)(std::forward<A$(index)>(a$(index)))
$endfor\
    , RemainingValues(std::forward<As>(remainingArgs)...)
  {
  }

  template <viskores::IdComponent Index>
  VISKORES_EXEC_CONT auto Get() -> decltype(this->GetImpl(viskores::internal::IndexTag<Index>{}))
  {
    return this->GetImpl(viskores::internal::IndexTag<Index>{});
  }

  template <viskores::IdComponent Index>
  VISKORES_EXEC_CONT auto Get() const -> decltype(this->GetImpl(viskores::internal::IndexTag<Index>{}))
  {
    return this->GetImpl(viskores::internal::IndexTag<Index>{});
  }

  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function, typename... Args>
  VISKORES_EXEC_CONT
  auto Apply(Function&& f, Args&&... args)
    -> decltype(this->RemainingValues.Apply(std::forward<Function>(f),
                                            std::forward<Args>(args)...,
$for(index in range(0, max_expanded - 1))\
                                            this->Value$(index),
$endfor\
                                            this->Value$(max_expanded - 1)))
  {
    return this->RemainingValues.Apply(std::forward<Function>(f),
                                       std::forward<Args>(args)...,
$for(index in range(0, max_expanded - 1))\
                                       this->Value$(index),
$endfor\
                                       this->Value$(max_expanded - 1));
  }
  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function, typename... Args>
  VISKORES_EXEC_CONT
  auto Apply(Function&& f, Args&&... args) const
    -> decltype(this->RemainingValues.Apply(std::forward<Function>(f),
                                            std::forward<Args>(args)...,
$for(index in range(0, max_expanded - 1))\
                                            this->Value$(index),
$endfor\
                                            this->Value$(max_expanded - 1)))
  {
    return this->RemainingValues.Apply(std::forward<Function>(f),
                                       std::forward<Args>(args)...,
$for(index in range(0, max_expanded - 1))\
                                       this->Value$(index),
$endfor\
                                       this->Value$(max_expanded - 1));
  }

  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function>
  VISKORES_EXEC_CONT void ForEach(Function&& f)
  {
    viskores::ForEach(*this, std::forward<Function>(f));
  }
  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function>
  VISKORES_EXEC_CONT void ForEach(Function&& f) const
  {
    viskores::ForEach(*this, std::forward<Function>(f));
  }

  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function>
  VISKORES_EXEC_CONT auto Transform(Function&& f)
    -> decltype(viskores::Transform(*this, std::forward<Function>(f)))
  {
    return viskores::Transform(*this, std::forward<Function>(f));
  }
  VISKORES_SUPPRESS_EXEC_WARNINGS
  template <typename Function>
  VISKORES_EXEC_CONT auto Transform(Function&& f) const
    -> decltype(viskores::Transform(*this, std::forward<Function>(f)))
  {
    return viskores::Transform(*this, std::forward<Function>(f));
  }
};

///@}

// clang-format on

} // namespace viskores

#endif //viskores_Tuple_h
