diff --git a/include/xtensor/xadapt.hpp b/include/xtensor/xadapt.hpp index 4b3b87207..359dcec86 100644 --- a/include/xtensor/xadapt.hpp +++ b/include/xtensor/xadapt.hpp @@ -89,7 +89,7 @@ namespace xt */ template xtensor_adaptor - xadapt(C& container, const std::array& shape, layout_type l = L); + xadapt(C& container, const stat_shape& shape, layout_type l = L); /** * Constructs an xtensor_adaptor of the given stl-like container, @@ -100,7 +100,7 @@ namespace xt */ template xtensor_adaptor - xadapt(C& container, const std::array& shape, const std::array& strides); + xadapt(C& container, const stat_shape& shape, const stat_shape& strides); /** * Constructs an xtensor_adaptor of the given dynamically allocated C array, @@ -116,7 +116,7 @@ namespace xt template >> xtensor_adaptor, O, A>, N, L> xadapt(P& pointer, typename A::size_type size, O ownership, - const std::array& shape, layout_type l = L, const A& alloc = A()); + const stat_shape& shape, layout_type l = L, const A& alloc = A()); /** * Constructs an xtensor_adaptor of the given dynamically allocated C array, @@ -132,7 +132,7 @@ namespace xt template >> xtensor_adaptor, O, A>, N, layout_type::dynamic> xadapt(P& pointer, typename A::size_type size, O ownership, - const std::array& shape, const std::array& strides, const A& alloc = A()); + const stat_shape& shape, const stat_shape& strides, const A& alloc = A()); /***************************************** * xarray_adaptor builder implementation * @@ -176,14 +176,14 @@ namespace xt template inline xtensor_adaptor - xadapt(C& container, const std::array& shape, layout_type l) + xadapt(C& container, const stat_shape& shape, layout_type l) { return xtensor_adaptor(container, shape, l); } template inline xtensor_adaptor - xadapt(C& container, const std::array& shape, const std::array& strides) + xadapt(C& container, const stat_shape& shape, const stat_shape& strides) { return xtensor_adaptor(container, shape, strides); } @@ -191,7 +191,7 @@ namespace xt template inline xtensor_adaptor, O, A>, N, L> xadapt(P& pointer, typename A::size_type size, O, - const std::array& shape, layout_type l, const A& alloc) + const stat_shape& shape, layout_type l, const A& alloc) { using buffer_type = xbuffer_adaptor, O, A>; buffer_type buf(pointer, size, alloc); @@ -201,7 +201,7 @@ namespace xt template inline xtensor_adaptor, O, A>, N, layout_type::dynamic> xadapt(P& pointer, typename A::size_type size, O, - const std::array& shape, const std::array& strides, const A& alloc) + const stat_shape& shape, const stat_shape& strides, const A& alloc) { using buffer_type = xbuffer_adaptor, O, A>; buffer_type buf(pointer, size, alloc); diff --git a/include/xtensor/xarray.hpp b/include/xtensor/xarray.hpp index c40b1a64a..e4bfbf988 100644 --- a/include/xtensor/xarray.hpp +++ b/include/xtensor/xarray.hpp @@ -121,7 +121,7 @@ namespace xt * xarray_adaptor declaration * ******************************/ - template > + template > class xarray_adaptor; template diff --git a/include/xtensor/xassign.hpp b/include/xtensor/xassign.hpp index 137b55090..9d5c66820 100644 --- a/include/xtensor/xassign.hpp +++ b/include/xtensor/xassign.hpp @@ -157,6 +157,7 @@ namespace xt shape_type shape = make_sequence(dim, size_type(1)); bool trivial_broadcast = de2.broadcast_shape(shape); + // FIXME: The second comparison is lexicographic. Comment why this is intended. if (dim > de1.dimension() || shape > de1.shape()) { typename E1::temporary_type tmp(shape); diff --git a/include/xtensor/xbroadcast.hpp b/include/xtensor/xbroadcast.hpp index 0413e29c8..3da0df8d7 100644 --- a/include/xtensor/xbroadcast.hpp +++ b/include/xtensor/xbroadcast.hpp @@ -159,7 +159,7 @@ namespace xt template inline auto broadcast(E&& e, std::initializer_list s) noexcept { - using broadcast_type = xbroadcast, std::vector>; + using broadcast_type = xbroadcast, dyn_shape>; using shape_type = typename broadcast_type::shape_type; return broadcast_type(std::forward(e), forward_sequence(s)); } @@ -167,7 +167,7 @@ namespace xt template inline auto broadcast(E&& e, const I (&s)[L]) noexcept { - using broadcast_type = xbroadcast, std::array>; + using broadcast_type = xbroadcast, stat_shape>; using shape_type = typename broadcast_type::shape_type; return broadcast_type(std::forward(e), forward_sequence(s)); } diff --git a/include/xtensor/xbuilder.hpp b/include/xtensor/xbuilder.hpp index eb16ed0e7..e51f38e7b 100644 --- a/include/xtensor/xbuilder.hpp +++ b/include/xtensor/xbuilder.hpp @@ -207,7 +207,7 @@ namespace xt * @return xgenerator that generates the values on access */ template - inline auto eye(const std::vector& shape, int k = 0) + inline auto eye(const dyn_shape& shape, int k = 0) { return detail::make_xgenerator(detail::fn_impl>(detail::eye_fn(k)), shape); } @@ -379,7 +379,8 @@ namespace xt private: - inline value_type access_impl(xindex idx) const + template + inline value_type access_impl(std::vector idx) const { auto get_item = [&idx](auto& arr) { @@ -390,6 +391,18 @@ namespace xt return apply(i, get_item, m_t); } + template + inline value_type access_impl(tiny_array const & old_idx) const + { + size_type i = old_idx[m_axis]; + auto idx = old_idx.erase(m_axis); + auto get_item = [&idx](auto& arr) + { + return arr[idx]; + }; + return apply(i, get_item, m_t); + } + const std::tuple m_t; const size_type m_axis; }; @@ -412,7 +425,7 @@ namespace xt template value_type operator()(Args... args) const { - std::array args_arr = {static_cast(args)...}; + stat_shape args_arr = {static_cast(args)...}; return m_source(args_arr[m_axis]); } @@ -478,6 +491,12 @@ namespace xt return temp; } + template + inline auto add_axis(tiny_array arr, std::size_t axis, std::size_t value) + { + return arr.insert(axis, value); + } + template inline T add_axis(T arr, std::size_t axis, std::size_t value) { @@ -520,7 +539,7 @@ namespace xt inline auto meshgrid_impl(std::index_sequence, E&&... e) noexcept { #if defined X_OLD_CLANG || defined _MSC_VER - const std::array shape { e.shape()[0]... }; + const stat_shape shape { e.shape()[0]... }; return std::make_tuple( detail::make_xgenerator( detail::repeat_impl>(std::forward(e), I), @@ -652,7 +671,7 @@ namespace xt template inline value_type operator()(Args... args) const { - std::array idx({static_cast(args)...}); + stat_shape idx({static_cast(args)...}); return access_impl(idx.begin(), idx.end()); } @@ -723,23 +742,31 @@ namespace xt { using type = std::array; }; + + template + struct diagonal_shape_type> + { + using type = std::conditional_t<(L > 0), + tiny_array, + tiny_array>; + }; } /** * @brief Returns the elements on the diagonal of arr - * If arr has more than two dimensions, then the axes specified by - * axis_1 and axis_2 are used to determine the 2-D sub-array whose - * diagonal is returned. The shape of the resulting array can be - * determined by removing axis1 and axis2 and appending an index + * If arr has more than two dimensions, then the axes specified by + * axis_1 and axis_2 are used to determine the 2-D sub-array whose + * diagonal is returned. The shape of the resulting array can be + * determined by removing axis1 and axis2 and appending an index * to the right equal to the size of the resulting diagonals. * * @param arr the input array * @param offset offset of the diagonal from the main diagonal. Can * be positive or negative. - * @param axis_1 Axis to be used as the first axis of the 2-D sub-arrays - * from which the diagonals should be taken. - * @param axis_2 Axis to be used as the second axis of the 2-D sub-arrays - * from which the diagonals should be taken. + * @param axis_1 Axis to be used as the first axis of the 2-D sub-arrays + * from which the diagonals should be taken. + * @param axis_2 Axis to be used as the second axis of the 2-D sub-arrays + * from which the diagonals should be taken. * @returns xexpression with values of the diagonal * * \code{.cpp} @@ -810,7 +837,7 @@ namespace xt * @brief Reverse the order of elements in an xexpression along the given axis. * Note: A NumPy/Matlab style `flipud(arr)` is equivalent to `xt::flip(arr, 0)`, * `fliplr(arr)` to `xt::flip(arr, 1)`. - * + * * @param arr the input xexpression * @param axis the axis along which elements should be reversed * diff --git a/include/xtensor/xconcepts.hpp b/include/xtensor/xconcepts.hpp new file mode 100644 index 000000000..7b621aa68 --- /dev/null +++ b/include/xtensor/xconcepts.hpp @@ -0,0 +1,272 @@ +/*************************************************************************** +* Copyright (c) 2017, Ullrich Koethe * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XCONCEPTS_HPP +#define XCONCEPTS_HPP + +#include + +/**********************************************************/ +/* */ +/* concept checking and type inference functionality */ +/* */ +/**********************************************************/ + +namespace xt +{ + +/**********************************************************/ +/* */ +/* XTENSOR_REQUIRE concept checking macro */ +/* */ +/**********************************************************/ + +struct require_ok {}; + +template +using concept_check = typename std::enable_if::type; + +#define XTENSOR_REQUIRE typename Require = concept_check + +/**********************************************************/ +/* */ +/* xexpression_concept */ +/* */ +/**********************************************************/ + +struct xexpression_tag {}; + + // xexpression_concept is fulfilled by data structures that can be used + // in xexpressions (xarray, xtensor, xfunction). By default, 'T' fulfills + // the concept if it is derived from xexpression_tag. + // + // Alternatively, one can partially specialize xexpression_concept. +template +struct xexpression_concept +{ + static const bool value = std::is_base_of >::value; +}; + +/**********************************************************/ +/* */ +/* tiny_array_concept */ +/* */ +/**********************************************************/ + +struct tiny_array_tag {}; + + // tiny_array_concept refers to tiny_array_base and tiny_array. + // By default, 'ARRAY' fulfills the tiny_array_concept if it is derived + // from tiny_array_tag. + // + // Alternatively, one can partially specialize tiny_array_concept. +template +struct tiny_array_concept +{ + static const bool value = std::is_base_of >::value; +}; + +/**********************************************************/ +/* */ +/* iterator_concept */ +/* */ +/**********************************************************/ + + // currently, we apply only the simple rule that class T + // is either a pointer or array or has an embedded typedef + // 'iterator_category'. More sophisticated checks should + // be added when needed. +template +struct iterator_concept +{ + using V = std::decay_t; + + static char test(...); + + template + static int test(U*, typename U::iterator_category * = 0); + + static const bool value = + std::is_array::value || + std::is_pointer::value || + std::is_same::value; +}; + +/**********************************************************/ +/* */ +/* promote types */ +/* */ +/**********************************************************/ + +namespace concepts_detail +{ + using std::sqrt; + + template + using real_promote_t = decltype(sqrt(*(std::decay_t*)0)); +} + + // result of arithmetic expressions + // (e.g. unsigned char + unsigned char => int) +template +using promote_t = decltype(*(std::decay_t*)0 + *(std::decay_t*)0); + + // result of algebraic expressions + // (e.g. sqrt(int) => double) +template +using real_promote_t = concepts_detail::real_promote_t; + + // replace 'bool' with 'uint8_t', keep everything else +template +using bool_promote_t = typename std::conditional::value, uint8_t, T>::type; + +/**********************************************************/ +/* */ +/* type inference for norm and squared norm */ +/* */ +/**********************************************************/ + +template +struct norm_traits; + +namespace concepts_detail { + +template ::value> +struct norm_of_scalar_impl; + +template +struct norm_of_scalar_impl +{ + static const bool value = false; + using norm_type = void *; + using squared_norm_type = void *; +}; + +template +struct norm_of_scalar_impl +{ + static const bool value = true; + using norm_type = decltype(norm(*(T*)0)); + using squared_norm_type = decltype(squared_norm(*(T*)0)); +}; + +template ::value, + bool floating = std::is_floating_point::value> +struct norm_of_array_elements_impl; + +template <> +struct norm_of_array_elements_impl +{ + using norm_type = void *; + using squared_norm_type = void *; +}; + +template +struct norm_of_array_elements_impl +{ + using norm_type = typename norm_traits::norm_type; + using squared_norm_type = typename norm_traits::squared_norm_type; +}; + +template +struct norm_of_array_elements_impl +{ + static_assert(!std::is_same::value, + "'char' is not a numeric type, use 'signed char' or 'unsigned char'."); + + using norm_type = double; + using squared_norm_type = uint64_t; +}; + +template +struct norm_of_array_elements_impl +{ + using norm_type = double; + using squared_norm_type = double; +}; + +template <> +struct norm_of_array_elements_impl +{ + using norm_type = long double; + using squared_norm_type = long double; +}; + +template +struct norm_of_vector_impl +{ + static void * test(...); + + template + static typename U::value_type test(U*, typename U::value_type * = 0); + + using T = decltype(test((ARRAY*)0)); + + static const bool value = !std::is_same::value; + + using norm_type = typename norm_of_array_elements_impl::norm_type; + using squared_norm_type = typename norm_of_array_elements_impl::squared_norm_type; +}; + +} // namespace concepts_detail + + /* norm_traits implement the following default rules, which are + designed to minimize the possibility of overflow: + * T is a built-in type: + norm_type is the result type of abs(), + squared_norm_type is the result type of sq() + * T is a container of 'long double' elements: + norm_type and squared_norm_type are 'long double' + * T is a container of another floating-point type: + norm_type and squared_norm_type are 'double', + * T is a container of integer elements: + norm_type is 'double', + squared_norm_type is 'uint64_t' + * T is a container of some other type: + norm_type is the element's norm type, + squared_norm_type is the element's squared norm type + Containers are recognized by having an embedded typedef 'value_type'. + + To change the behavior for a particular case or extend it to cases + not covered here, simply specialize the norm_traits template. + */ +template +struct norm_traits +{ + using T = std::decay_t; + + static_assert(!std::is_same::value, + "'char' is not a numeric type, use 'signed char' or 'unsigned char'."); + + using norm_of_scalar = concepts_detail::norm_of_scalar_impl; + using norm_of_vector = concepts_detail::norm_of_vector_impl; + + static const bool value = norm_of_scalar::value || norm_of_vector::value; + + static_assert(value, "norm_traits are undefined for type U."); + + using norm_type = + typename std::conditional::type; + + using squared_norm_type = + typename std::conditional::type; +}; + +template +using squared_norm_t = typename norm_traits::squared_norm_type; + +template +using norm_t = typename norm_traits::norm_type; + +} // namespace xt + +#endif // XCONCEPTS_HPP diff --git a/include/xtensor/xeval.hpp b/include/xtensor/xeval.hpp index 7fdeb25ac..d842e8061 100644 --- a/include/xtensor/xeval.hpp +++ b/include/xtensor/xeval.hpp @@ -19,11 +19,28 @@ namespace xt { template using is_container = std::is_base_of>, T>; + + template + struct shape_ndim + : public std::tuple_size + {}; + + template + struct shape_ndim> + { + static const int value = N; + }; + + template + struct shape_ndim> + { + static const int value = 0; + }; } /** * Force evaluation of xexpression. * @return xarray or xtensor depending on shape type - * + * * \code{.cpp} * xarray a = {1,2,3,4}; * auto&& b = xt::eval(a); // b is a reference to a, no copy! @@ -40,9 +57,9 @@ namespace xt /// @cond DOXYGEN_INCLUDE_SFINAE template > inline auto eval(T&& t) - -> std::enable_if_t::value && detail::is_array::value, xtensor::value>> + -> std::enable_if_t::value && detail::is_array::value, xtensor::value>> { - return xtensor::value>(std::forward(t)); + return xtensor::value>(std::forward(t)); } template > diff --git a/include/xtensor/xexception.hpp b/include/xtensor/xexception.hpp index 983b4bcd6..fe89e0682 100644 --- a/include/xtensor/xexception.hpp +++ b/include/xtensor/xexception.hpp @@ -167,4 +167,20 @@ namespace xt #define XTENSOR_ASSERT(expr) #endif } + +#ifdef XTENSOR_ENABLE_ASSERT +#define XTENSOR_ASSERT_MSG(PREDICATE, MESSAGE) \ + if((PREDICATE)) {} else { \ + throw std::runtime_error(std::string("Assertion error!\n") + MESSAGE + \ + "\n " + __FILE__ + '(' + std::to_string(__LINE__) + ")\n"); } + +#else +#define XTENSOR_ASSERT_MSG(PREDICATE, MESSAGE) #endif + +#define xtensor_precondition(PREDICATE, MESSAGE) \ + if((PREDICATE)) {} else { \ + throw std::runtime_error(std::string("Precondition violation!\n") + MESSAGE + \ + "\n " + __FILE__ + '(' + std::to_string(__LINE__) + ")\n"); } + +#endif // XEXCEPTION_HPP diff --git a/include/xtensor/xexpression.hpp b/include/xtensor/xexpression.hpp index ff13fb4f8..7defa5744 100644 --- a/include/xtensor/xexpression.hpp +++ b/include/xtensor/xexpression.hpp @@ -13,12 +13,13 @@ #include #include +#include "xconcepts.hpp" #include "xutils.hpp" namespace xt { - using xindex = std::vector; + using xindex = dyn_shape; /*************************** * xexpression declaration * @@ -38,6 +39,7 @@ namespace xt */ template class xexpression + : public xexpression_tag { public: diff --git a/include/xtensor/xfunctorview.hpp b/include/xtensor/xfunctorview.hpp index b9db6bc02..c9c805807 100644 --- a/include/xtensor/xfunctorview.hpp +++ b/include/xtensor/xfunctorview.hpp @@ -56,6 +56,18 @@ namespace xt { using type = xtensor; }; + + template + struct functorview_temporary_type_impl, L> + { + using type = xtensor; + }; + + template + struct functorview_temporary_type_impl, L> + { + using type = xarray; + }; } template diff --git a/include/xtensor/xgenerator.hpp b/include/xtensor/xgenerator.hpp index 734c03840..81a8ce42a 100644 --- a/include/xtensor/xgenerator.hpp +++ b/include/xtensor/xgenerator.hpp @@ -124,7 +124,7 @@ namespace xt */ //@{ /** - * Constructs an xgenerator applying the specified function over the + * Constructs an xgenerator applying the specified function over the * given shape. * @param f the function to apply * @param shape the shape of the xgenerator @@ -272,7 +272,7 @@ namespace xt template inline auto make_xgenerator(Functor&& f, std::initializer_list shape) noexcept { - using shape_type = std::vector; + using shape_type = dyn_shape; using type = xgenerator; return type(std::forward(f), forward_sequence(shape)); } @@ -280,7 +280,7 @@ namespace xt template inline auto make_xgenerator(Functor&& f, const I (&shape)[L]) noexcept { - using shape_type = std::array; + using shape_type = stat_shape; using type = xgenerator; return type(std::forward(f), forward_sequence(shape)); } diff --git a/include/xtensor/xindexview.hpp b/include/xtensor/xindexview.hpp index 7f4a0b1c6..78b4ef96c 100644 --- a/include/xtensor/xindexview.hpp +++ b/include/xtensor/xindexview.hpp @@ -18,7 +18,6 @@ #include "xexpression.hpp" #include "xiterable.hpp" #include "xstrides.hpp" -#include "xutils.hpp" namespace xt { @@ -36,7 +35,7 @@ namespace xt template struct xiterable_inner_types> { - using inner_shape_type = std::array; + using inner_shape_type = stat_shape; using const_stepper = xindexed_stepper>; using stepper = xindexed_stepper, false>; using const_iterator = xiterator; @@ -224,7 +223,7 @@ namespace xt /** * Constructs an xindexview, selecting the indices specified by \a indices. * The resulting xexpression has a 1D shape with a length of n for n indices. - * + * * @param e the underlying xexpression for this view * @param indices the indices to select */ @@ -327,8 +326,8 @@ namespace xt } /** - * Returns the element at the specified position in the xindexview. - * + * Returns the element at the specified position in the xindexview. + * * @param idx the position in the view */ template @@ -457,7 +456,7 @@ namespace xt //@{ /** * Constructs a xfiltration on the given expression \c e, selecting - * the elements matching the specified \c condition. + * the elements matching the specified \c condition. * * @param e the \ref xexpression to filter. * @param condition the filtering \ref xexpression to apply. @@ -548,12 +547,12 @@ namespace xt /** * @brief creates an indexview from a container of indices. - * + * * Returns a 1D view with the elements at \a indices selected. * * @param e the underlying xexpression * @param indices the indices to select - * + * * \code{.cpp} * xarray a = {{1,5,3}, {4,5,6}}; * b = index_view(a, {{0, 0}, {1, 0}, {1, 1}}); @@ -591,7 +590,7 @@ namespace xt /** * @brief creates a view into \a e filtered by \a condition. - * + * * Returns a 1D view with the elements selected where \a condition evaluates to \em true. * This is equivalent to \verbatim{index_view(e, where(condition));}\endverbatim * The returned view is not optimal if you just want to assign a scalar to the filtered diff --git a/include/xtensor/xio.hpp b/include/xtensor/xio.hpp index 5b70d72bb..3ed0231ed 100644 --- a/include/xtensor/xio.hpp +++ b/include/xtensor/xio.hpp @@ -609,13 +609,24 @@ namespace xt static constexpr std::size_t value = 5; }; -// Note: std::min is not constexpr on old versions of gcc (4.x) and clang. + template + struct recursion_depth> + { + static constexpr std::size_t value = 5; + }; + + // Note: std::min is not constexpr on old versions of gcc (4.x) and clang. #define XTENSOR_MIN(x, y) (x > y ? y : x) template struct recursion_depth> { static constexpr std::size_t value = XTENSOR_MIN(5, N); }; + template + struct recursion_depth> + { + static constexpr std::size_t value = XTENSOR_MIN(5, N); + }; #undef XTENSOR_MIN } diff --git a/include/xtensor/xiterable.hpp b/include/xtensor/xiterable.hpp index 7b45f00f8..238d9f65a 100644 --- a/include/xtensor/xiterable.hpp +++ b/include/xtensor/xiterable.hpp @@ -9,6 +9,7 @@ #ifndef XITERABLE_HPP #define XITERABLE_HPP +#include "xconcepts.hpp" #include "xiterator.hpp" namespace xt @@ -35,6 +36,7 @@ namespace xt */ template class xconst_iterable + : public xexpression_tag { public: diff --git a/include/xtensor/xiterator.hpp b/include/xtensor/xiterator.hpp index 02605cb27..3e37a431c 100644 --- a/include/xtensor/xiterator.hpp +++ b/include/xtensor/xiterator.hpp @@ -64,7 +64,7 @@ namespace xt template struct index_type_impl { - using type = std::vector; + using type = dyn_shape; }; template @@ -72,6 +72,12 @@ namespace xt { using type = std::array; }; + + template + struct index_type_impl> + { + using type = tiny_array; + }; } template diff --git a/include/xtensor/xmath.hpp b/include/xtensor/xmath.hpp index 9079d4224..f714c63be 100644 --- a/include/xtensor/xmath.hpp +++ b/include/xtensor/xmath.hpp @@ -17,28 +17,12 @@ #include #include +#include "xmathutil.hpp" #include "xoperation.hpp" #include "xreducer.hpp" namespace xt { - template - struct numeric_constants - { - static constexpr T PI = 3.141592653589793238463; - static constexpr T PI_2 = 1.57079632679489661923; - static constexpr T PI_4 = 0.785398163397448309616; - static constexpr T D_1_PI = 0.318309886183790671538; - static constexpr T D_2_PI = 0.636619772367581343076; - static constexpr T D_2_SQRTPI = 1.12837916709551257390; - static constexpr T SQRT2 = 1.41421356237309504880; - static constexpr T SQRT1_2 = 0.707106781186547524401; - static constexpr T E = 2.71828182845904523536; - static constexpr T LOG2E = 1.44269504088896340736; - static constexpr T LOG10E = 0.434294481903251827651; - static constexpr T LN2 = 0.693147180559945309417; - }; - /*********** * Helpers * ***********/ @@ -185,7 +169,7 @@ namespace xt /** * @ingroup basic_functions * @brief Absolute value function. - * + * * Returns an \ref xfunction for the element-wise absolute value * of \em e. * @param e an \ref xexpression @@ -201,7 +185,7 @@ namespace xt /** * @ingroup basic_functions * @brief Absolute value function. - * + * * Returns an \ref xfunction for the element-wise absolute value * of \em e. * @param e an \ref xexpression @@ -217,7 +201,7 @@ namespace xt /** * @ingroup basic_functions * @brief Remainder of the floating point division operation. - * + * * Returns an \ref xfunction for the element-wise remainder of * the floating point division operation e1 / e2. * @param e1 an \ref xexpression or a scalar @@ -235,7 +219,7 @@ namespace xt /** * @ingroup basic_functions * @brief Signed remainder of the division operation. - * + * * Returns an \ref xfunction for the element-wise signed remainder * of the floating point division operation e1 / e2. * @param e1 an \ref xexpression or a scalar @@ -478,8 +462,8 @@ namespace xt /** * @ingroup basic_functions * @brief Clip values between hi and lo - * - * Returns an \ref xfunction for the element-wise clipped + * + * Returns an \ref xfunction for the element-wise clipped * values between lo and hi * @param e1 an \ref xexpression or a scalar * @param lo a scalar @@ -502,6 +486,7 @@ namespace xt constexpr std::enable_if_t::value, T> sign_impl(T x) { + using namespace cmath; return std::isnan(x) ? std::numeric_limits::quiet_NaN() : x == 0 ? (T)copysign(T(0), x) : (T)copysign(T(1), x); } @@ -690,7 +675,8 @@ namespace xt * @return an \ref xfunction * @note e1 and e2 can't be both scalars. */ - template + template ::value || xexpression_concept::value> > inline auto pow(E1&& e1, E2&& e2) noexcept -> detail::xfunction_type_t { @@ -701,7 +687,7 @@ namespace xt * @ingroup pow_functions * @brief Square root function. * - * Returns an \ref xfunction for the element-wise square + * Returns an \ref xfunction for the element-wise square * root of \em e. * @param e an \ref xexpression * @return an \ref xfunction @@ -1271,7 +1257,8 @@ namespace xt * @param equal_nan if true, isclose returns true if both elements of e1 and e2 are NaN * @return an \ref xfunction */ - template + template ::value || xexpression_concept::value> > inline auto isclose(E1&& e1, E2&& e2, double rtol = 1e-05, double atol = 1e-08, bool equal_nan = false) noexcept { return detail::make_xfunction(std::make_tuple(rtol, atol, equal_nan), @@ -1322,7 +1309,8 @@ namespace xt return reduce(functor_type(), std::forward(e), std::forward(axes)); } - template + template ::value> > inline auto sum(E&& e) noexcept { using functor_type = std::plus::value_type>; @@ -1362,7 +1350,8 @@ namespace xt return reduce(functor_type(), std::forward(e), std::forward(axes)); } - template + template ::value> > inline auto prod(E&& e) noexcept { using functor_type = std::multiplies::value_type>; @@ -1404,7 +1393,8 @@ namespace xt return std::move(s) / value_type(size / s.size()); } - template + template ::value> > inline auto mean(E&& e) noexcept { using value_type = typename std::decay_t::value_type; diff --git a/include/xtensor/xmathutil.hpp b/include/xtensor/xmathutil.hpp new file mode 100644 index 000000000..441c1e4e8 --- /dev/null +++ b/include/xtensor/xmathutil.hpp @@ -0,0 +1,415 @@ +/*************************************************************************** +* Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +/** + * @brief standard mathematical functions for xexpressions + */ + +#ifndef XMATHUTIL_HPP +#define XMATHUTIL_HPP + +#include +#include // std::abs(int) prior to C++ 17 +#include +#include // std::min, std::max +#include + +#include "xconcepts.hpp" + +namespace xt +{ + template + struct numeric_constants + { + static constexpr T PI = 3.141592653589793238463; + static constexpr T PI_2 = 1.57079632679489661923; + static constexpr T PI_4 = 0.785398163397448309616; + static constexpr T D_1_PI = 0.318309886183790671538; + static constexpr T D_2_PI = 0.636619772367581343076; + static constexpr T D_2_SQRTPI = 1.12837916709551257390; + static constexpr T SQRT2 = 1.41421356237309504880; + static constexpr T SQRT1_2 = 0.707106781186547524401; + static constexpr T E = 2.71828182845904523536; + static constexpr T LOG2E = 1.44269504088896340736; + static constexpr T LOG10E = 0.434294481903251827651; + static constexpr T LN2 = 0.693147180559945309417; + }; + + namespace cmath + { + // create a namespace for just the algebraic functions from std + // and add a few fixes to avoid ambiguous overloads in templates + + using std::abs; + using std::fabs; + + using std::cos; + using std::sin; + using std::tan; + using std::acos; + using std::asin; + using std::atan; + + using std::cosh; + using std::sinh; + using std::tanh; + using std::acosh; + using std::asinh; + using std::atanh; + + using std::sqrt; + using std::cbrt; + + using std::exp; + using std::exp2; + using std::expm1; + using std::log; + using std::log2; + using std::log10; + using std::log1p; + using std::logb; + using std::ilogb; + + using std::floor; + using std::ceil; + using std::trunc; + using std::round; + using std::lround; + using std::llround; + + using std::erf; + using std::erfc; + using std::erfc; + using std::tgamma; + using std::lgamma; + + using std::conj; + using std::real; + using std::imag; + using std::arg; + + using std::atan2; + using std::copysign; + using std::fdim; + using std::fmax; + using std::fmin; + using std::fmod; + using std::hypot; + using std::pow; + + // add missing abs() overloads for unsigned types + #define XTENSOR_DEFINE_UNSIGNED_ABS(T) \ + inline T abs(T t) { return t; } + + XTENSOR_DEFINE_UNSIGNED_ABS(bool) + XTENSOR_DEFINE_UNSIGNED_ABS(unsigned char) + XTENSOR_DEFINE_UNSIGNED_ABS(unsigned short) + XTENSOR_DEFINE_UNSIGNED_ABS(unsigned int) + XTENSOR_DEFINE_UNSIGNED_ABS(unsigned long) + XTENSOR_DEFINE_UNSIGNED_ABS(unsigned long long) + + #undef XTENSOR_DEFINE_UNSIGNED_ABS + + // add missing floor() and ceil() overloads for integral types + #define XTENSOR_DEFINE_INTEGER_FLOOR_CEIL(T) \ + inline T floor(signed T t) { return t; } \ + inline T floor(unsigned T t) { return t; } \ + inline T ceil(signed T t) { return t; } \ + inline T ceil(unsigned T t) { return t; } + + XTENSOR_DEFINE_INTEGER_FLOOR_CEIL(char) + XTENSOR_DEFINE_INTEGER_FLOOR_CEIL(short) + XTENSOR_DEFINE_INTEGER_FLOOR_CEIL(int) + XTENSOR_DEFINE_INTEGER_FLOOR_CEIL(long) + XTENSOR_DEFINE_INTEGER_FLOOR_CEIL(long long) + + #undef XTENSOR_DEFINE_INTEGER_FLOOR_CEIL + + // support 'double' exponents for all floating-point versions of pow() + inline float pow(float v, double e) + { + return std::pow(v, (float)e); + } + + inline long double pow(long double v, double e) + { + return std::pow(v, (long double)e); + } + } // namespace cmath + + + /**********************************************************/ + /* */ + /* isclose() */ + /* */ + /**********************************************************/ + + template >::value> > + inline bool + isclose(U u, V v, double rtol = 1e-05, double atol = 1e-08, bool equal_nan = false) + { + using namespace cmath; + using P = promote_t; + P a = static_cast

