// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   vtkArrayDispatch
 * @brief   vtkDataArray code generator/dispatcher.
 *
 * vtkArrayDispatch implements a mechanism for generating optimized code for
 * multiple subclasses of vtkDataArray at once. Using a TypeList based
 * approach (see vtkTypeList), a templated worker implementation is generated
 * for a restricted or unrestricted set of vtkDataArray subclasses.
 *
 * A more detailed description of this class and related tools can be found
 * [here](https://docs.vtk.org/en/latest/design_documents/array_dispatch.html).
 *
 * The primary goals of this class are to simplify multi-array dispatch
 * implementations, and provide tools to lower compilation time and binary
 * size (i.e. avoiding 'template explosions').
 *
 * vtkArrayDispatch is also intended to replace code that currently relies on
 * the encapsulation-breaking vtkDataArray::GetVoidPointer method. Not all
 * subclasses of vtkDataArray use the memory layout assumed by GetVoidPointer;
 * calling this method on, e.g. a vtkSOADataArrayTemplate will trigger a deep
 * copy of the array data into an AOS buffer. This is very inefficient and
 * should be avoided.
 *
 * The vtkDataArrayRange.h utilities are worth mentioning here, as they allow
 * vtkArrayDispatch workers to operate on selected concrete subclasses for
 * 'fast paths', yet fallback to using the slower vtkDataArray API for uncommon
 * array types. This helps mitigate the "template explosion" issues that can
 * result from instantiating a large worker functions for many array types.
 *
 * These dispatchers extend the basic functionality of vtkTemplateMacro with
 * the following features:
 * - Multiarray dispatch: A single call can dispatch up to 3 arrays at once.
 * - Array restriction: The set of valid arrays for a particular dispatch
 *   can be restricted. For example, if only vtkDoubleArray or vtkFloatArray
 *   will be passed into the call, the dispatcher will only generate code paths
 *   that use those arrays.
 * - ValueType restriction: If both SoA and AoS arrays need to be supported,
 *   but only certain ValueTypes are expected, the dispatcher can restrict
 *   itself to only use arrays that match this criteria.
 * - Application-wide array restrictions: If a VTK application uses only a few
 *   arraytype / valuetype combinations, certain dispatchers will eliminate
 *   paths using unsupported arrays at compile time.
 *
 * The basic Dispatch implementation will generate code paths for all arrays
 * in the application-wide array list, and operates on a single array.
 *
 * Dispatchers that start with Dispatch2 operate on 2 arrays simultaneously,
 * while those that begin with Dispatch3 operate on 3 arrays.
 *
 * To reduce compile time and binary size, the following dispatchers can be
 * used to restrict the set of arrays that will be used. There are versions of
 * these that operate on 1, 2, or 3 arrays:
 *
 * - DispatchByArray:
 *   Accepts an explicit TypeList of arrays to consider.
 *   These dispatchers do NOT respect the application-wide array restrictions.
 *   Example usecase: A filter that creates either vtkFloatArray or
 *   vtkDoubleArray depending on configuration can use this to restrict itself
 *   to only these specific types.
 *   Note that these should not be used for operating on filter inputs, instead
 *   use DispatchByValueType, which also considers variations in vtkDataArray
 *   subclasses and respects the application-wide array restrictions.
 *
 * - DispatchByValueType:
 *   Accepts an explicit TypeList of ValueTypes to consider.
 *   These dispatchers respect the application-wide array restrictions.
 *   Example usecase: An image filter that operates on an input array that must
 *   have either a float or unsigned char ValueType.
 *
 * - DispatchNByArrayWithSameValueType:
 *   Multiarray dispatcher that accepts an explicit TypeList of arrays for
 *   consideration. Generated code paths are further restricted to enforce that
 *   all dispatched arrays will have the same ValueType.
 *   Example usecase: A filter that creates and operates on multiple arrays at
 *   the same time, where the arrays are guaranteed to have the same ValueType.
 *   Note that these should not be used for operating on filter inputs, instead
 *   use DispatchNBySameValueType, which also considers variations in
 *   vtkDataArray subclasses and respects the application-wide array
 *   restrictions.
 *
 * - DispatchNBySameValueType:
 *   Multiarray dispatcher that accepts an explicit TypeList of ValueTypes to
 *   consider. Generated code paths are further restricted to enforce that
 *   all dispatched arrays will have the same ValueType.
 *   Example usecase: A filter that creates a modified version of an input
 *   array using NewInstance(). Both arrays may be passed into the dispatcher
 *   using a worker function that produces the output from the input.
 *
 * Execution:
 * There are three components to a dispatch: The dispatcher, the worker, and
 * the array(s). They are combined like so:
 *
 * @code
 * bool result = Dispatcher<...>::Execute(array, worker);
 * @endcode
 *
 * For convenience, the dispatcher may be aliased to a shorter name, e.g.:
 *
 * @code
 * using MyDispatcher = vtkArrayDispatch::SomeDispatcher<...>;
 * MyWorker worker;
 * bool result = MyDispatcher::Execute(array, worker);
 * @endcode
 *
 * Return value:
 * The Execute method of the dispatcher will return true if a code path matching
 * the array arguments is found, or false if the arrays are not supported. If
 * false is returned, the arrays will not be modified, and the worker will not
 * be executed.
 *
 * Workers:
 * The dispatch requires a Worker functor that performs the work.
 * For single array, the functor must be callable where the first parameter is
 * an array object. For 2-array dispatch, the first two arguments must be (array1, array2).
 * For 3-array dispatch, the first three arguments must be (array1, array2, array3).
 * Workers are passed by reference, so stateful functors are permitted if
 * additional input/output data is needed and not being passed as additional
 * parameters to the Execute method.
 *
 * A simple worker implementation for triple dispatch:
 * @code
 * struct MyWorker
 * {
 *   template <typename Array1T, typename Array2T, typename Array3T>
 *   void operator()(Array1T *array1, Array2T *array2, Array3T *array3)
 *   {
 *     // Do work using vtkGenericDataArray API...
 *   }
 * };
 * @endcode
 *
 * Note that optimized implementations (e.g. for AoS arrays vs SoA arrays) can
 * be supported by providing overloads of operator() that have more restrictive
 * template parameters.
 *
 * A worker's operator() implementation can accept additional parameters that
 * follow the arrays. These parameters are passed to the dispatcher during
 * execution. For instance, this worker scales an array by a runtime-value,
 * writing it into a second array:
 *
 * @code
 * struct ScaleArray
 * {
 *   template <typename ArraySrc, typename ArrayDst>
 *   void operator()(ArraySrc *srcArray, ArrayDst *dstArray,
 *                   double scaleFactor) const
 *   {
 *     using SrcType = vtk::GetAPIType<ArraySrc>;
 *     using DstType = vtk::GetAPIType<ArrayDst>;
 *
 *     const auto srcRange = vtk::DataArrayValueRange(srcArray);
 *     auto dstRange = vtk::DataArrayValueRange(dstArray);
 *
 *     assert(srcRange.size() == dstRange.size());
 *
 *     auto dstIter = dstRange.begin();
 *     for (SrcType srcVal : srcRange)
 *     {
 *       *dstIter++ = static_cast<DstType>(srcVal * scaleFactor);
 *     }
 *   }
 * };
 *
 * vtkDataArray *src = ...;
 * vtkDataArray *dst = ...;
 * // Scale src by 3 (scaleFactor) and store in dst:
 * if (!vtkArrayDispatch::Dispatch2::Execute(src, dst, ScaleArray, 3))
 * {
 *   scaleArray(src, dst, 3);
 * }
 * @endcode
 *
 * Examples:
 * See TestArrayDispatchers.cxx for examples of each dispatch type and
 * ExampleDataArrayRangeDispatch.cxx for more real-world examples.
 *
 * @sa
 * vtkDataArrayAccessor
 */

#ifndef vtkArrayDispatch_h
#define vtkArrayDispatch_h

#include "vtkArrayDispatchArrayList.h"
#include "vtkType.h"
#include "vtkTypeList.h"

namespace vtkArrayDispatch
{
VTK_ABI_NAMESPACE_BEGIN

/**
 * A TypeList containing all real ValueTypes.
 */
typedef vtkTypeList::Create<double, float> Reals;

/**
 * A Typelist containing all integral ValueTypes.
 */
typedef vtkTypeList::Unique<
  vtkTypeList::Create<char, int, long, long long, short, signed char, unsigned char, unsigned int,
    unsigned long, unsigned long long, unsigned short, vtkIdType>>::Result Integrals;

/**
 * A Typelist containing all standard VTK array ValueTypes.
 */
typedef vtkTypeList::Append<Reals, Integrals>::Result AllTypes;

//------------------------------------------------------------------------------
/**
 * Dispatch a single array against all array types in the application-wide
 * vtkArrayDispatch::Arrays list.
 * The entry point is:
 * bool Dispatch::Execute(vtkDataArray *array, Worker &worker).
 */
struct Dispatch;

//------------------------------------------------------------------------------
/**
 * Dispatch a single array against all array types mentioned in the ArrayList
 * template parameter.
 * The entry point is:
 * bool DispatchByArray<...>::Execute(vtkDataArray *array, Worker &worker).
 */
template <typename ArrayList>
struct DispatchByArray;

//------------------------------------------------------------------------------
/**
 * Dispatch a single array against all array types in the application-wide
 * vtkArrayDispatch::Arrays list with the added restriction that the array
 * must have a type that appears the ValueTypeList TypeList.
 * The entry point is:
 * bool DispatchByValueType<...>::Execute(vtkDataArray *array, Worker &worker).
 */
template <typename ArrayList, typename ValueTypeList>
struct DispatchByValueTypeUsingArrays;
template <typename ValueTypeList>
struct DispatchByValueType;

//------------------------------------------------------------------------------
/**
 * Dispatch two arrays using all array types in the application-wide
 * vtkArrayDispatch::Arrays list.
 * The entry point is:
 * bool Dispatch2::Execute(vtkDataArray *array, vtkDataArray *array2,
 * Worker &worker).
 */
struct Dispatch2;

//------------------------------------------------------------------------------
/**
 * Dispatch two arrays, restricting the valid code paths to use only arrays that
 * have the same ValueType.
 * All application-wide arrays in vtkArrayDispatch::Arrays are used.
 * The entry point is:
 * bool Dispatch2SameValueType::Execute(
 * vtkDataArray *a1, vtkDataArray *a2, Worker &worker).
 */
template <typename ArrayList>
struct Dispatch2SameValueTypeUsingArrays;
struct Dispatch2SameValueType;

//------------------------------------------------------------------------------
/**
 * Dispatch two arrays with the restriction that the type of the first array is
 * in the ArrayList1 TypeList, and the second is in ArrayList2.
 * If all application-wide arrays are desired, use vtkArrayDispatch::Arrays for
 * the first two template parameters.
 * The entry point is:
 * bool Dispatch2ByArray<...>::Execute(vtkDataArray *a1, vtkDataArray *a2,
 * Worker &worker).
 */
template <typename ArrayList1, typename ArrayList2>
struct Dispatch2ByArray;

//------------------------------------------------------------------------------
/**
 * Dispatch two arrays, restricting the valid code paths to use
 * ValueType-filtered versions of the application-wide vtkArrayDispatch::Arrays
 * TypeList. The first array's ValueType must be in the ValueTypeList1 TypeList,
 * and the second's must be in ValueTypeList2.
 * If all types are to be considered, use vtkArrayDispatch::AllTypes for the
 * first two template parameters.
 * The entry point is:
 * bool Dispatch2ByValueType<...>::Execute(vtkDataArray *a1, vtkDataArray *a2,
 * Worker &worker).
 */
template <typename ArrayList, typename ValueTypeList1, typename ValueTypeList2>
struct Dispatch2ByValueTypeUsingArrays;
template <typename ValueTypeList1, typename ValueTypeList2>
struct Dispatch2ByValueType;

//------------------------------------------------------------------------------
/**
 * Dispatch two arrays, restricting the valid code paths to use only array types
 * specified in the ArrayList TypeList, additionally enforcing that all arrays
 * must have the same ValueType.
 * If all application-wide arrays are desired, use vtkArrayDispatch::Arrays for
 * the first two template parameters.
 * The entry point is:
 * bool Dispatch2ByArrayWithSameValueType<...>::Execute(
 * vtkDataArray *a1, vtkDataArray *a2, Worker &worker).
 */
template <typename ArrayList1, typename ArrayList2>
struct Dispatch2ByArrayWithSameValueType;

//------------------------------------------------------------------------------
/**
 * Dispatch two arrays, restricting the valid code paths to use only array types
 * found in application-wide vtkArrayDispatch::Arrays TypeList that have a
 * ValueType contained in the ValueTypeList TypeList. This dispatcher also
 * enforces that all arrays have the same ValueType.
 * If all types are to be considered, use vtkArrayDispatch::AllTypes for the
 * first two template parameters.
 * The entry point is:
 * bool Dispatch2BySameValueType<...>::Execute(
 * vtkDataArray *a1, vtkDataArray *a2, Worker &worker).
 */
template <typename ArrayList, typename ValueTypeList>
struct Dispatch2BySameValueTypeUsingArrays;
template <typename ValueTypeList>
struct Dispatch2BySameValueType;

//------------------------------------------------------------------------------
/**
 * Dispatch three arrays using all array types in the application-wide
 * vtkArrayDispatch::Arrays list.
 * The entry point is:
 * bool Dispatch3::Execute(vtkDataArray *array1, vtkDataArray *array2,
 * vtkDataArray *array3, Worker &worker).
 */
struct Dispatch3;

//------------------------------------------------------------------------------
/**
 * Dispatch three arrays, restricting the valid code paths to use only arrays
 * that have the same ValueType.
 * All application-wide arrays in vtkArrayDispatch::Arrays are used.
 * The entry point is:
 * bool Dispatch3SameValueType::Execute(
 * vtkDataArray *a1, vtkDataArray *a2, vtkDataArray *a3, Worker &worker).
 */
template <typename ArrayList>
struct Dispatch3SameValueTypeUsingArrays;
struct Dispatch3SameValueType;

//------------------------------------------------------------------------------
/**
 * Dispatch three arrays with the restriction that the type of the first array
 * is in the ArrayList1 TypeList, the second is in ArrayList2, and the third
 * is in ArrayList3.
 * If all application-wide arrays are desired, use vtkArrayDispatch::Arrays for
 * the first three template parameters.
 * The entry point is:
 * bool Dispatch3ByArray::Execute<...>(vtkDataArray *a1, vtkDataArray *a2,
 * vtkDataArray *a3, Worker &worker).
 */
template <typename ArrayList1, typename ArrayList2, typename ArrayList3>
struct Dispatch3ByArray;

//------------------------------------------------------------------------------
/**
 * Dispatch three arrays, restricting the valid code paths to use
 * ValueType-filtered versions of the application-wide vtkArrayDispatch::Arrays
 * TypeList. The first array's ValueType must be in the ValueTypeList1 TypeList,
 * the second's must be in ValueTypeList2, and the third's must be in
 * ValueTypeList3.
 * If all types are to be considered, use vtkArrayDispatch::AllTypes for the
 * first three template parameters.
 * The entry point is:
 * bool Dispatch3ByValueType<...>::Execute(vtkDataArray *a1, vtkDataArray *a2,
 * vtkDataArray *a3, Worker &worker).
 */

template <typename ArrayList, typename ValueTypeList1, typename ValueTypeList2,
  typename ValueTypeList3>
struct Dispatch3ByValueTypeUsingArrays;
template <typename ValueTypeList1, typename ValueTypeList2, typename ValueTypeList3>
struct Dispatch3ByValueType;

//------------------------------------------------------------------------------
/**
 * Dispatch three arrays, restricting the valid code paths to use only array
 * types specified in the ArrayList TypeList, additionally enforcing that all
 * arrays must have the same ValueType.
 * If all application-wide arrays are desired, use vtkArrayDispatch::Arrays for
 * the first three template parameters.
 * The entry point is:
 * bool Dispatch3ByArrayWithSameValueType<...>::Execute(
 * vtkDataArray *a1, vtkDataArray *a2, vtkDataArray *a3, Worker &worker).
 */
template <typename ArrayList1, typename ArrayList2, typename ArrayList3>
struct Dispatch3ByArrayWithSameValueType;

//------------------------------------------------------------------------------
/**
 * Dispatch three arrays, restricting the valid code paths to use only array
 * types found in application-wide vtkArrayDispatch::Arrays TypeList that have a
 * ValueType contained in the ValueTypeList TypeList. This dispatcher also
 * enforces that all arrays have the same ValueType.
 * If all types are to be considered, use vtkArrayDispatch::AllTypes for the
 * first three template parameters.
 * The entry point is:
 * bool Dispatch3BySameValueType<...>::Execute(
 * vtkDataArray *a1, vtkDataArray *a2, vtkDataArray *a3, Worker &worker).
 */
template <typename ArrayList, typename ValueTypeList>
struct Dispatch3BySameValueTypeUsingArrays;
template <typename ValueTypeList>
struct Dispatch3BySameValueType;

//------------------------------------------------------------------------------
/**
 * Filter the ArrayList to contain only arrays with ArrayType::ValueType that
 * exist in ValueList. The result TypeList is stored in Result.
 */
template <typename ArrayList, typename ValueList>
struct FilterArraysByValueType;

VTK_ABI_NAMESPACE_END
} // end namespace vtkArrayDispatch

#include "vtkArrayDispatch.txx"

#endif // vtkArrayDispatch_h
// VTK-HeaderTest-Exclude: vtkArrayDispatch.h
