Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-07 10:37:13

0001 /***************************************************************************
0002  * Copyright (c) Johan Mabille, Sylvain Corlay, Wolf Vollprecht and         *
0003  * Martin Renou                                                             *
0004  * Copyright (c) QuantStack                                                 *
0005  * Copyright (c) Serge Guelton                                              *
0006  *                                                                          *
0007  * Distributed under the terms of the BSD 3-Clause License.                 *
0008  *                                                                          *
0009  * The full license is in the file LICENSE, distributed with this software. *
0010  ****************************************************************************/
0011 
0012 #ifndef XSIMD_VSX_HPP
0013 #define XSIMD_VSX_HPP
0014 
0015 #include "../types/xsimd_batch_fwd.hpp"
0016 #include "../types/xsimd_vsx_register.hpp"
0017 #include "./common/xsimd_common_cast.hpp"
0018 
0019 #include <endian.h>
0020 
0021 #include <complex>
0022 #include <type_traits>
0023 
0024 namespace xsimd
0025 {
0026     template <typename T, class A, bool... Values>
0027     struct batch_bool_constant;
0028 
0029     template <class T_out, class T_in, class A>
0030     XSIMD_INLINE batch<T_out, A> bitwise_cast(batch<T_in, A> const& x) noexcept;
0031 
0032     template <typename T, class A, T... Values>
0033     struct batch_constant;
0034 
0035     namespace kernel
0036     {
0037         // builtin_t<T> - the scalar type as it would be used for a vector intrinsic
0038         // VSX vector intrinsics do not support long, unsigned long, and char
0039         // The builtin<T> definition can be used to map the incoming
0040         // type to the right one to be used with the intrinsics.
0041         template <typename T>
0042         struct builtin_scalar
0043         {
0044             using type = T;
0045         };
0046 
0047         template <>
0048         struct builtin_scalar<unsigned long>
0049         {
0050             using type = unsigned long long;
0051         };
0052 
0053         template <>
0054         struct builtin_scalar<long>
0055         {
0056             using type = long long;
0057         };
0058 
0059         template <>
0060         struct builtin_scalar<char>
0061         {
0062             using type = typename std::conditional<std::is_signed<char>::value, signed char, unsigned char>::type;
0063         };
0064 
0065         template <typename T>
0066         using builtin_t = typename builtin_scalar<T>::type;
0067 
0068         template <class A, class T>
0069         XSIMD_INLINE batch<T, A> avg(batch<T, A> const&, batch<T, A> const&, requires_arch<common>) noexcept;
0070         template <class A, class T>
0071         XSIMD_INLINE batch<T, A> avgr(batch<T, A> const&, batch<T, A> const&, requires_arch<common>) noexcept;
0072 
0073         // abs
0074         template <class A>
0075         XSIMD_INLINE batch<float, A> abs(batch<float, A> const& self, requires_arch<vsx>) noexcept
0076         {
0077             return vec_abs(self.data);
0078         }
0079 
0080         template <class A>
0081         XSIMD_INLINE batch<double, A> abs(batch<double, A> const& self, requires_arch<vsx>) noexcept
0082         {
0083             return vec_abs(self.data);
0084         }
0085 
0086         // add
0087         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0088         XSIMD_INLINE batch<T, A> add(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0089         {
0090             return vec_add(self.data, other.data);
0091         }
0092 
0093         // all
0094         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0095         XSIMD_INLINE bool all(batch_bool<T, A> const& self, requires_arch<vsx>) noexcept
0096         {
0097             return vec_all_ne(self.data, vec_xor(self.data, self.data));
0098         }
0099 
0100         // any
0101         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0102         XSIMD_INLINE bool any(batch_bool<T, A> const& self, requires_arch<vsx>) noexcept
0103         {
0104             return vec_any_ne(self.data, vec_xor(self.data, self.data));
0105         }
0106 
0107         // avgr
0108         template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value && sizeof(T) < 8>>
0109         XSIMD_INLINE batch<T, A> avgr(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0110         {
0111             return vec_avg(self.data, other.data);
0112         }
0113         template <class A>
0114         XSIMD_INLINE batch<float, A> avgr(batch<float, A> const& self, batch<float, A> const& other, requires_arch<vsx>) noexcept
0115         {
0116             return avgr(self, other, common {});
0117         }
0118         template <class A>
0119         XSIMD_INLINE batch<double, A> avgr(batch<double, A> const& self, batch<double, A> const& other, requires_arch<vsx>) noexcept
0120         {
0121             return avgr(self, other, common {});
0122         }
0123 
0124         // avg
0125         template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0126         XSIMD_INLINE batch<T, A> avg(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0127         {
0128             XSIMD_IF_CONSTEXPR(sizeof(T) < 8)
0129             {
0130                 constexpr auto nbit = 8 * sizeof(T) - 1;
0131                 auto adj = bitwise_cast<T>(bitwise_cast<as_unsigned_integer_t<T>>((self ^ other) << nbit) >> nbit);
0132                 return avgr(self, other, A {}) - adj;
0133             }
0134             else
0135             {
0136                 return avg(self, other, common {});
0137             }
0138         }
0139         template <class A>
0140         XSIMD_INLINE batch<float, A> avg(batch<float, A> const& self, batch<float, A> const& other, requires_arch<vsx>) noexcept
0141         {
0142             return avg(self, other, common {});
0143         }
0144         template <class A>
0145         XSIMD_INLINE batch<double, A> avg(batch<double, A> const& self, batch<double, A> const& other, requires_arch<vsx>) noexcept
0146         {
0147             return avg(self, other, common {});
0148         }
0149 
0150         // batch_bool_cast
0151         template <class A, class T_out, class T_in>
0152         XSIMD_INLINE batch_bool<T_out, A> batch_bool_cast(batch_bool<T_in, A> const& self, batch_bool<T_out, A> const&, requires_arch<vsx>) noexcept
0153         {
0154             return (typename batch_bool<T_out, A>::register_type)self.data;
0155         }
0156 
0157         // bitwise_and
0158         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0159         XSIMD_INLINE batch<T, A> bitwise_and(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0160         {
0161             return vec_and(self.data, other.data);
0162         }
0163         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0164         XSIMD_INLINE batch_bool<T, A> bitwise_and(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<vsx>) noexcept
0165         {
0166             return vec_and(self.data, other.data);
0167         }
0168 
0169         // bitwise_andnot
0170         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0171         XSIMD_INLINE batch<T, A> bitwise_andnot(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0172         {
0173             return vec_and(self.data, vec_nor(other.data, other.data));
0174         }
0175         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0176         XSIMD_INLINE batch_bool<T, A> bitwise_andnot(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<vsx>) noexcept
0177         {
0178             return self.data & ~other.data;
0179         }
0180 
0181         // bitwise_lshift
0182         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0183         XSIMD_INLINE batch<T, A> bitwise_lshift(batch<T, A> const& self, int32_t other, requires_arch<vsx>) noexcept
0184         {
0185             using shift_type = as_unsigned_integer_t<T>;
0186             batch<shift_type, A> shift(static_cast<shift_type>(other));
0187             return vec_sl(self.data, shift.data);
0188         }
0189 
0190         // bitwise_not
0191         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0192         XSIMD_INLINE batch<T, A> bitwise_not(batch<T, A> const& self, requires_arch<vsx>) noexcept
0193         {
0194             return vec_nor(self.data, self.data);
0195         }
0196         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0197         XSIMD_INLINE batch_bool<T, A> bitwise_not(batch_bool<T, A> const& self, requires_arch<vsx>) noexcept
0198         {
0199             return vec_nor(self.data, self.data);
0200         }
0201 
0202         // bitwise_or
0203         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0204         XSIMD_INLINE batch<T, A> bitwise_or(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0205         {
0206             return vec_or(self.data, other.data);
0207         }
0208         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0209         XSIMD_INLINE batch_bool<T, A> bitwise_or(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<vsx>) noexcept
0210         {
0211             return vec_or(self.data, other.data);
0212         }
0213 
0214         // bitwise_rshift
0215         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0216         XSIMD_INLINE batch<T, A> bitwise_rshift(batch<T, A> const& self, int32_t other, requires_arch<vsx>) noexcept
0217         {
0218             using shift_type = as_unsigned_integer_t<T>;
0219             batch<shift_type, A> shift(static_cast<shift_type>(other));
0220             XSIMD_IF_CONSTEXPR(std::is_signed<T>::value)
0221             {
0222                 return vec_sra(self.data, shift.data);
0223             }
0224             else
0225             {
0226                 return vec_sr(self.data, shift.data);
0227             }
0228         }
0229 
0230         // bitwise_xor
0231         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0232         XSIMD_INLINE batch<T, A> bitwise_xor(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0233         {
0234             return vec_xor(self.data, other.data);
0235         }
0236         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0237         XSIMD_INLINE batch_bool<T, A> bitwise_xor(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<vsx>) noexcept
0238         {
0239             return vec_xor(self.data, other.data);
0240         }
0241 
0242         // bitwise_cast
0243         template <class A, class T_in, class T_out>
0244         XSIMD_INLINE batch<T_out, A> bitwise_cast(batch<T_in, A> const& self, batch<T_out, A> const&, requires_arch<vsx>) noexcept
0245         {
0246             return (typename batch<T_out, A>::register_type)(self.data);
0247         }
0248 
0249         // broadcast
0250         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0251         XSIMD_INLINE batch<T, A> broadcast(T val, requires_arch<vsx>) noexcept
0252         {
0253             return vec_splats(static_cast<builtin_t<T>>(val));
0254         }
0255 
0256         // ceil
0257         template <class A, class T, class = std::enable_if_t<std::is_floating_point<T>::value>>
0258         XSIMD_INLINE batch<T, A> ceil(batch<T, A> const& self, requires_arch<vsx>) noexcept
0259         {
0260             return vec_ceil(self.data);
0261         }
0262 
0263         // store_complex
0264         namespace detail
0265         {
0266             // complex_low
0267             template <class A>
0268             XSIMD_INLINE batch<float, A> complex_low(batch<std::complex<float>, A> const& self, requires_arch<vsx>) noexcept
0269             {
0270                 return vec_mergeh(self.real().data, self.imag().data);
0271             }
0272             template <class A>
0273             XSIMD_INLINE batch<double, A> complex_low(batch<std::complex<double>, A> const& self, requires_arch<vsx>) noexcept
0274             {
0275                 return vec_mergeh(self.real().data, self.imag().data);
0276             }
0277             // complex_high
0278             template <class A>
0279             XSIMD_INLINE batch<float, A> complex_high(batch<std::complex<float>, A> const& self, requires_arch<vsx>) noexcept
0280             {
0281                 return vec_mergel(self.real().data, self.imag().data);
0282             }
0283             template <class A>
0284             XSIMD_INLINE batch<double, A> complex_high(batch<std::complex<double>, A> const& self, requires_arch<vsx>) noexcept
0285             {
0286                 return vec_mergel(self.real().data, self.imag().data);
0287             }
0288         }
0289 
0290         // decr_if
0291         template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0292         XSIMD_INLINE batch<T, A> decr_if(batch<T, A> const& self, batch_bool<T, A> const& mask, requires_arch<vsx>) noexcept
0293         {
0294             return self + batch<T, A>((typename batch<T, A>::register_type)mask.data);
0295         }
0296 
0297         // div
0298         template <class A>
0299         XSIMD_INLINE batch<float, A> div(batch<float, A> const& self, batch<float, A> const& other, requires_arch<vsx>) noexcept
0300         {
0301             return vec_div(self.data, other.data);
0302         }
0303         template <class A>
0304         XSIMD_INLINE batch<double, A> div(batch<double, A> const& self, batch<double, A> const& other, requires_arch<vsx>) noexcept
0305         {
0306             return vec_div(self.data, other.data);
0307         }
0308 
0309         // fast_cast
0310         namespace detail
0311         {
0312             template <class A>
0313             XSIMD_INLINE batch<float, A> fast_cast(batch<int32_t, A> const& self, batch<float, A> const&, requires_arch<vsx>) noexcept
0314             {
0315                 return vec_ctf(self.data, 0);
0316             }
0317             template <class A>
0318             XSIMD_INLINE batch<float, A> fast_cast(batch<uint32_t, A> const& self, batch<float, A> const&, requires_arch<vsx>) noexcept
0319             {
0320                 return vec_ctf(self.data, 0);
0321             }
0322 
0323             template <class A>
0324             XSIMD_INLINE batch<int32_t, A> fast_cast(batch<float, A> const& self, batch<int32_t, A> const&, requires_arch<vsx>) noexcept
0325             {
0326                 return vec_cts(self.data, 0);
0327             }
0328 
0329             template <class A>
0330             XSIMD_INLINE batch<uint32_t, A> fast_cast(batch<float, A> const& self, batch<uint32_t, A> const&, requires_arch<vsx>) noexcept
0331             {
0332                 return vec_ctu(self.data, 0);
0333             }
0334         }
0335 
0336         // fma
0337         template <class A>
0338         XSIMD_INLINE batch<float, A> fma(batch<float, A> const& x, batch<float, A> const& y, batch<float, A> const& z, requires_arch<vsx>) noexcept
0339         {
0340             return vec_madd(x.data, y.data, z.data);
0341         }
0342 
0343         template <class A>
0344         XSIMD_INLINE batch<double, A> fma(batch<double, A> const& x, batch<double, A> const& y, batch<double, A> const& z, requires_arch<vsx>) noexcept
0345         {
0346             return vec_madd(x.data, y.data, z.data);
0347         }
0348 
0349         // fms
0350         template <class A>
0351         XSIMD_INLINE batch<float, A> fms(batch<float, A> const& x, batch<float, A> const& y, batch<float, A> const& z, requires_arch<vsx>) noexcept
0352         {
0353             return vec_msub(x.data, y.data, z.data);
0354         }
0355 
0356         template <class A>
0357         XSIMD_INLINE batch<double, A> fms(batch<double, A> const& x, batch<double, A> const& y, batch<double, A> const& z, requires_arch<vsx>) noexcept
0358         {
0359             return vec_msub(x.data, y.data, z.data);
0360         }
0361 
0362         // eq
0363         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0364         XSIMD_INLINE batch_bool<T, A> eq(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0365         {
0366             auto res = vec_cmpeq(self.data, other.data);
0367             return *reinterpret_cast<typename batch_bool<T, A>::register_type*>(&res);
0368         }
0369         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0370         XSIMD_INLINE batch_bool<T, A> eq(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<vsx>) noexcept
0371         {
0372             auto res = vec_cmpeq(self.data, other.data);
0373             return *reinterpret_cast<typename batch_bool<T, A>::register_type*>(&res);
0374         }
0375 
0376         // first
0377         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0378         XSIMD_INLINE T first(batch<T, A> const& self, requires_arch<vsx>) noexcept
0379         {
0380             return vec_extract(self.data, 0);
0381         }
0382 
0383         // floor
0384         template <class A, class T, class = std::enable_if_t<std::is_floating_point<T>::value>>
0385         XSIMD_INLINE batch<T, A> floor(batch<T, A> const& self, requires_arch<vsx>) noexcept
0386         {
0387             return vec_floor(self.data);
0388         }
0389 
0390         // ge
0391         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0392         XSIMD_INLINE batch_bool<T, A> ge(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0393         {
0394             return vec_cmpge(self.data, other.data);
0395         }
0396 
0397         // gt
0398         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0399         XSIMD_INLINE batch_bool<T, A> gt(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0400         {
0401             return vec_cmpgt(self.data, other.data);
0402         }
0403 
0404         // haddp
0405         template <class A>
0406         XSIMD_INLINE batch<float, A> haddp(batch<float, A> const* row, requires_arch<vsx>) noexcept
0407         {
0408             auto tmp0 = vec_mergee(row[0].data, row[1].data); // v00 v10 v02 v12
0409             auto tmp1 = vec_mergeo(row[0].data, row[1].data); // v01 v11 v03 v13
0410             auto tmp4 = vec_add(tmp0, tmp1); // (v00 + v01, v10 + v11, v02 + v03, v12 + v13)
0411 
0412             auto tmp2 = vec_mergee(row[2].data, row[3].data); // v20 v30 v22 v32
0413             auto tmp3 = vec_mergeo(row[2].data, row[3].data); // v21 v31 v23 v33
0414             auto tmp5 = vec_add(tmp0, tmp1); // (v20 + v21, v30 + v31, v22 + v23, v32 + v33)
0415 
0416             auto tmp6 = vec_perm(tmp4, tmp5, (__vector unsigned char) { 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23 }); // (v00 + v01, v10 + v11, v20 + v21, v30 + v31
0417             auto tmp7 = vec_perm(tmp4, tmp5, (__vector unsigned char) { 8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 26, 27, 28, 29, 30, 31 }); // (v02 + v03, v12 + v13, v12 + v13, v32 + v33)
0418 
0419             return vec_add(tmp6, tmp7);
0420         }
0421 
0422         template <class A>
0423         XSIMD_INLINE batch<double, A> haddp(batch<double, A> const* row, requires_arch<vsx>) noexcept
0424         {
0425             auto tmp0 = vec_mergee(row[0].data, row[1].data); // v00 v10 v02 v12
0426             auto tmp1 = vec_mergeo(row[0].data, row[1].data); // v01 v11 v03 v13
0427             return vec_add(tmp0, tmp1);
0428         }
0429 
0430         // incr_if
0431         template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value>>
0432         XSIMD_INLINE batch<T, A> incr_if(batch<T, A> const& self, batch_bool<T, A> const& mask, requires_arch<vsx>) noexcept
0433         {
0434             return self - batch<T, A>((typename batch<T, A>::register_type)mask.data);
0435         }
0436 
0437         // insert
0438         template <class A, class T, size_t I, class = std::enable_if_t<std::is_scalar<T>::value>>
0439         XSIMD_INLINE batch<T, A> insert(batch<T, A> const& self, T val, index<I>, requires_arch<vsx>) noexcept
0440         {
0441             return vec_insert(val, self.data, I);
0442         }
0443 
0444         // isnan
0445         template <class A>
0446         XSIMD_INLINE batch_bool<float, A> isnan(batch<float, A> const& self, requires_arch<vsx>) noexcept
0447         {
0448             return ~vec_cmpeq(self.data, self.data);
0449         }
0450         template <class A>
0451         XSIMD_INLINE batch_bool<double, A> isnan(batch<double, A> const& self, requires_arch<vsx>) noexcept
0452         {
0453             return ~vec_cmpeq(self.data, self.data);
0454         }
0455 
0456         // load_unaligned
0457         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0458         XSIMD_INLINE batch<T, A> load_unaligned(T const* mem, convert<T>, requires_arch<vsx>) noexcept
0459         {
0460             return (typename batch<T, A>::register_type)vec_xl(0, (builtin_t<T>*)mem);
0461         }
0462 
0463         // load_aligned
0464         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0465         XSIMD_INLINE batch<T, A> load_aligned(T const* mem, convert<T>, requires_arch<vsx>) noexcept
0466         {
0467             return load_unaligned<A>(mem, kernel::convert<T> {}, vsx {});
0468         }
0469 
0470         // load_complex
0471         namespace detail
0472         {
0473             template <class A>
0474             XSIMD_INLINE batch<std::complex<float>, A> load_complex(batch<float, A> const& hi, batch<float, A> const& lo, requires_arch<vsx>) noexcept
0475             {
0476                 __vector unsigned char perme = { 0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27 };
0477                 __vector unsigned char permo = { 4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31 };
0478                 return { vec_perm(hi.data, lo.data, perme), vec_perm(hi.data, lo.data, permo) };
0479             }
0480             template <class A>
0481             XSIMD_INLINE batch<std::complex<double>, A> load_complex(batch<double, A> const& hi, batch<double, A> const& lo, requires_arch<vsx>) noexcept
0482             {
0483                 return { vec_mergee(hi.data, lo.data), vec_mergeo(hi.data, lo.data) };
0484             }
0485         }
0486 
0487         // le
0488         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0489         XSIMD_INLINE batch_bool<T, A> le(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0490         {
0491             return vec_cmple(self.data, other.data);
0492         }
0493 
0494         // lt
0495         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0496         XSIMD_INLINE batch_bool<T, A> lt(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0497         {
0498             return vec_cmplt(self.data, other.data);
0499         }
0500 
0501         // max
0502         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0503         XSIMD_INLINE batch<T, A> max(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0504         {
0505             return vec_max(self.data, other.data);
0506         }
0507 
0508         // min
0509         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0510         XSIMD_INLINE batch<T, A> min(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0511         {
0512             return vec_min(self.data, other.data);
0513         }
0514 
0515         // mul
0516         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0517         XSIMD_INLINE batch<T, A> mul(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0518         {
0519             return self.data * other.data;
0520         }
0521 
0522         // neg
0523         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0524         XSIMD_INLINE batch<T, A> neg(batch<T, A> const& self, requires_arch<vsx>) noexcept
0525         {
0526             return -(self.data);
0527         }
0528 
0529         // neq
0530         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0531         XSIMD_INLINE batch_bool<T, A> neq(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0532         {
0533             return ~vec_cmpeq(self.data, other.data);
0534         }
0535         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0536         XSIMD_INLINE batch_bool<T, A> neq(batch_bool<T, A> const& self, batch_bool<T, A> const& other, requires_arch<vsx>) noexcept
0537         {
0538             return ~vec_cmpeq(self.data, other.data);
0539         }
0540 
0541         // reciprocal
0542         template <class A>
0543         XSIMD_INLINE batch<float, A> reciprocal(batch<float, A> const& self,
0544                                                 kernel::requires_arch<vsx>)
0545         {
0546             return vec_re(self.data);
0547         }
0548         template <class A>
0549         XSIMD_INLINE batch<double, A> reciprocal(batch<double, A> const& self,
0550                                                  kernel::requires_arch<vsx>)
0551         {
0552             return vec_re(self.data);
0553         }
0554 
0555         // reduce_add
0556         template <class A>
0557         XSIMD_INLINE signed reduce_add(batch<signed, A> const& self, requires_arch<vsx>) noexcept
0558         {
0559             auto tmp0 = vec_reve(self.data); // v3, v2, v1, v0
0560             auto tmp1 = vec_add(self.data, tmp0); // v0 + v3, v1 + v2, v2 + v1, v3 + v0
0561             auto tmp2 = vec_mergel(tmp1, tmp1); // v2 + v1, v2 + v1, v3 + v0, v3 + v0
0562             auto tmp3 = vec_add(tmp1, tmp2);
0563             return vec_extract(tmp3, 0);
0564         }
0565         template <class A>
0566         XSIMD_INLINE unsigned reduce_add(batch<unsigned, A> const& self, requires_arch<vsx>) noexcept
0567         {
0568             auto tmp0 = vec_reve(self.data); // v3, v2, v1, v0
0569             auto tmp1 = vec_add(self.data, tmp0); // v0 + v3, v1 + v2, v2 + v1, v3 + v0
0570             auto tmp2 = vec_mergel(tmp1, tmp1); // v2 + v1, v2 + v1, v3 + v0, v3 + v0
0571             auto tmp3 = vec_add(tmp1, tmp2);
0572             return vec_extract(tmp3, 0);
0573         }
0574         template <class A>
0575         XSIMD_INLINE float reduce_add(batch<float, A> const& self, requires_arch<vsx>) noexcept
0576         {
0577             // FIXME: find an in-order approach
0578             auto tmp0 = vec_reve(self.data); // v3, v2, v1, v0
0579             auto tmp1 = vec_add(self.data, tmp0); // v0 + v3, v1 + v2, v2 + v1, v3 + v0
0580             auto tmp2 = vec_mergel(tmp1, tmp1); // v2 + v1, v2 + v1, v3 + v0, v3 + v0
0581             auto tmp3 = vec_add(tmp1, tmp2);
0582             return vec_extract(tmp3, 0);
0583         }
0584         template <class A>
0585         XSIMD_INLINE double reduce_add(batch<double, A> const& self, requires_arch<vsx>) noexcept
0586         {
0587             auto tmp0 = vec_reve(self.data); // v1, v0
0588             auto tmp1 = vec_add(self.data, tmp0); // v0 + v1, v1 + v0
0589             return vec_extract(tmp1, 0);
0590         }
0591         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0592         XSIMD_INLINE T reduce_add(batch<T, A> const& self, requires_arch<vsx>) noexcept
0593         {
0594             return reduce_add(self, common {});
0595         }
0596 
0597         // reduce_mul
0598         template <class A>
0599         XSIMD_INLINE signed reduce_mul(batch<signed, A> const& self, requires_arch<vsx>) noexcept
0600         {
0601             auto tmp0 = vec_reve(self.data); // v3, v2, v1, v0
0602             auto tmp1 = vec_mul(self.data, tmp0); // v0 * v3, v1 * v2, v2 * v1, v3 * v0
0603             auto tmp2 = vec_mergel(tmp1, tmp1); // v2 * v1, v2 * v1, v3 * v0, v3 * v0
0604             auto tmp3 = vec_mul(tmp1, tmp2);
0605             return vec_extract(tmp3, 0);
0606         }
0607         template <class A>
0608         XSIMD_INLINE unsigned reduce_mul(batch<unsigned, A> const& self, requires_arch<vsx>) noexcept
0609         {
0610             auto tmp0 = vec_reve(self.data); // v3, v2, v1, v0
0611             auto tmp1 = vec_mul(self.data, tmp0); // v0 * v3, v1 * v2, v2 * v1, v3 * v0
0612             auto tmp2 = vec_mergel(tmp1, tmp1); // v2 * v1, v2 * v1, v3 * v0, v3 * v0
0613             auto tmp3 = vec_mul(tmp1, tmp2);
0614             return vec_extract(tmp3, 0);
0615         }
0616         template <class A>
0617         XSIMD_INLINE float reduce_mul(batch<float, A> const& self, requires_arch<vsx>) noexcept
0618         {
0619             // FIXME: find an in-order approach
0620             auto tmp0 = vec_reve(self.data); // v3, v2, v1, v0
0621             auto tmp1 = vec_mul(self.data, tmp0); // v0 * v3, v1 * v2, v2 * v1, v3 * v0
0622             auto tmp2 = vec_mergel(tmp1, tmp1); // v2 * v1, v2 * v1, v3 * v0, v3 * v0
0623             auto tmp3 = vec_mul(tmp1, tmp2);
0624             return vec_extract(tmp3, 0);
0625         }
0626         template <class A>
0627         XSIMD_INLINE double reduce_mul(batch<double, A> const& self, requires_arch<vsx>) noexcept
0628         {
0629             auto tmp0 = vec_reve(self.data); // v1, v0
0630             auto tmp1 = vec_mul(self.data, tmp0); // v0 * v1, v1 * v0
0631             return vec_extract(tmp1, 0);
0632         }
0633         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0634         XSIMD_INLINE T reduce_mul(batch<T, A> const& self, requires_arch<vsx>) noexcept
0635         {
0636             return reduce_mul(self, common {});
0637         }
0638 
0639         // round
0640 
0641         // vec_round exists also for float vectors but is mapped to vrfin instruction which uses the wrong rounding mode
0642 #if defined __has_builtin && __has_builtin(__builtin_vsx_xvrspi)
0643         template <class A>
0644         XSIMD_INLINE batch<float, A> round(batch<float, A> const& self, requires_arch<vsx>) noexcept
0645         {
0646             return __builtin_vsx_xvrspi(self.data);
0647         }
0648 #endif
0649         // For double vectors vec_round uses xvrdpi which does the right thing
0650         template <class A>
0651         XSIMD_INLINE batch<double, A> round(batch<double, A> const& self, requires_arch<vsx>) noexcept
0652         {
0653             return vec_round(self.data);
0654         }
0655 
0656         // rsqrt
0657         template <class A>
0658         XSIMD_INLINE batch<float, A> rsqrt(batch<float, A> const& val, requires_arch<vsx>) noexcept
0659         {
0660             return vec_rsqrt(val.data);
0661         }
0662         template <class A>
0663         XSIMD_INLINE batch<double, A> rsqrt(batch<double, A> const& val, requires_arch<vsx>) noexcept
0664         {
0665             return vec_rsqrt(val.data);
0666         }
0667 
0668         // select
0669         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0670         XSIMD_INLINE batch<T, A> select(batch_bool<T, A> const& cond, batch<T, A> const& true_br, batch<T, A> const& false_br, requires_arch<vsx>) noexcept
0671         {
0672             return vec_sel(false_br.data, true_br.data, cond.data);
0673         }
0674         template <class A, class T, bool... Values, class = std::enable_if_t<std::is_scalar<T>::value>>
0675         XSIMD_INLINE batch<T, A> select(batch_bool_constant<T, A, Values...> const&, batch<T, A> const& true_br, batch<T, A> const& false_br, requires_arch<vsx>) noexcept
0676         {
0677             return select(batch_bool<T, A> { Values... }, true_br, false_br, vsx {});
0678         }
0679 
0680         // shuffle
0681         template <class A, class ITy, ITy I0, ITy I1, ITy I2, ITy I3>
0682         XSIMD_INLINE batch<float, A> shuffle(batch<float, A> const& x, batch<float, A> const& y, batch_constant<ITy, A, I0, I1, I2, I3>, requires_arch<vsx>) noexcept
0683         {
0684             return vec_perm(x.data, y.data,
0685                             (__vector unsigned char) {
0686                                 4 * I0 + 0, 4 * I0 + 1, 4 * I0 + 2, 4 * I0 + 3,
0687                                 4 * I1 + 0, 4 * I1 + 1, 4 * I1 + 2, 4 * I1 + 3,
0688                                 4 * I2 + 0, 4 * I2 + 1, 4 * I2 + 2, 4 * I2 + 3,
0689                                 4 * I3 + 0, 4 * I3 + 1, 4 * I3 + 2, 4 * I3 + 3 });
0690         }
0691 
0692         template <class A, class ITy, ITy I0, ITy I1>
0693         XSIMD_INLINE batch<double, A> shuffle(batch<double, A> const& x, batch<double, A> const& y, batch_constant<ITy, A, I0, I1>, requires_arch<vsx>) noexcept
0694         {
0695             return vec_perm(x.data, y.data,
0696                             (__vector unsigned char) {
0697                                 8 * I0 + 0,
0698                                 8 * I0 + 1,
0699                                 8 * I0 + 2,
0700                                 8 * I0 + 3,
0701                                 8 * I0 + 4,
0702                                 8 * I0 + 5,
0703                                 8 * I0 + 6,
0704                                 8 * I0 + 7,
0705                                 8 * I1 + 0,
0706                                 8 * I1 + 1,
0707                                 8 * I1 + 2,
0708                                 8 * I1 + 3,
0709                                 8 * I1 + 4,
0710                                 8 * I1 + 5,
0711                                 8 * I1 + 6,
0712                                 8 * I1 + 7,
0713                             });
0714         }
0715 
0716         // sqrt
0717         template <class A>
0718         XSIMD_INLINE batch<float, A> sqrt(batch<float, A> const& val, requires_arch<vsx>) noexcept
0719         {
0720             return vec_sqrt(val.data);
0721         }
0722 
0723         template <class A>
0724         XSIMD_INLINE batch<double, A> sqrt(batch<double, A> const& val, requires_arch<vsx>) noexcept
0725         {
0726             return vec_sqrt(val.data);
0727         }
0728 
0729         // slide_left
0730         template <size_t N, class A, class T>
0731         XSIMD_INLINE batch<T, A> slide_left(batch<T, A> const& x, requires_arch<vsx>) noexcept
0732         {
0733             XSIMD_IF_CONSTEXPR(N == batch<T, A>::size * sizeof(T))
0734             {
0735                 return batch<T, A>(0);
0736             }
0737             else
0738             {
0739                 auto slider = vec_splats((uint8_t)(8 * N));
0740                 return (typename batch<T, A>::register_type)vec_slo(x.data, slider);
0741             }
0742         }
0743 
0744         // slide_right
0745         template <size_t N, class A, class T>
0746         XSIMD_INLINE batch<T, A> slide_right(batch<T, A> const& x, requires_arch<vsx>) noexcept
0747         {
0748             XSIMD_IF_CONSTEXPR(N == batch<T, A>::size * sizeof(T))
0749             {
0750                 return batch<T, A>(0);
0751             }
0752             else
0753             {
0754                 auto slider = vec_splats((uint8_t)(8 * N));
0755                 return (typename batch<T, A>::register_type)vec_sro((__vector unsigned char)x.data, slider);
0756             }
0757         }
0758 
0759         // sadd
0760         template <class A, class T, class = std::enable_if_t<std::is_integral<T>::value && sizeof(T) != 8>>
0761         XSIMD_INLINE batch<T, A> sadd(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0762         {
0763             return vec_adds(self.data, other.data);
0764         }
0765 
0766         // set
0767         template <class A, class T, class... Values>
0768         XSIMD_INLINE batch<T, A> set(batch<T, A> const&, requires_arch<vsx>, Values... values) noexcept
0769         {
0770             static_assert(sizeof...(Values) == batch<T, A>::size, "consistent init");
0771             return typename batch<T, A>::register_type { values... };
0772         }
0773 
0774         template <class A, class T, class... Values, class = std::enable_if_t<std::is_scalar<T>::value>>
0775         XSIMD_INLINE batch_bool<T, A> set(batch_bool<T, A> const&, requires_arch<vsx>, Values... values) noexcept
0776         {
0777             static_assert(sizeof...(Values) == batch_bool<T, A>::size, "consistent init");
0778             return typename batch_bool<T, A>::register_type { static_cast<decltype(std::declval<typename batch_bool<T, A>::register_type>()[0])>(values ? -1LL : 0LL)... };
0779         }
0780 
0781         // ssub
0782 
0783         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value && sizeof(T) == 1>>
0784         XSIMD_INLINE batch<T, A> ssub(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0785         {
0786             return vec_subs(self.data, other.data);
0787         }
0788 
0789         // store_aligned
0790         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0791         XSIMD_INLINE void store_aligned(T* mem, batch<T, A> const& self, requires_arch<vsx>) noexcept
0792         {
0793             vec_xst((typename batch<T, A>::register_type)self.data, 0, (builtin_t<T>*)mem);
0794         }
0795 
0796         // store_unaligned
0797         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0798         XSIMD_INLINE void store_unaligned(T* mem, batch<T, A> const& self, requires_arch<vsx>) noexcept
0799         {
0800             store_aligned<A>(mem, self, vsx {});
0801         }
0802 
0803         // sub
0804         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0805         XSIMD_INLINE batch<T, A> sub(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0806         {
0807             return vec_sub(self.data, other.data);
0808         }
0809 
0810         // swizzle
0811 
0812         template <class A, uint32_t V0, uint32_t V1, uint32_t V2, uint32_t V3>
0813         XSIMD_INLINE batch<float, A> swizzle(batch<float, A> const& self, batch_constant<uint32_t, A, V0, V1, V2, V3>, requires_arch<vsx>) noexcept
0814         {
0815             return vec_perm(self.data, self.data,
0816                             (__vector unsigned char) {
0817                                 4 * V0 + 0, 4 * V0 + 1, 4 * V0 + 2, 4 * V0 + 3,
0818                                 4 * V1 + 0, 4 * V1 + 1, 4 * V1 + 2, 4 * V1 + 3,
0819                                 4 * V2 + 0, 4 * V2 + 1, 4 * V2 + 2, 4 * V2 + 3,
0820                                 4 * V3 + 0, 4 * V3 + 1, 4 * V3 + 2, 4 * V3 + 3 });
0821         }
0822 
0823         template <class A, uint64_t V0, uint64_t V1>
0824         XSIMD_INLINE batch<double, A> swizzle(batch<double, A> const& self, batch_constant<uint64_t, A, V0, V1>, requires_arch<vsx>) noexcept
0825         {
0826             return vec_perm(self.data, self.data,
0827                             (__vector unsigned char) {
0828                                 8 * V0 + 0,
0829                                 8 * V0 + 1,
0830                                 8 * V0 + 2,
0831                                 8 * V0 + 3,
0832                                 8 * V0 + 4,
0833                                 8 * V0 + 5,
0834                                 8 * V0 + 6,
0835                                 8 * V0 + 7,
0836                                 8 * V1 + 0,
0837                                 8 * V1 + 1,
0838                                 8 * V1 + 2,
0839                                 8 * V1 + 3,
0840                                 8 * V1 + 4,
0841                                 8 * V1 + 5,
0842                                 8 * V1 + 6,
0843                                 8 * V1 + 7,
0844                             });
0845         }
0846 
0847         template <class A, uint64_t V0, uint64_t V1>
0848         XSIMD_INLINE batch<uint64_t, A> swizzle(batch<uint64_t, A> const& self, batch_constant<uint64_t, A, V0, V1>, requires_arch<vsx>) noexcept
0849         {
0850             return vec_perm(self.data, self.data,
0851                             (__vector unsigned char) {
0852                                 8 * V0 + 0,
0853                                 8 * V0 + 1,
0854                                 8 * V0 + 2,
0855                                 8 * V0 + 3,
0856                                 8 * V0 + 4,
0857                                 8 * V0 + 5,
0858                                 8 * V0 + 6,
0859                                 8 * V0 + 7,
0860                                 8 * V1 + 0,
0861                                 8 * V1 + 1,
0862                                 8 * V1 + 2,
0863                                 8 * V1 + 3,
0864                                 8 * V1 + 4,
0865                                 8 * V1 + 5,
0866                                 8 * V1 + 6,
0867                                 8 * V1 + 7,
0868                             });
0869         }
0870 
0871         template <class A, uint64_t V0, uint64_t V1>
0872         XSIMD_INLINE batch<int64_t, A> swizzle(batch<int64_t, A> const& self, batch_constant<uint64_t, A, V0, V1> mask, requires_arch<vsx>) noexcept
0873         {
0874             return bitwise_cast<int64_t>(swizzle(bitwise_cast<uint64_t>(self), mask, vsx {}));
0875         }
0876 
0877         template <class A, uint32_t V0, uint32_t V1, uint32_t V2, uint32_t V3>
0878         XSIMD_INLINE batch<uint32_t, A> swizzle(batch<uint32_t, A> const& self, batch_constant<uint32_t, A, V0, V1, V2, V3>, requires_arch<vsx>) noexcept
0879         {
0880             return vec_perm(self.data, self.data,
0881                             (__vector unsigned char) {
0882                                 4 * V0 + 0, 4 * V0 + 1, 4 * V0 + 2, 4 * V0 + 3,
0883                                 4 * V1 + 0, 4 * V1 + 1, 4 * V1 + 2, 4 * V1 + 3,
0884                                 4 * V2 + 0, 4 * V2 + 1, 4 * V2 + 2, 4 * V2 + 3,
0885                                 4 * V3 + 0, 4 * V3 + 1, 4 * V3 + 2, 4 * V3 + 3 });
0886         }
0887 
0888         template <class A, uint32_t V0, uint32_t V1, uint32_t V2, uint32_t V3>
0889         XSIMD_INLINE batch<int32_t, A> swizzle(batch<int32_t, A> const& self, batch_constant<uint32_t, A, V0, V1, V2, V3> mask, requires_arch<vsx>) noexcept
0890         {
0891             return bitwise_cast<int32_t>(swizzle(bitwise_cast<uint32_t>(self), mask, vsx {}));
0892         }
0893 
0894         template <class A, uint16_t V0, uint16_t V1, uint16_t V2, uint16_t V3, uint16_t V4, uint16_t V5, uint16_t V6, uint16_t V7>
0895         XSIMD_INLINE batch<uint16_t, A> swizzle(batch<uint16_t, A> const& self, batch_constant<uint16_t, A, V0, V1, V2, V3, V4, V5, V6, V7>, requires_arch<vsx>) noexcept
0896         {
0897             return vec_perm(self.data, self.data,
0898                             (__vector unsigned char) {
0899                                 2 * V0 + 0, 2 * V0 + 1, 2 * V1 + 0, 2 * V1 + 1,
0900                                 2 * V2 + 0, 2 * V2 + 1, 2 * V3 + 0, 2 * V3 + 1,
0901                                 2 * V4 + 0, 2 * V4 + 1, 2 * V5 + 0, 2 * V5 + 1,
0902                                 2 * V6 + 0, 2 * V6 + 1, 2 * V7 + 0, 2 * V7 + 1 });
0903         }
0904 
0905         template <class A, uint16_t V0, uint16_t V1, uint16_t V2, uint16_t V3, uint16_t V4, uint16_t V5, uint16_t V6, uint16_t V7>
0906         XSIMD_INLINE batch<int16_t, A> swizzle(batch<int16_t, A> const& self, batch_constant<uint16_t, A, V0, V1, V2, V3, V4, V5, V6, V7> mask, requires_arch<vsx>) noexcept
0907         {
0908             return bitwise_cast<int16_t>(swizzle(bitwise_cast<uint16_t>(self), mask, vsx {}));
0909         }
0910 
0911         // trunc
0912         template <class A, class T, class = std::enable_if_t<std::is_floating_point<T>::value>>
0913         XSIMD_INLINE batch<T, A> trunc(batch<T, A> const& self, requires_arch<vsx>) noexcept
0914         {
0915             return vec_trunc(self.data);
0916         }
0917 
0918         // widen
0919         template <class A>
0920         XSIMD_INLINE std::array<batch<double, A>, 2> widen(batch<float, A> const& x, requires_arch<vsx>) noexcept
0921         {
0922             return { batch<double, A>(vec_doublel(x.data)), batch<double, A>(vec_doubleh(x.data)) };
0923         }
0924         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0925         XSIMD_INLINE std::array<batch<widen_t<T>, A>, 2> widen(batch<T, A> const& x, requires_arch<vsx>) noexcept
0926         {
0927             auto even = vec_mule(x.data, vec_splats(T(1))); // x0, x2, x4, x6
0928             auto odd = vec_mulo(x.data, vec_splats(T(1))); // x1, x3, x5, x7
0929             return { batch<widen_t<T>, A>(vec_mergel(even, odd)), batch<widen_t<T>, A>(vec_mergeh(even, odd)) };
0930         }
0931 
0932         // zip_hi
0933         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0934         XSIMD_INLINE batch<T, A> zip_hi(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0935         {
0936             return vec_mergel(self.data, other.data);
0937         }
0938 
0939         // zip_lo
0940         template <class A, class T, class = std::enable_if_t<std::is_scalar<T>::value>>
0941         XSIMD_INLINE batch<T, A> zip_lo(batch<T, A> const& self, batch<T, A> const& other, requires_arch<vsx>) noexcept
0942         {
0943             return vec_mergeh(self.data, other.data);
0944         }
0945     }
0946 }
0947 
0948 #endif