#include <vtkm/TypeListTag.h>

#include <vtkm/cont/ArrayHandleGroupVec.h>
#include <vtkm/cont/ArrayRangeCompute.h>

#include <vtkm/cont/arg/Transport.h>
#include <vtkm/cont/arg/TypeCheck.h>
#include <vtkm/cont/arg/TypeCheckTagArray.h>

#include <vtkm/exec/arg/AspectTagDefault.h>
#include <vtkm/exec/arg/Fetch.h>
#include <vtkm/exec/arg/ThreadIndicesBasic.h>

#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/ScatterCounting.h>
#include <vtkm/worklet/WorkletMapField.h>

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

#include <fstream>
#include <type_traits>

template<typename T>
static vtkm::Vec<T, 2> TransformSVGPoint(const vtkm::Vec<T, 2>& point,
                                         const vtkm::Range xRange,
                                         const vtkm::Range yRange,
                                         float padding)
{
  return vtkm::Vec<T, 2>(static_cast<T>(point[0] - xRange.Min + padding),
                         static_cast<T>(yRange.Max - point[1] + padding));
}

template<typename T>
static void WriteSVG(const std::string& filename,
                     const vtkm::cont::ArrayHandle<vtkm::Vec<T, 2>>& data,
                     float width = 2.0,
                     const std::string& color = "black")
{
  static const float PADDING = 0.05f;

  vtkm::cont::ArrayHandle<vtkm::Range> ranges = vtkm::cont::ArrayRangeCompute(data);
  vtkm::Range xRange = ranges.GetPortalConstControl().Get(0);
  vtkm::Range yRange = ranges.GetPortalConstControl().Get(1);

  std::ofstream file(filename.c_str());

  file << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
  file << "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" "
       << "width=\"" << xRange.Length() + 2 * PADDING << "in\" "
       << "height=\"" << yRange.Length() + 2 * PADDING << "in\" "
       << ">\n";

  typename vtkm::cont::ArrayHandle<vtkm::Vec<T, 2>>::PortalConstControl portal =
    data.GetPortalConstControl();
  for (vtkm::Id lineIndex = 0; lineIndex < portal.GetNumberOfValues() / 2;
       ++lineIndex)
  {
    vtkm::Vec<T, 2> p1 =
      TransformSVGPoint(portal.Get(lineIndex * 2 + 0), xRange, yRange, PADDING);
    vtkm::Vec<T, 2> p2 =
      TransformSVGPoint(portal.Get(lineIndex * 2 + 1), xRange, yRange, PADDING);

    file << "  <line x1=\"" << p1[0] << "in\" y1=\"" << p1[1] << "in\" x2=\""
         << p2[0] << "in\" y2=\"" << p2[1] << "in\" stroke=\"" << color
         << "\" stroke-width=\"" << width << "\" stroke-linecap=\"round\" />\n";
  }

  file << "</svg>\n";
  file.close();
}

////
//// BEGIN-EXAMPLE TypeCheckImpl.h
////
namespace vtkm
{
namespace cont
{
namespace arg
{

struct TypeCheckTag2DCoordinates
{
};

template<typename ArrayType>
struct TypeCheck<TypeCheckTag2DCoordinates, ArrayType>
{
  static constexpr bool value = false;
};

template<typename T, typename Storage>
struct TypeCheck<TypeCheckTag2DCoordinates, vtkm::cont::ArrayHandle<T, Storage>>
{
  static constexpr bool value =
    vtkm::ListContains<vtkm::TypeListTagFieldVec2, T>::value;
};

} // namespace arg
} // namespace cont
} // namespace vtkm
////
//// END-EXAMPLE TypeCheckImpl.h
////

////
//// BEGIN-EXAMPLE TransportImpl.h
////
namespace vtkm
{
namespace cont
{
namespace arg
{

struct TransportTag2DLineSegmentsIn
{
};

template<typename ContObjectType, typename Device>
struct Transport<vtkm::cont::arg::TransportTag2DLineSegmentsIn,
                 ContObjectType,
                 Device>
{
  VTKM_IS_ARRAY_HANDLE(ContObjectType);

  using GroupedArrayType = vtkm::cont::ArrayHandleGroupVec<ContObjectType, 2>;

  using ExecObjectType =
    typename GroupedArrayType::template ExecutionTypes<Device>::PortalConst;

  template<typename InputDomainType>
  VTKM_CONT ExecObjectType operator()(const ContObjectType& object,
                                      const InputDomainType&,
                                      vtkm::Id inputRange,
                                      vtkm::Id) const
  {
    if (object.GetNumberOfValues() != inputRange * 2)
    {
      throw vtkm::cont::ErrorBadValue(
        "2D line segment array size does not agree with input size.");
    }

    GroupedArrayType groupedArray(object);
    return groupedArray.PrepareForInput(Device());
  }
};

} // namespace arg
} // namespace cont
} // namespace vtkm
////
//// END-EXAMPLE TransportImpl.h
////

