// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause

#include "vtkDataArray.h"
#include "vtkAOSDataArrayTemplate.h" // For fast paths
#include "vtkArrayDispatch.h"
#include "vtkBitArray.h"
#include "vtkCharArray.h"
#include "vtkDataArrayPrivate.txx"
#include "vtkDataArrayRange.h"
#include "vtkDoubleArray.h"
#include "vtkFloatArray.h"
#include "vtkGenericDataArray.h"
#include "vtkIdList.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkInformationDoubleVectorKey.h"
#include "vtkInformationInformationVectorKey.h"
#include "vtkInformationStringKey.h"
#include "vtkInformationVector.h"
#include "vtkIntArray.h"
#include "vtkLongArray.h"
#include "vtkLookupTable.h"
#include "vtkMath.h"
#include "vtkSOADataArrayTemplate.h" // For fast paths
#ifdef VTK_USE_SCALED_SOA_ARRAYS
#include "vtkScaledSOADataArrayTemplate.h" // For fast paths
#endif
#include "vtkSMPTools.h"
#include "vtkShortArray.h"
#include "vtkSignedCharArray.h"
#include "vtkTypeTraits.h"
#include "vtkUnsignedCharArray.h"
#include "vtkUnsignedIntArray.h"
#include "vtkUnsignedLongArray.h"
#include "vtkUnsignedShortArray.h"

#include <algorithm> // for min(), max()
#include <vector>

namespace
{
template <typename ValueType>
struct threadedCopyFunctor
{
  ValueType* src;
  ValueType* dst;
  int nComp;
  void operator()(vtkIdType begin, vtkIdType end) const
  {
    // std::copy(src+begin, src+end, dst+begin); //slower
    memcpy(dst + begin * nComp, src + begin * nComp, (end - begin) * nComp * sizeof(ValueType));
  }
};

//--------Copy tuples from src to dest------------------------------------------
struct DeepCopyWorker
{
  // AoS --> AoS same-type specialization:
  template <typename ValueType>
  void operator()(
    vtkAOSDataArrayTemplate<ValueType>* src, vtkAOSDataArrayTemplate<ValueType>* dst) const
  {
    vtkIdType len = src->GetNumberOfTuples();
    if (len < 1024 * 1024)
    {
      // With less than a megabyte or so threading is likely to hurt performance. so don't
      std::copy(src->Begin(), src->End(), dst->Begin());
    }
    else
    {
      threadedCopyFunctor<ValueType> worker;
      worker.src = src->GetPointer(0);
      worker.dst = dst->GetPointer(0);
      worker.nComp = src->GetNumberOfComponents();
      // High granularity is likely to hurt performance too, so limit calls. 16 is about maximal.
      int numThreads = std::min(vtkSMPTools::GetEstimatedNumberOfThreads(), 16);
      vtkSMPTools::For(0, len, len / numThreads, worker);
    }
  }

#if defined(__clang__) && defined(__has_warning)
#if __has_warning("-Wunused-template")
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-template"
#endif
#endif

  // SoA --> SoA same-type specialization:
  template <typename ValueType>
  void operator()(
    vtkSOADataArrayTemplate<ValueType>* src, vtkSOADataArrayTemplate<ValueType>* dst) const
  {
    dst->CopyData(src);
  }

#ifdef VTK_USE_SCALED_SOA_ARRAYS
  // ScaleSoA --> ScaleSoA same-type specialization:
  template <typename ValueType>
  void operator()(
    vtkScaledSOADataArrayTemplate<ValueType>* src, vtkScaledSOADataArrayTemplate<ValueType>* dst)
  {
    vtkIdType numTuples = src->GetNumberOfTuples();
    for (int comp = 0; comp < src->GetNumberOfComponents(); ++comp)
    {
      ValueType* srcBegin = src->GetComponentArrayPointer(comp);
      ValueType* srcEnd = srcBegin + numTuples;
      ValueType* dstBegin = dst->GetComponentArrayPointer(comp);

      std::copy(srcBegin, srcEnd, dstBegin);
    }
    dst->SetScale(src->GetScale());
  }
#endif

// Undo warning suppression.
#if defined(__clang__) && defined(__has_warning)
#if __has_warning("-Wunused-template")
#pragma clang diagnostic pop
#endif
#endif

  // Generic implementation:
  template <typename SrcArrayT, typename DstArrayT>
  void DoGenericCopy(SrcArrayT* src, DstArrayT* dst) const
  {
    const auto srcRange = vtk::DataArrayValueRange(src);
    auto dstRange = vtk::DataArrayValueRange(dst);

    using DstT = typename decltype(dstRange)::ValueType;
    auto destIter = dstRange.begin();
    // use for loop instead of copy to avoid -Wconversion warnings
    for (auto v = srcRange.cbegin(); v != srcRange.cend(); ++v, ++destIter)
    {
      *destIter = static_cast<DstT>(*v);
    }
  }

  // These overloads are split so that the above specializations will be
  // used properly.
  template <typename Array1DerivedT, typename Array1ValueT, typename Array2DerivedT,
    typename Array2ValueT>
  void operator()(vtkGenericDataArray<Array1DerivedT, Array1ValueT>* src,
    vtkGenericDataArray<Array2DerivedT, Array2ValueT>* dst) const
  {
    this->DoGenericCopy(src, dst);
  }

  void operator()(vtkDataArray* src, vtkDataArray* dst) const { this->DoGenericCopy(src, dst); }
};

//------------InterpolateTuple workers------------------------------------------
struct InterpolateMultiTupleWorker
{
  vtkIdType DestTuple;
  vtkIdType* TupleIds;
  vtkIdType NumTuples;
  double* Weights;

  InterpolateMultiTupleWorker(
    vtkIdType destTuple, vtkIdType* tupleIds, vtkIdType numTuples, double* weights)
    : DestTuple(destTuple)
    , TupleIds(tupleIds)
    , NumTuples(numTuples)
    , Weights(weights)
  {
  }

  template <typename Array1T, typename Array2T>
  void operator()(Array1T* src, Array2T* dst) const
  {
    // Use vtkDataArrayAccessor here instead of a range, since we need to use
    // Insert for legacy compat
    vtkDataArrayAccessor<Array1T> s(src);
    vtkDataArrayAccessor<Array2T> d(dst);

    typedef typename vtkDataArrayAccessor<Array2T>::APIType DestType;

    int numComp = src->GetNumberOfComponents();

    for (int c = 0; c < numComp; ++c)
    {
      double val = 0.;
      for (vtkIdType tupleId = 0; tupleId < this->NumTuples; ++tupleId)
      {
        vtkIdType t = this->TupleIds[tupleId];
        double weight = this->Weights[tupleId];
        val += weight * static_cast<double>(s.Get(t, c));
      }
      DestType valT;
      vtkMath::RoundDoubleToIntegralIfNecessary(val, &valT);
      d.Insert(this->DestTuple, c, valT);
    }
  }
};

struct InterpolateTupleWorker
{
  vtkIdType SrcTuple1;
  vtkIdType SrcTuple2;
  vtkIdType DstTuple;
  double Weight;

  InterpolateTupleWorker(
    vtkIdType srcTuple1, vtkIdType srcTuple2, vtkIdType dstTuple, double weight)
    : SrcTuple1(srcTuple1)
    , SrcTuple2(srcTuple2)
    , DstTuple(dstTuple)
    , Weight(weight)
  {
  }

  template <typename Array1T, typename Array2T, typename Array3T>
  void operator()(Array1T* src1, Array2T* src2, Array3T* dst) const
  {
    // Use accessor here instead of ranges since we need to use Insert for
    // legacy compat
    vtkDataArrayAccessor<Array1T> s1(src1);
    vtkDataArrayAccessor<Array2T> s2(src2);
    vtkDataArrayAccessor<Array3T> d(dst);

    typedef typename vtkDataArrayAccessor<Array3T>::APIType DestType;

    const int numComps = dst->GetNumberOfComponents();
    const double oneMinusT = 1. - this->Weight;
    double val;
    DestType valT;

    for (int c = 0; c < numComps; ++c)
    {
      val = s1.Get(this->SrcTuple1, c) * oneMinusT + s2.Get(this->SrcTuple2, c) * this->Weight;
      vtkMath::RoundDoubleToIntegralIfNecessary(val, &valT);
      d.Insert(this->DstTuple, c, valT);
    }
  }
};

//-----------------GetTuples (id list)------------------------------------------
struct GetTuplesFromListWorker
{
  vtkIdList* Ids;

