diff --git a/include/experimental/__p0009_bits/layout_left.hpp b/include/experimental/__p0009_bits/layout_left.hpp index 622137a7..f9729f16 100644 --- a/include/experimental/__p0009_bits/layout_left.hpp +++ b/include/experimental/__p0009_bits/layout_left.hpp @@ -18,6 +18,7 @@ #include "macros.hpp" #include "trait_backports.hpp" #include "extents.hpp" +#include "../__p2642_bits/layout_padded_fwd.hpp" namespace MDSPAN_IMPL_STANDARD_NAMESPACE { @@ -108,6 +109,36 @@ class layout_left::mapping { */ } +#if MDSPAN_HAS_CXX_17 + /** + * Converting constructor from `layout_left_padded::mapping`. + * + * This overload participates in overload resolution only if _Mapping is a layout_left_padded mapping and + * extents_type is constructible from _Mapping::extents_type. + * + * \note There is currently a difference from p2642r2, where this function is specified as taking + * `layout_left_padded< padding_value >::mapping< Extents>`. However, this makes `padding_value` non-deducible. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::is_layout_left_padded_mapping<_Mapping>::value + && std::is_constructible_v + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v)) + mapping(const _Mapping& __other) noexcept + : __extents(__other.extents()) + { + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: + check_padded_layout_converting_constructor_mandates(); + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: + check_padded_layout_converting_constructor_preconditions< + extents_type>(__other); + } +#endif + MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( diff --git a/include/experimental/__p0009_bits/layout_right.hpp b/include/experimental/__p0009_bits/layout_right.hpp index 76adca81..16c06f75 100644 --- a/include/experimental/__p0009_bits/layout_right.hpp +++ b/include/experimental/__p0009_bits/layout_right.hpp @@ -20,6 +20,7 @@ #include "extents.hpp" #include #include "layout_stride.hpp" +#include "../__p2642_bits/layout_padded_fwd.hpp" namespace MDSPAN_IMPL_STANDARD_NAMESPACE { @@ -113,6 +114,34 @@ class layout_right::mapping { */ } + /** + * Converting constructor from `layout_right_padded::mapping`. + * + * This overload participates in overload resolution only if _Mapping is a layout_right_padded mapping and + * extents_type is constructible from _Mapping::extents_type. + * + * \note There is currently a difference from p2642r2, where this function is specified as taking + * `layout_right_padded< padding_value >::mapping< Extents>`. However, this makes `padding_value` non-deducible. + */ +#if MDSPAN_HAS_CXX_17 + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail::is_layout_right_padded_mapping<_Mapping>::value + && std::is_constructible_v)) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v)) + mapping(const _Mapping &__other) noexcept + : __extents(__other.extents()) + { + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: + check_padded_layout_converting_constructor_mandates(); + MDSPAN_IMPL_PROPOSED_NAMESPACE::detail:: + check_padded_layout_converting_constructor_preconditions< + extents_type>(__other); + } +#endif + MDSPAN_TEMPLATE_REQUIRES( class OtherExtents, /* requires */ ( diff --git a/include/experimental/__p2642_bits/layout_padded.hpp b/include/experimental/__p2642_bits/layout_padded.hpp new file mode 100644 index 00000000..4b74626b --- /dev/null +++ b/include/experimental/__p2642_bits/layout_padded.hpp @@ -0,0 +1,795 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include +#include "layout_padded_fwd.hpp" +#include "../__p0009_bits/dynamic_extent.hpp" +#include "../__p0009_bits/extents.hpp" +#include "../__p0009_bits/mdspan.hpp" +#include "../__p0009_bits/layout_left.hpp" +#include "../__p0009_bits/layout_right.hpp" +#include "../__p0009_bits/layout_stride.hpp" + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { + +namespace detail { +template +MDSPAN_INLINE_FUNCTION +constexpr _T +find_next_multiple(_T alignment, _T offset) +{ + if ( alignment == 0 ) { + return _T(0); + } else { + return ( ( offset + alignment - 1 ) / alignment) * alignment; + } +} + +template +MDSPAN_INLINE_FUNCTION constexpr size_t get_actual_static_padding_value() { + constexpr auto rank = _ExtentsType::rank(); + + if constexpr (rank <= typename _ExtentsType::rank_type(1)) { + return 0; + } else if constexpr (_PaddingValue != dynamic_extent && + _ExtentsType::static_extent(_ExtentToPadIdx) != + dynamic_extent) { + static_assert( + (_PaddingValue != 0) || + (_ExtentsType::static_extent(_ExtentToPadIdx) == 0), + "padding stride can be 0 only if " + "extents_type::static_extent(extent-to-pad) is 0 or dynamic_extent"); + return find_next_multiple(_PaddingValue, + _ExtentsType::static_extent(_ExtentToPadIdx)); + } else { + return dynamic_extent; + } +} + +template +struct static_array_type_for_padded_extent +{ + static constexpr size_t padding_value = _PaddingValue; + using index_type = typename _Extents::index_type; + using extents_type = _Extents; + using type = ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::maybe_static_array< + index_type, size_t, dynamic_extent, + detail::get_actual_static_padding_value()>; +}; + +template +struct static_array_type_for_padded_extent<_PaddingValue, _Extents, + _ExtentToPadIdx, Rank, std::enable_if_t> { + using index_type = typename _Extents::index_type; + using extents_type = _Extents; + using type = + ::MDSPAN_IMPL_STANDARD_NAMESPACE::detail::maybe_static_array< + index_type, size_t, dynamic_extent, 0>; +}; + +template +struct padded_extent { + static constexpr size_t padding_value = _PaddingValue; + using index_type = typename _Extents::index_type; + using extents_type = _Extents; + using static_array_type = typename static_array_type_for_padded_extent< + padding_value, _Extents, _ExtentToPadIdx, _Extents::rank()>::type; + + static constexpr auto static_value() { return static_array_type::static_value(0); } + + MDSPAN_INLINE_FUNCTION + static constexpr static_array_type + init_padding(const _Extents &exts) { + if constexpr ((_Extents::rank() > 1) && (padding_value == dynamic_extent)) { + return {exts.extent(_ExtentToPadIdx)}; + } else { + return init_padding(exts, padding_value); + } + } + + MDSPAN_INLINE_FUNCTION static constexpr static_array_type + init_padding(const _Extents &exts, + index_type padding_value) { + if constexpr (_Extents::rank() > 1) { + return {find_next_multiple(padding_value, + exts.extent(_ExtentToPadIdx))}; + } else { + return {}; + } + } + + template + MDSPAN_INLINE_FUNCTION static constexpr static_array_type + init_padding(const _Mapping &other_mapping, + std::integral_constant) { + if constexpr (_Extents::rank() > 1) { + return {other_mapping.stride(_PaddingStrideIdx)}; + } else { + return {}; + } + } +}; +} // namespace detail + +template +template +class layout_left_padded::mapping { +public: + static constexpr size_t padding_value = PaddingValue; + + using extents_type = Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_left_padded; + +#ifndef MDSPAN_INTERNAL_TEST +private: +#endif // MDSPAN_INTERNAL_TEST + + static constexpr rank_type padded_stride_idx = detail::layout_padded_constants::padded_stride_idx; + static constexpr rank_type extent_to_pad_idx = detail::layout_padded_constants::extent_to_pad_idx; + + static_assert((padding_value != 0) + || (extents_type::static_extent(extent_to_pad_idx) == 0) + || (extents_type::static_extent(extent_to_pad_idx) == dynamic_extent), + "out of bounds access for rank 0"); + + using padded_stride_type = detail::padded_extent< padding_value, extents_type, extent_to_pad_idx >; + + static constexpr size_t static_padding_stride = padded_stride_type::static_value(); + + typename padded_stride_type::static_array_type padded_stride = {}; + extents_type exts = {}; + + constexpr index_type compute_offset(std::index_sequence<>) const { + return 0; + } + + template + constexpr index_type compute_offset(std::index_sequence, + IndexOffset index_offset) const { + return index_offset; + } + + template + constexpr index_type compute_offset(std::index_sequence, + IndexOffsets... index_offsets) const { + index_type indices[] = {static_cast(index_offsets)...}; + // self-recursive fold trick from + // https://github.com/llvm/llvm-project/blob/96e1914aa2e6d8966acbfbe2f4d184201f1aa318/libcxx/include/mdspan/layout_left.h#L144 + index_type res = 0; + ((res = indices[extents_type::rank() - 1 - Ranks] + + ((extents_type::rank() - 1 - Ranks) == extent_to_pad_idx + ? padded_stride.value(0) + : exts.extent(extents_type::rank() - 1 - Ranks)) * + res), + ...); + return res; + } + +public: +#if !MDSPAN_HAS_CXX_20 + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr mapping() + : mapping(extents_type{}) + {} +#else + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr mapping() + requires(static_padding_stride != dynamic_extent) = default; + + MDSPAN_INLINE_FUNCTION + constexpr mapping() + requires(static_padding_stride == dynamic_extent) + : mapping(extents_type{}) + {} +#endif + + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(const mapping&) noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED mapping& operator=(const mapping&) noexcept = default; + + /** + * Initializes the mapping with the given extents. + * + * \param ext the given extents + */ + MDSPAN_INLINE_FUNCTION + constexpr mapping(const extents_type& ext) + : padded_stride(padded_stride_type::init_padding(ext)), exts(ext) + {} + + /** + * Initializes the mapping with the given extents and the specified padding value. + * + * This overload participates in overload resolution only if `is_convertible_v` + * is `true` and `is_nothrow_constructible_v` is `true` + * + * \param ext the given extents + * \param padding_value the padding value + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Size, + /* requires */ ( + std::is_convertible_v<_Size, index_type> + && std::is_nothrow_constructible_v + ) + ) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const extents_type &ext, _Size dynamic_padding_value) + : padded_stride(padded_stride_type::init_padding(ext, dynamic_padding_value)), exts(ext) + { + assert((padding_value == dynamic_extent) || (static_cast(padding_value) == static_cast(dynamic_padding_value))); + } + + /** + * Converting constructor from `layout_left::mapping`. + * + * This overload participates in overload resolution only if `is_constructible_v` is true. + * If `OtherExtents::rank() > 1` then one of `padding_value`, `static_extent(0)`, or `OtherExtents::static_extent(0)` must be `dynamic_extent`; + * otherwise, `OtherExtents::static_extent(0)` must be equal to the least multiple of `padding_value` greater than or equal to `extents_type::static_extent(0)` + */ + MDSPAN_TEMPLATE_REQUIRES( + class _OtherExtents, + /* requires */ ( + std::is_constructible_v + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v<_OtherExtents, extents_type>)) + constexpr mapping(const layout_left::mapping<_OtherExtents> &other_mapping) + : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), + exts(other_mapping.extents()) + { + static_assert((_OtherExtents::rank() > 1) || (static_padding_stride != dynamic_extent) || (_OtherExtents::static_extent(extent_to_pad_idx) != dynamic_extent) + || (static_padding_stride == _OtherExtents::static_extent(extent_to_pad_idx))); + } + + /** + * Converting constructor from `layout_stride::mapping`. + * + * This overload participates in overload resolution only if `is_constructible_v` is true + */ + MDSPAN_TEMPLATE_REQUIRES( + class _OtherExtents, + /* requires */ ( + std::is_constructible_v + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) + constexpr mapping(const layout_stride::mapping<_OtherExtents> &other_mapping) + : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), + exts(other_mapping.extents()) + { + } + + /** + * Converting constructor from `layout_left_padded::mapping`. + * + * This overload participates in overload resolution only if `is_constructible_v` is true. + * Either `padding_value` or `OtherPaddingStride` must be `std::dynamic_extent`, or `padding_value == OtherPaddingStride`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + detail::is_layout_left_padded_mapping<_Mapping>::value + && std::is_constructible_v + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 1 && (padding_value == dynamic_extent || _Mapping::padding_value == dynamic_extent))) + constexpr + mapping(const _Mapping &other_mapping) + : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), + exts(other_mapping.extents()) + { + static_assert(padding_value == dynamic_extent || + _Mapping::padding_value == dynamic_extent || + padding_value == _Mapping::padding_value); + } + + /** + * Converting constructor from `layout_right_padded::mapping`. + * + * This overload participates in overload resolution only if `extents_type::rank()` is 0 or 1 and `is_constructible_v` is `true`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + detail::is_layout_right_padded_mapping<_Mapping>::value + && extents_type::rank() <= 1 + && std::is_constructible_v + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v)) + constexpr + mapping(const _Mapping &other_mapping) noexcept + : padded_stride(padded_stride_type::init_padding(other_mapping.extents(), other_mapping.extents().extent(extent_to_pad_idx))), + exts(other_mapping.extents()) + {} + + constexpr const extents_type &extents() const noexcept + { + return exts; + } + + constexpr std::array + strides() const noexcept + { + if constexpr ( extents_type::rank() == 0 ) { + return {}; + } else if constexpr ( extents_type::rank() == 1 ) { + return {1}; + } else { + index_type value = 1; + std::array s{}; + s[extent_to_pad_idx] = value; + value *= padded_stride.value(0); + for (rank_type r = extent_to_pad_idx + 1; r < extents_type::rank() - 1; ++r) + { + s[r] = value; + value *= exts.extent(r); + } + s[extents_type::rank() - 1] = value; + return s; + } + } + + constexpr index_type + required_span_size() const noexcept + { + if constexpr ( extents_type::rank() == 0 ) { + return 1; + } else if constexpr ( extents_type::rank() == 1 ) { + return exts.extent(0); + } else { + index_type value = padded_stride.value(0); + for (rank_type r = 1; r < extents_type::rank(); ++r) { + value *= exts.extent(r); + } + return value; + } + } + + /** + * Return the mapping given the provided indices per rank. + * + * This overload participates in overload resolution only if: + * - `sizeof...(Indices) == extents_type::rank()`, + * - `(is_convertible_v && ...) is true`, and + * - (is_nothrow_constructible_v && ...) is true. + */ + MDSPAN_TEMPLATE_REQUIRES( + class... _Indices, + /* requires */ ( + sizeof...(_Indices) == extents_type::rank() + && (std::is_convertible_v<_Indices, index_type> && ...) + && (std::is_nothrow_constructible_v && ...) + ) + ) + constexpr size_t operator()(_Indices... idxs) const noexcept + { + return compute_offset(std::index_sequence_for<_Indices...>{}, idxs...); + } + + static constexpr bool is_always_unique() noexcept { return true; } + static constexpr bool is_always_exhaustive() noexcept + { + return (extents_type::rank() <= rank_type(1)) + || (extents_type::static_extent(extent_to_pad_idx) != dynamic_extent + && extents_type::static_extent(extent_to_pad_idx) == padded_stride_type::static_value()); + } + static constexpr bool is_always_strided() noexcept { return true; } + + static constexpr bool is_unique() noexcept { return true; } + constexpr bool is_exhaustive() const noexcept + { + return (extents_type::rank() < 2) + || (exts.extent(extent_to_pad_idx) == padded_stride.value(0)); + } + static constexpr bool is_strided() noexcept { return true; } + + constexpr index_type stride(rank_type r) const noexcept + { + assert(r < extents_type::rank()); + if(r == 0) return index_type(1); + + index_type value = padded_stride.value(0); + for (rank_type k = 1; k < r; k++) value *= exts.extent(k); + + return value; + } + + /** + * Equality operator between `layout_left_padded`s + * + * This overload only participates in overload resolution if `OtherExtents::rank() == extents_type::rank()`. + * + * \note There is currently a difference from p2642r2, where this function is specified as taking + * `layout_left_padded< padding_value >::mapping< Extents>`. However, this makes `padding_value` non-deducible. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + detail::is_layout_left_padded_mapping<_Mapping>::value + && (_Mapping::extents_type::rank() == extents_type::rank()) + ) + ) + friend constexpr bool operator==(const mapping &left, const _Mapping &right) noexcept + { + // Workaround for some compilers not short-circuiting properly with compile-time checks + // i.e. we can't access stride(_padding_stride_idx) of a rank 0 mapping + bool strides_equal = true; + if constexpr (extents_type::rank() > rank_type(1)) + { + strides_equal = left.stride(padded_stride_idx) == right.stride(padded_stride_idx); + } + return (left.extents() == right.extents()) && strides_equal; + } + +#if !MDSPAN_HAS_CXX_20 + /** + * Inequality operator between `layout_left_padded`s + * + * This overload only participates in overload resolution if `OtherExtents::rank() == extents_type::rank()`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + detail::is_layout_left_padded_mapping<_Mapping>::value + && (_Mapping::extents_type::rank() == extents_type::rank()) + ) + ) + friend constexpr bool operator!=(const mapping &left, const _Mapping &right) noexcept + { + return !(left == right); + } +#endif +}; + +template +template +class layout_right_padded::mapping { +public: + static constexpr size_t padding_value = PaddingValue; + + using extents_type = Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = layout_right_padded; + +#ifndef MDSPAN_INTERNAL_TEST + private: +#endif // MDSPAN_INTERNAL_TEST + + static constexpr rank_type padded_stride_idx = detail::layout_padded_constants::padded_stride_idx; + static constexpr rank_type extent_to_pad_idx = detail::layout_padded_constants::extent_to_pad_idx; + + static_assert((padding_value != 0) + || (extents_type::static_extent(extent_to_pad_idx) == 0) + || (extents_type::static_extent(extent_to_pad_idx) == dynamic_extent), + "if padding stride is 0, static_extent(extent-to-pad-rank) must also be 0 or dynamic_extent"); + + using padded_stride_type = detail::padded_extent< padding_value, extents_type, extent_to_pad_idx >; + static constexpr size_t static_padding_stride = padded_stride_type::static_value(); + + typename padded_stride_type::static_array_type padded_stride = {}; + extents_type exts = {}; + + constexpr index_type compute_offset(std::index_sequence<>) const { + return 0; + } + + template + constexpr index_type compute_offset(std::index_sequence, + IndexOffset index_offset) const { + return index_offset; + } + + template + constexpr index_type compute_offset(std::index_sequence, + IndexOffsets... index_offsets) const { + // self-recursive fold trick from + // https://github.com/llvm/llvm-project/blob/4d9771741d40cc9cfcccb6b033f43689d36b705a/libcxx/include/mdspan/layout_right.h#L141 + index_type res = 0; + ((res = static_cast(index_offsets) + + (Ranks == extent_to_pad_idx ? padded_stride.value(0) + : exts.extent(Ranks)) * + res), + ...); + return res; + } + +public: +#if !MDSPAN_HAS_CXX_20 + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr mapping() + : mapping(extents_type{}) + {} +#else + MDSPAN_INLINE_FUNCTION_DEFAULTED + constexpr mapping() + requires(static_padding_stride != dynamic_extent) = default; + + MDSPAN_INLINE_FUNCTION + constexpr mapping() + requires(static_padding_stride == dynamic_extent) + : mapping(extents_type{}) + {} +#endif + + MDSPAN_INLINE_FUNCTION_DEFAULTED constexpr mapping(const mapping&) noexcept = default; + MDSPAN_INLINE_FUNCTION_DEFAULTED mapping& operator=(const mapping&) noexcept = default; + + /** + * Initializes the mapping with the given extents. + * + * \param ext the given extents + */ + MDSPAN_INLINE_FUNCTION + constexpr mapping(const extents_type &ext) + : padded_stride(padded_stride_type::init_padding(ext)), exts(ext) {} + + /** + * Initializes the mapping with the given extents and the specified padding value. + * + * This overload participates in overload resolution only if `is_convertible_v` + * is `true` and `is_nothrow_constructible_v` is `true` + * + * \param ext the given extents + * \param padding_value the padding value + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Size, + /* requires */ ( + std::is_convertible_v<_Size, index_type> + && std::is_nothrow_constructible_v + ) + ) + MDSPAN_INLINE_FUNCTION + constexpr mapping(const extents_type &ext, _Size dynamic_padding_value) + : padded_stride(padded_stride_type::init_padding(ext, static_cast(dynamic_padding_value))), + exts(ext) { + assert((padding_value == dynamic_extent) || + (static_cast(padding_value) == static_cast(dynamic_padding_value))); + } + + /** + * Converting constructor from `layout_right::mapping`. + * + * This overload participates in overload resolution only if `is_constructible_v` is true. + * If `OtherExtents::rank() > 1` then one of `padding_value`, `static_extent(0)`, or `OtherExtents::static_extent(0)` must be `dynamic_extent`; + * otherwise, `OtherExtents::static_extent(0)` must be equal to the least multiple of `padding_value` greater than or equal to `extents_type::static_extent(0)` + */ + MDSPAN_TEMPLATE_REQUIRES( + class _OtherExtents, + /* requires */ ( + std::is_constructible_v + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v<_OtherExtents, extents_type>)) + constexpr mapping(const layout_right::mapping<_OtherExtents> &other_mapping) + : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), + exts(other_mapping.extents()) + { + static_assert((_OtherExtents::rank() > 1) || (padded_stride_type::static_value() != dynamic_extent) || (_OtherExtents::static_extent(extent_to_pad_idx) != dynamic_extent) + || (padded_stride_type::static_value() == _OtherExtents::static_extent(extent_to_pad_idx))); + } + + /** + * Converting constructor from `layout_stride::mapping`. + * + * This overload participates in overload resolution only if `is_constructible_v` is true + */ + MDSPAN_TEMPLATE_REQUIRES( + class _OtherExtents, + /* requires */ ( + std::is_constructible_v + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 0)) + constexpr mapping(const layout_stride::mapping<_OtherExtents> &other_mapping) + : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), + exts(other_mapping.extents()) + {} + + /** + * Converting constructor from `layout_right_padded::mapping`. + * + * This overload participates in overload resolution only if `is_constructible_v` is true. + * Either `padding_value` or `OtherPaddingStride` must be `std::dynamic_extent`, or `padding_value == OtherPaddingStride`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + detail::is_layout_right_padded_mapping<_Mapping>::value + && std::is_constructible_v + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((extents_type::rank() > 1 && + (padding_value == dynamic_extent || + _Mapping::padding_value == dynamic_extent))) + constexpr mapping(const _Mapping &other_mapping) + : padded_stride(padded_stride_type::init_padding(other_mapping, std::integral_constant{})), + exts(other_mapping.extents()) + { + static_assert(padding_value == dynamic_extent || + _Mapping::padding_value == dynamic_extent || + padding_value == _Mapping::padding_value); + } + + /** + * Converting constructor from `layout_left_padded::mapping`. + * + * This overload participates in overload resolution only if `extents_type::rank()` is 0 or 1 and `is_constructible_v` is `true`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + detail::is_layout_left_padded_mapping<_Mapping>::value + && extents_type::rank() <= 1 + && std::is_constructible_v + ) + ) + MDSPAN_CONDITIONAL_EXPLICIT((!std::is_convertible_v)) + constexpr mapping(const _Mapping &other_mapping) noexcept + : padded_stride(padded_stride_type::init_padding(other_mapping.extents(), other_mapping.extents().extent(extent_to_pad_idx))), + exts(other_mapping.extents()) + {} + + constexpr const extents_type &extents() const noexcept + { + return exts; + } + + constexpr std::array + strides() const noexcept + { + if constexpr ( extents_type::rank() == 0 ) { + return {}; + } else if constexpr ( extents_type::rank() == 1 ) { + return {1}; + } else { + index_type value = 1; + std::array s{}; + s[extent_to_pad_idx] = value; + value *= padded_stride.value(0); + for (rank_type r = extent_to_pad_idx - 1; r > 0; --r) + { + s[r] = value; + value *= exts.extent(r); + } + s[0] = value; + return s; + } + } + + constexpr index_type + required_span_size() const noexcept + { + if constexpr ( extents_type::rank() == 0 ) { + return 1; + } else if constexpr ( extents_type::rank() == 1 ) { + return exts.extent(0); + } else { + index_type value = 1; + for (rank_type r = 0; r < extent_to_pad_idx; ++r) + { + value *= exts.extent(r); + } + return value * padded_stride.value(0); + } + } + + /** + * Return the mapping given the provided indices per rank. + * + * This overload participates in overload resolution only if: + * - `sizeof...(Indices) == extents_type::rank()`, + * - `(is_convertible_v && ...) is true`, and + * - (is_nothrow_constructible_v && ...) is true. + */ + MDSPAN_TEMPLATE_REQUIRES( + class... _Indices, + /* requires */ ( + sizeof...(_Indices) == extents_type::rank() + && (std::is_convertible_v<_Indices, index_type> && ...) + && (std::is_nothrow_constructible_v && ...) + ) + ) + constexpr size_t operator()(_Indices... idxs) const noexcept + { + return compute_offset(std::index_sequence_for<_Indices...>{}, idxs...); + } + + static constexpr bool is_always_unique() noexcept { return true; } + static constexpr bool is_always_exhaustive() noexcept + { + return (extents_type::rank() <= rank_type(1)) + || (extents_type::static_extent(extent_to_pad_idx) != dynamic_extent + && extents_type::static_extent(extent_to_pad_idx) == padded_stride_type::static_value()); + } + static constexpr bool is_always_strided() noexcept { return true; } + + static constexpr bool is_unique() noexcept { return true; } + constexpr bool is_exhaustive() const noexcept + { + return (extents_type::rank() < 2) + || (exts.extent(extent_to_pad_idx) == padded_stride.value(0)); + } + static constexpr bool is_strided() noexcept { return true; } + + constexpr index_type stride(rank_type r) const noexcept + { + assert(r < extents_type::rank()); + if(r == extents_type::rank() - 1) return index_type(1); + + index_type value = padded_stride.value(0); + for (rank_type k = extents_type::rank() - 2; k > r; k--) value *= exts.extent(k); + + return value; + } + + /** + * Equality operator between `layout_right_padded`s + * + * This overload only participates in overload resolution if `OtherExtents::rank() == extents_type::rank()`. + * + * \note There is currently a difference from p2642r2, where this function is specified as taking + * `layout_right_padded< padding_value >::mapping< Extents>`. However, this makes `padding_value` non-deducible. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + detail::is_layout_right_padded_mapping<_Mapping>::value + && (_Mapping::extents_type::rank() == extents_type::rank()) + ) + ) + friend constexpr bool operator==(const mapping &left, const _Mapping &right) noexcept + { + // Workaround for some compilers not short-circuiting properly with compile-time checks + // i.e. we can't access stride(_padding_stride_idx) of a rank 0 mapping + bool strides_equal = true; + if constexpr (extents_type::rank() > rank_type(1)) + { + strides_equal = left.stride(padded_stride_idx) == right.stride(padded_stride_idx); + } + return (left.extents() == right.extents()) && strides_equal; + } + +#if !MDSPAN_HAS_CXX_20 + /** + * Inequality operator between `layout_right_padded`s + * + * This overload only participates in overload resolution if `OtherExtents::rank() == extents_type::rank()`. + */ + MDSPAN_TEMPLATE_REQUIRES( + class _Mapping, + /* requires */ ( + detail::is_layout_right_padded_mapping<_Mapping>::value + && (_Mapping::extents_type::rank() == extents_type::rank()) + ) + ) + friend constexpr bool operator!=(const mapping &left, const _Mapping &right) noexcept + { + return !(left == right); + } +#endif +}; +} +} diff --git a/include/experimental/__p2642_bits/layout_padded_fwd.hpp b/include/experimental/__p2642_bits/layout_padded_fwd.hpp new file mode 100644 index 00000000..e44c1d13 --- /dev/null +++ b/include/experimental/__p2642_bits/layout_padded_fwd.hpp @@ -0,0 +1,117 @@ +//@HEADER +// ************************************************************************ +// +// Kokkos v. 4.0 +// Copyright (2022) National Technology & Engineering +// Solutions of Sandia, LLC (NTESS). +// +// Under the terms of Contract DE-NA0003525 with NTESS, +// the U.S. Government retains certain rights in this software. +// +// Part of Kokkos, under the Apache License v2.0 with LLVM Exceptions. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//@HEADER +#pragma once + +#include +#include "../__p0009_bits/dynamic_extent.hpp" + +namespace MDSPAN_IMPL_STANDARD_NAMESPACE { +namespace MDSPAN_IMPL_PROPOSED_NAMESPACE { + +template +struct layout_left_padded { + template + class mapping; +}; + +template +struct layout_right_padded { + template + class mapping; +}; + +namespace detail { +// The layout_padded_constants structs are only useful if rank > 1, otherwise they may wrap +template +struct layout_padded_constants; + +template +struct layout_padded_constants, _ExtentsType> +{ + using rank_type = typename _ExtentsType::rank_type; + static constexpr rank_type padded_stride_idx = 1; + static constexpr rank_type extent_to_pad_idx = 0; +}; + +template +struct layout_padded_constants, _ExtentsType> +{ + using rank_type = typename _ExtentsType::rank_type; + static constexpr rank_type padded_stride_idx = _ExtentsType::rank() - 2; + static constexpr rank_type extent_to_pad_idx = _ExtentsType::rank() - 1; +}; + +template +struct is_layout_left_padded : std::false_type {}; + +template +struct is_layout_left_padded> : std::true_type {}; + +template +struct is_layout_left_padded_mapping : std::false_type {}; + +template +struct is_layout_left_padded_mapping<_Mapping, + std::enable_if_t::template mapping>>> + : std::true_type {}; + +template +struct is_layout_right_padded : std::false_type {}; + +template +struct is_layout_right_padded> : std::true_type {}; + +template +struct is_layout_right_padded_mapping : std::false_type {}; + +template +struct is_layout_right_padded_mapping<_Mapping, + std::enable_if_t::template mapping>>> + : std::true_type {}; + +template +constexpr void check_padded_layout_converting_constructor_mandates() +{ + if constexpr (_LayoutExtentsType::rank() > 1) { + using extents_type = typename _PaddedLayoutMappingType::extents_type; + constexpr auto padding_value = _PaddedLayoutMappingType::padding_value; + constexpr auto idx = layout_padded_constants::extent_to_pad_idx; + if constexpr ((_LayoutExtentsType::static_extent(idx) != dynamic_extent) && + (extents_type::static_extent(idx) != dynamic_extent) && + (padding_value != dynamic_extent)) { + if constexpr (padding_value == 0) { + static_assert(_LayoutExtentsType::static_extent(idx) == 0); + } else { + static_assert( + _LayoutExtentsType::static_extent(idx) % padding_value == 0); + } + } + } +} + +template +constexpr void check_padded_layout_converting_constructor_preconditions([[maybe_unused]] const _OtherMapping &other_mapping) { + if constexpr (_ExtentsType::rank() > 1) { + constexpr auto padded_stride_idx = + layout_padded_constants::padded_stride_idx; + constexpr auto extent_to_pad_idx = layout_padded_constants::extent_to_pad_idx; + assert(other_mapping.stride(padded_stride_idx) == other_mapping.extents().extent(extent_to_pad_idx)); + } +} +} +} +} diff --git a/include/mdspan/mdspan.hpp b/include/mdspan/mdspan.hpp index b4408735..ac72a1a4 100644 --- a/include/mdspan/mdspan.hpp +++ b/include/mdspan/mdspan.hpp @@ -35,6 +35,7 @@ #include "../experimental/__p0009_bits/layout_right.hpp" #include "../experimental/__p0009_bits/macros.hpp" #if MDSPAN_HAS_CXX_17 +#include "../experimental/__p2642_bits/layout_padded.hpp" #include "../experimental/__p2630_bits/submdspan.hpp" #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 61b663f1..21144d48 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -60,6 +60,8 @@ mdspan_add_test(test_layout_stride) if(NOT CMAKE_CXX_STANDARD STREQUAL "14") mdspan_add_test(test_submdspan) mdspan_add_test(test_submdspan_static_slice) +mdspan_add_test(test_layout_padded_left) +mdspan_add_test(test_layout_padded_right) endif() # both of those don't work yet since its using vector if(NOT MDSPAN_ENABLE_CUDA AND NOT MDSPAN_ENABLE_HIP) diff --git a/tests/test_layout_padded_left.cpp b/tests/test_layout_padded_left.cpp new file mode 100644 index 00000000..c30be2b1 --- /dev/null +++ b/tests/test_layout_padded_left.cpp @@ -0,0 +1,478 @@ +#define MDSPAN_INTERNAL_TEST +#define _MDSPAN_DEBUG +#include + +#include +#include + +namespace KokkosEx = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; + +// Compile time tests + +// For internal traits +struct fake_mapping { + using layout_type = Kokkos::Experimental::layout_left_padded<5>; +}; + +static_assert(!Kokkos::Experimental::detail::is_layout_left_padded_mapping< + fake_mapping>::value); + +static_assert(Kokkos::Experimental::detail::is_layout_left_padded_mapping< + Kokkos::Experimental::layout_left_padded<4>::mapping< + Kokkos::extents>>::value); + +// layout_left_padded must be trivial +static_assert(std::is_trivial_v>); +static_assert(std::is_trivial_v>); +static_assert(std::is_trivial_v>); + +// actual padding stride +// If extents_type::rank() equals zero or one, then 0. +static_assert(KokkosEx::layout_left_padded<0>::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_left_padded<2>::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_left_padded<2>::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_left_padded<2>::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_left_padded::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_left_padded::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_left_padded::mapping>::static_padding_stride == 0); + +// Else, if +// - padding_stride does not equal dynamic_extent and +// - extents_type::static_extent(0) does not equal dynamic_extent, +// then the size_t value which is the least multiple of padding_stride that is greater than or equal to extents_type::static_extent(0). +static_assert(KokkosEx::layout_left_padded<2>::mapping>::static_padding_stride == 4); +static_assert(KokkosEx::layout_left_padded<2>::mapping>::static_padding_stride == 4); + +// Otherwise, dynamic_extent. +static_assert(KokkosEx::layout_left_padded<2>::mapping>::static_padding_stride == Kokkos::dynamic_extent); +static_assert(KokkosEx::layout_left_padded::mapping>::static_padding_stride == Kokkos::dynamic_extent); +static_assert(KokkosEx::layout_left_padded::mapping>::static_padding_stride == Kokkos::dynamic_extent); + +namespace +{ +template +void test_padding_stride(const Extents &extents, const TestExtents &test_extents) +{ + auto mapping = typename LayoutLeftPadded::template mapping(extents); + if constexpr (TestExtents::rank() > 1) { + ASSERT_EQ(mapping.padded_stride.value(0), test_extents.extent(decltype(mapping)::extent_to_pad_idx)); + } else { + ASSERT_EQ(mapping.padded_stride.value(0), 0); + } + + auto strs = mapping.strides(); + size_t prod = 1; + for (typename decltype(mapping)::rank_type r = 0; r < TestExtents::rank(); ++r) + { + ASSERT_EQ(strs[r], prod); + prod *= test_extents.extent(r); + } + + ASSERT_EQ(prod, mapping.required_span_size()); +} + +template +void test_padding_stride(const Extents &extents, const TestExtents &test_extents, Size padding_value) +{ + auto mapping = typename LayoutLeftPadded::template mapping(extents, padding_value); + if constexpr (TestExtents::rank() > 1) { + ASSERT_EQ(mapping.padded_stride.value(0), test_extents.extent(decltype(mapping)::extent_to_pad_idx)); + } else { + ASSERT_EQ(mapping.padded_stride.value(0), 0); + } + + auto strs = mapping.strides(); + size_t prod = 1; + for (typename decltype(mapping)::rank_type r = 0; r < TestExtents::rank(); ++r) + { + ASSERT_EQ(strs[r], prod); + prod *= test_extents.extent(r); + } + + ASSERT_EQ(prod, mapping.required_span_size()); +} + +template +void test_0_or_1_rank_padding_stride(const Extents &extents) +{ + test_padding_stride(extents, extents); +} + +template +void test_0_or_1_rank_padding_stride(const Extents &extents, Size padding_value) +{ + test_padding_stride(extents, extents, padding_value); +} + +template +void test_default_constructor_equivalence() +{ + using mapping_type = typename LayoutLeftPadded::template mapping; + + ASSERT_EQ(mapping_type(), mapping_type(Extents{})); +} + +template +void test_copy_constructor(const Extents &extents) +{ + using mapping_type = typename LayoutLeftPadded::template mapping; + auto a = mapping_type(extents); + auto b = a; + + ASSERT_EQ(a, b); +} + +template +void test_copy_constructor(const Extents &extents, Size padding_value) +{ + using mapping_type = typename LayoutLeftPadded::template mapping; + auto a = mapping_type(extents, padding_value); + auto b = a; + + ASSERT_EQ(a, b); +} + +template +void test_copy_assignment(const Extents &extents) +{ + using mapping_type = typename LayoutLeftPadded::template mapping; + auto a = mapping_type(extents); + mapping_type b; + + b = a; + + ASSERT_EQ(a, b); +} + +template +void test_copy_assignment(const Extents &extents, Size padding_value) +{ + using mapping_type = typename LayoutLeftPadded::template mapping; + auto a = mapping_type(extents, padding_value); + mapping_type b; + + b = a; + + ASSERT_EQ(a, b); +} +} + +TEST(LayoutLeftTests, construction) +{ + // Default Constructor + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + + // Copy constructor + test_copy_constructor>(Kokkos::extents()); + test_copy_constructor>(Kokkos::extents()); + test_copy_constructor>(Kokkos::extents()); + test_copy_constructor>(Kokkos::extents()); + test_copy_constructor>(Kokkos::extents(10)); + test_copy_constructor>(Kokkos::extents(10, 9)); + test_copy_constructor>(Kokkos::extents(10, 9, 8)); + + test_copy_constructor>(Kokkos::extents(), 5); + test_copy_constructor>(Kokkos::extents(), 5); + test_copy_constructor>(Kokkos::extents(), 5); + test_copy_constructor>(Kokkos::extents(), 5); + + // Copy assignment + test_copy_assignment>(Kokkos::extents()); + test_copy_assignment>(Kokkos::extents()); + test_copy_assignment>(Kokkos::extents()); + test_copy_assignment>(Kokkos::extents()); + test_copy_assignment>(Kokkos::extents(10)); + test_copy_assignment>(Kokkos::extents(10, 9)); + test_copy_assignment>(Kokkos::extents(10, 9, 8)); + + test_copy_assignment>(Kokkos::extents(), 5); + test_copy_assignment>(Kokkos::extents(), 5); + test_copy_assignment>(Kokkos::extents(), 5); + test_copy_assignment>(Kokkos::extents(), 5); + + // Constructor only taking an extent + // Direct-non-list-initializes inner-mapping with: + // - ext, if extents_type::rank() is zero or one; else, + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{7}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{7}); + + // - ext.extent(0), ext.extent(P_left)..., if padding_stride is dynamic_extent + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{0}); + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{5}); + test_padding_stride>(Kokkos::extents{7}, Kokkos::extents{7}); + + // - S_left, ext.extent(P_left)..., where S_left is the least multiple of padding_stride greater than or equal to ext.extent(0) + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{}); + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{}); + test_padding_stride>(Kokkos::extents{7}, Kokkos::extents{8}); + + // Constructor taking an extent and a dynamic value + // Direct-non-list-initializes inner-mapping with: + // - ext, if extents_type::rank() is zero or one; else, + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 4); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 0); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 4); + test_0_or_1_rank_padding_stride>(Kokkos::extents{7}, 4); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 3); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 3255); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 1337); + test_0_or_1_rank_padding_stride>(Kokkos::extents{7}, 6323); + + // - S_left, ext.extent(P_left)..., where S_left is the least multiple of padding_value greater than or equal to ext.extent(0) + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{}, 0); + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{}, 4); + test_padding_stride>(Kokkos::extents{7}, Kokkos::extents{8}, 4); + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{0}, 2); + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{8}, 4); + test_padding_stride>(Kokkos::extents{7}, Kokkos::extents{8}, 4); + + // Construct layout_left_padded mapping from layout_left mapping + ASSERT_EQ(KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_left::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_left::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_left::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_left::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_left::mapping>()).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_left::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_left::mapping>()).padded_stride.value(0)), 4); + + ASSERT_EQ(KokkosEx::layout_left_padded::mapping>(Kokkos::layout_left::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_left::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_left::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_left::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_left::mapping>()).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_left::mapping>(Kokkos::extents{4})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_left::mapping>(Kokkos::extents{4})).padded_stride.value(0)), 4); + + // Construct layout_left_padded mapping from layout stride + ASSERT_EQ(KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_stride::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1})).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 4})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 4})).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 4})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 4})).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 8})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 8})).padded_stride.value(0)), 8); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 8})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 8})).padded_stride.value(0)), 8); + + ASSERT_EQ(KokkosEx::layout_left_padded::mapping>(Kokkos::layout_stride::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1})).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 4})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 4})).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 4})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 4})).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 8})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 8})).padded_stride.value(0)), 8); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 8})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1, 8})).padded_stride.value(0)), 8); + + // Construct layout_left_padded mapping from another layout_left_padded mapping + ASSERT_EQ(KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).padded_stride.value(0)), 4); + + ASSERT_EQ(KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).padded_stride.value(0)), 8); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).padded_stride.value(0)), 8); + + // Construct layout_left_padded mapping from layout_right_padded mapping + ASSERT_EQ(KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_right_padded::mapping>({}, 4)).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>(KokkosEx::layout_right_padded::mapping>({}, 4)).padded_stride.value(0)), 0); + + ASSERT_EQ(KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_right_padded::mapping>({}, 4)).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>(KokkosEx::layout_right_padded::mapping>({}, 4)).padded_stride.value(0)), 0); + + // Construct layout_left mapping from layout_left_padded mapping + ASSERT_EQ(Kokkos::layout_left::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((Kokkos::layout_left::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((Kokkos::layout_left::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((Kokkos::layout_left::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents()), (Kokkos::extents())); +} + +namespace +{ + template + void test_extent(const Extents &input_extents) + { + auto mapping = typename PaddedLayout::template mapping(input_extents); + ASSERT_EQ(mapping.extents(), input_extents); + } + + template + void test_extent(const Extents &input_extents, Size padding_value) + { + auto mapping = typename PaddedLayout::template mapping(input_extents, padding_value); + ASSERT_EQ(mapping.extents(), input_extents); + } +} + +TEST(LayoutLeftTests, extents) +{ + test_extent>(Kokkos::extents{}); + test_extent>(Kokkos::extents{}); + test_extent>(Kokkos::extents{}); + test_extent>(Kokkos::extents{7}); + test_extent>(Kokkos::extents{}); + test_extent>(Kokkos::extents{}); + test_extent>(Kokkos::extents{7}); + + test_extent>(Kokkos::extents{}, 0); + test_extent>(Kokkos::extents{}, 4); + test_extent>(Kokkos::extents{7}, 4); + test_extent>(Kokkos::extents{}, 1); + test_extent>(Kokkos::extents{}, 3); + test_extent>(Kokkos::extents{7}, 5); +} + +// is_always_exhaustive +static_assert(KokkosEx::layout_left_padded<4>::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_left_padded<4>::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{5}}.is_always_exhaustive()); +static_assert(KokkosEx::layout_left_padded::mapping>{}.is_always_exhaustive()); + +static_assert(!KokkosEx::layout_left_padded<4>::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_left_padded<4>::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_left_padded<4>::mapping>{}.is_always_exhaustive()); +static_assert(!KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{5}}.is_always_exhaustive()); +static_assert(!KokkosEx::layout_left_padded::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_left_padded<0>::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_left_padded<4>::mapping>{}.is_always_exhaustive()); + +TEST(LayoutLeftTests, properties) +{ + // is_exhaustive + // Sanity check -- if it is always exhaustive it should be exhaustive ^-^ + ASSERT_TRUE((KokkosEx::layout_left_padded<4>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_left_padded<4>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_left_padded<4>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_left_padded::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_left_padded<4>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_left_padded<4>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_left_padded<0>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_left_padded<4>::mapping>{}.is_exhaustive())); + + // is_exhaustive with dynamic values + ASSERT_TRUE((KokkosEx::layout_left_padded::mapping>{Kokkos::extents{}, 4}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{8}}.is_exhaustive())); + ASSERT_FALSE((KokkosEx::layout_left_padded::mapping>{Kokkos::extents{}, 4}.is_exhaustive())); + ASSERT_FALSE((KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{7}}.is_exhaustive())); + + // Equality + ASSERT_EQ((KokkosEx::layout_left_padded<0>::mapping>{}), (KokkosEx::layout_left_padded<0>::mapping>{})); + ASSERT_EQ((KokkosEx::layout_left_padded<0>::mapping>{}), (KokkosEx::layout_left_padded<4>::mapping>{})); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>{}), (KokkosEx::layout_left_padded<4>::mapping>{})); + ASSERT_NE((KokkosEx::layout_left_padded<4>::mapping>{}), (KokkosEx::layout_left_padded<4>::mapping>{})); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{5}}), + (KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{5}})); + ASSERT_NE((KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{3}}), + (KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{5}})); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>{}), (KokkosEx::layout_left_padded<4>::mapping>{})); + ASSERT_NE((KokkosEx::layout_left_padded<4>::mapping>{}), (KokkosEx::layout_left_padded<4>::mapping>{})); + ASSERT_NE((KokkosEx::layout_left_padded<4>::mapping>{}), (KokkosEx::layout_left_padded<8>::mapping>{})); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{5}}), + (KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{5}})); + ASSERT_NE((KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{3}}), + (KokkosEx::layout_left_padded<4>::mapping>{Kokkos::extents{5}})); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>{{}, 4}), (KokkosEx::layout_left_padded<4>::mapping>{})); + ASSERT_EQ((KokkosEx::layout_left_padded::mapping>{{}, 4}), (KokkosEx::layout_left_padded::mapping>{{}, 4})); + ASSERT_NE((KokkosEx::layout_left_padded::mapping>{{}, 4}), (KokkosEx::layout_left_padded<4>::mapping>{})); + ASSERT_NE((KokkosEx::layout_left_padded::mapping>{{}, 4}), (KokkosEx::layout_left_padded::mapping>{{}, 8})); +} + +TEST(LayoutLeftTests, stride) +{ + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>().stride(0)), 1); + ASSERT_EQ((KokkosEx::layout_left_padded<4>::mapping>().stride(1)), 8); +} + +TEST(LayoutRightTests, access) { + auto mapping1 = KokkosEx::layout_left_padded<4>::mapping< + Kokkos::extents>(); + ASSERT_EQ(mapping1(0, 0), 0); + ASSERT_EQ(mapping1(1, 0), 1); + ASSERT_EQ(mapping1(4, 0), 4); + ASSERT_EQ(mapping1(0, 1), 8); + ASSERT_EQ(mapping1(4, 1), 12); + ASSERT_EQ(mapping1(0, 6), 48); + ASSERT_EQ(mapping1(4, 6), 52); + + auto mapping2 = KokkosEx::layout_left_padded::mapping< + Kokkos::extents>({}, 6); + ASSERT_EQ(mapping2(0, 0), 0); + ASSERT_EQ(mapping2(1, 0), 1); + ASSERT_EQ(mapping2(4, 0), 4); + ASSERT_EQ(mapping2(0, 1), 6); + ASSERT_EQ(mapping2(4, 1), 10); + ASSERT_EQ(mapping2(0, 6), 36); + ASSERT_EQ(mapping2(4, 6), 40); + + auto mapping3 = KokkosEx::layout_left_padded<2>::mapping< + Kokkos::extents>( + Kokkos::extents{3}); + ASSERT_EQ(mapping3(0, 0), 0); + ASSERT_EQ(mapping3(1, 0), 1); + ASSERT_EQ(mapping3(2, 0), 2); + ASSERT_EQ(mapping3(0, 1), 4); + ASSERT_EQ(mapping3(2, 1), 6); + ASSERT_EQ(mapping3(0, 6), 24); + ASSERT_EQ(mapping3(2, 6), 26); + + auto mapping4 = KokkosEx::layout_left_padded::mapping< + Kokkos::extents>( + Kokkos::extents{7}, 10); + ASSERT_EQ(mapping4(0, 0), 0); + ASSERT_EQ(mapping4(1, 0), 1); + ASSERT_EQ(mapping4(6, 0), 6); + ASSERT_EQ(mapping4(0, 1), 10); + ASSERT_EQ(mapping4(6, 1), 16); + ASSERT_EQ(mapping4(0, 6), 60); + ASSERT_EQ(mapping4(6, 6), 66); + + auto mapping5 = + KokkosEx::layout_left_padded::mapping< + Kokkos::extents>({}, 4); + ASSERT_EQ(mapping5(0), 0); + ASSERT_EQ(mapping5(1), 1); + ASSERT_EQ(mapping5(2), 2); + ASSERT_EQ(mapping5(3), 3); + ASSERT_EQ(mapping5(4), 4); + ASSERT_EQ(mapping5(5), 5); + ASSERT_EQ(mapping5(6), 6); + + auto mapping6 = + KokkosEx::layout_left_padded::mapping< + Kokkos::extents>({}, 4); + ASSERT_EQ(mapping6(), 0); +} diff --git a/tests/test_layout_padded_right.cpp b/tests/test_layout_padded_right.cpp new file mode 100644 index 00000000..8e567f43 --- /dev/null +++ b/tests/test_layout_padded_right.cpp @@ -0,0 +1,482 @@ +#define MDSPAN_INTERNAL_TEST +#define _MDSPAN_DEBUG +#include + +#include +#include + +namespace KokkosEx = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE; + +// Compile time tests + +// For internal traits +struct fake_mapping { + using layout_type = Kokkos::Experimental::layout_right_padded<5>; +}; + +static_assert(!Kokkos::Experimental::detail::is_layout_right_padded_mapping< + fake_mapping>::value); + +static_assert(Kokkos::Experimental::detail::is_layout_right_padded_mapping< + Kokkos::Experimental::layout_right_padded<4>::mapping< + Kokkos::extents>>::value); + +// layout_left_padded must be trivial +static_assert(std::is_trivial_v>); +static_assert(std::is_trivial_v>); +static_assert(std::is_trivial_v>); + +// actual padding stride +// If extents_type::rank() equals zero or one, then padding_stride. +static_assert(KokkosEx::layout_right_padded<0>::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_right_padded<2>::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_right_padded<2>::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_right_padded<2>::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_right_padded::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_right_padded::mapping>::static_padding_stride == 0); +static_assert(KokkosEx::layout_right_padded::mapping>::static_padding_stride == 0); + +// Else, if +// - padding_stride does not equal dynamic_extent and +// - extents_type::static_extent(0) does not equal dynamic_extent, +// then the size_t value which is the least multiple of padding_stride that is greater than or equal to extents_type::static_extent(0). +static_assert(KokkosEx::layout_right_padded<2>::mapping>::static_padding_stride == 4); +static_assert(KokkosEx::layout_right_padded<2>::mapping>::static_padding_stride == 4); + +// Otherwise, dynamic_extent. +static_assert(KokkosEx::layout_right_padded<2>::mapping>::static_padding_stride == Kokkos::dynamic_extent); +static_assert(KokkosEx::layout_right_padded::mapping>::static_padding_stride == Kokkos::dynamic_extent); +static_assert(KokkosEx::layout_right_padded::mapping>::static_padding_stride == Kokkos::dynamic_extent); + +namespace +{ +template +void test_padding_stride(const Extents &extents, const TestExtents &test_extents) +{ + auto mapping = typename LayoutrightPadded::template mapping(extents); + if constexpr (TestExtents::rank() > 1) { + ASSERT_EQ(mapping.padded_stride.value(0), test_extents.extent(decltype(mapping)::extent_to_pad_idx)); + } else { + ASSERT_EQ(mapping.padded_stride.value(0), 0); + } + + auto strs = mapping.strides(); + size_t prod = 1; + for (typename decltype(mapping)::rank_type rrev = 0; rrev < TestExtents::rank(); ++rrev) + { + auto r = TestExtents::rank() - 1 - rrev; + ASSERT_EQ(strs[r], prod); + prod *= test_extents.extent(r); + } + + ASSERT_EQ(prod, mapping.required_span_size()); +} + +template +void test_padding_stride(const Extents &extents, const TestExtents &test_extents, Size padding_value) +{ + auto mapping = typename LayoutrightPadded::template mapping(extents, padding_value); + if constexpr (TestExtents::rank() > 1) { + ASSERT_EQ(mapping.padded_stride.value(0), test_extents.extent(decltype(mapping)::extent_to_pad_idx)); + } else { + ASSERT_EQ(mapping.padded_stride.value(0), 0); + } + + auto strs = mapping.strides(); + size_t prod = 1; + for (typename decltype(mapping)::rank_type rrev = 0; rrev < TestExtents::rank(); ++rrev) + { + auto r = TestExtents::rank() - 1 - rrev; + ASSERT_EQ(strs[r], prod); + prod *= test_extents.extent(r); + } + + ASSERT_EQ(prod, mapping.required_span_size()); +} + +template +void test_0_or_1_rank_padding_stride(const Extents &extents) +{ + test_padding_stride(extents, extents); +} + +template +void test_0_or_1_rank_padding_stride(const Extents &extents, Size padding_value) +{ + test_padding_stride(extents, extents, padding_value); +} + +template +void test_default_constructor_equivalence() +{ + using mapping_type = typename LayoutRightPadded::template mapping; + + ASSERT_EQ(mapping_type(), mapping_type(Extents{})); +} + +template +void test_copy_constructor(const Extents &extents) +{ + using mapping_type = typename LayoutRightPadded::template mapping; + auto a = mapping_type(extents); + auto b = a; + + ASSERT_EQ(a, b); +} + +template +void test_copy_constructor(const Extents &extents, Size padding_value) +{ + using mapping_type = typename LayoutRightPadded::template mapping; + auto a = mapping_type(extents, padding_value); + auto b = a; + + ASSERT_EQ(a, b); +} + +template +void test_copy_assignment(const Extents &extents) +{ + using mapping_type = typename LayoutRightPadded::template mapping; + auto a = mapping_type(extents); + mapping_type b; + + b = a; + + ASSERT_EQ(a, b); +} + +template +void test_copy_assignment(const Extents &extents, Size padding_value) +{ + using mapping_type = typename LayoutRightPadded::template mapping; + auto a = mapping_type(extents, padding_value); + mapping_type b; + + b = a; + + ASSERT_EQ(a, b); +} +} + +TEST(LayoutrightTests, construction) +{ + // Default Constructor + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + test_default_constructor_equivalence, Kokkos::extents>(); + + // Copy constructor + test_copy_constructor>(Kokkos::extents()); + test_copy_constructor>(Kokkos::extents()); + test_copy_constructor>(Kokkos::extents()); + test_copy_constructor>(Kokkos::extents()); + test_copy_constructor>(Kokkos::extents(10)); + test_copy_constructor>(Kokkos::extents(9, 10)); + test_copy_constructor>(Kokkos::extents(8, 9, 10)); + + test_copy_constructor>(Kokkos::extents(), 5); + test_copy_constructor>(Kokkos::extents(), 5); + test_copy_constructor>(Kokkos::extents(), 5); + test_copy_constructor>(Kokkos::extents(), 5); + + // Copy assignment + test_copy_assignment>(Kokkos::extents()); + test_copy_assignment>(Kokkos::extents()); + test_copy_assignment>(Kokkos::extents()); + test_copy_assignment>(Kokkos::extents()); + test_copy_assignment>(Kokkos::extents(10)); + test_copy_assignment>(Kokkos::extents(9, 10)); + test_copy_assignment>(Kokkos::extents(8, 9, 10)); + + test_copy_assignment>(Kokkos::extents(), 5); + test_copy_assignment>(Kokkos::extents(), 5); + test_copy_assignment>(Kokkos::extents(), 5); + test_copy_assignment>(Kokkos::extents(), 5); + + // Constructor only taking an extent + // Direct-non-list-initializes inner-mapping with: + // - ext, if extents_type::rank() is zero or one; else, + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{7}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}); + test_0_or_1_rank_padding_stride>(Kokkos::extents{7}); + + // - ext.extent(0), ext.extent(P_right)..., if padding_stride is dynamic_extent + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{ 0 }); + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{ 5 }); + test_padding_stride>(Kokkos::extents{7}, Kokkos::extents{7}); + + // - S_right, ext.extent(P_right)..., where S_right is the least multiple of padding_stride greater than or equal to ext.extent(0) + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{}); + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{}); + test_padding_stride>(Kokkos::extents{7}, Kokkos::extents{8}); + + // Constructor taking an extent and a dynamic value + // Direct-non-list-initializes inner-mapping with: + // - ext, if extents_type::rank() is zero or one; else, + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 4); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 0); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 4); + test_0_or_1_rank_padding_stride>(Kokkos::extents{7}, 4); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 3); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 3255); + test_0_or_1_rank_padding_stride>(Kokkos::extents{}, 1337); + test_0_or_1_rank_padding_stride>(Kokkos::extents{7}, 6323); + + // - S_right, ext.extent(P_right)..., where S_right is the least multiple of padding_value greater than or equal to ext.extent(0) + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{}, 0); + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{}, 4); + test_padding_stride>(Kokkos::extents{7}, Kokkos::extents{8}, 4); + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{0}, 2); + test_padding_stride>(Kokkos::extents{}, Kokkos::extents{8}, 4); + test_padding_stride>(Kokkos::extents{7}, Kokkos::extents{8}, 4); + + // Construct layout_right_padded mapping from layout_right mapping + ASSERT_EQ(KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_right::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_right::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_right::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_right::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_right::mapping>()).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_right::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_right::mapping>()).padded_stride.value(0)), 4); + + ASSERT_EQ(KokkosEx::layout_right_padded::mapping>(Kokkos::layout_right::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_right::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_right::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_right::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_right::mapping>()).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_right::mapping>(Kokkos::extents{4})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_right::mapping>(Kokkos::extents{4})).padded_stride.value(0)), 4); + + // Construct layout_right_padded mapping from layout stride + ASSERT_EQ(KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_stride::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1})).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{4, 1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{4, 1})).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{4, 1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{4, 1})).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{8, 1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{8, 1})).padded_stride.value(0)), 8); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{8, 1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(Kokkos::layout_stride::mapping>({}, std::array{8, 1})).padded_stride.value(0)), 8); + + ASSERT_EQ(KokkosEx::layout_right_padded::mapping>(Kokkos::layout_stride::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{1})).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{4, 1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{4, 1})).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{4, 1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{4, 1})).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{8, 1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{8, 1})).padded_stride.value(0)), 8); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{8, 1})).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(Kokkos::layout_stride::mapping>({}, std::array{8, 1})).padded_stride.value(0)), 8); + + // Construct layout_right_padded from another layout_right_padded + ASSERT_EQ(KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).padded_stride.value(0)), 4); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).padded_stride.value(0)), 4); + + ASSERT_EQ(KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).padded_stride.value(0)), 8); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).padded_stride.value(0)), 8); + + // Construct layout_right_padded mapping from layout_left_padded mapping + ASSERT_EQ(KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_left_padded::mapping>({}, 4)).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>(KokkosEx::layout_left_padded::mapping>({}, 4)).padded_stride.value(0)), 0); + + ASSERT_EQ(KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_left_padded<4>::mapping>()).padded_stride.value(0)), 0); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_left_padded::mapping>({}, 4)).extents()), (Kokkos::extents())); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>(KokkosEx::layout_left_padded::mapping>({}, 4)).padded_stride.value(0)), 0); + + // Construct layout_right mapping from layout_right_padded mapping + ASSERT_EQ(Kokkos::layout_right::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents(), Kokkos::extents()); + ASSERT_EQ((Kokkos::layout_right::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((Kokkos::layout_right::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents()), (Kokkos::extents())); + ASSERT_EQ((Kokkos::layout_right::mapping>(KokkosEx::layout_right_padded<4>::mapping>()).extents()), (Kokkos::extents())); +} + +namespace +{ + template + void test_extent(const Extents &input_extents) + { + auto mapping = typename PaddedLayout::template mapping(input_extents); + ASSERT_EQ(mapping.extents(), input_extents); + } + + template + void test_extent(const Extents &input_extents, Size padding_value) + { + auto mapping = typename PaddedLayout::template mapping(input_extents, padding_value); + ASSERT_EQ(mapping.extents(), input_extents); + } +} + +TEST(LayoutrightTests, extents) +{ + test_extent>(Kokkos::extents{}); + test_extent>(Kokkos::extents{}); + test_extent>(Kokkos::extents{}); + test_extent>(Kokkos::extents{7}); + test_extent>(Kokkos::extents{}); + test_extent>(Kokkos::extents{}); + test_extent>(Kokkos::extents{7}); + + test_extent>(Kokkos::extents{}, 0); + test_extent>(Kokkos::extents{}, 4); + test_extent>(Kokkos::extents{7}, 4); + test_extent>(Kokkos::extents{}, 1); + test_extent>(Kokkos::extents{}, 3); + test_extent>(Kokkos::extents{7}, 5); +} + +// is_always_exhaustive +static_assert(KokkosEx::layout_right_padded<4>::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_right_padded<4>::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{5}}.is_always_exhaustive()); +static_assert(KokkosEx::layout_right_padded::mapping>{}.is_always_exhaustive()); + +static_assert(!KokkosEx::layout_right_padded<4>::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_right_padded<4>::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_right_padded<4>::mapping>{}.is_always_exhaustive()); +static_assert(!KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{5}}.is_always_exhaustive()); +static_assert(!KokkosEx::layout_right_padded::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_right_padded<0>::mapping>{}.is_always_exhaustive()); +static_assert(KokkosEx::layout_right_padded<4>::mapping>{}.is_always_exhaustive()); + +TEST(LayoutrightTests, properties) +{ + // is_exhaustive + // Sanity check -- if it is always exhaustive it should be exhaustive ^-^ + ASSERT_TRUE((KokkosEx::layout_right_padded<4>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_right_padded<4>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_right_padded<4>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_right_padded::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_right_padded<4>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_right_padded<4>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_right_padded<0>::mapping>{}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_right_padded<4>::mapping>{}.is_exhaustive())); + ASSERT_FALSE((KokkosEx::layout_right_padded<4>::mapping>{}.is_exhaustive())); + + // is_exhaustive with dynamic values + ASSERT_TRUE((KokkosEx::layout_right_padded::mapping>{Kokkos::extents{}, 4}.is_exhaustive())); + ASSERT_TRUE((KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{8}}.is_exhaustive())); + ASSERT_FALSE((KokkosEx::layout_right_padded::mapping>{Kokkos::extents{}, 4}.is_exhaustive())); + ASSERT_FALSE((KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{7}}.is_exhaustive())); + + // Equality + ASSERT_EQ((KokkosEx::layout_right_padded<0>::mapping>{}), (KokkosEx::layout_right_padded<0>::mapping>{})); + ASSERT_EQ((KokkosEx::layout_right_padded<0>::mapping>{}), (KokkosEx::layout_right_padded<4>::mapping>{})); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>{}), (KokkosEx::layout_right_padded<4>::mapping>{})); + ASSERT_NE((KokkosEx::layout_right_padded<4>::mapping>{}), (KokkosEx::layout_right_padded<4>::mapping>{})); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{5}}), + (KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{5}})); + ASSERT_NE((KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{3}}), + (KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{5}})); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>{}), (KokkosEx::layout_right_padded<4>::mapping>{})); + ASSERT_NE((KokkosEx::layout_right_padded<4>::mapping>{}), (KokkosEx::layout_right_padded<4>::mapping>{})); + ASSERT_NE((KokkosEx::layout_right_padded<4>::mapping>{}), (KokkosEx::layout_right_padded<8>::mapping>{})); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{5}}), + (KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{5}})); + ASSERT_NE((KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{3}}), + (KokkosEx::layout_right_padded<4>::mapping>{Kokkos::extents{5}})); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>{{}, 4}), (KokkosEx::layout_right_padded<4>::mapping>{})); + ASSERT_EQ((KokkosEx::layout_right_padded::mapping>{{}, 4}), (KokkosEx::layout_right_padded::mapping>{{}, 4})); + ASSERT_NE((KokkosEx::layout_right_padded::mapping>{{}, 4}), (KokkosEx::layout_right_padded<4>::mapping>{})); + ASSERT_NE((KokkosEx::layout_right_padded::mapping>{{}, 4}), (KokkosEx::layout_right_padded::mapping>{{}, 8})); +} + +TEST(LayoutRightTests, stride) +{ + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>().stride(0)), 8); + ASSERT_EQ((KokkosEx::layout_right_padded<4>::mapping>().stride(1)), 1); +} + +TEST(LayoutRightTests, access) +{ + auto mapping1 = KokkosEx::layout_right_padded<4>::mapping>(); + ASSERT_EQ(mapping1(0, 0), 0); + ASSERT_EQ(mapping1(0, 1), 1); + ASSERT_EQ(mapping1(0, 4), 4); + ASSERT_EQ(mapping1(1, 0), 8); + ASSERT_EQ(mapping1(1, 4), 12); + ASSERT_EQ(mapping1(6, 0), 48); + ASSERT_EQ(mapping1(6, 4), 52); + + auto mapping2 = KokkosEx::layout_right_padded::mapping< + Kokkos::extents>({}, 6); + ASSERT_EQ(mapping2(0, 0), 0); + ASSERT_EQ(mapping2(0, 1), 1); + ASSERT_EQ(mapping2(0, 4), 4); + ASSERT_EQ(mapping2(1, 0), 6); + ASSERT_EQ(mapping2(1, 4), 10); + ASSERT_EQ(mapping2(6, 0), 36); + ASSERT_EQ(mapping2(6, 4), 40); + + auto mapping3 = KokkosEx::layout_right_padded<2>::mapping< + Kokkos::extents>( + Kokkos::extents{3}); + ASSERT_EQ(mapping3(0, 0), 0); + ASSERT_EQ(mapping3(0, 1), 1); + ASSERT_EQ(mapping3(0, 2), 2); + ASSERT_EQ(mapping3(1, 0), 4); + ASSERT_EQ(mapping3(1, 2), 6); + ASSERT_EQ(mapping3(6, 0), 24); + ASSERT_EQ(mapping3(6, 2), 26); + + auto mapping4 = + KokkosEx::layout_right_padded::mapping< + Kokkos::extents>( + Kokkos::extents{7}, 10); + ASSERT_EQ(mapping4(0, 0), 0); + ASSERT_EQ(mapping4(0, 1), 1); + ASSERT_EQ(mapping4(0, 6), 6); + ASSERT_EQ(mapping4(1, 0), 10); + ASSERT_EQ(mapping4(1, 6), 16); + ASSERT_EQ(mapping4(6, 0), 60); + ASSERT_EQ(mapping4(6, 6), 66); + + auto mapping5 = + KokkosEx::layout_right_padded::mapping< + Kokkos::extents>({}, 4); + ASSERT_EQ(mapping5(0), 0); + ASSERT_EQ(mapping5(1), 1); + ASSERT_EQ(mapping5(2), 2); + ASSERT_EQ(mapping5(3), 3); + ASSERT_EQ(mapping5(4), 4); + ASSERT_EQ(mapping5(5), 5); + ASSERT_EQ(mapping5(6), 6); + + auto mapping6 = + KokkosEx::layout_right_padded::mapping< + Kokkos::extents>({}, 4); + ASSERT_EQ(mapping6(), 0); +}