////
//// BEGIN-EXAMPLE FetchImplBasic.h
////
namespace vtkm
{
namespace exec
{
namespace arg
{

struct FetchTag2DLineSegmentsIn
{
};

template<typename ThreadIndicesType, typename ExecObjectType>
struct Fetch<vtkm::exec::arg::FetchTag2DLineSegmentsIn,
             vtkm::exec::arg::AspectTagDefault,
             ThreadIndicesType,
             ExecObjectType>
{
  using ValueType = typename ExecObjectType::ValueType;

  VTKM_SUPPRESS_EXEC_WARNINGS
  VTKM_EXEC
  ValueType Load(const ThreadIndicesType& indices,
                 const ExecObjectType& arrayPortal) const
  {
    return arrayPortal.Get(indices.GetInputIndex());
  }

  VTKM_EXEC
  void Store(const ThreadIndicesType&, const ExecObjectType&, const ValueType&) const
  {
    // Store is a no-op for this fetch.
  }
};

} // namespace arg
} // namespace exec
} // namespace vtkm
////
//// END-EXAMPLE FetchImplBasic.h
////

////
//// BEGIN-EXAMPLE AspectImpl.h
////
namespace vtkm
{
namespace exec
{
namespace arg
{

struct AspectTagFirstPoint
{
};

template<typename ThreadIndicesType, typename ExecObjectType>
struct Fetch<vtkm::exec::arg::FetchTag2DLineSegmentsIn,
             vtkm::exec::arg::AspectTagFirstPoint,
             ThreadIndicesType,
             ExecObjectType>
{
  using ValueType = typename ExecObjectType::ValueType::ComponentType;

  VTKM_SUPPRESS_EXEC_WARNINGS
  VTKM_EXEC
  ValueType Load(const ThreadIndicesType& indices,
                 const ExecObjectType& arrayPortal) const
  {
    return arrayPortal.Get(indices.GetInputIndex())[0];
  }

  VTKM_EXEC
  void Store(const ThreadIndicesType&, const ExecObjectType&, const ValueType&) const
  {
    // Store is a no-op for this fetch.
  }
};

//// PAUSE-EXAMPLE
struct AspectTagSecondPoint
{
};

template<typename ThreadIndicesType, typename ExecObjectType>
struct Fetch<vtkm::exec::arg::FetchTag2DLineSegmentsIn,
             vtkm::exec::arg::AspectTagSecondPoint,
             ThreadIndicesType,
             ExecObjectType>
{
  using ValueType = typename ExecObjectType::ValueType::ComponentType;

  VTKM_SUPPRESS_EXEC_WARNINGS
  VTKM_EXEC
  ValueType Load(const ThreadIndicesType& indices,
                 const ExecObjectType& arrayPortal) const
  {
    return arrayPortal.Get(indices.GetInputIndex())[1];
  }

  VTKM_EXEC
  void Store(const ThreadIndicesType&, const ExecObjectType&, const ValueType&) const
  {
    // Store is a no-op for this fetch.
  }
};

//// RESUME-EXAMPLE
} // namespace arg
} // namespace exec
} // namespace vtkm
////
//// END-EXAMPLE AspectImpl.h
////

struct VecLineSegments : vtkm::worklet::WorkletMapField
{
  ////
  //// BEGIN-EXAMPLE CustomControlSignatureTag.cxx
  ////
  struct LineSegment2DCoordinatesIn : vtkm::cont::arg::ControlSignatureTagBase
  {
    using TypeCheckTag = vtkm::cont::arg::TypeCheckTag2DCoordinates;
    using TransportTag = vtkm::cont::arg::TransportTag2DLineSegmentsIn;
    using FetchTag = vtkm::exec::arg::FetchTag2DLineSegmentsIn;
  };
  ////
  //// END-EXAMPLE CustomControlSignatureTag.cxx
  ////

  ////
  //// BEGIN-EXAMPLE CustomExecutionSignatureTag.cxx
  ////
  template<typename ArgTag>
  struct FirstPoint : vtkm::exec::arg::ExecutionSignatureTagBase
  {
    static const vtkm::IdComponent INDEX = ArgTag::INDEX;
    using AspectTag = vtkm::exec::arg::AspectTagFirstPoint;
  };
  ////
  //// END-EXAMPLE CustomExecutionSignatureTag.cxx
  ////

  template<typename ArgTag>
  struct SecondPoint : vtkm::exec::arg::ExecutionSignatureTagBase
  {
    static const vtkm::IdComponent INDEX = ArgTag::INDEX;
    using AspectTag = vtkm::exec::arg::AspectTagSecondPoint;
  };

  ////
  //// BEGIN-EXAMPLE UseCustomControlSignatureTag.cxx
  ////
  ////
  //// BEGIN-EXAMPLE UseCustomExecutionSignatureTag.cxx
  ////
  using ControlSignature = void(LineSegment2DCoordinatesIn coordsIn,
                                FieldOut vecOut,
                                FieldIn index);
  ////
  //// END-EXAMPLE UseCustomControlSignatureTag.cxx
  ////
  using ExecutionSignature = void(FirstPoint<_1>, SecondPoint<_1>, _2);
  ////
  //// END-EXAMPLE UseCustomExecutionSignatureTag.cxx
  ////
  using InputDomain = _3;

  template<typename T>
  VTKM_EXEC void operator()(const vtkm::Vec<T, 2>& firstPoint,
                            const vtkm::Vec<T, 2>& secondPoint,
                            vtkm::Vec<T, 2>& vecOut) const
  {
    vecOut = secondPoint - firstPoint;
  }
};

