#include <vtkm/BinaryOperators.h>
#include <vtkm/BinaryPredicates.h>
#include <vtkm/UnaryPredicates.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/TryExecute.h>

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

#include <vector>

////
//// BEGIN-EXAMPLE DeviceAdapterAlgorithmPrototype.cxx
////
namespace vtkm
{
namespace cont
{

struct Algorithm;
}
} // namespace vtkm
////
//// END-EXAMPLE DeviceAdapterAlgorithmPrototype.cxx
////

namespace
{

template<typename T>
void CheckArray(const std::string& name,
                const vtkm::cont::ArrayHandle<T>& array,
                const std::vector<T>& expected)
{
  vtkm::Id numValues = array.GetNumberOfValues();
  auto portal = array.ReadPortal();
  std::cout << name << ": { ";
  for (vtkm::Id index = 0; index < numValues; ++index)
  {
    std::cout << portal.Get(index) << ", ";
  }
  std::cout << "}" << std::endl;

  VTKM_TEST_ASSERT(numValues == static_cast<vtkm::Id>(expected.size()),
                   "Array is wrong size.");

  for (vtkm::Id index = 0; index < numValues; ++index)
  {
    VTKM_TEST_ASSERT(test_equal(portal.Get(index), expected[std::size_t(index)]),
                     "Bad values.");
  }
}

#define CHECK_ARRAY(array, T, ...)                                                  \
  CheckArray(#array, array, std::vector<T>{ __VA_ARGS__ })

struct DoFunctor
{
  VTKM_CONT void DoBitFieldToUnorderedSet()
  {
    std::cout << "Testing BitFieldToUnorderedSet" << std::endl;
    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmBitFieldToUnorderedSet.cxx
    ////
    vtkm::cont::BitField bits;
    bits.Allocate(32);

    auto fillPortal = bits.WritePortal();
    fillPortal.SetWord(0, vtkm::UInt32(0xaa770011));

    vtkm::cont::ArrayHandle<vtkm::Id> output;
    auto setBits = vtkm::cont::Algorithm::BitFieldToUnorderedSet(bits, output);
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmBitFieldToUnorderedSet.cxx
    ////
    VTKM_TEST_ASSERT(setBits == 12, "Wrong number of Values.");
    vtkm::cont::Algorithm::Sort(output);
    CHECK_ARRAY(output, vtkm::Id, 0, 4, 16, 17, 18, 20, 21, 22, 25, 27, 29, 31);
  }

  VTKM_CONT void DoCopy()
  {
    std::cout << "Testing Copy" << std::endl;
    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmCopy.cxx
    ////
    vtkm::cont::ArrayHandleIndex input(12);

    vtkm::cont::ArrayHandle<vtkm::Int32> output;

    vtkm::cont::Algorithm::Copy(input, output);

    // output has { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmCopy.cxx
    ////
    CHECK_ARRAY(output, vtkm::Int32, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
  }

  VTKM_CONT void DoCopyIf()
  {
    std::cout << "Testing CopyIf" << std::endl;
    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmCopyIf.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> input =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });
    vtkm::cont::ArrayHandle<vtkm::UInt8> stencil =
      vtkm::cont::make_ArrayHandle<vtkm::UInt8>(
        { 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1 });

    vtkm::cont::ArrayHandle<vtkm::Int32> output;

    vtkm::cont::Algorithm::CopyIf(input, stencil, output);


    // output has { 0, 5, 3, 8, 3 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(output, vtkm::Int32, 0, 5, 3, 8, 3);
    //// RESUME-EXAMPLE

    struct LessThan5
    {
      VTKM_EXEC_CONT bool operator()(vtkm::Int32 x) const { return x < 5; }
    };

    vtkm::cont::Algorithm::CopyIf(input, input, output, LessThan5());

    // output has { 0, 1, 1, 4, 3, 3 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(output, vtkm::Int32, 0, 1, 1, 4, 3, 3);
    //// RESUME-EXAMPLE
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmCopyIf.cxx
    ////
  }

  VTKM_CONT void DoCopySubRange()
  {
    std::cout << "Testing CopySubRange" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmCopySubRange.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> input =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });

