10#ifndef XTENSOR_FIXED_HPP
11#define XTENSOR_FIXED_HPP
19#include <xtl/xsequence.hpp>
21#include "xcontainer.hpp"
22#include "xsemantic.hpp"
23#include "xstorage.hpp"
24#include "xstrides.hpp"
25#include "xtensor_config.hpp"
31 template <
class T, std::
size_t N>
32 struct sequence_builder<xt::const_array<T, N>>
34 using sequence_type = xt::const_array<T, N>;
35 using value_type =
typename sequence_type::value_type;
36 using size_type =
typename sequence_type::size_type;
38 inline static sequence_type make(size_type , value_type )
40 return sequence_type();
53 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
96 template <
layout_type L, std::size_t I, std::size_t... X>
97 struct calculate_stride;
99 template <std::size_t I, std::size_t Y, std::size_t... X>
102 static constexpr std::ptrdiff_t value = Y
106 template <std::size_t Y, std::size_t... X>
109 static constexpr std::ptrdiff_t value = 1;
112 template <std::size_t I, std::size_t... X>
113 struct calculate_stride_row_major
115 static constexpr std::ptrdiff_t value = at<
sizeof...(X) - I, X...>::value
116 * calculate_stride_row_major<I - 1, X...>::value;
119 template <std::size_t... X>
120 struct calculate_stride_row_major<0, X...>
122 static constexpr std::ptrdiff_t value = 1;
125 template <std::size_t I, std::size_t... X>
128 static constexpr std::ptrdiff_t value = calculate_stride_row_major<
sizeof...(X) - I - 1, X...>::value;
133 template <layout_type L,
size_t I,
class SEQ>
134 struct computed_strides;
137 struct computed_strides<L, I, std::index_sequence<X...>>
139 static constexpr std::ptrdiff_t value = calculate_stride<L, I, X...>::value;
142 template <layout_type L,
size_t I,
class SEQ>
143 constexpr std::ptrdiff_t get_computed_strides(
bool cond)
145 return cond ? 0 : computed_strides<L, I, SEQ>::value;
149 template <
layout_type L,
class R, std::size_t... X, std::size_t... I>
150 constexpr R get_strides_impl(
const xt::fixed_shape<X...>& shape, std::index_sequence<I...>)
154 "Layout not supported for fixed array"
156#if (_MSC_VER >= 1910)
157 using temp_type = std::index_sequence<X...>;
158 return R({workaround::get_computed_strides<L, I, temp_type>(shape[I] == 1)...});
160 return R({shape[I] == 1 ? 0 : calculate_stride<L, I, X...>::value...});
164 template <
class S,
class T, std::size_t... I>
165 constexpr T get_backstrides_impl(
const S& shape,
const T&
strides, std::index_sequence<I...>)
167 return T({(
strides[I] * std::ptrdiff_t(shape[I] - 1))...});
170 template <std::size_t... X>
171 struct fixed_compute_size_impl;
173 template <std::size_t Y, std::size_t... X>
174 struct fixed_compute_size_impl<Y, X...>
176 static constexpr std::size_t value = Y * fixed_compute_size_impl<X...>::value;
179 template <std::
size_t X>
180 struct fixed_compute_size_impl<X>
182 static constexpr std::size_t value = X;
186 struct fixed_compute_size_impl<>
189 static constexpr std::size_t value = 1;
194 struct fixed_compute_size;
196 template <std::size_t... X>
197 struct fixed_compute_size<xt::fixed_shape<X...>>
199 static constexpr std::size_t value = fixed_compute_size_impl<X...>::value;
202 template <
class V, std::size_t... X>
203 struct get_init_type_impl;
205 template <
class V, std::
size_t Y>
206 struct get_init_type_impl<V, Y>
212 struct get_init_type_impl<V>
217 template <
class V, std::size_t Y, std::size_t... X>
218 struct get_init_type_impl<V, Y, X...>
220 using tmp_type =
typename get_init_type_impl<V, X...>::type;
221 using type = tmp_type[Y];
225 template <
layout_type L,
class R, std::size_t... X>
228 return detail::get_strides_impl<L, R>(shape, std::make_index_sequence<
sizeof...(X)>{});
231 template <
class S,
class T>
232 constexpr T get_backstrides(
const S& shape,
const T&
strides)
noexcept
234 return detail::get_backstrides_impl(shape,
strides, std::make_index_sequence<std::tuple_size<T>::value>{});
237 template <
class V,
class S>
240 template <
class V, std::size_t... X>
243 using type =
typename detail::get_init_type_impl<V, X...>::type;
246 template <
class V,
class S>
249 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
252 using shape_type = S;
253 using inner_shape_type =
typename S::cast_type;
254 using strides_type = get_strides_t<inner_shape_type>;
255 using inner_strides_type = strides_type;
256 using backstrides_type = inner_strides_type;
257 using inner_backstrides_type = backstrides_type;
260#if defined(_MSC_VER) && _MSC_VER < 1910 && !defined(_WIN64)
262 using storage_type = std::array<ET, detail::fixed_compute_size<S>::value>;
267 using reference =
typename storage_type::reference;
268 using const_reference =
typename storage_type::const_reference;
269 using size_type =
typename storage_type::size_type;
274 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
295 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
296 class xfixed_container :
public xcontainer<xfixed_container<ET, S, L, SH, Tag>>,
297 public xcontainer_semantic<xfixed_container<ET, S, L, SH, Tag>>
301 using self_type = xfixed_container<ET, S, L, SH, Tag>;
302 using base_type = xcontainer<self_type>;
303 using semantic_base = xcontainer_semantic<self_type>;
305 using storage_type =
typename base_type::storage_type;
306 using value_type =
typename base_type::value_type;
307 using reference =
typename base_type::reference;
308 using const_reference =
typename base_type::const_reference;
309 using pointer =
typename base_type::pointer;
310 using const_pointer =
typename base_type::const_pointer;
311 using shape_type =
typename base_type::shape_type;
312 using inner_shape_type =
typename base_type::inner_shape_type;
313 using strides_type =
typename base_type::strides_type;
314 using backstrides_type =
typename base_type::backstrides_type;
315 using inner_backstrides_type =
typename base_type::inner_backstrides_type;
316 using inner_strides_type =
typename base_type::inner_strides_type;
317 using temporary_type =
typename semantic_base::temporary_type;
318 using expression_tag = Tag;
320 static constexpr std::size_t N = std::tuple_size<shape_type>::value;
321 static constexpr std::size_t rank = N;
323 xfixed_container() =
default;
324 xfixed_container(
const value_type& v);
329 template <
class IX = std::
integral_constant<std::
size_t, N>,
class EN = std::enable_if_t<IX::value != 0,
int>>
332 ~xfixed_container() =
default;
334 xfixed_container(
const xfixed_container&) =
default;
335 xfixed_container& operator=(
const xfixed_container&) =
default;
337 xfixed_container(xfixed_container&&) =
default;
338 xfixed_container& operator=(xfixed_container&&) =
default;
346 template <
class ST = std::array<std::
size_t, N>>
347 static xfixed_container from_shape(ST&& );
349 template <
class ST = std::array<std::
size_t, N>>
351 template <
class ST = shape_type>
353 template <
class ST = shape_type>
356 template <
class ST = std::array<std::
size_t, N>>
360 bool broadcast_shape(ST& s,
bool reuse_cache =
false)
const;
363 bool is_contiguous() const noexcept;
367 storage_type m_storage;
369 XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_shape_type m_shape = S();
370 XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_strides_type m_strides = get_strides<L, inner_strides_type>(S());
371 XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_backstrides_type
372 m_backstrides = get_backstrides(m_shape, m_strides);
374 storage_type& storage_impl() noexcept;
375 const storage_type& storage_impl() const noexcept;
377 XTENSOR_CONSTEXPR_RETURN const inner_shape_type& shape_impl() const noexcept;
378 XTENSOR_CONSTEXPR_RETURN const inner_strides_type& strides_impl() const noexcept;
379 XTENSOR_CONSTEXPR_RETURN const inner_backstrides_type& backstrides_impl() const noexcept;
381 friend class xcontainer<xfixed_container<ET, S, L, SH, Tag>>;
384#ifdef XTENSOR_HAS_CONSTEXPR_ENHANCED
386 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
388 typename xfixed_container<ET, S, L, SH, Tag>::inner_shape_type xfixed_container<ET, S, L, SH, Tag>::m_shape;
390 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
392 typename xfixed_container<ET, S, L, SH, Tag>::inner_strides_type xfixed_container<ET, S, L, SH, Tag>::m_strides;
394 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
395 constexpr typename xfixed_container<ET, S, L, SH, Tag>::inner_backstrides_type
396 xfixed_container<ET, S, L, SH, Tag>::m_backstrides;
403 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
406 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
409 using storage_type = std::remove_reference_t<EC>;
410 using reference =
typename storage_type::reference;
411 using const_reference =
typename storage_type::const_reference;
412 using size_type =
typename storage_type::size_type;
413 using shape_type = S;
414 using inner_shape_type =
typename S::cast_type;
415 using strides_type = get_strides_t<inner_shape_type>;
416 using backstrides_type = strides_type;
417 using inner_strides_type = strides_type;
418 using inner_backstrides_type = backstrides_type;
423 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
445 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
447 public xcontainer_semantic<xfixed_adaptor<EC, S, L, SH, Tag>>
451 using container_closure_type = EC;
454 using base_type = xcontainer<self_type>;
455 using semantic_base = xcontainer_semantic<self_type>;
456 using storage_type =
typename base_type::storage_type;
457 using shape_type =
typename base_type::shape_type;
458 using strides_type =
typename base_type::strides_type;
459 using backstrides_type =
typename base_type::backstrides_type;
460 using inner_shape_type =
typename base_type::inner_shape_type;
461 using inner_strides_type =
typename base_type::inner_strides_type;
462 using inner_backstrides_type =
typename base_type::inner_backstrides_type;
463 using temporary_type =
typename semantic_base::temporary_type;
464 using expression_tag = Tag;
466 static constexpr std::size_t N = S::size();
486 template <
class ST = std::array<std::
size_t, N>>
488 template <
class ST = shape_type>
490 template <
class ST = shape_type>
493 template <
class ST = std::array<std::
size_t, N>>
497 bool broadcast_shape(ST& s,
bool reuse_cache =
false)
const;
500 bool is_contiguous()
const noexcept;
504 container_closure_type m_storage;
506 XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_shape_type m_shape = S();
507 XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_strides_type m_strides = get_strides<L, inner_strides_type>(S());
508 XTENSOR_CONSTEXPR_ENHANCED_STATIC inner_backstrides_type
509 m_backstrides = get_backstrides(m_shape, m_strides);
511 storage_type& storage_impl()
noexcept;
512 const storage_type& storage_impl()
const noexcept;
514 XTENSOR_CONSTEXPR_RETURN
const inner_shape_type& shape_impl()
const noexcept;
515 XTENSOR_CONSTEXPR_RETURN
const inner_strides_type& strides_impl()
const noexcept;
516 XTENSOR_CONSTEXPR_RETURN
const inner_backstrides_type& backstrides_impl()
const noexcept;
521#ifdef XTENSOR_HAS_CONSTEXPR_ENHANCED
523 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
525 typename xfixed_adaptor<EC, S, L, SH, Tag>::inner_shape_type xfixed_adaptor<EC, S, L, SH, Tag>::m_shape;
527 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
529 typename xfixed_adaptor<EC, S, L, SH, Tag>::inner_strides_type xfixed_adaptor<EC, S, L, SH, Tag>::m_strides;
531 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
532 constexpr typename xfixed_adaptor<EC, S, L, SH, Tag>::inner_backstrides_type
533 xfixed_adaptor<EC, S, L, SH, Tag>::m_backstrides;
553 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
554 inline xfixed_container<ET, S, L, SH, Tag>::xfixed_container(
const inner_shape_type&
shape,
layout_type l)
558 XTENSOR_ASSERT(
shape.size() == N && std::equal(
shape.begin(),
shape.end(), m_shape.begin()));
559 XTENSOR_ASSERT(L == l);
562 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
563 inline xfixed_container<ET, S, L, SH, Tag>::xfixed_container(
const value_type& v)
565 if (this->
size() != 1)
567 XTENSOR_THROW(std::runtime_error,
"wrong shape for scalar assignment (has to be xshape<>).");
581 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
582 inline xfixed_container<ET, S, L, SH, Tag>::xfixed_container(
583 const inner_shape_type&
shape,
590 XTENSOR_ASSERT(
shape.size() == N && std::equal(
shape.begin(),
shape.end(), m_shape.begin()));
591 XTENSOR_ASSERT(L == l);
592 std::fill(m_storage.begin(), m_storage.end(), v);
597 template <std::
size_t X>
598 struct check_initializer_list_shape
600 template <
class T,
class S>
601 static bool run(
const T& t,
const S& shape)
603 std::size_t IX = shape.size() - X;
604 bool result = (shape[IX] == t.size());
605 for (std::size_t i = 0; i < shape[IX]; ++i)
607 result = result && check_initializer_list_shape<X - 1>::run(t.begin()[i], shape);
614 struct check_initializer_list_shape<0>
616 template <
class T,
class S>
617 static bool run(
const T& ,
const S& )
624 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
626 inline xfixed_container<ET, S, L, SH, Tag> xfixed_container<ET, S, L, SH, Tag>::from_shape(ST&&
shape)
630 XTENSOR_ASSERT(
shape.size() == N && std::equal(
shape.begin(),
shape.end(), tmp.shape().begin()));
641 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
642 template <
class IX,
class EN>
643 inline xfixed_container<ET, S, L, SH, Tag>::xfixed_container(nested_initializer_list_t<value_type, N> t)
646 detail::check_initializer_list_shape<N>::run(t, this->
shape()) ==
true,
647 "initializer list shape does not match fixed shape"
650 L == tmp ? nested_copy(m_storage.begin(), t) : nested_copy(this->
template begin<tmp>(), t);
662 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
664 inline xfixed_container<ET, S, L, SH, Tag>::xfixed_container(
const xexpression<E>& e)
666 semantic_base::assign(e);
672 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
674 inline auto xfixed_container<ET, S, L, SH, Tag>::operator=(
const xexpression<E>& e) -> self_type&
676 return semantic_base::operator=(e);
685 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
690 XTENSOR_ASSERT(std::equal(
shape.begin(),
shape.end(), m_shape.begin()) &&
shape.size() == m_shape.size());
697 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
704 std::equal(
shape.begin(),
shape.end(), m_shape.begin()) &&
shape.size() == m_shape.size() && L == l
712 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
718 XTENSOR_ASSERT(std::equal(
shape.begin(),
shape.end(), m_shape.begin()) &&
shape.size() == m_shape.size());
727 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
731 if (!(std::equal(
shape.begin(),
shape.end(), m_shape.begin()) &&
shape.size() == m_shape.size()
734 XTENSOR_THROW(std::runtime_error,
"Trying to reshape xtensor_fixed with different shape or layout.");
739 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
741 inline bool xfixed_container<ET, S, L, SH, Tag>::broadcast_shape(ST& shape,
bool)
const
743 return xt::broadcast_shape(m_shape, shape);
746 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
747 constexpr layout_type xfixed_container<ET, S, L, SH, Tag>::layout() const noexcept
749 return base_type::static_layout;
752 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
753 inline bool xfixed_container<ET, S, L, SH, Tag>::is_contiguous() const noexcept
755 using str_type =
typename inner_strides_type::value_type;
760 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
761 inline auto xfixed_container<ET, S, L, SH, Tag>::storage_impl() noexcept -> storage_type&
766 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
767 inline auto xfixed_container<ET, S, L, SH, Tag>::storage_impl() const noexcept -> const storage_type&
772 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
773 XTENSOR_CONSTEXPR_RETURN
auto xfixed_container<ET, S, L, SH, Tag>::shape_impl() const noexcept
774 -> const inner_shape_type&
779 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
780 XTENSOR_CONSTEXPR_RETURN
auto xfixed_container<ET, S, L, SH, Tag>::strides_impl() const noexcept
781 -> const inner_strides_type&
786 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
787 XTENSOR_CONSTEXPR_RETURN
auto xfixed_container<ET, S, L, SH, Tag>::backstrides_impl() const noexcept
788 -> const inner_backstrides_type&
790 return m_backstrides;
805 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
808 , m_storage(std::move(
data))
816 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
828 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
832 , m_storage(std::forward<D>(
data))
838 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
839 inline auto xfixed_adaptor<EC, S, L, SH, Tag>::operator=(
const xfixed_adaptor& rhs) -> self_type&
841 base_type::operator=(rhs);
842 m_storage = rhs.m_storage;
846 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
847 inline auto xfixed_adaptor<EC, S, L, SH, Tag>::operator=(xfixed_adaptor&& rhs) -> self_type&
849 base_type::operator=(std::move(rhs));
850 m_storage = rhs.m_storage;
854 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
855 inline auto xfixed_adaptor<EC, S, L, SH, Tag>::operator=(
temporary_type&& rhs) -> self_type&
857 m_storage.resize(rhs.storage().size());
858 std::copy(rhs.storage().cbegin(), rhs.storage().cend(), m_storage.begin());
869 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
871 inline auto xfixed_adaptor<EC, S, L, SH, Tag>::operator=(
const xexpression<E>& e) -> self_type&
873 return semantic_base::operator=(e);
882 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
887 XTENSOR_ASSERT(std::equal(
shape.begin(),
shape.end(), m_shape.begin()) &&
shape.size() == m_shape.size());
894 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
901 std::equal(
shape.begin(),
shape.end(), m_shape.begin()) &&
shape.size() == m_shape.size() && L == l
909 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
915 XTENSOR_ASSERT(std::equal(
shape.begin(),
shape.end(), m_shape.begin()) &&
shape.size() == m_shape.size());
924 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
928 if (!(std::equal(
shape.begin(),
shape.end(), m_shape.begin()) &&
shape.size() == m_shape.size()
931 XTENSOR_THROW(std::runtime_error,
"Trying to reshape xtensor_fixed with different shape or layout.");
936 template <
class ET,
class S, layout_type L,
bool SH,
class Tag>
938 inline bool xfixed_adaptor<ET, S, L, SH, Tag>::broadcast_shape(ST& shape,
bool)
const
940 return xt::broadcast_shape(m_shape, shape);
943 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
944 inline auto xfixed_adaptor<EC, S, L, SH, Tag>::storage_impl() noexcept -> storage_type&
949 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
950 inline auto xfixed_adaptor<EC, S, L, SH, Tag>::storage_impl() const noexcept -> const storage_type&
955 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
956 constexpr layout_type xfixed_adaptor<EC, S, L, SH, Tag>::layout() const noexcept
958 return base_type::static_layout;
961 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
962 inline bool xfixed_adaptor<EC, S, L, SH, Tag>::is_contiguous() const noexcept
964 using str_type =
typename inner_strides_type::value_type;
969 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
970 XTENSOR_CONSTEXPR_RETURN
auto xfixed_adaptor<EC, S, L, SH, Tag>::shape_impl() const noexcept
971 -> const inner_shape_type&
976 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
977 XTENSOR_CONSTEXPR_RETURN
auto xfixed_adaptor<EC, S, L, SH, Tag>::strides_impl() const noexcept
978 -> const inner_strides_type&
983 template <
class EC,
class S, layout_type L,
bool SH,
class Tag>
984 XTENSOR_CONSTEXPR_RETURN
auto xfixed_adaptor<EC, S, L, SH, Tag>::backstrides_impl() const noexcept
985 -> const inner_backstrides_type&
987 return m_backstrides;
This array class is modeled after std::array but adds optional alignment through a template parameter...
Fixed shape implementation for compile time defined arrays.
size_type shape(size_type index) const
size_type size() const noexcept
constexpr const inner_strides_type & strides() const noexcept
Returns the strides of the container.
constexpr const inner_shape_type & shape() const noexcept
Returns the shape of the container.
auto begin() noexcept -> select_iterator< L >
Base class for xexpressions.
Dense multidimensional container adaptor with tensor semantic and fixed dimension.
xfixed_adaptor(storage_type &&data)
Constructs an xfixed_adaptor of the given stl-like container.
const auto & reshape(ST &&shape, layout_type layout=L) const
Note that the xfixed_container cannot be reshaped to a shape different from S.
void resize(ST &&shape, bool force=false) const
Note that the xfixed_adaptor cannot be resized.
Dense multidimensional container with tensor semantic and fixed dimension.
xfixed_container(const inner_shape_type &shape, layout_type l=L)
Create an uninitialized xfixed_container.
xfixed_container(const xexpression< E > &e)
The extended copy constructor.
void resize(ST &&shape, bool force=false) const
Note that the xfixed_container cannot be resized.
void resize(ST &&shape, const strides_type &strides) const
Note that the xfixed_container cannot be resized.
xfixed_container(const inner_shape_type &shape, value_type v, layout_type l=L)
Create an xfixed_container, and initialize with the value of v.
void resize(ST &&shape, layout_type l) const
Note that the xfixed_container cannot be resized.
xfixed_container(nested_initializer_list_t< value_type, N > t)
Allocates an xfixed_container with shape S with values from a C array.
const auto & reshape(ST &&shape, layout_type layout=L) const
Note that the xfixed_container cannot be reshaped to a shape different from S.
auto strides(const E &e, stride_type type=stride_type::normal) noexcept
Get strides of an object.
standard mathematical functions for xexpressions