From 4b5e5a3a3708927a053c58a77d71b8d563b398fe Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Wed, 25 Mar 2026 15:47:24 -0700 Subject: [PATCH 01/18] add ZeroTo RangeSegment --- examples/CMakeLists.txt | 4 ++ examples/zero-to-segment.cpp | 57 +++++++++++++++++++++++++++ include/RAJA/index/RangeSegment.hpp | 27 +++++++++++++ test/unit/index/test-rangesegment.cpp | 20 ++++++++++ 4 files changed, 108 insertions(+) create mode 100644 examples/zero-to-segment.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index e77b5dc493..cc55eee9c4 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -19,6 +19,10 @@ raja_add_executable( NAME resource-forall SOURCES resource-forall.cpp) +raja_add_executable( + NAME zero-to-segment + SOURCES zero-to-segment.cpp) + raja_add_executable( NAME dynamic-forall SOURCES dynamic-forall.cpp) diff --git a/examples/zero-to-segment.cpp b/examples/zero-to-segment.cpp new file mode 100644 index 0000000000..05daddc868 --- /dev/null +++ b/examples/zero-to-segment.cpp @@ -0,0 +1,57 @@ +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +// Copyright (c) Lawrence Livermore National Security, LLC and other +// RAJA Project Developers. See top-level LICENSE and COPYRIGHT +// files for dates and other details. No copyright assignment is required +// to contribute to RAJA. +// +// SPDX-License-Identifier: (BSD-3-Clause) +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +#include + +#include "RAJA/RAJA.hpp" + +/* + * ZeroTo helper example + * + * Demonstrates two convenience forms for building the half-open interval + * [0, N): + * 1. RAJA::ZeroTo(N) for the default RAJA::Index_type-based RangeSegment + * 2. RAJA::ZeroTo(N) for a TypedRangeSegment + */ + +RAJA_INDEX_VALUE(CellIndex, "CellIndex"); + +int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) +{ + constexpr RAJA::Index_type N = 8; + + int values[N] = {}; + int* values_ptr = values; + int typed_values[N] = {}; + int* typed_values_ptr = typed_values; + + // Build a RangeSegment over [0, N) using the default RAJA::Index_type. + RAJA::forall(RAJA::ZeroTo(N), [=](RAJA::Index_type i) { + values_ptr[i] = static_cast(i * i); + }); + + // Build a TypedRangeSegment over the same [0, N) interval. + RAJA::forall(RAJA::ZeroTo(N), [=](CellIndex i) { + typed_values_ptr[*i] = static_cast(*i + 10); + }); + + std::cout << "values:"; + for (auto i : RAJA::ZeroTo(N)) { + std::cout << ' ' << values[i]; + } + std::cout << '\n'; + + std::cout << "typed values:"; + for (auto i : RAJA::ZeroTo(N)) { + std::cout << ' ' << typed_values[*i]; + } + std::cout << '\n'; + + return 0; +} diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index b03215a585..618e3175ec 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -530,6 +530,33 @@ using RangeSegment = TypedRangeSegment; //! Alias for TypedRangeStrideSegment using RangeStrideSegment = TypedRangeStrideSegment; +/*! + * \brief Function to make a RangeSegment for the interval [0, end) + * + * \return a newly constructed RangeSegment over the half-open interval + * starting at zero and ending at @end. + */ +RAJA_HOST_DEVICE RAJA_INLINE constexpr RangeSegment ZeroTo( + Index_type end) noexcept +{ + return {0, end}; +} + +/*! + * \brief Function to make a TypedRangeSegment for the interval [0, end) + * + * \return a newly constructed TypedRangeSegment over the half-open interval + * starting at zero and ending at @end. + */ +template +RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment ZeroTo( + strip_index_type_t end) noexcept +{ + static_assert(!std::is_floating_point>::value, + "ZeroTo requires a non-floating point index type."); + return {strip_index_type_t {0}, end}; +} + namespace detail { diff --git a/test/unit/index/test-rangesegment.cpp b/test/unit/index/test-rangesegment.cpp index 7e6d8c9100..3d493ee61c 100644 --- a/test/unit/index/test-rangesegment.cpp +++ b/test/unit/index/test-rangesegment.cpp @@ -15,6 +15,8 @@ #include "RAJA_unit-test-types.hpp" +RAJA_INDEX_VALUE(ZeroToStrongIndex, "ZeroToStrongIndex"); + template class RangeSegmentUnitTest : public ::testing::Test {}; @@ -166,3 +168,21 @@ TYPED_TEST(RangeSegmentUnitTest, Equality) ASSERT_NE(r1, r3); } + +TEST(RangeSegmentUnitTest, ZeroTo) +{ + auto r = RAJA::ZeroTo(RAJA::Index_type(17)); + + ASSERT_EQ(RAJA::RangeSegment(RAJA::Index_type(0), RAJA::Index_type(17)), r); + ASSERT_EQ(RAJA::Index_type(0), *r.begin()); + ASSERT_EQ(RAJA::Index_type(17), r.size()); +} + +TEST(RangeSegmentUnitTest, TypedZeroTo) +{ + auto r = RAJA::ZeroTo(17); + + ASSERT_EQ((RAJA::TypedRangeSegment(0, 17)), r); + ASSERT_EQ(ZeroToStrongIndex(0), *r.begin()); + ASSERT_EQ(RAJA::Index_type(17), r.size()); +} From 124338dc0ccd6a5b81c998ad03d283433185652e Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 09:53:18 -0700 Subject: [PATCH 02/18] add python like behavior for RAJA::Range --- examples/CMakeLists.txt | 4 +- examples/raja-ranges.cpp | 86 +++++++++++++++++++++++++++ examples/zero-to-segment.cpp | 57 ------------------ include/RAJA/index/RangeSegment.hpp | 56 ++++++++++++++++- test/unit/index/test-rangesegment.cpp | 40 ++++++++++--- 5 files changed, 174 insertions(+), 69 deletions(-) create mode 100644 examples/raja-ranges.cpp delete mode 100644 examples/zero-to-segment.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index cc55eee9c4..24d3831f5b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -20,8 +20,8 @@ raja_add_executable( SOURCES resource-forall.cpp) raja_add_executable( - NAME zero-to-segment - SOURCES zero-to-segment.cpp) + NAME raja-ranges + SOURCES raja-ranges.cpp) raja_add_executable( NAME dynamic-forall diff --git a/examples/raja-ranges.cpp b/examples/raja-ranges.cpp new file mode 100644 index 0000000000..eb002637dc --- /dev/null +++ b/examples/raja-ranges.cpp @@ -0,0 +1,86 @@ +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +// Copyright (c) Lawrence Livermore National Security, LLC and other +// RAJA Project Developers. See top-level LICENSE and COPYRIGHT +// files for dates and other details. No copyright assignment is required +// to contribute to RAJA. +// +// SPDX-License-Identifier: (BSD-3-Clause) +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +#include + +#include "RAJA/RAJA.hpp" + +/* + * RAJA ranges example + * + * Demonstrates Python-like range helpers backed by RAJA segments: + * 1. RAJA::Range(N) for [0, N) + * 2. RAJA::Range(N) for a TypedRangeSegment over [0, N) + * 3. RAJA::Range(begin, end) for [begin, end) + * 4. RAJA::Range(begin, end, step) for a strided half-open interval + */ + +RAJA_INDEX_VALUE(CellIndex, "CellIndex"); + +int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) +{ + constexpr RAJA::Index_type N = 8; + + int values[N] = {}; + int typed_values[N] = {}; + int subrange_values[N] = {}; + int odd_values[N] = {}; + + // Equivalent to Python range(N): [0, N) + RAJA::forall(RAJA::Range(N), [&](RAJA::Index_type i) { + values[i] = static_cast(i * i); + }); + + // Equivalent to Python range(N), but preserving a strong index type. + RAJA::forall(RAJA::Range(N), [&](CellIndex i) { + typed_values[*i] = static_cast(*i + 10); + }); + + // Equivalent to Python range(2, 6): [2, 6) + RAJA::forall(RAJA::Range(2, 6), [&](int i) { + subrange_values[i] = i; + }); + + // Equivalent to Python range(1, N, 2): odd indices in [1, N) + RAJA::forall(RAJA::Range(1, N, 2), [&](int i) { + odd_values[i] = i; + }); + + std::cout << "Range(N):"; + for (auto i : RAJA::Range(N)) { + std::cout << ' ' << values[i]; + } + std::cout << '\n'; + + std::cout << "Range(N):"; + for (auto i : RAJA::Range(N)) { + std::cout << ' ' << typed_values[*i]; + } + std::cout << '\n'; + + std::cout << "Range(2, 6):"; + for (auto i : RAJA::Range(2, 6)) { + std::cout << ' ' << subrange_values[i]; + } + std::cout << '\n'; + + std::cout << "Range(1, N, 2):"; + for (auto i : RAJA::Range(1, N, 2)) { + std::cout << ' ' << odd_values[i]; + } + std::cout << '\n'; + + std::cout << "Range(N - 1, -1, -2):"; + for (auto i : RAJA::Range(N - 1, -1, -2)) { + std::cout << ' ' << i; + } + std::cout << '\n'; + + return 0; +} diff --git a/examples/zero-to-segment.cpp b/examples/zero-to-segment.cpp deleted file mode 100644 index 05daddc868..0000000000 --- a/examples/zero-to-segment.cpp +++ /dev/null @@ -1,57 +0,0 @@ -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -// Copyright (c) Lawrence Livermore National Security, LLC and other -// RAJA Project Developers. See top-level LICENSE and COPYRIGHT -// files for dates and other details. No copyright assignment is required -// to contribute to RAJA. -// -// SPDX-License-Identifier: (BSD-3-Clause) -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - -#include - -#include "RAJA/RAJA.hpp" - -/* - * ZeroTo helper example - * - * Demonstrates two convenience forms for building the half-open interval - * [0, N): - * 1. RAJA::ZeroTo(N) for the default RAJA::Index_type-based RangeSegment - * 2. RAJA::ZeroTo(N) for a TypedRangeSegment - */ - -RAJA_INDEX_VALUE(CellIndex, "CellIndex"); - -int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) -{ - constexpr RAJA::Index_type N = 8; - - int values[N] = {}; - int* values_ptr = values; - int typed_values[N] = {}; - int* typed_values_ptr = typed_values; - - // Build a RangeSegment over [0, N) using the default RAJA::Index_type. - RAJA::forall(RAJA::ZeroTo(N), [=](RAJA::Index_type i) { - values_ptr[i] = static_cast(i * i); - }); - - // Build a TypedRangeSegment over the same [0, N) interval. - RAJA::forall(RAJA::ZeroTo(N), [=](CellIndex i) { - typed_values_ptr[*i] = static_cast(*i + 10); - }); - - std::cout << "values:"; - for (auto i : RAJA::ZeroTo(N)) { - std::cout << ' ' << values[i]; - } - std::cout << '\n'; - - std::cout << "typed values:"; - for (auto i : RAJA::ZeroTo(N)) { - std::cout << ' ' << typed_values[*i]; - } - std::cout << '\n'; - - return 0; -} diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index 618e3175ec..1dedb597a6 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -536,7 +536,7 @@ using RangeStrideSegment = TypedRangeStrideSegment; * \return a newly constructed RangeSegment over the half-open interval * starting at zero and ending at @end. */ -RAJA_HOST_DEVICE RAJA_INLINE constexpr RangeSegment ZeroTo( +RAJA_HOST_DEVICE RAJA_INLINE constexpr RangeSegment Range( Index_type end) noexcept { return {0, end}; @@ -549,11 +549,11 @@ RAJA_HOST_DEVICE RAJA_INLINE constexpr RangeSegment ZeroTo( * starting at zero and ending at @end. */ template -RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment ZeroTo( +RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment Range( strip_index_type_t end) noexcept { static_assert(!std::is_floating_point>::value, - "ZeroTo requires a non-floating point index type."); + "Range requires a non-floating point index type."); return {strip_index_type_t {0}, end}; } @@ -576,6 +576,56 @@ using common_type_t = typename common_type::type; } // namespace detail +/*! + * \brief Function to make a TypedRangeSegment for the interval [begin, end) + * + * \return a newly constructed TypedRangeSegment where the + * value_type is equivilent to the common type of + * @begin and @end. If there is no common type, then + * a compiler error will be produced. + */ +template> +RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment Range( + BeginT&& begin, + EndT&& end) noexcept +{ + return {begin, end}; +} + +/*! + * \brief Function to make a TypedRangeStrideSegment for the interval + * [begin, end) with given stride + * + * \return a newly constructed TypedRangeStrideSegment where the + * value_type is equivilent to the common type of + * @begin and @end. If stride is zero, execution aborts or throws. + */ +template, + typename DiffT = make_signed_t>> +RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment Range( + BeginT&& begin, + EndT&& end, + StrideT&& stride) +{ + static_assert(std::is_signed>::value, + "Range requires a signed stride type."); + static_assert(!std::is_floating_point>::value, + "Range requires a non-floating point stride type."); + + DiffT const typed_stride = static_cast(stride); + if (typed_stride == DiffT {0}) + { + RAJA_ABORT_OR_THROW("RAJA::Range requires a non-zero stride."); + } + + return {begin, end, typed_stride}; +} + /*! * \brief Function to make a TypedRangeSegment for the interval [begin, end) * diff --git a/test/unit/index/test-rangesegment.cpp b/test/unit/index/test-rangesegment.cpp index 3d493ee61c..e241706e17 100644 --- a/test/unit/index/test-rangesegment.cpp +++ b/test/unit/index/test-rangesegment.cpp @@ -15,7 +15,7 @@ #include "RAJA_unit-test-types.hpp" -RAJA_INDEX_VALUE(ZeroToStrongIndex, "ZeroToStrongIndex"); +RAJA_INDEX_VALUE(RangeStrongIndex, "RangeStrongIndex"); template class RangeSegmentUnitTest : public ::testing::Test {}; @@ -169,20 +169,46 @@ TYPED_TEST(RangeSegmentUnitTest, Equality) ASSERT_NE(r1, r3); } -TEST(RangeSegmentUnitTest, ZeroTo) +TEST(RangeSegmentUnitTest, RangeEnd) { - auto r = RAJA::ZeroTo(RAJA::Index_type(17)); + auto r = RAJA::Range(RAJA::Index_type(17)); ASSERT_EQ(RAJA::RangeSegment(RAJA::Index_type(0), RAJA::Index_type(17)), r); ASSERT_EQ(RAJA::Index_type(0), *r.begin()); ASSERT_EQ(RAJA::Index_type(17), r.size()); } -TEST(RangeSegmentUnitTest, TypedZeroTo) +TEST(RangeSegmentUnitTest, TypedRangeEnd) { - auto r = RAJA::ZeroTo(17); + auto r = RAJA::Range(17); - ASSERT_EQ((RAJA::TypedRangeSegment(0, 17)), r); - ASSERT_EQ(ZeroToStrongIndex(0), *r.begin()); + ASSERT_EQ((RAJA::TypedRangeSegment(0, 17)), r); + ASSERT_EQ(RangeStrongIndex(0), *r.begin()); ASSERT_EQ(RAJA::Index_type(17), r.size()); } + +TEST(RangeSegmentUnitTest, RangeBeginEnd) +{ + auto r = RAJA::Range(3, 17); + + ASSERT_EQ((RAJA::TypedRangeSegment(3, 17)), r); + ASSERT_EQ(14, r.size()); +} + +TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) +{ + auto r = RAJA::Range(2, 11, 3); + + ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); + ASSERT_EQ(4, r.size()); + ASSERT_EQ(2, *r.begin()); +} + +TEST(RangeSegmentUnitTest, RangeBeginEndStrideNegative) +{ + auto r = RAJA::Range(10, -1, -2); + + ASSERT_EQ((RAJA::TypedRangeStrideSegment(10, -1, -2)), r); + ASSERT_EQ(6, r.size()); + ASSERT_EQ(10, *r.begin()); +} From 801bd00e40b1adf6cbc7cc3150a78882e0847bee Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 13:15:24 -0700 Subject: [PATCH 03/18] pr comments --- examples/raja-ranges.cpp | 36 +++++----- include/RAJA/index/RangeSegment.hpp | 100 +++++++++++++++----------- test/unit/index/test-rangesegment.cpp | 44 ++++++++++-- 3 files changed, 115 insertions(+), 65 deletions(-) diff --git a/examples/raja-ranges.cpp b/examples/raja-ranges.cpp index eb002637dc..76196ccf09 100644 --- a/examples/raja-ranges.cpp +++ b/examples/raja-ranges.cpp @@ -15,10 +15,10 @@ * RAJA ranges example * * Demonstrates Python-like range helpers backed by RAJA segments: - * 1. RAJA::Range(N) for [0, N) - * 2. RAJA::Range(N) for a TypedRangeSegment over [0, N) - * 3. RAJA::Range(begin, end) for [begin, end) - * 4. RAJA::Range(begin, end, step) for a strided half-open interval + * 1. RAJA::range(N) for [0, N) + * 2. RAJA::range(N) for a TypedRangeSegment over [0, N) + * 3. RAJA::range(begin, end) for [begin, end) + * 4. RAJA::range(begin, end, step) for a strided half-open interval */ RAJA_INDEX_VALUE(CellIndex, "CellIndex"); @@ -33,51 +33,51 @@ int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) int odd_values[N] = {}; // Equivalent to Python range(N): [0, N) - RAJA::forall(RAJA::Range(N), [&](RAJA::Index_type i) { + RAJA::forall(RAJA::range(N), [&](RAJA::Index_type i) { values[i] = static_cast(i * i); }); // Equivalent to Python range(N), but preserving a strong index type. - RAJA::forall(RAJA::Range(N), [&](CellIndex i) { + RAJA::forall(RAJA::range(N), [&](CellIndex i) { typed_values[*i] = static_cast(*i + 10); }); // Equivalent to Python range(2, 6): [2, 6) - RAJA::forall(RAJA::Range(2, 6), [&](int i) { + RAJA::forall(RAJA::range(2, 6), [&](int i) { subrange_values[i] = i; }); // Equivalent to Python range(1, N, 2): odd indices in [1, N) - RAJA::forall(RAJA::Range(1, N, 2), [&](int i) { + RAJA::forall(RAJA::range(1, N, 2), [&](int i) { odd_values[i] = i; }); - std::cout << "Range(N):"; - for (auto i : RAJA::Range(N)) { + std::cout << "range(N):"; + for (auto i : RAJA::range(N)) { std::cout << ' ' << values[i]; } std::cout << '\n'; - std::cout << "Range(N):"; - for (auto i : RAJA::Range(N)) { + std::cout << "range(N):"; + for (auto i : RAJA::range(N)) { std::cout << ' ' << typed_values[*i]; } std::cout << '\n'; - std::cout << "Range(2, 6):"; - for (auto i : RAJA::Range(2, 6)) { + std::cout << "range(2, 6):"; + for (auto i : RAJA::range(2, 6)) { std::cout << ' ' << subrange_values[i]; } std::cout << '\n'; - std::cout << "Range(1, N, 2):"; - for (auto i : RAJA::Range(1, N, 2)) { + std::cout << "range(1, N, 2):"; + for (auto i : RAJA::range(1, N, 2)) { std::cout << ' ' << odd_values[i]; } std::cout << '\n'; - std::cout << "Range(N - 1, -1, -2):"; - for (auto i : RAJA::Range(N - 1, -1, -2)) { + std::cout << "range(N - 1, -1, -2):"; + for (auto i : RAJA::range(N - 1, -1, -2)) { std::cout << ' ' << i; } std::cout << '\n'; diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index 1dedb597a6..7290d61a32 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -530,33 +530,6 @@ using RangeSegment = TypedRangeSegment; //! Alias for TypedRangeStrideSegment using RangeStrideSegment = TypedRangeStrideSegment; -/*! - * \brief Function to make a RangeSegment for the interval [0, end) - * - * \return a newly constructed RangeSegment over the half-open interval - * starting at zero and ending at @end. - */ -RAJA_HOST_DEVICE RAJA_INLINE constexpr RangeSegment Range( - Index_type end) noexcept -{ - return {0, end}; -} - -/*! - * \brief Function to make a TypedRangeSegment for the interval [0, end) - * - * \return a newly constructed TypedRangeSegment over the half-open interval - * starting at zero and ending at @end. - */ -template -RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment Range( - strip_index_type_t end) noexcept -{ - static_assert(!std::is_floating_point>::value, - "Range requires a non-floating point index type."); - return {strip_index_type_t {0}, end}; -} - namespace detail { @@ -574,24 +547,61 @@ struct common_type template using common_type_t = typename common_type::type; +template +struct range_storage_type +{ + using type = StorageT; +}; + +template +struct range_storage_type +{ + using type = common_type_t; +}; + +template +using range_storage_type_t = typename range_storage_type::type; + } // namespace detail +/*! + * \brief Function to make a TypedRangeSegment for the interval [0, end) + * + * \return a newly constructed TypedRangeSegment over the half-open interval + * starting at zero and ending at @end. An explicit template argument + * may be used to select the segment storage type. + */ +template, + typename StripCommon = strip_index_type_t> +RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment range( + EndT&& end) noexcept +{ + static_assert(!std::is_floating_point::value, + "range requires a non-floating point index type."); + return {StripCommon {0}, static_cast(stripIndexType(end))}; +} + /*! * \brief Function to make a TypedRangeSegment for the interval [begin, end) * * \return a newly constructed TypedRangeSegment where the * value_type is equivilent to the common type of - * @begin and @end. If there is no common type, then - * a compiler error will be produced. + * @begin and @end unless an explicit template argument + * is provided for the segment storage type. */ -template> -RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment Range( + typename Common = detail::range_storage_type_t, + typename StripCommon = strip_index_type_t> +RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment range( BeginT&& begin, EndT&& end) noexcept { - return {begin, end}; + return {static_cast(stripIndexType(begin)), + static_cast(stripIndexType(end))}; } /*! @@ -600,30 +610,36 @@ RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment Range( * * \return a newly constructed TypedRangeStrideSegment where the * value_type is equivilent to the common type of - * @begin and @end. If stride is zero, execution aborts or throws. + * @begin and @end unless an explicit template argument + * is provided for the segment storage type. If stride is zero, + * execution aborts or throws. */ -template, + typename Common = detail::range_storage_type_t, typename DiffT = make_signed_t>> -RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment Range( +RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment range( BeginT&& begin, EndT&& end, StrideT&& stride) { static_assert(std::is_signed>::value, - "Range requires a signed stride type."); + "range requires a signed stride type."); static_assert(!std::is_floating_point>::value, - "Range requires a non-floating point stride type."); + "range requires a non-floating point stride type."); - DiffT const typed_stride = static_cast(stride); + DiffT const typed_stride = static_cast(stripIndexType(stride)); if (typed_stride == DiffT {0}) { - RAJA_ABORT_OR_THROW("RAJA::Range requires a non-zero stride."); + RAJA_ABORT_OR_THROW("RAJA::range requires a non-zero stride."); } - return {begin, end, typed_stride}; + return { + static_cast>(stripIndexType(begin)), + static_cast>(stripIndexType(end)), + typed_stride}; } /*! diff --git a/test/unit/index/test-rangesegment.cpp b/test/unit/index/test-rangesegment.cpp index e241706e17..82d88efc41 100644 --- a/test/unit/index/test-rangesegment.cpp +++ b/test/unit/index/test-rangesegment.cpp @@ -171,25 +171,59 @@ TYPED_TEST(RangeSegmentUnitTest, Equality) TEST(RangeSegmentUnitTest, RangeEnd) { - auto r = RAJA::Range(RAJA::Index_type(17)); + auto r = RAJA::range(RAJA::Index_type(17)); ASSERT_EQ(RAJA::RangeSegment(RAJA::Index_type(0), RAJA::Index_type(17)), r); ASSERT_EQ(RAJA::Index_type(0), *r.begin()); ASSERT_EQ(RAJA::Index_type(17), r.size()); } +TEST(RangeSegmentUnitTest, LongRangeEnd) +{ + auto r = RAJA::range(long {10}); + + static_assert(std::is_same>::value, + "range(long) should deduce the segment storage type."); + ASSERT_EQ((RAJA::TypedRangeSegment(0, 10)), r); + ASSERT_EQ(0, *r.begin()); + ASSERT_EQ(10, r.size()); +} + TEST(RangeSegmentUnitTest, TypedRangeEnd) { - auto r = RAJA::Range(17); + auto r = RAJA::range(17); ASSERT_EQ((RAJA::TypedRangeSegment(0, 17)), r); ASSERT_EQ(RangeStrongIndex(0), *r.begin()); ASSERT_EQ(RAJA::Index_type(17), r.size()); } +TEST(RangeSegmentUnitTest, DeducedStrongRangeEnd) +{ + auto r = RAJA::range(RangeStrongIndex(17)); + + static_assert( + std::is_same>::value, + "range(StrongIndex) should deduce the segment storage type."); + ASSERT_EQ((RAJA::TypedRangeSegment(0, 17)), r); + ASSERT_EQ(RangeStrongIndex(0), *r.begin()); + ASSERT_EQ(RAJA::Index_type(17), r.size()); +} + +TEST(RangeSegmentUnitTest, ExplicitTypedRangeEnd) +{ + auto r = RAJA::range(10); + + static_assert(std::is_same>::value, + "range(end) should use the explicit storage type."); + ASSERT_EQ((RAJA::TypedRangeSegment(0, 10)), r); + ASSERT_EQ(0, *r.begin()); + ASSERT_EQ(10, r.size()); +} + TEST(RangeSegmentUnitTest, RangeBeginEnd) { - auto r = RAJA::Range(3, 17); + auto r = RAJA::range(3, 17); ASSERT_EQ((RAJA::TypedRangeSegment(3, 17)), r); ASSERT_EQ(14, r.size()); @@ -197,7 +231,7 @@ TEST(RangeSegmentUnitTest, RangeBeginEnd) TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) { - auto r = RAJA::Range(2, 11, 3); + auto r = RAJA::range(2, 11, 3); ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); ASSERT_EQ(4, r.size()); @@ -206,7 +240,7 @@ TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) TEST(RangeSegmentUnitTest, RangeBeginEndStrideNegative) { - auto r = RAJA::Range(10, -1, -2); + auto r = RAJA::range(10, -1, -2); ASSERT_EQ((RAJA::TypedRangeStrideSegment(10, -1, -2)), r); ASSERT_EQ(6, r.size()); From 6dbb36702b9b205f053c591b77f7e5eef18aa5fc Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 14:10:37 -0700 Subject: [PATCH 04/18] Update include/RAJA/index/RangeSegment.hpp Co-authored-by: Jason Burmark --- include/RAJA/index/RangeSegment.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index 7290d61a32..110a156d2a 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -627,7 +627,7 @@ RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment range( { static_assert(std::is_signed>::value, "range requires a signed stride type."); - static_assert(!std::is_floating_point>::value, + static_assert(!std::is_floating_point_v>, "range requires a non-floating point stride type."); DiffT const typed_stride = static_cast(stripIndexType(stride)); From 17b5e571495b75c33b1d9b0ea1ca181707a65c1b Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 14:56:10 -0700 Subject: [PATCH 05/18] include stride in common type find --- include/RAJA/index/RangeSegment.hpp | 12 ++++++------ test/unit/index/test-rangesegment.cpp | 27 ++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index 7290d61a32..1ce2dddc84 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -618,7 +618,8 @@ template, + typename Common = + detail::range_storage_type_t, typename DiffT = make_signed_t>> RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment range( BeginT&& begin, @@ -671,17 +672,16 @@ RAJA_HOST_DEVICE TypedRangeSegment make_range(BeginT&& begin, template> + typename Common = detail::common_type_t> RAJA_HOST_DEVICE TypedRangeStrideSegment make_strided_range( BeginT&& begin, EndT&& end, StrideT&& stride) { - static_assert(std::is_signed::value, + static_assert(std::is_signed>::value, "make_strided_segment : stride must be signed."); - static_assert( - std::is_same, StrideT>::value, - "make_stride_segment : stride and end must be of similar types."); + static_assert(!std::is_floating_point>::value, + "make_strided_segment : stride must be non-floating point."); return {begin, end, stride}; } diff --git a/test/unit/index/test-rangesegment.cpp b/test/unit/index/test-rangesegment.cpp index 82d88efc41..c22a03b576 100644 --- a/test/unit/index/test-rangesegment.cpp +++ b/test/unit/index/test-rangesegment.cpp @@ -234,7 +234,7 @@ TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) auto r = RAJA::range(2, 11, 3); ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); - ASSERT_EQ(4, r.size()); + ASSERT_EQ(3, r.size()); ASSERT_EQ(2, *r.begin()); } @@ -246,3 +246,28 @@ TEST(RangeSegmentUnitTest, RangeBeginEndStrideNegative) ASSERT_EQ(6, r.size()); ASSERT_EQ(10, *r.begin()); } + +TEST(RangeSegmentUnitTest, RangeBeginEndStrideDeducesFromStride) +{ + auto r = RAJA::range(2, 11, long {3}); + + static_assert( + std::is_same>::value, + "range(begin, end, stride) should include stride in the storage type."); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); + ASSERT_EQ(3, r.size()); + ASSERT_EQ(2, *r.begin()); +} + +TEST(RangeSegmentUnitTest, MakeStridedRangeDeducesFromStride) +{ + auto r = RAJA::make_strided_range(2, 11, long {3}); + + static_assert( + std::is_same>::value, + "make_strided_range(begin, end, stride) should include stride in the " + "storage type."); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); + ASSERT_EQ(3, r.size()); + ASSERT_EQ(2, *r.begin()); +} From 3d0195af08fb041c2e3d0050aed0276eb600039a Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 15:02:01 -0700 Subject: [PATCH 06/18] PR comments --- include/RAJA/index/RangeSegment.hpp | 32 ++++++++++++++++++--------- test/unit/index/test-rangesegment.cpp | 26 ++++++++++++++++++++++ 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index eb66326aac..85765cec04 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -547,6 +547,9 @@ struct common_type template using common_type_t = typename common_type::type; +template +using range_stride_type_t = make_signed_t>; + template struct range_storage_type { @@ -619,17 +622,18 @@ template, + detail::range_storage_type_t>, typename DiffT = make_signed_t>> RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment range( BeginT&& begin, EndT&& end, StrideT&& stride) { - static_assert(std::is_signed>::value, - "range requires a signed stride type."); - static_assert(!std::is_floating_point_v>, - "range requires a non-floating point stride type."); + static_assert(std::is_integral_v>, + "range requires an integral stride type."); DiffT const typed_stride = static_cast(stripIndexType(stride)); if (typed_stride == DiffT {0}) @@ -672,17 +676,23 @@ RAJA_HOST_DEVICE TypedRangeSegment make_range(BeginT&& begin, template> + typename Common = detail::common_type_t>, + typename DiffT = make_signed_t>> RAJA_HOST_DEVICE TypedRangeStrideSegment make_strided_range( BeginT&& begin, EndT&& end, StrideT&& stride) { - static_assert(std::is_signed>::value, - "make_strided_segment : stride must be signed."); - static_assert(!std::is_floating_point>::value, - "make_strided_segment : stride must be non-floating point."); - return {begin, end, stride}; + static_assert(std::is_integral_v>, + "make_strided_segment : stride must be integral."); + + return { + static_cast>(stripIndexType(begin)), + static_cast>(stripIndexType(end)), + static_cast(stripIndexType(stride))}; } namespace concepts diff --git a/test/unit/index/test-rangesegment.cpp b/test/unit/index/test-rangesegment.cpp index c22a03b576..98a41be1cb 100644 --- a/test/unit/index/test-rangesegment.cpp +++ b/test/unit/index/test-rangesegment.cpp @@ -259,6 +259,19 @@ TEST(RangeSegmentUnitTest, RangeBeginEndStrideDeducesFromStride) ASSERT_EQ(2, *r.begin()); } +TEST(RangeSegmentUnitTest, RangeBeginEndUnsignedStrideWorks) +{ + auto r = RAJA::range(-2, 11, 3u); + + static_assert( + std::is_same>::value, + "range(begin, end, unsigned stride) should preserve signed storage when " + "begin/end are signed."); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(-2, 11, 3)), r); + ASSERT_EQ(5, r.size()); + ASSERT_EQ(-2, *r.begin()); +} + TEST(RangeSegmentUnitTest, MakeStridedRangeDeducesFromStride) { auto r = RAJA::make_strided_range(2, 11, long {3}); @@ -271,3 +284,16 @@ TEST(RangeSegmentUnitTest, MakeStridedRangeDeducesFromStride) ASSERT_EQ(3, r.size()); ASSERT_EQ(2, *r.begin()); } + +TEST(RangeSegmentUnitTest, MakeStridedRangeUnsignedStrideWorks) +{ + auto r = RAJA::make_strided_range(-2, 11, 3u); + + static_assert( + std::is_same>::value, + "make_strided_range(begin, end, unsigned stride) should preserve signed " + "storage when begin/end are signed."); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(-2, 11, 3)), r); + ASSERT_EQ(5, r.size()); + ASSERT_EQ(-2, *r.begin()); +} From ddae21a1949404e13c2944b6440896d5f42f8854 Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 15:03:45 -0700 Subject: [PATCH 07/18] use _v instead of ::value --- include/RAJA/index/RangeSegment.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index 85765cec04..881c93ed3f 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -103,9 +103,9 @@ struct TypedRangeSegment // Static asserts to provide some useful error messages during compilation // for incorrect usage. // - static_assert(std::is_signed::value, + static_assert(std::is_signed_v, "TypedRangeSegment DiffT requires signed type."); - static_assert(!std::is_floating_point::value, + static_assert(!std::is_floating_point_v, "TypedRangeSegment Type must be non floating point."); //@{ @@ -336,9 +336,9 @@ struct TypedRangeStrideSegment // Static asserts to provide some useful error messages during compilation // for incorrect usage. // - static_assert(std::is_signed::value, + static_assert(std::is_signed_v, "TypedRangeStrideSegment DiffT requires signed type."); - static_assert(!std::is_floating_point::value, + static_assert(!std::is_floating_point_v, "TypedRangeStrideSegment Type must be non floating point."); //@{ @@ -581,7 +581,7 @@ template range( EndT&& end) noexcept { - static_assert(!std::is_floating_point::value, + static_assert(!std::is_floating_point_v, "range requires a non-floating point index type."); return {StripCommon {0}, static_cast(stripIndexType(end))}; } From e243737b46e3a651f6095f47d8d8b57fb0d2dece Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 15:11:07 -0700 Subject: [PATCH 08/18] build fix --- include/RAJA/index/RangeSegment.hpp | 10 +++++++--- test/unit/index/test-rangesegment.cpp | 12 ++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index 881c93ed3f..e5c9047952 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -533,22 +533,26 @@ using RangeStrideSegment = TypedRangeStrideSegment; namespace detail { +template +using range_arg_type_t = std::decay_t; + template struct common_type - : std::common_type::type> + : std::common_type, typename common_type::type> {}; template struct common_type { - using type = T; + using type = range_arg_type_t; }; template using common_type_t = typename common_type::type; template -using range_stride_type_t = make_signed_t>; +using range_stride_type_t = + make_signed_t>>; template struct range_storage_type diff --git a/test/unit/index/test-rangesegment.cpp b/test/unit/index/test-rangesegment.cpp index 98a41be1cb..872ca7e340 100644 --- a/test/unit/index/test-rangesegment.cpp +++ b/test/unit/index/test-rangesegment.cpp @@ -189,6 +189,18 @@ TEST(RangeSegmentUnitTest, LongRangeEnd) ASSERT_EQ(10, r.size()); } +TEST(RangeSegmentUnitTest, LongRangeEndLValue) +{ + const long end = 10; + auto r = RAJA::range(end); + + static_assert(std::is_same>::value, + "range(const long&) should decay the deduced storage type."); + ASSERT_EQ((RAJA::TypedRangeSegment(0, 10)), r); + ASSERT_EQ(0, *r.begin()); + ASSERT_EQ(10, r.size()); +} + TEST(RangeSegmentUnitTest, TypedRangeEnd) { auto r = RAJA::range(17); From 23a04d6beac1a46482167d355e2d2b1e86d97749 Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 15:16:28 -0700 Subject: [PATCH 09/18] clean up pass --- include/RAJA/index/RangeSegment.hpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index e5c9047952..5b96262fcc 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -580,7 +580,7 @@ using range_storage_type_t = typename range_storage_type::type; */ template, + typename Common = detail::range_storage_type_t, typename StripCommon = strip_index_type_t> RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment range( EndT&& end) noexcept @@ -645,10 +645,9 @@ RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment range( RAJA_ABORT_OR_THROW("RAJA::range requires a non-zero stride."); } - return { - static_cast>(stripIndexType(begin)), - static_cast>(stripIndexType(end)), - typed_stride}; + return {static_cast>(stripIndexType(begin)), + static_cast>(stripIndexType(end)), + typed_stride}; } /*! @@ -680,10 +679,8 @@ RAJA_HOST_DEVICE TypedRangeSegment make_range(BeginT&& begin, template>, + typename Common = detail:: + common_type_t>, typename DiffT = make_signed_t>> RAJA_HOST_DEVICE TypedRangeStrideSegment make_strided_range( BeginT&& begin, @@ -693,10 +690,9 @@ RAJA_HOST_DEVICE TypedRangeStrideSegment make_strided_range( static_assert(std::is_integral_v>, "make_strided_segment : stride must be integral."); - return { - static_cast>(stripIndexType(begin)), - static_cast>(stripIndexType(end)), - static_cast(stripIndexType(stride))}; + return {static_cast>(stripIndexType(begin)), + static_cast>(stripIndexType(end)), + static_cast(stripIndexType(stride))}; } namespace concepts From 6a8995a0ab51b07e7b21e0ab9a8ce2b4cd3bff89 Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 16:00:21 -0700 Subject: [PATCH 10/18] just use decay_t --- include/RAJA/index/RangeSegment.hpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index 5b96262fcc..19c627f4ff 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -533,18 +533,15 @@ using RangeStrideSegment = TypedRangeStrideSegment; namespace detail { -template -using range_arg_type_t = std::decay_t; - template struct common_type - : std::common_type, typename common_type::type> + : std::common_type, typename common_type::type> {}; template struct common_type { - using type = range_arg_type_t; + using type = std::decay_t; }; template @@ -552,7 +549,7 @@ using common_type_t = typename common_type::type; template using range_stride_type_t = - make_signed_t>>; + make_signed_t>>; template struct range_storage_type From 66be94f308a1460814119b983e8e7f1392454a19 Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 16:03:40 -0700 Subject: [PATCH 11/18] simplify --- include/RAJA/index/RangeSegment.hpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index 19c627f4ff..fb6dd528a0 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -533,19 +533,8 @@ using RangeStrideSegment = TypedRangeStrideSegment; namespace detail { -template -struct common_type - : std::common_type, typename common_type::type> -{}; - -template -struct common_type -{ - using type = std::decay_t; -}; - template -using common_type_t = typename common_type::type; +using common_type_t = std::common_type_t...>; template using range_stride_type_t = From 0c4666e24f5a0ee2b3e68a64e556547db0744d20 Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 16:09:26 -0700 Subject: [PATCH 12/18] remove redundant decay --- include/RAJA/index/RangeSegment.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index fb6dd528a0..0eb5de0424 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -534,7 +534,7 @@ namespace detail { template -using common_type_t = std::common_type_t...>; +using common_type_t = std::common_type_t; template using range_stride_type_t = From d2886c015c3a64c3f76e1856c2adbc7b3c4d5aae Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 16:11:16 -0700 Subject: [PATCH 13/18] add strongly typed begin and end test --- test/unit/index/test-rangesegment.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/unit/index/test-rangesegment.cpp b/test/unit/index/test-rangesegment.cpp index 872ca7e340..d6349d401b 100644 --- a/test/unit/index/test-rangesegment.cpp +++ b/test/unit/index/test-rangesegment.cpp @@ -241,6 +241,20 @@ TEST(RangeSegmentUnitTest, RangeBeginEnd) ASSERT_EQ(14, r.size()); } +TEST(RangeSegmentUnitTest, StrongRangeBeginEnd) +{ + RangeStrongIndex begin(3); + RangeStrongIndex end(17); + auto r = RAJA::range(begin, end); + + static_assert( + std::is_same>::value, + "range(StrongIndex, StrongIndex) should deduce the strong storage type."); + ASSERT_EQ((RAJA::TypedRangeSegment(3, 17)), r); + ASSERT_EQ(RangeStrongIndex(3), *r.begin()); + ASSERT_EQ(RAJA::Index_type(14), r.size()); +} + TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) { auto r = RAJA::range(2, 11, 3); From ab3c185cf9d33b60f5d7e21d94a2860c5afe6f88 Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 31 Mar 2026 17:42:55 -0700 Subject: [PATCH 14/18] add mixed index support --- examples/raja-ranges.cpp | 27 +++++ include/RAJA/index/RangeSegment.hpp | 142 ++++++++++++++++++++------ test/unit/index/test-rangesegment.cpp | 65 ++++++++++++ 3 files changed, 200 insertions(+), 34 deletions(-) diff --git a/examples/raja-ranges.cpp b/examples/raja-ranges.cpp index 76196ccf09..1c1381f532 100644 --- a/examples/raja-ranges.cpp +++ b/examples/raja-ranges.cpp @@ -19,6 +19,7 @@ * 2. RAJA::range(N) for a TypedRangeSegment over [0, N) * 3. RAJA::range(begin, end) for [begin, end) * 4. RAJA::range(begin, end, step) for a strided half-open interval + * 5. Mixed strong/numeric begin/end and strided ranges */ RAJA_INDEX_VALUE(CellIndex, "CellIndex"); @@ -31,6 +32,8 @@ int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) int typed_values[N] = {}; int subrange_values[N] = {}; int odd_values[N] = {}; + int mixed_end_values[N] = {}; + int mixed_stride_values[N] = {}; // Equivalent to Python range(N): [0, N) RAJA::forall(RAJA::range(N), [&](RAJA::Index_type i) { @@ -52,6 +55,18 @@ int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) odd_values[i] = i; }); + // Mixed numeric/strong range: preserves CellIndex for the loop index. + RAJA::forall(RAJA::range(1, CellIndex {6}), [&](CellIndex i) { + mixed_end_values[*i] = static_cast(*i * 10); + }); + + // Mixed strong begin with numeric end/stride for a strided range. + RAJA::forall(RAJA::range(CellIndex {1}, N, 2), + [&](CellIndex i) { + mixed_stride_values[*i] = + static_cast(*i * 100); + }); + std::cout << "range(N):"; for (auto i : RAJA::range(N)) { std::cout << ' ' << values[i]; @@ -76,6 +91,18 @@ int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) } std::cout << '\n'; + std::cout << "range(1, CellIndex{6}):"; + for (auto i : RAJA::range(1, CellIndex {6})) { + std::cout << ' ' << mixed_end_values[*i]; + } + std::cout << '\n'; + + std::cout << "range(CellIndex{1}, N, 2):"; + for (auto i : RAJA::range(CellIndex {1}, N, 2)) { + std::cout << ' ' << mixed_stride_values[*i]; + } + std::cout << '\n'; + std::cout << "range(N - 1, -1, -2):"; for (auto i : RAJA::range(N - 1, -1, -2)) { std::cout << ' ' << i; diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index 0eb5de0424..c6bdfa09ee 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -23,6 +23,7 @@ #include "RAJA/config.hpp" #include +#include #include "RAJA/internal/Iterators.hpp" @@ -536,24 +537,88 @@ namespace detail template using common_type_t = std::common_type_t; +struct no_strong_index +{}; + template -using range_stride_type_t = - make_signed_t>>; +using strong_index_candidate_t = + std::conditional_t>, + std::decay_t, + no_strong_index>; + +template +struct strong_index_type +{ + using type = no_strong_index; +}; -template -struct range_storage_type +template +struct strong_index_type +{ + using current = strong_index_candidate_t; + using rest = typename strong_index_type::type; + + static_assert( + std::is_same_v || + std::is_same_v || + std::is_same_v, + "range requires matching strong index types for begin and end."); + + using type = std:: + conditional_t, current, rest>; +}; + +template +using strong_index_type_t = typename strong_index_type::type; + +template +using range_stride_type_t = make_signed_t>>; + +template +struct selected_range_storage { using type = StorageT; }; +template +struct selected_range_storage +{ + using type = DeducedT; +}; + +template +using selected_range_storage_t = + typename selected_range_storage::type; + +template +struct range_storage_from_strong +{ + using type = StrongT; +}; + template -struct range_storage_type +struct range_storage_from_strong { using type = common_type_t; }; -template -using range_storage_type_t = typename range_storage_type::type; +template +using deduced_range_storage_type_t = + typename range_storage_from_strong, + BeginT, + EndT>::type; + +template +using deduced_range_stride_storage_type_t = + typename range_storage_from_strong, + BeginT, + EndT, + range_stride_type_t>::type; + +template +using deduced_range_stride_diff_type_t = + common_type_t>, + range_stride_type_t>; } // namespace detail @@ -564,10 +629,12 @@ using range_storage_type_t = typename range_storage_type::type; * starting at zero and ending at @end. An explicit template argument * may be used to select the segment storage type. */ -template, - typename StripCommon = strip_index_type_t> +template< + typename StorageT = void, + typename EndT, + typename Common = + detail::selected_range_storage_t>, + typename StripCommon = strip_index_type_t> RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment range( EndT&& end) noexcept { @@ -587,7 +654,9 @@ RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment range( template, + typename Common = detail::selected_range_storage_t< + StorageT, + detail::deduced_range_storage_type_t>, typename StripCommon = strip_index_type_t> RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment range( BeginT&& begin, @@ -607,17 +676,16 @@ RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment range( * is provided for the segment storage type. If stride is zero, * execution aborts or throws. */ -template>, - typename DiffT = make_signed_t>> -RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment range( +template< + typename StorageT = void, + typename BeginT, + typename EndT, + typename StrideT, + typename Common = detail::selected_range_storage_t< + StorageT, + detail::deduced_range_stride_storage_type_t>, + typename DiffT = detail::deduced_range_stride_diff_type_t> +RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment range( BeginT&& begin, EndT&& end, StrideT&& stride) @@ -646,11 +714,13 @@ RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment range( */ template> + typename Common = detail::deduced_range_storage_type_t, + typename StripCommon = strip_index_type_t> RAJA_HOST_DEVICE TypedRangeSegment make_range(BeginT&& begin, EndT&& end) { - return {begin, end}; + return {static_cast(stripIndexType(begin)), + static_cast(stripIndexType(end))}; } /*! @@ -662,13 +732,14 @@ RAJA_HOST_DEVICE TypedRangeSegment make_range(BeginT&& begin, * @begin, @end, and @stride. If there is no common * type, then a compiler error will be produced. */ -template>, - typename DiffT = make_signed_t>> -RAJA_HOST_DEVICE TypedRangeStrideSegment make_strided_range( +template< + typename BeginT, + typename EndT, + typename StrideT, + typename Common = + detail::deduced_range_stride_storage_type_t, + typename DiffT = detail::deduced_range_stride_diff_type_t> +RAJA_HOST_DEVICE TypedRangeStrideSegment make_strided_range( BeginT&& begin, EndT&& end, StrideT&& stride) @@ -686,11 +757,14 @@ namespace concepts template struct RangeConstructible - : DefineConcept(camp::val>()) {}; + : DefineConcept( + camp::val>()) {}; template struct RangeStrideConstructible - : DefineConcept(camp::val>()) {}; + : DefineConcept( + camp::val< + RAJA::detail::deduced_range_stride_storage_type_t>()) {}; } // namespace concepts diff --git a/test/unit/index/test-rangesegment.cpp b/test/unit/index/test-rangesegment.cpp index d6349d401b..6f453222cb 100644 --- a/test/unit/index/test-rangesegment.cpp +++ b/test/unit/index/test-rangesegment.cpp @@ -255,6 +255,30 @@ TEST(RangeSegmentUnitTest, StrongRangeBeginEnd) ASSERT_EQ(RAJA::Index_type(14), r.size()); } +TEST(RangeSegmentUnitTest, MixedStrongRangeBeginEnd) +{ + auto r = RAJA::range(3, RangeStrongIndex(17)); + + static_assert( + std::is_same>::value, + "range(int, StrongIndex) should preserve the strong storage type."); + ASSERT_EQ((RAJA::TypedRangeSegment(3, 17)), r); + ASSERT_EQ(RangeStrongIndex(3), *r.begin()); + ASSERT_EQ(RAJA::Index_type(14), r.size()); +} + +TEST(RangeSegmentUnitTest, MixedStrongRangeStrongBegin) +{ + auto r = RAJA::range(RangeStrongIndex(3), 17); + + static_assert( + std::is_same>::value, + "range(StrongIndex, int) should preserve the strong storage type."); + ASSERT_EQ((RAJA::TypedRangeSegment(3, 17)), r); + ASSERT_EQ(RangeStrongIndex(3), *r.begin()); + ASSERT_EQ(RAJA::Index_type(14), r.size()); +} + TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) { auto r = RAJA::range(2, 11, 3); @@ -264,6 +288,47 @@ TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) ASSERT_EQ(2, *r.begin()); } +TEST(RangeSegmentUnitTest, MixedStrongRangeBeginEndStrideStrongEnd) +{ + auto r = RAJA::range(2, RangeStrongIndex(11), 3); + + static_assert( + std::is_same>::value, + "range(int, StrongIndex, stride) should preserve the strong storage " + "type."); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); + ASSERT_EQ(3, r.size()); + ASSERT_EQ(RangeStrongIndex(2), *r.begin()); +} + +TEST(RangeSegmentUnitTest, MixedStrongRangeBeginEndStrideStrongBegin) +{ + auto r = RAJA::range(RangeStrongIndex(2), 11, 3); + + static_assert( + std::is_same>::value, + "range(StrongIndex, int, stride) should preserve the strong storage " + "type."); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); + ASSERT_EQ(3, r.size()); + ASSERT_EQ(RangeStrongIndex(2), *r.begin()); +} + +TEST(RangeSegmentUnitTest, RangeBeginEndStrongStride) +{ + auto r = RAJA::range(2, 11, RangeStrongIndex(3)); + + static_assert( + std::is_same>::value, + "range(begin, end, StrongIndex stride) should include stride in the " + "storage type."); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); + ASSERT_EQ(3, r.size()); + ASSERT_EQ(2, *r.begin()); +} + TEST(RangeSegmentUnitTest, RangeBeginEndStrideNegative) { auto r = RAJA::range(10, -1, -2); From ad5f13229b70760c70562dd36fd1baf7d3ba7e69 Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Thu, 2 Apr 2026 06:34:58 -0700 Subject: [PATCH 15/18] add sphinx docs --- .../user_guide/feature/iteration_spaces.rst | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/docs/sphinx/user_guide/feature/iteration_spaces.rst b/docs/sphinx/user_guide/feature/iteration_spaces.rst index aeba1da60d..768d90ce79 100644 --- a/docs/sphinx/user_guide/feature/iteration_spaces.rst +++ b/docs/sphinx/user_guide/feature/iteration_spaces.rst @@ -94,3 +94,68 @@ and two types: Thus, any iterable type that defines these methods and types appropriately can be used as a segment with RAJA kernel execution templates. + +Python-like Range Helpers +^^^^^^^^^^^^^^^^^^^^^^^^^ + +RAJA also provides ``RAJA::range(...)`` helpers that construct the segment +type for common half-open iteration patterns. These helpers are intended to +mirror the shape of Python's ``range`` while returning RAJA segment objects +that can be passed directly to ``RAJA::forall`` and other execution +interfaces. + +The supported forms are:: + + RAJA::range(end) // [0, end) + RAJA::range(end) // [0, end) with explicit storage type + RAJA::range(begin, end) // [begin, end) + RAJA::range(begin, end, stride) // [begin, end) with stride + +The return type depends on the arguments: + +* ``RAJA::range(end)`` and ``RAJA::range(begin, end)`` return a + ``RAJA::TypedRangeSegment``. +* ``RAJA::range(begin, end, stride)`` returns a + ``RAJA::TypedRangeStrideSegment``. +* When one of the bounds is a RAJA strong index type, such as a type created + with ``RAJA_INDEX_VALUE``, that strong type is preserved for the loop + variable when possible. +* Providing an explicit template argument, such as + ``RAJA::range(end)``, overrides the deduced storage type. + +For example:: + + RAJA_INDEX_VALUE(CellIndex, "CellIndex"); + + RAJA::forall(RAJA::range(N), [=](RAJA::Index_type i) { + values[i] = i * i; + }); + + RAJA::forall(RAJA::range(N), [=](CellIndex i) { + typed_values[*i] = *i + 10; + }); + + RAJA::forall(RAJA::range(2, 6), [=](int i) { + subrange_values[i] = i; + }); + + RAJA::forall(RAJA::range(CellIndex {1}, N, 2), + [=](CellIndex i) { + strided_values[*i] = *i; + }); + +Strided ranges follow the same half-open interval convention as +``RAJA::TypedRangeStrideSegment``. Positive strides move forward, and negative +strides move backward. For example, ``RAJA::range(N - 1, -1, -2)`` visits +``N - 1, N - 3, ...`` down to the first value that remains greater than +``-1``. A zero stride is invalid and causes RAJA to abort or throw, depending +on the build configuration. + +The older ``RAJA::make_range`` and ``RAJA::make_strided_range`` helpers remain +available. Use ``RAJA::range(...)`` when the Python-like spelling improves +readability or when you want the one-argument ``[0, end)`` shorthand. + +The complete example added in this branch is shown below: + +.. literalinclude:: ../../../../examples/raja-ranges.cpp + :language: c++ From 744cbc5aeca1bf901916c3e1ac985c5b1e6c6d8c Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Fri, 19 Jun 2026 13:29:59 -0700 Subject: [PATCH 16/18] clean up pass, throw error for mixed types --- .../user_guide/feature/iteration_spaces.rst | 4 +- include/RAJA/index/RangeSegment.hpp | 40 +++++++++++++++---- test/unit/index/test-rangesegment.cpp | 14 +++++++ 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/docs/sphinx/user_guide/feature/iteration_spaces.rst b/docs/sphinx/user_guide/feature/iteration_spaces.rst index 768d90ce79..1e8e0e4696 100644 --- a/docs/sphinx/user_guide/feature/iteration_spaces.rst +++ b/docs/sphinx/user_guide/feature/iteration_spaces.rst @@ -119,7 +119,9 @@ The return type depends on the arguments: ``RAJA::TypedRangeStrideSegment``. * When one of the bounds is a RAJA strong index type, such as a type created with ``RAJA_INDEX_VALUE``, that strong type is preserved for the loop - variable when possible. + variable. Mixed strong and plain integral bounds are only accepted when the + plain bound already matches the strong type's underlying storage type. + Otherwise deduction fails rather than narrowing the value. * Providing an explicit template argument, such as ``RAJA::range(end)``, overrides the deduced storage type. diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index 10437da509..89d771cb9d 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -591,10 +591,7 @@ using selected_range_storage_t = typename selected_range_storage::type; template -struct range_storage_from_strong -{ - using type = StrongT; -}; +struct range_storage_from_strong; template struct range_storage_from_strong @@ -602,6 +599,33 @@ struct range_storage_from_strong using type = common_type_t; }; +template + requires ((std::is_same_v>, + strip_index_type_t>> && ...)) +struct range_storage_from_strong +{ + using type = StrongT; +}; + +template +struct range_stride_storage_from_strong; + +template +struct range_stride_storage_from_strong +{ + using type = common_type_t>; +}; + +template + requires (std::is_same_v>, + strip_index_type_t>> && + std::is_same_v>, + strip_index_type_t>>) +struct range_stride_storage_from_strong +{ + using type = StrongT; +}; + template using deduced_range_storage_type_t = typename range_storage_from_strong, @@ -610,10 +634,10 @@ using deduced_range_storage_type_t = template using deduced_range_stride_storage_type_t = - typename range_storage_from_strong, - BeginT, - EndT, - range_stride_type_t>::type; + typename range_stride_storage_from_strong, + BeginT, + EndT, + StrideT>::type; template using deduced_range_stride_diff_type_t = diff --git a/test/unit/index/test-rangesegment.cpp b/test/unit/index/test-rangesegment.cpp index 6f453222cb..33ae8daa7c 100644 --- a/test/unit/index/test-rangesegment.cpp +++ b/test/unit/index/test-rangesegment.cpp @@ -267,6 +267,10 @@ TEST(RangeSegmentUnitTest, MixedStrongRangeBeginEnd) ASSERT_EQ(RAJA::Index_type(14), r.size()); } +static_assert(RAJA::concepts::RangeConstructible); +static_assert(!RAJA::concepts::RangeConstructible); +static_assert(!RAJA::concepts::RangeConstructible); + TEST(RangeSegmentUnitTest, MixedStrongRangeStrongBegin) { auto r = RAJA::range(RangeStrongIndex(3), 17); @@ -279,6 +283,16 @@ TEST(RangeSegmentUnitTest, MixedStrongRangeStrongBegin) ASSERT_EQ(RAJA::Index_type(14), r.size()); } +static_assert(RAJA::concepts::RangeStrideConstructible); +static_assert(!RAJA::concepts::RangeStrideConstructible); +static_assert(!RAJA::concepts::RangeStrideConstructible); + TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) { auto r = RAJA::range(2, 11, 3); From c30299d973280436b13e344d0f22e5186708bf80 Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Fri, 19 Jun 2026 14:58:23 -0700 Subject: [PATCH 17/18] clean up pass --- .../user_guide/feature/iteration_spaces.rst | 19 ++- examples/raja-ranges.cpp | 45 +++---- include/RAJA/index/RangeSegment.hpp | 122 +++++++++++++++--- test/unit/index/test-rangesegment.cpp | 90 ++++++------- 4 files changed, 180 insertions(+), 96 deletions(-) diff --git a/docs/sphinx/user_guide/feature/iteration_spaces.rst b/docs/sphinx/user_guide/feature/iteration_spaces.rst index 1e8e0e4696..166c59d03f 100644 --- a/docs/sphinx/user_guide/feature/iteration_spaces.rst +++ b/docs/sphinx/user_guide/feature/iteration_spaces.rst @@ -118,12 +118,15 @@ The return type depends on the arguments: * ``RAJA::range(begin, end, stride)`` returns a ``RAJA::TypedRangeStrideSegment``. * When one of the bounds is a RAJA strong index type, such as a type created - with ``RAJA_INDEX_VALUE``, that strong type is preserved for the loop - variable. Mixed strong and plain integral bounds are only accepted when the - plain bound already matches the strong type's underlying storage type. - Otherwise deduction fails rather than narrowing the value. + with ``RAJA_INDEX_VALUE``, all bounds must use that same strong type. Mixed + strong and plain integral bounds are rejected rather than narrowed. * Providing an explicit template argument, such as - ``RAJA::range(end)``, overrides the deduced storage type. + ``RAJA::range(end)``, overrides the deduced storage type, but + explicit storage must still match the argument types. For example, + ``RAJA::range(RangeStrongIndex(3), 17)`` and + ``RAJA::range(AnotherRangeStrongIndex(3), 17)`` are + rejected, while ``RAJA::range(RangeStrongIndex(3), + RangeStrongIndex(17))`` is valid. For example:: @@ -133,7 +136,8 @@ For example:: values[i] = i * i; }); - RAJA::forall(RAJA::range(N), [=](CellIndex i) { + RAJA::forall(RAJA::range(CellIndex {N}), + [=](CellIndex i) { typed_values[*i] = *i + 10; }); @@ -141,7 +145,8 @@ For example:: subrange_values[i] = i; }); - RAJA::forall(RAJA::range(CellIndex {1}, N, 2), + RAJA::forall(RAJA::range(CellIndex {1}, CellIndex {N}, + CellIndex {2}), [=](CellIndex i) { strided_values[*i] = *i; }); diff --git a/examples/raja-ranges.cpp b/examples/raja-ranges.cpp index 1c1381f532..eb217a048f 100644 --- a/examples/raja-ranges.cpp +++ b/examples/raja-ranges.cpp @@ -19,7 +19,7 @@ * 2. RAJA::range(N) for a TypedRangeSegment over [0, N) * 3. RAJA::range(begin, end) for [begin, end) * 4. RAJA::range(begin, end, step) for a strided half-open interval - * 5. Mixed strong/numeric begin/end and strided ranges + * 5. Strong-index ranges using consistent strong types */ RAJA_INDEX_VALUE(CellIndex, "CellIndex"); @@ -32,8 +32,8 @@ int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) int typed_values[N] = {}; int subrange_values[N] = {}; int odd_values[N] = {}; - int mixed_end_values[N] = {}; - int mixed_stride_values[N] = {}; + int strong_end_values[N] = {}; + int strong_stride_values[N] = {}; // Equivalent to Python range(N): [0, N) RAJA::forall(RAJA::range(N), [&](RAJA::Index_type i) { @@ -41,7 +41,8 @@ int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) }); // Equivalent to Python range(N), but preserving a strong index type. - RAJA::forall(RAJA::range(N), [&](CellIndex i) { + RAJA::forall( + RAJA::range(CellIndex {N}), [&](CellIndex i) { typed_values[*i] = static_cast(*i + 10); }); @@ -55,17 +56,17 @@ int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) odd_values[i] = i; }); - // Mixed numeric/strong range: preserves CellIndex for the loop index. - RAJA::forall(RAJA::range(1, CellIndex {6}), [&](CellIndex i) { - mixed_end_values[*i] = static_cast(*i * 10); - }); + // Strong-index range with consistent strong bounds. + RAJA::forall( + RAJA::range(CellIndex {1}, CellIndex {6}), + [&](CellIndex i) { strong_end_values[*i] = static_cast(*i * 10); }); - // Mixed strong begin with numeric end/stride for a strided range. - RAJA::forall(RAJA::range(CellIndex {1}, N, 2), - [&](CellIndex i) { - mixed_stride_values[*i] = - static_cast(*i * 100); - }); + // Strong-index strided range with consistent strong bounds. + RAJA::forall( + RAJA::range(CellIndex {1}, CellIndex {N}, CellIndex {2}), + [&](CellIndex i) { + strong_stride_values[*i] = static_cast(*i * 100); + }); std::cout << "range(N):"; for (auto i : RAJA::range(N)) { @@ -73,8 +74,8 @@ int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) } std::cout << '\n'; - std::cout << "range(N):"; - for (auto i : RAJA::range(N)) { + std::cout << "range(CellIndex{N}):"; + for (auto i : RAJA::range(CellIndex {N})) { std::cout << ' ' << typed_values[*i]; } std::cout << '\n'; @@ -91,15 +92,15 @@ int main(int RAJA_UNUSED_ARG(argc), char** RAJA_UNUSED_ARG(argv)) } std::cout << '\n'; - std::cout << "range(1, CellIndex{6}):"; - for (auto i : RAJA::range(1, CellIndex {6})) { - std::cout << ' ' << mixed_end_values[*i]; + std::cout << "range(CellIndex{1}, CellIndex{6}):"; + for (auto i : RAJA::range(CellIndex {1}, CellIndex {6})) { + std::cout << ' ' << strong_end_values[*i]; } std::cout << '\n'; - std::cout << "range(CellIndex{1}, N, 2):"; - for (auto i : RAJA::range(CellIndex {1}, N, 2)) { - std::cout << ' ' << mixed_stride_values[*i]; + std::cout << "range(CellIndex{1}, CellIndex{N}, CellIndex{2}):"; + for (auto i : RAJA::range(CellIndex {1}, CellIndex {N}, CellIndex {2})) { + std::cout << ' ' << strong_stride_values[*i]; } std::cout << '\n'; diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index 89d771cb9d..cc20b03197 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -599,9 +599,22 @@ struct range_storage_from_strong using type = common_type_t; }; +template +struct strong_range_arg_compatible + : std::bool_constant> && + std::is_same_v, std::decay_t>> +{}; + template - requires ((std::is_same_v>, - strip_index_type_t>> && ...)) +struct range_storage_from_strong_compatible + : std::bool_constant || + (strong_range_arg_compatible::value && + ...)> +{}; + +template + requires(std::is_same_v || + (strong_range_arg_compatible::value && ...)) struct range_storage_from_strong { using type = StrongT; @@ -617,10 +630,18 @@ struct range_stride_storage_from_strong }; template - requires (std::is_same_v>, - strip_index_type_t>> && - std::is_same_v>, - strip_index_type_t>>) +struct range_stride_storage_from_strong_compatible + : std::bool_constant || + (strong_range_arg_compatible::value && + strong_range_arg_compatible::value && + strong_range_arg_compatible::value)> +{}; + +template + requires(std::is_same_v || + (strong_range_arg_compatible::value && + strong_range_arg_compatible::value && + strong_range_arg_compatible::value)) struct range_stride_storage_from_strong { using type = StrongT; @@ -644,6 +665,65 @@ using deduced_range_stride_diff_type_t = common_type_t>, range_stride_type_t>; +template>, + bool ArgIsStrong = std::is_base_of_v>> +struct explicit_range_arg_compatible_impl : std::false_type +{}; + +template +struct explicit_range_arg_compatible_impl + : std::true_type +{}; + +template +struct explicit_range_arg_compatible_impl + : std::bool_constant< + std::is_same_v, std::decay_t>> +{}; + +template +using explicit_range_arg_compatible = + explicit_range_arg_compatible_impl; + +template +struct explicit_range_storage_compatible + : std::bool_constant<(explicit_range_arg_compatible::value && + ...)> +{}; + +template +inline constexpr bool explicit_range_storage_compatible_v = + explicit_range_storage_compatible::value; + +template +inline constexpr bool deduced_range_storage_compatible_v = + range_storage_from_strong_compatible, + BeginT, + EndT>::value; + +template +inline constexpr bool deduced_range_stride_storage_compatible_v = + range_stride_storage_from_strong_compatible< + strong_index_type_t, + BeginT, + EndT, + StrideT>::value; + +template +inline constexpr bool range_storage_compatible_v = + std::is_same_v + ? deduced_range_storage_compatible_v + : explicit_range_storage_compatible_v; + +template +inline constexpr bool range_stride_storage_compatible_v = + std::is_same_v + ? deduced_range_stride_storage_compatible_v + : explicit_range_storage_compatible_v; + } // namespace detail /*! @@ -659,6 +739,7 @@ template< typename Common = detail::selected_range_storage_t>, typename StripCommon = strip_index_type_t> + requires detail::range_storage_compatible_v RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment range( EndT&& end) noexcept { @@ -682,6 +763,7 @@ template>, typename StripCommon = strip_index_type_t> + requires detail::range_storage_compatible_v RAJA_HOST_DEVICE RAJA_INLINE constexpr TypedRangeSegment range( BeginT&& begin, EndT&& end) noexcept @@ -709,10 +791,12 @@ template< StorageT, detail::deduced_range_stride_storage_type_t>, typename DiffT = detail::deduced_range_stride_diff_type_t> -RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment range( - BeginT&& begin, - EndT&& end, - StrideT&& stride) + requires detail:: + range_stride_storage_compatible_v + RAJA_HOST_DEVICE RAJA_INLINE TypedRangeStrideSegment range( + BeginT&& begin, + EndT&& end, + StrideT&& stride) { static_assert(std::is_integral_v>, "range requires an integral stride type."); @@ -740,6 +824,7 @@ template, typename StripCommon = strip_index_type_t> + requires detail::deduced_range_storage_compatible_v RAJA_HOST_DEVICE TypedRangeSegment make_range(BeginT&& begin, EndT&& end) { @@ -763,10 +848,12 @@ template< typename Common = detail::deduced_range_stride_storage_type_t, typename DiffT = detail::deduced_range_stride_diff_type_t> -RAJA_HOST_DEVICE TypedRangeStrideSegment make_strided_range( - BeginT&& begin, - EndT&& end, - StrideT&& stride) + requires detail:: + deduced_range_stride_storage_compatible_v + RAJA_HOST_DEVICE TypedRangeStrideSegment make_strided_range( + BeginT&& begin, + EndT&& end, + StrideT&& stride) { static_assert(std::is_integral_v>, "make_strided_segment : stride must be integral."); @@ -781,12 +868,11 @@ namespace concepts template concept RangeConstructible = - requires { typename RAJA::detail::deduced_range_storage_type_t; }; + RAJA::detail::deduced_range_storage_compatible_v; template -concept RangeStrideConstructible = requires { - typename RAJA::detail::deduced_range_stride_storage_type_t; -}; +concept RangeStrideConstructible = + RAJA::detail::deduced_range_stride_storage_compatible_v; } // namespace concepts diff --git a/test/unit/index/test-rangesegment.cpp b/test/unit/index/test-rangesegment.cpp index 33ae8daa7c..9f39925f3f 100644 --- a/test/unit/index/test-rangesegment.cpp +++ b/test/unit/index/test-rangesegment.cpp @@ -16,6 +16,7 @@ #include "RAJA_unit-test-types.hpp" RAJA_INDEX_VALUE(RangeStrongIndex, "RangeStrongIndex"); +RAJA_INDEX_VALUE(AnotherRangeStrongIndex, "AnotherRangeStrongIndex"); template class RangeSegmentUnitTest : public ::testing::Test {}; @@ -203,7 +204,7 @@ TEST(RangeSegmentUnitTest, LongRangeEndLValue) TEST(RangeSegmentUnitTest, TypedRangeEnd) { - auto r = RAJA::range(17); + auto r = RAJA::range(RangeStrongIndex(17)); ASSERT_EQ((RAJA::TypedRangeSegment(0, 17)), r); ASSERT_EQ(RangeStrongIndex(0), *r.begin()); @@ -257,41 +258,58 @@ TEST(RangeSegmentUnitTest, StrongRangeBeginEnd) TEST(RangeSegmentUnitTest, MixedStrongRangeBeginEnd) { - auto r = RAJA::range(3, RangeStrongIndex(17)); - - static_assert( - std::is_same>::value, - "range(int, StrongIndex) should preserve the strong storage type."); - ASSERT_EQ((RAJA::TypedRangeSegment(3, 17)), r); - ASSERT_EQ(RangeStrongIndex(3), *r.begin()); - ASSERT_EQ(RAJA::Index_type(14), r.size()); + static_assert(!requires { RAJA::range(3, RangeStrongIndex(17)); }); } -static_assert(RAJA::concepts::RangeConstructible); +static_assert(RAJA::concepts::RangeConstructible); +static_assert(!RAJA::concepts::RangeConstructible); static_assert(!RAJA::concepts::RangeConstructible); static_assert(!RAJA::concepts::RangeConstructible); +static_assert(requires { + RAJA::range(RangeStrongIndex(3), RangeStrongIndex(17)); +}); +static_assert(!requires { RAJA::range(RangeStrongIndex(3), 17); }); +static_assert(!requires { + RAJA::range(RangeStrongIndex(3), 17); +}); +static_assert(!requires { RAJA::range(AnotherRangeStrongIndex(3), 17); }); TEST(RangeSegmentUnitTest, MixedStrongRangeStrongBegin) { - auto r = RAJA::range(RangeStrongIndex(3), 17); - - static_assert( - std::is_same>::value, - "range(StrongIndex, int) should preserve the strong storage type."); - ASSERT_EQ((RAJA::TypedRangeSegment(3, 17)), r); - ASSERT_EQ(RangeStrongIndex(3), *r.begin()); - ASSERT_EQ(RAJA::Index_type(14), r.size()); + static_assert(!requires { RAJA::range(RangeStrongIndex(3), 17); }); } static_assert(RAJA::concepts::RangeStrideConstructible); + RangeStrongIndex, + RangeStrongIndex>); +static_assert(!RAJA::concepts::RangeStrideConstructible); static_assert(!RAJA::concepts::RangeStrideConstructible); static_assert(!RAJA::concepts::RangeStrideConstructible); +static_assert(!requires { + RAJA::range(RangeStrongIndex(2), 11, 3); +}); +static_assert(!requires { + RAJA::range(RangeStrongIndex(2), 11, 3); +}); +static_assert(!requires { + RAJA::range(AnotherRangeStrongIndex(2), 11, 3); +}); +static_assert(!requires { RAJA::range(2, 11, RangeStrongIndex(3)); }); +static_assert(!requires { + RAJA::range(2, 11, AnotherRangeStrongIndex(3)); +}); +static_assert(requires { + RAJA::range(RangeStrongIndex(2), + RangeStrongIndex(11), + RangeStrongIndex(3)); +}); TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) { @@ -304,43 +322,17 @@ TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) TEST(RangeSegmentUnitTest, MixedStrongRangeBeginEndStrideStrongEnd) { - auto r = RAJA::range(2, RangeStrongIndex(11), 3); - - static_assert( - std::is_same>::value, - "range(int, StrongIndex, stride) should preserve the strong storage " - "type."); - ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); - ASSERT_EQ(3, r.size()); - ASSERT_EQ(RangeStrongIndex(2), *r.begin()); + static_assert(!requires { RAJA::range(2, RangeStrongIndex(11), 3); }); } TEST(RangeSegmentUnitTest, MixedStrongRangeBeginEndStrideStrongBegin) { - auto r = RAJA::range(RangeStrongIndex(2), 11, 3); - - static_assert( - std::is_same>::value, - "range(StrongIndex, int, stride) should preserve the strong storage " - "type."); - ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); - ASSERT_EQ(3, r.size()); - ASSERT_EQ(RangeStrongIndex(2), *r.begin()); + static_assert(!requires { RAJA::range(RangeStrongIndex(2), 11, 3); }); } TEST(RangeSegmentUnitTest, RangeBeginEndStrongStride) { - auto r = RAJA::range(2, 11, RangeStrongIndex(3)); - - static_assert( - std::is_same>::value, - "range(begin, end, StrongIndex stride) should include stride in the " - "storage type."); - ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); - ASSERT_EQ(3, r.size()); - ASSERT_EQ(2, *r.begin()); + static_assert(!requires { RAJA::range(2, 11, RangeStrongIndex(3)); }); } TEST(RangeSegmentUnitTest, RangeBeginEndStrideNegative) From b7b14db1c12979b7a71e52b6a30f7dad0dc9ec1b Mon Sep 17 00:00:00 2001 From: Arturo Vargas Date: Tue, 30 Jun 2026 10:55:20 -0700 Subject: [PATCH 18/18] PR comments, add testing and checks --- include/RAJA/index/RangeSegment.hpp | 115 ++++++++++++-- test/unit/index/test-rangesegment.cpp | 216 +++++++++++++++++++------- 2 files changed, 259 insertions(+), 72 deletions(-) diff --git a/include/RAJA/index/RangeSegment.hpp b/include/RAJA/index/RangeSegment.hpp index cc20b03197..9865830579 100644 --- a/include/RAJA/index/RangeSegment.hpp +++ b/include/RAJA/index/RangeSegment.hpp @@ -571,6 +571,15 @@ struct strong_index_type template using strong_index_type_t = typename strong_index_type::type; +template +inline constexpr bool is_strong_index_v = + std::is_base_of_v>; + +template +inline constexpr bool is_non_strong_integral_index_v = + !is_strong_index_v && + std::is_integral_v>>; + template using range_stride_type_t = make_signed_t>>; @@ -591,7 +600,10 @@ using selected_range_storage_t = typename selected_range_storage::type; template -struct range_storage_from_strong; +struct range_storage_from_strong +{ + using type = StrongT; +}; template struct range_storage_from_strong @@ -605,23 +617,29 @@ struct strong_range_arg_compatible std::is_same_v, std::decay_t>> {}; +template +inline constexpr bool strong_range_arg_compatible_v = + strong_range_arg_compatible::value; + template struct range_storage_from_strong_compatible : std::bool_constant || - (strong_range_arg_compatible::value && - ...)> + (strong_range_arg_compatible_v && ...)> {}; template requires(std::is_same_v || - (strong_range_arg_compatible::value && ...)) + (strong_range_arg_compatible_v && ...)) struct range_storage_from_strong { using type = StrongT; }; template -struct range_stride_storage_from_strong; +struct range_stride_storage_from_strong +{ + using type = StrongT; +}; template struct range_stride_storage_from_strong @@ -632,16 +650,32 @@ struct range_stride_storage_from_strong template struct range_stride_storage_from_strong_compatible : std::bool_constant || - (strong_range_arg_compatible::value && - strong_range_arg_compatible::value && - strong_range_arg_compatible::value)> + (strong_range_arg_compatible_v && + strong_range_arg_compatible_v && + strong_range_arg_compatible_v)> +{}; + +template +struct range_stride_storage_from_strong_compatible_for_range + : std::bool_constant< + std::is_same_v + ? !is_strong_index_v + : ((strong_range_arg_compatible_v || + is_non_strong_integral_index_v) && + (strong_range_arg_compatible_v || + is_non_strong_integral_index_v) && + (strong_range_arg_compatible_v || + is_non_strong_integral_index_v))> {}; template requires(std::is_same_v || - (strong_range_arg_compatible::value && - strong_range_arg_compatible::value && - strong_range_arg_compatible::value)) + ((strong_range_arg_compatible_v || + is_non_strong_integral_index_v) && + (strong_range_arg_compatible_v || + is_non_strong_integral_index_v) && + (strong_range_arg_compatible_v || + is_non_strong_integral_index_v))) struct range_stride_storage_from_strong { using type = StrongT; @@ -688,16 +722,52 @@ template using explicit_range_arg_compatible = explicit_range_arg_compatible_impl; +template +inline constexpr bool explicit_range_arg_compatible_v = + explicit_range_arg_compatible::value; + +template>, + bool ArgIsStrong = std::is_base_of_v>> +struct explicit_range_stride_arg_compatible_impl + : explicit_range_arg_compatible_impl +{}; + +template +struct explicit_range_stride_arg_compatible_impl + : std::bool_constant< + std::is_integral_v>>> +{}; + +template +using explicit_range_stride_arg_compatible = + explicit_range_stride_arg_compatible_impl; + +template +inline constexpr bool explicit_range_stride_arg_compatible_v = + explicit_range_stride_arg_compatible::value; + template struct explicit_range_storage_compatible - : std::bool_constant<(explicit_range_arg_compatible::value && - ...)> + : std::bool_constant<(explicit_range_arg_compatible_v && ...)> +{}; + +template +struct explicit_range_stride_storage_compatible + : std::bool_constant<( + explicit_range_stride_arg_compatible_v && ...)> {}; template inline constexpr bool explicit_range_storage_compatible_v = explicit_range_storage_compatible::value; +template +inline constexpr bool explicit_range_stride_storage_compatible_v = + explicit_range_stride_storage_compatible::value; + template inline constexpr bool deduced_range_storage_compatible_v = range_storage_from_strong_compatible, @@ -712,6 +782,14 @@ inline constexpr bool deduced_range_stride_storage_compatible_v = EndT, StrideT>::value; +template +inline constexpr bool deduced_range_stride_storage_compatible_for_range_v = + range_stride_storage_from_strong_compatible_for_range< + strong_index_type_t, + BeginT, + EndT, + StrideT>::value; + template inline constexpr bool range_storage_compatible_v = std::is_same_v @@ -721,8 +799,13 @@ inline constexpr bool range_storage_compatible_v = template inline constexpr bool range_stride_storage_compatible_v = std::is_same_v - ? deduced_range_stride_storage_compatible_v - : explicit_range_storage_compatible_v; + ? deduced_range_stride_storage_compatible_for_range_v + : explicit_range_stride_storage_compatible_v; } // namespace detail @@ -872,7 +955,7 @@ concept RangeConstructible = template concept RangeStrideConstructible = - RAJA::detail::deduced_range_stride_storage_compatible_v; + RAJA::detail::deduced_range_stride_storage_compatible_for_range_v; } // namespace concepts diff --git a/test/unit/index/test-rangesegment.cpp b/test/unit/index/test-rangesegment.cpp index 9f39925f3f..d9340810a3 100644 --- a/test/unit/index/test-rangesegment.cpp +++ b/test/unit/index/test-rangesegment.cpp @@ -15,21 +15,33 @@ #include "RAJA_unit-test-types.hpp" +#include + RAJA_INDEX_VALUE(RangeStrongIndex, "RangeStrongIndex"); RAJA_INDEX_VALUE(AnotherRangeStrongIndex, "AnotherRangeStrongIndex"); +template +concept RangeCallable = requires { + RAJA::range(std::declval()...); +}; + +template +concept TypedRangeCallable = requires { + RAJA::range(std::declval()...); +}; + template class RangeSegmentUnitTest : public ::testing::Test {}; TYPED_TEST_SUITE(RangeSegmentUnitTest, UnitIndexTypes); -template< typename T, typename std::enable_if::value>::type* = nullptr> +template>* = nullptr> void NegativeRangeSegConstructorsTest() { } -template< typename T, typename std::enable_if::value>::type* = nullptr> +template>* = nullptr> void NegativeRangeSegConstructorsTest() { RAJA::TypedRangeSegment r1(-10, 7); @@ -79,12 +91,12 @@ TYPED_TEST(RangeSegmentUnitTest, Swaps) ASSERT_EQ(r2, r3); } -template< typename T, typename std::enable_if::value>::type* = nullptr> +template>* = nullptr> void NegativeRangeSegIteratorsTest() { } -template< typename T, typename std::enable_if::value>::type* = nullptr> +template>* = nullptr> void NegativeRangeSegIteratorsTest() { RAJA::TypedRangeSegment r3(-2, 100); @@ -104,14 +116,16 @@ TYPED_TEST(RangeSegmentUnitTest, Iterators) NegativeRangeSegIteratorsTest(); } -template >::value>::type* = nullptr> +template>>* = nullptr> void runNegativeIndexSliceTests() { } -template >::value>::type* = nullptr> +template>>* = nullptr> void runNegativeIndexSliceTests() { auto r1 = RAJA::TypedRangeSegment(-4, 4); @@ -183,7 +197,7 @@ TEST(RangeSegmentUnitTest, LongRangeEnd) { auto r = RAJA::range(long {10}); - static_assert(std::is_same>::value, + static_assert(std::is_same_v>, "range(long) should deduce the segment storage type."); ASSERT_EQ((RAJA::TypedRangeSegment(0, 10)), r); ASSERT_EQ(0, *r.begin()); @@ -195,7 +209,7 @@ TEST(RangeSegmentUnitTest, LongRangeEndLValue) const long end = 10; auto r = RAJA::range(end); - static_assert(std::is_same>::value, + static_assert(std::is_same_v>, "range(const long&) should decay the deduced storage type."); ASSERT_EQ((RAJA::TypedRangeSegment(0, 10)), r); ASSERT_EQ(0, *r.begin()); @@ -216,7 +230,7 @@ TEST(RangeSegmentUnitTest, DeducedStrongRangeEnd) auto r = RAJA::range(RangeStrongIndex(17)); static_assert( - std::is_same>::value, + std::is_same_v>, "range(StrongIndex) should deduce the segment storage type."); ASSERT_EQ((RAJA::TypedRangeSegment(0, 17)), r); ASSERT_EQ(RangeStrongIndex(0), *r.begin()); @@ -227,7 +241,7 @@ TEST(RangeSegmentUnitTest, ExplicitTypedRangeEnd) { auto r = RAJA::range(10); - static_assert(std::is_same>::value, + static_assert(std::is_same_v>, "range(end) should use the explicit storage type."); ASSERT_EQ((RAJA::TypedRangeSegment(0, 10)), r); ASSERT_EQ(0, *r.begin()); @@ -249,7 +263,7 @@ TEST(RangeSegmentUnitTest, StrongRangeBeginEnd) auto r = RAJA::range(begin, end); static_assert( - std::is_same>::value, + std::is_same_v>, "range(StrongIndex, StrongIndex) should deduce the strong storage type."); ASSERT_EQ((RAJA::TypedRangeSegment(3, 17)), r); ASSERT_EQ(RangeStrongIndex(3), *r.begin()); @@ -258,7 +272,7 @@ TEST(RangeSegmentUnitTest, StrongRangeBeginEnd) TEST(RangeSegmentUnitTest, MixedStrongRangeBeginEnd) { - static_assert(!requires { RAJA::range(3, RangeStrongIndex(17)); }); + static_assert(!RangeCallable); } static_assert(RAJA::concepts::RangeConstructible); static_assert(!RAJA::concepts::RangeConstructible); static_assert(!RAJA::concepts::RangeConstructible); -static_assert(requires { - RAJA::range(RangeStrongIndex(3), RangeStrongIndex(17)); -}); -static_assert(!requires { RAJA::range(RangeStrongIndex(3), 17); }); -static_assert(!requires { - RAJA::range(RangeStrongIndex(3), 17); -}); -static_assert(!requires { RAJA::range(AnotherRangeStrongIndex(3), 17); }); +static_assert( + TypedRangeCallable); +static_assert(!TypedRangeCallable); +static_assert(!TypedRangeCallable); +static_assert( + !TypedRangeCallable); TEST(RangeSegmentUnitTest, MixedStrongRangeStrongBegin) { - static_assert(!requires { RAJA::range(RangeStrongIndex(3), 17); }); + static_assert(!RangeCallable); } static_assert(RAJA::concepts::RangeStrideConstructible); -static_assert(!RAJA::concepts::RangeStrideConstructible); +static_assert(RAJA::concepts::RangeStrideConstructible); +static_assert(RAJA::concepts::RangeStrideConstructible); +static_assert(!RAJA::concepts::RangeStrideConstructible); -static_assert(!RAJA::concepts::RangeStrideConstructible); -static_assert(!RAJA::concepts::RangeStrideConstructible); -static_assert(!requires { - RAJA::range(RangeStrongIndex(2), 11, 3); -}); -static_assert(!requires { - RAJA::range(RangeStrongIndex(2), 11, 3); -}); -static_assert(!requires { - RAJA::range(AnotherRangeStrongIndex(2), 11, 3); -}); -static_assert(!requires { RAJA::range(2, 11, RangeStrongIndex(3)); }); -static_assert(!requires { - RAJA::range(2, 11, AnotherRangeStrongIndex(3)); -}); -static_assert(requires { - RAJA::range(RangeStrongIndex(2), - RangeStrongIndex(11), - RangeStrongIndex(3)); -}); + RangeStrongIndex>); +static_assert(!TypedRangeCallable); +static_assert(TypedRangeCallable); +static_assert(TypedRangeCallable); +static_assert(TypedRangeCallable); +static_assert(!TypedRangeCallable); +static_assert(TypedRangeCallable); +static_assert( + TypedRangeCallable); +static_assert( + !TypedRangeCallable); +static_assert( + !TypedRangeCallable); +static_assert(TypedRangeCallable); +static_assert(TypedRangeCallable); +static_assert(!RangeCallable); +static_assert( + RangeCallable); +static_assert(RangeCallable); TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) { @@ -322,17 +343,88 @@ TEST(RangeSegmentUnitTest, RangeBeginEndStridePositive) TEST(RangeSegmentUnitTest, MixedStrongRangeBeginEndStrideStrongEnd) { - static_assert(!requires { RAJA::range(2, RangeStrongIndex(11), 3); }); + auto r = RAJA::range(2, RangeStrongIndex(11), 3); + + static_assert( + std::is_same_v>, + "range(begin, StrongIndex, stride) should deduce the strong storage " + "type."); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); + ASSERT_EQ(RAJA::Index_type(3), r.size()); + ASSERT_EQ(RangeStrongIndex(2), *r.begin()); } TEST(RangeSegmentUnitTest, MixedStrongRangeBeginEndStrideStrongBegin) { - static_assert(!requires { RAJA::range(RangeStrongIndex(2), 11, 3); }); + auto r = RAJA::range(RangeStrongIndex(2), 11, 3); + + static_assert( + std::is_same_v>, + "range(StrongIndex, end, stride) should deduce the strong storage " + "type."); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); + ASSERT_EQ(RAJA::Index_type(3), r.size()); + ASSERT_EQ(RangeStrongIndex(2), *r.begin()); } TEST(RangeSegmentUnitTest, RangeBeginEndStrongStride) { - static_assert(!requires { RAJA::range(2, 11, RangeStrongIndex(3)); }); + static_assert(!RangeCallable); +} + +TEST(RangeSegmentUnitTest, ExplicitTypedRangeBeginEndStride) +{ + auto r_int = RAJA::range(3, 17, 1); + auto r_short = RAJA::range(3, 17, static_cast(1)); + auto r_long = RAJA::range(3, 17, static_cast(1)); + + static_assert( + std::is_same_v>, + "range(begin, end, int stride) should use int storage."); + static_assert( + std::is_same_v>, + "range(begin, end, short stride) should use int storage."); + static_assert( + std::is_same_v, + "range(begin, end, long stride) should use int storage."); + + ASSERT_EQ((RAJA::TypedRangeStrideSegment(3, 17, 1)), r_int); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(3, 17, 1)), r_short); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(3, 17, 1)), r_long); +} + +TEST(RangeSegmentUnitTest, ExplicitStrongRangeBeginEndStride) +{ + auto r_int = + RAJA::range(3, 17, 1); + auto r_strong_bounds = RAJA::range( + RangeStrongIndex(3), RangeStrongIndex(17), 1); + auto r_strong_stride = RAJA::range( + RangeStrongIndex(3), RangeStrongIndex(17), RangeStrongIndex(1)); + + static_assert( + std::is_same_v>, + "range(begin, end, stride) should use strong storage."); + static_assert( + std::is_same_v>, + "range(StrongIndex, StrongIndex, stride) should use strong " + "storage."); + static_assert( + std::is_same_v>, + "range(StrongIndex, StrongIndex, StrongIndex) should use " + "strong storage."); + + ASSERT_EQ((RAJA::TypedRangeStrideSegment(3, 17, 1)), + r_int); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(3, 17, 1)), + r_strong_bounds); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(3, 17, 1)), + r_strong_stride); } TEST(RangeSegmentUnitTest, RangeBeginEndStrideNegative) @@ -349,19 +441,31 @@ TEST(RangeSegmentUnitTest, RangeBeginEndStrideDeducesFromStride) auto r = RAJA::range(2, 11, long {3}); static_assert( - std::is_same>::value, + std::is_same_v>, "range(begin, end, stride) should include stride in the storage type."); ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); ASSERT_EQ(3, r.size()); ASSERT_EQ(2, *r.begin()); } +TEST(RangeSegmentUnitTest, RangeBeginEndStrideDeducesCommonType) +{ + auto r = RAJA::range(3, 17, static_cast(1)); + + static_assert( + std::is_same_v>, + "range(begin, end, stride) should use the common storage type."); + ASSERT_EQ((RAJA::TypedRangeStrideSegment(3, 17, 1)), r); + ASSERT_EQ(14, r.size()); + ASSERT_EQ(3, *r.begin()); +} + TEST(RangeSegmentUnitTest, RangeBeginEndUnsignedStrideWorks) { auto r = RAJA::range(-2, 11, 3u); static_assert( - std::is_same>::value, + std::is_same_v>, "range(begin, end, unsigned stride) should preserve signed storage when " "begin/end are signed."); ASSERT_EQ((RAJA::TypedRangeStrideSegment(-2, 11, 3)), r); @@ -374,7 +478,7 @@ TEST(RangeSegmentUnitTest, MakeStridedRangeDeducesFromStride) auto r = RAJA::make_strided_range(2, 11, long {3}); static_assert( - std::is_same>::value, + std::is_same_v>, "make_strided_range(begin, end, stride) should include stride in the " "storage type."); ASSERT_EQ((RAJA::TypedRangeStrideSegment(2, 11, 3)), r); @@ -387,7 +491,7 @@ TEST(RangeSegmentUnitTest, MakeStridedRangeUnsignedStrideWorks) auto r = RAJA::make_strided_range(-2, 11, 3u); static_assert( - std::is_same>::value, + std::is_same_v>, "make_strided_range(begin, end, unsigned stride) should preserve signed " "storage when begin/end are signed."); ASSERT_EQ((RAJA::TypedRangeStrideSegment(-2, 11, 3)), r);