    vtkm::cont::ArrayHandle<vtkm::Int32> output;

    vtkm::cont::Algorithm::CopySubRange(input, 1, 7, output);

    // output has { 0, 1, 1, 5, 5, 4, 3 }
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmCopySubRange.cxx
    ////
    CHECK_ARRAY(output, vtkm::Int32, 0, 1, 1, 5, 5, 4, 3);
  }

  VTKM_CONT void DoCountSetBits()
  {
    std::cout << "Testing CountSetBits" << std::endl;
    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmCountSetBits.cxx
    ////
    vtkm::cont::BitField bits;
    bits.Allocate(32);

    auto fillPortal = bits.WritePortal();
    fillPortal.SetWord(0, vtkm::UInt32(0xaa770011));

    vtkm::cont::ArrayHandle<vtkm::Id> output;
    auto setBits = vtkm::cont::Algorithm::CountSetBits(bits);

    // Will return that there are 12 set bits
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmCountSetBits.cxx
    ////
    VTKM_TEST_ASSERT(setBits == 12, "Wrong number of Values.");
  }

  VTKM_CONT void DoFill()
  {
    std::cout << "Testing Fill" << std::endl;
    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmFill.cxx
    ////
    // Fill a BitField
    vtkm::cont::BitField bits;
    bits.Allocate(32);
    vtkm::cont::Algorithm::Fill(bits, true);
    //// PAUSE-EXAMPLE
    VTKM_TEST_ASSERT(vtkm::cont::Algorithm::CountSetBits(bits) == 32,
                     "Wrong number of Values.");
    //// RESUME-EXAMPLE
    // Will stamp the 8 bit word across 32 bits to result in bits = 0xf0f0f0f0
    vtkm::cont::Algorithm::Fill(bits, vtkm::UInt8(0xf0));
    //// PAUSE-EXAMPLE
    VTKM_TEST_ASSERT(vtkm::cont::Algorithm::CountSetBits(bits) == 16,
                     "Wrong number of Values.");
    //// RESUME-EXAMPLE
    vtkm::cont::Algorithm::Fill(bits, vtkm::UInt8(0xf0), 16);
    //// PAUSE-EXAMPLE
    VTKM_TEST_ASSERT(vtkm::cont::Algorithm::CountSetBits(bits) == 8,
                     "Wrong number of Values.");
    //// RESUME-EXAMPLE

    // Fill an ArrayHandle
    vtkm::cont::ArrayHandle<vtkm::Id> arrayHandle;
    arrayHandle.Allocate(10);
    vtkm::cont::Algorithm::Fill(arrayHandle, vtkm::Id(5));
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(arrayHandle, vtkm::Id, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5);
    //// RESUME-EXAMPLE
    vtkm::cont::Algorithm::Fill(arrayHandle, vtkm::Id(10), 5);
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmFill.cxx
    ////
    CHECK_ARRAY(arrayHandle, vtkm::Id, 10, 10, 10, 10, 10);
  }


  VTKM_CONT void DoLowerBounds()
  {
    std::cout << "Testing LowerBounds" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmLowerBounds.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> sorted =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 0, 1, 1, 3, 3, 4, 5, 5, 7, 7, 8, 9 });
    vtkm::cont::ArrayHandle<vtkm::Int32> values =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });

    vtkm::cont::ArrayHandle<vtkm::Id> output;

    vtkm::cont::Algorithm::LowerBounds(sorted, values, output);

    // output has { 8, 0, 1, 1, 6, 6, 5, 3, 8, 10, 11, 3 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(output, vtkm::Id, 8, 0, 1, 1, 6, 6, 5, 3, 8, 10, 11, 3);
    //// RESUME-EXAMPLE

    vtkm::cont::ArrayHandle<vtkm::Int32> reverseSorted =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 9, 8, 7, 7, 5, 5, 4, 3, 3, 1, 1, 0 });

    vtkm::cont::Algorithm::LowerBounds(
      reverseSorted, values, output, vtkm::SortGreater());

    // output has { 2, 11, 9, 9, 4, 4, 6, 7, 2, 1, 0, 7 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(output, vtkm::Id, 2, 11, 9, 9, 4, 4, 6, 7, 2, 1, 0, 7);
    //// RESUME-EXAMPLE
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmLowerBounds.cxx
    ////
  }

  VTKM_CONT void DoReduce()
  {
    std::cout << "Testing Reduce" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmReduce.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Id> input =
      vtkm::cont::make_ArrayHandle<vtkm::Id>({ 5, 1, 1, 6 });

    vtkm::Id sum = vtkm::cont::Algorithm::Reduce(input, 0);

    // sum is 13

    vtkm::Id product = vtkm::cont::Algorithm::Reduce(input, 1, vtkm::Multiply());
    // product is 30
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmReduce.cxx
    ////

    VTKM_TEST_ASSERT(sum == 13, "Sum wrong.");
    VTKM_TEST_ASSERT(product == 30, "Product wrong.");
  }

  VTKM_CONT void DoReduceByKey()
  {
    std::cout << "Testing ReduceByKey" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmReduceByKey.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Id> keys =
      vtkm::cont::make_ArrayHandle<vtkm::Id>({ 0, 0, 3, 3, 3, 3, 5, 6, 6, 6, 6, 6 });
    vtkm::cont::ArrayHandle<vtkm::Int32> input =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });

    vtkm::cont::ArrayHandle<vtkm::Id> uniqueKeys;
    vtkm::cont::ArrayHandle<vtkm::Int32> sums;

    vtkm::cont::Algorithm::ReduceByKey(keys, input, uniqueKeys, sums, vtkm::Add());

    // uniqueKeys is { 0, 3, 5, 6 }
    // sums is { 7, 12, 4, 30 }

    vtkm::cont::ArrayHandle<vtkm::Int32> products;

    vtkm::cont::Algorithm::ReduceByKey(
      keys, input, uniqueKeys, products, vtkm::Multiply());

    // products is { 0, 25, 4, 4536 }
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmReduceByKey.cxx
    ////

    CHECK_ARRAY(uniqueKeys, vtkm::Id, 0, 3, 5, 6);
    CHECK_ARRAY(sums, vtkm::Int32, 7, 12, 4, 30);
    CHECK_ARRAY(products, vtkm::Int32, 0, 25, 4, 4536);
  }

  VTKM_CONT void DoScanInclusive()
  {
    std::cout << "Testing ScanInclusive" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmScanInclusive.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> input =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });

    vtkm::cont::ArrayHandle<vtkm::Int32> runningSum;

    vtkm::cont::Algorithm::ScanInclusive(input, runningSum);

    // runningSum is { 7, 7, 8, 9, 14, 19, 23, 26, 33, 41, 50, 53 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(runningSum, vtkm::Int32, 7, 7, 8, 9, 14, 19, 23, 26, 33, 41, 50, 53);
    //// RESUME-EXAMPLE

    vtkm::cont::ArrayHandle<vtkm::Int32> runningMax;

    vtkm::cont::Algorithm::ScanInclusive(input, runningMax, vtkm::Maximum());

    // runningMax is { 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 9, 9 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(runningMax, vtkm::Int32, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 9, 9);
    //// RESUME-EXAMPLE
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmScanInclusive.cxx
    ////
  }

  VTKM_CONT void DoScanInclusiveByKey()
  {
    std::cout << "Testing ScanInclusiveByKey" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmScanInclusiveByKey.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Id> keys =
      vtkm::cont::make_ArrayHandle<vtkm::Id>({ 0, 0, 3, 3, 3, 3, 5, 6, 6, 6, 6, 6 });
    vtkm::cont::ArrayHandle<vtkm::Int32> input =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });

    vtkm::cont::ArrayHandle<vtkm::Int32> runningSums;

    vtkm::cont::Algorithm::ScanInclusiveByKey(keys, input, runningSums);

    // runningSums is { 7, 7, 1, 2, 7, 12, 4, 3, 10, 18, 27, 30 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(runningSums, vtkm::Int32, 7, 7, 1, 2, 7, 12, 4, 3, 10, 18, 27, 30);
    //// RESUME-EXAMPLE

    vtkm::cont::ArrayHandle<vtkm::Int32> runningMaxes;

    vtkm::cont::Algorithm::ScanInclusiveByKey(
      keys, input, runningMaxes, vtkm::Maximum());

    // runningMax is { 7, 7, 1, 1, 5, 5, 4, 3, 7, 8, 9, 9 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(runningMaxes, vtkm::Int32, 7, 7, 1, 1, 5, 5, 4, 3, 7, 8, 9, 9);
    //// RESUME-EXAMPLE
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmScanInclusiveByKey.cxx
    ////
  }

  VTKM_CONT void DoScanExclusive()
  {
    std::cout << "Testing ScanExclusive" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmScanExclusive.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> input =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });

    vtkm::cont::ArrayHandle<vtkm::Int32> runningSum;

    vtkm::cont::Algorithm::ScanExclusive(input, runningSum);

    // runningSum is { 0, 7, 7, 8, 9, 14, 19, 23, 26, 33, 41, 50 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(runningSum, vtkm::Int32, 0, 7, 7, 8, 9, 14, 19, 23, 26, 33, 41, 50);
    //// RESUME-EXAMPLE

    vtkm::cont::ArrayHandle<vtkm::Int32> runningMax;

    vtkm::cont::Algorithm::ScanExclusive(input, runningMax, vtkm::Maximum(), -1);

    // runningMax is { -1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 9 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(runningMax, vtkm::Int32, -1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 9);
    //// RESUME-EXAMPLE
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmScanExclusive.cxx
    ////
  }

  VTKM_CONT void DoScanExclusiveByKey()
  {
    std::cout << "Testing ScanExclusiveByKey" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmScanExclusiveByKey.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Id> keys =
      vtkm::cont::make_ArrayHandle<vtkm::Id>({ 0, 0, 3, 3, 3, 3, 5, 6, 6, 6, 6, 6 });
    vtkm::cont::ArrayHandle<vtkm::Int32> input =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });

    vtkm::cont::ArrayHandle<vtkm::Int32> runningSums;

    vtkm::cont::Algorithm::ScanExclusiveByKey(keys, input, runningSums);

    // runningSums is { 0, 7, 0, 1, 2, 7, 0, 0, 3, 10, 18, 27 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(runningSums, vtkm::Int32, 0, 7, 0, 1, 2, 7, 0, 0, 3, 10, 18, 27);
    //// RESUME-EXAMPLE

    vtkm::cont::ArrayHandle<vtkm::Int32> runningMaxes;

    vtkm::cont::Algorithm::ScanExclusiveByKey(
      keys, input, runningMaxes, -1, vtkm::Maximum());

    // runningMax is { -1, 7, -1, 1, 1, 5, -1, -1, 3, 7, 8, 9 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(runningMaxes, vtkm::Int32, -1, 7, -1, 1, 1, 5, -1, -1, 3, 7, 8, 9);
    //// RESUME-EXAMPLE
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmScanExclusiveByKey.cxx
    ////
  }

  VTKM_CONT void DoScanExtended()
  {
    std::cout << "Testing ScanExtended" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmScanExtended.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> input =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });

    vtkm::cont::ArrayHandle<vtkm::Int32> runningSum;
    vtkm::cont::Algorithm::ScanExtended(input, runningSum);

    // runningSum is { 0, 7, 7, 8, 9, 14, 19, 23, 26, 33, 41, 50, 53 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(
      runningSum, vtkm::Int32, 0, 7, 7, 8, 9, 14, 19, 23, 26, 33, 41, 50, 53);
    //// RESUME-EXAMPLE

    vtkm::cont::ArrayHandle<vtkm::Int32> runningMax;
    vtkm::cont::Algorithm::ScanExtended(input, runningMax, vtkm::Maximum(), -1);

    // runningMax is { -1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 9, 9 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(runningMax, vtkm::Int32, -1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 9, 9);
    //// RESUME-EXAMPLE
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmScanExtended.cxx
    ////
  }

  VTKM_CONT void DoSort()
  {
    std::cout << "Testing Sort" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmSort.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> array =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });

    vtkm::cont::Algorithm::Sort(array);

    // array has { 0, 1, 1, 3, 3, 4, 5, 5, 7, 7, 8, 9 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(array, vtkm::Int32, 0, 1, 1, 3, 3, 4, 5, 5, 7, 7, 8, 9);
    //// RESUME-EXAMPLE

    vtkm::cont::Algorithm::Sort(array, vtkm::SortGreater());

    // array has { 9, 8, 7, 7, 5, 5, 4, 3, 3, 1, 1, 0 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(array, vtkm::Int32, 9, 8, 7, 7, 5, 5, 4, 3, 3, 1, 1, 0);
    //// RESUME-EXAMPLE
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmSort.cxx
    ////
  }

  VTKM_CONT void DoSortByKey()
  {
    std::cout << "Testing SortByKey" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmSortByKey.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> keys =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>({ 7, 0, 1, 5, 4, 8, 9, 3 });
    vtkm::cont::ArrayHandle<vtkm::Id> values =
      vtkm::cont::make_ArrayHandle<vtkm::Id>({ 0, 1, 2, 3, 4, 5, 6, 7 });

    vtkm::cont::Algorithm::SortByKey(keys, values);

    // keys has   { 0, 1, 3, 4, 5, 7, 8, 9 }
    // values has { 1, 2, 7, 4, 3, 0, 5, 6 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(keys, vtkm::Int32, 0, 1, 3, 4, 5, 7, 8, 9);
    CHECK_ARRAY(values, vtkm::Id, 1, 2, 7, 4, 3, 0, 5, 6);
    //// RESUME-EXAMPLE

    vtkm::cont::Algorithm::SortByKey(keys, values, vtkm::SortGreater());

    // keys has   { 9, 8, 7, 5, 4, 3, 1, 0 }
    // values has { 6, 5, 0, 3, 4, 7, 2, 1 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(keys, vtkm::Int32, 9, 8, 7, 5, 4, 3, 1, 0);
    CHECK_ARRAY(values, vtkm::Id, 6, 5, 0, 3, 4, 7, 2, 1);
    //// RESUME-EXAMPLE
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmSortByKey.cxx
    ////
  }

  VTKM_CONT void DoTransform()
  {
    std::cout << "Testing Transform" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmTransform.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> input1 =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
    vtkm::cont::ArrayHandle<vtkm::Int32> input2 =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>({ 2, 3, 4, 5, 6, 7, 8, 9, 10 });

    vtkm::cont::ArrayHandle<vtkm::Int32> output;
    vtkm::cont::Algorithm::Transform(input1, input2, output, vtkm::Sum());

    // output is { 3, 5, 7, 9, 11, 13, 15, 17, 19 }
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmTransform.cxx
    ////
    CHECK_ARRAY(output, vtkm::Int32, 3, 5, 7, 9, 11, 13, 15, 17, 19);
  }

  VTKM_CONT void DoUnique()
  {
    std::cout << "Testing Unqiue" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmUnique.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> values =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 0, 1, 1, 3, 3, 4, 5, 5, 7, 7, 7, 9 });

    vtkm::cont::Algorithm::Unique(values);

    // values has {0, 1, 3, 4, 5, 7, 9}

    vtkm::cont::ArrayHandle<vtkm::Float64> fvalues =
      vtkm::cont::make_ArrayHandle<vtkm::Float64>(
        { 0.0, 0.001, 0.0, 1.5, 1.499, 2.0 });

    struct AlmostEqualFunctor
    {
      VTKM_EXEC_CONT bool operator()(vtkm::Float64 x, vtkm::Float64 y) const
      {
        return (vtkm::Abs(x - y) < 0.1);
      }
    };

    vtkm::cont::Algorithm::Unique(fvalues, AlmostEqualFunctor());

    // values has {0.0, 1.5, 2.0}
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmUnique.cxx
    ////

    CHECK_ARRAY(values, vtkm::Int32, 0, 1, 3, 4, 5, 7, 9);
    CHECK_ARRAY(fvalues, vtkm::Float64, 0.0, 1.5, 2.0);
  }

  VTKM_CONT void DoUpperBounds()
  {
    std::cout << "Testing UpperBounds" << std::endl;

    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmUpperBounds.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> sorted =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 0, 1, 1, 3, 3, 4, 5, 5, 7, 7, 8, 9 });
    vtkm::cont::ArrayHandle<vtkm::Int32> values =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });

    vtkm::cont::ArrayHandle<vtkm::Id> output;

    vtkm::cont::Algorithm::UpperBounds(sorted, values, output);

    // output has { 10, 1, 3, 3, 8, 8, 6, 5, 10, 11, 12, 5 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(output, vtkm::Id, 10, 1, 3, 3, 8, 8, 6, 5, 10, 11, 12, 5);
    //// RESUME-EXAMPLE

    vtkm::cont::ArrayHandle<vtkm::Int32> reverseSorted =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 9, 8, 7, 7, 5, 5, 4, 3, 3, 1, 1, 0 });

    vtkm::cont::Algorithm::UpperBounds(
      reverseSorted, values, output, vtkm::SortGreater());

    // output has { 4, 12, 11, 11, 6, 6, 7, 9, 4, 2, 1, 9 }
    //// PAUSE-EXAMPLE
    CHECK_ARRAY(output, vtkm::Id, 4, 12, 11, 11, 6, 6, 7, 9, 4, 2, 1, 9);
    //// RESUME-EXAMPLE
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmUpperBounds.cxx
    ////
  }

  VTKM_CONT void DoDeviceAdapter()
  {
    std::cout << "Testing Copy" << std::endl;
    ////
    //// BEGIN-EXAMPLE DeviceAdapterAlgorithmDeviceAdapter.cxx
    ////
    vtkm::cont::ArrayHandle<vtkm::Int32> input =
      vtkm::cont::make_ArrayHandle<vtkm::Int32>(
        { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 });

    vtkm::cont::ArrayHandle<vtkm::Int32> output_no_device_specified;

    vtkm::cont::ArrayHandle<vtkm::Int32> output_device_specified;

    vtkm::cont::Algorithm::Copy(input, output_no_device_specified);

    //optional we can pass the device or int id number
    vtkm::cont::Algorithm::Copy(
      vtkm::cont::DeviceAdapterTagSerial(), input, output_device_specified);

    // output has { 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3 }
    ////
    //// END-EXAMPLE DeviceAdapterAlgorithmDeviceAdapter.cxx
    ////
    CHECK_ARRAY(
      output_no_device_specified, vtkm::Int32, 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3);
    CHECK_ARRAY(
      output_device_specified, vtkm::Int32, 7, 0, 1, 1, 5, 5, 4, 3, 7, 8, 9, 3);
  }

  VTKM_CONT void Run()
  {
    this->DoBitFieldToUnorderedSet();
    this->DoCopy();
    this->DoCopyIf();
    this->DoCopySubRange();
    this->DoCountSetBits();
    this->DoFill();
    this->DoLowerBounds();
    this->DoReduce();
    this->DoReduceByKey();
    this->DoScanInclusive();
    this->DoScanInclusiveByKey();
    this->DoScanExclusive();
    this->DoScanExclusiveByKey();
    this->DoScanExtended();
    this->DoSort();
    this->DoSortByKey();
    this->DoTransform();
    this->DoUnique();
    this->DoUpperBounds();
    this->DoDeviceAdapter();
  }
};

void Test()
{
  DoFunctor functor;
  functor.Run();
}

} // anonymous namespace

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