#include <vtkm/cont/ArrayHandleCast.h>
#include <vtkm/cont/ArrayHandleGroupVecVariable.h>
#include <vtkm/cont/CellSetExplicit.h>

#include <vtkm/exec/CellFace.h>

#include <vtkm/Hash.h>

#include <vtkm/worklet/AverageByKey.h>
#include <vtkm/worklet/Keys.h>
#include <vtkm/worklet/ScatterCounting.h>
#include <vtkm/worklet/WorkletMapTopology.h>

#include <vtkm/filter/FilterDataSet.h>

#include <vtkm/cont/testing/MakeTestDataSet.h>
#include <vtkm/cont/testing/Testing.h>

//#define CHECK_COLLISIONS

namespace vtkm
{
namespace worklet
{

namespace
{

struct CountFacesWorklet : vtkm::worklet::WorkletVisitCellsWithPoints
{
  using ControlSignature = void(CellSetIn cellSet, FieldOut numFaces);
  using ExecutionSignature = _2(CellShape);
  using InputDomain = _1;

  template<typename CellShapeTag>
  VTKM_EXEC_CONT vtkm::IdComponent operator()(CellShapeTag cellShape) const
  {
    vtkm::IdComponent numFaces;
    vtkm::ErrorCode status = vtkm::exec::CellFaceNumberOfFaces(cellShape, numFaces);
    if (status != vtkm::ErrorCode::Success)
    {
      // There is an error in the cell. As good as it would be to return an
      // error, we probably don't want to invalidate the entire run if there
      // is just one malformed cell. Instead, ignore the cell.
      return 0;
    }
    return numFaces;
  }
};

class FaceHashesWorklet : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
  using ControlSignature = void(CellSetIn cellSet, FieldOut hashValues);
  using ExecutionSignature = _2(CellShape cellShape,
                                PointIndices globalPointIndices,
                                VisitIndex localFaceIndex);
  using InputDomain = _1;

  using ScatterType = vtkm::worklet::ScatterCounting;

  template<typename CellShapeTag, typename PointIndexVecType>
  VTKM_EXEC vtkm::HashType operator()(
    CellShapeTag cellShape,
    const PointIndexVecType& globalPointIndicesForCell,
    vtkm::IdComponent localFaceIndex) const
  {
    vtkm::Id3 canonicalId;
    vtkm::ErrorCode status = vtkm::exec::CellFaceCanonicalId(
      localFaceIndex, cellShape, globalPointIndicesForCell, canonicalId);
    if (status != vtkm::ErrorCode::Success)
    {
      this->RaiseError(vtkm::ErrorString(status));
      return vtkm::HashType(-1);
    }
//// PAUSE-EXAMPLE
#ifndef CHECK_COLLISIONS
    //// RESUME-EXAMPLE
    return vtkm::Hash(canonicalId);
//// PAUSE-EXAMPLE
#else  // ! CHECK_COLLISIONS                                                        \
       // Intentionally use a bad hash value to cause collisions to check to make   \
       // sure that collision resolution works.
    return vtkm::HashType(canonicalId[0]);
#endif // !CHECK_COLLISIONS
    //// RESUME-EXAMPLE
  }
};

class FaceHashCollisionsWorklet : public vtkm::worklet::WorkletReduceByKey
{
public:
  using ControlSignature = void(KeysIn keys,
                                WholeCellSetIn<> inputCells,
                                ValuesIn originCells,
                                ValuesIn originFaces,
                                ValuesOut localFaceIndices,
                                ReducedValuesOut numFaces);
  using ExecutionSignature = _6(_2 inputCells,
                                _3 originCells,
                                _4 originFaces,
                                _5 localFaceIndices);
  using InputDomain = _1;

