28template <std::
integral T>
30 return ((step == T{0} && beg != end) ||
31 (beg < end && step <= T{0}) ||
32 (beg > end && step >= T{0}));
70template <std::
integral T>
71constexpr size_t distance(T beg, T end, T step) {
72 if constexpr (std::is_unsigned_v<T>) {
74 ?
static_cast<size_t>((end - beg + step - T{1}) / step)
77 return static_cast<size_t>(
78 std::max(T{0}, (end - beg + step + (step > T{0} ? T{-1} : T{1})) / step)
187template <std::
integral T,
size_t N = 1>
200 static constexpr size_t rank = N;
251 template <
typename... Ranges>
252 requires (
sizeof...(Ranges) == N) &&
255 : _dims{ ranges.
dim(0)... } {}
273 explicit IndexRanges(
const std::array<std::tuple<T, T, T>, N>& dims) : _dims{dims} {}
297 const std::tuple<T, T, T>&
dim(
size_t d)
const {
return _dims[d]; }
330 std::tuple<T, T, T>&
dim(
size_t d) {
return _dims[d]; }
346 T
begin() const requires (N == 1) {
return std::get<0>(_dims[0]); }
358 T
end() const requires (N == 1) {
return std::get<1>(_dims[0]); }
370 T
step_size() const requires (N == 1) {
return std::get<2>(_dims[0]); }
561 size_t ceil(
size_t chunk_size)
const requires (N > 1);
591 size_t floor(
size_t chunk_size)
const requires (N > 1);
659 std::array<std::tuple<T, T, T>, N> _dims;
666template <std::integral T,
size_t N>
673template <std::
integral T,
size_t N>
676 std::get<0>(_dims[0]) = new_begin;
680template <std::
integral T,
size_t N>
683 std::get<1>(_dims[0]) = new_end;
687template <std::
integral T,
size_t N>
690 std::get<2>(_dims[0]) = new_step_size;
694template <std::
integral T,
size_t N>
697 auto [beg,
end, step] = _dims[0];
699 static_cast<T
>(part_beg) * step + beg,
700 static_cast<T
>(part_end) * step + beg,
705template <std::
integral T,
size_t N>
707 if constexpr (N == 1) {
714 auto compute = [
this]<
size_t D>(
auto& self,
size_t total) ->
size_t {
715 if constexpr (D == N) {
718 size_t s = this->
size(D);
719 if (s == 0)
return D == 0 ? 0 : total;
720 return self.template operator()<D + 1>(self, total * s);
723 return compute.template operator()<0>(compute, 1);
731template <std::
integral T,
size_t N>
734 std::array<std::tuple<T, T, T>, N> box_dims = _dims;
735 box_dims[0] = std::tuple<T, T, T>{
736 std::get<0>(_dims[0]), std::get<0>(_dims[0]), std::get<2>(_dims[0])
741template <std::
integral T,
size_t N>
746 for (
size_t d = 0; d < N; ++d) {
747 dim_sizes[d] =
size(d);
748 if (dim_sizes[d] == 0) { d_active = d;
break; }
750 if (d_active == 0)
return 0;
754 size_t inner_volume = 1;
755 if (inner_volume >= chunk_size)
return inner_volume;
756 for (
size_t d = d_active; d-- > 0; ) {
757 inner_volume *= dim_sizes[d];
758 if (inner_volume >= chunk_size)
return inner_volume;
763template <std::
integral T,
size_t N>
768 for (
size_t d = 0; d < N; ++d) {
769 dim_sizes[d] =
size(d);
770 if (dim_sizes[d] == 0) { d_active = d;
break; }
772 if (d_active == 0)
return 0;
776 size_t inner_volume = 1;
778 for (
size_t d = d_active; d-- > 0; ) {
779 size_t next = inner_volume * dim_sizes[d];
780 if (next > chunk_size)
break;
782 last_fit = inner_volume;
787template <std::
integral T,
size_t N>
791 if (chunk_size == 0) {
800 for (
size_t d = 0; d < N; ++d) {
801 dim_sizes[d] =
size(d);
802 if (dim_sizes[d] == 0) { d_active = d;
break; }
805 if (d_active == 0)
return *
this;
808 size_t coords[N] = {};
809 size_t temp = flat_beg;
810 for (
size_t d = d_active; d-- > 0; ) {
811 coords[d] = temp % dim_sizes[d];
812 temp /= dim_sizes[d];
817 size_t grow_dim = d_active - 1;
818 size_t inner_volume = 1;
819 size_t active_inner_vol = 1;
821 for (
size_t d = d_active; d-- > 0; ) {
823 if (d + 1 < d_active && coords[d + 1] != 0)
break;
827 active_inner_vol = inner_volume;
829 if (inner_volume >= chunk_size)
break;
831 inner_volume *= dim_sizes[d];
835 size_t steps_left = dim_sizes[grow_dim] - coords[grow_dim];
836 size_t steps_needed = (std::max)(
size_t{1}, chunk_size / active_inner_vol);
837 size_t steps_to_take = (std::min)(steps_left, steps_needed);
843 std::array<std::tuple<T, T, T>, N> box_dims;
845 for (
size_t d = 0; d < grow_dim; ++d) {
846 const T beg = std::get<0>(_dims[d]);
847 const T step = std::get<2>(_dims[d]);
848 box_dims[d] = std::tuple<T, T, T>{
849 beg +
static_cast<T
>(coords[d]) * step,
850 beg +
static_cast<T
>(coords[d] + 1) * step,
856 const T beg = std::get<0>(_dims[grow_dim]);
857 const T step = std::get<2>(_dims[grow_dim]);
858 box_dims[grow_dim] = std::tuple<T, T, T>{
859 beg +
static_cast<T
>(coords[grow_dim]) * step,
860 beg +
static_cast<T
>(coords[grow_dim] + steps_to_take) * step,
865 for (
size_t d = grow_dim + 1; d < N; ++d) {
866 box_dims[d] = _dims[d];
872template <std::
integral T,
size_t N>
876 if (chunk_size == 0) {
883 for (
size_t d = 0; d < N; ++d) {
884 dim_sizes[d] =
size(d);
885 if (dim_sizes[d] == 0) { d_active = d;
break; }
888 if (d_active == 0)
return *
this;
891 size_t coords[N] = {};
892 size_t temp = flat_beg;
893 for (
size_t d = d_active; d-- > 0; ) {
894 coords[d] = temp % dim_sizes[d];
895 temp /= dim_sizes[d];
900 size_t grow_dim = d_active - 1;
901 size_t inner_volume = 1;
902 size_t active_inner_vol = 1;
904 for (
size_t d = d_active; d-- > 0; ) {
906 if (d + 1 < d_active && coords[d + 1] != 0)
break;
909 size_t next_vol = inner_volume * dim_sizes[d];
910 if (next_vol > chunk_size && inner_volume > 1)
break;
913 active_inner_vol = inner_volume;
914 inner_volume = next_vol;
918 size_t steps_left = dim_sizes[grow_dim] - coords[grow_dim];
919 size_t steps_needed = (std::max)(
size_t{1}, chunk_size / active_inner_vol);
920 size_t steps_to_take = (std::min)(steps_left, steps_needed);
923 std::array<std::tuple<T, T, T>, N> box_dims;
925 for (
size_t d = 0; d < grow_dim; ++d) {
926 const T beg = std::get<0>(_dims[d]);
927 const T step = std::get<2>(_dims[d]);
928 box_dims[d] = std::tuple<T, T, T>{
929 beg +
static_cast<T
>(coords[d]) * step,
930 beg +
static_cast<T
>(coords[d] + 1) * step,
936 const T beg = std::get<0>(_dims[grow_dim]);
937 const T step = std::get<2>(_dims[grow_dim]);
938 box_dims[grow_dim] = std::tuple<T, T, T>{
939 beg +
static_cast<T
>(coords[grow_dim]) * step,
940 beg +
static_cast<T
>(coords[grow_dim] + steps_to_take) * step,
945 for (
size_t d = grow_dim + 1; d < N; ++d) {
946 box_dims[d] = _dims[d];
970template <std::
integral T>
992template <
typename T,
size_t N>
1003template <
typename R>
1016template <
typename R>
1018 (std::decay_t<std::unwrap_ref_decay_t<R>>::rank == 1);
1031template <
typename R>
1033 (std::decay_t<std::unwrap_ref_decay_t<R>>::rank > 1);
class to create an N-dimensional index range of integral indices
Definition iterator.hpp:188
IndexRanges & reset(T beg, T end, T step_size)
updates the range with a new starting index, ending index, and step size (only available when N == 1)
Definition iterator.hpp:668
IndexRanges upper_slice(size_t flat_beg, size_t chunk_size) const
returns the smallest perfectly-aligned hyperbox reachable from flat_beg, rounding up to the next hype...
Definition iterator.hpp:789
T end() const
Definition iterator.hpp:358
const std::tuple< T, T, T > & dim(size_t d) const
Definition iterator.hpp:297
IndexRanges & end(T new_end)
updates the ending index of the range (only available when N == 1)
Definition iterator.hpp:682
size_t ceil(size_t chunk_size) const
returns the smallest hyperplane-aligned chunk size that is >= chunk_size, capped at size() when chunk...
Definition iterator.hpp:742
std::tuple< T, T, T > & dim(size_t d)
returns the (begin, end, step) tuple for dimension d (mutable)
Definition iterator.hpp:330
IndexRanges unravel(size_t part_beg, size_t part_end) const
maps a contiguous index partition back to the corresponding subrange (only available when N == 1)
Definition iterator.hpp:696
IndexRanges & step_size(T new_step_size)
updates the step size of the range (only available when N == 1)
Definition iterator.hpp:689
static constexpr size_t rank
Definition iterator.hpp:200
IndexRanges()=default
constructs an index range without initialization
size_t size(size_t d) const
returns the number of iterations along dimension d
Definition iterator.hpp:491
IndexRanges(Ranges &&... ranges)
constructs an N-D index range from N 1D ranges
Definition iterator.hpp:254
IndexRanges lower_slice(size_t flat_beg, size_t chunk_size) const
returns the largest perfectly-aligned hyperbox reachable from flat_beg whose size does not exceed chu...
Definition iterator.hpp:874
size_t size() const
returns the number of active flat iterations
Definition iterator.hpp:706
T index_type
alias for the index type
Definition iterator.hpp:195
IndexRanges(const std::array< std::tuple< T, T, T >, N > &dims)
constructs an index range from an array of (begin, end, step) tuples
Definition iterator.hpp:273
size_t floor(size_t chunk_size) const
returns the largest hyperplane-aligned chunk size that is <= chunk_size (only available when N > 1)
Definition iterator.hpp:764
IndexRanges empty_box() const
returns a box whose size() is 0 (only available when N > 1)
Definition iterator.hpp:733
IndexRanges(T beg, T end, T step_size)
constructs a 1D index range (only available when N == 1)
Definition iterator.hpp:226
IndexRanges & begin(T new_begin)
updates the starting index of the range (only available when N == 1)
Definition iterator.hpp:675
T begin() const
queries the starting index of the range (only available when N == 1)
Definition iterator.hpp:346
T step_size() const
Definition iterator.hpp:370
concept to check if a type is a tf::IndexRanges<T, 1> (i.e., tf::IndexRange<T>)
Definition iterator.hpp:1017
concept to check if a type is a tf::IndexRanges, regardless of dimensionality
Definition iterator.hpp:1004
concept to check if a type is a tf::IndexRanges<T, N> with rank > 1
Definition iterator.hpp:1032
taskflow namespace
Definition small_vector.hpp:20
IndexRanges< T, 1 > IndexRange
alias for the common 1D case of tf::IndexRanges
Definition iterator.hpp:971
constexpr bool is_index_ranges_v
base type trait to detect if a type is a tf::IndexRanges
Definition iterator.hpp:982
constexpr size_t distance(T beg, T end, T step)
calculates the number of iterations in the given index range
Definition iterator.hpp:71
constexpr bool is_index_range_invalid(T beg, T end, T step)
checks if the given index range is invalid
Definition iterator.hpp:29