  GetTuplesFromListWorker(vtkIdList* ids)
    : Ids(ids)
  {
  }

  template <typename Array1T, typename Array2T>
  void operator()(Array1T* src, Array2T* dst) const
  {
    const auto srcTuples = vtk::DataArrayTupleRange(src);
    auto dstTuples = vtk::DataArrayTupleRange(dst);

    vtkIdType* srcTupleId = this->Ids->GetPointer(0);
    vtkIdType* srcTupleIdEnd = this->Ids->GetPointer(Ids->GetNumberOfIds());

    auto dstTupleIter = dstTuples.begin();
    while (srcTupleId != srcTupleIdEnd)
    {
      *dstTupleIter++ = srcTuples[*srcTupleId++];
    }
  }
};

//-----------------GetTuples (tuple range)--------------------------------------
struct GetTuplesRangeWorker
{
  vtkIdType Start;
  vtkIdType End; // Note that End is inclusive.

  GetTuplesRangeWorker(vtkIdType start, vtkIdType end)
    : Start(start)
    , End(end)
  {
  }

  template <typename Array1T, typename Array2T>
  void operator()(Array1T* src, Array2T* dst) const
  {
    const auto srcTuples = vtk::DataArrayTupleRange(src);
    auto dstTuples = vtk::DataArrayTupleRange(dst);

    for (vtkIdType srcT = this->Start, dstT = 0; srcT <= this->End; ++srcT, ++dstT)
    {
      dstTuples[dstT] = srcTuples[srcT];
    }
  }
};

//----------------SetTuple (from array)-----------------------------------------
struct SetTupleArrayWorker
{
  vtkIdType SrcTuple;
  vtkIdType DstTuple;

  SetTupleArrayWorker(vtkIdType srcTuple, vtkIdType dstTuple)
    : SrcTuple(srcTuple)
    , DstTuple(dstTuple)
  {
  }

  template <typename SrcArrayT, typename DstArrayT>
  void operator()(SrcArrayT* src, DstArrayT* dst) const
  {
    const auto srcTuples = vtk::DataArrayTupleRange(src);
    auto dstTuples = vtk::DataArrayTupleRange(dst);

    dstTuples[this->DstTuple] = srcTuples[this->SrcTuple];
  }
};

//----------------SetTuples (from array+vtkIdList)------------------------------
struct SetTuplesIdListWorker
{
  vtkIdList* SrcTuples;
  vtkIdList* DstTuples;

  SetTuplesIdListWorker(vtkIdList* srcTuples, vtkIdList* dstTuples)
    : SrcTuples(srcTuples)
    , DstTuples(dstTuples)
  {
  }

  template <typename SrcArrayT, typename DstArrayT>
  void operator()(SrcArrayT* src, DstArrayT* dst) const
  {
    const auto srcTuples = vtk::DataArrayTupleRange(src);
    auto dstTuples = vtk::DataArrayTupleRange(dst);

    vtkIdType numTuples = this->SrcTuples->GetNumberOfIds();
    for (vtkIdType t = 0; t < numTuples; ++t)
    {
      vtkIdType srcT = this->SrcTuples->GetId(t);
      vtkIdType dstT = this->DstTuples->GetId(t);

      dstTuples[dstT] = srcTuples[srcT];
    }
  }
};

//----------------SetTuples (from array+vtkIdList to range)---------------------
struct SetTuplesIdListRangeWorker
{
  vtkIdList* SrcTuples;
  vtkIdType DstStartTuple;

  SetTuplesIdListRangeWorker(vtkIdList* srcTuples, vtkIdType dstStartTuple)
    : SrcTuples(srcTuples)
    , DstStartTuple(dstStartTuple)
  {
  }

  template <typename SrcArrayT, typename DstArrayT>
  void operator()(SrcArrayT* src, DstArrayT* dst) const
  {
    const auto srcTuples = vtk::DataArrayTupleRange(src);
    auto dstTuples = vtk::DataArrayTupleRange(dst);

    vtkIdType numTuples = this->SrcTuples->GetNumberOfIds();
    vtkIdType dstT = this->DstStartTuple;

    for (vtkIdType t = 0; t < numTuples; ++t)
    {
      vtkIdType srcT = this->SrcTuples->GetId(t);
      dstTuples[dstT + t] = srcTuples[srcT];
    }
  }
};

//----------------SetTuples (from array+range)----------------------------------
struct SetTuplesRangeWorker
{
  vtkIdType SrcStartTuple;
  vtkIdType DstStartTuple;
  vtkIdType NumTuples;

  SetTuplesRangeWorker(vtkIdType srcStartTuple, vtkIdType dstStartTuple, vtkIdType numTuples)
    : SrcStartTuple(srcStartTuple)
    , DstStartTuple(dstStartTuple)
    , NumTuples(numTuples)
  {
  }

  // Generic implementation. We perform the obvious optimizations for AOS/SOA
  // in the derived class implementations.
  template <typename SrcArrayT, typename DstArrayT>
  void operator()(SrcArrayT* src, DstArrayT* dst) const
  {
    const auto srcTuples = vtk::DataArrayTupleRange(src);
    auto dstTuples = vtk::DataArrayTupleRange(dst);

    vtkIdType srcT = this->SrcStartTuple;
    vtkIdType srcTEnd = srcT + this->NumTuples;
    vtkIdType dstT = this->DstStartTuple;

    while (srcT < srcTEnd)
    {
      dstTuples[dstT++] = srcTuples[srcT++];
    }
  }
};

template <typename InfoType, typename KeyType>
bool hasValidKey(InfoType info, KeyType key, double range[2])
{
  if (info->Has(key))
  {
    info->Get(key, range);
    return true;
  }
  return false;
}

template <typename InfoType, typename KeyType, typename ComponentKeyType>
bool hasValidKey(InfoType info, KeyType key, ComponentKeyType ckey, double range[2], int comp)
{
  if (info->Has(key))
  {
    info->Get(key)->GetInformationObject(comp)->Get(ckey, range);
    return true;
  }
  return false;
}

} // end anon namespace

VTK_ABI_NAMESPACE_BEGIN

vtkInformationKeyRestrictedMacro(vtkDataArray, COMPONENT_RANGE, DoubleVector, 2);
vtkInformationKeyRestrictedMacro(vtkDataArray, L2_NORM_RANGE, DoubleVector, 2);
vtkInformationKeyRestrictedMacro(vtkDataArray, L2_NORM_FINITE_RANGE, DoubleVector, 2);
vtkInformationKeyMacro(vtkDataArray, UNITS_LABEL, String);

//------------------------------------------------------------------------------
// Construct object with default tuple dimension (number of components) of 1.
vtkDataArray::vtkDataArray()
{
  this->LookupTable = nullptr;
  this->Range[0] = 0;
  this->Range[1] = 0;
  this->FiniteRange[0] = 0;
  this->FiniteRange[1] = 0;
}

//------------------------------------------------------------------------------
vtkDataArray::~vtkDataArray()
{
  if (this->LookupTable)
  {
    this->LookupTable->Delete();
  }
  this->SetName(nullptr);
}

//------------------------------------------------------------------------------
void vtkDataArray::DeepCopy(vtkAbstractArray* aa)
{
  if (aa == nullptr)
  {
    return;
  }

  vtkDataArray* da = vtkDataArray::FastDownCast(aa);
  if (da == nullptr)
  {
    vtkErrorMacro(<< "Input array is not a vtkDataArray (" << aa->GetClassName() << ")");
    return;
  }

  this->DeepCopy(da);
}