  template<typename CellSetType,
           typename OriginCellsType,
           typename OriginFacesType,
           typename localFaceIndicesType>
  VTKM_EXEC vtkm::IdComponent operator()(
    const CellSetType& cellSet,
    const OriginCellsType& originCells,
    const OriginFacesType& originFaces,
    localFaceIndicesType& localFaceIndices) const
  {
    vtkm::IdComponent numFacesInHash = localFaceIndices.GetNumberOfComponents();

    // Sanity checks.
    VTKM_ASSERT(originCells.GetNumberOfComponents() == numFacesInHash);
    VTKM_ASSERT(originFaces.GetNumberOfComponents() == numFacesInHash);

    // Clear out localFaceIndices
    for (vtkm::IdComponent index = 0; index < numFacesInHash; ++index)
    {
      localFaceIndices[index] = -1;
    }

    // Count how many unique faces there are and create an id for each;
    vtkm::IdComponent numUniqueFaces = 0;
    for (vtkm::IdComponent firstFaceIndex = 0; firstFaceIndex < numFacesInHash;
         ++firstFaceIndex)
    {
      if (localFaceIndices[firstFaceIndex] == -1)
      {
        vtkm::IdComponent faceId = numUniqueFaces;
        localFaceIndices[firstFaceIndex] = faceId;
        // Find all matching faces.
        vtkm::Id firstCellIndex = originCells[firstFaceIndex];
        vtkm::Id3 canonicalFaceId;
        vtkm::exec::CellFaceCanonicalId(originFaces[firstFaceIndex],
                                        cellSet.GetCellShape(firstCellIndex),
                                        cellSet.GetIndices(firstCellIndex),
                                        canonicalFaceId);
        for (vtkm::IdComponent laterFaceIndex = firstFaceIndex + 1;
             laterFaceIndex < numFacesInHash;
             ++laterFaceIndex)
        {
          vtkm::Id laterCellIndex = originCells[laterFaceIndex];
          vtkm::Id3 otherCanonicalFaceId;
          vtkm::exec::CellFaceCanonicalId(originFaces[laterFaceIndex],
                                          cellSet.GetCellShape(laterCellIndex),
                                          cellSet.GetIndices(laterCellIndex),
                                          otherCanonicalFaceId);
          if (canonicalFaceId == otherCanonicalFaceId)
          {
            localFaceIndices[laterFaceIndex] = faceId;
          }
        }
        ++numUniqueFaces;
      }
    }

    return numUniqueFaces;
  }
};

////
//// BEGIN-EXAMPLE GenerateMeshVariableShapeCountPointsInFace.cxx
////
class CountPointsInFaceWorklet : public vtkm::worklet::WorkletReduceByKey
{
public:
  using ControlSignature = void(KeysIn keys,
                                WholeCellSetIn<> inputCells,
                                ValuesIn originCells,
                                ValuesIn originFaces,
                                ValuesIn localFaceIndices,
                                ReducedValuesOut faceShape,
                                ReducedValuesOut numPointsInEachFace);
  using ExecutionSignature = void(_2 inputCells,
                                  _3 originCell,
                                  _4 originFace,
                                  _5 localFaceIndices,
                                  VisitIndex localFaceIndex,
                                  _6 faceShape,
                                  _7 numPointsInFace);
  using InputDomain = _1;

  using ScatterType = vtkm::worklet::ScatterCounting;

  template<typename CellSetType,
           typename OriginCellsType,
           typename OriginFacesType,
           typename LocalFaceIndicesType>
  VTKM_EXEC void operator()(const CellSetType& cellSet,
                            const OriginCellsType& originCells,
                            const OriginFacesType& originFaces,
                            const LocalFaceIndicesType& localFaceIndices,
                            vtkm::IdComponent localFaceIndex,
                            vtkm::UInt8& faceShape,
                            vtkm::IdComponent& numPointsInFace) const
  {
    // Find the first face that matches the index given.
    for (vtkm::IdComponent faceIndex = 0;; ++faceIndex)
    {
      if (localFaceIndices[faceIndex] == localFaceIndex)
      {
        vtkm::Id cellIndex = originCells[faceIndex];
        vtkm::exec::CellFaceShape(
          originFaces[faceIndex], cellSet.GetCellShape(cellIndex), faceShape);
        vtkm::exec::CellFaceNumberOfPoints(
          originFaces[faceIndex], cellSet.GetCellShape(cellIndex), numPointsInFace);
        break;
      }
    }
  }
};
////
//// END-EXAMPLE GenerateMeshVariableShapeCountPointsInFace.cxx
////

////
//// BEGIN-EXAMPLE GenerateMeshVariableShapeGenIndices.cxx
////
class FaceIndicesWorklet : public vtkm::worklet::WorkletReduceByKey
{
public:
  using ControlSignature = void(KeysIn keys,
                                WholeCellSetIn<> inputCells,
                                ValuesIn originCells,
                                ValuesIn originFaces,
                                ValuesIn localFaceIndices,
                                ReducedValuesOut connectivityOut);
  using ExecutionSignature = void(_2 inputCells,
                                  _3 originCell,
                                  _4 originFace,
                                  _5 localFaceIndices,
                                  VisitIndex localFaceIndex,
                                  _6 connectivityOut);
  using InputDomain = _1;