void TryVecLineSegments()
{
  static const vtkm::Id ARRAY_SIZE = 10;

  vtkm::cont::ArrayHandle<vtkm::Vec2f> inputArray;
  inputArray.Allocate(ARRAY_SIZE * 2);
  SetPortal(inputArray.GetPortalControl());

  vtkm::cont::ArrayHandle<vtkm::Vec2f> outputArray;

  vtkm::worklet::DispatcherMapField<VecLineSegments> dispatcher;
  dispatcher.Invoke(
    inputArray, outputArray, vtkm::cont::ArrayHandleIndex(ARRAY_SIZE));

  VTKM_TEST_ASSERT(outputArray.GetNumberOfValues() == ARRAY_SIZE,
                   "Output wrong size.");

  for (vtkm::Id index = 0; index < ARRAY_SIZE; ++index)
  {
    vtkm::Vec2f expectedVec =
      TestValue(index * 2 + 1, vtkm::Vec2f()) - TestValue(index * 2, vtkm::Vec2f());
    vtkm::Vec2f computedVec = outputArray.GetPortalConstControl().Get(index);
    VTKM_TEST_ASSERT(test_equal(expectedVec, computedVec), "Bad value.");
  }
}

////
//// BEGIN-EXAMPLE TransportImpl2.h
////
namespace vtkm
{
namespace cont
{
namespace arg
{

template<vtkm::IdComponent NumOutputPerInput>
struct TransportTag2DLineSegmentsOut
{
};

template<vtkm::IdComponent NumOutputPerInput,
         typename ContObjectType,
         typename Device>
struct Transport<vtkm::cont::arg::TransportTag2DLineSegmentsOut<NumOutputPerInput>,
                 ContObjectType,
                 Device>
{
  VTKM_IS_ARRAY_HANDLE(ContObjectType);

  using GroupedArrayType = vtkm::cont::ArrayHandleGroupVec<
    vtkm::cont::ArrayHandleGroupVec<ContObjectType, 2>,
    NumOutputPerInput>;

  using ExecObjectType =
    typename GroupedArrayType::template ExecutionTypes<Device>::Portal;

  template<typename InputDomainType>
  VTKM_CONT ExecObjectType operator()(const ContObjectType& object,
                                      const InputDomainType&,
                                      vtkm::Id,
                                      vtkm::Id outputRange) const
  {
    GroupedArrayType groupedArray(vtkm::cont::make_ArrayHandleGroupVec<2>(object));
    return groupedArray.PrepareForOutput(outputRange, Device());
  }
};

} // namespace arg
} // namespace cont
} // namespace vtkm
////
//// END-EXAMPLE TransportImpl2.h
////

////
//// BEGIN-EXAMPLE ThreadIndicesLineFractal.h
////
namespace vtkm
{
namespace exec
{
namespace arg
{

class ThreadIndicesLineFractal : public vtkm::exec::arg::ThreadIndicesBasic
{
  using Superclass = vtkm::exec::arg::ThreadIndicesBasic;

public:
  using CoordinateType = vtkm::Vec2f;

  VTKM_SUPPRESS_EXEC_WARNINGS
  template<typename InputPointPortal>
  VTKM_EXEC ThreadIndicesLineFractal(vtkm::Id threadIndex,
                                     vtkm::Id inputIndex,
                                     vtkm::IdComponent visitIndex,
                                     vtkm::Id outputIndex,
                                     const InputPointPortal& inputPoints,
                                     vtkm::Id globalThreadIndexOffset = 0)
    : Superclass(threadIndex,
                 inputIndex,
                 visitIndex,
                 outputIndex,
                 globalThreadIndexOffset)
  {
    this->Point0 = inputPoints.Get(this->GetInputIndex())[0];
    this->Point1 = inputPoints.Get(this->GetInputIndex())[1];
  }

  VTKM_EXEC
  const CoordinateType& GetPoint0() const { return this->Point0; }

  VTKM_EXEC
  const CoordinateType& GetPoint1() const { return this->Point1; }

private:
  CoordinateType Point0;
  CoordinateType Point1;
};

} // namespace arg
} // namespace exec
} // namespace vtkm
////
//// END-EXAMPLE ThreadIndicesLineFractal.h
////

////
//// BEGIN-EXAMPLE LineFractalTransform.h
////
namespace vtkm
{
namespace exec
{

class LineFractalTransform
{
public:
  template<typename T>
  VTKM_EXEC LineFractalTransform(const vtkm::Vec<T, 2>& point0,
                                 const vtkm::Vec<T, 2>& point1)
  {
    this->Offset = point0;
    this->UAxis = point1 - point0;
    this->VAxis = vtkm::make_Vec(-this->UAxis[1], this->UAxis[0]);
  }

  template<typename T>
  VTKM_EXEC vtkm::Vec<T, 2> operator()(const vtkm::Vec<T, 2>& ppoint) const
  {
    vtkm::Vec2f ppointCast(ppoint);
    vtkm::Vec2f transform =
      ppointCast[0] * this->UAxis + ppointCast[1] * this->VAxis + this->Offset;
    return vtkm::Vec<T, 2>(transform);
  }

