#include <vtkm/BinaryOperators.h>
#include <vtkm/BinaryPredicates.h>
#include <vtkm/UnaryPredicates.h>

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

namespace
{

////
//// BEGIN-EXAMPLE CustomUnaryPredicateImplementation.cxx
////
struct PowerOfTwo
{
  VTKM_EXEC_CONT bool operator()(const vtkm::Id& x) const
  {
    if (x <= 0)
    {
      return false;
    }
    vtkm::BitwiseAnd bitwise_and;
    return bitwise_and(x, vtkm::Id(x - 1)) == 0;
  }
};
////
//// END-EXAMPLE CustomUnaryPredicateImplementation.cxx
////

void Test()
{
  ////
  //// BEGIN-EXAMPLE CustomUnaryPredicateUsage.cxx
  ////
  PowerOfTwo power_of_two;

  bool powerOfTwo = power_of_two(vtkm::Id(4)); // returns true
  //// PAUSE-EXAMPLE
  VTKM_TEST_ASSERT(powerOfTwo == true, "PowerOfTwo wrong");
  //// RESUME-EXAMPLE
  powerOfTwo = power_of_two(vtkm::Id(5)); // returns false
  ////
  //// END-EXAMPLE CustomUnaryPredicateUsage.cxx
  ////
  VTKM_TEST_ASSERT(powerOfTwo == false, "PowerOfTwo wrong");

  ////
  //// BEGIN-EXAMPLE UnaryPredicates.cxx
  ////
  vtkm::IsZeroInitialized zero_initialized;
  vtkm::NotZeroInitialized not_zero_initialized;
  vtkm::LogicalNot logical_not;

  bool zeroed = zero_initialized(vtkm::TypeTraits<vtkm::Id>::ZeroInitialization());
  bool notZeroed = not_zero_initialized(vtkm::Id(1));
  bool logicalNot = logical_not(false);
  ////
  //// END-EXAMPLE UnaryPredicates.cxx
  ////
  VTKM_TEST_ASSERT(zeroed == true, "IsZeroInitialized wrong");
  VTKM_TEST_ASSERT(notZeroed = true, "NotZeroInitialized wrong");
  VTKM_TEST_ASSERT(logicalNot = true, "LogicalNot wrong");

  ////
  //// BEGIN-EXAMPLE BinaryPredicates.cxx
  ////
  vtkm::Equal equal_;
  vtkm::NotEqual not_equal;
  vtkm::SortLess sort_less;
  vtkm::SortGreater sort_greater;
  vtkm::LogicalAnd logical_and;
  vtkm::LogicalOr logical_or;

  bool equal = equal_(vtkm::Id(1), vtkm::Id(1));
  bool notEqual = not_equal(vtkm::Id(1), vtkm::Id(2));
  bool sortLess = sort_less(vtkm::Id(1), vtkm::Id(2));
  bool sortGreater = sort_greater(vtkm::Id(2), vtkm::Id(1));
  bool logicalAnd = logical_and(true, true);
  bool logicalOr = logical_or(true, false);
  ////
  //// END-EXAMPLE BinaryPredicates.cxx
  ////
  VTKM_TEST_ASSERT(equal == true, "Equal wrong");
  VTKM_TEST_ASSERT(notEqual == true, "NotEqual wrong");
  VTKM_TEST_ASSERT(sortLess == true, "SortLess wrong");
  VTKM_TEST_ASSERT(sortGreater == true, "SortGreater wrong");
  VTKM_TEST_ASSERT(logicalAnd == true, "LogicalAnd wrong");
  VTKM_TEST_ASSERT(logicalOr == true, "LogicalOr wrong");

  ////
  //// BEGIN-EXAMPLE BinaryOperators.cxx
  ////
  vtkm::Sum sum_;
  vtkm::Product product_;
  vtkm::Maximum maximum_;
  vtkm::Minimum minimum_;
  vtkm::MinAndMax<vtkm::Id> min_and_max;
  vtkm::BitwiseAnd bitwise_and;
  vtkm::BitwiseOr bitwise_or;
  vtkm::BitwiseXor bitwise_xor;

  vtkm::Id sum = sum_(vtkm::Id(1), vtkm::Id(1));
  vtkm::Id product = product_(vtkm::Id(2), vtkm::Id(2));
  vtkm::Id max = maximum_(vtkm::Id(1), vtkm::Id(2));
  vtkm::Id min = minimum_(vtkm::Id(1), vtkm::Id(2));
  vtkm::Id2 minAndMax = min_and_max(vtkm::Id(3), vtkm::Id(4));
  vtkm::Id bitwiseAnd = bitwise_and(vtkm::Id(1), vtkm::Id(3));
  vtkm::Id bitwiseOr = bitwise_or(vtkm::Id(1), vtkm::Id(2));
  vtkm::Id bitwiseXor = bitwise_xor(vtkm::Id(7), vtkm::Id(4));
  ////
  //// END-EXAMPLE BinaryOperators.cxx
  ////

  VTKM_TEST_ASSERT(sum == vtkm::Id(2), "Sum wrong");
  VTKM_TEST_ASSERT(product == vtkm::Id(4), "Product wrong");
  VTKM_TEST_ASSERT(max == vtkm::Id(2), "Maximum wrong");
  VTKM_TEST_ASSERT(min == vtkm::Id(1), "Minimum wrong");
  VTKM_TEST_ASSERT(bitwiseAnd == vtkm::Id(1), "BitwiseAnd wrong");
  VTKM_TEST_ASSERT(bitwiseOr == vtkm::Id(3), "BitwiseOr wrong");
  VTKM_TEST_ASSERT(bitwiseXor == vtkm::Id(3), "BitwiseXor wrong");
  VTKM_TEST_ASSERT(test_equal(minAndMax, vtkm::Id2(3, 4)), "MinAndMax wrong");
}

} // anonymous namespace

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