  using ScatterType = vtkm::worklet::ScatterCounting;

  template<typename CellSetType,
           typename OriginCellsType,
           typename OriginFacesType,
           typename LocalFaceIndicesType,
           typename ConnectivityVecType>
  VTKM_EXEC void operator()(const CellSetType& cellSet,
                            const OriginCellsType& originCells,
                            const OriginFacesType& originFaces,
                            const LocalFaceIndicesType& localFaceIndices,
                            vtkm::IdComponent localFaceIndex,
                            ConnectivityVecType& connectivityOut) const
  {
    // Find the first face that matches the index given and return it.
    for (vtkm::IdComponent faceIndex = 0;; ++faceIndex)
    {
      if (localFaceIndices[faceIndex] == localFaceIndex)
      {
        vtkm::Id cellIndex = originCells[faceIndex];
        vtkm::IdComponent faceInCellIndex = originFaces[faceIndex];
        auto cellShape = cellSet.GetCellShape(cellIndex);
        vtkm::IdComponent numPointsInFace = connectivityOut.GetNumberOfComponents();

        auto globalPointIndicesForCell = cellSet.GetIndices(cellIndex);
        for (vtkm::IdComponent localPointI = 0; localPointI < numPointsInFace;
             ++localPointI)
        {
          vtkm::IdComponent pointInCellIndex;
          vtkm::exec::CellFaceLocalIndex(
            localPointI, faceInCellIndex, cellShape, pointInCellIndex);
          connectivityOut[localPointI] = globalPointIndicesForCell[pointInCellIndex];
        }

        break;
      }
    }
  }
};
////
//// END-EXAMPLE GenerateMeshVariableShapeGenIndices.cxx
////

class AverageCellFacesFieldWorklet : public vtkm::worklet::WorkletReduceByKey
{
public:
  using ControlSignature = void(KeysIn keys,
                                ValuesIn inFieldValues,
                                ValuesIn localFaceIndices,
                                ReducedValuesOut averagedField);
  using ExecutionSignature = _4(_2 inFieldValues,
                                _3 localFaceIndices,
                                VisitIndex localFaceIndex);
  using InputDomain = _1;

  using ScatterType = vtkm::worklet::ScatterCounting;

  template<typename InFieldValuesType, typename LocalFaceIndicesType>
  VTKM_EXEC typename InFieldValuesType::ComponentType operator()(
    const InFieldValuesType& inFieldValues,
    const LocalFaceIndicesType& localFaceIndices,
    vtkm::IdComponent localFaceIndex) const
  {
    using FieldType = typename InFieldValuesType::ComponentType;

    FieldType averageField = FieldType(0);
    vtkm::IdComponent numValues = 0;
    for (vtkm::IdComponent reduceIndex = 0;
         reduceIndex < inFieldValues.GetNumberOfComponents();
         ++reduceIndex)
    {
      if (localFaceIndices[reduceIndex] == localFaceIndex)
      {
        FieldType fieldValue = inFieldValues[reduceIndex];
        averageField = averageField + fieldValue;
        ++numValues;
      }
    }
    VTKM_ASSERT(numValues > 0);
    return static_cast<FieldType>(averageField / numValues);
  }
};

} // anonymous namespace

} // namespace worklet
} // namespace vtkm

namespace vtkm
{
namespace filter
{

//// PAUSE-EXAMPLE
namespace
{

//// RESUME-EXAMPLE
class ExtractFaces : public vtkm::filter::FilterDataSet<ExtractFaces>
{
public:
  template<typename Policy>
  VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& inData,
                                          vtkm::filter::PolicyBase<Policy> policy);

  template<typename T, typename StorageType, typename Policy>
  VTKM_CONT bool DoMapField(vtkm::cont::DataSet& result,
                            const vtkm::cont::ArrayHandle<T, StorageType>& input,
                            const vtkm::filter::FieldMetadata& fieldMeta,
                            const vtkm::filter::PolicyBase<Policy>& policy);

private:
  vtkm::worklet::ScatterCounting::OutputToInputMapType OutputToInputCellMap;
  vtkm::worklet::Keys<vtkm::HashType> CellToFaceKeys;
  vtkm::cont::ArrayHandle<vtkm::IdComponent> LocalFaceIndices;
  std::shared_ptr<vtkm::worklet::ScatterCounting> HashCollisionScatter;
};

//// PAUSE-EXAMPLE
} // anonymous namespace
//// RESUME-EXAMPLE
} // namespace filter
} // namespace vtkm

