10#ifndef XTENSOR_UTILS_HPP
11#define XTENSOR_UTILS_HPP
16#include <initializer_list>
25#include <xtl/xfunctional.hpp>
26#include <xtl/xmeta_utils.hpp>
27#include <xtl/xsequence.hpp>
28#include <xtl/xtype_traits.hpp>
30#include "../core/xtensor_config.hpp"
47 template <std::size_t I,
class... Args>
48 constexpr decltype(
auto) argument(Args&&... args)
noexcept;
50 template <
class R,
class F,
class... S>
51 R apply(std::size_t index, F&& func,
const std::tuple<S...>& s)
noexcept(
noexcept(func(std::get<0>(s))));
53 template <
class T,
class S>
54 void nested_copy(T&& iter,
const S& s);
56 template <
class T,
class S>
57 void nested_copy(T&& iter, std::initializer_list<S> s);
60 bool resize_container(C& c,
typename C::size_type size);
62 template <
class T, std::
size_t N>
63 bool resize_container(std::array<T, N>& a,
typename std::array<T, N>::size_type size);
65 template <std::size_t... I>
68 template <std::size_t... I>
71 template <
class X,
class C>
74 template <
class X,
class C>
77 std::size_t normalize_axis(std::size_t dim, std::ptrdiff_t axis);
88 using void_t =
typename make_void<T...>::type;
102 template <
class T,
class R>
103 using disable_integral_t = std::enable_if_t<!xtl::is_integral<T>::value, R>;
109 template <
template <
class...>
class TT,
class T>
114 template <
template <
class...>
class TT,
class... Ts>
128 template <
class C,
class R,
class... Args>
131 typedef R type(Args...);
134 template <
class C,
class R,
class... Args>
137 typedef R type(Args...);
149 template <
class F,
size_t... I,
class... Ts>
150 void for_each(F&& f, std::tuple<Ts...>& t, std::index_sequence<I...>)
noexcept(
151 (
noexcept(f(std::get<I>(t))) && ...)
154 (f(std::get<I>(t)), ...);
157 template <
class F,
size_t... I,
class... Ts>
158 void for_each(F&& f,
const std::tuple<Ts...>& t, std::index_sequence<I...>)
noexcept(
159 (
noexcept(f(std::get<I>(t))) && ...)
162 (f(std::get<I>(t)), ...);
166 template <
class F,
class... Ts>
167 inline void for_each(F&& f, std::tuple<Ts...>& t)
noexcept(
168 noexcept(detail::for_each(std::forward<F>(f), t, std::make_index_sequence<
sizeof...(Ts)>{}))
171 detail::for_each(std::forward<F>(f), t, std::make_index_sequence<
sizeof...(Ts)>{});
174 template <
class F,
class... Ts>
175 inline void for_each(F&& f,
const std::tuple<Ts...>& t)
noexcept(
176 noexcept(detail::for_each(std::forward<F>(f), t, std::make_index_sequence<
sizeof...(Ts)>{}))
179 detail::for_each(std::forward<F>(f), t, std::make_index_sequence<
sizeof...(Ts)>{});
190 template <
class F,
class R,
class... T,
size_t... I>
191 R accumulate_impl(F&& f, R init,
const std::tuple<T...>& t, std::index_sequence<I...> )
noexcept(
192 (
noexcept(f(init, std::get<I>(t))) && ...)
196 auto wrapper = [&](
const auto& i,
const auto& j)
200 (wrapper(res, std::get<I>(t)), ...);
205 template <
class F,
class R,
class... T>
206 inline R
accumulate(F&& f, R init,
const std::tuple<T...>& t)
noexcept(
207 noexcept(detail::accumulate_impl(std::forward<F>(f), init, t, std::make_index_sequence<
sizeof...(T)>{}))
210 return detail::accumulate_impl(std::forward<F>(f), init, t, std::make_index_sequence<
sizeof...(T)>{});
221 template <std::
size_t I>
224 template <
class Arg,
class... Args>
225 static constexpr decltype(
auto) get(Arg&& , Args&&... args)
noexcept
227 return getter<I - 1>::get(std::forward<Args>(args)...);
234 template <
class Arg,
class... Args>
235 static constexpr Arg&& get(Arg&&
arg, Args&&... ) noexcept
237 return std::forward<Arg>(
arg);
242 template <std::size_t I,
class... Args>
243 constexpr decltype(
auto) argument(Args&&... args)
noexcept
245 static_assert(I <
sizeof...(Args),
"I should be lesser than sizeof...(Args)");
246 return detail::getter<I>::get(std::forward<Args>(args)...);
253 template <
class R,
class F,
class... S>
255 apply(std::size_t index, F&& func,
const std::tuple<S...>& s)
noexcept(
noexcept(func(std::get<0>(s))))
257 XTENSOR_ASSERT(
sizeof...(S) > index);
259 [&](
const S&... args) -> R
261 auto f_impl = [&](
auto&& self,
auto&& i,
auto&& h,
auto&&... t) -> R
265 return static_cast<R
>(func(h));
267 if constexpr (
sizeof...(t) > 0)
269 return self(self, std::size_t{i + 1}, t...);
273 return f_impl(f_impl, std::size_t{0}, args...);
283 template <
class T, std::
size_t I>
295 template <
class T, std::
size_t I>
296 using nested_initializer_list_t =
typename nested_initializer_list<T, I>::type;
302 template <
class T,
class S>
303 inline void nested_copy(T&& iter,
const S& s)
308 template <
class T,
class S>
309 inline void nested_copy(T&& iter, std::initializer_list<S> s)
311 for (
auto it = s.begin(); it != s.end(); ++it)
313 nested_copy(std::forward<T>(iter), *it);
321 inline bool resize_container(C& c,
typename C::size_type size)
327 template <
class T, std::
size_t N>
328 inline bool resize_container(std::array<T, N>& ,
typename std::array<T, N>::size_type size)
333 template <std::size_t... I>
334 inline bool resize_container(xt::fixed_shape<I...>&, std::size_t size)
336 return sizeof...(I) == size;
344 inline std::size_t normalize_axis(std::size_t dim, std::ptrdiff_t axis)
346 return axis < 0 ? static_cast<std::size_t>(
static_cast<std::ptrdiff_t
>(dim) + axis)
347 : static_cast<std::size_t>(axis);
350 template <
class E,
class C>
351 inline std::enable_if_t<
352 !xtl::is_integral<std::decay_t<C>>::value && xtl::is_signed<typename std::decay_t<C>::value_type>::value,
353 rebind_container_t<std::size_t, std::decay_t<C>>>
354 normalize_axis(E& expr, C&& axes)
356 rebind_container_t<std::size_t, std::decay_t<C>> res;
357 resize_container(res, axes.size());
359 for (std::size_t i = 0; i < axes.size(); ++i)
361 res[i] = normalize_axis(expr.dimension(), axes[i]);
364 XTENSOR_ASSERT(std::all_of(
369 return ax_el < expr.dimension();
376 template <
class C,
class E>
377 inline std::enable_if_t<
378 !xtl::is_integral<std::decay_t<C>>::value && std::is_unsigned<typename std::decay_t<C>::value_type>::value,
380 normalize_axis(E& expr, C&& axes)
382 static_cast<void>(expr);
383 XTENSOR_ASSERT(std::all_of(
388 return ax_el < expr.dimension();
391 return std::forward<C>(axes);
394 template <
class R,
class E,
class C>
395 inline auto forward_normalize(E& expr, C&& axes)
396 -> std::enable_if_t<xtl::is_signed<std::decay_t<
decltype(*std::begin(axes))>>::value, R>
399 xt::resize_container(res, std::size(axes));
400 auto dim = expr.dimension();
407 return normalize_axis(dim, ax_el);
411 XTENSOR_ASSERT(std::all_of(
416 return ax_el < expr.dimension();
423 template <
class R,
class E,
class C>
424 inline auto forward_normalize(E& expr, C&& axes) -> std::enable_if_t<
425 !xtl::is_signed<std::decay_t<
decltype(*std::begin(axes))>>::value && !std::is_same<R, std::decay_t<C>>::value,
428 static_cast<void>(expr);
431 xt::resize_container(res, std::size(axes));
432 std::copy(std::begin(axes), std::end(axes), std::begin(res));
433 XTENSOR_ASSERT(std::all_of(
438 return ax_el < expr.dimension();
444 template <
class R,
class E,
class C>
445 inline auto forward_normalize(E& expr, C&& axes) -> std::enable_if_t<
446 !xtl::is_signed<std::decay_t<
decltype(*std::begin(axes))>>::value && std::is_same<R, std::decay_t<C>>::value,
449 static_cast<void>(expr);
450 XTENSOR_ASSERT(std::all_of(
455 return ax_el < expr.dimension();
458 return std::move(axes);
472 requires requires {
typename T::value_type; }
475 using type =
typename T::value_type;
479 using get_value_type_t =
typename get_value_type<T>::type;
487 template <std::size_t I,
template <
typename... Args>
class T, typename... Args>
488 decltype(auto) get(T<Args...>&& v)
490 return std::get<I>(
static_cast<std::tuple<Args...
>&&>(v));
493 template <std::size_t I,
template <
typename... Args>
class T, typename... Args>
494 decltype(auto) get(T<Args...>& v)
496 return std::get<I>(
static_cast<std::tuple<Args...
>&>(v));
499 template <std::size_t I,
template <
typename... Args>
class T, typename... Args>
500 decltype(auto) get(const T<Args...>& v)
502 return std::get<I>(
static_cast<const std::tuple<Args...
>&>(v));
511 template <
class T, std::size_t N, std::size_t... I>
512 constexpr std::array<std::remove_cv_t<T>, N> to_array_impl(T (&a)[N], std::index_sequence<I...>)
518 template <
class T, std::
size_t N>
519 constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N])
521 return detail::to_array_impl(a, std::make_index_sequence<N>{});
534 requires !std::is_same_v<typename std::remove_cv<typename xcontainer_inner_types<T>::storage_type>::type,
invalid_type>;
553 template <
bool condition,
class T>
565 inline auto operator()(U&& u)
const
567 return static_cast<T
>(std::forward<U>(u));
579 template <
bool condition,
class T,
class U>
589 namespace alloc_tracking
591 inline bool& enabled()
602 inline void disable()
614 template <
class T,
class A, alloc_tracking::policy P>
615 struct tracking_allocator :
private A
618 using value_type =
typename A::value_type;
619 using reference = value_type&;
620 using const_reference =
const value_type&;
621 using pointer =
typename std::allocator_traits<A>::pointer;
622 using const_pointer =
typename std::allocator_traits<A>::const_pointer;
623 using size_type =
typename std::allocator_traits<A>::size_type;
624 using difference_type =
typename std::allocator_traits<A>::difference_type;
626 tracking_allocator() =
default;
628 T* allocate(std::size_t n)
630 if (alloc_tracking::enabled())
632 if (P == alloc_tracking::print)
634 std::cout <<
"xtensor allocating: " << n <<
"" << std::endl;
636 else if (P == alloc_tracking::assert)
640 "xtensor allocation of " + std::to_string(n) +
" elements detected"
644 return base_type::allocate(n);
647 using base_type::deallocate;
650#if ((defined(__cplusplus) && __cplusplus < 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG < 202002L))
651 using base_type::construct;
652 using base_type::destroy;
658 using traits = std::allocator_traits<A>;
659 using other = tracking_allocator<U, typename traits::template rebind_alloc<U>, P>;
663 template <
class T,
class AT, alloc_tracking::policy PT,
class U,
class AU, alloc_tracking::policy PU>
666 return std::is_same<AT, AU>::value;
669 template <
class T,
class AT, alloc_tracking::policy PT,
class U,
class AU, alloc_tracking::policy PU>
670 inline bool operator!=(
const tracking_allocator<T, AT, PT>& a,
const tracking_allocator<U, AU, PU>& b)
679 template <
class E1,
class E2>
693 const uintptr_t m_first = 0;
694 const uintptr_t m_last = 0;
696 explicit memory_range() =
default;
699 explicit memory_range(T* first, T* last)
700 : m_first(
reinterpret_cast<uintptr_t
>(last < first ? last : first))
701 , m_last(
reinterpret_cast<uintptr_t
>(last < first ? first : last))
706 bool overlaps(T* first, T* last)
const
710 return reinterpret_cast<uintptr_t
>(first) <= m_last
711 &&
reinterpret_cast<uintptr_t
>(last) >= m_first;
715 return reinterpret_cast<uintptr_t
>(last) <= m_last
716 &&
reinterpret_cast<uintptr_t
>(first) >= m_first;
721 template <
class E,
class Enable =
void>
724 static bool check_overlap(
const E&,
const memory_range&)
733 static bool check_overlap(
const E& expr,
const memory_range& dst_range)
735 if (expr.size() == 0)
741 return dst_range.overlaps(std::addressof(*expr.begin()), std::addressof(*expr.rbegin()));
746 struct overlapping_memory_checker_base
750 explicit overlapping_memory_checker_base() =
default;
752 explicit overlapping_memory_checker_base(
memory_range dst_memory_range)
753 : m_dst_range(std::move(dst_memory_range))
758 bool check_overlap(
const E& expr)
const
760 if (!m_dst_range.m_first || !m_dst_range.m_last)
766 return overlapping_memory_checker_traits<E>::check_overlap(expr, m_dst_range);
771 template <
class Dst,
class Enable =
void>
772 struct overlapping_memory_checker : overlapping_memory_checker_base
774 explicit overlapping_memory_checker(
const Dst&)
775 : overlapping_memory_checker_base()
781 struct overlapping_memory_checker<Dst, std::enable_if_t<addressable_to_expression<Dst>>>
782 : overlapping_memory_checker_base
784 explicit overlapping_memory_checker(
const Dst& aDst)
785 : overlapping_memory_checker_base(
788 if (aDst.size() == 0)
794 return memory_range(std::addressof(*aDst.begin()), std::addressof(*aDst.rbegin()));
803 auto make_overlapping_memory_checker(
const Dst& a_dst)
812 template <
class X,
template <
class,
class>
class C,
class T,
class A>
815 using traits = std::allocator_traits<A>;
816 using allocator =
typename traits::template rebind_alloc<X>;
817 using type = C<X, allocator>;
821#ifdef __cpp_template_template_args
822 template <
class X,
class T, std::
size_t N>
825 using type = std::array<X, N>;
828 template <
class X,
template <
class, std::
size_t>
class C,
class T, std::size_t N>
831 using type = C<X, N>;
839 template <
class CP,
class O,
class A>
845 inline constexpr bool is_fixed_shape_v =
false;
847 template <std::size_t... I>
848 inline constexpr bool is_fixed_shape_v<
fixed_shape<I...>> =
true;
851 inline constexpr bool is_xbuffer_adaptor_v =
false;
853 template <
class CP,
class O,
class A>
854 inline constexpr bool is_xbuffer_adaptor_v<xbuffer_adaptor<CP, O, A>> =
true;
857 concept fixed_shape_type = is_fixed_shape_v<S>;
860 concept xbuffer_adaptor_type = is_xbuffer_adaptor_v<S>;
871 template <detail::fixed_shape_type S>
877 using type = std::array<std::ptrdiff_t, S::size()>;
880 template <detail::xbuffer_adaptor_type S>
886 using type = std::vector<typename S::value_type, typename S::allocator_type>;
890 using get_strides_t =
typename get_strides_type<S>::type;
897 using inner_reference_t = std::conditional_t<
898 std::is_const<std::remove_reference_t<ST>>::value,
899 typename std::decay_t<ST>::const_reference,
900 typename std::decay_t<ST>::reference>;
907 template <
typename T>
913 constexpr std::size_t has_rank()
919 constexpr std::size_t get_rank()
921 if constexpr (HasRank<std::decay_t<E>>)
923 return std::decay_t<E>::rank;
929 constexpr std::size_t has_fixed_rank()
931 return get_rank<E>() != SIZE_MAX;
Fixed shape implementation for compile time defined arrays.
auto arg(E &&e) noexcept
Calculates the phase angle (in radians) elementwise for the complex numbers in e.
standard mathematical functions for xexpressions
auto accumulate(F &&f, E &&e, EVS evaluation_strategy=EVS())
Accumulate and flatten array NOTE This function is not lazy!
auto conditional_cast(U &&u)
Perform a type cast when a condition is true.