  template<typename T>
  VTKM_EXEC vtkm::Vec<T, 2> operator()(T x, T y) const
  {
    return (*this)(vtkm::Vec<T, 2>(x, y));
  }

private:
  vtkm::Vec2f Offset;
  vtkm::Vec2f UAxis;
  vtkm::Vec2f VAxis;
};

} // namespace exec
} // namespace vtkm
////
//// END-EXAMPLE LineFractalTransform.h
////

////
//// BEGIN-EXAMPLE InputDomainFetch.h
////
namespace vtkm
{
namespace exec
{
namespace arg
{

struct AspectTagLineFractalTransform
{
};

template<typename FetchTag, typename ExecObjectType>
struct Fetch<FetchTag,
             vtkm::exec::arg::AspectTagLineFractalTransform,
             vtkm::exec::arg::ThreadIndicesLineFractal,
             ExecObjectType>
{
  using ValueType = LineFractalTransform;

  VTKM_SUPPRESS_EXEC_WARNINGS
  VTKM_EXEC
  ValueType Load(const vtkm::exec::arg::ThreadIndicesLineFractal& indices,
                 const ExecObjectType&) const
  {
    return ValueType(indices.GetPoint0(), indices.GetPoint1());
  }

  VTKM_EXEC
  void Store(const vtkm::exec::arg::ThreadIndicesLineFractal&,
             const ExecObjectType&,
             const ValueType&) const
  {
    // Store is a no-op for this fetch.
  }
};

} // namespace arg
} // namespace exec
} // namespace vtkm
////
//// END-EXAMPLE InputDomainFetch.h
////

////
//// BEGIN-EXAMPLE WorkletLineFractal.h
////
namespace vtkm
{
namespace worklet
{

class WorkletLineFractal : public vtkm::worklet::internal::WorkletBase
{
public:
  /// Control signature tag for line segments in the plane. Used as the input
  /// domain.
  ///
  ////
  //// BEGIN-EXAMPLE WorkletLineFractalInputDomainTag.cxx
  ////
  struct SegmentsIn : vtkm::cont::arg::ControlSignatureTagBase
  {
    using TypeCheckTag = vtkm::cont::arg::TypeCheckTag2DCoordinates;
    using TransportTag = vtkm::cont::arg::TransportTag2DLineSegmentsIn;
    using FetchTag = vtkm::exec::arg::FetchTag2DLineSegmentsIn;
  };
  ////
  //// END-EXAMPLE WorkletLineFractalInputDomainTag.cxx
  ////

  /// Control signature tag for a group of output line segments. The template
  /// argument specifies how many line segments are outputted for each input.
  /// The type is a Vec-like (of size NumSegments) of Vec-2's.
  ///
  ////
  //// BEGIN-EXAMPLE WorkletLineFractalOutputTag.cxx
  ////
  template<vtkm::IdComponent NumSegments>
  struct SegmentsOut : vtkm::cont::arg::ControlSignatureTagBase
  {
    using TypeCheckTag = vtkm::cont::arg::TypeCheckTag2DCoordinates;
    using TransportTag = vtkm::cont::arg::TransportTag2DLineSegmentsOut<NumSegments>;
    using FetchTag = vtkm::exec::arg::FetchTagArrayDirectOut;
  };
  ////
  //// END-EXAMPLE WorkletLineFractalOutputTag.cxx
  ////

  /// Control signature tag for input fields. There is one entry per input line
  /// segment. This tag takes a template argument that is a type list tag that
  /// limits the possible value types in the array.
  ///
  ////
  //// BEGIN-EXAMPLE WorkletLineFractalFieldInTag.cxx
  ////
  struct FieldIn : vtkm::cont::arg::ControlSignatureTagBase
  {
    using TypeCheckTag = vtkm::cont::arg::TypeCheckTagArray;
    using TransportTag = vtkm::cont::arg::TransportTagArrayIn;
    using FetchTag = vtkm::exec::arg::FetchTagArrayDirectIn;
  };
  ////
  //// END-EXAMPLE WorkletLineFractalFieldInTag.cxx
  ////

  /// Control signature tag for input fields. There is one entry per input line
  /// segment. This tag takes a template argument that is a type list tag that
  /// limits the possible value types in the array.
  ///
  struct FieldOut : vtkm::cont::arg::ControlSignatureTagBase
  {
    using TypeCheckTag = vtkm::cont::arg::TypeCheckTagArray;
    using TransportTag = vtkm::cont::arg::TransportTagArrayOut;
    using FetchTag = vtkm::exec::arg::FetchTagArrayDirectOut;
  };

  /// Execution signature tag for a LineFractalTransform from the input.
  ///
  ////
  //// BEGIN-EXAMPLE WorkletLineFractalTransformTag.cxx
  ////
  struct Transform : vtkm::exec::arg::ExecutionSignatureTagBase
  {
    static const vtkm::IdComponent INDEX = 1;
    using AspectTag = vtkm::exec::arg::AspectTagLineFractalTransform;
  };
  ////
  //// END-EXAMPLE WorkletLineFractalTransformTag.cxx
  ////