//------------------------------------------------------------------------------
// Normally subclasses will do this when the input and output type of the
// DeepCopy are the same. When they are not the same, then we use the
// templated code below.
void vtkDataArray::DeepCopy(vtkDataArray* da)
{
  // Match the behavior of the old AttributeData
  if (da == nullptr)
  {
    return;
  }

  if (this != da)
  {
    this->Superclass::DeepCopy(da); // copy Information object

    vtkIdType numTuples = da->GetNumberOfTuples();
    int numComps = da->NumberOfComponents;

    this->SetNumberOfComponents(numComps);
    this->SetNumberOfTuples(numTuples);

    if (numTuples != 0)
    {
      DeepCopyWorker worker;
      if (!vtkArrayDispatch::Dispatch2::Execute(da, this, worker))
      {
        // If dispatch fails, use fallback:
        worker(da, this);
      }
    }

    this->SetLookupTable(nullptr);
    if (da->LookupTable)
    {
      this->LookupTable = da->LookupTable->NewInstance();
      this->LookupTable->DeepCopy(da->LookupTable);
    }
  }

  this->Squeeze();
}

//------------------------------------------------------------------------------
void vtkDataArray::ShallowCopy(vtkDataArray* other)
{
  // Deep copy by default. Subclasses may override this behavior.
  this->DeepCopy(other);
}