namespace vtkm
{
namespace filter
{

//// PAUSE-EXAMPLE
namespace
{

//// RESUME-EXAMPLE
////
//// BEGIN-EXAMPLE GenerateMeshVariableShapeInvoke.cxx
////
template<typename Policy>
inline VTKM_CONT vtkm::cont::DataSet ExtractFaces::DoExecute(
  const vtkm::cont::DataSet& inData,
  vtkm::filter::PolicyBase<Policy> policy)
{

  auto inCellSet =
    vtkm::filter::ApplyPolicyCellSet(inData.GetCellSet(), policy, *this);

  // First, count the faces in each cell.
  vtkm::cont::ArrayHandle<vtkm::IdComponent> faceCounts;
  this->Invoke(vtkm::worklet::CountFacesWorklet{}, inCellSet, faceCounts);

  // Second, using these counts build a scatter that repeats a cell's visit
  // for each edge in the cell.
  vtkm::worklet::ScatterCounting scatter(faceCounts);
  this->OutputToInputCellMap =
    scatter.GetOutputToInputMap(inCellSet.GetNumberOfCells());
  vtkm::worklet::ScatterCounting::VisitArrayType outputToInputFaceMap =
    scatter.GetVisitArray(inCellSet.GetNumberOfCells());

  // Third, for each face, extract a hash.
  vtkm::cont::ArrayHandle<vtkm::HashType> hashValues;
  this->Invoke(vtkm::worklet::FaceHashesWorklet{}, scatter, inCellSet, hashValues);

  // Fourth, use a Keys object to combine all like hashes.
  this->CellToFaceKeys = vtkm::worklet::Keys<vtkm::HashType>(hashValues);

  // Fifth, use a reduce-by-key to collect like hash values, resolve collisions,
  // and count the number of unique faces associated with each hash.
  vtkm::cont::ArrayHandle<vtkm::IdComponent> numUniqueFacesInEachHash;
  this->Invoke(vtkm::worklet::FaceHashCollisionsWorklet{},
               this->CellToFaceKeys,
               inCellSet,
               this->OutputToInputCellMap,
               outputToInputFaceMap,
               this->LocalFaceIndices,
               numUniqueFacesInEachHash);

  // Sixth, use a reduce-by-key to count the number of points in each unique face.
  // Also identify the shape of each face.
  this->HashCollisionScatter.reset(
    new vtkm::worklet::ScatterCounting(numUniqueFacesInEachHash));

  vtkm::cont::CellSetExplicit<>::ShapesArrayType shapeArray;
  vtkm::cont::ArrayHandle<vtkm::IdComponent> numPointsInEachFace;

  this->Invoke(vtkm::worklet::CountPointsInFaceWorklet{},
               *this->HashCollisionScatter,
               this->CellToFaceKeys,
               inCellSet,
               this->OutputToInputCellMap,
               outputToInputFaceMap,
               this->LocalFaceIndices,
               shapeArray,
               numPointsInEachFace);

  // Seventh, convert the numPointsInEachFace array to an offsets array and use that
  // to create an ArrayHandleGroupVecVariable.
  ////
  //// BEGIN-EXAMPLE GenerateMeshVariableShapeOffsetsArray.cxx
  ////
  vtkm::cont::ArrayHandle<vtkm::Id> offsets;
  vtkm::Id connectivityArraySize;
  vtkm::cont::ConvertNumIndicesToOffsets(
    numPointsInEachFace, offsets, connectivityArraySize);

  vtkm::cont::CellSetExplicit<>::ConnectivityArrayType connectivityArray;
  connectivityArray.Allocate(connectivityArraySize);
  auto connectivityArrayVecs =
    vtkm::cont::make_ArrayHandleGroupVecVariable(connectivityArray, offsets);
  ////
  //// END-EXAMPLE GenerateMeshVariableShapeOffsetsArray.cxx
  ////

  // Eigth, use a reduce-by-key to extract indices for each unique face.
  this->Invoke(vtkm::worklet::FaceIndicesWorklet{},
               *this->HashCollisionScatter,
               this->CellToFaceKeys,
               inCellSet,
               this->OutputToInputCellMap,
               outputToInputFaceMap,
               this->LocalFaceIndices,
               connectivityArrayVecs);

  // Ninth, use the created connectivity array and others to build a cell set.
  vtkm::cont::CellSetExplicit<> outCellSet;
  outCellSet.Fill(
    inCellSet.GetNumberOfPoints(), shapeArray, connectivityArray, offsets);

  vtkm::cont::DataSet outData;

  outData.SetCellSet(outCellSet);

  for (vtkm::IdComponent coordSystemIndex = 0;
       coordSystemIndex < inData.GetNumberOfCoordinateSystems();
       ++coordSystemIndex)
  {
    outData.AddCoordinateSystem(inData.GetCoordinateSystem(coordSystemIndex));
  }

  return outData;
}
////
//// END-EXAMPLE GenerateMeshVariableShapeInvoke.cxx
////

////
//// BEGIN-EXAMPLE GenerateMeshVariableShapeMapCellField.cxx
////
template<typename T, typename StorageType, typename Policy>
inline VTKM_CONT bool ExtractFaces::DoMapField(
  vtkm::cont::DataSet& result,
  const vtkm::cont::ArrayHandle<T, StorageType>& inputArray,
  const vtkm::filter::FieldMetadata& fieldMeta,
  const vtkm::filter::PolicyBase<Policy>&)
{
  vtkm::cont::Field outputField;

  if (fieldMeta.IsPointField())
  {
    outputField = fieldMeta.AsField(inputArray); // pass through
  }
  else if (fieldMeta.IsCellField())
  {
    vtkm::cont::ArrayHandle<T> averageFieldArray;
    this->Invoke(vtkm::worklet::AverageCellFacesFieldWorklet{},
                 *this->HashCollisionScatter,
                 this->CellToFaceKeys,
                 vtkm::cont::make_ArrayHandlePermutation(this->OutputToInputCellMap,
                                                         inputArray),
                 this->LocalFaceIndices,
                 averageFieldArray);

    outputField = fieldMeta.AsField(averageFieldArray);
  }
  else
  {
    return false;
  }

  result.AddField(outputField);

  return true;
}
////
//// END-EXAMPLE GenerateMeshVariableShapeMapCellField.cxx
////

//// PAUSE-EXAMPLE
} // anonymous namespace

//// RESUME-EXAMPLE
} // namespace filter
} // namespace vtkm