  ////
  //// BEGIN-EXAMPLE GetThreadIndices.cxx
  ////
  VTKM_SUPPRESS_EXEC_WARNINGS
  template<typename OutToInPortalType,
           typename VisitPortalType,
           typename ThreadToOutType,
           typename InputDomainType>
  VTKM_EXEC vtkm::exec::arg::ThreadIndicesLineFractal GetThreadIndices(
    vtkm::Id threadIndex,
    const OutToInPortalType& outToIn,
    const VisitPortalType& visit,
    const ThreadToOutType& threadToOut,
    const InputDomainType& inputPoints,
    vtkm::Id globalThreadIndexOffset) const
  {
    vtkm::Id outputIndex = threadToOut.Get(threadIndex);
    vtkm::Id inputIndex = outToIn.Get(outputIndex);
    vtkm::IdComponent visitIndex = visit.Get(outputIndex);
    return vtkm::exec::arg::ThreadIndicesLineFractal(threadIndex,
                                                     inputIndex,
                                                     visitIndex,
                                                     outputIndex,
                                                     inputPoints,
                                                     globalThreadIndexOffset);
  }
  ////
  //// END-EXAMPLE GetThreadIndices.cxx
  ////
};

} // namespace worklet
} // namespace vtkm
////
//// END-EXAMPLE WorkletLineFractal.h
////

////
//// BEGIN-EXAMPLE DispatcherLineFractal.h
////
namespace vtkm
{
namespace worklet
{

////
//// BEGIN-EXAMPLE DispatcherSuperclass.cxx
////
////
//// BEGIN-EXAMPLE DispatcherTemplate.cxx
////
template<typename WorkletType>
class DispatcherLineFractal
  ////
  //// END-EXAMPLE DispatcherTemplate.cxx
  ////
  : public vtkm::worklet::internal::DispatcherBase<
      DispatcherLineFractal<WorkletType>,
      WorkletType,
      vtkm::worklet::WorkletLineFractal>
////
//// END-EXAMPLE DispatcherSuperclass.cxx
////
{
  using Superclass =
    vtkm::worklet::internal::DispatcherBase<DispatcherLineFractal<WorkletType>,
                                            WorkletType,
                                            vtkm::worklet::WorkletLineFractal>;
  using ScatterType = typename Superclass::ScatterType;

public:
  ////
  //// BEGIN-EXAMPLE DispatcherConstructor.cxx
  ////
  // If you get a compile error here about there being no appropriate constructor
  // for ScatterType, then that probably means that the worklet you are trying to
  // execute has defined a custom ScatterType and that you need to create one
  // (because there is no default way to construct the scatter). By convention,
  // worklets that define a custom scatter type usually provide a static method
  // named MakeScatter that constructs a scatter object.
  VTKM_CONT
  DispatcherLineFractal(const WorkletType& worklet = WorkletType(),
                        const ScatterType& scatter = ScatterType())
    : Superclass(worklet, scatter)
  {
  }

  VTKM_CONT
  DispatcherLineFractal(const ScatterType& scatter)
    : Superclass(WorkletType(), scatter)
  {
  }
  ////
  //// END-EXAMPLE DispatcherConstructor.cxx
  ////

  ////
  //// BEGIN-EXAMPLE DispatcherDoInvokePrototype.cxx
  ////
  template<typename Invocation>
  VTKM_CONT void DoInvoke(Invocation& invocation) const
  ////
  //// END-EXAMPLE DispatcherDoInvokePrototype.cxx
  ////
  {
    ////
    //// BEGIN-EXAMPLE CheckInputDomainType.cxx
    ////
    // Get the control signature tag for the input domain.
    using InputDomainTag = typename Invocation::InputDomainTag;

    // If you get a compile error on this line, then you have set the input
    // domain to something that is not a SegmentsIn parameter, which is not
    // valid.
    VTKM_STATIC_ASSERT(
      (std::is_same<InputDomainTag,
                    vtkm::worklet::WorkletLineFractal::SegmentsIn>::value));

    // This is the type for the input domain
    using InputDomainType = typename Invocation::InputDomainType;

    // If you get a compile error on this line, then you have tried to use
    // something that is not a vtkm::cont::ArrayHandle as the input domain to a
    // topology operation (that operates on a cell set connection domain).
    VTKM_IS_ARRAY_HANDLE(InputDomainType);
    ////
    //// END-EXAMPLE CheckInputDomainType.cxx
    ////

    ////
    //// BEGIN-EXAMPLE CallBasicInvoke.cxx
    ////
    // We can pull the input domain parameter (the data specifying the input
    // domain) from the invocation object.
    const InputDomainType& inputDomain = invocation.GetInputDomain();

    // Now that we have the input domain, we can extract the range of the
    // scheduling and call BasicInvoke.
    this->BasicInvoke(invocation, inputDomain.GetNumberOfValues() / 2);
    ////
    //// END-EXAMPLE CallBasicInvoke.cxx
    ////
  }
};

} // namespace worklet
} // namespace vtkm
////
//// END-EXAMPLE DispatcherLineFractal.h
////

////
//// BEGIN-EXAMPLE KochSnowflake.cxx
////
struct KochSnowflake
{
  struct FractalWorklet : vtkm::worklet::WorkletLineFractal
  {
    using ControlSignature = void(SegmentsIn, SegmentsOut<4>);
    using ExecutionSignature = void(Transform, _2);
    using InputDomain = _1;

