diff --git a/vtkm/cont/ArrayHandleSwizzle.h b/vtkm/cont/ArrayHandleSwizzle.h new file mode 100644 index 000000000..d91469645 --- /dev/null +++ b/vtkm/cont/ArrayHandleSwizzle.h @@ -0,0 +1,525 @@ +//============================================================================= +// +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +// +// Copyright 2017 Sandia Corporation. +// Copyright 2017 UT-Battelle, LLC. +// Copyright 2017 Los Alamos National Security. +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +// +//============================================================================= +#ifndef vtk_m_cont_ArrayHandleSwizzle_h +#define vtk_m_cont_ArrayHandleSwizzle_h + +#include +#include + +#include + +namespace vtkm +{ +namespace cont +{ +namespace internal +{ + +// If TestValue appears more than once in ComponentMap, IsUnique will be false, +// but true if TestValue is unique. +template +struct ComponentIsUnique; + +// Terminal case: +template +struct ComponentIsUnique +{ + const static bool IsUnique = TestValue != Head; +}; + +// Recursive case: +template +struct ComponentIsUnique +{ + using Next = ComponentIsUnique; + const static bool IsUnique = TestValue != Head && Next::IsUnique; +}; + +// Validate the component map. +// All elements must be (1) unique, (2) >= 0, and (3) < InputSize +template +struct ValidateComponentMap; + +// Terminal impl: +template +struct ValidateComponentMap +{ + static const bool Valid = Head >= 0 && Head < InputSize; +}; + +// Recursive impl: +template +struct ValidateComponentMap +{ + using Next = ValidateComponentMap; + static const bool IsUnique = ComponentIsUnique::IsUnique; + static const bool Valid = Head >= 0 && Head < InputSize && IsUnique && Next::Valid; +}; + +} // end namespace internal + +/// This class collects metadata for an ArrayHandleSwizzle. +template +struct ArrayHandleSwizzleTraits +{ + /// The number of elements in the ComponentMap. + static const vtkm::IdComponent COUNT = static_cast(sizeof...(ComponentMap)); + VTKM_STATIC_ASSERT_MSG(COUNT > 0, "Invalid ComponentMap: Cannot swizzle zero components."); + + /// A std::array containing the ComponentMap for runtime querying. + using RuntimeComponentMapType = std::array; + static VTKM_CONSTEXPR RuntimeComponentMapType GenerateRuntimeComponentMap() + { + return RuntimeComponentMapType{ { ComponentMap... } }; + } + + /// The ValueType of the ArrayHandleSwizzle's internal ArrayHandle. + using InputType = InputValueType; + + /// The VecTraits for InputType. + using InputTraits = VecTraits; + + using Validator = internal::ValidateComponentMap; + VTKM_STATIC_ASSERT_MSG(Validator::Valid, + "Invalid ComponentMap: Ids in ComponentMap must be unique, positive, and " + "less than the number of input components."); + + /// The ComponentType of the ArrayHandleSwizzle. + using ComponentType = typename InputTraits::ComponentType; + + /// The ValueType of the ArrayHandleSwizzle. + using OutputType = vtkm::Vec; + + // The VecTraits for OutputType. + using OutputTraits = VecTraits; + + /// If true, we use all components in the input vector. If false, we'll need + /// to make sure to preserve existing values on write. + static const bool ALL_COMPS_USED = InputTraits::NUM_COMPONENTS == COUNT; + +private: + template + struct GetImpl; + + // Terminal case: + template + struct GetImpl + { + VTKM_CONSTEXPR vtkm::IdComponent operator()() const { return OutputIndex == 0 ? Head : -1; } + }; + + // Recursive case: + template + struct GetImpl + { + using Next = GetImpl; + + VTKM_CONSTEXPR vtkm::IdComponent operator()() const + { + return OutputIndex == 0 ? Head : Next()(); + } + }; + +public: + /// Get the component from ComponentMap at the specified index as a + /// compile-time constant: + template + static VTKM_CONSTEXPR vtkm::IdComponent Get() + { + return GetImpl()(); + } + +private: + template + struct SwizzleImpl; + + // Terminal case: + template + struct SwizzleImpl + { + static const vtkm::IdComponent InputIndex = Head; + + void operator()(const InputType& in, OutputType& out) const + { + OutputTraits::SetComponent(out, OutputIndex, InputTraits::GetComponent(in, InputIndex)); + } + }; + + // Recursive case: + template + struct SwizzleImpl + { + using Next = SwizzleImpl; + static const vtkm::IdComponent InputIndex = Head; + + void operator()(const InputType& in, OutputType& out) const + { + OutputTraits::SetComponent(out, OutputIndex, InputTraits::GetComponent(in, InputIndex)); + Next()(in, out); + } + }; + +public: + /// Swizzle the input type into the output type. + static void Swizzle(const InputType& in, OutputType& out) + { + SwizzleImpl<0, ComponentMap...>()(in, out); + } + + // UnSwizzle output type --> input type +private: + template + struct UnSwizzleImpl; + + // Terminal case: + template + struct UnSwizzleImpl + { + static const vtkm::IdComponent InputIndex = Head; + + void operator()(const OutputType& out, InputType& in) const + { + InputTraits::SetComponent(in, InputIndex, OutputTraits::GetComponent(out, OutputIndex)); + } + }; + + // Recursive case: + template + struct UnSwizzleImpl + { + using Next = UnSwizzleImpl; + static const vtkm::IdComponent InputIndex = Head; + + void operator()(const OutputType& out, InputType& in) const + { + InputTraits::SetComponent(in, InputIndex, OutputTraits::GetComponent(out, OutputIndex)); + Next()(out, in); + } + }; + + // Entry point: +public: + /// Unswizzle the output type back into the input type. + /// @warning If the entire vector is not used, there may be uninitialized + /// data in the resulting InputType vector. See ALL_COMPS_USED flag. + static void UnSwizzle(const OutputType& out, InputType& in) + { + UnSwizzleImpl<0, ComponentMap...>()(out, in); + } +}; + +namespace internal +{ + +template +class VTKM_ALWAYS_EXPORT ArrayPortalSwizzle +{ + using Traits = ArrayHandleSwizzleTraits; + +public: + using ValueType = typename Traits::OutputType; + + VTKM_EXEC_CONT + ArrayPortalSwizzle() + : Portal() + { + } + + VTKM_EXEC_CONT + ArrayPortalSwizzle(const PortalType& portal) + : Portal(portal) + { + } + + // Copy constructor + VTKM_EXEC_CONT ArrayPortalSwizzle(const ArrayPortalSwizzle& src) + : Portal(src.GetPortal()) + { + } + + VTKM_EXEC_CONT + vtkm::Id GetNumberOfValues() const { return this->Portal.GetNumberOfValues(); } + + VTKM_EXEC_CONT + ValueType Get(vtkm::Id index) const + { + typename Traits::OutputType result; + Traits::Swizzle(this->Portal.Get(index), result); + return result; + } + + VTKM_EXEC_CONT + void Set(vtkm::Id index, const ValueType& value) const + { + SetImpl(this->Portal)(index, value); + } + +private: + // If NeedsRead is true, we need to initialize the InputType vector we write + // with the current values at @a index to avoid overwriting unused components. + template + struct SetImpl + { + const PortalType& Portal; + + SetImpl(const PortalType& portal) + : Portal(portal) + { + } + + void operator()(const vtkm::Id& index, const ValueType& value) + { + typename Traits::InputType in; + if (NeedsRead) + { + in = this->Portal.Get(index); + } + Traits::UnSwizzle(value, in); + this->Portal.Set(index, in); + } + }; + +public: + VTKM_EXEC_CONT + const PortalType& GetPortal() const { return this->Portal; } + +private: + PortalType Portal; +}; // class ArrayPortalSwizzle + +} // namespace internal + +template +class StorageTagSwizzle +{ +}; + +namespace internal +{ + +template +class Storage::OutputType, + StorageTagSwizzle> +{ +public: + using PortalType = ArrayPortalSwizzle; + using PortalConstType = + ArrayPortalSwizzle; + using ValueType = typename PortalType::ValueType; + + VTKM_CONT + Storage() + : Valid(false) + { + } + + VTKM_CONT + Storage(const ArrayHandleType& array) + : Array(array) + , Valid(true) + { + } + + VTKM_CONT + PortalConstType GetPortalConst() const + { + VTKM_ASSERT(this->Valid); + return PortalConstType(this->Array.GetPortalConstControl()); + } + + VTKM_CONT + PortalType GetPortal() + { + VTKM_ASSERT(this->Valid); + return PortalType(this->Array.GetPortalControl()); + } + + VTKM_CONT + vtkm::Id GetNumberOfValues() const + { + VTKM_ASSERT(this->Valid); + return this->Array.GetNumberOfValues(); + } + + VTKM_CONT + void Allocate(vtkm::Id numberOfValues) + { + VTKM_ASSERT(this->Valid); + this->Array.Allocate(numberOfValues); + } + + VTKM_CONT + void Shrink(vtkm::Id numberOfValues) + { + VTKM_ASSERT(this->Valid); + this->Array.Shrink(numberOfValues); + } + + VTKM_CONT + void ReleaseResources() + { + VTKM_ASSERT(this->Valid); + this->Array.ReleaseResources(); + } + + VTKM_CONT + const ArrayHandleType& GetArray() const + { + VTKM_ASSERT(this->Valid); + return this->Array; + } + +private: + ArrayHandleType Array; + bool Valid; +}; // class Storage + +template +class ArrayTransfer::OutputType, + StorageTagSwizzle, + Device> +{ + using ArrayExecutionTypes = typename ArrayHandleType::template ExecutionTypes; + using StorageTag = StorageTagSwizzle; + +public: + using SwizzleTraits = + ArrayHandleSwizzleTraits; + using ValueType = typename SwizzleTraits::OutputType; + +private: + using StorageType = vtkm::cont::internal::Storage; + +public: + using PortalControl = typename StorageType::PortalType; + using PortalConstControl = typename StorageType::PortalConstType; + + using PortalExecution = ArrayPortalSwizzle; + using PortalConstExecution = + ArrayPortalSwizzle; + + VTKM_CONT + ArrayTransfer(StorageType* storage) + : Array(storage->GetArray()) + { + } + + VTKM_CONT + vtkm::Id GetNumberOfValues() const { return this->Array.GetNumberOfValues(); } + + VTKM_CONT + PortalConstExecution PrepareForInput(bool vtkmNotUsed(updateData)) + { + return PortalConstExecution(this->Array.PrepareForInput(Device())); + } + + VTKM_CONT + PortalExecution PrepareForInPlace(bool vtkmNotUsed(updateData)) + { + return PortalExecution(this->Array.PrepareForInPlace(Device())); + } + + VTKM_CONT + PortalExecution PrepareForOutput(vtkm::Id numberOfValues) + { + return PortalExecution(this->Array.PrepareForOutput(numberOfValues, Device())); + } + + VTKM_CONT + void RetrieveOutputData(StorageType* vtkmNotUsed(storage)) const + { + // Implementation of this method should be unnecessary. The internal + // array handle should automatically retrieve the output data as + // necessary. + } + + VTKM_CONT + void Shrink(vtkm::Id numberOfValues) { this->Array.Shrink(numberOfValues); } + + VTKM_CONT + void ReleaseResources() { this->Array.ReleaseResourcesExecution(); } + +private: + ArrayHandleType Array; +}; +} +} +} // namespace vtkm::cont::internal + +namespace vtkm +{ +namespace cont +{ + +/// \brief A fancy ArrayHandle that rearranges and/or removes components of an +/// ArrayHandle with a vtkm::Vec ValueType. +/// +/// ArrayHandleSwizzle is a specialization of ArrayHandle. It takes an +/// input ArrayHandle with a vtkm::Vec ValueType and a compile-time component +/// map and uses this information to create a new array consisting of the +/// specified components of the input ArrayHandle in the specified order. So for +/// a given index i, ArrayHandleSwizzle looks up the i-th vtkm::Vec in +/// the index array and reads or writes to the specified components, leaving all +/// other components unmodified. This is done on the fly rather than creating a +/// copy of the array. +template +class ArrayHandleSwizzle : public vtkm::cont::ArrayHandle< + typename ArrayHandleSwizzleTraits::OutputType, + StorageTagSwizzle> +{ +public: + VTKM_ARRAY_HANDLE_SUBCLASS( + ArrayHandleSwizzle, + (ArrayHandleSwizzle), + (vtkm::cont::ArrayHandle::OutputType, + StorageTagSwizzle>)); + + using SwizzleTraits = + ArrayHandleSwizzleTraits; + +protected: + using StorageType = vtkm::cont::internal::Storage; + +public: + VTKM_CONT + ArrayHandleSwizzle(const ArrayHandleType& array) + : Superclass(StorageType(array)) + { + } +}; + +/// make_ArrayHandleSwizzle is convenience function to generate an +/// ArrayHandleSwizzle. +template +VTKM_CONT ArrayHandleSwizzle make_ArrayHandleSwizzle( + const ArrayHandleType& array) +{ + return ArrayHandleSwizzle(array); +} +} +} // namespace vtkm::cont + +#endif // vtk_m_cont_ArrayHandleSwizzle_h diff --git a/vtkm/cont/CMakeLists.txt b/vtkm/cont/CMakeLists.txt index 5862ce917..0d06927d4 100644 --- a/vtkm/cont/CMakeLists.txt +++ b/vtkm/cont/CMakeLists.txt @@ -33,6 +33,7 @@ set(headers ArrayHandlePermutation.h ArrayHandleReverse.h ArrayHandleStreaming.h + ArrayHandleSwizzle.h ArrayHandleTransform.h ArrayHandleUniformPointCoordinates.h ArrayHandleZip.h diff --git a/vtkm/cont/testing/CMakeLists.txt b/vtkm/cont/testing/CMakeLists.txt index 52e65c319..d4ea05423 100644 --- a/vtkm/cont/testing/CMakeLists.txt +++ b/vtkm/cont/testing/CMakeLists.txt @@ -43,6 +43,7 @@ set(unit_tests UnitTestArrayHandleIndex.cxx UnitTestArrayHandleReverse.cxx UnitTestArrayHandlePermutation.cxx + UnitTestArrayHandleSwizzle.cxx UnitTestArrayHandleTransform.cxx UnitTestArrayHandleUniformPointCoordinates.cxx UnitTestArrayHandleConcatenate.cxx diff --git a/vtkm/cont/testing/UnitTestArrayHandleSwizzle.cxx b/vtkm/cont/testing/UnitTestArrayHandleSwizzle.cxx new file mode 100644 index 000000000..8354af849 --- /dev/null +++ b/vtkm/cont/testing/UnitTestArrayHandleSwizzle.cxx @@ -0,0 +1,400 @@ +//============================================================================= +// +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +// +// Copyright 2015 Sandia Corporation. +// Copyright 2015 UT-Battelle, LLC. +// Copyright 2015 Los Alamos National Security. +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +// +//============================================================================= + +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace +{ + +template +struct SwizzleTests +{ + using SwizzleInputArrayType = vtkm::cont::ArrayHandle>; + + template + using SwizzleArrayType = vtkm::cont::ArrayHandleSwizzle; + + using ReferenceComponentArrayType = vtkm::cont::ArrayHandleCounting; + using ReferenceArrayType = + typename vtkm::cont::ArrayHandleCompositeVectorType::type; + + using DeviceTag = VTKM_DEFAULT_DEVICE_ADAPTER_TAG; + using Algo = vtkm::cont::DeviceAdapterAlgorithm; + + // This is used to build a ArrayHandleSwizzle's internal array. + ReferenceArrayType RefArray; + + void ConstructReferenceArray() + { + // Build the Ref array + const vtkm::Id numValues = 32; + ReferenceComponentArrayType c1 = + vtkm::cont::make_ArrayHandleCounting(3, 2, numValues); + ReferenceComponentArrayType c2 = + vtkm::cont::make_ArrayHandleCounting(2, 3, numValues); + ReferenceComponentArrayType c3 = + vtkm::cont::make_ArrayHandleCounting(4, 4, numValues); + ReferenceComponentArrayType c4 = + vtkm::cont::make_ArrayHandleCounting(1, 3, numValues); + + this->RefArray = vtkm::cont::make_ArrayHandleCompositeVector(c1, 0, c2, 0, c3, 0, c4, 0); + } + + SwizzleInputArrayType BuildSwizzleInputArray() const + { + SwizzleInputArrayType result; + Algo::Copy(this->RefArray, result); + return result; + } + + template + void SanityCheck() const + { + using Swizzle = SwizzleArrayType; + using Traits = typename Swizzle::SwizzleTraits; + + VTKM_TEST_ASSERT(Traits::COUNT == vtkm::VecTraits::NUM_COMPONENTS, + "Traits::COUNT invalid."); + VTKM_TEST_ASSERT( + VTKM_PASS_COMMAS(std::is_same::value), + "Traits::ComponentType invalid."); + VTKM_TEST_ASSERT( + VTKM_PASS_COMMAS( + std::is_same< + typename Traits::OutputType, + vtkm::Vec(sizeof...(ComponentMap))>>::value), + "Traits::OutputType invalid."); + + SwizzleInputArrayType input = this->BuildSwizzleInputArray(); + SwizzleArrayType swizzle = + vtkm::cont::make_ArrayHandleSwizzle(input); + + VTKM_TEST_ASSERT(input.GetNumberOfValues() == swizzle.GetNumberOfValues(), + "Number of values in copied Swizzle array does not match input."); + } + + template + void ReadTest() const + { + using Traits = typename SwizzleArrayType::SwizzleTraits; + + // Test that the expected values are read from an Swizzle array. + SwizzleInputArrayType input = this->BuildSwizzleInputArray(); + SwizzleArrayType swizzle = + vtkm::cont::make_ArrayHandleSwizzle(input); + + // Test reading the data back in the control env: + this->ValidateReadTest(swizzle); + + // Copy the extract array in the execution environment to test reading: + vtkm::cont::ArrayHandle execCopy; + Algo::Copy(swizzle, execCopy); + this->ValidateReadTest(execCopy); + } + + template + void ValidateReadTest(ArrayHandleType testArray) const + { + using Traits = typename SwizzleArrayType::SwizzleTraits; + using MapType = typename Traits::RuntimeComponentMapType; + const MapType map = Traits::GenerateRuntimeComponentMap(); + + using ReferenceVectorType = typename ReferenceArrayType::ValueType; + using SwizzleVectorType = typename Traits::OutputType; + + VTKM_TEST_ASSERT(map.size() == vtkm::VecTraits::NUM_COMPONENTS, + "Unexpected runtime component map size."); + VTKM_TEST_ASSERT(testArray.GetNumberOfValues() == this->RefArray.GetNumberOfValues(), + "Number of values incorrect in Read test."); + + auto refPortal = this->RefArray.GetPortalConstControl(); + auto testPortal = testArray.GetPortalConstControl(); + + for (vtkm::Id i = 0; i < testArray.GetNumberOfValues(); ++i) + { + // Manually swizzle the reference vector using the runtime map information: + ReferenceVectorType refVec = refPortal.Get(i); + SwizzleVectorType refVecSwizzle; + + for (size_t j = 0; j < map.size(); ++j) + { + refVecSwizzle[static_cast(j)] = refVec[map[j]]; + } + + VTKM_TEST_ASSERT(test_equal(refVecSwizzle, testPortal.Get(i), 0.), + "Invalid value encountered in Read test."); + } + } + + // Doubles everything in the input portal. + template + struct WriteTestFunctor : vtkm::exec::FunctorBase + { + PortalType Portal; + + VTKM_CONT + WriteTestFunctor(const PortalType& portal) + : Portal(portal) + { + } + + VTKM_EXEC_CONT + void operator()(vtkm::Id index) const { this->Portal.Set(index, this->Portal.Get(index) * 2.); } + }; + + template + void WriteTest() const + { + // Control test: + { + SwizzleInputArrayType input = this->BuildSwizzleInputArray(); + SwizzleArrayType swizzle = + vtkm::cont::make_ArrayHandleSwizzle(input); + + WriteTestFunctor::PortalControl> functor( + swizzle.GetPortalControl()); + + for (vtkm::Id i = 0; i < swizzle.GetNumberOfValues(); ++i) + { + functor(i); + } + + this->ValidateWriteTestArray(input); + } + + // Exec test: + { + SwizzleInputArrayType input = this->BuildSwizzleInputArray(); + SwizzleArrayType swizzle = + vtkm::cont::make_ArrayHandleSwizzle(input); + + using Portal = + typename SwizzleArrayType::template ExecutionTypes::Portal; + + WriteTestFunctor functor(swizzle.PrepareForInPlace(DeviceTag())); + + Algo::Schedule(functor, swizzle.GetNumberOfValues()); + this->ValidateWriteTestArray(input); + } + } + + // Check that the swizzled components are twice the reference value. + template + void ValidateWriteTestArray(SwizzleInputArrayType testArray) const + { + using Traits = typename SwizzleArrayType::SwizzleTraits; + using MapType = typename Traits::RuntimeComponentMapType; + const MapType map = Traits::GenerateRuntimeComponentMap(); + + auto refPortal = this->RefArray.GetPortalConstControl(); + auto portal = testArray.GetPortalConstControl(); + + VTKM_TEST_ASSERT(portal.GetNumberOfValues() == refPortal.GetNumberOfValues(), + "Number of values in write test output do not match input."); + + for (vtkm::Id i = 0; i < portal.GetNumberOfValues(); ++i) + { + auto value = portal.Get(i); + auto refValue = refPortal.Get(i); + + // Double all of the components that appear in the map to replicate the + // test result: + for (size_t j = 0; j < map.size(); ++j) + { + refValue[map[j]] *= 2; + } + + VTKM_TEST_ASSERT(test_equal(refValue, value, 0.), "Value mismatch in Write test."); + } + } + + template + void TestSwizzle() const + { + this->SanityCheck(); + this->ReadTest(); + this->WriteTest(); + } + + void operator()() + { + this->ConstructReferenceArray(); + +// Enable for full test. We normally test a reduced set of component maps +// to keep compile times/sizes down: +#if 0 + this->TestSwizzle<0, 1>(); + this->TestSwizzle<0, 2>(); + this->TestSwizzle<0, 3>(); + this->TestSwizzle<1, 0>(); + this->TestSwizzle<1, 2>(); + this->TestSwizzle<1, 3>(); + this->TestSwizzle<2, 0>(); + this->TestSwizzle<2, 1>(); + this->TestSwizzle<2, 3>(); + this->TestSwizzle<3, 0>(); + this->TestSwizzle<3, 1>(); + this->TestSwizzle<3, 2>(); + this->TestSwizzle<0, 1, 2>(); + this->TestSwizzle<0, 1, 3>(); + this->TestSwizzle<0, 2, 1>(); + this->TestSwizzle<0, 2, 3>(); + this->TestSwizzle<0, 3, 1>(); + this->TestSwizzle<0, 3, 2>(); + this->TestSwizzle<1, 0, 2>(); + this->TestSwizzle<1, 0, 3>(); + this->TestSwizzle<1, 2, 0>(); + this->TestSwizzle<1, 2, 3>(); + this->TestSwizzle<1, 3, 0>(); + this->TestSwizzle<1, 3, 2>(); + this->TestSwizzle<2, 0, 1>(); + this->TestSwizzle<2, 0, 3>(); + this->TestSwizzle<2, 1, 0>(); + this->TestSwizzle<2, 1, 3>(); + this->TestSwizzle<2, 3, 0>(); + this->TestSwizzle<2, 3, 1>(); + this->TestSwizzle<3, 0, 1>(); + this->TestSwizzle<3, 0, 2>(); + this->TestSwizzle<3, 1, 0>(); + this->TestSwizzle<3, 1, 2>(); + this->TestSwizzle<3, 2, 0>(); + this->TestSwizzle<3, 2, 1>(); + this->TestSwizzle<0, 1, 2, 3>(); + this->TestSwizzle<0, 1, 3, 2>(); + this->TestSwizzle<0, 2, 1, 3>(); + this->TestSwizzle<0, 2, 3, 1>(); + this->TestSwizzle<0, 3, 1, 2>(); + this->TestSwizzle<0, 3, 2, 1>(); + this->TestSwizzle<1, 0, 2, 3>(); + this->TestSwizzle<1, 0, 3, 2>(); + this->TestSwizzle<1, 2, 0, 3>(); + this->TestSwizzle<1, 2, 3, 0>(); + this->TestSwizzle<1, 3, 0, 2>(); + this->TestSwizzle<1, 3, 2, 0>(); + this->TestSwizzle<2, 0, 1, 3>(); + this->TestSwizzle<2, 0, 3, 1>(); + this->TestSwizzle<2, 1, 0, 3>(); + this->TestSwizzle<2, 1, 3, 0>(); + this->TestSwizzle<2, 3, 0, 1>(); + this->TestSwizzle<2, 3, 1, 0>(); + this->TestSwizzle<3, 0, 1, 2>(); + this->TestSwizzle<3, 0, 2, 1>(); + this->TestSwizzle<3, 1, 0, 2>(); + this->TestSwizzle<3, 1, 2, 0>(); + this->TestSwizzle<3, 2, 0, 1>(); + this->TestSwizzle<3, 2, 1, 0>(); +#else + this->TestSwizzle<0, 1>(); + this->TestSwizzle<1, 0>(); + this->TestSwizzle<2, 3>(); + this->TestSwizzle<3, 2>(); + this->TestSwizzle<0, 1, 2>(); + this->TestSwizzle<0, 3, 1>(); + this->TestSwizzle<2, 0, 3>(); + this->TestSwizzle<3, 2, 1>(); + this->TestSwizzle<0, 1, 2, 3>(); + this->TestSwizzle<1, 3, 2, 0>(); + this->TestSwizzle<2, 0, 1, 3>(); + this->TestSwizzle<3, 1, 0, 2>(); + this->TestSwizzle<3, 2, 1, 0>(); +#endif + } +}; + +struct ArgToTemplateType +{ + template + void operator()(ValueType) const + { + SwizzleTests()(); + } +}; + +void TestArrayHandleSwizzle() +{ + using TestTypes = vtkm::ListTagBase; + vtkm::testing::Testing::TryTypes(ArgToTemplateType(), TestTypes()); +} + +template +using Validator = vtkm::cont::internal::ValidateComponentMap; + +void TestComponentMapValidator() +{ + using RepeatComps = Validator<5, 0, 3, 2, 3, 1, 4>; + VTKM_TEST_ASSERT(!RepeatComps::Valid, "Repeat components allowed."); + + using NegativeComps = Validator<5, 0, 4, -3, 1, 2>; + VTKM_TEST_ASSERT(!NegativeComps::Valid, "Negative components allowed."); + + using OutOfBoundsComps = Validator<5, 0, 2, 3, 5>; + VTKM_TEST_ASSERT(!OutOfBoundsComps::Valid, "Out-of-bounds components allowed."); +} + +void TestRuntimeComponentMapGenerator() +{ + // Dummy input vector type. Only concerned with the component map: + using Dummy = vtkm::Vec; + + using Traits = vtkm::cont::ArrayHandleSwizzleTraits; + using MapType = Traits::RuntimeComponentMapType; + + const MapType map = Traits::GenerateRuntimeComponentMap(); + + VTKM_TEST_ASSERT(map.size() == 6, "Invalid map size."); + VTKM_TEST_ASSERT(map[0] == 3, "Invalid map entry."); + VTKM_TEST_ASSERT(map[1] == 2, "Invalid map entry."); + VTKM_TEST_ASSERT(map[2] == 4, "Invalid map entry."); + VTKM_TEST_ASSERT(map[3] == 1, "Invalid map entry."); + VTKM_TEST_ASSERT(map[4] == 6, "Invalid map entry."); + VTKM_TEST_ASSERT(map[5] == 0, "Invalid map entry."); +} + +} // end anon namespace + +int UnitTestArrayHandleSwizzle(int, char* []) +{ + try + { + TestComponentMapValidator(); + TestRuntimeComponentMapGenerator(); + } + catch (vtkm::cont::Error& e) + { + std::cerr << "Error: " << e.what() << "\n"; + return EXIT_FAILURE; + } + + return vtkm::cont::testing::Testing::Run(TestArrayHandleSwizzle); +}