namespace
{

template<typename FaceVecLikeType>
bool IsFace(const FaceVecLikeType& faceIndices, const vtkm::Id3& expectedIndices)
{
  for (vtkm::IdComponent expectedIndex = 0; expectedIndex < 3; ++expectedIndex)
  {
    bool foundIndex = false;
    for (vtkm::IdComponent faceIndex = 0;
         faceIndex < faceIndices.GetNumberOfComponents();
         ++faceIndex)
    {
      if (expectedIndices[expectedIndex] == faceIndices[faceIndex])
      {
        foundIndex = true;
        break;
      }
    }
    if (!foundIndex)
    {
      return false;
    }
  }
  return true;
}

template<typename NumPointsInFaceType,
         typename ConnectivityPortalType,
         typename OffsetsPortalType>
vtkm::Id FindFace(const NumPointsInFaceType& numPointsInFace,
                  const ConnectivityPortalType& connectivity,
                  const OffsetsPortalType& offsets,
                  const vtkm::Id3& face)
{
  vtkm::Id faceIndex = -1;

  for (vtkm::Id offsetIndex = 0; offsetIndex < offsets.GetNumberOfValues();
       ++offsetIndex)
  {
    if (IsFace(vtkm::VecFromPortal<ConnectivityPortalType>(
                 connectivity,
                 numPointsInFace.Get(offsetIndex),
                 offsets.Get(offsetIndex)),
               face))
    {
      faceIndex = offsetIndex;
      break;
    }
  }

  VTKM_TEST_ASSERT(faceIndex >= 0, "Did not find expected face.");

  for (vtkm::Id offsetIndex = faceIndex + 1;
       offsetIndex < offsets.GetNumberOfValues();
       ++offsetIndex)
  {
    if (IsFace(vtkm::VecFromPortal<ConnectivityPortalType>(
                 connectivity,
                 numPointsInFace.Get(offsetIndex),
                 offsets.Get(offsetIndex)),
               face))
    {
      VTKM_TEST_FAIL("Face duplicated.");
    }
  }

  return faceIndex;
}

template<typename NumPointsInFaceType,
         typename ConnectivityPortalType,
         typename OffsetsPortalType,
         typename FieldPortalType>
void CheckFace(const NumPointsInFaceType& numPointsInFace,
               const ConnectivityPortalType& connectivity,
               const OffsetsPortalType& offsets,
               const FieldPortalType& field,
               const vtkm::Id3& face,
               const vtkm::Float32 expectedFieldValue)
{
  std::cout << "  Checking for face " << face << " with field value "
            << expectedFieldValue << std::endl;

  vtkm::Id faceIndex = FindFace(numPointsInFace, connectivity, offsets, face);

  vtkm::Float32 fieldValue = field.Get(faceIndex);
  VTKM_TEST_ASSERT(test_equal(expectedFieldValue, fieldValue), "Bad field value.");
}

void CheckOutput(const vtkm::cont::CellSetExplicit<>& cellSet,
                 const vtkm::cont::ArrayHandle<vtkm::Float32>& cellField)
{
  std::cout << "Num cells: " << cellSet.GetNumberOfCells() << std::endl;
  VTKM_TEST_ASSERT(cellSet.GetNumberOfCells() == 16, "Wrong # of cells.");

  auto numPointsPerFace = cellSet.GetNumIndicesArray(
    vtkm::TopologyElementTagCell(), vtkm::TopologyElementTagPoint());
  auto connectivity = cellSet.GetConnectivityArray(vtkm::TopologyElementTagCell(),
                                                   vtkm::TopologyElementTagPoint());
  auto offsetsExtended = cellSet.GetOffsetsArray(vtkm::TopologyElementTagCell(),
                                                 vtkm::TopologyElementTagPoint());
  auto offsetsExclusive = vtkm::cont::make_ArrayHandleView(
    offsetsExtended, 0, offsetsExtended.GetNumberOfValues() - 1);

  std::cout << "Offsets:" << std::endl;
  vtkm::cont::printSummary_ArrayHandle(offsetsExclusive, std::cout, true);
  std::cout << "Connectivity:" << std::endl;
  vtkm::cont::printSummary_ArrayHandle(connectivity, std::cout, true);

  std::cout << "Cell field:" << std::endl;
  vtkm::cont::printSummary_ArrayHandle(cellField, std::cout, true);

  auto nPointsP = numPointsPerFace.ReadPortal();
  auto connectivityP = connectivity.ReadPortal();
  auto offsetsP = offsetsExclusive.ReadPortal();
  auto fieldP = cellField.ReadPortal();
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(0, 1, 2), 100.1f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(0, 1, 5), 100.1f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(0, 3, 7), 100.1f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(6, 2, 3), 100.1f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(6, 2, 1), 105.05f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(6, 5, 4), 115.3f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(8, 1, 2), 110.0f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(8, 1, 5), 110.0f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(8, 6, 2), 110.0f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(8, 6, 5), 115.1f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(10, 6, 8), 120.2f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(10, 5, 8), 120.2f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(10, 6, 5), 125.35f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(9, 4, 7), 130.5f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(9, 4, 5), 130.5f);
  CheckFace(nPointsP, connectivityP, offsetsP, fieldP, vtkm::Id3(9, 4, 10), 130.5f);
}