    template<typename SegmentsOutVecType>
    void operator()(const vtkm::exec::LineFractalTransform& transform,
                    SegmentsOutVecType& segmentsOutVec) const
    {
      segmentsOutVec[0][0] = transform(0.00f, 0.00f);
      segmentsOutVec[0][1] = transform(0.33f, 0.00f);

      segmentsOutVec[1][0] = transform(0.33f, 0.00f);
      segmentsOutVec[1][1] = transform(0.50f, 0.29f);

      segmentsOutVec[2][0] = transform(0.50f, 0.29f);
      segmentsOutVec[2][1] = transform(0.67f, 0.00f);

      segmentsOutVec[3][0] = transform(0.67f, 0.00f);
      segmentsOutVec[3][1] = transform(1.00f, 0.00f);
    }
  };

  VTKM_CONT static vtkm::cont::ArrayHandle<vtkm::Vec2f> Run(
    vtkm::IdComponent numIterations)
  {
    vtkm::cont::ArrayHandle<vtkm::Vec2f> points;

    // Initialize points array with a single line
    points.Allocate(2);
    points.GetPortalControl().Set(0, vtkm::Vec2f(0.0f, 0.0f));
    points.GetPortalControl().Set(1, vtkm::Vec2f(1.0f, 0.0f));

    vtkm::worklet::DispatcherLineFractal<KochSnowflake::FractalWorklet> dispatcher;

    for (vtkm::IdComponent i = 0; i < numIterations; ++i)
    {
      vtkm::cont::ArrayHandle<vtkm::Vec2f> outPoints;
      dispatcher.Invoke(points, outPoints);
      points = outPoints;
    }

    return points;
  }
};
////
//// END-EXAMPLE KochSnowflake.cxx
////

static void TryKoch()
{
  // Demonstrate a single line.
  vtkm::cont::ArrayHandle<vtkm::Vec2f> points;

  points = KochSnowflake::Run(1);
  WriteSVG("Koch1.svg", points);

  for (vtkm::Id index = 0; index < points.GetNumberOfValues() / 2; ++index)
  {
    std::cout << index << ": " << points.GetPortalConstControl().Get(index * 2 + 0)
              << " " << points.GetPortalConstControl().Get(index * 2 + 1)
              << std::endl;
  }

  points = KochSnowflake::Run(2);
  WriteSVG("Koch2.svg", points);

  for (vtkm::Id index = 0; index < points.GetNumberOfValues() / 2; ++index)
  {
    std::cout << index << ": " << points.GetPortalConstControl().Get(index * 2 + 0)
              << " " << points.GetPortalConstControl().Get(index * 2 + 1)
              << std::endl;
  }

  points = KochSnowflake::Run(5);
  WriteSVG("Koch5.svg", points, 0.1f);
}

////
//// BEGIN-EXAMPLE QuadraticType2.cxx
////
struct QuadraticType2
{
  struct FractalWorklet : vtkm::worklet::WorkletLineFractal
  {
    using ControlSignature = void(SegmentsIn, SegmentsOut<8>);
    using ExecutionSignature = void(Transform, _2);
    using InputDomain = _1;

    template<typename SegmentsOutVecType>
    void operator()(const vtkm::exec::LineFractalTransform& transform,
                    SegmentsOutVecType& segmentsOutVec) const
    {
      segmentsOutVec[0][0] = transform(0.00f, 0.00f);
      segmentsOutVec[0][1] = transform(0.25f, 0.00f);

      segmentsOutVec[1][0] = transform(0.25f, 0.00f);
      segmentsOutVec[1][1] = transform(0.25f, 0.25f);

      segmentsOutVec[2][0] = transform(0.25f, 0.25f);
      segmentsOutVec[2][1] = transform(0.50f, 0.25f);

      segmentsOutVec[3][0] = transform(0.50f, 0.25f);
      segmentsOutVec[3][1] = transform(0.50f, 0.00f);

      segmentsOutVec[4][0] = transform(0.50f, 0.00f);
      segmentsOutVec[4][1] = transform(0.50f, -0.25f);

      segmentsOutVec[5][0] = transform(0.50f, -0.25f);
      segmentsOutVec[5][1] = transform(0.75f, -0.25f);

      segmentsOutVec[6][0] = transform(0.75f, -0.25f);
      segmentsOutVec[6][1] = transform(0.75f, 0.00f);

      segmentsOutVec[7][0] = transform(0.75f, 0.00f);
      segmentsOutVec[7][1] = transform(1.00f, 0.00f);
    }
  };

  VTKM_CONT static vtkm::cont::ArrayHandle<vtkm::Vec2f> Run(
    vtkm::IdComponent numIterations)
  {
    vtkm::cont::ArrayHandle<vtkm::Vec2f> points;

    // Initialize points array with a single line
    points.Allocate(2);
    points.GetPortalControl().Set(0, vtkm::Vec2f(0.0f, 0.0f));
    points.GetPortalControl().Set(1, vtkm::Vec2f(1.0f, 0.0f));

    vtkm::worklet::DispatcherLineFractal<QuadraticType2::FractalWorklet> dispatcher;

    for (vtkm::IdComponent i = 0; i < numIterations; ++i)
    {
      vtkm::cont::ArrayHandle<vtkm::Vec2f> outPoints;
      dispatcher.Invoke(points, outPoints);
      points = outPoints;
    }

    return points;
  }
};
////
//// END-EXAMPLE QuadraticType2.cxx
////