//------------------------------------------------------------------------------
void vtkDataArray::SetTuple(vtkIdType dstTupleIdx, vtkIdType srcTupleIdx, vtkAbstractArray* source)
{
  vtkDataArray* srcDA = vtkDataArray::FastDownCast(source);
  if (!srcDA)
  {
    vtkErrorMacro(
      "Source array must be a vtkDataArray subclass (got " << source->GetClassName() << ").");
    return;
  }

  if (source->GetNumberOfComponents() != this->GetNumberOfComponents())
  {
    vtkErrorMacro("Number of components do not match: Source: "
      << source->GetNumberOfComponents() << " Dest: " << this->GetNumberOfComponents());
    return;
  }

  SetTupleArrayWorker worker(srcTupleIdx, dstTupleIdx);
  if (!vtkArrayDispatch::Dispatch2::Execute(srcDA, this, worker))
  {
    worker(srcDA, this);
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::SetTuple(vtkIdType i, const float* source)
{
  for (int c = 0; c < this->NumberOfComponents; ++c)
  {
    this->SetComponent(i, c, static_cast<double>(source[c]));
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::SetTuple(vtkIdType i, const double* source)
{
  for (int c = 0; c < this->NumberOfComponents; ++c)
  {
    this->SetComponent(i, c, source[c]);
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::InsertTuple(
  vtkIdType dstTupleIdx, vtkIdType srcTupleIdx, vtkAbstractArray* source)
{
  vtkIdType newSize = (dstTupleIdx + 1) * this->NumberOfComponents;
  if (this->Size < newSize)
  {
    if (!this->Resize(dstTupleIdx + 1))
    {
      vtkErrorMacro("Resize failed.");
      return;
    }
  }

  this->MaxId = std::max(this->MaxId, newSize - 1);

  this->SetTuple(dstTupleIdx, srcTupleIdx, source);
}

//------------------------------------------------------------------------------
vtkIdType vtkDataArray::InsertNextTuple(vtkIdType srcTupleIdx, vtkAbstractArray* source)
{
  vtkIdType tupleIdx = this->GetNumberOfTuples();
  this->InsertTuple(tupleIdx, srcTupleIdx, source);
  return tupleIdx;
}

//------------------------------------------------------------------------------
void vtkDataArray::InsertTuples(vtkIdList* dstIds, vtkIdList* srcIds, vtkAbstractArray* src)
{
  if (dstIds->GetNumberOfIds() == 0)
  {
    return;
  }
  if (dstIds->GetNumberOfIds() != srcIds->GetNumberOfIds())
  {
    vtkErrorMacro("Mismatched number of tuples ids. Source: "
      << srcIds->GetNumberOfIds() << " Dest: " << dstIds->GetNumberOfIds());
    return;
  }
  if (src->GetNumberOfComponents() != this->GetNumberOfComponents())
  {
    vtkErrorMacro("Number of components do not match: Source: "
      << src->GetNumberOfComponents() << " Dest: " << this->GetNumberOfComponents());
    return;
  }
  vtkDataArray* srcDA = vtkDataArray::FastDownCast(src);
  if (!srcDA)
  {
    vtkErrorMacro("Source array must be a subclass of vtkDataArray. Got: " << src->GetClassName());
    return;
  }

  vtkIdType maxSrcTupleId = srcIds->GetId(0);
  vtkIdType maxDstTupleId = dstIds->GetId(0);
  for (vtkIdType i = 1; i < dstIds->GetNumberOfIds(); ++i)
  {
    maxSrcTupleId = std::max(maxSrcTupleId, srcIds->GetId(i));
    maxDstTupleId = std::max(maxDstTupleId, dstIds->GetId(i));
  }

  if (maxSrcTupleId >= src->GetNumberOfTuples())
  {
    vtkErrorMacro("Source array too small, requested tuple at index "
      << maxSrcTupleId << ", but there are only " << src->GetNumberOfTuples()
      << " tuples in the array.");
    return;
  }

  vtkIdType newSize = (maxDstTupleId + 1) * this->NumberOfComponents;
  if (this->Size < newSize)
  {
    if (!this->Resize(maxDstTupleId + 1))
    {
      vtkErrorMacro("Resize failed.");
      return;
    }
  }

  this->MaxId = std::max(this->MaxId, newSize - 1);

  SetTuplesIdListWorker worker(srcIds, dstIds);
  if (!vtkArrayDispatch::Dispatch2::Execute(srcDA, this, worker))
  {
    worker(srcDA, this);
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::InsertTuplesStartingAt(
  vtkIdType dstStart, vtkIdList* srcIds, vtkAbstractArray* src)
{
  if (src->GetNumberOfComponents() != this->GetNumberOfComponents())
  {
    vtkErrorMacro("Number of components do not match: Source: "
      << src->GetNumberOfComponents() << " Dest: " << this->GetNumberOfComponents());
    return;
  }
  vtkDataArray* srcDA = vtkDataArray::FastDownCast(src);
  if (!srcDA)
  {
    vtkErrorMacro("Source array must be a subclass of vtkDataArray. Got: " << src->GetClassName());
    return;
  }

  vtkIdType maxSrcTupleId = srcIds->GetId(0);
  vtkIdType maxDstTupleId = dstStart + srcIds->GetNumberOfIds() - 1;
  for (vtkIdType i = 1; i < srcIds->GetNumberOfIds(); ++i)
  {
    maxSrcTupleId = std::max(maxSrcTupleId, srcIds->GetId(i));
  }

  if (maxSrcTupleId >= src->GetNumberOfTuples())
  {
    vtkErrorMacro("Source array too small, requested tuple at index "
      << maxSrcTupleId << ", but there are only " << src->GetNumberOfTuples()
      << " tuples in the array.");
    return;
  }

  vtkIdType newSize = (maxDstTupleId + 1) * this->NumberOfComponents;
  if (this->Size < newSize)
  {
    if (!this->Resize(maxDstTupleId + 1))
    {
      vtkErrorMacro("Resize failed.");
      return;
    }
  }

  this->MaxId = std::max(this->MaxId, newSize - 1);

  SetTuplesIdListRangeWorker worker(srcIds, dstStart);
  if (!vtkArrayDispatch::Dispatch2SameValueType::Execute(srcDA, this, worker))
  {
    worker(srcDA, this);
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::InsertTuples(
  vtkIdType dstStart, vtkIdType n, vtkIdType srcStart, vtkAbstractArray* src)
{
  if (n == 0)
  {
    return;
  }
  if (src->GetNumberOfComponents() != this->GetNumberOfComponents())
  {
    vtkErrorMacro("Number of components do not match: Source: "
      << src->GetNumberOfComponents() << " Dest: " << this->GetNumberOfComponents());
    return;
  }
  vtkDataArray* srcDA = vtkDataArray::FastDownCast(src);
  if (!srcDA)
  {
    vtkErrorMacro("Source array must be a subclass of vtkDataArray. Got: " << src->GetClassName());
    return;
  }

  vtkIdType maxSrcTupleId = srcStart + n - 1;
  vtkIdType maxDstTupleId = dstStart + n - 1;

  if (maxSrcTupleId >= src->GetNumberOfTuples())
  {
    vtkErrorMacro("Source array too small, requested tuple at index "
      << maxSrcTupleId << ", but there are only " << src->GetNumberOfTuples()
      << " tuples in the array.");
    return;
  }

  vtkIdType newSize = (maxDstTupleId + 1) * this->NumberOfComponents;
  if (this->Size < newSize)
  {
    if (!this->Resize(maxDstTupleId + 1))
    {
      vtkErrorMacro("Resize failed.");
      return;
    }
  }

  this->MaxId = std::max(this->MaxId, newSize - 1);

  SetTuplesRangeWorker worker(srcStart, dstStart, n);
  if (!vtkArrayDispatch::Dispatch2::Execute(srcDA, this, worker))
  {
    worker(srcDA, this);
  }
}

//------------------------------------------------------------------------------
// These can be overridden for more efficiency
double vtkDataArray::GetComponent(vtkIdType tupleIdx, int compIdx)
{
  double *tuple = new double[this->NumberOfComponents], c;

  this->GetTuple(tupleIdx, tuple);
  c = tuple[compIdx];
  delete[] tuple;

  return c;
}

//------------------------------------------------------------------------------
void vtkDataArray::SetComponent(vtkIdType tupleIdx, int compIdx, double value)
{
  double* tuple = new double[this->NumberOfComponents];

  if (tupleIdx < this->GetNumberOfTuples())
  {
    this->GetTuple(tupleIdx, tuple);
  }
  else
  {
    for (int k = 0; k < this->NumberOfComponents; k++)
    {
      tuple[k] = 0.0;
    }
  }

  tuple[compIdx] = value;
  this->SetTuple(tupleIdx, tuple);

  delete[] tuple;
}

//------------------------------------------------------------------------------
void vtkDataArray::InsertComponent(vtkIdType tupleIdx, int compIdx, double value)
{
  double* tuple = new double[this->NumberOfComponents];

  if (tupleIdx < this->GetNumberOfTuples())
  {
    this->GetTuple(tupleIdx, tuple);
  }
  else
  {
    for (int k = 0; k < this->NumberOfComponents; k++)
    {
      tuple[k] = 0.0;
    }
  }

  tuple[compIdx] = value;
  this->InsertTuple(tupleIdx, tuple);

  delete[] tuple;
}

//------------------------------------------------------------------------------
void vtkDataArray::GetData(
  vtkIdType tupleMin, vtkIdType tupleMax, int compMin, int compMax, vtkDoubleArray* data)
{
  int i;
  vtkIdType j;
  int numComp = this->GetNumberOfComponents();
  double* tuple = new double[numComp];
  double* ptr = data->WritePointer(0, (tupleMax - tupleMin + 1) * (compMax - compMin + 1));

  for (j = tupleMin; j <= tupleMax; j++)
  {
    this->GetTuple(j, tuple);
    for (i = compMin; i <= compMax; i++)
    {
      *ptr++ = tuple[i];
    }
  }
  delete[] tuple;
}

//------------------------------------------------------------------------------
// Interpolate array value from other array value given the
// indices and associated interpolation weights.
// This method assumes that the two arrays are of the same time.
void vtkDataArray::InterpolateTuple(
  vtkIdType dstTupleIdx, vtkIdList* tupleIds, vtkAbstractArray* source, double* weights)
{
  if (!vtkDataTypesCompare(this->GetDataType(), source->GetDataType()))
  {
    vtkErrorMacro("Cannot interpolate arrays of different type.");
    return;
  }

  vtkDataArray* da = vtkDataArray::FastDownCast(source);
  if (!da)
  {
    vtkErrorMacro(<< "Source array is not a vtkDataArray.");
    return;
  }

  int numComps = this->GetNumberOfComponents();
  if (da->GetNumberOfComponents() != numComps)
  {
    vtkErrorMacro("Number of components do not match: Source: "
      << source->GetNumberOfComponents() << " Dest: " << this->GetNumberOfComponents());
    return;
  }

  vtkIdType numIds = tupleIds->GetNumberOfIds();
  vtkIdType* ids = tupleIds->GetPointer(0);

  bool fallback = da->GetDataType() == VTK_BIT || this->GetDataType() == VTK_BIT;

  if (!fallback)
  {
    InterpolateMultiTupleWorker worker(dstTupleIdx, ids, numIds, weights);
    // Use fallback if dispatch fails.
    fallback = !vtkArrayDispatch::Dispatch2SameValueType::Execute(da, this, worker);
  }

  // Fallback to a separate implementation that checks vtkDataArray::GetDataType
  // rather than relying on API types, since we'll need to round differently
  // depending on type, and the API type for vtkDataArray is always double.
  if (fallback)
  {
    bool doRound = !(this->GetDataType() == VTK_FLOAT || this->GetDataType() == VTK_DOUBLE);
    double typeMin = this->GetDataTypeMin();
    double typeMax = this->GetDataTypeMax();

    for (int c = 0; c < numComps; ++c)
    {
      double val = 0.;
      for (vtkIdType j = 0; j < numIds; ++j)
      {
        val += weights[j] * da->GetComponent(ids[j], c);
      }

      // Clamp to data type range:
      val = std::max(val, typeMin);
      val = std::min(val, typeMax);

      // Round for floating point types:
      if (doRound)
      {
        val = std::floor((val >= 0.) ? (val + 0.5) : (val - 0.5));
      }

      this->InsertComponent(dstTupleIdx, c, val);
    }
  }
}

//------------------------------------------------------------------------------
// Interpolate value from the two values, p1 and p2, and an
// interpolation factor, t. The interpolation factor ranges from (0,1),
// with t=0 located at p1. This method assumes that the three arrays are of
// the same type. p1 is value at index id1 in fromArray1, while, p2 is
// value at index id2 in fromArray2.
void vtkDataArray::InterpolateTuple(vtkIdType dstTuple, vtkIdType srcTuple1,
  vtkAbstractArray* source1, vtkIdType srcTuple2, vtkAbstractArray* source2, double t)
{
  int type = this->GetDataType();

  if (!vtkDataTypesCompare(type, source1->GetDataType()) ||
    !vtkDataTypesCompare(type, source2->GetDataType()))
  {
    vtkErrorMacro("All arrays to InterpolateValue must be of same type.");
    return;
  }

  if (srcTuple1 >= source1->GetNumberOfTuples())
  {
    vtkErrorMacro("Tuple 1 out of range for provided array. "
                  "Requested tuple: "
      << srcTuple1
      << " "
         "Tuples: "
      << source1->GetNumberOfTuples());
    return;
  }

  if (srcTuple2 >= source2->GetNumberOfTuples())
  {
    vtkErrorMacro("Tuple 2 out of range for provided array. "
                  "Requested tuple: "
      << srcTuple2
      << " "
         "Tuples: "
      << source2->GetNumberOfTuples());
    return;
  }

  vtkDataArray* src1DA = vtkDataArray::FastDownCast(source1);
  vtkDataArray* src2DA = vtkDataArray::FastDownCast(source2);
  if (!src1DA || !src2DA)
  {
    vtkErrorMacro("Both arrays must be vtkDataArray subclasses.");
    return;
  }

  bool fallback = type == VTK_BIT;

  if (!fallback)
  {
    InterpolateTupleWorker worker(srcTuple1, srcTuple2, dstTuple, t);
    // Use fallback if dispatch fails:
    fallback = !vtkArrayDispatch::Dispatch3SameValueType::Execute(src1DA, src2DA, this, worker);
  }

  // Fallback to a separate implementation that checks vtkDataArray::GetDataType
  // rather than relying on API types, since we'll need to round differently
  // depending on type, and the API type for vtkDataArray is always double.
  if (fallback)
  {
    bool doRound = !(this->GetDataType() == VTK_FLOAT || this->GetDataType() == VTK_DOUBLE);
    double typeMin = this->GetDataTypeMin();
    double typeMax = this->GetDataTypeMax();
    int numComp = source1->GetNumberOfComponents();
    double in1;
    double in2;
    double out;
    for (int c = 0; c < numComp; c++)
    {
      in1 = src1DA->GetComponent(srcTuple1, c);
      in2 = src2DA->GetComponent(srcTuple2, c);
      out = in1 + t * (in2 - in1);
      // Clamp to datatype range:
      out = std::max(out, typeMin);
      out = std::min(out, typeMax);
      // Round if needed:
      if (doRound)
      {
        out = std::floor((out >= 0.) ? (out + 0.5) : (out - 0.5));
      }
      this->InsertComponent(dstTuple, c, out);
    }
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::CreateDefaultLookupTable()
{
  if (this->LookupTable)
  {
    this->LookupTable->UnRegister(this);
  }
  this->LookupTable = vtkLookupTable::New();
  // make sure it is built
  // otherwise problems with InsertScalar trying to map through
  // non built lut
  this->LookupTable->Build();
}

//------------------------------------------------------------------------------
void vtkDataArray::SetLookupTable(vtkLookupTable* lut)
{
  if (this->LookupTable != lut)
  {
    if (this->LookupTable)
    {
      this->LookupTable->UnRegister(this);
    }
    this->LookupTable = lut;
    if (this->LookupTable)
    {
      this->LookupTable->Register(this);
    }
    this->Modified();
  }
}

//------------------------------------------------------------------------------
double* vtkDataArray::GetTupleN(vtkIdType i, int n)
{
  int numComp = this->GetNumberOfComponents();
  if (numComp != n)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != " << n);
  }
  return this->GetTuple(i);
}

//------------------------------------------------------------------------------
double vtkDataArray::GetTuple1(vtkIdType i)
{
  int numComp = this->GetNumberOfComponents();
  if (numComp != 1)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 1");
  }
  return *(this->GetTuple(i));
}

//------------------------------------------------------------------------------
double* vtkDataArray::GetTuple2(vtkIdType i)
{
  return this->GetTupleN(i, 2);
}
//------------------------------------------------------------------------------
double* vtkDataArray::GetTuple3(vtkIdType i)
{
  return this->GetTupleN(i, 3);
}
//------------------------------------------------------------------------------
double* vtkDataArray::GetTuple4(vtkIdType i)
{
  return this->GetTupleN(i, 4);
}
//------------------------------------------------------------------------------
double* vtkDataArray::GetTuple6(vtkIdType i)
{
  return this->GetTupleN(i, 6);
}
//------------------------------------------------------------------------------
double* vtkDataArray::GetTuple9(vtkIdType i)
{
  return this->GetTupleN(i, 9);
}

//------------------------------------------------------------------------------
void vtkDataArray::SetTuple1(vtkIdType i, double value)
{
  int numComp = this->GetNumberOfComponents();
  if (numComp != 1)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 1");
  }
  this->SetTuple(i, &value);
}
//------------------------------------------------------------------------------
void vtkDataArray::SetTuple2(vtkIdType i, double val0, double val1)
{
  double tuple[2];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 2)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 2");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  this->SetTuple(i, tuple);
}
//------------------------------------------------------------------------------
void vtkDataArray::SetTuple3(vtkIdType i, double val0, double val1, double val2)
{
  double tuple[3];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 3)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 3");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  tuple[2] = val2;
  this->SetTuple(i, tuple);
}
//------------------------------------------------------------------------------
void vtkDataArray::SetTuple4(vtkIdType i, double val0, double val1, double val2, double val3)
{
  double tuple[4];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 4)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 4");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  tuple[2] = val2;
  tuple[3] = val3;
  this->SetTuple(i, tuple);
}
//------------------------------------------------------------------------------
void vtkDataArray::SetTuple6(
  vtkIdType i, double val0, double val1, double val2, double val3, double val4, double val5)
{
  double tuple[6];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 6)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 6");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  tuple[2] = val2;
  tuple[3] = val3;
  tuple[4] = val4;
  tuple[5] = val5;
  this->SetTuple(i, tuple);
}
//------------------------------------------------------------------------------
void vtkDataArray::SetTuple9(vtkIdType i, double val0, double val1, double val2, double val3,
  double val4, double val5, double val6, double val7, double val8)
{
  double tuple[9];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 9)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 9");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  tuple[2] = val2;
  tuple[3] = val3;
  tuple[4] = val4;
  tuple[5] = val5;
  tuple[6] = val6;
  tuple[7] = val7;
  tuple[8] = val8;
  this->SetTuple(i, tuple);
}

//------------------------------------------------------------------------------
void vtkDataArray::InsertTuple1(vtkIdType i, double value)
{
  int numComp = this->GetNumberOfComponents();
  if (numComp != 1)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 1");
  }
  this->InsertTuple(i, &value);
}
//------------------------------------------------------------------------------
void vtkDataArray::InsertTuple2(vtkIdType i, double val0, double val1)
{
  double tuple[2];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 2)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 2");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  this->InsertTuple(i, tuple);
}
//------------------------------------------------------------------------------
void vtkDataArray::InsertTuple3(vtkIdType i, double val0, double val1, double val2)
{
  double tuple[3];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 3)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 3");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  tuple[2] = val2;
  this->InsertTuple(i, tuple);
}
//------------------------------------------------------------------------------
void vtkDataArray::InsertTuple4(vtkIdType i, double val0, double val1, double val2, double val3)
{
  double tuple[4];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 4)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 4");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  tuple[2] = val2;
  tuple[3] = val3;
  this->InsertTuple(i, tuple);
}