// void TryWorklet()
//{
//  std::cout << std::endl << "Trying calling worklet." << std::endl;
//  vtkm::cont::DataSet inDataSet =
//      vtkm::cont::testing::MakeTestDataSet().Make3DExplicitDataSet5();
//  vtkm::cont::CellSetExplicit<> inCellSet;
//  inDataSet.GetCellSet().CopyTo(inCellSet);

//  vtkm::worklet::ExtractFaces worklet;
//  vtkm::cont::CellSetExplicit<> outCellSet = worklet.Run(inCellSet);
//  CheckOutput(outCellSet);
//}

void TryFilter()
{
  std::cout << std::endl << "Trying calling filter." << std::endl;
  vtkm::cont::DataSet inDataSet =
    vtkm::cont::testing::MakeTestDataSet().Make3DExplicitDataSet5();

  vtkm::filter::ExtractFaces filter;

  vtkm::cont::DataSet outDataSet = filter.Execute(inDataSet);

  vtkm::cont::CellSetExplicit<> outCellSet;
  outDataSet.GetCellSet().CopyTo(outCellSet);

  vtkm::cont::Field outCellField = outDataSet.GetField("cellvar");
  vtkm::cont::ArrayHandle<vtkm::Float32> outCellData;
  outCellField.GetData().AsArrayHandle(outCellData);

  CheckOutput(outCellSet, outCellData);
}

void DoTest()
{
  //  TryWorklet();
  TryFilter();
}

} // anonymous namespace

int GenerateMeshVariableShape(int argc, char* argv[])
{
  return vtkm::cont::testing::Testing::Run(DoTest, argc, argv);
}