static void TryQuadraticType2()
{
  // Demonstrate a single line.
  vtkm::cont::ArrayHandle<vtkm::Vec2f> points;

  points = QuadraticType2::Run(1);
  WriteSVG("QuadraticType2_1.svg", points);

  for (vtkm::Id index = 0; index < points.GetNumberOfValues() / 2; ++index)
  {
    std::cout << index << ": " << points.GetPortalConstControl().Get(index * 2 + 0)
              << " " << points.GetPortalConstControl().Get(index * 2 + 1)
              << std::endl;
  }

  points = QuadraticType2::Run(2);
  WriteSVG("QuadraticType2_2.svg", points);

  for (vtkm::Id index = 0; index < points.GetNumberOfValues() / 2; ++index)
  {
    std::cout << index << ": " << points.GetPortalConstControl().Get(index * 2 + 0)
              << " " << points.GetPortalConstControl().Get(index * 2 + 1)
              << std::endl;
  }

  points = QuadraticType2::Run(4);
  WriteSVG("QuadraticType2_4.svg", points, 0.1f);
}

////
//// BEGIN-EXAMPLE DragonFractal.cxx
////
struct DragonFractal
{
  struct FractalWorklet : vtkm::worklet::WorkletLineFractal
  {
    using ControlSignature = void(SegmentsIn, SegmentsOut<2>);
    using ExecutionSignature = void(Transform, _2);
    using InputDomain = _1;

    template<typename SegmentsOutVecType>
    void operator()(const vtkm::exec::LineFractalTransform& transform,
                    SegmentsOutVecType& segmentsOutVec) const
    {
      segmentsOutVec[0][0] = transform(0.5f, 0.5f);
      segmentsOutVec[0][1] = transform(0.0f, 0.0f);

      segmentsOutVec[1][0] = transform(0.5f, 0.5f);
      segmentsOutVec[1][1] = transform(1.0f, 0.0f);
    }
  };

  VTKM_CONT static vtkm::cont::ArrayHandle<vtkm::Vec2f> Run(
    vtkm::IdComponent numIterations)
  {
    vtkm::cont::ArrayHandle<vtkm::Vec2f> points;

    // Initialize points array with a single line
    points.Allocate(2);
    points.GetPortalControl().Set(0, vtkm::Vec2f(0.0f, 0.0f));
    points.GetPortalControl().Set(1, vtkm::Vec2f(1.0f, 0.0f));

    vtkm::worklet::DispatcherLineFractal<DragonFractal::FractalWorklet> dispatcher;

    for (vtkm::IdComponent i = 0; i < numIterations; ++i)
    {
      vtkm::cont::ArrayHandle<vtkm::Vec2f> outPoints;
      dispatcher.Invoke(points, outPoints);
      points = outPoints;
    }

    return points;
  }
};
////
//// END-EXAMPLE DragonFractal.cxx
////

static void TryDragon()
{
  // Demonstrate a single line.
  vtkm::cont::ArrayHandle<vtkm::Vec2f> points;

  for (vtkm::IdComponent numIterations = 1; numIterations <= 13; ++numIterations)
  {
    points = DragonFractal::Run(numIterations);
    char filename[FILENAME_MAX];
    sprintf(filename, "Dragon%02d.svg", numIterations);
    WriteSVG(filename, points, 2.0f / numIterations);
  }
}

////
//// BEGIN-EXAMPLE HilbertCurve.cxx
////
struct HilbertCurve
{
  struct FractalWorklet : vtkm::worklet::WorkletLineFractal
  {
    using ControlSignature = void(SegmentsIn,
                                  FieldIn directionIn,
                                  SegmentsOut<4>,
                                  FieldOut directionOut);
    using ExecutionSignature = void(Transform, _2, _3, _4);
    using InputDomain = _1;

    template<typename SegmentsOutVecType>
    void operator()(const vtkm::exec::LineFractalTransform& transform,
                    vtkm::Int8 directionIn,
                    SegmentsOutVecType& segmentsOutVec,
                    vtkm::Vec4i_8& directionOut) const
    {
      segmentsOutVec[0][0] = transform(0.0f, directionIn * 0.0f);
      segmentsOutVec[0][1] = transform(0.0f, directionIn * 0.5f);
      directionOut[0] = -directionIn;

      segmentsOutVec[1][0] = transform(0.0f, directionIn * 0.5f);
      segmentsOutVec[1][1] = transform(0.5f, directionIn * 0.5f);
      directionOut[1] = directionIn;

      segmentsOutVec[2][0] = transform(0.5f, directionIn * 0.5f);
      segmentsOutVec[2][1] = transform(1.0f, directionIn * 0.5f);
      directionOut[2] = directionIn;

      segmentsOutVec[3][0] = transform(1.0f, directionIn * 0.5f);
      segmentsOutVec[3][1] = transform(1.0f, directionIn * 0.0f);
      directionOut[3] = -directionIn;
    }
  };