//------------------------------------------------------------------------------
void vtkDataArray::InsertTuple6(
  vtkIdType i, double val0, double val1, double val2, double val3, double val4, double val5)
{
  if (this->NumberOfComponents != 6)
  {
    vtkErrorMacro("The number of components do not match the number requested: "
      << this->NumberOfComponents << " != 6");
  }
  double tuple[6] = { val0, val1, val2, val3, val4, val5 };
  this->InsertTuple(i, tuple);
}

//------------------------------------------------------------------------------
void vtkDataArray::InsertTuple9(vtkIdType i, double val0, double val1, double val2, double val3,
  double val4, double val5, double val6, double val7, double val8)
{
  double tuple[9];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 9)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 9");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  tuple[2] = val2;
  tuple[3] = val3;
  tuple[4] = val4;
  tuple[5] = val5;
  tuple[6] = val6;
  tuple[7] = val7;
  tuple[8] = val8;
  this->InsertTuple(i, tuple);
}

//------------------------------------------------------------------------------
void vtkDataArray::InsertNextTuple1(double value)
{
  int numComp = this->GetNumberOfComponents();
  if (numComp != 1)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 1");
  }
  this->InsertNextTuple(&value);
}
//------------------------------------------------------------------------------
void vtkDataArray::InsertNextTuple2(double val0, double val1)
{
  double tuple[2];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 2)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 2");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  this->InsertNextTuple(tuple);
}
//------------------------------------------------------------------------------
void vtkDataArray::InsertNextTuple3(double val0, double val1, double val2)
{
  double tuple[3];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 3)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 3");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  tuple[2] = val2;
  this->InsertNextTuple(tuple);
}
//------------------------------------------------------------------------------
void vtkDataArray::InsertNextTuple4(double val0, double val1, double val2, double val3)
{
  double tuple[4];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 4)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 4");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  tuple[2] = val2;
  tuple[3] = val3;
  this->InsertNextTuple(tuple);
}

//------------------------------------------------------------------------------
void vtkDataArray::InsertNextTuple6(
  double val0, double val1, double val2, double val3, double val4, double val5)
{
  if (this->NumberOfComponents != 6)
  {
    vtkErrorMacro("The number of components do not match the number requested: "
      << this->NumberOfComponents << " != 6");
  }

  double tuple[6] = { val0, val1, val2, val3, val4, val5 };
  this->InsertNextTuple(tuple);
}

