/*************************************************************************** * Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and * * Martin Renou * * Copyright (c) QuantStack * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ #ifndef XSIMD_FALLBACK_HPP #define XSIMD_FALLBACK_HPP #include #include #include #include #include "xsimd_scalar.hpp" #include "xsimd_base.hpp" #include "xsimd_complex_base.hpp" #include "xsimd_utils.hpp" #ifdef XSIMD_ENABLE_XTL_COMPLEX #include "xtl/xcomplex.hpp" #endif namespace xsimd { /*********************************************************** * Generic fallback implementation of batch and batch_bool * * * * Basically, generate a scalar loop and cross fingers: * * maybe the compiler will autovectorize, maybe not. * ***********************************************************/ /******************** * batch_bool * ********************/ template struct simd_batch_traits> { using value_type = T; static constexpr std::size_t size = N; using batch_type = batch; static constexpr std::size_t align = XSIMD_DEFAULT_ALIGNMENT; }; template class batch_bool : public simd_batch_bool> { public: batch_bool(); explicit batch_bool(bool b); // Constructor from N boolean parameters template < typename... Args, typename Enable = detail::is_array_initializer_t > batch_bool(Args... exactly_N_bools); batch_bool(const std::array& rhs); batch_bool& operator=(const std::array& rhs); operator std::array() const; const bool& operator[](std::size_t index) const; bool& operator[](std::size_t index); const std::array& get_value() const; private: template batch_bool& load_values(Args... args); std::array m_value; friend class simd_batch_bool>; }; /*************** * batch * ***************/ template struct simd_batch_traits> { using value_type = T; static constexpr std::size_t size = N; using batch_bool_type = batch_bool; static constexpr std::size_t align = XSIMD_DEFAULT_ALIGNMENT; using storage_type = std::array; }; template class batch : public simd_batch> { public: using self_type = batch; using base_type = simd_batch; using storage_type = typename base_type::storage_type; using batch_bool_type = typename base_type::batch_bool_type; batch(); explicit batch(T f); // Constructor from N scalar parameters template < typename... Args, typename Enable = typename detail::is_array_initializer::type > batch(Args... exactly_N_scalars); // Constructor from value_type of batch_bool batch(const std::array& src); explicit batch(const T* src); batch(const T* src, aligned_mode); batch(const T* src, unaligned_mode); batch(const std::array& rhs); batch(const batch_bool_type& rhs); batch& operator=(const std::array& rhs); batch& operator=(const std::array& rhs); batch& operator=(const batch_bool_type&); operator std::array() const; XSIMD_DECLARE_LOAD_STORE_ALL(T, N) using base_type::load_aligned; using base_type::load_unaligned; using base_type::store_aligned; using base_type::store_unaligned; T& operator[](std::size_t index); const T& operator[](std::size_t index) const; private: template batch& load_unaligned_impl(const U* src); template void store_unaligned_impl(U* src) const; }; template batch operator<<(const batch& lhs, int32_t rhs); template batch operator>>(const batch& lhs, int32_t rhs); /********************************** * batch_bool, N> * **********************************/ template struct simd_batch_traits, N>> : complex_batch_bool_traits, T, N, XSIMD_DEFAULT_ALIGNMENT> { }; template class batch_bool, N> : public simd_complex_batch_bool, N>> { public: using self_type = batch_bool, N>; using base_type = simd_complex_batch_bool; using real_batch = batch_bool; batch_bool() = default; using base_type::base_type; // Constructor from N boolean parameters template < typename... Args, typename Enable = detail::is_array_initializer_t > batch_bool(Args... exactly_N_bools) : base_type(real_batch{ exactly_N_bools... }) { } }; /***************************** * batch, N> * *****************************/ template struct simd_batch_traits, N>> : complex_batch_traits, T, N, XSIMD_DEFAULT_ALIGNMENT> { }; template class batch, N> : public simd_complex_batch, N>> { public: using self_type = batch, N>; using base_type = simd_complex_batch; using value_type = std::complex; using real_batch = batch; using real_value_type = T; batch() = default; using base_type::base_type; // Constructor from N scalar parameters template < typename... Args, typename Enable = typename detail::is_array_initializer::type > batch(Args... exactly_N_scalars) : base_type(real_batch{ exactly_N_scalars.real()... }, real_batch{ exactly_N_scalars.imag()... }) { } using base_type::load_aligned; using base_type::load_unaligned; using base_type::store_aligned; using base_type::store_unaligned; template typename std::enable_if::value, self_type&>::type load_aligned(const U* src); template typename std::enable_if::value, self_type&>::type load_unaligned(const U* src); template typename std::enable_if::value, self_type&>::type load_aligned(const U* src); template typename std::enable_if::value, self_type&>::type load_unaligned(const U* src); template void store_aligned(U* dst) const; template void store_unaligned(U* dst) const; }; #ifdef XSIMD_ENABLE_XTL_COMPLEX /******************************************** * batch_bool, N> * ********************************************/ template struct simd_batch_traits, N>> : complex_batch_bool_traits, T, N, XSIMD_DEFAULT_ALIGNMENT> { }; template class batch_bool, N> : public simd_complex_batch_bool, N>> { public: using self_type = batch_bool, N>; using base_type = simd_complex_batch_bool; using real_batch = batch_bool; batch_bool() = default; using base_type::base_type; // VS2015 has a bug with inheriting constructors involving SFINAE // Constructor from N boolean parameters template < typename... Args, typename Enable = detail::is_array_initializer_t > batch_bool(Args... exactly_N_bools) : base_type(real_batch{ exactly_N_bools... }) { } }; /*************************************** * batch, N> * ***************************************/ template struct simd_batch_traits, N>> : complex_batch_traits, T, N, XSIMD_DEFAULT_ALIGNMENT> { }; template class batch, N> : public simd_complex_batch, N>> { public: using self_type = batch, N>; using base_type = simd_complex_batch; using value_type = xtl::xcomplex; using real_batch = batch; using real_value_type = T; batch() = default; using base_type::base_type; // Constructor from N scalar parameters template < typename... Args, typename Enable = typename detail::is_array_initializer::type > batch(Args... exactly_N_scalars) : base_type(real_batch{ exactly_N_scalars.real()... }, real_batch{ exactly_N_scalars.imag()... }) { } using base_type::load_aligned; using base_type::load_unaligned; using base_type::store_aligned; using base_type::store_unaligned; template typename std::enable_if::value, self_type&>::type load_aligned(const U* src); template typename std::enable_if::value, self_type&>::type load_unaligned(const U* src); template typename std::enable_if::value, self_type&>::type load_aligned(const U* src); template typename std::enable_if::value, self_type&>::type load_unaligned(const U* src); template void store_aligned(U* dst) const; template void store_unaligned(U* dst) const; }; #endif /************************ * conversion functions * ************************/ template batch to_int(const batch& x); template batch to_int(const batch& x); template batch to_float(const batch& x); template batch to_float(const batch& x); /************************** * boolean cast functions * **************************/ template batch_bool bool_cast(const batch_bool& x); template batch_bool bool_cast(const batch_bool& x); template batch_bool bool_cast(const batch_bool& x); template batch_bool bool_cast(const batch_bool& x); /************************** * Boilerplate generators * **************************/ // These macros all asume that T and N are in scope and have the meaning used in // the definitions of batch and batch_bool. #define XSIMD_FALLBACK_MAPPING_LOOP(RESULT_TYPE, EXPRESSION) \ RESULT_TYPE result; \ for(std::size_t i = 0; i < N; ++i) { \ result[i] = (EXPRESSION); \ } \ return result; #define XSIMD_FALLBACK_UNARY_OP(RESULT_TYPE, OPERATOR, X) \ XSIMD_FALLBACK_MAPPING_LOOP(RESULT_TYPE, (OPERATOR X[i])) #define XSIMD_FALLBACK_BINARY_OP(RESULT_TYPE, OPERATOR, X, Y) \ XSIMD_FALLBACK_MAPPING_LOOP(RESULT_TYPE, (X[i] OPERATOR Y[i])) #define XSIMD_FALLBACK_BATCH_BITWISE_UNARY_OP(OPERATOR, X) \ XSIMD_FALLBACK_MAPPING_LOOP( \ batch, \ detail::from_unsigned_integer( \ OPERATOR detail::to_unsigned_integer(X[i]) \ ) \ ) #define XSIMD_FALLBACK_BATCH_BITWISE_BINARY_OP(OPERATOR, X, Y) \ XSIMD_FALLBACK_MAPPING_LOOP( \ batch, \ detail::from_unsigned_integer( \ detail::to_unsigned_integer(X[i]) \ OPERATOR \ detail::to_unsigned_integer(Y[i]) \ ) \ ) #define XSIMD_FALLBACK_BATCH_UNARY_FUNC(FUNCTION, X) \ XSIMD_FALLBACK_MAPPING_LOOP(batch, FUNCTION(X[i])) #define XSIMD_FALLBACK_BATCH_BINARY_FUNC(FUNCTION, X, Y) \ XSIMD_FALLBACK_MAPPING_LOOP(batch, FUNCTION(X[i], Y[i])) #define XSIMD_FALLBACK_BATCH_TERNARY_FUNC(FUNCTION, X, Y, Z) \ XSIMD_FALLBACK_MAPPING_LOOP(batch, FUNCTION(X[i], Y[i], Z[i])) // NOTE: Static casting a vector is static casting every element #define XSIMD_FALLBACK_BATCH_STATIC_CAST(T_OUT, X) \ batch result; \ for(std::size_t i = 0; i < N; ++i) { \ result[i] = static_cast(X[i]); \ } \ return result; // NOTE: Casting between batch_bools of the same size is actually trivial! #define XSIMD_FALLBACK_BOOL_CAST(T_OUT, X) \ return batch_bool(static_cast>(X)); /*********************************** * batch_bool implementation * ***********************************/ template inline batch_bool::batch_bool() { } template inline batch_bool::batch_bool(bool b) : m_value(detail::array_from_scalar(b)) { } template template inline batch_bool::batch_bool(Args... exactly_N_bools) : m_value{ exactly_N_bools... } { } template inline batch_bool::batch_bool(const std::array& rhs) : m_value(rhs) { } template inline batch_bool& batch_bool::operator=(const std::array& rhs) { m_value = rhs; return *this; } template inline batch_bool::operator std::array() const { return m_value; } template inline const bool& batch_bool::operator[](std::size_t index) const { return m_value[index]; } template inline bool& batch_bool::operator[](std::size_t index) { return m_value[index]; } template inline const std::array& batch_bool::get_value() const { return m_value; } template template inline batch_bool& batch_bool::load_values(Args... args) { m_value = std::array({args...}); return *this; } namespace detail { template struct batch_bool_kernel { using batch_type = batch_bool; static batch_type bitwise_and(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch_bool, &, lhs, rhs) } static batch_type bitwise_or(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch_bool, | , lhs, rhs) } static batch_type bitwise_xor(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch_bool, ^, lhs, rhs) } static batch_type bitwise_not(const batch_type& rhs) { XSIMD_FALLBACK_UNARY_OP(batch_bool, !, rhs) } static batch_type bitwise_andnot(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_MAPPING_LOOP(batch_bool, (!(lhs[i] & rhs[i]))) } static batch_type equal(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch_bool, == , lhs, rhs) } static batch_type not_equal(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch_bool, != , lhs, rhs) } static bool all(const batch_type& rhs) { for (std::size_t i = 0; i < N; ++i) { if (!rhs[i]) return false; } return true; } static bool any(const batch_type& rhs) { for (std::size_t i = 0; i < N; ++i) { if (rhs[i]) return true; } return false; } }; } /********************************** * batch implementation * **********************************/ template inline batch::batch() { } template inline batch::batch(T f) : base_type(detail::array_from_scalar(f)) { } template template inline batch::batch(Args... exactly_N_scalars) : base_type(storage_type{ static_cast(exactly_N_scalars)... }) { } namespace detail { template struct all_bits { template static T get(T) { return ~T(0); } }; template <> struct all_bits { template static T get(T) { T res(0); using int_type = as_unsigned_integer_t; int_type value(~int_type(0)); std::memcpy(&res, &value, sizeof(int_type)); return res; } }; } template inline batch::batch(const std::array& src) { using all_bits = detail::all_bits::value>; for(std::size_t i = 0; i < N; ++i) { this->m_value[i] = src[i] ? all_bits::get(T(0)) : T(0); } } template inline batch::batch(const T* src) : batch(src, unaligned_mode()) { } template inline batch::batch(const T* src, aligned_mode) : batch(src, unaligned_mode()) { } template inline batch::batch(const T* src, unaligned_mode) : base_type(detail::array_from_pointer(src)) { } template inline batch::batch(const std::array& rhs) : base_type(rhs) { } template inline batch::batch(const batch_bool_type& rhs) { std::transform(rhs.get_value().cbegin(), rhs.get_value().cend(), this->m_value.begin(), [](bool b) -> T { return b ? T(1) : T(0); }); } template inline batch& batch::operator=(const std::array& rhs) { this->m_value = rhs; return *this; } template inline batch& batch::operator=(const std::array& rhs) { using all_bits = detail::all_bits::value>; std::transform(rhs.cbegin(), rhs.cend(), this->m_value.begin(), [](bool b) -> T { return b ? all_bits::get(T(0)) : T(0); }); return *this; } template inline batch& batch::operator=(const batch_bool_type& rhs) { std::transform(rhs.get_value().cbegin(), rhs.get_value().cend(), this->m_value.begin(), [](bool b) -> T { return b ? T(1) : T(0); }); return *this; } template inline batch::operator std::array() const { return this->m_value; } #define FALLBACK_DEFINE_LOAD_STORE(TYPE) \ template \ inline batch& batch::load_aligned(const TYPE* src) \ { \ return this->load_unaligned_impl(src); \ } \ template \ inline batch& batch::load_unaligned(const TYPE* src) \ { \ return this->load_unaligned_impl(src); \ } \ template \ inline void batch::store_aligned(TYPE* dst) const \ { \ this->store_unaligned_impl(dst); \ } \ template \ inline void batch::store_unaligned(TYPE* dst) const \ { \ this->store_unaligned_impl(dst); \ } FALLBACK_DEFINE_LOAD_STORE(bool) FALLBACK_DEFINE_LOAD_STORE(int8_t) FALLBACK_DEFINE_LOAD_STORE(uint8_t) FALLBACK_DEFINE_LOAD_STORE(int16_t) FALLBACK_DEFINE_LOAD_STORE(uint16_t) FALLBACK_DEFINE_LOAD_STORE(int32_t) FALLBACK_DEFINE_LOAD_STORE(uint32_t) FALLBACK_DEFINE_LOAD_STORE(int64_t) FALLBACK_DEFINE_LOAD_STORE(uint64_t) FALLBACK_DEFINE_LOAD_STORE(float) FALLBACK_DEFINE_LOAD_STORE(double) #undef FALLBACK_DEFINE_LOAD_STORE template template inline batch& batch::load_unaligned_impl(const U* src) { for(std::size_t i = 0; i < N; ++i) { this->m_value[i] = static_cast(src[i]); } return *this; } template template inline void batch::store_unaligned_impl(U* dst) const { for(std::size_t i = 0; i < N; ++i) { dst[i] = static_cast(this->m_value[i]); } } template inline T& batch::operator[](std::size_t index) { return this->m_value[index % base_type::size]; } template inline const T& batch::operator[](std::size_t index) const { return this->m_value[index % base_type::size]; } namespace detail { template struct batch_kernel { using batch_type = batch; using value_type = T; using batch_bool_type = batch_bool; static batch_type neg(const batch_type& rhs) { XSIMD_FALLBACK_UNARY_OP(batch, -, rhs) } static batch_type add(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch, +, lhs, rhs) } static batch_type sub(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch, -, lhs, rhs) } static batch_type sadd(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BATCH_BINARY_FUNC(xsimd::sadd, lhs, rhs) } static batch_type ssub(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BATCH_BINARY_FUNC(xsimd::ssub, lhs, rhs) } static batch_type mul(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch, *, lhs, rhs) } static batch_type div(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch, /, lhs, rhs) } static batch_type mod(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch, % , lhs, rhs) } static batch_bool_type eq(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch_bool, == , lhs, rhs) } static batch_bool_type neq(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch_bool, != , lhs, rhs) } static batch_bool_type lt(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch_bool, < , lhs, rhs) } static batch_bool_type lte(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BINARY_OP(batch_bool, <=, lhs, rhs) } static batch_type bitwise_and(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BATCH_BITWISE_BINARY_OP(&, lhs, rhs) } static batch_type bitwise_or(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BATCH_BITWISE_BINARY_OP(|, lhs, rhs) } static batch_type bitwise_xor(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BATCH_BITWISE_BINARY_OP(^ , lhs, rhs) } static batch_type bitwise_not(const batch_type& rhs) { XSIMD_FALLBACK_BATCH_BITWISE_UNARY_OP(~, rhs) } static batch_type bitwise_andnot(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_MAPPING_LOOP( batch, detail::from_unsigned_integer( ~( detail::to_unsigned_integer(lhs[i]) & detail::to_unsigned_integer(rhs[i]) ) ) ) } static batch_type min(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BATCH_BINARY_FUNC(std::min, lhs, rhs) } static batch_type max(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BATCH_BINARY_FUNC(std::max, lhs, rhs) } static batch_type fmin(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BATCH_BINARY_FUNC(std::fmin, lhs, rhs) } static batch_type fmax(const batch_type& lhs, const batch_type& rhs) { XSIMD_FALLBACK_BATCH_BINARY_FUNC(std::fmax, lhs, rhs) } static batch_type abs(const batch_type& rhs) { XSIMD_FALLBACK_BATCH_UNARY_FUNC(std::abs, rhs) } static batch_type fabs(const batch_type& rhs) { XSIMD_FALLBACK_BATCH_UNARY_FUNC(std::fabs, rhs) } static batch_type sqrt(const batch_type& rhs) { XSIMD_FALLBACK_BATCH_UNARY_FUNC(std::sqrt, rhs) } static batch_type fma(const batch_type& x, const batch_type& y, const batch_type& z) { XSIMD_FALLBACK_BATCH_TERNARY_FUNC(std::fma, x, y, z) } static batch_type fms(const batch_type& x, const batch_type& y, const batch_type& z) { return fma(x, y, -z); } static batch_type fnma(const batch_type& x, const batch_type& y, const batch_type& z) { return fma(-x, y, z); } static batch_type fnms(const batch_type& x, const batch_type& y, const batch_type& z) { return fma(-x, y, -z); } static value_type hadd(const batch_type& rhs) { value_type result = 0; for (std::size_t i = 0; i < N; ++i) { result += rhs[i]; } return result; } static batch_type haddp(const batch_type* row) { XSIMD_FALLBACK_MAPPING_LOOP(batch, hadd(row[i])) } static batch_type select(const batch_bool_type& cond, const batch_type& a, const batch_type& b) { XSIMD_FALLBACK_MAPPING_LOOP(batch, (cond[i] ? a[i] : b[i])) } template static batch_type select(const batch_bool_constant& cond, const batch_type& a, const batch_type& b) { XSIMD_FALLBACK_MAPPING_LOOP(batch, (cond[i] ? a[i] : b[i])) } static batch_bool_type isnan(const batch_type& x) { XSIMD_FALLBACK_MAPPING_LOOP(batch_bool, std::isnan(x[i])) } static batch_type zip_lo(const batch_type& lhs, const batch_type& rhs) { batch_type b_lo; for (std::size_t i = 0, j = 0; i < N/2; ++i, j = j + 2) { b_lo[j] = lhs[i]; b_lo[j + 1] = rhs[i]; } return b_lo; } static batch_type zip_hi(const batch_type& lhs, const batch_type& rhs) { batch_type b_hi; for (std::size_t i = 0, j = 0; i < N/2; ++i, j = j + 2) { b_hi[j] = lhs[i + N/2]; b_hi[j + 1] = rhs[i+ N/2]; } return b_hi; } /* 0 <= n <= N/2 */ static batch_type extract_pair(const batch_type& lhs, const batch_type& rhs, const int n) { batch_type b_concatenate; for (int i = 0 ; i < static_cast(N - n); ++i) { b_concatenate[i] = lhs[i + n]; if(i < n) { b_concatenate[N - 1 - i] = rhs[n - 1 - i]; } } return b_concatenate; } }; } template inline batch operator<<(const batch& lhs, int32_t rhs) { XSIMD_FALLBACK_MAPPING_LOOP(batch, (lhs[i] << rhs)) } template inline batch operator>>(const batch& lhs, int32_t rhs) { XSIMD_FALLBACK_MAPPING_LOOP(batch, (lhs[i] >> rhs)) } /*********************************************** * utility functions to avoid code duplication * ***********************************************/ namespace detail { template inline std::pair load_complex_impl(const U* src) { using value_type = typename U::value_type; using dst_value_type = typename B::value_type; const value_type* buf = reinterpret_cast(src); B real, imag; for (std::size_t i = 0; i < N; ++i) { real[i] = static_cast(buf[2 * i]); imag[i] = static_cast(buf[2 * i + 1]); } return std::make_pair(real, imag); } template inline void store_complex_impl(const B& real, const B& imag, U* dst) { using value_type = typename U::value_type; value_type* buf = reinterpret_cast(dst); for (std::size_t i = 0; i < N; ++i) { buf[2 * i] = static_cast(real[i]); buf[2 * i + 1] = static_cast(imag[i]); } } } /******************************************** * batch> implementation * ********************************************/ template template inline auto batch, N>::load_aligned(const U* src) -> typename std::enable_if::value, self_type&>::type { std::tie(this->m_real, this->m_imag) = detail::load_complex_impl(src); return *this; } template template inline auto batch, N>::load_unaligned(const U* src) -> typename std::enable_if::value, self_type&>::type { return load_aligned(src); } template template inline auto batch, N>::load_aligned(const U* src) -> typename std::enable_if::value, self_type&>::type { for (std::size_t i = 0; i < N; ++i) { this->m_real[i] = static_cast(src[i]); this->m_imag[i] = real_value_type(0); } return *this; } template template inline auto batch, N>::load_unaligned(const U* src) -> typename std::enable_if::value, self_type&>::type { return load_aligned(src); } template template inline void batch, N>::store_aligned(U* dst) const { detail::store_complex_impl(this->m_real, this->m_imag, dst); } template template inline void batch, N>::store_unaligned(U* dst) const { store_aligned(dst); } /****************************************************** * batch, N> implementation * ******************************************************/ #ifdef XSIMD_ENABLE_XTL_COMPLEX template template inline auto batch, N>::load_aligned(const U* src) -> typename std::enable_if::value, self_type&>::type { std::tie(this->m_real, this->m_imag) = detail::load_complex_impl(src); return *this; } template template inline auto batch, N>::load_unaligned(const U* src) -> typename std::enable_if::value, self_type&>::type { return load_aligned(src); } template template inline auto batch, N>::load_aligned(const U* src) -> typename std::enable_if::value, self_type&>::type { for (std::size_t i = 0; i < N; ++i) { this->m_real[i] = static_cast(src[i]); this->m_imag[i] = real_value_type(0); } return *this; } template template inline auto batch, N>::load_unaligned(const U* src) -> typename std::enable_if::value, self_type&>::type { return load_aligned(src); } template template inline void batch, N>::store_aligned(U* dst) const { detail::store_complex_impl(this->m_real, this->m_imag, dst); } template template inline void batch, N>::store_unaligned(U* dst) const { store_unaligned(dst); } #endif /*************************************** * conversion functions implementation * ***************************************/ template inline batch to_int(const batch& x) { XSIMD_FALLBACK_BATCH_STATIC_CAST(int32_t, x) } template inline batch to_int(const batch& x) { XSIMD_FALLBACK_BATCH_STATIC_CAST(int64_t, x) } template inline batch to_float(const batch& x) { XSIMD_FALLBACK_BATCH_STATIC_CAST(float, x) } template inline batch to_float(const batch& x) { XSIMD_FALLBACK_BATCH_STATIC_CAST(double, x) } /************************** * boolean cast functions * **************************/ template inline batch_bool bool_cast(const batch_bool& x) { XSIMD_FALLBACK_BOOL_CAST(int32_t, x) } template inline batch_bool bool_cast(const batch_bool& x) { XSIMD_FALLBACK_BOOL_CAST(int64_t, x) } template inline batch_bool bool_cast(const batch_bool& x) { XSIMD_FALLBACK_BOOL_CAST(float, x) } template inline batch_bool bool_cast(const batch_bool& x) { XSIMD_FALLBACK_BOOL_CAST(double, x) } /***************************************** * bitwise cast functions implementation * *****************************************/ template batch u8_to_u16(const batch& x); template batch u16_to_u8(const batch& x); template batch u8_to_u32(const batch& x); template batch u32_to_u8(const batch& x); template batch u8_to_u64(const batch& x); template batch u64_to_u8(const batch& x); template struct bitwise_cast_impl, batch> { private: static_assert(sizeof(T_in)*N_in % sizeof(T_out) == 0, "The input and output batches must have the same size"); static constexpr size_t N_out = sizeof(T_in)*N_in/sizeof(T_out); union Converter { std::array in; std::array out; }; public: static batch run(const batch& x) { Converter caster; caster.in = static_cast>(x); return batch(caster.out); } }; /*********************************************** * static_cast conversion by bitwise_cast_impl * ***********************************************/ template inline batch u8_to_u16(const batch& x) { return bitwise_cast_impl, batch>::run(x); } template inline batch u16_to_u8(const batch& x) { return bitwise_cast_impl, batch>::run(x); } template inline batch u8_to_u32(const batch& x) { return bitwise_cast_impl, batch>::run(x); } template inline batch u32_to_u8(const batch& x) { return bitwise_cast_impl, batch>::run(x); } template inline batch u8_to_u64(const batch& x) { return bitwise_cast_impl, batch>::run(x); } template inline batch u64_to_u8(const batch& x) { return bitwise_cast_impl, batch>::run(x); } } #endif