  VTKM_CONT static vtkm::cont::ArrayHandle<vtkm::Vec2f> Run(
    vtkm::IdComponent numIterations)
  {
    vtkm::cont::ArrayHandle<vtkm::Vec2f> points;

    // Initialize points array with a single line
    points.Allocate(2);
    points.GetPortalControl().Set(0, vtkm::Vec2f(0.0f, 0.0f));
    points.GetPortalControl().Set(1, vtkm::Vec2f(1.0f, 0.0f));

    vtkm::cont::ArrayHandle<vtkm::Int8> directions;

    // Initialize direction with positive.
    directions.Allocate(1);
    directions.GetPortalControl().Set(0, 1);

    vtkm::worklet::DispatcherLineFractal<HilbertCurve::FractalWorklet> dispatcher;

    for (vtkm::IdComponent i = 0; i < numIterations; ++i)
    {
      vtkm::cont::ArrayHandle<vtkm::Vec2f> outPoints;
      vtkm::cont::ArrayHandle<vtkm::Int8> outDirections;
      dispatcher.Invoke(points,
                        directions,
                        outPoints,
                        vtkm::cont::make_ArrayHandleGroupVec<4>(outDirections));
      points = outPoints;
      directions = outDirections;
    }

    return points;
  }
};
////
//// END-EXAMPLE HilbertCurve.cxx
////

static void TryHilbert()
{
  // Demonstrate a single line.
  vtkm::cont::ArrayHandle<vtkm::Vec2f> points;

  for (vtkm::IdComponent numIterations = 1; numIterations <= 6; ++numIterations)
  {
    points = HilbertCurve::Run(numIterations);
    char filename[FILENAME_MAX];
    sprintf(filename, "Hilbert%02d.svg", numIterations);
    WriteSVG(filename, points, 2.0f / numIterations);
  }
}

////
//// BEGIN-EXAMPLE TreeFractal.cxx
////
struct TreeFractal
{
  struct FractalWorklet : vtkm::worklet::WorkletLineFractal
  {
    using ControlSignature = void(SegmentsIn,
                                  SegmentsOut<1>,
                                  FieldOut countNextIteration);
    using ExecutionSignature = void(Transform, VisitIndex, _2, _3);
    using InputDomain = _1;

    using ScatterType = vtkm::worklet::ScatterCounting;

    template<typename Storage>
    VTKM_CONT static ScatterType MakeScatter(
      const vtkm::cont::ArrayHandle<vtkm::IdComponent, Storage>& count)
    {
      return ScatterType(count);
    }

    template<typename SegmentsOutVecType>
    void operator()(const vtkm::exec::LineFractalTransform& transform,
                    vtkm::IdComponent visitIndex,
                    SegmentsOutVecType& segmentsOutVec,
                    vtkm::IdComponent& countNextIteration) const
    {
      switch (visitIndex)
      {
        case 0:
          segmentsOutVec[0][0] = transform(0.0f, 0.0f);
          segmentsOutVec[0][1] = transform(1.0f, 0.0f);
          countNextIteration = 1;
          break;
        case 1:
          segmentsOutVec[0][0] = transform(1.0f, 0.0f);
          segmentsOutVec[0][1] = transform(1.5f, -0.25f);
          countNextIteration = 3;
          break;
        case 2:
          segmentsOutVec[0][0] = transform(1.0f, 0.0f);
          segmentsOutVec[0][1] = transform(1.5f, 0.35f);
          countNextIteration = 3;
          break;
        default:
          this->RaiseError("Unexpected visit index.");
      }
    }
  };

  VTKM_CONT static vtkm::cont::ArrayHandle<vtkm::Vec2f> Run(
    vtkm::IdComponent numIterations)
  {
    vtkm::cont::ArrayHandle<vtkm::Vec2f> points;

    // Initialize points array with a single line
    points.Allocate(2);
    points.GetPortalControl().Set(0, vtkm::Vec2f(0.0f, 0.0f));
    points.GetPortalControl().Set(1, vtkm::Vec2f(0.0f, 1.0f));

    vtkm::cont::ArrayHandle<vtkm::IdComponent> count;

    // Initialize count array with 3 (meaning iterate)
    count.Allocate(1);
    count.GetPortalControl().Set(0, 3);

    for (vtkm::IdComponent i = 0; i < numIterations; ++i)
    {
      vtkm::worklet::DispatcherLineFractal<TreeFractal::FractalWorklet> dispatcher(
        FractalWorklet::MakeScatter(count));

      vtkm::cont::ArrayHandle<vtkm::Vec2f> outPoints;
      dispatcher.Invoke(points, outPoints, count);
      points = outPoints;
    }

    return points;
  }
};
////
//// END-EXAMPLE TreeFractal.cxx
////

static void TryTree()
{
  // Demonstrate a single line.
  vtkm::cont::ArrayHandle<vtkm::Vec2f> points;

  for (vtkm::IdComponent numIterations = 1; numIterations <= 8; ++numIterations)
  {
    points = TreeFractal::Run(numIterations);
    char filename[FILENAME_MAX];
    sprintf(filename, "Tree%02d.svg", numIterations);
    WriteSVG(filename, points, 2.0f / numIterations);
  }
}

static void RunTests()
{
  TryVecLineSegments();
  TryKoch();
  TryQuadraticType2();
  TryDragon();
  TryHilbert();
  TryTree();
}

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