//------------------------------------------------------------------------------
void vtkDataArray::InsertNextTuple9(double val0, double val1, double val2, double val3, double val4,
  double val5, double val6, double val7, double val8)
{
  double tuple[9];
  int numComp = this->GetNumberOfComponents();
  if (numComp != 9)
  {
    vtkErrorMacro(
      "The number of components do not match the number requested: " << numComp << " != 9");
  }
  tuple[0] = val0;
  tuple[1] = val1;
  tuple[2] = val2;
  tuple[3] = val3;
  tuple[4] = val4;
  tuple[5] = val5;
  tuple[6] = val6;
  tuple[7] = val7;
  tuple[8] = val8;
  this->InsertNextTuple(tuple);
}

//------------------------------------------------------------------------------
unsigned long vtkDataArray::GetActualMemorySize() const
{
  vtkIdType numPrims;
  double size;
  // The allocated array may be larger than the number of primitives used.
  // numPrims = this->GetNumberOfTuples() * this->GetNumberOfComponents();
  numPrims = this->GetSize();

  size = vtkDataArray::GetDataTypeSize(this->GetDataType());

  // kibibytes
  return static_cast<unsigned long>(ceil((size * static_cast<double>(numPrims)) / 1024.0));
}

//------------------------------------------------------------------------------
vtkDataArray* vtkDataArray::CreateDataArray(int dataType)
{
  vtkAbstractArray* aa = vtkAbstractArray::CreateArray(dataType);
  vtkDataArray* da = vtkDataArray::FastDownCast(aa);
  if (!da && aa)
  {
    // Requested array is not a vtkDataArray. Delete the allocated array.
    aa->Delete();
  }
  return da;
}

