namespace tf {

/** @page ParallelTransforms Parallel Transforms

%Taskflow provides template functions for constructing tasks to 
perform parallel transforms over ranges of items.

@tableofcontents

@section ParallelTransformsInclude Include the Header

You need to include the header file, <tt>taskflow/algorithm/transform.hpp</tt>,
for creating a parallel-transform task.

@code{.cpp}
#include <taskflow/algorithm/transform.hpp>
@endcode

@section ParallelTransformsOverARange Create a Unary Parallel-Transform Task

Parallel-transform transforms a range of items, possibly
with a different type for the transformed data, and stores the result in another range.
The task created by tf::Taskflow::transform(B first1, E last1, O d_first, C c, P&& part)
is equivalent to a parallel execution of the following loop:

@code{.cpp}
while (first1 != last1) {
  *d_first++ = c(*first1++);
}
@endcode

tf::Taskflow::transform
simultaneously applies the callable @c c to the object obtained by dereferencing 
every iterator in the range <tt>[first1, last1)</tt> and stores
the result in another range beginning at @c d_first.
It is user's responsibility for ensuring the range is valid 
within the execution of the parallel-transform task. 

@code{.cpp}
std::vector<int> src = {1, 2, 3, 4, 5};
std::vector<int> tgt(src.size());
taskflow.transform(src.begin(), src.end(), tgt.begin(), [](int i){ 
  std::cout << "transforming item " << i << " to " << i + 1 << '\n';
  return i + 1;
});
@endcode

@section ParallelTransformsCaptureIteratorsByReference Capture Iterators by Reference

You can pass iterators by reference using @std_ref
to marshal parameter update between dependent tasks.
This is especially useful when the range is unknown
at the time of creating a parallel-transform task,
but needs initialization from another task.

@code{.cpp}
std::vector<int> src, tgt;
std::vector<int>::iterator first, last, d_first;

tf::Task init = taskflow.emplace([&](){
  src.resize(1000);
  tgt.resize(1000);
  first   = src.begin();
  last    = src.end();
  d_first = tgt.begin();
});

tf::Task transform = taskflow.for_each(
  std::ref(first), std::ref(last), std::ref(d_first), 
  [&](int i) {
    std::cout << "transforming item " << i << " to " << i + 1 << '\n';
    return i+1;
  }
);

init.precede(transform);
@endcode

When @c init finishes, the parallel-transform task @c transform 
will see @c first pointing to the beginning of @c src and 
@c last pointing to the end of @c src.
Then, it simultaneously transforms these 1000 items 
by adding one to each element and stores the result 
in another range starting at @c d_first.

@section ParallelBinaryTransformsOverARange Create a Binary Parallel-Transform Task

You can use the overload, 
tf::Taskflow::transform(B1 first1, E1 last1, B2 first2, O d_first, C c, P&& part),
to perform parallel transforms on two source ranges pointed by
@c first1 and @c first2 using the binary
operator @c c
and store the result in another range pointed by @c d_first.
This method is equivalent to the parallel execution of the following loop:
    
@code{.cpp}
while (first1 != last1) {
  *d_first++ = c(*first1++, *first2++);
}
@endcode

The following example creates a parallel-transform task 
that adds two ranges of elements one by one
and stores the result in a target range:

@code{.cpp}
std::vector<int> src1 = {1, 2, 3, 4, 5};
std::vector<int> src2 = {5, 4, 3, 2, 1};
std::vector<int> tgt(src1.size());
taskflow.transform(
  src1.begin(), src1.end(), src2.begin(), tgt.begin(), 
  [](int i, int j){ 
    return i + j;
  }
);
@endcode

@section ParallelTransformsCfigureAPartitioner Configure a Partitioner

You can configure a partitioner for parallel-transform tasks to run with different
scheduling methods, such as guided partitioning, dynamic partitioning, and static partitioning.
The following example creates two parallel-transform tasks using two different
partitioners, one with the static partitioning algorithm and 
another one with the guided partitioning algorithm:

@code{.cpp}
tf::StaticPartitioner static_partitioner;
tf::GuidedPartitioner guided_partitioner;

std::vector<int> src1 = {1, 2, 3, 4, 5};
std::vector<int> src2 = {5, 4, 3, 2, 1};
std::vector<int> tgt1(src1.size());
std::vector<int> tgt2(src2.size());

// create a parallel-transform task with static execution partitioner
taskflow.transform(
  src1.begin(), src1.end(), src2.begin(), tgt1.begin(), 
  [](int i, int j){ 
    return i + j;
  },
  static_partitioner
);

// create a parallel-transform task with guided execution partitioner
taskflow.transform(
  src1.begin(), src1.end(), src2.begin(), tgt2.begin(), 
  [](int i, int j){ 
    return i + j;
  },
  guided_partitioner
);
@endcode

@note
By default, parallel-transform tasks use tf::DefaultPartitioner
if no partitioner is specified. 

*/

}