(u), + b = static_cast

(v); + + if(std::isnan(a) && std::isnan(b)) + return equal_nan; + if(std::isinf(a) && std::isinf(b)) + return std::signbit(a) == std::signbit(b); + P d = abs(a - b); + return d <= atol || d <= rtol * std::max(abs(a), abs(b)); + } + + /**********************************************************/ + /* */ + /* sq() */ + /* */ + /**********************************************************/ + + /** \brief The square function. + + sq(x) = x*x is needed so often that it makes sense to define it as a function. + */ + template ::value> > + inline auto + sq(T t) + { + return t*t; + } + + /**********************************************************/ + /* */ + /* min() */ + /* */ + /**********************************************************/ + + /** \brief A proper minimum function. + + The std::min template matches everything -- this is way too + greedy to be useful. xtensor implements the basic min function + only for arithmetic types and provides explicit overloads for everything + else. Moreover, xtensor's min function also computes the minimum + between two different types, as long as they have a std::common_type. + */ + template ::value && std::is_arithmetic::value> > + inline std::common_type_t + min(T1 const & t1, T2 const & t2) + { + using T = std::common_type_t; + return std::min(static_cast(t1), static_cast(t2)); + } + + template ::value> > + inline T const & + min(T const & t1, T const & t2) + { + return std::min(t1, t2); + } + + /**********************************************************/ + /* */ + /* max() */ + /* */ + /**********************************************************/ + + /** \brief A proper maximum function. + + The std::max template matches everything -- this is way too + greedy to be useful. xtensor implements the basic max function + only for arithmetic types and provides explicit overloads for everything + else. Moreover, xtensor's max function also computes the maximum + between two different types, as long as they have a std::common_type. + */ + template ::value && std::is_arithmetic::value> > + inline std::common_type_t + max(T1 const & t1, T2 const & t2) + { + using T = std::common_type_t; + return std::max(static_cast(t1), static_cast(t2)); + } + + template ::value> > + inline T const & + max(T const & t1, T const & t2) + { + return std::max(t1, t2); + } + + /**********************************************************/ + /* */ + /* dot() */ + /* */ + /**********************************************************/ + + // scalar dot is needed for generic functions that should work with + // scalars and vectors alike + #define XTENSOR_DEFINE_SCALAR_DOT(T) \ + inline auto dot(T l, T r) { return l*r; } + + XTENSOR_DEFINE_SCALAR_DOT(unsigned char) + XTENSOR_DEFINE_SCALAR_DOT(unsigned short) + XTENSOR_DEFINE_SCALAR_DOT(unsigned int) + XTENSOR_DEFINE_SCALAR_DOT(unsigned long) + XTENSOR_DEFINE_SCALAR_DOT(unsigned long long) + XTENSOR_DEFINE_SCALAR_DOT(signed char) + XTENSOR_DEFINE_SCALAR_DOT(signed short) + XTENSOR_DEFINE_SCALAR_DOT(signed int) + XTENSOR_DEFINE_SCALAR_DOT(signed long) + XTENSOR_DEFINE_SCALAR_DOT(signed long long) + XTENSOR_DEFINE_SCALAR_DOT(float) + XTENSOR_DEFINE_SCALAR_DOT(double) + XTENSOR_DEFINE_SCALAR_DOT(long double) + + #undef XTENSOR_DEFINE_SCALAR_DOT + + /**********************************************************/ + /* */ + /* even(), odd() */ + /* */ + /**********************************************************/ + + /** \brief Check if an integer is even. + */ + template ::value> > + inline bool + even(T const t) + { + using UT = typename std::make_unsigned::type; + return (static_cast(t)&1) == 0; + } + + /** \brief Check if an integer is odd. + */ + template ::value> > + inline bool + odd(T const t) + { + using UT = typename std::make_unsigned::type; + return (static_cast(t)&1) != 0; + } + + /**********************************************************/ + /* */ + /* sin_pi(), cos_pi() */ + /* */ + /**********************************************************/ + + /** \brief sin(pi*x). + + Essentially calls std::sin(PI*x) but uses a more accurate implementation + to make sure that sin_pi(1.0) == 0.0 (which does not hold for + std::sin(PI) due to round-off error), and sin_pi(0.5) == 1.0. + */ + template ::value> > + REAL sin_pi(REAL x) + { + if(x < 0.0) + return -sin_pi(-x); + if(x < 0.5) + return std::sin(numeric_constants::PI * x); + + bool invert = false; + if(x < 1.0) + { + invert = true; + x = -x; + } + + REAL rem = std::floor(x); + if(odd((int)rem)) + invert = !invert; + rem = x - rem; + if(rem > 0.5) + rem = 1.0 - rem; + if(rem == 0.5) + rem = REAL(1); + else + rem = std::sin(numeric_constants::PI * rem); + return invert + ? -rem + : rem; + } + + /** \brief cos(pi*x). + + Essentially calls std::cos(PI*x) but uses a more accurate implementation + to make sure that cos_pi(1.0) == -1.0 and cos_pi(0.5) == 0.0. + */ + template ::value> > + REAL cos_pi(REAL x) + { + return sin_pi(x+0.5); + } + + /**********************************************************/ + /* */ + /* norm() */ + /* */ + /**********************************************************/ + + /** \brief The norm of a numerical object. + + For scalar types: implemented as abs(t)
+ otherwise: implemented as sqrt(squared_norm(t)). + */ + template + inline auto norm(T const & t) + { + using cmath::sqrt; + return sqrt(squared_norm(t)); + } + + /**********************************************************/ + /* */ + /* squared_norm() */ + /* */ + /**********************************************************/ + + template + inline auto + squared_norm(std::complex const & t) + { + return sq(t.real()) + sq(t.imag()); + } + + #define XTENSOR_DEFINE_NORM(T) \ + inline auto squared_norm(T t) { return sq(t); } \ + inline auto mean_square(T t) { return sq(t); } \ + inline auto elementwise_squared_norm(T t) { return sq(t); } \ + inline auto norm(T t) { return cmath::abs(t); } \ + inline auto elementwise_norm(T t) { return cmath::abs(t); } + + XTENSOR_DEFINE_NORM(signed char) + XTENSOR_DEFINE_NORM(unsigned char) + XTENSOR_DEFINE_NORM(short) + XTENSOR_DEFINE_NORM(unsigned short) + XTENSOR_DEFINE_NORM(int) + XTENSOR_DEFINE_NORM(unsigned int) + XTENSOR_DEFINE_NORM(long) + XTENSOR_DEFINE_NORM(unsigned long) + XTENSOR_DEFINE_NORM(long long) + XTENSOR_DEFINE_NORM(unsigned long long) + XTENSOR_DEFINE_NORM(float) + XTENSOR_DEFINE_NORM(double) + XTENSOR_DEFINE_NORM(long double) + + #undef XTENSOR_DEFINE_NORM +} + +#endif diff --git a/include/xtensor/xoperation.hpp b/include/xtensor/xoperation.hpp index a1ca7dfa3..495fa7c62 100644 --- a/include/xtensor/xoperation.hpp +++ b/include/xtensor/xoperation.hpp @@ -13,6 +13,7 @@ #include #include +#include "xconcepts.hpp" #include "xfunction.hpp" #include "xscalar.hpp" #include "xstrides.hpp" @@ -409,7 +410,7 @@ namespace xt /** * @ingroup logical_operators * @brief return vector of indices where T is not zero - * + * * @param arr input array * @return vector of \a index_types where arr is not equal to zero */ @@ -457,7 +458,7 @@ namespace xt * @ingroup logical_operators * @brief return vector of indices where condition is true * (equivalent to \a nonzero(condition)) - * + * * @param condition input array * @return vector of \a index_types where condition is not equal to zero */ @@ -477,7 +478,8 @@ namespace xt * @param e an \ref xexpression * @return a boolean */ - template + template ::value> > inline bool any(E&& e) { using xtype = std::decay_t; @@ -502,7 +504,8 @@ namespace xt * @param e an \ref xexpression * @return a boolean */ - template + template ::value> > inline bool all(E&& e) { using xtype = std::decay_t; diff --git a/include/xtensor/xreducer.hpp b/include/xtensor/xreducer.hpp index 204135d06..a3ef999de 100644 --- a/include/xtensor/xreducer.hpp +++ b/include/xtensor/xreducer.hpp @@ -126,6 +126,7 @@ namespace xt const inner_shape_type& shape() const noexcept; layout_type layout() const noexcept; + const_reference operator()() const; template const_reference operator()(Args... args) const; const_reference operator[](const xindex& index) const; @@ -192,7 +193,7 @@ namespace xt template inline auto reduce(F&& f, E&& e, std::initializer_list axes) noexcept { - using axes_type = std::vector::size_type>; + using axes_type = dyn_shape::size_type>; using reducer_type = xreducer, axes_type>; return reducer_type(std::forward(f), std::forward(e), forward_sequence(axes)); } @@ -200,7 +201,7 @@ namespace xt template inline auto reduce(F&& f, E&& e, const I (&axes)[N]) noexcept { - using axes_type = std::array::size_type, N>; + using axes_type = stat_shape::size_type, N>; using reducer_type = xreducer, axes_type>; return reducer_type(std::forward(f), std::forward(e), forward_sequence(axes)); } @@ -281,6 +282,14 @@ namespace xt using type = std::array; }; + template + struct xreducer_shape_type, tiny_array> + { + using type = std::conditional_t<(N1 > 0 && N2 > 0 && N1 > N2), + tiny_array, + tiny_array>; + }; + namespace detail { template @@ -397,10 +406,19 @@ namespace xt template inline auto xreducer::operator()(Args... args) const -> const_reference { - std::array arg_array = {{static_cast(args)...}}; + stat_shape arg_array = {{static_cast(args)...}}; return element(arg_array.cbegin(), arg_array.cend()); } + // FIXME: what does it mean if a reducer is called with zero arguments? + template + inline auto xreducer::operator()() const -> const_reference + { + stat_shape arg_array; + // call element() with empty range + return element(arg_array.cbegin(), arg_array.cbegin()); + } + /** * Returns a constant reference to the element at the specified position in the reducer. * @param index a sequence of indices specifying the position in the reducer. Indices diff --git a/include/xtensor/xscalar.hpp b/include/xtensor/xscalar.hpp index 39650ae0e..aff780983 100644 --- a/include/xtensor/xscalar.hpp +++ b/include/xtensor/xscalar.hpp @@ -469,7 +469,7 @@ namespace xt { return m_value; } - + template inline auto xscalar::data_element(size_type) const noexcept->const_reference { diff --git a/include/xtensor/xstridedview.hpp b/include/xtensor/xstridedview.hpp index 7f11a7ec1..b4e87f70c 100644 --- a/include/xtensor/xstridedview.hpp +++ b/include/xtensor/xstridedview.hpp @@ -55,12 +55,12 @@ namespace xt * @class xstrided_view * @brief View of an xexpression using strides * - * The xstrided_view class implements a view utilizing an offset and strides - * into a multidimensional xcontainer. The xstridedview is currently used + * The xstrided_view class implements a view utilizing an offset and strides + * into a multidimensional xcontainer. The xstridedview is currently used * to implement `transpose`. * @tparam CT the closure type of the \ref xexpression type underlying this view * @tparam CD the closure type of the underlying data container - * + * * @sa stridedview, transpose */ template @@ -199,8 +199,8 @@ namespace xt */ //@{ /** - * Constructs an xstrided_view - * + * Constructs an xstrided_view + * * @param e the underlying xexpression for this view * @param shape the shape of the view * @param strides the strides of the view @@ -357,8 +357,8 @@ namespace xt } /** - * Returns the element at the specified position in the xstrided_view. - * + * Returns the element at the specified position in the xstrided_view. + * * @param args a list of indices specifying the position in the view. Indices * must be unsigned integers, the number of indices should be equal or greater than * the number of dimensions of the view. @@ -722,7 +722,7 @@ namespace xt } template - inline slice_vector(const std::vector& shape, Args... args) + inline slice_vector(const dyn_shape& shape, Args... args) { m_shape = shape; append(args...); @@ -781,7 +781,7 @@ namespace xt private: - std::vector m_shape; + dyn_shape m_shape; std::size_t newaxis_count = 0; }; @@ -820,7 +820,7 @@ namespace xt template >::value>* = nullptr> inline auto get_strides(E&& e) { - std::vector strides; + dyn_shape strides; strides.resize(e.shape().size()); compute_strides(e.shape(), layout_type::row_major, strides); return strides; @@ -850,7 +850,7 @@ namespace xt // Compute strided view std::size_t offset = detail::get_offset(e); - using shape_type = typename std::vector; + using shape_type = dyn_shape; shape_type new_shape(dimension); shape_type new_strides(dimension); diff --git a/include/xtensor/xstrides.hpp b/include/xtensor/xstrides.hpp index a9e03a720..08ee167eb 100644 --- a/include/xtensor/xstrides.hpp +++ b/include/xtensor/xstrides.hpp @@ -120,7 +120,7 @@ namespace xt template inline size_type element_offset(const S& strides, It first, It last) noexcept { - auto size = std::min(static_cast(std::distance(first, last)), strides.size()); + auto size = min(static_cast(std::distance(first, last)), strides.size()); return std::inner_product(last - size, last, strides.cend() - size, size_type(0)); } diff --git a/include/xtensor/xtags.hpp b/include/xtensor/xtags.hpp new file mode 100644 index 000000000..9c7d2a185 --- /dev/null +++ b/include/xtensor/xtags.hpp @@ -0,0 +1,69 @@ +/*************************************************************************** +* Copyright (c) 2017, Ullrich Koethe * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTAGS_HPP +#define XTAGS_HPP + +/**********************************************************/ +/* */ +/* helper for keyword arguments and related stuff */ +/* */ +/**********************************************************/ + +namespace xt +{ + + /// Determine size of an array type at runtime. +static const int runtime_size = -1; + + /// Don't initialize memory that gets overwritten anyway. +enum skip_initialization_tag { dont_init }; + + /// Copy-construct array in reversed order. +enum reverse_copy_tag { copy_reversed }; + +namespace tags { + +/********************************************************/ +/* */ +/* tags::size */ +/* */ +/********************************************************/ + + // Support for tags::size keyword argument + // to disambiguate array sizes from initial values. +struct size_proxy +{ + size_t value; +}; + +struct size_tag +{ + size_proxy operator=(size_t s) const + { + return {s}; + } + + size_proxy operator()(size_t s) const + { + return {s}; + } +}; + +namespace { + +size_tag size; + +} + +} // namespace tags + + +} // namespace xt + +#endif // XTAGS_HPP diff --git a/include/xtensor/xtensor.hpp b/include/xtensor/xtensor.hpp index f75fae70b..61e305d4c 100644 --- a/include/xtensor/xtensor.hpp +++ b/include/xtensor/xtensor.hpp @@ -18,6 +18,7 @@ #include "xbuffer_adaptor.hpp" #include "xcontainer.hpp" #include "xsemantic.hpp" +#include "xtiny.hpp" namespace xt { @@ -30,7 +31,7 @@ namespace xt struct xcontainer_inner_types> { using container_type = EC; - using shape_type = std::array; + using shape_type = stat_shape; using strides_type = shape_type; using backstrides_type = shape_type; using inner_shape_type = shape_type; @@ -125,7 +126,7 @@ namespace xt struct xcontainer_inner_types> { using container_type = EC; - using shape_type = std::array; + using shape_type = stat_shape; using strides_type = shape_type; using backstrides_type = shape_type; using inner_shape_type = shape_type; diff --git a/include/xtensor/xtensor_config.hpp b/include/xtensor/xtensor_config.hpp index 72af5dca3..4ae57bb81 100644 --- a/include/xtensor/xtensor_config.hpp +++ b/include/xtensor/xtensor_config.hpp @@ -28,7 +28,7 @@ #ifndef DEFAULT_SHAPE_CONTAINER #define DEFAULT_SHAPE_CONTAINER(T, EA, SA) \ - std::vector + dyn_shape #endif #ifndef DEFAULT_LAYOUT diff --git a/include/xtensor/xtensor_forward.hpp b/include/xtensor/xtensor_forward.hpp index f14a06dde..38d9eafbe 100644 --- a/include/xtensor/xtensor_forward.hpp +++ b/include/xtensor/xtensor_forward.hpp @@ -41,7 +41,7 @@ namespace xt * \code{.cpp} * xt::xarray_container, std::vector> a = ... * \endcode - * + * * @tparam T The value type of the elements. * @tparam L The layout_type of the xarray_container (default: row_major). * @tparam A The allocator of the container holding the elements. @@ -49,7 +49,7 @@ namespace xt */ template , - class SA = std::allocator::size_type>> + class SA = std::allocator::size_type>> using xarray = xarray_container; template @@ -97,7 +97,7 @@ namespace xt template , class BA = std::allocator, - class SA = std::allocator::size_type>> + class SA = std::allocator::size_type>> using xarray_optional = xarray_container, L, DEFAULT_SHAPE_CONTAINER(T, A, SA)>; /** diff --git a/include/xtensor/xtiny.hpp b/include/xtensor/xtiny.hpp new file mode 100644 index 000000000..1c54f77aa --- /dev/null +++ b/include/xtensor/xtiny.hpp @@ -0,0 +1,2966 @@ +/*************************************************************************** +* Copyright (c) 2017, Ullrich Koethe * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_XTINY_HPP +#define XTENSOR_XTINY_HPP + +#include +#include +#include +#include +#include + +#include "xtags.hpp" +#include "xconcepts.hpp" +#include "xexception.hpp" +#include "xmathutil.hpp" + +#ifdef XTENSOR_CHECK_BOUNDS + #define XTENSOR_ASSERT_INSIDE(array, diff) \ + xtensor_precondition(diff >= 0 && diff < array.size(), "Index out of bounds") +#else + #define XTENSOR_ASSERT_INSIDE(array, diff) +#endif + +namespace xt { + + /** \brief The general type of array indices. + + Note that this is a signed type, so that negative indices + and index differences work as intuitively expected. + */ +using index_t = std::ptrdiff_t; + +template +class tiny_array; + +template +class tiny_array_view; + +namespace detail { + +template +struct may_use_uninitialized_memory +{ + static const bool value = std::is_scalar::value || std::is_pod::value; +}; + +template +struct may_use_uninitialized_memory> +{ + static const bool value = may_use_uninitialized_memory::value; +}; + +template +struct may_use_uninitialized_memory> +{ + static const bool value = false; +}; + +template +struct tiny_shape_helper; + +template +struct tiny_shape_helper +{ + static_assert(N >= 0, "tiny_array_base(): array must have non-negative shape."); + using next_type = tiny_shape_helper; + + static const index_t level = LEVEL; + static const index_t stride = next_type::total_size; + static const index_t total_size = N * stride; + static const index_t alloc_size = total_size; + + static index_t offset(index_t const * coord) + { + return stride*coord[level] + next_type::offset(coord); + } + + template + static index_t offset(index_t i, V...rest) + { + return stride*i + next_type::offset(rest...); + } +}; + +template +struct tiny_shape_helper +{ + static_assert(N >= 0, "tiny_array_base(): array must have non-negative shape."); + static const index_t level = LEVEL; + static const index_t stride = 1; + static const index_t total_size = N; + static const index_t alloc_size = total_size; + + static index_t offset(index_t const * coord) + { + return coord[level]; + } + + static index_t offset(index_t i) + { + return i; + } +}; + +template +struct tiny_shape_helper +{ + static const index_t level = LEVEL; + static const index_t stride = 1; + static const index_t total_size = 0; + static const index_t alloc_size = 1; + + static index_t offset(index_t const * coord) + { + return coord[level]; + } + + static index_t offset(index_t i) + { + return i; + } +}; + +template +struct tiny_size_helper +{ + static const index_t value = tiny_shape_helper<0, N...>::total_size; + static const index_t ndim = sizeof...(N); +}; + +template +struct tiny_array_is_static +{ + static const int ndim = sizeof...(N)+1; + static const bool value = ndim > 1 || N0 != runtime_size; +}; + +} // namespace detail + +#define XTENSOR_ASSERT_RUNTIME_SIZE(SHAPE, PREDICATE, MESSAGE) \ + if(detail::tiny_array_is_static::value) {} else \ + xtensor_precondition(PREDICATE, MESSAGE) + +/********************************************************/ +/* */ +/* tiny_array_base */ +/* */ +/********************************************************/ + +/** \brief Base class for fixed size vectors and matrices. + + This class contains functionality shared by + \ref tiny_array and \ref tiny_array_view, and enables these classes + to be freely mixed within expressions. It is typically not used directly. + + \#include \
+ Namespace: vigra +**/ +template +class tiny_array_base +: public tiny_array_tag +{ + protected: + using shape_helper = detail::tiny_shape_helper<0, N...>; + + static const bool derived_is_view = !std::is_same >::value; + using data_array_type = typename std::conditional::type; + + template + void init_impl(VALUETYPE v1, V2... v2) + { + data_[LEVEL] = v1; + init_impl(v2...); + } + + template + void init_impl(VALUETYPE v1) + { + data_[LEVEL] = v1; + } + + template ::value>> + void init_impl(ITERATOR i) + { + for(index_t k=0; k < static_size; ++k, ++i) + data_[k] = static_cast(*i); + } + + public: + + template + using as_type = tiny_array; + + using value_type = VALUETYPE; + using const_value_type = typename std::add_const::type; + using reference = value_type &; + using const_reference = const_value_type &; + using pointer = value_type *; + using const_pointer = const_value_type *; + using iterator = value_type *; + using const_iterator = const_value_type *; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using index_type = tiny_array; + + static const index_t static_ndim = sizeof...(N); + static const index_t static_size = shape_helper::total_size; + static const bool may_use_uninitialized_memory = + detail::may_use_uninitialized_memory::value; + + // constructors + + constexpr tiny_array_base(tiny_array_base const &) = default; + + protected: + + tiny_array_base(skip_initialization_tag) + {} + + // constructors to be used by tiny_array + + template + tiny_array_base(tiny_array_base const & other) + { + xtensor_precondition(size() == other.size(), + "tiny_array_base(): shape mismatch."); + for(int i=0; i(other[i]); + } + + // constructor for zero or one argument + explicit tiny_array_base(value_type v = value_type()) + { + for(int i=0; i + // constexpr tiny_array_base(value_type v0, value_type v1, V ... v) + // : data_{VALUETYPE(v0), VALUETYPE(v1), VALUETYPE(v)...} + // { + // static_assert(sizeof...(V)+2 == static_size, + // "tiny_array_base(): number of constructor arguments contradicts size()."); + // } + + template + explicit tiny_array_base(U const * u, U const * /* end */ = 0) + { + for(int i=0; i(u[i]); + } + + template + tiny_array_base(U const * u, reverse_copy_tag) + { + for(int i=0; i(u[static_size-1-i]); + } + + // for compatibility with tiny_array_base<..., runtime_size> + template + tiny_array_base(U const * u, U const * /* end */, reverse_copy_tag) + : tiny_array_base(u, copy_reversed) + {} + + template ::value> > + tiny_array_base(U u, U /* end */) + { + for(int i=0; i(*u); + } + + public: + + // assignment + + tiny_array_base & operator=(tiny_array_base const &) = default; + + tiny_array_base & operator=(value_type v) + { + for(int i=0; i + tiny_array_base & operator=(tiny_array_base const & other) + { + for(int i=0; i(other[i]); + return *this; + } + + template + tiny_array_base & operator=(tiny_array_base const & other) + { + xtensor_precondition(size() == other.size(), + "tiny_array_base::operator=(): size mismatch."); + for(int i=0; i(other[i]); + return *this; + } + + template + constexpr bool + is_same_shape(tiny_array_base const &) const + { + return false; + } + + template + constexpr bool + is_same_shape(tiny_array_base const &) const + { + return true; + } + + template + bool + is_same_shape(tiny_array_base const & other) const + { + return sizeof...(N) == 1 && size() == other.size(); + } + + DERIVED & init(value_type v = value_type()) + { + for(int i=0; i(*this); + } + + template + DERIVED & init(value_type v0, value_type v1, V... v) + { + static_assert(sizeof...(V)+2 == static_size, + "tiny_array_base::init(): wrong number of arguments."); + init_impl<0>(v0, v1, v...); + return static_cast(*this); + } + + template + DERIVED & init(Iterator first, Iterator end) + { + index_t range = std::distance(first, end); + if(static_size < range) + range = static_size; + for(index_t i=0; i(*first); + return static_cast(*this); + } + + // index access + + reference operator[](index_t i) + { + return data_[i]; + } + + constexpr const_reference operator[](index_t i) const + { + return data_[i]; + } + + reference at(index_t i) + { + if(i < 0 || i >= static_size) + throw std::out_of_range("tiny_array_base::at()"); + return data_[i]; + } + + const_reference at(index_t i) const + { + if(i < 0 || i >= static_size) + throw std::out_of_range("tiny_array_base::at()"); + return data_[i]; + } + + reference operator[](index_t const (&i)[static_ndim]) + { + return data_[shape_helper::offset(i)]; + } + + constexpr const_reference operator[](index_t const (&i)[static_ndim]) const + { + return data_[shape_helper::offset(i)]; + } + + reference at(index_t const (&i)[static_ndim]) + { + return at(shape_helper::offset(i)); + } + + const_reference at(index_t const (&i)[static_ndim]) const + { + return at(shape_helper::offset(i)); + } + + reference operator[](index_type const & i) + { + return data_[shape_helper::offset(i.data())]; + } + + constexpr const_reference operator[](index_type const & i) const + { + return data_[shape_helper::offset(i.data())]; + } + + reference at(index_type const & i) + { + return at(shape_helper::offset(i.data())); + } + + const_reference at(index_type const & i) const + { + return at(shape_helper::offset(i.data())); + } + + template + reference operator()(V...v) + { + static_assert(sizeof...(V) == static_ndim, + "tiny_array_base::operator(): wrong number of arguments."); + return data_[shape_helper::offset(v...)]; + } + + template + constexpr const_reference operator()(V...v) const + { + static_assert(sizeof...(V) == static_ndim, + "tiny_array_base::operator(): wrong number of arguments."); + return data_[shape_helper::offset(v...)]; + } + + /** Get a view to the subarray with length (TO-FROM) starting at FROM. + The bounds must fullfill 0 <= FROM < TO <= static_size. + Only available if static_ndim == 1. + */ + template + tiny_array_view + subarray() const + { + static_assert(sizeof...(N) == 1, + "tiny_array_base::subarray(): array must be 1-dimensional."); + static_assert(FROM >= 0 && FROM < TO && TO <= static_size, + "tiny_array_base::subarray(): range out of bounds."); + return tiny_array_view(const_cast(data_)+FROM); + } + + tiny_array_view + subarray(index_t FROM, index_t TO) const + { + xtensor_precondition(FROM >= 0 && FROM < TO && TO <= static_size, + "tiny_array_base::subarray(): range out of bounds."); + return tiny_array_view(TO-FROM, const_cast(data_)+FROM); + } + + template + tiny_array + erase(index_t m) const + { + static_assert(sizeof...(N) == 1, + "tiny_array_base::erase(): array must be 1-dimensional."); + xtensor_precondition(m >= 0 && m < static_size, "tiny_array::erase(): " + "Index "+std::to_string(m)+" out of bounds [0, "+std::to_string(size())+")."); + tiny_array res(static_size-1, dont_init); + for(int k=0; k + tiny_array + pop_front() const + { + static_assert(sizeof...(N) == 1, + "tiny_array_base::pop_front(): array must be 1-dimensional."); + return erase(0); + } + + template + tiny_array + pop_back() const + { + static_assert(sizeof...(N) == 1, + "tiny_array_base::pop_back(): array must be 1-dimensional."); + return erase(size()-1); + } + + template + tiny_array + insert(index_t m, value_type v) const + { + static_assert(sizeof...(N) == 1, + "tiny_array_base::insert(): array must be 1-dimensional."); + xtensor_precondition(m >= 0 && m <= static_size, "tiny_array::insert(): " + "Index "+std::to_string(m)+" out of bounds [0, "+std::to_string(size())+"]."); + tiny_array res(dont_init); + for(int k=0; k + inline + tiny_array + transpose(tiny_array_base const & permutation) const + { + static_assert(sizeof...(N) == 1, + "tiny_array::transpose(): only allowed for 1-dimensional arrays."); + static_assert(M == static_size || M == runtime_size, + "tiny_array::transpose(): size mismatch."); + XTENSOR_ASSERT_RUNTIME_SIZE(M, size() == 0 || size() == permutation.size(), + "tiny_array::transpose(): size mismatch."); + tiny_array res(dont_init); + for(int k=0; k < size(); ++k) + { + XTENSOR_ASSERT_MSG(permutation[k] >= 0 && permutation[k] < size(), + "transpose(): Permutation index out of bounds"); + res[k] = (*this)[permutation[k]]; + } + return res; + } + + // boiler plate + + iterator begin() { return data_; } + iterator end() { return data_ + static_size; } + const_iterator begin() const { return data_; } + const_iterator end() const { return data_ + static_size; } + const_iterator cbegin() const { return data_; } + const_iterator cend() const { return data_ + static_size; } + + reverse_iterator rbegin() { return reverse_iterator(data_ + static_size); } + reverse_iterator rend() { return reverse_iterator(data_); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(data_ + static_size); } + const_reverse_iterator rend() const { return const_reverse_iterator(data_); } + const_reverse_iterator crbegin() const { return const_reverse_iterator(data_ + static_size); } + const_reverse_iterator crend() const { return const_reverse_iterator(data_); } + + pointer data() { return data_; } + const_pointer data() const { return data_; } + + reference front() { return data_[0]; } + reference back() { return data_[static_size-1]; } + constexpr const_reference front() const { return data_[0]; } + constexpr const_reference back() const { return data_[static_size-1]; } + + constexpr bool empty() const { return static_size == 0; } + constexpr index_t size() const { return static_size; } + constexpr index_t max_size() const { return static_size; } + constexpr index_type shape() const { return index_type{ N... }; } + constexpr index_t ndim() const { return static_ndim; } + + tiny_array_base & reverse() + { + using std::swap; + index_t i=0, j=size()-1; + while(i < j) + swap(data_[i++], data_[j--]); + return *this; + } + + void swap(tiny_array_base & other) + { + using std::swap; + for(int k=0; k + void swap(tiny_array_base & other) + { + for(int k=0; k t = data_[k]; + data_[k] = static_cast(other[k]); + other[k] = static_cast(t); + } + } + + /// factory function for fixed-size unit matrix + template + static inline + tiny_array + eye() + { + tiny_array res; + for(int k=0; k + static inline + tiny_array + unit_vector(index_t k) + { + tiny_array res; + res(k) = 1; + return res; + } + + /// factory function for the k-th unit vector + // (for compatibility with tiny_array<..., runtime_size>) + static inline + tiny_array + unit_vector(tags::size_proxy const & size, index_t k) + { + XTENSOR_ASSERT_MSG(size.value == static_size, + "tiny_array::unit_vector(): size mismatch."); + tiny_array res; + res(k) = 1; + return res; + } + + /// factory function for fixed-size linear sequence starting at start with stepsize step + static inline + tiny_array + linear_sequence(value_type start = value_type(), value_type step = value_type(1)) + { + tiny_array res(dont_init); + for(index_t k=0; k < static_size; ++k, start += step) + res[k] = start; + return res; + } + + /// factory function for fixed-size linear sequence ending at end-1 + static inline + tiny_array + range(value_type end) + { + value_type start = end - static_cast(static_size); + tiny_array res(dont_init); + for(index_t k=0; k < static_size; ++k, ++start) + res[k] = start; + return res; + } + + protected: + data_array_type data_; +}; + +/********************************************************/ +/* */ +/* tiny_array_base output */ +/* */ +/********************************************************/ + +template +std::ostream & operator<<(std::ostream & o, tiny_array_base const & v) +{ + o << "{"; + if(v.size() > 0) + o << promote_t(v[0]); + for(int i=1; i < v.size(); ++i) + o << ", " << promote_t(v[i]); + o << "}"; + return o; +} + +template +std::ostream & operator<<(std::ostream & o, tiny_array_base const & v) +{ + o << "{"; + for(int i=0; N2>0 && i 0) + o << ",\n "; + o << promote_t(v(i,0)); + for(int j=1; j(v(i, j)); + } + } + o << "}"; + return o; +} + +/********************************************************/ +/* */ +/* tiny_array_base<..., runtime_size> */ +/* */ +/********************************************************/ + +/** \brief Specialization of tiny_array_base for dynamic arrays. + + This class contains functionality shared by + \ref tiny_array and \ref tiny_array_view, and enables these classes + to be freely mixed within expressions. It is typically not used directly. + + \#include \
+ Namespace: vigra +**/ +template +class tiny_array_base +: public tiny_array_tag +{ + public: + + template + using as_type = tiny_array; + + using value_type = VALUETYPE; + using const_value_type = typename std::add_const::type; + using reference = value_type &; + using const_reference = const_value_type &; + using pointer = value_type *; + using const_pointer = const_value_type *; + using iterator = value_type *; + using const_iterator = const_value_type *; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using index_type = index_t; + + static const index_t static_size = runtime_size; + static const index_t static_ndim = 1; + static const bool may_use_uninitialized_memory = + detail::may_use_uninitialized_memory::value; + + protected: + + template + void init_impl(VALUETYPE v1, V2... v2) + { + data_[LEVEL] = v1; + init_impl(v2...); + } + + template + void init_impl(VALUETYPE v1) + { + data_[LEVEL] = v1; + } + + public: + + tiny_array_base(index_t size=0, pointer data=0) + : size_(size) + , data_(data) + {} + + tiny_array_base(tiny_array_base const &) = default; + + // assignment + + tiny_array_base & operator=(value_type v) + { + for(int i=0; i + tiny_array_base & operator=(tiny_array_base const & other) + { + xtensor_precondition(size_ == other.size(), + "tiny_array_base::operator=(): size mismatch."); + for(int i=0; i(other[i]); + return *this; + } + + template + bool + is_same_shape(tiny_array_base const & other) const + { + return sizeof...(M) == 1 && size() == other.size();; + } + + template + bool + is_same_shape(tiny_array_base const & other) const + { + return size() == other.size(); + } + + DERIVED & init(value_type v = value_type()) + { + for(int i=0; i(*this); + } + + template + DERIVED & init(value_type v0, value_type v1, V... v) + { + xtensor_precondition(sizeof...(V)+2 == size_, + "tiny_array_base::init(): wrong number of arguments."); + init_impl<0>(v0, v1, v...); + return static_cast(*this); + } + + template + DERIVED & init(Iterator first, Iterator end) + { + index_t range = std::distance(first, end); + if(size_ < range) + range = size_; + for(index_t i=0; i(*first); + return static_cast(*this); + } + + template + DERIVED & init(std::initializer_list l) + { + return init(l.begin(), l.end()); + } + + // index access + + reference operator[](index_t i) + { + return data_[i]; + } + + constexpr const_reference operator[](index_t i) const + { + return data_[i]; + } + + reference at(index_t i) + { + if(i < 0 || i >= size_) + throw std::out_of_range("tiny_array_base::at()"); + return data_[i]; + } + + const_reference at(index_t i) const + { + if(i < 0 || i >= size_) + throw std::out_of_range("tiny_array_base::at()"); + return data_[i]; + } + + /** Get a view to the subarray with length (TO-FROM) starting at FROM. + The bounds must fullfill 0 <= FROM < TO <= size_. + */ + template + tiny_array_view + subarray() const + { + static_assert(FROM >= 0 && FROM < TO, + "tiny_array_base::subarray(): range out of bounds."); + xtensor_precondition(TO <= size_, + "tiny_array_base::subarray(): range out of bounds."); + return tiny_array_view(data_+FROM); + } + + /** Get a view to the subarray with length (TO-FROM) starting at FROM. + The bounds must fullfill 0 <= FROM < TO <= size_. + */ + tiny_array_view + subarray(index_t FROM, index_t TO) const + { + xtensor_precondition(FROM >= 0 && FROM < TO && TO <= size_, + "tiny_array_base::subarray(): range out of bounds."); + return tiny_array_view(TO-FROM, const_cast(data_)+FROM); + } + + + tiny_array + erase(index_t m) const + { + xtensor_precondition(m >= 0 && m < size(), "tiny_array::erase(): " + "Index "+std::to_string(m)+" out of bounds [0, "+std::to_string(size())+")."); + tiny_array res(size()-1, dont_init); + for(int k=0; k + pop_front() const + { + return erase(0); + } + + tiny_array + pop_back() const + { + return erase(size()-1); + } + + tiny_array + insert(index_t m, value_type v) const + { + xtensor_precondition(m >= 0 && m <= size(), "tiny_array::insert(): " + "Index "+std::to_string(m)+" out of bounds [0, "+std::to_string(size())+"]."); + tiny_array res(size()+1, dont_init); + for(int k=0; k + inline + tiny_array + transpose(tiny_array_base const & permutation) const + { + xtensor_precondition(size() == 0 || size() == permutation.size(), + "tiny_array::transpose(): size mismatch."); + tiny_array res(size(), dont_init); + for(index_t k=0; k < size(); ++k) + { + XTENSOR_ASSERT_MSG(permutation[k] >= 0 && permutation[k] < size(), + "transpose(): Permutation index out of bounds"); + res[k] = (*this)[permutation[k]]; + } + return res; + } + + // boiler plate + + iterator begin() { return data_; } + iterator end() { return data_ + size_; } + const_iterator begin() const { return data_; } + const_iterator end() const { return data_ + size_; } + const_iterator cbegin() const { return data_; } + const_iterator cend() const { return data_ + size_; } + + reverse_iterator rbegin() { return reverse_iterator(data_ + size_); } + reverse_iterator rend() { return reverse_iterator(data_); } + const_reverse_iterator rbegin() const { return const_reverse_iterator(data_ + size_); } + const_reverse_iterator rend() const { return const_reverse_iterator(data_); } + const_reverse_iterator crbegin() const { return const_reverse_iterator(data_ + size_); } + const_reverse_iterator crend() const { return const_reverse_iterator(data_); } + + pointer data() { return data_; } + const_pointer data() const { return data_; } + + reference front() { return data_[0]; } + reference back() { return data_[size_-1]; } + const_reference front() const { return data_[0]; } + const_reference back() const { return data_[size_-1]; } + + bool empty() const { return size_ == 0; } + index_t size() const { return size_; } + index_t max_size() const { return size_; } + index_t ndim() const { return static_ndim; } + + tiny_array_base & reverse() + { + using std::swap; + index_t i=0, j=size_-1; + while(i < j) + swap(data_[i++], data_[j--]); + return *this; + } + + void swap(tiny_array_base & other) + { + using std::swap; + swap(size_, other.size_); + swap(data_, other.data_); + } + + /// factory function for the fixed-size k-th unit vector + static inline + tiny_array + unit_vector(tags::size_proxy const & size, index_t k) + { + tiny_array res(size.value); + res[k] = 1; + return res; + } + + /// factory function for a linear sequence from begin to end + /// (exclusive) with stepsize step + static inline + tiny_array + range(value_type begin, + value_type end, + value_type step = value_type(1)) + { + using namespace cmath; + xtensor_precondition(step != 0, + "tiny_array::range(): step must be non-zero."); + xtensor_precondition((step > 0 && begin <= end) || (step < 0 && begin >= end), + "tiny_array::range(): sign mismatch between step and (end-begin)."); + index_t size = floor((abs(end-begin+step)-1)/abs(step)); + tiny_array res(size, dont_init); + for(index_t k=0; k < size; ++k, begin += step) + res[k] = begin; + return res; + } + + /// factory function for a linear sequence from 0 to end + /// (exclusive) with stepsize 1 + static inline + tiny_array + range(value_type end) + { + xtensor_precondition(end >= 0, + "tiny_array::range(): end must be non-negative."); + tiny_array res(end, dont_init); + auto begin = value_type(); + for(index_t k=0; k < res.size(); ++k, ++begin) + res[k] = begin; + return res; + } + + protected: + index_t size_; + pointer data_; +}; + +/********************************************************/ +/* */ +/* tiny_array */ +/* */ +/********************************************************/ + +/** \brief Class for fixed size arrays. + \ingroup RangesAndPoints + + This class contains an array of the specified VALUETYPE with + (possibly multi-dimensional) shape given by the sequence index_t ... N. + The interface conforms to STL vector, except that there are no functions + that change the size of a tiny_array. + + \ref TinyArrayOperators "Arithmetic operations" + on TinyArrays are defined as component-wise applications of these + operations. + + See also:
+

    +
  • \ref vigra::tiny_array_base +
  • \ref vigra::tiny_array_view +
  • \ref TinyArrayOperators +
+ + \#include \
+ Namespace: vigra +**/ +template +class tiny_array +: public tiny_array_base, M, N...> +{ + public: + using base_type = tiny_array_base, M, N...>; + using value_type = VALUETYPE; + static const int static_size = base_type::static_size; + + constexpr + tiny_array() + : base_type(value_type()) + {} + + explicit + tiny_array(skip_initialization_tag) + : base_type(dont_init) + {} + + // This constructor would allow construction with round brackets, e.g.: + // tiny_array a(2); + // However, this may lead to bugs when fixed-size arrays are mixed with + // runtime_size arrays, where + // tiny_array a(2); + // constructs an array of length 2 with initial value 0. To avoid such bugs, + // construction is restricted to curly braces: + // tiny_array a{2}; + // + // template + // constexpr tiny_array(value_type v0, V... v) + // : base_type(v0, v...) + // {} + + template + tiny_array(std::initializer_list v) + : base_type(dont_init) + { + if(v.size() == 1) + base_type::init(static_cast(*v.begin())); + else if(v.size() == static_size) + base_type::init_impl(v.begin()); + else + xtensor_precondition(false, + "tiny_array(std::initializer_list): wrong initialization size (expected: " + + std::to_string(static_size) + ", got: " + std::to_string(v.size()) +")"); + } + + // for compatibility with tiny_array + explicit + tiny_array(tags::size_proxy const & size, + value_type const & v = value_type()) + : base_type(v) + { + XTENSOR_ASSERT_MSG(size.value == static_size, + "tiny_array(size): size argument conflicts with array length."); + } + + // for compatibility with tiny_array + tiny_array(tags::size_proxy const & size, skip_initialization_tag) + : base_type(dont_init) + { + XTENSOR_ASSERT_MSG(size.value == static_size, + "tiny_array(size): size argument conflicts with array length."); + } + + // for compatibility with tiny_array + tiny_array(index_t size, skip_initialization_tag) + : base_type(dont_init) + { + XTENSOR_ASSERT_MSG(size == static_size, + "tiny_array(size): size argument conflicts with array length."); + } + + constexpr tiny_array(tiny_array const &) = default; + + template + tiny_array(tiny_array_base const & other) + : base_type(other) + {} + + template + tiny_array(tiny_array_base const & other) + : base_type(dont_init) + { + if(other.size() == 0) + { + this->init(value_type()); + } + else if(this->size() != 0) + { + xtensor_precondition(this->size() == other.size(), + "tiny_array(): shape mismatch."); + this->init_impl(other.begin()); + } + } + + explicit tiny_array(value_type const (&u)[1]) + : base_type(*u) + {} + + template + explicit tiny_array(U const (&u)[1]) + : base_type(static_cast(*u)) + {} + + template 1)>> + explicit tiny_array(U const (&u)[static_size]) + : base_type(u) + {} + + template + explicit tiny_array(U const * u, U const * /* end */ = 0) + : base_type(u) + {} + + template ::value> > + tiny_array(U u, reverse_copy_tag) + : base_type(u, copy_reversed) + {} + + // for compatibility with tiny_array<..., runtime_size> + template ::value> > + explicit tiny_array(U u, U end = U()) + : base_type(u, end) + {} + + // for compatibility with tiny_array<..., runtime_size> + template ::value> > + tiny_array(U u, U /* end */, reverse_copy_tag) + : base_type(u, copy_reversed) + {} + + tiny_array & operator=(tiny_array const &) = default; + + tiny_array & operator=(value_type v) + { + base_type::operator=(v); + return *this; + } + + tiny_array & operator=(value_type const (&v)[static_size]) + { + base_type::operator=(v); + return *this; + } + + template + tiny_array & operator=(tiny_array_base const & other) + { + base_type::operator=(other); + return *this; + } +}; + +/********************************************************/ +/* */ +/* tiny_array<..., runtime_size> */ +/* */ +/********************************************************/ + +/** \brief Specialization of tiny_array for dynamic arrays. + \ingroup RangesAndPoints + + This class contains an array of the specified VALUETYPE with + size specified at runtim. + The interface conforms to STL vector, except that there are no functions + that change the size of a tiny_array. + + \ref TinyArrayOperators "Arithmetic operations" + on TinyArrays are defined as component-wise applications of these + operations. + + See also:
+
    +
  • \ref vigra::tiny_array_base +
  • \ref vigra::tiny_array_view +
  • \ref TinyArrayOperators +
+ + \#include \
+ Namespace: vigra +**/ +template +class tiny_array +: public tiny_array_base, runtime_size> +{ + public: + using base_type = tiny_array_base, runtime_size>; + using value_type = VALUETYPE; + + tiny_array() + : base_type() + {} + + explicit + tiny_array(index_t size, + value_type const & initial = value_type()) + : base_type(size) + { + this->data_ = alloc_.allocate(this->size_); + std::uninitialized_fill(this->begin(), this->end(), initial); + } + + explicit + tiny_array(tags::size_proxy const & size, + value_type const & initial = value_type()) + : tiny_array(size.value, initial) + {} + + tiny_array(index_t size, skip_initialization_tag) + : base_type(size) + { + this->data_ = alloc_.allocate(this->size_); + if(!base_type::may_use_uninitialized_memory) + std::uninitialized_fill(this->begin(), this->end(), value_type()); + } + + tiny_array(tiny_array const & rhs ) + : base_type(rhs.size()) + { + this->data_ = alloc_.allocate(this->size_); + std::uninitialized_copy(rhs.begin(), rhs.end(), this->begin()); + } + + tiny_array(tiny_array && rhs) + : base_type() + { + this->swap(rhs); + } + + template + tiny_array(tiny_array_base const & other) + : tiny_array(other.begin(), other.end()) + {} + + template ::value> > + tiny_array(U begin, U end) + : base_type(std::distance(begin, end)) + { + this->data_ = alloc_.allocate(this->size_); + for(int i=0; isize_; ++i, ++begin) + new(this->data_+i) value_type(static_cast(*begin)); + } + + template ::value> > + tiny_array(U begin, U end, reverse_copy_tag) + : base_type(std::distance(begin, end)) + { + this->data_ = alloc_.allocate(this->size_); + for(int i=0; isize_; ++i, --end) + new(this->data_+i) value_type(static_cast(*(end-1))); + } + + template + tiny_array(const U (&u)[SIZE]) + : tiny_array(u, u+SIZE) + {} + + template + tiny_array(std::initializer_list rhs) + : tiny_array(rhs.begin(), rhs.end()) + {} + + tiny_array & operator=(value_type const & v) + { + base_type::operator=(v); + return *this; + } + + tiny_array & operator=(tiny_array && rhs) + { + if(this->size_ != rhs.size()) + rhs.swap(*this); + else + base_type::operator=(rhs); + return *this; + } + + tiny_array & operator=(tiny_array const & rhs) + { + if(this == &rhs) + return *this; + if(this->size_ != rhs.size()) + tiny_array(rhs).swap(*this); + else + base_type::operator=(rhs); + return *this; + } + + template + tiny_array & operator=(tiny_array_base const & rhs) + { + if(this->size_ == 0 || rhs.size() == 0) + tiny_array(rhs).swap(*this); + else + base_type::operator=(rhs); + return *this; + } + + ~tiny_array() + { + if(!base_type::may_use_uninitialized_memory) + { + for(index_t i=0; isize_; ++i) + (this->data_+i)->~value_type(); + } + alloc_.deallocate(this->data_, this->size_); + } + + void resize(size_t new_size) + { + if(new_size != this->size()) + { + tiny_array(new_size).swap(*this); + } + } + + private: + // FIXME: implement an optimized allocator + // FIXME: (look at Alexandrescu's Loki library or Kolmogorov's code) + std::allocator alloc_; +}; + +/********************************************************/ +/* */ +/* tiny_array_view */ +/* */ +/********************************************************/ + +/** \brief Wrapper for fixed size arrays. + + This class wraps the memory of an array of the specified VALUETYPE + with (possibly multi-dimensional) shape given by index_t....N. + Thus, the array can be accessed with an interface similar to + that of std::vector (except that there are no functions + that change the size of a tiny_array_view). The tiny_array_view + does not assume ownership of the given memory. + + \ref TinyArrayOperators "Arithmetic operations" + on TinyArrayViews are defined as component-wise applications of these + operations. + + See also: +
    +
  • \ref vigra::tiny_array_base +
  • \ref vigra::tiny_array +
  • \ref vigra::tiny_symmetric_view +
  • \ref TinyArrayOperators +
+ + \#include \
+ Namespace: vigra +**/ +template +class tiny_array_view +: public tiny_array_base, M, N...> +{ + public: + using base_type = tiny_array_base, M, N...>; + using value_type = VALUETYPE; + using pointer = typename base_type::pointer; + using const_pointer = typename base_type::const_pointer; + + static const index_t static_size = base_type::static_size; + static const index_t static_ndim = base_type::static_ndim; + + tiny_array_view() + : base_type(dont_init) + { + base_type::data_ = nullptr; + } + + /** Construct view for given data array + */ + tiny_array_view(const_pointer data) + : base_type(dont_init) + { + base_type::data_ = const_cast(data); + } + + /** Copy constructor (shallow copy). + */ + tiny_array_view(tiny_array_view const & other) + : base_type(dont_init) + { + base_type::data_ = const_cast(other.data()); + } + + /** Construct view from other tiny_array. + */ + template + tiny_array_view(tiny_array_base const & other) + : base_type(dont_init) + { + base_type::data_ = const_cast(other.data()); + } + + /** Reset to the other array's pointer. + */ + template + void reset(tiny_array_base const & other) + { + base_type::data_ = const_cast(other.data()); + } + + /** Copy the data (not the pointer) of the rhs. + */ + tiny_array_view & operator=(tiny_array_view const & r) + { + for(int k=0; k(r[k]); + return *this; + } + + /** Copy the data of the rhs with cast. + */ + template + tiny_array_view & operator=(tiny_array_base const & r) + { + for(int k=0; k(r[k]); + return *this; + } +}; + +template +class tiny_array_view +: public tiny_array_base, runtime_size> +{ + public: + using base_type = tiny_array_base, runtime_size>; + + using base_type::base_type; + using base_type::operator=; +}; + +/********************************************************/ +/* */ +/* tiny_symmetric_view */ +/* */ +/********************************************************/ + +/** \brief Wrapper for fixed size arrays. + + This class wraps the memory of an 1D array of the specified VALUETYPE + with size N*(N+1)/2 and interprets this array as a symmetric + matrix. Specifically, the data are interpreted as the row-wise + representation of the upper triangular part of the symmetric matrix. + All index access operations are overloaded such that the view appears + as if it were a full matrix. The tiny_symmetric_view + does not assume ownership of the given memory. + + \ref TinyArrayOperators "Arithmetic operations" + on tiny_symmetric_view are defined as component-wise applications of these + operations. + + See also: +
    +
  • \ref vigra::tiny_array_base +
  • \ref vigra::tiny_array +
  • \ref vigra::tiny_array_view +
  • \ref TinyArrayOperators +
+ + \#include \
+ Namespace: vigra +**/ +template +class tiny_symmetric_view +: public tiny_array_base, N*(N+1)/2> +{ + public: + using base_type = tiny_array_base, N*(N+1)/2>; + using value_type = VALUETYPE; + using reference = typename base_type::reference; + using const_reference = typename base_type::const_reference; + using pointer = typename base_type::pointer; + using const_pointer = typename base_type::const_pointer; + using index_type = tiny_array; + + static const index_t static_size = base_type::static_size; + static const index_t static_ndim = 2; + + tiny_symmetric_view() + : base_type(dont_init) + { + base_type::data_ = nullptr; + } + + /** Construct view for given data array + */ + tiny_symmetric_view(const_pointer data) + : base_type(dont_init) + { + base_type::data_ = const_cast(data); + } + + /** Copy constructor (shallow copy). + */ + tiny_symmetric_view(tiny_symmetric_view const & other) + : base_type(dont_init) + { + base_type::data_ = const_cast(other.data()); + } + + /** Construct view from other tiny_array. + */ + template + tiny_symmetric_view(tiny_array_base const & other) + : base_type(dont_init) + { + base_type::data_ = const_cast(other.data()); + } + + /** Reset to the other array's pointer. + */ + template + void reset(tiny_array_base const & other) + { + base_type::data_ = const_cast(other.data()); + } + + /** Copy the data (not the pointer) of the rhs. + */ + tiny_symmetric_view & operator=(tiny_symmetric_view const & r) + { + for(int k=0; k(r[k]); + return *this; + } + + /** Copy the data of the rhs with cast. + */ + template + tiny_symmetric_view & operator=(tiny_array_base const & r) + { + for(int k=0; k(r[k]); + return *this; + } + + // index access + + reference operator[](index_t i) + { + return base_type::operator[](i); + } + + constexpr const_reference operator[](index_t i) const + { + return base_type::operator[](i); + } + + reference at(index_t i) + { + return base_type::at(i); + } + + const_reference at(index_t i) const + { + return base_type::at(i); + } + + reference operator[](index_t const (&i)[2]) + { + return this->operator()(i[0], i[1]); + } + + constexpr const_reference operator[](index_t const (&i)[2]) const + { + return this->operator()(i[0], i[1]); + } + + reference at(index_t const (&i)[static_ndim]) + { + return this->at(i[0], i[1]); + } + + const_reference at(index_t const (&i)[static_ndim]) const + { + return this->at(i[0], i[1]); + } + + reference operator[](index_type const & i) + { + return this->operator()(i[0], i[1]); + } + + constexpr const_reference operator[](index_type const & i) const + { + return this->operator()(i[0], i[1]); + } + + reference at(index_type const & i) + { + return this->at(i[0], i[1]); + } + + const_reference at(index_type const & i) const + { + return this->at(i[0], i[1]); + } + + reference operator()(index_t i, index_t j) + { + return (i > j) + ? base_type::data_[i + N*j - (j*(j+1) >> 1)] + : base_type::data_[N*i + j - (i*(i+1) >> 1)]; + } + + constexpr const_reference operator()(index_t const i, index_t const j) const + { + return (i > j) + ? base_type::data_[i + (j*((2 * N - 1) - j) >> 1)] + : base_type::data_[j + (i*((2 * N - 1) - i) >> 1)]; + } + + reference at(index_t i, index_t j) + { + index_t k = (i > j) + ? i + (j*((2*N-1) - j) >> 1) + : j + (i*((2*N-1) - i) >> 1); + if(k < 0 || k >= static_size) + throw std::out_of_range("tiny_symmetric_view::at()"); + return base_type::data_[k]; + } + + const_reference at(index_t i, index_t j) const + { + index_t k = (i > j) + ? i + N*j - (j*(j+1) >> 1) + : N*i + j - (i*(i+1) >> 1); + if(k < 0 || k >= static_size) + throw std::out_of_range("tiny_symmetric_view::at()"); + return base_type::data_[k]; + } + + constexpr index_type shape() const { return index_type{N, N}; } + constexpr index_t ndim () const { return static_ndim; } +}; + +/********************************************************/ +/* */ +/* tiny_symmetric_view output */ +/* */ +/********************************************************/ + +template +std::ostream & operator<<(std::ostream & o, tiny_symmetric_view const & v) +{ + o << "{"; + for(int i=0; i 0) + o << ",\n "; + o << promote_t(v(i,0)); + for(int j=1; j(v(i, j)); + } + } + o << "}"; + return o; +} + + +/********************************************************/ +/* */ +/* tiny_array Comparison */ +/* */ +/********************************************************/ + +/** \addtogroup TinyArrayOperators Functions for tiny_array + + \brief Implement basic arithmetic and equality for tiny_array. + + These functions fulfill the requirements of a Linear Space (vector space). + Return types are determined according to \ref promote_t or \ref real_promote_t. + + \#include \
+ Namespace: vigra +*/ +//@{ + + /// element-wise equal +template +inline bool +operator==(tiny_array_base const & l, + tiny_array_base const & r) +{ + if(!l.is_same_shape(r)) + return false; + for(int k=0; k < l.size(); ++k) + if(l[k] != r[k]) + return false; + return true; +} + + /// element-wise equal to a constant +template ::value && + std::is_convertible::value> > +inline bool +operator==(tiny_array_base const & l, + V2 const & r) +{ + for(int k=0; k < l.size(); ++k) + if(l[k] != r) + return false; + return true; +} + + /// element-wise equal to a constant +template ::value && + std::is_convertible::value> > +inline bool +operator==(V1 const & l, + tiny_array_base const & r) +{ + for(int k=0; k < r.size(); ++k) + if(l != r[k]) + return false; + return true; +} + + /// element-wise not equal +template +inline bool +operator!=(tiny_array_base const & l, + tiny_array_base const & r) +{ + if(!l.is_same_shape(r)) + return true; + for(int k=0; k < l.size(); ++k) + if(l[k] != r[k]) + return true; + return false; +} + + /// element-wise not equal to a constant +template ::value && + std::is_convertible::value> > +inline bool +operator!=(tiny_array_base const & l, + V2 const & r) +{ + for(int k=0; k < l.size(); ++k) + if(l[k] != r) + return true; + return false; +} + + /// element-wise not equal to a constant +template ::value && + std::is_convertible::value> > +inline bool +operator!=(V1 const & l, + tiny_array_base const & r) +{ + for(int k=0; k < r.size(); ++k) + if(l != r[k]) + return true; + return false; +} + + /// lexicographical less +template +inline bool +operator<(tiny_array_base const & l, + tiny_array_base const & r) +{ + const int min_size = min(l.size(), r.size()); + for(int k = 0; k < min_size; ++k) + { + if(l[k] < r[k]) + return true; + if(r[k] < l[k]) + return false; + } + return (l.size() < r.size()); +} + + /// lexicographical less-equal +template +inline bool +operator<=(tiny_array_base const & l, + tiny_array_base const & r) +{ + return !(r < l); +} + + /// lexicographical greater +template +inline bool +operator>(tiny_array_base const & l, + tiny_array_base const & r) +{ + return r < l; +} + + /// lexicographical greater-equal +template +inline bool +operator>=(tiny_array_base const & l, + tiny_array_base const & r) +{ + return !(l < r); +} + + /// check if all elements are non-zero (or 'true' if V is bool) +template +inline bool +all(tiny_array_base const & t) +{ + for(int i=0; i +inline bool +any(tiny_array_base const & t) +{ + for(int i=0; i +inline bool +all_zero(tiny_array_base const & t) +{ + for(int i=0; i +inline bool +all_less(tiny_array_base const & l, + tiny_array_base const & r) +{ + XTENSOR_ASSERT_RUNTIME_SIZE(N..., l.size() == r.size(), + "tiny_array_base::all_less(): size mismatch."); + for(int k=0; k < l.size(); ++k) + if (l[k] >= r[k]) + return false; + return true; +} + + /// pointwise less than a constant + /// (typically used to check negativity with `r = 0`) +template ::value && + std::is_convertible::value> > +inline bool +all_less(tiny_array_base const & l, + V2 const & r) +{ + for(int k=0; k < l.size(); ++k) + if (l[k] >= r) + return false; + return true; +} + + /// constant pointwise less than the vector + /// (typically used to check positivity with `l = 0`) +template ::value && + std::is_convertible::value> > +inline bool +all_less(V1 const & l, + tiny_array_base const & r) +{ + for(int k=0; k < r.size(); ++k) + if (l >= r[k]) + return false; + return true; +} + + /// pointwise less-equal +template +inline bool +all_less_equal(tiny_array_base const & l, + tiny_array_base const & r) +{ + XTENSOR_ASSERT_RUNTIME_SIZE(N..., l.size() == r.size(), + "tiny_array_base::all_less_equal(): size mismatch."); + for(int k=0; k < l.size(); ++k) + if (l[k] > r[k]) + return false; + return true; +} + + /// pointwise less-equal with a constant + /// (typically used to check non-positivity with `r = 0`) +template ::value && + std::is_convertible::value> > +inline bool +all_less_equal(tiny_array_base const & l, + V2 const & r) +{ + for(int k=0; k < l.size(); ++k) + if (l[k] > r) + return false; + return true; +} + + /// pointwise less-equal with a constant + /// (typically used to check non-negativity with `l = 0`) +template ::value && + std::is_convertible::value> > +inline bool +all_less_equal(V1 const & l, + tiny_array_base const & r) +{ + for(int k=0; k < r.size(); ++k) + if (l > r[k]) + return false; + return true; +} + + /// pointwise greater-than +template +inline bool +all_greater(tiny_array_base const & l, + tiny_array_base const & r) +{ + XTENSOR_ASSERT_RUNTIME_SIZE(N..., l.size() == r.size(), + "tiny_array_base::all_greater(): size mismatch."); + for(int k=0; k < l.size(); ++k) + if (l[k] <= r[k]) + return false; + return true; +} + + /// pointwise greater-than with a constant + /// (typically used to check positivity with `r = 0`) +template ::value && + std::is_convertible::value> > +inline bool +all_greater(tiny_array_base const & l, + V2 const & r) +{ + for(int k=0; k < l.size(); ++k) + if (l[k] <= r) + return false; + return true; +} + + /// constant pointwise greater-than a vector + /// (typically used to check negativity with `l = 0`) +template ::value && + std::is_convertible::value> > +inline bool +all_greater(V1 const & l, + tiny_array_base const & r) +{ + for(int k=0; k < r.size(); ++k) + if (l <= r[k]) + return false; + return true; +} + + /// pointwise greater-equal +template +inline bool +all_greater_equal(tiny_array_base const & l, + tiny_array_base const & r) +{ + XTENSOR_ASSERT_RUNTIME_SIZE(N..., l.size() == r.size(), + "tiny_array_base::all_greater_equal(): size mismatch."); + for(int k=0; k < l.size(); ++k) + if (l[k] < r[k]) + return false; + return true; +} + + /// pointwise greater-equal with a constant + /// (typically used to check non-negativity with `r = 0`) +template ::value && + std::is_convertible::value> > +inline bool +all_greater_equal(tiny_array_base const & l, + V2 const & r) +{ + for(int k=0; k < l.size(); ++k) + if (l[k] < r) + return false; + return true; +} + + /// pointwise greater-equal with a constant + /// (typically used to check non-positivity with `l = 0`) +template ::value && + std::is_convertible::value> > +inline bool +all_greater_equal(V1 const & l, + tiny_array_base const & r) +{ + for(int k=0; k < r.size(); ++k) + if (l < r[k]) + return false; + return true; +} + +template +inline bool +isclose(tiny_array_base const & l, + tiny_array_base const & r, + promote_t epsilon = 2.0*std::numeric_limits >::epsilon()) +{ + XTENSOR_ASSERT_RUNTIME_SIZE(N..., l.size() == r.size(), + "tiny_array_base::isclose(): size mismatch."); + for(int k=0; k < l.size(); ++k) + if(!isclose(l[k], r[k], epsilon, epsilon)) + return false; + return true; +} + +/********************************************************/ +/* */ +/* tiny_array-Arithmetic */ +/* */ +/********************************************************/ + +/** \addtogroup TinyArrayOperators + */ +//@{ + +#ifdef DOXYGEN +// Declare arithmetic functions for documentation, +// the implementations are provided by a macro below. + + /// scalar add-assignment +template ::value> > +DERIVED & +operator+=(tiny_array_base & l, + V2 r); + + /// element-wise add-assignment +template +DERIVED & +operator+=(tiny_array_base & l, + tiny_array_base const & r); + + /// element-wise addition +template +tiny_array, N...> +operator+(tiny_array_base const & l, + tiny_array_base const & r); + + /// element-wise scalar addition +template +tiny_array, N...> +operator+(tiny_array_base const & l, + V2 r); + + /// element-wise left scalar addition +template +tiny_array, N...> +operator+(V1 l, + tiny_array_base const & r); + + /// scalar subtract-assignment +template ::value> > +DERIVED & +operator-=(tiny_array_base & l, + V2 r); + + /// element-wise subtract-assignment +template +DERIVED & +operator-=(tiny_array_base & l, + tiny_array_base const & r); + + /// element-wise subtraction +template +tiny_array, N...> +operator-(tiny_array_base const & l, + tiny_array_base const & r); + + /// element-wise scalar subtraction +template +tiny_array, N...> +operator-(tiny_array_base const & l, + V2 r); + + /// element-wise left scalar subtraction +template +tiny_array, N...> +operator-(V1 l, + tiny_array_base const & r); + + /// scalar multiply-assignment +template ::value> > +DERIVED & +operator*=(tiny_array_base & l, + V2 r); + + /// element-wise multiply-assignment +template +DERIVED & +operator*=(tiny_array_base & l, + tiny_array_base const & r); + + /// element-wise multiplication +template +tiny_array, N...> +operator*(tiny_array_base const & l, + tiny_array_base const & r); + + /// element-wise scalar multiplication +template +tiny_array, N...> +operator*(tiny_array_base const & l, + V2 r); + + /// element-wise left scalar multiplication +template +tiny_array, N...> +operator*(V1 l, + tiny_array_base const & r); + + /// scalar divide-assignment +template ::value> > +DERIVED & +operator/=(tiny_array_base & l, + V2 r); + + /// element-wise divide-assignment +template +DERIVED & +operator/=(tiny_array_base & l, + tiny_array_base const & r); + + /// element-wise division +template +tiny_array, N...> +operator/(tiny_array_base const & l, + tiny_array_base const & r); + + /// element-wise scalar division +template +tiny_array, N...> +operator/(tiny_array_base const & l, + V2 r); + + /// element-wise left scalar division +template +tiny_array, N...> +operator/(V1 l, + tiny_array_base const & r); + + /// scalar modulo-assignment +template ::value> > +DERIVED & +operator%=(tiny_array_base & l, + V2 r); + + /// element-wise modulo-assignment +template +DERIVED & +operator%=(tiny_array_base & l, + tiny_array_base const & r); + + /// element-wise modulo +template +tiny_array, N...> +operator%(tiny_array_base const & l, + tiny_array_base const & r); + + /// element-wise scalar modulo +template +tiny_array, N...> +operator%(tiny_array_base const & l, + V2 r); + + /// element-wise left scalar modulo +template +tiny_array, N...> +operator%(V1 l, + tiny_array_base const & r); + +#else + +#define XTENSOR_TINYARRAY_OPERATORS(OP) \ +template ::value && \ + std::is_convertible::value> > \ +inline DERIVED & \ +operator OP##=(tiny_array_base & l, \ + V2 r) \ +{ \ + for(int i=0; i(l); \ +} \ + \ +template \ +inline DERIVED & \ +operator OP##=(tiny_array_base & l, \ + tiny_array_base const & r) \ +{ \ + XTENSOR_ASSERT_MSG(l.size() == r.size(), \ + "tiny_array_base::operator" #OP "=(): size mismatch."); \ + for(int i=0; i(l); \ +} \ +template \ +inline \ +tiny_array \ +operator OP(tiny_array_base const & l, \ + tiny_array_base const & r) \ +{ \ + tiny_array res(l); \ + return res OP##= r; \ +} \ + \ +template ::value> >\ +inline \ +tiny_array \ +operator OP(tiny_array_base const & l, \ + V2 r) \ +{ \ + tiny_array res(l); \ + return res OP##= r; \ +} \ + \ +template ::value && \ + !std::is_base_of::value> >\ +inline \ +tiny_array \ +operator OP(V1 l, \ + tiny_array_base const & r) \ +{ \ + tiny_array res{l}; \ + return res OP##= r; \ +} \ + \ +template ::value && \ + !std::is_base_of::value> >\ +inline \ +tiny_array \ +operator OP(V1 l, \ + tiny_array_base const & r) \ +{ \ + tiny_array res(tags::size=r.size(), l); \ + return res OP##= r; \ +} + +XTENSOR_TINYARRAY_OPERATORS(+) +XTENSOR_TINYARRAY_OPERATORS(-) +XTENSOR_TINYARRAY_OPERATORS(*) +XTENSOR_TINYARRAY_OPERATORS(/) +XTENSOR_TINYARRAY_OPERATORS(%) +XTENSOR_TINYARRAY_OPERATORS(&) +XTENSOR_TINYARRAY_OPERATORS(|) +XTENSOR_TINYARRAY_OPERATORS(^) +XTENSOR_TINYARRAY_OPERATORS(<<) +XTENSOR_TINYARRAY_OPERATORS(>>) + +#undef XTENSOR_TINYARRAY_OPERATORS + +#endif // DOXYGEN + + // define sqrt() explicitly because its return type + // is needed for type inference +template +inline tiny_array, N...> +sqrt(tiny_array_base const & v) +{ + using namespace cmath; + tiny_array, N...> res(v.size(), dont_init); + for(int k=0; k < v.size(); ++k) + res[k] = sqrt(v[k]); + return res; +} + +#define XTENSOR_TINYARRAY_UNARY_FUNCTION(FCT) \ +template \ +inline auto \ +FCT(tiny_array_base const & v) \ +{ \ + using namespace cmath; \ + tiny_array, N...> res(v.size(), dont_init); \ + for(int k=0; k < v.size(); ++k) \ + res[k] = FCT(v[k]); \ + return res; \ +} + +XTENSOR_TINYARRAY_UNARY_FUNCTION(abs) +XTENSOR_TINYARRAY_UNARY_FUNCTION(fabs) + +XTENSOR_TINYARRAY_UNARY_FUNCTION(cos) +XTENSOR_TINYARRAY_UNARY_FUNCTION(sin) +XTENSOR_TINYARRAY_UNARY_FUNCTION(tan) +XTENSOR_TINYARRAY_UNARY_FUNCTION(sin_pi) +XTENSOR_TINYARRAY_UNARY_FUNCTION(cos_pi) +XTENSOR_TINYARRAY_UNARY_FUNCTION(acos) +XTENSOR_TINYARRAY_UNARY_FUNCTION(asin) +XTENSOR_TINYARRAY_UNARY_FUNCTION(atan) + +XTENSOR_TINYARRAY_UNARY_FUNCTION(cosh) +XTENSOR_TINYARRAY_UNARY_FUNCTION(sinh) +XTENSOR_TINYARRAY_UNARY_FUNCTION(tanh) +XTENSOR_TINYARRAY_UNARY_FUNCTION(acosh) +XTENSOR_TINYARRAY_UNARY_FUNCTION(asinh) +XTENSOR_TINYARRAY_UNARY_FUNCTION(atanh) + +XTENSOR_TINYARRAY_UNARY_FUNCTION(cbrt) +XTENSOR_TINYARRAY_UNARY_FUNCTION(sq) +XTENSOR_TINYARRAY_UNARY_FUNCTION(elementwise_norm) +XTENSOR_TINYARRAY_UNARY_FUNCTION(elementwise_squared_norm) + +XTENSOR_TINYARRAY_UNARY_FUNCTION(exp) +XTENSOR_TINYARRAY_UNARY_FUNCTION(exp2) +XTENSOR_TINYARRAY_UNARY_FUNCTION(expm1) +XTENSOR_TINYARRAY_UNARY_FUNCTION(log) +XTENSOR_TINYARRAY_UNARY_FUNCTION(log2) +XTENSOR_TINYARRAY_UNARY_FUNCTION(log10) +XTENSOR_TINYARRAY_UNARY_FUNCTION(log1p) +XTENSOR_TINYARRAY_UNARY_FUNCTION(logb) +XTENSOR_TINYARRAY_UNARY_FUNCTION(ilogb) + +XTENSOR_TINYARRAY_UNARY_FUNCTION(ceil) +XTENSOR_TINYARRAY_UNARY_FUNCTION(floor) +XTENSOR_TINYARRAY_UNARY_FUNCTION(trunc) +XTENSOR_TINYARRAY_UNARY_FUNCTION(round) +XTENSOR_TINYARRAY_UNARY_FUNCTION(lround) +XTENSOR_TINYARRAY_UNARY_FUNCTION(llround) +XTENSOR_TINYARRAY_UNARY_FUNCTION(even) +XTENSOR_TINYARRAY_UNARY_FUNCTION(odd) +XTENSOR_TINYARRAY_UNARY_FUNCTION(sign) +XTENSOR_TINYARRAY_UNARY_FUNCTION(signi) + +XTENSOR_TINYARRAY_UNARY_FUNCTION(erf) +XTENSOR_TINYARRAY_UNARY_FUNCTION(erfc) +XTENSOR_TINYARRAY_UNARY_FUNCTION(tgamma) +XTENSOR_TINYARRAY_UNARY_FUNCTION(lgamma) + +XTENSOR_TINYARRAY_UNARY_FUNCTION(conj) +XTENSOR_TINYARRAY_UNARY_FUNCTION(real) +XTENSOR_TINYARRAY_UNARY_FUNCTION(imag) +XTENSOR_TINYARRAY_UNARY_FUNCTION(arg) + +#undef XTENSOR_TINYARRAY_UNARY_FUNCTION + + /// Arithmetic negation +template +inline +tiny_array +operator-(tiny_array_base const & v) +{ + tiny_array res(v.size(), dont_init); + for(int k=0; k < v.size(); ++k) + res[k] = -v[k]; + return res; +} + + /// Boolean negation +template +inline +tiny_array +operator!(tiny_array_base const & v) +{ + tiny_array res(v.size(), dont_init); + for(int k=0; k < v.size(); ++k) + res[k] = !v[k]; + return res; +} + + /// Bitwise negation +template +inline +tiny_array +operator~(tiny_array_base const & v) +{ + tiny_array res(v.size(), dont_init); + for(int k=0; k < v.size(); ++k) + res[k] = ~v[k]; + return res; +} + +#define XTENSOR_TINYARRAY_BINARY_FUNCTION(FCT) \ +template \ +inline auto \ +FCT(tiny_array_base const & l, \ + tiny_array_base const & r) \ +{ \ + XTENSOR_ASSERT_MSG(l.size() == r.size(), #FCT "(tiny_array, tiny_array): size mismatch."); \ + using namespace cmath; \ + tiny_array res(l.size(), dont_init); \ + for(int k=0; k < l.size(); ++k) \ + res[k] = FCT(l[k], r[k]); \ + return res; \ +} + +XTENSOR_TINYARRAY_BINARY_FUNCTION(atan2) +XTENSOR_TINYARRAY_BINARY_FUNCTION(copysign) +XTENSOR_TINYARRAY_BINARY_FUNCTION(fdim) +XTENSOR_TINYARRAY_BINARY_FUNCTION(fmax) +XTENSOR_TINYARRAY_BINARY_FUNCTION(fmin) +XTENSOR_TINYARRAY_BINARY_FUNCTION(fmod) +XTENSOR_TINYARRAY_BINARY_FUNCTION(hypot) + +#undef XTENSOR_TINYARRAY_BINARY_FUNCTION + + /** Apply pow() function to each vector component. + */ +template +inline auto +pow(tiny_array_base const & v, E exponent) +{ + using namespace cmath; + tiny_array res(v.size(), dont_init); + for(int k=0; k < v.size(); ++k) + res[k] = pow(v[k], exponent); + return res; +} + + /// cross product +template > +inline +tiny_array, N> +cross(tiny_array_base const & r1, + tiny_array_base const & r2) +{ + XTENSOR_ASSERT_RUNTIME_SIZE(N, r1.size() == 3 && r2.size() == 3, + "cross(): cross product requires size() == 3."); + typedef tiny_array, N> Res; + return Res{r1[1]*r2[2] - r1[2]*r2[1], + r1[2]*r2[0] - r1[0]*r2[2], + r1[0]*r2[1] - r1[1]*r2[0]}; +} + + /// dot product of two vectors +template +inline +promote_t +dot(tiny_array_base const & l, + tiny_array_base const & r) +{ + XTENSOR_ASSERT_MSG(l.size() == r.size(), "dot(): size mismatch."); + promote_t res = promote_t(); + for(int k=0; k < l.size(); ++k) + res += l[k] * r[k]; + return res; +} + + /// sum of the vector's elements +template +inline +promote_t +sum(tiny_array_base const & l) +{ + promote_t res = promote_t(); + for(int k=0; k < l.size(); ++k) + res += l[k]; + return res; +} + + /// mean of the vector's elements +template +inline real_promote_t +mean(tiny_array_base const & t) +{ + using Promote = real_promote_t; + const Promote sumVal = static_cast(sum(t)); + if(t.size() > 0) + return sumVal / static_cast(t.size()); + else + return sumVal; +} + + /// cumulative sum of the vector's elements +template +inline +tiny_array, N...> +cumsum(tiny_array_base const & l) +{ + tiny_array, N...> res(l); + for(int k=1; k < l.size(); ++k) + res[k] += res[k-1]; + return res; +} + + /// product of the vector's elements +template +inline +promote_t +prod(tiny_array_base const & l) +{ + using Promote = promote_t; + if(l.size() == 0) + return Promote(); + Promote res = Promote(1); + for(int k=0; k < l.size(); ++k) + res *= l[k]; + return res; +} + + /// cumulative product of the vector's elements +template +inline +tiny_array, N...> +cumprod(tiny_array_base const & l) +{ + tiny_array, N...> res(l); + for(int k=1; k < l.size(); ++k) + res[k] *= res[k-1]; + return res; +} + + /// element-wise minimum +template +inline +tiny_array, N...> +min(tiny_array_base const & l, + tiny_array_base const & r) +{ + XTENSOR_ASSERT_RUNTIME_SIZE(N..., l.size() == r.size(), + "min(): size mismatch."); + tiny_array, N...> res(l.size(), dont_init); + for(int k=0; k < l.size(); ++k) + res[k] = min(l[k], r[k]); + return res; +} + + /// element-wise minimum with a constant +template ::value>> +inline +tiny_array, N...> +min(tiny_array_base const & l, + V2 const & r) +{ + tiny_array, N...> res(l.size(), dont_init); + for(int k=0; k < l.size(); ++k) + res[k] = min(l[k], r); + return res; +} + + /// element-wise minimum with a constant +template ::value>> +inline +tiny_array, N...> +min(V1 const & l, + tiny_array_base const & r) +{ + tiny_array, N...> res(r.size(), dont_init); + for(int k=0; k < r.size(); ++k) + res[k] = min(l, r[k]); + return res; +} + + /** Index of minimal element. + + Returns -1 for an empty array. + */ +template +inline int +min_element(tiny_array_base const & l) +{ + if(l.size() == 0) + return -1; + int m = 0; + for(int i=1; i +inline +V const & +min(tiny_array_base const & l) +{ + int m = min_element(l); + xtensor_precondition(m >= 0, "min() on empty tiny_array."); + return l[m]; +} + + /// element-wise maximum +template +inline +tiny_array, N...> +max(tiny_array_base const & l, + tiny_array_base const & r) +{ + XTENSOR_ASSERT_RUNTIME_SIZE(N..., l.size() == r.size(), + "max(): size mismatch."); + tiny_array, N...> res(l.size(), dont_init); + for(int k=0; k < l.size(); ++k) + res[k] = max(l[k], r[k]); + return res; +} + + /// element-wise maximum with a constant +template ::value>> +inline +tiny_array, N...> +max(tiny_array_base const & l, + V2 const & r) +{ + tiny_array, N...> res(l.size(), dont_init); + for(int k=0; k < l.size(); ++k) + res[k] = max(l[k], r); + return res; +} + + /// element-wise maximum with a constant +template ::value>> +inline +tiny_array, N...> +max(V1 const & l, + tiny_array_base const & r) +{ + tiny_array, N...> res(r.size(), dont_init); + for(int k=0; k < r.size(); ++k) + res[k] = max(l, r[k]); + return res; +} + + /** Index of maximal element. + + Returns -1 for an empty array. + */ +template +inline int +max_element(tiny_array_base const & l) +{ + if(l.size() == 0) + return -1; + int m = 0; + for(int i=1; i +inline V const & +max(tiny_array_base const & l) +{ + int m = max_element(l); + xtensor_precondition(m >= 0, "max() on empty tiny_array."); + return l[m]; +} + +/// squared norm +template +inline squared_norm_t > +squared_norm(tiny_array_base const & t) +{ + using Type = squared_norm_t >; + Type result = Type(); + for(int i=0; i +inline squared_norm_t > +squared_norm(tiny_symmetric_view const & t) +{ + using Type = squared_norm_t >; + Type result = Type(); + for (int i = 0; i < N; ++i) + { + result += squared_norm(t(i, i)); + for (int j = i + 1; j < N; ++j) + { + auto c = squared_norm(t(i, j)); + result += c + c; + } + } + + return result; +} + +template +inline +norm_t +mean_square(tiny_array_base const & t) +{ + return norm_t(squared_norm(t)) / t.size(); +} + + /// reversed copy +template +inline +tiny_array +reversed(tiny_array_base const & t) +{ + return tiny_array(t.begin(), t.end(), copy_reversed); +} + + /** \brief transposed copy + + Elements are arranged such that res[k] = t[permutation[k]]. + */ +template +inline +tiny_array +transpose(tiny_array_base const & v, + tiny_array_base const & permutation) +{ + return v.transpose(permutation); +} + +template +inline +tiny_array +transpose(tiny_array_base const & v) +{ + return reversed(v); +} + +template +inline +tiny_array +transpose(tiny_array_base const & v) +{ + tiny_array res(dont_init); + for(int i=0; i < N1; ++i) + { + for(int j=0; j < N2; ++j) + { + res(j,i) = v(i,j); + } + } + return res; +} + +template +inline +tiny_symmetric_view +transpose(tiny_symmetric_view const & v) +{ + return v; +} + + /** \brief Clip negative values. + + All elements smaller than 0 are set to zero. + */ +template +inline +tiny_array +clip_lower(tiny_array_base const & t) +{ + return clip_lower(t, V()); +} + + /** \brief Clip values below a threshold. + + All elements smaller than \a val are set to \a val. + */ +template +inline +tiny_array +clip_lower(tiny_array_base const & t, const V val) +{ + tiny_array res(t.size(), dont_init); + for(int k=0; k < t.size(); ++k) + { + res[k] = t[k] < val ? val : t[k]; + } + return res; +} + + /** \brief Clip values above a threshold. + + All elements bigger than \a val are set to \a val. + */ +template +inline +tiny_array +clip_upper(tiny_array_base const & t, const V val) +{ + tiny_array res(t.size(), dont_init); + for(int k=0; k < t.size(); ++k) + { + res[k] = t[k] > val ? val : t[k]; + } + return res; +} + + /** \brief Clip values to an interval. + + All elements less than \a valLower are set to \a valLower, all elements + bigger than \a valUpper are set to \a valUpper. + */ +template +inline +tiny_array +clip(tiny_array_base const & t, + const V valLower, const V valUpper) +{ + tiny_array res(t.size(), dont_init); + for(int k=0; k < t.size(); ++k) + { + res[k] = (t[k] < valLower) + ? valLower + : (t[k] > valUpper) + ? valUpper + : t[k]; + } + return res; +} + + /** \brief Clip values to a vector of intervals. + + All elements less than \a valLower are set to \a valLower, all elements + bigger than \a valUpper are set to \a valUpper. + */ +template +inline +tiny_array +clip(tiny_array_base const & t, + tiny_array_base const & valLower, + tiny_array_base const & valUpper) +{ + XTENSOR_ASSERT_RUNTIME_SIZE(N..., t.size() == valLower.size() && t.size() == valUpper.size(), + "clip(): size mismatch."); + tiny_array res(t.size(), dont_init); + for(int k=0; k < t.size(); ++k) + { + res[k] = (t[k] < valLower[k]) + ? valLower[k] + : (t[k] > valUpper[k]) + ? valUpper[k] + : t[k]; + } + return res; +} + +template +inline void +swap(tiny_array_base & l, + tiny_array_base & r) +{ + l.swap(r); +} + +} // namespace xt + +#undef XTENSOR_ASSERT_INSIDE + +#endif // XTENSOR_XTINY_HPP diff --git a/include/xtensor/xutils.hpp b/include/xtensor/xutils.hpp index 6361edceb..8149424e9 100644 --- a/include/xtensor/xutils.hpp +++ b/include/xtensor/xutils.hpp @@ -20,9 +20,20 @@ #include #include "xtensor_config.hpp" +#include "xtiny.hpp" namespace xt { + // dyn_shape and stat_shape are centralized declarations of + // shape types for xarray and xtensor respectively + template > + //using dyn_shape = std::vector; + using dyn_shape = tiny_array; + + template + //using stat_shape = std::array; + using stat_shape = tiny_array; + /**************** * declarations * ****************/ @@ -440,6 +451,19 @@ namespace xt return size == N; } + template + inline bool resize_container(tiny_array& a, typename tiny_array::size_type size) + { + return size == N; + } + + template + inline bool resize_container(tiny_array& a, typename tiny_array::size_type size) + { + tiny_array(size).swap(a); + return true; + } + /******************************** * make_sequence implementation * ********************************/ @@ -472,6 +496,19 @@ namespace xt return s; } }; + + template + struct sequence_builder> + { + using sequence_type = tiny_array; + using value_type = typename sequence_type::value_type; + using size_type = typename sequence_type::size_type; + + inline static sequence_type make(size_type size, value_type v) + { + return sequence_type(tags::size=size, v ); + } + }; } template @@ -545,7 +582,7 @@ namespace xt namespace detail { template - constexpr std::common_type_t imax(const T1& a, const T2& b) + constexpr std::common_type_t imax(const T1 a, const T2 b) { return a > b ? a : b; } @@ -565,6 +602,16 @@ namespace xt { }; + template + struct max_array_size, Ts...> : std::integral_constant::value)> + { + }; + + template + struct max_array_size, Ts...> : std::integral_constant::value> + { + }; + // Simple is_array and only_array meta-functions template struct is_array @@ -578,12 +625,24 @@ namespace xt static constexpr bool value = true; }; + template + struct is_array> + { + static constexpr bool value = true; + }; + + template + struct is_array> + { + static constexpr bool value = false; + }; + template using only_array = and_...>; - // The promote_index meta-function returns std::vector in the + // The promote_index meta-function returns dyn_shape in the // general case and an array of the promoted value type and maximal size if all - // arguments are of type std::array + // arguments are of type stat_shape template struct promote_index_impl; @@ -591,19 +650,19 @@ namespace xt template struct promote_index_impl { - using type = std::vector::type>; + using type = dyn_shape::type>; }; template struct promote_index_impl { - using type = std::array::type, max_array_size::value>; + using type = stat_shape::type, max_array_size::value>; }; template <> struct promote_index_impl { - using type = std::array; + using type = stat_shape; }; template @@ -917,7 +976,7 @@ namespace xt }; /***************************** - * is_complete implemenation * + * is_complete implemenation * *****************************/ namespace detail @@ -936,7 +995,7 @@ namespace xt struct is_complete : detail::is_complete_impl::type {}; /******************************************** - * xtrivial_default_construct implemenation * + * xtrivial_default_construct implemenation * ********************************************/ #if defined(__clang__) @@ -963,14 +1022,14 @@ namespace xt struct xtrivial_default_construct_impl : std::has_trivial_default_constructor {}; } - template + template using xtrivially_default_constructible = detail::xtrivial_default_construct_impl>::value, T>; #pragma clang diagnostic pop #else // CLANG && APPLE - template + template using xtrivially_default_constructible = std::is_trivially_default_constructible; #endif @@ -979,18 +1038,18 @@ namespace xt #if defined(__GNUC__) && (__GNUC__ < 5 || (__GNUC__ == 5 && __GNUC_MINOR__ < 1)) // OLD GCC - template + template using xtrivially_default_constructible = std::has_trivial_default_constructor; #else - template + template using xtrivially_default_constructible = std::is_trivially_default_constructible; #endif #endif - + } #endif diff --git a/include/xtensor/xview.hpp b/include/xtensor/xview.hpp index 2d78600d6..e5cc74bfd 100644 --- a/include/xtensor/xview.hpp +++ b/include/xtensor/xview.hpp @@ -315,6 +315,18 @@ namespace xt using type = std::array() + newaxis_count()>; }; + template + struct xview_shape_type, S...> + { + using type = tiny_array() + newaxis_count())>; + }; + + template + struct xview_shape_type, S...> + { + using type = tiny_array; + }; + /************************ * xview implementation * ************************/ diff --git a/include/xtensor/xview_utils.hpp b/include/xtensor/xview_utils.hpp index f0e7f4044..38bedc3c2 100644 --- a/include/xtensor/xview_utils.hpp +++ b/include/xtensor/xview_utils.hpp @@ -74,6 +74,18 @@ namespace xt { using type = xtensor() - integral_count(), L>; }; + + template + struct view_temporary_type_impl, L, SL...> + { + using type = xtensor() - integral_count(), L>; + }; + + template + struct view_temporary_type_impl, L, SL...> + { + using type = xarray; + }; } template diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c67bfb7df..1fd112ea0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -119,6 +119,7 @@ set(XTENSOR_TESTS test_xoptional.cpp test_xstorage.cpp test_xcsv.cpp + test_xtiny.cpp ) set(XTENSOR_TARGET test_xtensor) diff --git a/test/test_common.hpp b/test/test_common.hpp index 05c5623c6..0d392547e 100644 --- a/test/test_common.hpp +++ b/test/test_common.hpp @@ -11,6 +11,7 @@ #include "xtensor/xlayout.hpp" #include "xtensor/xstridedview.hpp" +#include "xtensor/xtiny.hpp" namespace xt { @@ -26,7 +27,7 @@ namespace xt return rhs == lhs; } - template > + template > struct layout_result { using vector_type = uvector; @@ -67,7 +68,7 @@ namespace xt inline const vector_type& data() const { return m_data; } }; - template > + template > struct row_major_result : layout_result { inline row_major_result() @@ -81,7 +82,7 @@ namespace xt } }; - template > + template > struct column_major_result : layout_result { inline column_major_result() @@ -96,7 +97,7 @@ namespace xt } }; - template > + template > struct central_major_result : layout_result { inline central_major_result() @@ -110,7 +111,7 @@ namespace xt } }; - template > + template > struct unit_shape_result { using vector_type = std::vector; @@ -165,7 +166,7 @@ namespace xt } } - template > + template > void test_reshape(V& vec) { { @@ -180,8 +181,8 @@ namespace xt row_major_result rm; auto v_copy_a = vec; auto v_copy_b = vec; - std::array ar = {3, 2, 4}; - std::vector vr = {3, 2, 4}; + stat_shape ar = {3, 2, 4}; + dyn_shape vr = {3, 2, 4}; v_copy_a.reshape(ar, true); compare_shape(v_copy_a, rm); v_copy_b.reshape(vr, true); @@ -211,7 +212,7 @@ namespace xt } } - template > + template > void test_transpose(V& vec) { using shape_type = typename V::shape_type; @@ -288,7 +289,7 @@ namespace xt EXPECT_EQ(vec_copy(1, 1, 2), vt(1, 1, 2)); // Compilation check only - std::vector perm = {1, 0, 2}; + dyn_shape perm = {1, 0, 2}; transpose(vec, perm); } @@ -329,7 +330,7 @@ namespace xt #endif } - template > + template > void test_access(V& vec) { { @@ -377,7 +378,7 @@ namespace xt } } - template > + template > void test_element(V& vec) { { @@ -386,10 +387,10 @@ namespace xt vec.reshape(rm.m_shape, layout_type::row_major); assign_array(vec, rm.m_assigner); EXPECT_EQ(vec.data(), rm.m_data); - std::vector index1 = {0, 1, 1}; - std::vector index2 = {1, 1}; - std::vector index3 = {2, 1, 3}; - std::vector index4 = {2, 2, 2, 1, 3}; + dyn_shape index1 = {0, 1, 1}; + dyn_shape index2 = {1, 1}; + dyn_shape index3 = {2, 1, 3}; + dyn_shape index4 = {2, 2, 2, 1, 3}; EXPECT_EQ(vec.element(index1.begin(), index1.end()), vec.element(index2.begin(), index2.end())); EXPECT_EQ(vec.element(index3.begin(), index3.end()), vec.element(index4.begin(), index4.end())); test_bound_check(vec); @@ -401,10 +402,10 @@ namespace xt vec.reshape(cm.m_shape, layout_type::column_major); assign_array(vec, cm.m_assigner); EXPECT_EQ(vec.data(), cm.m_data); - std::vector index1 = {0, 1, 1}; - std::vector index2 = {1, 1}; - std::vector index3 = {2, 1, 3}; - std::vector index4 = {2, 2, 2, 1, 3}; + dyn_shape index1 = {0, 1, 1}; + dyn_shape index2 = {1, 1}; + dyn_shape index3 = {2, 1, 3}; + dyn_shape index4 = {2, 2, 2, 1, 3}; EXPECT_EQ(vec.element(index1.begin(), index1.end()), vec.element(index2.begin(), index2.end())); EXPECT_EQ(vec.element(index3.begin(), index3.end()), vec.element(index4.begin(), index4.end())); test_bound_check(vec); @@ -416,10 +417,10 @@ namespace xt vec.reshape(cem.m_shape, cem.m_strides); assign_array(vec, cem.m_assigner); EXPECT_EQ(vec.data(), cem.m_data); - std::vector index1 = {0, 1, 1}; - std::vector index2 = {1, 1}; - std::vector index3 = {2, 1, 3}; - std::vector index4 = {2, 2, 2, 1, 3}; + dyn_shape index1 = {0, 1, 1}; + dyn_shape index2 = {1, 1}; + dyn_shape index3 = {2, 1, 3}; + dyn_shape index4 = {2, 2, 2, 1, 3}; EXPECT_EQ(vec.element(index1.begin(), index1.end()), vec.element(index2.begin(), index2.end())); EXPECT_EQ(vec.element(index3.begin(), index3.end()), vec.element(index4.begin(), index4.end())); test_bound_check(vec); @@ -431,10 +432,10 @@ namespace xt vec.reshape(usr.m_shape, layout_type::row_major); assign_array(vec, usr.m_assigner); EXPECT_EQ(vec.data(), usr.m_data); - std::vector index1 = {0, 1, 0}; - std::vector index2 = {1, 0}; - std::vector index3 = {2, 0, 3}; - std::vector index4 = {2, 2, 2, 0, 3}; + dyn_shape index1 = {0, 1, 0}; + dyn_shape index2 = {1, 0}; + dyn_shape index3 = {2, 0, 3}; + dyn_shape index4 = {2, 2, 2, 0, 3}; EXPECT_EQ(vec.element(index1.begin(), index1.end()), vec.element(index2.begin(), index2.end())); EXPECT_EQ(vec.element(index3.begin(), index3.end()), vec.element(index4.begin(), index4.end())); test_bound_check(vec); @@ -460,7 +461,7 @@ namespace xt } } - template > + template > void test_indexed_access(V& vec) { xindex index1 = {1, 1}; @@ -567,7 +568,7 @@ namespace xt } } - template > + template > void test_iterator(V& vec) { { @@ -607,14 +608,14 @@ namespace xt } } - template > + template > void test_xiterator(V& vec) { row_major_result rm; vec.reshape(rm.m_shape, layout_type::row_major); indexed_assign_array(vec, rm.m_assigner); size_t nb_iter = vec.size() / 2; - using shape_type = std::vector; + using shape_type = dyn_shape; // broadcast_iterator { @@ -671,7 +672,7 @@ namespace xt } } - template > + template > void test_reverse_xiterator(V& vec) { row_major_result rm; @@ -693,7 +694,7 @@ namespace xt // shaped_xiterator { - using shape_type = std::vector; + using shape_type = dyn_shape; shape_type shape(rm.m_shape.size() + 1); std::copy(rm.m_shape.begin(), rm.m_shape.end(), shape.begin() + 1); shape[0] = 2; diff --git a/test/test_xadapt.cpp b/test/test_xadapt.cpp index 79ec86a4a..e3738acc1 100644 --- a/test/test_xadapt.cpp +++ b/test/test_xadapt.cpp @@ -16,7 +16,7 @@ namespace xt TEST(xarray_adaptor, xadapt) { vec_type v(4, 0); - using shape_type = std::vector; + using shape_type = dyn_shape; shape_type s({2, 2}); auto a1 = xadapt(v, s); @@ -33,7 +33,7 @@ namespace xt { size_t size = 4; int* data = new int[size]; - using shape_type = std::vector; + using shape_type = dyn_shape; shape_type s({2, 2}); auto a1 = xadapt(data, size, no_ownership(), s); @@ -53,7 +53,7 @@ namespace xt size_t size = 4; int* data = new int[size]; int* data2 = new int[size]; - using shape_type = std::vector; + using shape_type = dyn_shape; shape_type s({2, 2}); auto a1 = xadapt(data, size, acquire_ownership(), s); @@ -69,7 +69,7 @@ namespace xt TEST(xtensor_adaptor, xadapt) { vec_type v(4, 0); - using shape_type = std::array; + using shape_type = stat_shape; shape_type s = {2, 2}; auto a1 = xadapt(v, s); @@ -86,7 +86,7 @@ namespace xt { size_t size = 4; int* data = new int[size]; - using shape_type = std::array; + using shape_type = stat_shape; shape_type s = {2, 2}; auto a1 = xadapt(data, size, no_ownership(), s); @@ -106,7 +106,7 @@ namespace xt size_t size = 4; int* data = new int[size]; int* data2 = new int[size]; - using shape_type = std::array; + using shape_type = stat_shape; shape_type s = {2, 2}; auto a1 = xadapt(data, size, acquire_ownership(), s); diff --git a/test/test_xarray.cpp b/test/test_xarray.cpp index 408d9ba06..474a80f77 100644 --- a/test/test_xarray.cpp +++ b/test/test_xarray.cpp @@ -32,11 +32,11 @@ namespace xt { SCOPED_TRACE("from shape"); - std::array shp = {5, 4, 2}; - std::vector shp_as_vec = {5, 4, 2}; + stat_shape shp = {5, 4, 2}; + dyn_shape shp_as_vec = {5, 4, 2}; auto ca = xarray::from_shape({3, 2, 1}); auto cb = xarray::from_shape(shp); - std::vector expected_shape = {3, 2, 1}; + dyn_shape expected_shape = {3, 2, 1}; EXPECT_EQ(expected_shape, ca.shape()); EXPECT_EQ(shp_as_vec, cb.shape()); } diff --git a/test/test_xbroadcast.cpp b/test/test_xbroadcast.cpp index e8b96b619..4de8b19a6 100644 --- a/test/test_xbroadcast.cpp +++ b/test/test_xbroadcast.cpp @@ -24,7 +24,7 @@ namespace xt ASSERT_EQ(5.0, m1_broadcast(0, 1, 1)); ASSERT_EQ(m1_broadcast.layout(), m1.layout()); - auto shape = std::vector{1, 2, 3}; + auto shape = dyn_shape{1, 2, 3}; auto m1_broadcast2 = broadcast(m1, shape); ASSERT_EQ(1.0, m1_broadcast2(0, 0, 0)); ASSERT_EQ(4.0, m1_broadcast2(0, 1, 0)); @@ -44,16 +44,16 @@ namespace xt auto m1_broadcast = broadcast(m1, {4, 2, 3}); // access with the right number of arguments - std::array index1 = {0, 1, 1}; + stat_shape index1 = {0, 1, 1}; ASSERT_EQ(5.0, m1_broadcast.element(index1.begin(), index1.end())); // too many arguments = using the last ones only - std::array index3 = {4, 0, 1, 1}; + stat_shape index3 = {4, 0, 1, 1}; ASSERT_EQ(5.0, m1_broadcast.element(index3.begin(), index3.end())); } TEST(xbroadcast, shape_forwarding) { - std::array bc_shape; + stat_shape bc_shape; auto m1_broadcast = broadcast(123, bc_shape); } @@ -62,7 +62,7 @@ namespace xt xarray m1 = {1, 2, 3}; auto m1_broadcast = broadcast(m1, {2, 3}); size_t nb_iter = 3; - using shape_type = std::vector; + using shape_type = dyn_shape; // broadcast_iterator { @@ -95,7 +95,7 @@ namespace xt xarray m1 = {1, 2, 3}; auto m1_broadcast = broadcast(m1, {2, 3}); size_t nb_iter = 3; - using shape_type = std::vector; + using shape_type = dyn_shape; // reverse_broadcast_iterator { diff --git a/test/test_xbuilder.cpp b/test/test_xbuilder.cpp index 628eb2a8d..bd6ec3d5f 100644 --- a/test/test_xbuilder.cpp +++ b/test/test_xbuilder.cpp @@ -16,7 +16,7 @@ namespace xt { using std::size_t; - using shape_t = std::vector; + using shape_t = dyn_shape; TEST(xbuilder, ones) { diff --git a/test/test_xdynamicview.cpp b/test/test_xdynamicview.cpp index 8c994c2dc..832899c84 100644 --- a/test/test_xdynamicview.cpp +++ b/test/test_xdynamicview.cpp @@ -15,7 +15,7 @@ namespace xt { using std::size_t; - using view_shape_type = std::vector; + using view_shape_type = dyn_shape; TEST(xdynview, simple) { @@ -92,7 +92,7 @@ namespace xt EXPECT_EQ(a(1, 1, 0), view1(1, 0)); EXPECT_EQ(a(1, 1, 1), view1(1, 1)); - std::array idx = {1, 1}; + stat_shape idx = {1, 1}; EXPECT_EQ(a(1, 1, 1), view1.element(idx.cbegin(), idx.cend())); } @@ -164,7 +164,7 @@ namespace xt TEST(xdynview, xdynview_on_xtensor) { - xtensor a({3, 4}); + xtensor a({ 3, 4 }); std::vector data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; std::copy(data.cbegin(), data.cend(), a.xbegin()); @@ -241,10 +241,10 @@ namespace xt EXPECT_EQ(a(1, 2), view6(2, 0)); EXPECT_EQ(2, view6.dimension()); - std::array idx1 = {1, 0, 2}; + stat_shape idx1 = {1, 0, 2}; EXPECT_EQ(a(1, 2), view1.element(idx1.begin(), idx1.end())); - std::array idx2 = {1, 2, 0}; + stat_shape idx2 = {1, 2, 0}; EXPECT_EQ(a(1, 2), view2.element(idx2.begin(), idx2.end())); } diff --git a/test/test_xeval.cpp b/test/test_xeval.cpp index 47596c037..1002efe49 100644 --- a/test/test_xeval.cpp +++ b/test/test_xeval.cpp @@ -26,7 +26,7 @@ namespace xt bool type_eq = std::is_same&>::value; EXPECT_TRUE(type_eq); - xtensor t({3, 3}); + xtensor t({ 3, 3 }); auto&& i = eval(t); diff --git a/test/test_xiterator.cpp b/test/test_xiterator.cpp index 81f393fca..fd8a89ecb 100644 --- a/test/test_xiterator.cpp +++ b/test/test_xiterator.cpp @@ -12,6 +12,19 @@ namespace xt { + template + void shape_insert(SHAPE & s, int i, int j) + { + s.insert(s.begin(), i); + s.insert(s.begin(), j); + } + + template + void shape_insert(tiny_array & s, int i, int j) + { + s.insert(0, i).insert(0, j).swap(s); + } + using std::size_t; template @@ -48,8 +61,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_increment(rm, rm.shape()); } } @@ -65,8 +77,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_increment(rm, rm.shape()); } } @@ -82,8 +93,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_increment(rm, rm.shape()); } } @@ -99,8 +109,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_increment(rm, rm.shape()); } } @@ -136,8 +145,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_end(rm); } } @@ -153,8 +161,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_end(rm); } } @@ -170,8 +177,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_end(rm); } } @@ -187,8 +193,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_end(rm); } } @@ -226,8 +231,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_decrement(rm, rm.shape()); } } @@ -243,8 +247,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_decrement(rm, rm.shape()); } } @@ -260,8 +263,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_decrement(rm, rm.shape()); } } @@ -277,8 +279,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_decrement(rm, rm.shape()); } } @@ -314,8 +315,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_rend(rm); } } @@ -331,8 +331,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_rend(rm); } } @@ -348,8 +347,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_rend(rm); } } @@ -365,8 +363,7 @@ namespace xt { SCOPED_TRACE("broadcasting shape"); layout_result<>::shape_type sh = rm.shape(); - sh.insert(sh.begin(), 2); - sh.insert(sh.begin(), 4); + shape_insert(sh, 2, 4); test_rend(rm); } } diff --git a/test/test_xmath.cpp b/test/test_xmath.cpp index 968c41eff..c580926fb 100644 --- a/test/test_xmath.cpp +++ b/test/test_xmath.cpp @@ -16,7 +16,7 @@ namespace xt { using std::size_t; - using shape_type = std::vector; + using shape_type = dyn_shape; /******************** * Basic operations * diff --git a/test/test_xreducer.cpp b/test/test_xreducer.cpp index 203960c8f..af6c654ee 100644 --- a/test/test_xreducer.cpp +++ b/test/test_xreducer.cpp @@ -17,7 +17,7 @@ namespace xt { struct xreducer_features { - using axes_type = std::array; + using axes_type = stat_shape; axes_type m_axes; xarray m_a; using shape_type = xarray::shape_type; diff --git a/test/test_xtensor.cpp b/test/test_xtensor.cpp index 9d6aa0e3b..4f3cee090 100644 --- a/test/test_xtensor.cpp +++ b/test/test_xtensor.cpp @@ -12,7 +12,7 @@ namespace xt { - using container_type = std::array; + using container_type = stat_shape; using xtensor_dynamic = xtensor; TEST(xtensor, initializer_constructor) @@ -47,11 +47,11 @@ namespace xt { SCOPED_TRACE("from shape"); - std::array shp = {5, 4, 2}; - std::vector shp_as_vec = {5, 4, 2}; + stat_shape shp = {5, 4, 2}; + dyn_shape shp_as_vec = {5, 4, 2}; auto ca = xtensor::from_shape({3, 2, 1}); auto cb = xtensor::from_shape(shp_as_vec); - std::vector expected_shape = {3, 2, 1}; + dyn_shape expected_shape = {3, 2, 1}; EXPECT_TRUE(std::equal(expected_shape.begin(), expected_shape.end(), ca.shape().begin())); EXPECT_TRUE(std::equal(shp.begin(), shp.end(), cb.shape().begin())); } diff --git a/test/test_xtensor_adaptor.cpp b/test/test_xtensor_adaptor.cpp index 84c3b0948..9b8d07682 100644 --- a/test/test_xtensor_adaptor.cpp +++ b/test/test_xtensor_adaptor.cpp @@ -14,7 +14,7 @@ namespace xt { using vec_type = std::vector; using adaptor_type = xtensor_adaptor; - using container_type = std::array; + using container_type = stat_shape; TEST(xtensor_adaptor, shaped_constructor) { diff --git a/test/test_xtiny.cpp b/test/test_xtiny.cpp new file mode 100644 index 000000000..39abfbd76 --- /dev/null +++ b/test/test_xtiny.cpp @@ -0,0 +1,635 @@ +/*************************************************************************** +* Copyright (c) 2017, Ullrich Koethe * +* * +* Distributed under the terms of the BSD 3-Clause License. * +* * +* The full license is in the file LICENSE, distributed with this software. * +****************************************************************************/ + +#ifndef XTENSOR_ENABLE_ASSERT +#define XTENSOR_ENABLE_ASSERT +#endif + +#include +#include +#include + +#include "gtest/gtest.h" +#include "xtensor/xtiny.hpp" + +template +bool equalValue(VECTOR const & v, VALUE const & vv) +{ + for (unsigned int i = 0; i +bool equalVector(VECTOR1 const & v1, VECTOR2 const & v2) +{ + for (unsigned int i = 0; i +bool equalIter(ITER1 i1, ITER1 i1end, ITER2 i2, xt::index_t size) +{ + if (i1end - i1 != size) + return false; + for (; i1; + using IV = tiny_array; + using FV = tiny_array; + + static float di[] = { 1, 2, 4}; + static float df[] = { 1.2f, 2.4f, 3.6f}; + BV bv0, bv1{1}, bv3(di); + IV iv0, iv1{1}, iv3(di); + FV fv0, fv1{1.0f}, fv3(df); + + TEST(xtiny, traits) + { + EXPECT_TRUE(BV::may_use_uninitialized_memory); + EXPECT_TRUE((tiny_array::may_use_uninitialized_memory)); + EXPECT_FALSE((tiny_array, SIZE>::may_use_uninitialized_memory)); + + EXPECT_TRUE((std::is_same>::value)); + EXPECT_TRUE((std::is_same, real_promote_t>::value)); + EXPECT_TRUE((std::is_same, real_promote_t>::value)); + + EXPECT_TRUE((std::is_same > >::value)); + EXPECT_TRUE((std::is_same, 1> > >::value)); + EXPECT_TRUE((std::is_same, 1> > >::value)); + EXPECT_TRUE((std::is_same > >::value)); + EXPECT_TRUE((std::is_same, 1> > >::value)); + EXPECT_TRUE((std::is_same, decltype(cos(iv3))>::value)); + } + + TEST(xtiny, construct) + { + EXPECT_TRUE(bv0.size() == SIZE); + EXPECT_TRUE(iv0.size() == SIZE); + EXPECT_TRUE(fv0.size() == SIZE); + + EXPECT_TRUE(equalValue(bv0, 0)); + EXPECT_TRUE(equalValue(iv0, 0)); + EXPECT_TRUE(equalValue(fv0, 0.0f)); + + EXPECT_TRUE(equalValue(bv1, 1)); + EXPECT_TRUE(equalValue(iv1, 1)); + EXPECT_TRUE(equalValue(fv1, 1.0f)); + + EXPECT_TRUE(equalIter(bv3.begin(), bv3.end(), di, SIZE)); + EXPECT_TRUE(equalIter(iv3.begin(), iv3.end(), di, SIZE)); + EXPECT_TRUE(equalIter(fv3.begin(), fv3.end(), df, SIZE)); + + EXPECT_TRUE(!equalVector(bv3, fv3)); + EXPECT_TRUE(!equalVector(iv3, fv3)); + + EXPECT_EQ(iv3, (IV{ 1, 2, 4 })); + EXPECT_EQ(iv3, (IV{ 1.1, 2.2, 4.4 })); + EXPECT_EQ(iv3, (IV({ 1, 2, 4 }))); + EXPECT_EQ(iv3, (IV({ 1.1, 2.2, 4.4 }))); + EXPECT_EQ(iv1, (IV{ 1 })); + EXPECT_EQ(iv1, (IV{ 1.1 })); + EXPECT_EQ(iv1, (IV({ 1 }))); + EXPECT_EQ(iv1, (IV({ 1.1 }))); + EXPECT_EQ(iv1, IV(tags::size = SIZE, 1)); + // these should not compile: + // EXPECT_EQ(iv1, IV(1)); + // EXPECT_EQ(iv3, IV(1, 2, 4)); + + BV bv(round(fv3)); + EXPECT_TRUE(equalIter(bv3.begin(), bv3.end(), bv.begin(), SIZE)); + EXPECT_TRUE(equalVector(bv3, bv)); + + BV bv4(bv3.begin()); + EXPECT_TRUE(equalIter(bv3.begin(), bv3.end(), bv4.begin(), SIZE)); + EXPECT_TRUE(equalVector(bv3, bv4)); + + BV bv5(bv3.begin(), copy_reversed); + EXPECT_TRUE(equalIter(bv3.begin(), bv3.end(), + std::reverse_iterator(bv5.end()), SIZE)); + + FV fv(iv3); + EXPECT_TRUE(equalIter(iv3.begin(), iv3.end(), fv.begin(), SIZE)); + EXPECT_TRUE(equalVector(iv3, fv)); + + fv = fv3; + EXPECT_TRUE(equalIter(fv3.begin(), fv3.end(), fv.begin(), SIZE)); + EXPECT_TRUE(equalVector(fv3, fv)); + + fv = bv3; + EXPECT_TRUE(equalIter(bv3.begin(), bv3.end(), fv.begin(), SIZE)); + EXPECT_TRUE(equalVector(bv3, fv)); + + tiny_array fv5; + fv5.init(fv3.begin(), fv3.end()); + EXPECT_EQ(fv5[0], fv3[0]); + EXPECT_EQ(fv5[1], fv3[1]); + EXPECT_EQ(fv5[2], fv3[2]); + EXPECT_EQ(fv5[3], SIZE <= 3 ? 0.0 : fv3[3]); + EXPECT_EQ(fv5[4], SIZE <= 4 ? 0.0 : fv3[4]); + + EXPECT_EQ(iv3, (iv3.template subarray<0, SIZE>())); + EXPECT_EQ(2, (iv3.template subarray<0, 2>().size())); + EXPECT_EQ(iv3[0], (iv3.template subarray<0, 2>()[0])); + EXPECT_EQ(iv3[1], (iv3.template subarray<0, 2>()[1])); + EXPECT_EQ(2, (iv3.template subarray<1, 3>().size())); + EXPECT_EQ(iv3[1], (iv3.template subarray<1, 3>()[0])); + EXPECT_EQ(iv3[2], (iv3.template subarray<1, 3>()[1])); + EXPECT_EQ(1, (iv3.template subarray<1, 2>().size())); + EXPECT_EQ(iv3[1], (iv3.template subarray<1, 2>()[0])); + EXPECT_EQ(1, (iv3.subarray(1, 2).size())); + EXPECT_EQ(iv3[1], (iv3.subarray(1, 2)[0])); + + for (int k = 0; k FV1; + FV1 fv10(fv3.begin()); + EXPECT_EQ(fv10, fv3.erase(SIZE - 1)); + EXPECT_EQ(fv3, fv10.insert(SIZE - 1, fv3[SIZE - 1])); + FV1 fv11(fv3.begin() + 1); + EXPECT_EQ(fv11, fv3.erase(0)); + } + + TEST(xtiny, comparison) + { + EXPECT_TRUE(bv0 == bv0); + EXPECT_TRUE(bv0 == 0); + EXPECT_TRUE(0 == bv0); + EXPECT_TRUE(iv0 == iv0); + EXPECT_TRUE(fv0 == fv0); + EXPECT_TRUE(fv0 == 0); + EXPECT_TRUE(0 == fv0); + EXPECT_TRUE(iv0 == bv0); + EXPECT_TRUE(iv0 == fv0); + EXPECT_TRUE(fv0 == bv0); + + EXPECT_TRUE(bv3 == bv3); + EXPECT_TRUE(iv3 == iv3); + EXPECT_TRUE(fv3 == fv3); + EXPECT_TRUE(iv3 == bv3); + EXPECT_TRUE(iv3 != fv3); + EXPECT_TRUE(iv3 != 0); + EXPECT_TRUE(0 != iv3); + EXPECT_TRUE(fv3 != bv3); + EXPECT_TRUE(fv3 != 0); + EXPECT_TRUE(0 != fv3); + + EXPECT_TRUE(bv0 < bv1); + + EXPECT_TRUE(all_less(bv0, bv1)); + EXPECT_TRUE(!all_less(bv1, bv3)); + EXPECT_TRUE(all_greater(bv1, bv0)); + EXPECT_TRUE(!all_greater(bv3, bv1)); + EXPECT_TRUE(all_less_equal(bv0, bv1)); + EXPECT_TRUE(all_less_equal(0, bv0)); + EXPECT_TRUE(all_less_equal(bv0, 0)); + EXPECT_TRUE(all_less_equal(bv1, bv3)); + EXPECT_TRUE(!all_less_equal(bv3, bv1)); + EXPECT_TRUE(all_greater_equal(bv1, bv0)); + EXPECT_TRUE(all_greater_equal(bv3, bv1)); + EXPECT_TRUE(!all_greater_equal(bv1, bv3)); + + EXPECT_TRUE(isclose(fv3, fv3)); + + EXPECT_TRUE(!any(bv0) && !all(bv0) && any(bv1) && all(bv1)); + EXPECT_TRUE(!any(iv0) && !all(iv0) && any(iv1) && all(iv1)); + EXPECT_TRUE(!any(fv0) && !all(fv0) && any(fv1) && all(fv1)); + IV iv; + iv = IV(); iv[0] = 1; + EXPECT_TRUE(any(iv) && !all(iv)); + iv = IV(); iv[1] = 1; + EXPECT_TRUE(any(iv) && !all(iv)); + iv = IV(); iv[SIZE - 1] = 1; + EXPECT_TRUE(any(iv) && !all(iv)); + iv = IV{ 1 }; iv[0] = 0; + EXPECT_TRUE(any(iv) && !all(iv)); + iv = IV{ 1 }; iv[1] = 0; + EXPECT_TRUE(any(iv) && !all(iv)); + iv = IV{ 1 }; iv[SIZE - 1] = 0; + EXPECT_TRUE(any(iv) && !all(iv)); + } + + TEST(xtiny, arithmetic) + { + using namespace cmath; + + IV ivm3 = -iv3; + FV fvm3 = -fv3; + + int mi[] = { -1, -2, -4, -5, -8, -10 }; + float mf[] = { -1.2f, -2.4f, -3.6f, -4.8f, -8.1f, -9.7f }; + + EXPECT_TRUE(equalIter(ivm3.begin(), ivm3.end(), mi, SIZE)); + EXPECT_TRUE(equalIter(fvm3.begin(), fvm3.end(), mf, SIZE)); + + IV iva3 = abs(ivm3); + FV fva3 = abs(fvm3); + EXPECT_TRUE(equalVector(iv3, iva3)); + EXPECT_TRUE(equalVector(fv3, fva3)); + + int fmi[] = { -2, -3, -4, -5, -9, -10 }; + int fpi[] = { 1, 2, 3, 4, 8, 9 }; + int ri[] = { 1, 2, 4, 5, 8, 10 }; + IV ivi3 = floor(fvm3); + EXPECT_TRUE(equalIter(ivi3.begin(), ivi3.end(), fmi, SIZE)); + ivi3 = -ceil(fv3); + EXPECT_TRUE(equalIter(ivi3.begin(), ivi3.end(), fmi, SIZE)); + ivi3 = round(fv3); + EXPECT_TRUE(equalIter(ivi3.begin(), ivi3.end(), ri, SIZE)); + ivi3 = floor(fv3); + EXPECT_TRUE(equalIter(ivi3.begin(), ivi3.end(), fpi, SIZE)); + ivi3 = -ceil(fvm3); + EXPECT_TRUE(equalIter(ivi3.begin(), ivi3.end(), fpi, SIZE)); + ivi3 = -round(fvm3); + EXPECT_TRUE(equalIter(ivi3.begin(), ivi3.end(), ri, SIZE)); + + EXPECT_EQ(clip_lower(iv3), iv3); + EXPECT_EQ(clip_lower(iv3, 11), IV{ 11 }); + EXPECT_EQ(clip_upper(iv3, 0), IV{ 0 }); + EXPECT_EQ(clip_upper(iv3, 11), iv3); + EXPECT_EQ(clip(iv3, 0, 11), iv3); + EXPECT_EQ(clip(iv3, 11, 12), IV{ 11 }); + EXPECT_EQ(clip(iv3, -1, 0), IV{ 0 }); + EXPECT_EQ(clip(iv3, IV{0 }, IV{11}), iv3); + EXPECT_EQ(clip(iv3, IV{11}, IV{12}), IV{11}); + EXPECT_EQ(clip(iv3, IV{-1}, IV{0 }), IV{0 }); + + EXPECT_TRUE(squared_norm(bv1) == SIZE); + EXPECT_TRUE(squared_norm(iv1) == SIZE); + EXPECT_TRUE(squared_norm(fv1) == (float)SIZE); + + float expectedSM = 1.2f*1.2f + 2.4f*2.4f + 3.6f*3.6f; + EXPECT_NEAR(squared_norm(fv3), expectedSM, 1e-6); + + EXPECT_EQ(static_cast(dot(bv3, bv3)), squared_norm(bv3)); + EXPECT_EQ(static_cast(dot(iv3, bv3)), squared_norm(iv3)); + EXPECT_NEAR(dot(fv3, fv3), squared_norm(fv3), 1e-6); + + tiny_array ivv{ iv3, iv3, iv3 }; + EXPECT_EQ(squared_norm(ivv), 3 * squared_norm(iv3)); + EXPECT_EQ(norm(ivv), sqrt(3.0*static_cast(squared_norm(iv3)))); + EXPECT_EQ(elementwise_norm(iv3), iv3); + EXPECT_EQ(elementwise_squared_norm(iv3), (IV{ 1, 4, 16 })); + + EXPECT_TRUE(isclose(sqrt(dot(bv3, bv3)), norm(bv3), 0.0)); + EXPECT_TRUE(isclose(sqrt(dot(iv3, bv3)), norm(iv3), 0.0)); + EXPECT_TRUE(isclose(sqrt(dot(fv3, fv3)), norm(fv3), 0.0)); + EXPECT_NEAR(sqrt(dot(bv3, bv3)), norm(bv3), 1e-6); + EXPECT_NEAR(sqrt(dot(iv3, bv3)), norm(iv3), 1e-6); + EXPECT_NEAR(sqrt(dot(fv3, fv3)), norm(fv3), 1e-6); + + BV bv = bv3; + bv[2] = 200; + uint64_t expectedSM2 = 40005; + EXPECT_EQ(static_cast(dot(bv, bv)), expectedSM2); + EXPECT_EQ(squared_norm(bv), expectedSM2); + + EXPECT_TRUE(equalVector(bv0 + 1.0, fv1)); + EXPECT_TRUE(equalVector(1.0 + bv0, fv1)); + EXPECT_TRUE(equalVector(bv1 - 1.0, fv0)); + EXPECT_TRUE(equalVector(1.0 - bv1, fv0)); + EXPECT_TRUE(equalVector(bv3 - iv3, bv0)); + EXPECT_TRUE(equalVector(fv3 - fv3, fv0)); + BV bvp = (bv3 + bv3)*0.5; + FV fvp = (fv3 + fv3)*0.5; + EXPECT_TRUE(equalVector(bvp, bv3)); + EXPECT_TRUE(equalVector(fvp, fv3)); + bvp = 2.0*bv3 - bv3; + fvp = 2.0*fv3 - fv3; + EXPECT_TRUE(equalVector(bvp, bv3)); + EXPECT_TRUE(equalVector(fvp, fv3)); + + IV ivp = bv + bv; + int ip1[] = { 2, 4, 400, 10, 16, 20 }; + EXPECT_TRUE(equalIter(ivp.begin(), ivp.end(), ip1, SIZE)); + EXPECT_TRUE(equalVector(bv0 - iv1, -iv1)); + + bvp = bv3 / 2.0; + fvp = bv3 / 2.0; + int ip[] = { 0, 1, 2, 3, 4, 5 }; + float fp[] = { 0.5, 1.0, 2.0, 2.5, 4.0, 5.0 }; + EXPECT_TRUE(equalIter(bvp.begin(), bvp.end(), ip, SIZE)); + EXPECT_TRUE(equalIter(fvp.begin(), fvp.end(), fp, SIZE)); + fvp = fv3 / 2.0; + float fp1[] = { 0.6f, 1.2f, 1.8f, 2.4f, 4.05f, 4.85f }; + EXPECT_TRUE(equalIter(fvp.begin(), fvp.end(), fp1, SIZE)); + EXPECT_EQ(2.0 / fv1, 2.0 * fv1); + float fp2[] = { 1.0f, 0.5f, 0.25f, 0.2f, 0.125f, 0.1f }; + fvp = 1.0 / bv3; + EXPECT_TRUE(equalIter(fvp.begin(), fvp.end(), fp2, SIZE)); + + int ivsq[] = { 1, 4, 16, 25, 64, 100 }; + ivp = iv3*iv3; + EXPECT_TRUE(equalIter(ivp.begin(), ivp.end(), ivsq, SIZE)); + EXPECT_EQ(iv3 * iv1, iv3); + EXPECT_EQ(iv0 * iv3, iv0); + EXPECT_EQ(iv3 / iv3, iv1); + EXPECT_EQ(iv3 % iv3, iv0); + EXPECT_EQ(iv3 % (iv3 + iv1), iv3); + + float minRef[] = { 1.0f, 2.0f, 3.6f, 4.8f, 8.0f, 9.7f }; + float minRefScalar[] = { 1.2f, 2.4f, 3.6f, 4.0f, 4.0f, 4.0f }; + auto minRes = min(iv3, fv3); + EXPECT_TRUE(equalIter(minRef, minRef + SIZE, minRes.cbegin(), SIZE)); + minRes = min(4.0f, fv3); + EXPECT_TRUE(equalIter(minRefScalar, minRefScalar + SIZE, minRes.cbegin(), SIZE)); + minRes = min(fv3, 4.0f); + EXPECT_TRUE(equalIter(minRefScalar, minRefScalar + SIZE, minRes.cbegin(), SIZE)); + IV ivmin = floor(fv3); + ivmin[1] = 3; + int minRef2[] = { 1, 2, 3, 4, 8, 9 }; + auto minRes2 = min(iv3, ivmin); + EXPECT_TRUE(equalIter(minRef2, minRef2 + SIZE, minRes2.cbegin(), SIZE)); + EXPECT_EQ(min(iv3), di[0]); + EXPECT_EQ(min(fv3), df[0]); + EXPECT_EQ(max(iv3), di[SIZE - 1]); + EXPECT_EQ(max(fv3), df[SIZE - 1]); + + float maxRef[] = { 1.2f, 2.4f, 4.0f, 5.0f, 8.1f, 10.0f }; + EXPECT_TRUE(equalIter(maxRef, maxRef + SIZE, max(iv3, fv3).begin(), SIZE)); + float maxRefScalar[] = { 4.0f, 4.0f, 4.0f, 4.8f, 8.1f, 9.7f }; + EXPECT_TRUE(equalIter(maxRefScalar, maxRefScalar + SIZE, max(4.0f, fv3).begin(), SIZE)); + EXPECT_TRUE(equalIter(maxRefScalar, maxRefScalar + SIZE, max(fv3, 4.0f).begin(), SIZE)); + IV ivmax = floor(fv3); + ivmax[1] = 3; + int maxRef2[] = { 1, 3, 4, 5, 8, 10 }; + EXPECT_TRUE(equalIter(maxRef2, maxRef2 + SIZE, max(iv3, ivmax).begin(), SIZE)); + + EXPECT_EQ(sqrt(iv3 * iv3), iv3); + EXPECT_EQ(sqrt(pow(iv3, 2)), iv3); + + EXPECT_EQ(sum(iv3), 7); + EXPECT_EQ(sum(fv3), 7.2f); + EXPECT_EQ(prod(iv3), 8); + EXPECT_EQ(prod(fv3), 10.368f); + EXPECT_NEAR(mean(iv3), 7.0 / SIZE, 1e-7); + + float cumsumRef[] = { 1.2f, 3.6f, 7.2f}; + FV cs = cumsum(fv3), csr(cumsumRef); + EXPECT_TRUE(isclose(cs, csr, 1e-6f)); + float cumprodRef[] = { 1.2f, 2.88f, 10.368f}; + FV cr = cumprod(fv3), crr(cumprodRef); + EXPECT_TRUE(isclose(cr, crr, 1e-6f)); + + tiny_array src{ 1, 2, -3, -4 }, signs{ 2, -3, 4, -5 }; + EXPECT_EQ(copysign(src, signs), (tiny_array{1, -2, 3, -4})); + + tiny_array left{ 3., 5., 8. }, right{ 4., 12., 15. }; + EXPECT_EQ(hypot(left, right), (tiny_array{5., 13., 17.})); + + int oddRef[] = { 1, 0, 0, 1, 0, 0 }; + EXPECT_TRUE(equalIter(oddRef, oddRef + SIZE, odd(iv3).begin(), SIZE)); + EXPECT_TRUE(equalIter(oddRef, oddRef + SIZE, (iv3 & 1).begin(), SIZE)); + } + + TEST(xtiny, cross_product) + { + EXPECT_EQ(cross(bv3, bv3), IV{0}); + EXPECT_EQ(cross(iv3, bv3), IV{0}); + EXPECT_TRUE(isclose(cross(fv3, fv3), FV{ 0.0f }, 1e-6f)); + + FV cr = cross(fv1, fv3), crr{ 1.2f, -2.4f, 1.2f }; + EXPECT_TRUE(isclose(cr, crr, 1e-6f)); + } + + TEST(xtiny, ostream) + { + std::ostringstream out; + out << iv3; + std::string expected("{1, 2, 4}"); + EXPECT_EQ(expected, out.str()); + out << "Testing.." << fv3 << 42; + out << bv3 << std::endl; + } + + TEST(xtiny, 2D) + { + using std::sqrt; + + using Array = tiny_array; + using Index = tiny_array; + + EXPECT_TRUE(Array::static_ndim == 2); + EXPECT_TRUE(Array::static_size == 6); + EXPECT_TRUE((std::is_same::value)); + + int adata[] = { 4,5,6,7,8,9 }; + Array a(adata); + EXPECT_EQ(a.ndim(), 2); + EXPECT_EQ(a.size(), 6); + EXPECT_EQ(a.shape(), (Index{ 2, 3 })); + + int count = 0, i, j; + Index idx; + for (i = 0, idx[0] = 0; i<2; ++i, ++idx[0]) + { + for (j = 0, idx[1] = 0; j<3; ++j, ++count, ++idx[1]) + { + EXPECT_EQ(a[count], adata[count]); + EXPECT_EQ((a[{i, j}]), adata[count]); + EXPECT_EQ(a[idx], adata[count]); + EXPECT_EQ(a(i, j), adata[count]); + } + } + { + std::string s = "{4, 5, 6,\n 7, 8, 9}"; + std::stringstream ss; + ss << a; + EXPECT_EQ(s, ss.str()); + } + + tiny_symmetric_view sym(a.data()); + EXPECT_EQ(sym.shape(), (Index{ 3, 3 })); + { + std::string s = "{4, 5, 6,\n 5, 7, 8,\n 6, 8, 9}"; + std::stringstream ss; + ss << sym; + EXPECT_EQ(s, ss.str()); + } + + Array::as_type b = a; + EXPECT_EQ(a, b); + + int adata2[] = { 0,1,2,3,4,5 }; + a = { 0,1,2,3,4,5 }; + EXPECT_TRUE(equalIter(a.begin(), a.end(), adata2, a.size())); + Array c = reversed(a); + EXPECT_TRUE(equalIter(c.rbegin(), c.rend(), adata2, c.size())); + + EXPECT_TRUE(a == a); + EXPECT_TRUE(a != b); + EXPECT_TRUE(a < b); + EXPECT_TRUE(any(a)); + EXPECT_TRUE(!all(a)); + EXPECT_TRUE(any(b)); + EXPECT_TRUE(all(b)); + EXPECT_TRUE(!all_zero(a)); + EXPECT_TRUE(all_less(a, b)); + EXPECT_TRUE(all_less_equal(a, b)); + EXPECT_TRUE(!all_greater(a, b)); + EXPECT_TRUE(!all_greater_equal(a, b)); + EXPECT_TRUE(isclose(a, b, 10.0f)); + + EXPECT_EQ(squared_norm(a), 55u); + EXPECT_TRUE(isclose(norm(a), sqrt(55.0), 1e-15)); + EXPECT_NEAR(norm(a), sqrt(55.0), 1e-15); + EXPECT_EQ(min(a), 0); + EXPECT_EQ(max(a), 5); + EXPECT_EQ(max(a, b), b); + + swap(b, c); + EXPECT_TRUE(equalIter(c.cbegin(), c.cend(), adata, c.size())); + EXPECT_TRUE(equalIter(b.crbegin(), b.crend(), adata2, b.size())); + + int eyedata[] = { 1, 0, 0, 0, 1, 0, 0, 0, 1 }; + auto eye = Array::eye<3>(); + EXPECT_TRUE(equalIter(eye.begin(), eye.end(), eyedata, eye.size())); + } + + TEST(xtiny, runtime_size) + { + using A = tiny_array; + using V1 = tiny_array; + + EXPECT_TRUE(typeid(A) == typeid(tiny_array)); + + A a{ 1,2,3 }, b{ 1,2,3 }, c = a, d = a + b, e(3); + EXPECT_EQ(a.size(), 3); + EXPECT_EQ(b.size(), 3); + EXPECT_EQ(c.size(), 3); + EXPECT_EQ(d.size(), 3); + EXPECT_EQ(e.size(), 3); + EXPECT_EQ(a, b); + EXPECT_EQ(a, c); + EXPECT_TRUE(a != d); + EXPECT_TRUE(a != e); + EXPECT_TRUE(a < d); + EXPECT_TRUE(e < a); + EXPECT_EQ(e, (A{ 0,0,0 })); + + EXPECT_EQ(iv3, (A{ 1, 2, 4 })); + EXPECT_EQ(iv3, (A{ 1.1, 2.2, 4.4 })); + EXPECT_EQ(iv3, (A({ 1, 2, 4 }))); + EXPECT_EQ(iv3, (A({ 1.1, 2.2, 4.4 }))); + EXPECT_EQ(V1{1}, (A{ 1 })); + EXPECT_EQ(V1{1}, (A{ 1.1 })); + EXPECT_EQ(V1{1}, (A({ 1 }))); + EXPECT_EQ(V1{1}, (A({ 1.1 }))); + EXPECT_EQ(iv0, A(SIZE)); + EXPECT_EQ(iv0, A(tags::size = SIZE)); + EXPECT_EQ(iv1, A(SIZE, 1)); + EXPECT_EQ(iv1, A(tags::size = SIZE, 1)); + + c.init(2, 4, 6); + EXPECT_EQ(d, c); + c.init({ 1,2,3 }); + EXPECT_EQ(a, c); + c = 2 * a; + EXPECT_EQ(d, c); + c.reverse(); + EXPECT_EQ(c, (A{ 6,4,2 })); + EXPECT_EQ(c, reversed(d)); + c = c - 2; + EXPECT_TRUE(all(d)); + EXPECT_TRUE(!all(c)); + EXPECT_TRUE(any(c)); + EXPECT_TRUE(!all_zero(c)); + EXPECT_TRUE(!all(e)); + EXPECT_TRUE(!any(e)); + EXPECT_TRUE(all_zero(e)); + + EXPECT_EQ(prod(a), 6); + EXPECT_EQ(prod(A()), 0); + + EXPECT_EQ(cross(a, a), e); + EXPECT_EQ(dot(a, a), 14); + EXPECT_EQ(squared_norm(a), 14u); + + EXPECT_EQ(a.erase(1), (A{ 1,3 })); + EXPECT_EQ(a.insert(3, 4), (A{ 1,2,3,4 })); + + // testing move constructor and assignment + EXPECT_EQ(std::move(A{ 1,2,3 }), (A{ 1,2,3 })); + EXPECT_EQ(A(a.insert(3, 4)), (A{ 1,2,3,4 })); + a = a.insert(3, 4); + EXPECT_EQ(a, (A{ 1,2,3,4 })); + + A r = A::range(2, 6); + EXPECT_EQ(r, (A{ 2,3,4,5 })); + EXPECT_EQ(r.subarray(1, 3).size(), 2); + EXPECT_EQ(r.subarray(1, 3), (A{ 3,4 })); + EXPECT_EQ((r.template subarray<1, 3>().size()), 2); + EXPECT_EQ((r.template subarray<1, 3>()), (A{ 3,4 })); + + EXPECT_EQ(A::range(0, 6, 3), (A{ 0,3 })); + EXPECT_EQ(A::range(0, 7, 3), (A{ 0,3,6 })); + EXPECT_EQ(A::range(0, 8, 3), (A{ 0,3,6 })); + EXPECT_EQ(A::range(0, 9, 3), (A{ 0,3,6 })); + EXPECT_EQ(A::range(10, 2, -2), (A{ 10, 8, 6, 4 })); + EXPECT_EQ(A::range(10, 1, -2), (A{ 10, 8, 6, 4, 2 })); + EXPECT_EQ(A::range(10, 0, -2), (A{ 10, 8, 6, 4, 2 })); + + EXPECT_EQ(transpose(A::range(1, 4)), (A{ 3,2,1 })); + EXPECT_EQ(transpose(A::range(1, 4), A{ 1,2,0 }), (A{ 2,3,1 })); + + EXPECT_THROW(A(3) / A(2), std::runtime_error); + + using TA = tiny_array; + TA s(A{ 1,2,3 }); + EXPECT_EQ(s, (TA{ 1,2,3 })); + s = A{ 3,4,5 }; + EXPECT_EQ(s, (TA{ 3,4,5 })); + + EXPECT_THROW({ TA(A{ 1,2,3,4 }); }, std::runtime_error); + + EXPECT_EQ((A{ 0,0,0 }), A(tags::size = 3)); + EXPECT_EQ((A{ 1,1,1 }), A(tags::size = 3, 1)); + + EXPECT_EQ(A::unit_vector(tags::size = 3, 1), TA::unit_vector(1)); + } + +} // namespace xt \ No newline at end of file diff --git a/test/test_xutils.cpp b/test/test_xutils.cpp index 49238c066..c664c62ea 100644 --- a/test/test_xutils.cpp +++ b/test/test_xutils.cpp @@ -94,13 +94,13 @@ namespace xt TEST(utils, promote_shape) { bool expect_v = std::is_same< - std::vector, - promote_shape_t, std::array, std::array> + dyn_shape, + promote_shape_t, stat_shape, stat_shape> >::value; bool expect_a = std::is_same< - std::array, - promote_shape_t, std::array, std::array> + stat_shape, + promote_shape_t, stat_shape, stat_shape> >::value; ASSERT_TRUE(expect_v); diff --git a/test/test_xvectorize.cpp b/test/test_xvectorize.cpp index ecf3aa4b4..112c63f02 100644 --- a/test/test_xvectorize.cpp +++ b/test/test_xvectorize.cpp @@ -18,7 +18,7 @@ namespace xt return d1 + d2; } - using shape_type = std::vector; + using shape_type = dyn_shape; TEST(xvectorize, function) { diff --git a/test/test_xview.cpp b/test/test_xview.cpp index 09bf801e9..191810cbd 100644 --- a/test/test_xview.cpp +++ b/test/test_xview.cpp @@ -15,7 +15,7 @@ namespace xt { using std::size_t; - using view_shape_type = std::vector; + using view_shape_type = dyn_shape; TEST(xview, temporary_type) { @@ -114,7 +114,7 @@ namespace xt EXPECT_EQ(a(1, 1, 0), view1(1, 0)); EXPECT_EQ(a(1, 1, 1), view1(1, 1)); - std::array idx = {1, 1}; + stat_shape idx = {1, 1}; EXPECT_EQ(a(1, 1, 1), view1.element(idx.cbegin(), idx.cend())); } @@ -377,14 +377,15 @@ namespace xt EXPECT_EQ(a(1, 2), view6(2, 0)); EXPECT_EQ(2, view6.dimension()); - std::array idx1 = {1, 0, 2}; + stat_shape idx1 = {1, 0, 2}; EXPECT_EQ(a(1, 2), view1.element(idx1.begin(), idx1.end())); - std::array idx2 = {1, 2, 0}; + stat_shape idx2 = {1, 2, 0}; EXPECT_EQ(a(1, 2), view2.element(idx2.begin(), idx2.end())); - std::array idx3 = {1, 2}; - EXPECT_EQ(a(1, 2), view3.element(idx2.begin(), idx2.end())); + // FIXME: test is broken, idx3 is incorrectly initialized and unused + //stat_shape idx3 = {1, 2}; + //EXPECT_EQ(a(1, 2), view3.element(idx2.begin(), idx2.end())); } TEST(xview, newaxis_iterating)