//------------------------------------------------------------------------------
void vtkDataArray::GetTuples(vtkIdList* tupleIds, vtkAbstractArray* aa)
{
  vtkDataArray* da = vtkDataArray::FastDownCast(aa);
  if (!da)
  {
    vtkErrorMacro("Input is not a vtkDataArray, but " << aa->GetClassName());
    return;
  }

  if ((da->GetNumberOfComponents() != this->GetNumberOfComponents()))
  {
    vtkErrorMacro("Number of components for input and output do not match.\n"
                  "Source: "
      << this->GetNumberOfComponents()
      << "\n"
         "Destination: "
      << da->GetNumberOfComponents());
    return;
  }

  GetTuplesFromListWorker worker(tupleIds);
  if (!vtkArrayDispatch::Dispatch2::Execute(this, da, worker))
  {
    // Use fallback if dispatch fails.
    worker(this, da);
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::GetTuples(vtkIdType p1, vtkIdType p2, vtkAbstractArray* aa)
{
  vtkDataArray* da = vtkDataArray::FastDownCast(aa);
  if (!da)
  {
    vtkWarningMacro("Input is not a vtkDataArray.");
    return;
  }

  if ((da->GetNumberOfComponents() != this->GetNumberOfComponents()))
  {
    vtkErrorMacro("Number of components for input and output do not match.\n"
                  "Source: "
      << this->GetNumberOfComponents()
      << "\n"
         "Destination: "
      << da->GetNumberOfComponents());
    return;
  }

  GetTuplesRangeWorker worker(p1, p2);
  if (!vtkArrayDispatch::Dispatch2::Execute(this, da, worker))
  {
    // Use fallback if dispatch fails.
    worker(this, da);
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::FillComponent(int compIdx, double value)
{
  if (compIdx < 0 || compIdx >= this->GetNumberOfComponents())
  {
    vtkErrorMacro(<< "Specified component " << compIdx << " is not in [0, "
                  << this->GetNumberOfComponents() << ")");
    return;
  }

  // Xcode 8.2 calls GetNumberOfTuples() after each iteration.
  // Prevent this by storing the result in a local variable.
  vtkIdType numberOfTuples = this->GetNumberOfTuples();
  for (vtkIdType i = 0; i < numberOfTuples; i++)
  {
    this->SetComponent(i, compIdx, value);
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::Fill(double value)
{
  for (int i = 0; i < this->GetNumberOfComponents(); ++i)
  {
    this->FillComponent(i, value);
  }
}
VTK_ABI_NAMESPACE_END

namespace
{
struct CopyComponentWorker
{
  CopyComponentWorker(int srcComponent, int dstComponent)
    : SourceComponent(srcComponent)
    , DestinationComponent(dstComponent)
  {
  }

  template <typename ArraySrc, typename ArrayDst>
  void operator()(ArraySrc* dst, ArrayDst* src) const
  {
    const auto srcRange = vtk::DataArrayTupleRange(src);
    auto dstRange = vtk::DataArrayTupleRange(dst);

    using DstType = vtk::GetAPIType<ArrayDst>;
    auto dstIter = dstRange.begin();

    for (auto v : srcRange)
    {
      (*dstIter)[DestinationComponent] = static_cast<DstType>(v[SourceComponent]);
      ++dstIter;
    }
  }

  int SourceComponent = 0;
  int DestinationComponent = 0;
};
}

VTK_ABI_NAMESPACE_BEGIN
//------------------------------------------------------------------------------
void vtkDataArray::CopyComponent(int dstComponent, vtkDataArray* src, int srcComponent)
{
  if (this->GetNumberOfTuples() != src->GetNumberOfTuples())
  {
    vtkErrorMacro(<< "Number of tuples in 'from' (" << src->GetNumberOfTuples() << ") and 'to' ("
                  << this->GetNumberOfTuples() << ") do not match.");
    return;
  }

  if (dstComponent < 0 || dstComponent >= this->GetNumberOfComponents())
  {
    vtkErrorMacro(<< "Specified component " << dstComponent << " in 'to' array is not in [0, "
                  << this->GetNumberOfComponents() << ")");
    return;
  }

  if (srcComponent < 0 || srcComponent >= src->GetNumberOfComponents())
  {
    vtkErrorMacro(<< "Specified component " << srcComponent << " in 'from' array is not in [0, "
                  << src->GetNumberOfComponents() << ")");
    return;
  }

  CopyComponentWorker copyComponentWorker(srcComponent, dstComponent);
  if (!vtkArrayDispatch::Dispatch2::Execute(this, src, copyComponentWorker))
  {
    copyComponentWorker(this, src);
  }
}

//------------------------------------------------------------------------------
double vtkDataArray::GetMaxNorm()
{
  vtkIdType i;
  double norm, maxNorm;
  int nComponents = this->GetNumberOfComponents();

  maxNorm = 0.0;
  for (i = 0; i < this->GetNumberOfTuples(); i++)
  {
    norm = vtkMath::Norm(this->GetTuple(i), nComponents);
    if (norm > maxNorm)
    {
      maxNorm = norm;
    }
  }

  return maxNorm;
}

//------------------------------------------------------------------------------
int vtkDataArray::CopyInformation(vtkInformation* infoFrom, vtkTypeBool deep)
{
  // Copy everything + give base classes a chance to
  // Exclude keys which they don't want copied.
  this->Superclass::CopyInformation(infoFrom, deep);

  // Remove any keys we own that are not to be copied here.
  vtkInformation* myInfo = this->GetInformation();
  // Range:
  if (myInfo->Has(L2_NORM_RANGE()))
  {
    myInfo->Remove(L2_NORM_RANGE());
  }

  return 1;
}

//------------------------------------------------------------------------------
void vtkDataArray::ComputeFiniteRange(double range[2], int comp)
{
  this->ComputeFiniteRange(range, comp, nullptr);
}

//------------------------------------------------------------------------------
void vtkDataArray::ComputeFiniteRange(
  double range[2], int comp, const unsigned char* ghosts, unsigned char ghostsToSkip)
{
  // this method needs a large refactoring to be way easier to read

  if (comp >= this->NumberOfComponents)
  { // Ignore requests for nonexistent components.
    return;
  }
  // If we got component -1 on a vector array, compute vector magnitude.
  if (comp < 0 && this->NumberOfComponents == 1)
  {
    comp = 0;
  }

  range[0] = vtkTypeTraits<double>::Max();
  range[1] = vtkTypeTraits<double>::Min();

  vtkInformation* info = this->GetInformation();
  vtkInformationDoubleVectorKey* rkey;
  if (comp < 0)
  {
    if (ghosts)
    {
      this->ComputeFiniteVectorRange(range, ghosts, ghostsToSkip);
      return;
    }
    rkey = L2_NORM_FINITE_RANGE();
    // hasValidKey will update range to the cached value if it exists.
    if (!hasValidKey(info, rkey, range))
    {
      this->ComputeFiniteVectorRange(range);
      info->Set(rkey, range, 2);
    }
    return;
  }
  else
  {
    std::vector<double> allCompRanges(this->NumberOfComponents * 2);
    if (ghosts)
    {
      if (this->ComputeFiniteScalarRange(allCompRanges.data(), ghosts, ghostsToSkip))
      {
        range[0] = allCompRanges[comp * 2];
        range[1] = allCompRanges[(comp * 2) + 1];
      }
      return;
    }
    rkey = COMPONENT_RANGE();

    // hasValidKey will update range to the cached value if it exists.
    if (!hasValidKey(info, PER_FINITE_COMPONENT(), rkey, range, comp))
    {
      const bool computed = this->ComputeFiniteScalarRange(allCompRanges.data());
      if (computed)
      {
        // construct the keys and add them to the info object
        vtkInformationVector* infoVec = vtkInformationVector::New();
        info->Set(PER_FINITE_COMPONENT(), infoVec);

        infoVec->SetNumberOfInformationObjects(this->NumberOfComponents);
        for (int i = 0; i < this->NumberOfComponents; ++i)
        {
          infoVec->GetInformationObject(i)->Set(rkey, allCompRanges.data() + (i * 2), 2);
        }
        infoVec->FastDelete();

        // update the range passed in since we have a valid range.
        range[0] = allCompRanges[comp * 2];
        range[1] = allCompRanges[(comp * 2) + 1];
      }
    }
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::ComputeRange(double range[2], int comp)
{
  this->ComputeRange(range, comp, nullptr);
}

//------------------------------------------------------------------------------
void vtkDataArray::ComputeRange(
  double range[2], int comp, const unsigned char* ghosts, unsigned char ghostsToSkip)
{
  // this method needs a large refactoring to be way easier to read

  if (comp >= this->NumberOfComponents)
  { // Ignore requests for nonexistent components.
    return;
  }
  // If we got component -1 on a vector array, compute vector magnitude.
  if (comp < 0 && this->NumberOfComponents == 1)
  {
    comp = 0;
  }

  range[0] = vtkTypeTraits<double>::Max();
  range[1] = vtkTypeTraits<double>::Min();

  vtkInformation* info = this->GetInformation();
  vtkInformationDoubleVectorKey* rkey;
  if (comp < 0)
  {
    if (ghosts)
    {
      this->ComputeVectorRange(range, ghosts, ghostsToSkip);
      return;
    }
    rkey = L2_NORM_RANGE();
    // hasValidKey will update range to the cached value if it exists.
    if (!hasValidKey(info, rkey, range))
    {
      this->ComputeVectorRange(range);
      info->Set(rkey, range, 2);
    }
    return;
  }
  else
  {
    std::vector<double> allCompRanges(this->NumberOfComponents * 2);
    if (ghosts)
    {
      if (this->ComputeScalarRange(allCompRanges.data(), ghosts, ghostsToSkip))
      {
        range[0] = allCompRanges[comp * 2];
        range[1] = allCompRanges[(comp * 2) + 1];
      }
      return;
    }
    rkey = COMPONENT_RANGE();

    // hasValidKey will update range to the cached value if it exists.
    if (!hasValidKey(info, PER_COMPONENT(), rkey, range, comp))
    {
      const bool computed = this->ComputeScalarRange(allCompRanges.data());
      if (computed)
      {
        // construct the keys and add them to the info object
        vtkInformationVector* infoVec = vtkInformationVector::New();
        info->Set(PER_COMPONENT(), infoVec);

        infoVec->SetNumberOfInformationObjects(this->NumberOfComponents);
        for (int i = 0; i < this->NumberOfComponents; ++i)
        {
          infoVec->GetInformationObject(i)->Set(rkey, allCompRanges.data() + (i * 2), 2);
        }
        infoVec->FastDelete();

        // update the range passed in since we have a valid range.
        range[0] = allCompRanges[comp * 2];
        range[1] = allCompRanges[(comp * 2) + 1];
      }
    }
  }
}

//------------------------------------------------------------------------------
// call modified on superclass
void vtkDataArray::Modified()
{
  if (this->HasInformation())
  {
    // Clear key-value pairs that are now out of date.
    vtkInformation* info = this->GetInformation();
    info->Remove(L2_NORM_RANGE());
    info->Remove(L2_NORM_FINITE_RANGE());
  }
  this->Superclass::Modified();
}
VTK_ABI_NAMESPACE_END

namespace
{

// Wrap the DoCompute[Scalar|Vector]Range calls for vtkArrayDispatch:
struct ScalarRangeDispatchWrapper
{
  bool Success;
  double* Range;
  const unsigned char* GhostArray;
  unsigned char GhostTypesToSkip;

  ScalarRangeDispatchWrapper(
    double* range, const unsigned char* ghostArray, unsigned char ghostTypesToSkip)
    : Success(false)
    , Range(range)
    , GhostArray(ghostArray)
    , GhostTypesToSkip(ghostTypesToSkip)
  {
  }

  template <typename ArrayT>
  void operator()(ArrayT* array)
  {
    this->Success = vtkDataArrayPrivate::DoComputeScalarRange(array, this->Range,
      vtkDataArrayPrivate::AllValues(), this->GhostArray, this->GhostTypesToSkip);
  }
};

struct VectorRangeDispatchWrapper
{
  bool Success;
  double* Range;
  const unsigned char* GhostArray;
  unsigned char GhostTypesToSkip;

  VectorRangeDispatchWrapper(
    double* range, const unsigned char* ghostArray, unsigned char ghostTypesToSkip)
    : Success(false)
    , Range(range)
    , GhostArray(ghostArray)
    , GhostTypesToSkip(ghostTypesToSkip)
  {
  }

  template <typename ArrayT>
  void operator()(ArrayT* array)
  {
    this->Success = vtkDataArrayPrivate::DoComputeVectorRange(array, this->Range,
      vtkDataArrayPrivate::AllValues(), this->GhostArray, this->GhostTypesToSkip);
  }
};

// Wrap the DoCompute[Scalar|Vector]Range calls for vtkArrayDispatch:
struct FiniteScalarRangeDispatchWrapper
{
  bool Success;
  double* Range;
  const unsigned char* GhostArray;
  unsigned char GhostTypesToSkip;

  FiniteScalarRangeDispatchWrapper(
    double* range, const unsigned char* ghostArray, unsigned char ghostTypesToSkip)
    : Success(false)
    , Range(range)
    , GhostArray(ghostArray)
    , GhostTypesToSkip(ghostTypesToSkip)
  {
  }

  template <typename ArrayT>
  void operator()(ArrayT* array)
  {
    this->Success = vtkDataArrayPrivate::DoComputeScalarRange(array, this->Range,
      vtkDataArrayPrivate::FiniteValues(), this->GhostArray, this->GhostTypesToSkip);
  }
};

struct FiniteVectorRangeDispatchWrapper
{
  bool Success;
  double* Range;
  const unsigned char* GhostArray;
  unsigned char GhostTypesToSkip;

  FiniteVectorRangeDispatchWrapper(
    double* range, const unsigned char* ghostArray, unsigned char ghostTypesToSkip)
    : Success(false)
    , Range(range)
    , GhostArray(ghostArray)
    , GhostTypesToSkip(ghostTypesToSkip)
  {
  }

  template <typename ArrayT>
  void operator()(ArrayT* array)
  {
    this->Success = vtkDataArrayPrivate::DoComputeVectorRange(array, this->Range,
      vtkDataArrayPrivate::FiniteValues(), this->GhostArray, this->GhostTypesToSkip);
  }
};

} // end anon namespace

VTK_ABI_NAMESPACE_BEGIN
//------------------------------------------------------------------------------
bool vtkDataArray::ComputeScalarRange(double* ranges)
{
  return this->ComputeScalarRange(ranges, nullptr);
}

//------------------------------------------------------------------------------
bool vtkDataArray::ComputeScalarRange(
  double* ranges, const unsigned char* ghosts, unsigned char ghostsToSkip)
{
  ScalarRangeDispatchWrapper worker(ranges, ghosts, ghostsToSkip);
  if (!vtkArrayDispatch::Dispatch::Execute(this, worker))
  {
    worker(this);
  }
  return worker.Success;
}

//------------------------------------------------------------------------------
bool vtkDataArray::ComputeVectorRange(double* ranges)
{
  return this->ComputeVectorRange(ranges, nullptr);
}

//------------------------------------------------------------------------------
bool vtkDataArray::ComputeVectorRange(
  double range[2], const unsigned char* ghosts, unsigned char ghostsToSkip)
{
  VectorRangeDispatchWrapper worker(range, ghosts, ghostsToSkip);
  if (!vtkArrayDispatch::Dispatch::Execute(this, worker))
  {
    worker(this);
  }
  return worker.Success;
}

//------------------------------------------------------------------------------
bool vtkDataArray::ComputeFiniteScalarRange(double* ranges)
{
  return this->ComputeFiniteScalarRange(ranges, nullptr);
}

//------------------------------------------------------------------------------
bool vtkDataArray::ComputeFiniteScalarRange(
  double* ranges, const unsigned char* ghosts, unsigned char ghostsToSkip)
{
  FiniteScalarRangeDispatchWrapper worker(ranges, ghosts, ghostsToSkip);
  if (!vtkArrayDispatch::Dispatch::Execute(this, worker))
  {
    worker(this);
  }
  return worker.Success;
}

//------------------------------------------------------------------------------
bool vtkDataArray::ComputeFiniteVectorRange(double range[2])
{
  return this->ComputeFiniteVectorRange(range, nullptr);
}

//------------------------------------------------------------------------------
bool vtkDataArray::ComputeFiniteVectorRange(
  double range[2], const unsigned char* ghosts, unsigned char ghostsToSkip)
{
  FiniteVectorRangeDispatchWrapper worker(range, ghosts, ghostsToSkip);
  if (!vtkArrayDispatch::Dispatch::Execute(this, worker))
  {
    worker(this);
  }
  return worker.Success;
}

//------------------------------------------------------------------------------
void vtkDataArray::GetDataTypeRange(double range[2])
{
  vtkDataArray::GetDataTypeRange(this->GetDataType(), range);
}

//------------------------------------------------------------------------------
double vtkDataArray::GetDataTypeMin()
{
  return vtkDataArray::GetDataTypeMin(this->GetDataType());
}

//------------------------------------------------------------------------------
double vtkDataArray::GetDataTypeMax()
{
  return vtkDataArray::GetDataTypeMax(this->GetDataType());
}

//------------------------------------------------------------------------------
void vtkDataArray::GetDataTypeRange(int type, double range[2])
{
  range[0] = vtkDataArray::GetDataTypeMin(type);
  range[1] = vtkDataArray::GetDataTypeMax(type);
}

//------------------------------------------------------------------------------
double vtkDataArray::GetDataTypeMin(int type)
{
  switch (type)
  {
    case VTK_BIT:
      return static_cast<double>(VTK_BIT_MIN);
    case VTK_SIGNED_CHAR:
      return static_cast<double>(VTK_SIGNED_CHAR_MIN);
    case VTK_UNSIGNED_CHAR:
      return static_cast<double>(VTK_UNSIGNED_CHAR_MIN);
    case VTK_CHAR:
      return static_cast<double>(VTK_CHAR_MIN);
    case VTK_UNSIGNED_SHORT:
      return static_cast<double>(VTK_UNSIGNED_SHORT_MIN);
    case VTK_SHORT:
      return static_cast<double>(VTK_SHORT_MIN);
    case VTK_UNSIGNED_INT:
      return static_cast<double>(VTK_UNSIGNED_INT_MIN);
    case VTK_INT:
      return static_cast<double>(VTK_INT_MIN);
    case VTK_UNSIGNED_LONG:
      return static_cast<double>(VTK_UNSIGNED_LONG_MIN);
    case VTK_LONG:
      return static_cast<double>(VTK_LONG_MIN);
    case VTK_UNSIGNED_LONG_LONG:
      return static_cast<double>(VTK_UNSIGNED_LONG_LONG_MIN);
    case VTK_LONG_LONG:
      return static_cast<double>(VTK_LONG_LONG_MIN);
    case VTK_FLOAT:
      return static_cast<double>(VTK_FLOAT_MIN);
    case VTK_DOUBLE:
      return static_cast<double>(VTK_DOUBLE_MIN);
    case VTK_ID_TYPE:
      return static_cast<double>(VTK_ID_MIN);
    default:
      return 0;
  }
}

//------------------------------------------------------------------------------
double vtkDataArray::GetDataTypeMax(int type)
{
  switch (type)
  {
    case VTK_BIT:
      return static_cast<double>(VTK_BIT_MAX);
    case VTK_SIGNED_CHAR:
      return static_cast<double>(VTK_SIGNED_CHAR_MAX);
    case VTK_UNSIGNED_CHAR:
      return static_cast<double>(VTK_UNSIGNED_CHAR_MAX);
    case VTK_CHAR:
      return static_cast<double>(VTK_CHAR_MAX);
    case VTK_UNSIGNED_SHORT:
      return static_cast<double>(VTK_UNSIGNED_SHORT_MAX);
    case VTK_SHORT:
      return static_cast<double>(VTK_SHORT_MAX);
    case VTK_UNSIGNED_INT:
      return static_cast<double>(VTK_UNSIGNED_INT_MAX);
    case VTK_INT:
      return static_cast<double>(VTK_INT_MAX);
    case VTK_UNSIGNED_LONG:
      return static_cast<double>(VTK_UNSIGNED_LONG_MAX);
    case VTK_LONG:
      return static_cast<double>(VTK_LONG_MAX);
    case VTK_UNSIGNED_LONG_LONG:
      return static_cast<double>(VTK_UNSIGNED_LONG_LONG_MAX);
    case VTK_LONG_LONG:
      return static_cast<double>(VTK_LONG_LONG_MAX);
    case VTK_FLOAT:
      return static_cast<double>(VTK_FLOAT_MAX);
    case VTK_DOUBLE:
      return static_cast<double>(VTK_DOUBLE_MAX);
    case VTK_ID_TYPE:
      return static_cast<double>(VTK_ID_MAX);
    default:
      return 1;
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::RemoveLastTuple()
{
  if (this->GetNumberOfTuples() > 0)
  {
    this->Resize(this->GetNumberOfTuples() - 1);
  }
}

//------------------------------------------------------------------------------
void vtkDataArray::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);

  const char* name = this->GetName();
  if (name)
  {
    os << indent << "Name: " << name << "\n";
  }
  else
  {
    os << indent << "Name: (none)\n";
  }
  os << indent << "Number Of Components: " << this->NumberOfComponents << "\n";
  os << indent << "Number Of Tuples: " << this->GetNumberOfTuples() << "\n";
  os << indent << "Size: " << this->Size << "\n";
  os << indent << "MaxId: " << this->MaxId << "\n";
  if (this->LookupTable)
  {
    os << indent << "Lookup Table:\n";
    this->LookupTable->PrintSelf(os, indent.GetNextIndent());
  }
  else
  {
    os << indent << "LookupTable: (none)\n";
  }
}
VTK_ABI_NAMESPACE_END
