File indexing completed on 2026-11-07 10:36:59
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #ifndef XSIMD_COMMON_MEMORY_HPP
0013 #define XSIMD_COMMON_MEMORY_HPP
0014
0015 #include "../../types/xsimd_batch_constant.hpp"
0016 #include "../../utils/xsimd_type_traits.hpp"
0017 #include "./xsimd_common_details.hpp"
0018
0019 #include <algorithm>
0020 #include <array>
0021 #include <complex>
0022
0023 namespace xsimd
0024 {
0025 template <typename T, class A, T... Values>
0026 struct batch_constant;
0027
0028 template <typename T, class A, bool... Values>
0029 struct batch_bool_constant;
0030
0031 namespace kernel
0032 {
0033
0034 using namespace types;
0035
0036
0037 namespace detail
0038 {
0039 template <class T, class A>
0040 struct broadcaster
0041 {
0042 using return_type = batch<T, A>;
0043
0044 static XSIMD_INLINE return_type run(T v) noexcept
0045 {
0046 return return_type::broadcast(v);
0047 }
0048 };
0049
0050 template <class A>
0051 struct broadcaster<bool, A>
0052 {
0053 using return_type = batch_bool<xsimd::as_unsigned_integer_t<bool>, A>;
0054
0055 static XSIMD_INLINE return_type run(bool b) noexcept
0056 {
0057 return return_type(b);
0058 }
0059 };
0060 }
0061
0062
0063 namespace detail
0064 {
0065 template <class IT, class A, class I, size_t... Is>
0066 XSIMD_INLINE batch<IT, A> create_compress_swizzle_mask(I bitmask, std::index_sequence<Is...>)
0067 {
0068 batch<IT, A> swizzle_mask(IT(0));
0069 alignas(A::alignment()) IT mask_buffer[batch<IT, A>::size] = { Is... };
0070 size_t inserted = 0;
0071 for (size_t i = 0; i < sizeof...(Is); ++i)
0072 if ((bitmask >> i) & 1u)
0073 std::swap(mask_buffer[inserted++], mask_buffer[i]);
0074 return batch<IT, A>::load_aligned(&mask_buffer[0]);
0075 }
0076 }
0077
0078 template <typename A, typename T>
0079 XSIMD_INLINE batch<T, A>
0080 compress(batch<T, A> const& x, batch_bool<T, A> const& mask,
0081 kernel::requires_arch<common>) noexcept
0082 {
0083 using IT = as_unsigned_integer_t<T>;
0084 constexpr std::size_t size = batch_bool<T, A>::size;
0085 auto bitmask = mask.mask();
0086 auto z = select(mask, x, batch<T, A>((T)0));
0087 auto compress_mask = detail::create_compress_swizzle_mask<IT, A>(bitmask, std::make_index_sequence<size>());
0088 return swizzle(z, compress_mask);
0089 }
0090
0091
0092 template <typename A, typename T>
0093 XSIMD_INLINE batch<T, A>
0094 expand(batch<T, A> const& x, batch_bool<T, A> const& mask,
0095 kernel::requires_arch<common>) noexcept
0096 {
0097 constexpr auto size = batch<T, A>::size;
0098 alignas(A::alignment()) T x_in[size], x_out[size] = { T() };
0099 x.store_aligned(x_in);
0100 int i = 0, j = 0;
0101 for (auto bitmask = mask.mask(); bitmask; bitmask >>= 1, ++i)
0102 {
0103 if (bitmask & 1)
0104 x_out[i] = x_in[j++];
0105 }
0106 return xsimd::batch<T, A>::load_aligned(x_out);
0107 }
0108
0109
0110 template <class A, class T>
0111 XSIMD_INLINE batch<T, A> extract_pair(batch<T, A> const& self, batch<T, A> const& other, std::size_t i, requires_arch<common>) noexcept
0112 {
0113 constexpr std::size_t size = batch<T, A>::size;
0114 assert(i < size && "index in bounds");
0115
0116 alignas(A::alignment()) T self_buffer[size];
0117 self.store_aligned(self_buffer);
0118
0119 alignas(A::alignment()) T other_buffer[size];
0120 other.store_aligned(other_buffer);
0121
0122 alignas(A::alignment()) T concat_buffer[size];
0123
0124 for (std::size_t j = 0; j < (size - i); ++j)
0125 {
0126 concat_buffer[j] = other_buffer[i + j];
0127 if (j < i)
0128 {
0129 concat_buffer[size - 1 - j] = self_buffer[i - 1 - j];
0130 }
0131 }
0132 return batch<T, A>::load_aligned(concat_buffer);
0133 }
0134
0135
0136 namespace detail
0137 {
0138
0139 template <size_t N, typename T, typename A, typename U, typename V, std::enable_if_t<N == 0, int> = 0>
0140 inline batch<T, A> gather(U const* src, batch<V, A> const& index,
0141 ::xsimd::index<N> I) noexcept
0142 {
0143 return insert(batch<T, A> {}, static_cast<T>(src[index.get(I)]), I);
0144 }
0145
0146 template <size_t N, typename T, typename A, typename U, typename V, std::enable_if_t<N != 0, int> = 0>
0147 inline batch<T, A>
0148 gather(U const* src, batch<V, A> const& index, ::xsimd::index<N> I) noexcept
0149 {
0150 static_assert(N <= batch<V, A>::size, "Incorrect value in recursion!");
0151
0152 const auto test = gather<N - 1, T, A>(src, index, {});
0153 return insert(test, static_cast<T>(src[index.get(I)]), I);
0154 }
0155 }
0156
0157 template <typename T, typename A, typename V>
0158 XSIMD_INLINE batch<T, A>
0159 gather(batch<T, A> const&, T const* src, batch<V, A> const& index,
0160 kernel::requires_arch<common>) noexcept
0161 {
0162 static_assert(batch<T, A>::size == batch<V, A>::size,
0163 "Index and destination sizes must match");
0164
0165 return detail::gather<batch<V, A>::size - 1, T, A>(src, index, {});
0166 }
0167
0168
0169 template <typename T, typename A, typename U, typename V>
0170 XSIMD_INLINE detail::sizes_mismatch_t<T, U, batch<T, A>>
0171 gather(batch<T, A> const&, U const* src, batch<V, A> const& index,
0172 kernel::requires_arch<common>) noexcept
0173 {
0174 static_assert(batch<T, A>::size == batch<V, A>::size,
0175 "Index and destination sizes must match");
0176
0177 return detail::gather<batch<V, A>::size - 1, T, A>(src, index, {});
0178 }
0179
0180
0181 template <typename T, typename A, typename U, typename V>
0182 XSIMD_INLINE detail::stride_match_t<T, U, batch<T, A>>
0183 gather(batch<T, A> const&, U const* src, batch<V, A> const& index,
0184 kernel::requires_arch<common>) noexcept
0185 {
0186 static_assert(batch<T, A>::size == batch<V, A>::size,
0187 "Index and destination sizes must match");
0188
0189 return batch_cast<T>(kernel::gather(batch<U, A> {}, src, index, A {}));
0190 }
0191
0192
0193 template <class A, class T, size_t I>
0194 XSIMD_INLINE batch<T, A> insert(batch<T, A> const& self, T val, index<I>, requires_arch<common>) noexcept
0195 {
0196 struct index_mask
0197 {
0198 static constexpr bool get(size_t index, size_t )
0199 {
0200 return index != I;
0201 }
0202 };
0203 batch<T, A> tmp(val);
0204 return select(make_batch_bool_constant<T, index_mask, A>(), self, tmp);
0205 }
0206
0207
0208 template <class A, size_t I, class T>
0209 XSIMD_INLINE T get(batch<T, A> const& self, ::xsimd::index<I>, requires_arch<common>) noexcept
0210 {
0211 alignas(A::alignment()) T buffer[batch<T, A>::size];
0212 self.store_aligned(&buffer[0]);
0213 return buffer[I];
0214 }
0215
0216 template <class A, size_t I, class T>
0217 XSIMD_INLINE T get(batch_bool<T, A> const& self, ::xsimd::index<I>, requires_arch<common>) noexcept
0218 {
0219 alignas(A::alignment()) T buffer[batch_bool<T, A>::size];
0220 self.store_aligned(&buffer[0]);
0221 return buffer[I];
0222 }
0223
0224 template <class A, size_t I, class T>
0225 XSIMD_INLINE typename batch<std::complex<T>, A>::value_type get(batch<std::complex<T>, A> const& self, ::xsimd::index<I>, requires_arch<common>) noexcept
0226 {
0227 using value_type = typename batch<std::complex<T>, A>::value_type;
0228 alignas(A::alignment()) value_type buffer[batch<std::complex<T>, A>::size];
0229 self.store_aligned(&buffer[0]);
0230 return buffer[I];
0231 }
0232
0233 template <class A, class T>
0234 XSIMD_INLINE T get(batch<T, A> const& self, std::size_t i, requires_arch<common>) noexcept
0235 {
0236 alignas(A::alignment()) T buffer[batch<T, A>::size];
0237 self.store_aligned(&buffer[0]);
0238 return buffer[i];
0239 }
0240
0241 template <class A, class T>
0242 XSIMD_INLINE T get(batch_bool<T, A> const& self, std::size_t i, requires_arch<common>) noexcept
0243 {
0244 alignas(A::alignment()) bool buffer[batch_bool<T, A>::size];
0245 self.store_aligned(&buffer[0]);
0246 return buffer[i];
0247 }
0248
0249 template <class A, class T>
0250 XSIMD_INLINE typename batch<std::complex<T>, A>::value_type get(batch<std::complex<T>, A> const& self, std::size_t i, requires_arch<common>) noexcept
0251 {
0252 using T2 = typename batch<std::complex<T>, A>::value_type;
0253 alignas(A::alignment()) T2 buffer[batch<std::complex<T>, A>::size];
0254 self.store_aligned(&buffer[0]);
0255 return buffer[i];
0256 }
0257
0258
0259 template <class A, class T>
0260 XSIMD_INLINE T first(batch<T, A> const& self, requires_arch<common>) noexcept
0261 {
0262 return get(self, 0, common {});
0263 }
0264
0265 template <class A, class T>
0266 XSIMD_INLINE T first(batch_bool<T, A> const& self, requires_arch<common>) noexcept
0267 {
0268 return first(batch<T, A>(self), A {});
0269 }
0270
0271 template <class A, class T>
0272 XSIMD_INLINE typename batch<std::complex<T>, A>::value_type first(batch<std::complex<T>, A> const& self, requires_arch<common>) noexcept
0273 {
0274 return { first(self.real(), A {}), first(self.imag(), A {}) };
0275 }
0276
0277
0278 template <class A, class T>
0279 XSIMD_INLINE batch_bool<T, A> load_unaligned(bool const* mem, batch_bool<T, A>, requires_arch<common>) noexcept
0280 {
0281 using batch_type = batch<T, A>;
0282 batch_type ref(0);
0283 constexpr auto size = batch_bool<T, A>::size;
0284 alignas(A::alignment()) T buffer[size];
0285 for (std::size_t i = 0; i < size; ++i)
0286 buffer[i] = mem[i] ? 1 : 0;
0287 return ref != batch_type::load_aligned(&buffer[0]);
0288 }
0289
0290 template <class A, class T>
0291 XSIMD_INLINE batch_bool<T, A> load_aligned(bool const* mem, batch_bool<T, A> b, requires_arch<common>) noexcept
0292 {
0293 return load_unaligned(mem, b, A {});
0294 }
0295
0296 template <class A, class T>
0297 XSIMD_INLINE batch_bool<T, A> load_stream(bool const* mem, batch_bool<T, A> b, requires_arch<common>) noexcept
0298 {
0299 return load_aligned(mem, b, A {});
0300 }
0301
0302
0303 namespace detail
0304 {
0305 template <class A, class T_in, class T_out>
0306 XSIMD_INLINE batch<T_out, A> load_aligned(T_in const* mem, convert<T_out>, requires_arch<common>, with_fast_conversion) noexcept
0307 {
0308 using batch_type_in = batch<T_in, A>;
0309 using batch_type_out = batch<T_out, A>;
0310 return fast_cast(batch_type_in::load_aligned(mem), batch_type_out(), A {});
0311 }
0312 template <class A, class T_in, class T_out>
0313 XSIMD_INLINE batch<T_out, A> load_aligned(T_in const* mem, convert<T_out>, requires_arch<common>, with_slow_conversion) noexcept
0314 {
0315 static_assert(!std::is_same<T_in, T_out>::value, "there should be a direct load for this type combination");
0316 using batch_type_out = batch<T_out, A>;
0317 alignas(A::alignment()) T_out buffer[batch_type_out::size];
0318 std::copy(mem, mem + batch_type_out::size, std::begin(buffer));
0319 return batch_type_out::load_aligned(buffer);
0320 }
0321 }
0322 template <class A, class T_in, class T_out>
0323 XSIMD_INLINE batch<T_out, A> load_aligned(T_in const* mem, convert<T_out> cvt, requires_arch<common>) noexcept
0324 {
0325 return detail::load_aligned<A>(mem, cvt, A {}, detail::conversion_type<A, T_in, T_out> {});
0326 }
0327
0328
0329 namespace detail
0330 {
0331 template <class A, class T_in, class T_out>
0332 XSIMD_INLINE batch<T_out, A> load_unaligned(T_in const* mem, convert<T_out>, requires_arch<common>, with_fast_conversion) noexcept
0333 {
0334 using batch_type_in = batch<T_in, A>;
0335 using batch_type_out = batch<T_out, A>;
0336 return fast_cast(batch_type_in::load_unaligned(mem), batch_type_out(), A {});
0337 }
0338
0339 template <class A, class T_in, class T_out>
0340 XSIMD_INLINE batch<T_out, A> load_unaligned(T_in const* mem, convert<T_out> cvt, requires_arch<common>, with_slow_conversion) noexcept
0341 {
0342 static_assert(!std::is_same<T_in, T_out>::value, "there should be a direct load for this type combination");
0343 return load_aligned<A>(mem, cvt, common {}, with_slow_conversion {});
0344 }
0345 }
0346 template <class A, class T_in, class T_out>
0347 XSIMD_INLINE batch<T_out, A> load_unaligned(T_in const* mem, convert<T_out> cvt, requires_arch<common>) noexcept
0348 {
0349 return detail::load_unaligned<A>(mem, cvt, common {}, detail::conversion_type<A, T_in, T_out> {});
0350 }
0351
0352 template <class A, class T>
0353 XSIMD_INLINE batch<T, A> load(T const* mem, aligned_mode, requires_arch<A>) noexcept
0354 {
0355 return load_aligned<A>(mem, convert<T> {}, A {});
0356 }
0357
0358 template <class A, class T>
0359 XSIMD_INLINE batch<T, A> load(T const* mem, unaligned_mode, requires_arch<A>) noexcept
0360 {
0361 return load_unaligned<A>(mem, convert<T> {}, A {});
0362 }
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372 namespace detail
0373 {
0374
0375
0376
0377 template <class A, class T_in, class T_out>
0378 using masked_memory_uses_fp_bitcast = std::integral_constant<bool,
0379 std::is_same<T_in, T_out>::value
0380 && std::is_integral<T_out>::value
0381 && !std::is_void<sized_fp_t<sizeof(T_out)>>::value
0382 && types::has_simd_register<sized_fp_t<sizeof(T_out)>, A>::value>;
0383
0384
0385 template <class A, class T_in, class T_out, bool... Values, class alignment>
0386 XSIMD_INLINE batch<T_out, A>
0387 load_masked_common(T_in const* mem, batch_bool_constant<T_out, A, Values...>, convert<T_out>, alignment, std::false_type ) noexcept
0388 {
0389 constexpr std::size_t size = batch<T_out, A>::size;
0390 alignas(A::alignment()) std::array<T_out, size> buffer {};
0391 constexpr bool mask[size] = { Values... };
0392
0393 for (std::size_t i = 0; i < size; ++i)
0394 buffer[i] = mask[i] ? static_cast<T_out>(mem[i]) : T_out(0);
0395
0396 return batch<T_out, A>::load(buffer.data(), aligned_mode {});
0397 }
0398
0399
0400
0401 template <class A, class T, bool... Values, class Mode>
0402 XSIMD_INLINE batch<T, A>
0403 load_masked_common(T const* mem, batch_bool_constant<T, A, Values...>, convert<T>, Mode, std::true_type ) noexcept
0404 {
0405 using fp_t = sized_fp_t<sizeof(T)>;
0406 const auto f = ::xsimd::kernel::load_masked<A>(reinterpret_cast<const fp_t*>(mem), batch_bool_constant<fp_t, A, Values...> {}, convert<fp_t> {}, Mode {}, A {});
0407 return bitwise_cast<T>(f);
0408 }
0409
0410 template <class A, class T_in, class T_out, bool... Values, class alignment>
0411 XSIMD_INLINE void
0412 store_masked_common(T_out* mem, batch<T_in, A> const& src, batch_bool_constant<T_in, A, Values...>, alignment, std::false_type ) noexcept
0413 {
0414 constexpr std::size_t size = batch<T_in, A>::size;
0415 constexpr bool mask[size] = { Values... };
0416
0417 for (std::size_t i = 0; i < size; ++i)
0418 if (mask[i])
0419 {
0420 mem[i] = static_cast<T_out>(src.get(i));
0421 }
0422 }
0423
0424 template <class A, class T, bool... Values, class Mode>
0425 XSIMD_INLINE void
0426 store_masked_common(T* mem, batch<T, A> const& src, batch_bool_constant<T, A, Values...>, Mode, std::true_type ) noexcept
0427 {
0428 using fp_t = sized_fp_t<sizeof(T)>;
0429 ::xsimd::kernel::store_masked<A>(reinterpret_cast<fp_t*>(mem), bitwise_cast<fp_t>(src), batch_bool_constant<fp_t, A, Values...> {}, Mode {}, A {});
0430 }
0431 }
0432
0433 template <class A, class T_in, class T_out, bool... Values, class alignment>
0434 XSIMD_INLINE batch<T_out, A>
0435 load_masked(T_in const* mem, batch_bool_constant<T_out, A, Values...> mask, convert<T_out> cvt, alignment mode, requires_arch<common>) noexcept
0436 {
0437 return detail::load_masked_common(mem, mask, cvt, mode, detail::masked_memory_uses_fp_bitcast<A, T_in, T_out> {});
0438 }
0439
0440 template <class A, class T, class Mode>
0441 XSIMD_INLINE batch<T, A>
0442 load_masked(T const* mem, batch_bool<T, A> mask, convert<T>, Mode, requires_arch<common>) noexcept
0443 {
0444
0445
0446 constexpr std::size_t size = batch<T, A>::size;
0447 alignas(A::alignment()) std::array<T, size> buffer;
0448 for (std::size_t i = 0; i < size; ++i)
0449 buffer[i] = mask.get(i) ? mem[i] : T(0);
0450 return batch<T, A>::load_aligned(buffer.data());
0451 }
0452
0453 template <class A, class T_in, class T_out, bool... Values, class alignment>
0454 XSIMD_INLINE void
0455 store_masked(T_out* mem, batch<T_in, A> const& src, batch_bool_constant<T_in, A, Values...> mask, alignment mode, requires_arch<common>) noexcept
0456 {
0457 detail::store_masked_common(mem, src, mask, mode, detail::masked_memory_uses_fp_bitcast<A, T_in, T_out> {});
0458 }
0459
0460 template <class A, class T, class Mode>
0461 XSIMD_INLINE void
0462 store_masked(T* mem, batch<T, A> const& src, batch_bool<T, A> mask, Mode, requires_arch<common>) noexcept
0463 {
0464
0465
0466 constexpr std::size_t size = batch<T, A>::size;
0467 alignas(A::alignment()) std::array<T, size> src_buf;
0468 src.store_aligned(src_buf.data());
0469 for (std::size_t i = 0; i < size; ++i)
0470 if (mask.get(i))
0471 mem[i] = src_buf[i];
0472 }
0473
0474 template <class A, class T_in, class T_out>
0475 XSIMD_INLINE batch<T_out, A> load_stream(T_in const* mem, convert<T_out> cvt, requires_arch<common>) noexcept
0476 {
0477 return load_aligned<A>(mem, cvt, A {});
0478 }
0479
0480
0481 template <size_t N, class A, class T>
0482 XSIMD_INLINE batch<T, A> rotate_right(batch<T, A> const& self, requires_arch<common>) noexcept
0483 {
0484 struct rotate_generator
0485 {
0486 static constexpr size_t get(size_t index, size_t size)
0487 {
0488 return (index - N) % size;
0489 }
0490 };
0491
0492 return swizzle(self, make_batch_constant<as_unsigned_integer_t<T>, rotate_generator, A>());
0493 }
0494
0495 template <size_t N, class A, class T>
0496 XSIMD_INLINE batch<std::complex<T>, A> rotate_right(batch<std::complex<T>, A> const& self, requires_arch<common>) noexcept
0497 {
0498 return { rotate_right<N>(self.real()), rotate_right<N>(self.imag()) };
0499 }
0500
0501
0502 template <size_t N, class A, class T>
0503 XSIMD_INLINE batch<T, A> rotate_left(batch<T, A> const& self, requires_arch<common>) noexcept
0504 {
0505 struct rotate_generator
0506 {
0507 static constexpr size_t get(size_t index, size_t size)
0508 {
0509 return (index + N) % size;
0510 }
0511 };
0512
0513 return swizzle(self, make_batch_constant<as_unsigned_integer_t<T>, rotate_generator, A>());
0514 }
0515
0516 template <size_t N, class A, class T>
0517 XSIMD_INLINE batch<std::complex<T>, A> rotate_left(batch<std::complex<T>, A> const& self, requires_arch<common>) noexcept
0518 {
0519 return { rotate_left<N>(self.real()), rotate_left<N>(self.imag()) };
0520 }
0521
0522
0523 namespace detail
0524 {
0525 template <size_t N, typename T, typename A, typename U, typename V, std::enable_if_t<N == 0, int> = 0>
0526 XSIMD_INLINE void scatter(batch<T, A> const& src, U* dst,
0527 batch<V, A> const& index,
0528 ::xsimd::index<N> I) noexcept
0529 {
0530 dst[index.get(I)] = static_cast<U>(src.get(I));
0531 }
0532
0533 template <size_t N, typename T, typename A, typename U, typename V, std::enable_if_t<N != 0, int> = 0>
0534 XSIMD_INLINE void
0535 scatter(batch<T, A> const& src, U* dst, batch<V, A> const& index,
0536 ::xsimd::index<N> I) noexcept
0537 {
0538 static_assert(N <= batch<V, A>::size, "Incorrect value in recursion!");
0539
0540 kernel::detail::scatter<N - 1, T, A, U, V>(
0541 src, dst, index, {});
0542 dst[index.get(I)] = static_cast<U>(src.get(I));
0543 }
0544 }
0545
0546 template <typename A, typename T, typename V>
0547 XSIMD_INLINE void
0548 scatter(batch<T, A> const& src, T* dst,
0549 batch<V, A> const& index,
0550 kernel::requires_arch<common>) noexcept
0551 {
0552 static_assert(batch<T, A>::size == batch<V, A>::size,
0553 "Source and index sizes must match");
0554 kernel::detail::scatter<batch<V, A>::size - 1, T, A, T, V>(
0555 src, dst, index, {});
0556 }
0557
0558 template <typename A, typename T, typename U, typename V>
0559 XSIMD_INLINE detail::sizes_mismatch_t<T, U, void>
0560 scatter(batch<T, A> const& src, U* dst,
0561 batch<V, A> const& index,
0562 kernel::requires_arch<common>) noexcept
0563 {
0564 static_assert(batch<T, A>::size == batch<V, A>::size,
0565 "Source and index sizes must match");
0566 kernel::detail::scatter<batch<V, A>::size - 1, T, A, U, V>(
0567 src, dst, index, {});
0568 }
0569
0570 template <typename A, typename T, typename U, typename V>
0571 XSIMD_INLINE detail::stride_match_t<T, U, void>
0572 scatter(batch<T, A> const& src, U* dst,
0573 batch<V, A> const& index,
0574 kernel::requires_arch<common>) noexcept
0575 {
0576 static_assert(batch<T, A>::size == batch<V, A>::size,
0577 "Source and index sizes must match");
0578 const auto tmp = batch_cast<U>(src);
0579 kernel::scatter<A>(tmp, dst, index, A {});
0580 }
0581
0582
0583 namespace detail
0584 {
0585 constexpr bool is_swizzle_fst(size_t)
0586 {
0587 return true;
0588 }
0589 template <typename ITy, typename... ITys>
0590 constexpr bool is_swizzle_fst(size_t bsize, ITy index, ITys... indices)
0591 {
0592 return index < bsize && is_swizzle_fst(bsize, indices...);
0593 }
0594 constexpr bool is_swizzle_snd(size_t)
0595 {
0596 return true;
0597 }
0598 template <typename ITy, typename... ITys>
0599 constexpr bool is_swizzle_snd(size_t bsize, ITy index, ITys... indices)
0600 {
0601 return index >= bsize && is_swizzle_snd(bsize, indices...);
0602 }
0603
0604 constexpr bool is_zip_lo(size_t)
0605 {
0606 return true;
0607 }
0608
0609 template <typename ITy>
0610 constexpr bool is_zip_lo(size_t, ITy)
0611 {
0612 return false;
0613 }
0614
0615 template <typename ITy0, typename ITy1, typename... ITys>
0616 constexpr bool is_zip_lo(size_t bsize, ITy0 index0, ITy1 index1, ITys... indices)
0617 {
0618 return index0 == (bsize - (sizeof...(indices) + 2)) && index1 == (2 * bsize - (sizeof...(indices) + 2)) && is_zip_lo(bsize, indices...);
0619 }
0620
0621 constexpr bool is_zip_hi(size_t)
0622 {
0623 return true;
0624 }
0625
0626 template <typename ITy>
0627 constexpr bool is_zip_hi(size_t, ITy)
0628 {
0629 return false;
0630 }
0631
0632 template <typename ITy0, typename ITy1, typename... ITys>
0633 constexpr bool is_zip_hi(size_t bsize, ITy0 index0, ITy1 index1, ITys... indices)
0634 {
0635 return index0 == (bsize / 2 + bsize - (sizeof...(indices) + 2)) && index1 == (bsize / 2 + 2 * bsize - (sizeof...(indices) + 2)) && is_zip_hi(bsize, indices...);
0636 }
0637
0638 constexpr bool is_select(size_t)
0639 {
0640 return true;
0641 }
0642
0643 template <typename ITy, typename... ITys>
0644 constexpr bool is_select(size_t bsize, ITy index, ITys... indices)
0645 {
0646 return (index < bsize ? index : index - bsize) == (bsize - sizeof...(ITys)) && is_select(bsize, indices...);
0647 }
0648
0649 }
0650
0651 template <class A, typename T, typename ITy, ITy... Indices>
0652 XSIMD_INLINE batch<T, A> shuffle(batch<T, A> const& x, batch<T, A> const& y, batch_constant<ITy, A, Indices...>, requires_arch<common>) noexcept
0653 {
0654 constexpr size_t bsize = sizeof...(Indices);
0655 static_assert(bsize == batch<T, A>::size, "valid shuffle");
0656
0657
0658 XSIMD_IF_CONSTEXPR(detail::is_swizzle_fst(bsize, Indices...))
0659 {
0660 return swizzle(x, batch_constant<ITy, A, ((Indices >= bsize) ? 0 : Indices)...>());
0661 }
0662
0663 XSIMD_IF_CONSTEXPR(detail::is_swizzle_snd(bsize, Indices...))
0664 {
0665 return swizzle(y, batch_constant<ITy, A, ((Indices >= bsize) ? (Indices - bsize) : 0 )...>());
0666 }
0667
0668 XSIMD_IF_CONSTEXPR(detail::is_zip_lo(bsize, Indices...))
0669 {
0670 return zip_lo(x, y);
0671 }
0672
0673 XSIMD_IF_CONSTEXPR(detail::is_zip_hi(bsize, Indices...))
0674 {
0675 return zip_hi(x, y);
0676 }
0677
0678 XSIMD_IF_CONSTEXPR(detail::is_select(bsize, Indices...))
0679 {
0680 return select(batch_bool_constant<T, A, (Indices < bsize)...>(), x, y);
0681 }
0682
0683 #if defined(__has_builtin) && !defined(XSIMD_WITH_EMULATED)
0684 #if __has_builtin(__builtin_shufflevector)
0685 #define builtin_shuffle __builtin_shufflevector
0686 #endif
0687 #endif
0688
0689 #if defined(builtin_shuffle)
0690 typedef T vty __attribute__((__vector_size__(sizeof(batch<T, A>))));
0691 return (typename batch<T, A>::register_type)builtin_shuffle((vty)x.data, (vty)y.data, Indices...);
0692
0693
0694
0695
0696
0697
0698
0699 #else
0700
0701
0702 batch<T, A> x_lane = swizzle(x, batch_constant<ITy, A, ((Indices >= bsize) ? (Indices - bsize) : Indices)...>());
0703 batch<T, A> y_lane = swizzle(y, batch_constant<ITy, A, ((Indices >= bsize) ? (Indices - bsize) : Indices)...>());
0704 batch_bool_constant<T, A, (Indices < bsize)...> select_x_lane;
0705 return select(select_x_lane, x_lane, y_lane);
0706 #endif
0707 }
0708
0709
0710 template <class A, class T>
0711 XSIMD_INLINE void store(batch_bool<T, A> const& self, bool* mem, requires_arch<common>) noexcept
0712 {
0713 using batch_type = batch<T, A>;
0714 constexpr auto size = batch_bool<T, A>::size;
0715 alignas(A::alignment()) T buffer[size];
0716 kernel::store_aligned<A>(&buffer[0], batch_type(self), A {});
0717 for (std::size_t i = 0; i < size; ++i)
0718 mem[i] = bool(buffer[i]);
0719 }
0720
0721 template <class A, class T>
0722 XSIMD_INLINE void store_stream(batch_bool<T, A> const& self, bool* mem, requires_arch<common>) noexcept
0723 {
0724 store(self, mem, A {});
0725 }
0726
0727
0728 template <class A, class T_in, class T_out>
0729 XSIMD_INLINE void store_aligned(T_out* mem, batch<T_in, A> const& self, requires_arch<common>) noexcept
0730 {
0731 static_assert(!std::is_same<T_in, T_out>::value, "there should be a direct store for this type combination");
0732 alignas(A::alignment()) T_in buffer[batch<T_in, A>::size];
0733 store_aligned(&buffer[0], self);
0734 std::copy(std::begin(buffer), std::end(buffer), mem);
0735 }
0736
0737
0738 template <class A, class T_in, class T_out>
0739 XSIMD_INLINE void store_unaligned(T_out* mem, batch<T_in, A> const& self, requires_arch<common>) noexcept
0740 {
0741 static_assert(!std::is_same<T_in, T_out>::value, "there should be a direct store for this type combination");
0742 return store_aligned<A>(mem, self, common {});
0743 }
0744
0745 template <class A, class T_in, class T_out>
0746 XSIMD_INLINE void store_stream(T_out* mem, batch<T_in, A> const& self, requires_arch<common>) noexcept
0747 {
0748 store_aligned<A>(mem, self, A {});
0749 }
0750
0751
0752 template <class A, class T, class ITy, ITy... Vs>
0753 XSIMD_INLINE batch<std::complex<T>, A> swizzle(batch<std::complex<T>, A> const& self, batch_constant<ITy, A, Vs...> mask, requires_arch<common>) noexcept
0754 {
0755 return { swizzle(self.real(), mask), swizzle(self.imag(), mask) };
0756 }
0757
0758 template <class A, class T, class ITy>
0759 XSIMD_INLINE batch<T, A> swizzle(batch<T, A> const& self, batch<ITy, A> mask, requires_arch<common>) noexcept
0760 {
0761 constexpr size_t size = batch<T, A>::size;
0762 alignas(A::alignment()) T self_buffer[size];
0763 store_aligned(&self_buffer[0], self);
0764
0765 alignas(A::alignment()) ITy mask_buffer[size];
0766 store_aligned(&mask_buffer[0], mask);
0767
0768 alignas(A::alignment()) T out_buffer[size];
0769 for (size_t i = 0; i < size; ++i)
0770 out_buffer[i] = self_buffer[mask_buffer[i]];
0771 return batch<T, A>::load_aligned(out_buffer);
0772 }
0773
0774 template <class A, class T, class ITy, ITy... Is>
0775 XSIMD_INLINE batch<T, A> swizzle(batch<T, A> const& self, batch_constant<ITy, A, Is...>, requires_arch<common>) noexcept
0776 {
0777 constexpr size_t size = batch<T, A>::size;
0778 alignas(A::alignment()) T self_buffer[size];
0779 store_aligned(&self_buffer[0], self);
0780 return { self_buffer[Is]... };
0781 }
0782
0783 template <class A, class T, class ITy>
0784 XSIMD_INLINE batch<std::complex<T>, A> swizzle(batch<std::complex<T>, A> const& self, batch<ITy, A> mask, requires_arch<common>) noexcept
0785 {
0786 return { swizzle(self.real(), mask), swizzle(self.imag(), mask) };
0787 }
0788
0789
0790 namespace detail
0791 {
0792 template <class A, class T>
0793 XSIMD_INLINE batch<std::complex<T>, A> load_complex(batch<T, A> const& , batch<T, A> const& , requires_arch<common>) noexcept
0794 {
0795 static_assert(std::is_same<T, void>::value, "load_complex not implemented for the required architecture");
0796 }
0797
0798 template <class A, class T>
0799 XSIMD_INLINE batch<T, A> complex_high(batch<std::complex<T>, A> const& , requires_arch<common>) noexcept
0800 {
0801 static_assert(std::is_same<T, void>::value, "complex_high not implemented for the required architecture");
0802 }
0803
0804 template <class A, class T>
0805 XSIMD_INLINE batch<T, A> complex_low(batch<std::complex<T>, A> const& , requires_arch<common>) noexcept
0806 {
0807 static_assert(std::is_same<T, void>::value, "complex_low not implemented for the required architecture");
0808 }
0809 }
0810
0811 template <class A, class T_out, class T_in>
0812 XSIMD_INLINE batch<std::complex<T_out>, A> load_complex_aligned(std::complex<T_in> const* mem, convert<std::complex<T_out>>, requires_arch<common>) noexcept
0813 {
0814 using real_batch = batch<T_out, A>;
0815 T_in const* buffer = reinterpret_cast<T_in const*>(mem);
0816 real_batch hi = real_batch::load_aligned(buffer),
0817 lo = real_batch::load_aligned(buffer + real_batch::size);
0818 return detail::load_complex(hi, lo, A {});
0819 }
0820
0821
0822 template <class A, class T_out, class T_in>
0823 XSIMD_INLINE batch<std::complex<T_out>, A> load_complex_unaligned(std::complex<T_in> const* mem, convert<std::complex<T_out>>, requires_arch<common>) noexcept
0824 {
0825 using real_batch = batch<T_out, A>;
0826 T_in const* buffer = reinterpret_cast<T_in const*>(mem);
0827 real_batch hi = real_batch::load_unaligned(buffer),
0828 lo = real_batch::load_unaligned(buffer + real_batch::size);
0829 return detail::load_complex(hi, lo, A {});
0830 }
0831
0832 template <class A, class T_out, class T_in>
0833 XSIMD_INLINE batch<std::complex<T_out>, A> load_complex_stream(std::complex<T_in> const* mem, convert<std::complex<T_out>>, requires_arch<common>) noexcept
0834 {
0835 return load_complex_aligned<A>(mem, kernel::convert<std::complex<T_out>> {}, A {});
0836 }
0837
0838
0839 template <class A, class T_out, class T_in>
0840 XSIMD_INLINE void store_complex_aligned(std::complex<T_out>* dst, batch<std::complex<T_in>, A> const& src, requires_arch<common>) noexcept
0841 {
0842 using real_batch = batch<T_in, A>;
0843 real_batch hi = detail::complex_high(src, A {});
0844 real_batch lo = detail::complex_low(src, A {});
0845 T_out* buffer = reinterpret_cast<T_out*>(dst);
0846 lo.store_aligned(buffer);
0847 hi.store_aligned(buffer + real_batch::size);
0848 }
0849
0850
0851 template <class A, class T_out, class T_in>
0852 XSIMD_INLINE void store_complex_unaligned(std::complex<T_out>* dst, batch<std::complex<T_in>, A> const& src, requires_arch<common>) noexcept
0853 {
0854 using real_batch = batch<T_in, A>;
0855 real_batch hi = detail::complex_high(src, A {});
0856 real_batch lo = detail::complex_low(src, A {});
0857 T_out* buffer = reinterpret_cast<T_out*>(dst);
0858 lo.store_unaligned(buffer);
0859 hi.store_unaligned(buffer + real_batch::size);
0860 }
0861
0862 template <class A, class T_out, class T_in>
0863 XSIMD_INLINE void store_complex_stream(std::complex<T_out>* dst, batch<std::complex<T_in>, A> const& src, requires_arch<common>) noexcept
0864 {
0865 store_complex_aligned<A>(dst, src, A {});
0866 }
0867
0868
0869 template <class A, class T>
0870 XSIMD_INLINE void transpose(batch<T, A>* matrix_begin, batch<T, A>* matrix_end, requires_arch<common>) noexcept
0871 {
0872 assert((matrix_end - matrix_begin == batch<T, A>::size) && "correctly sized matrix");
0873 (void)matrix_end;
0874 alignas(A::alignment()) T scratch_buffer[batch<T, A>::size * batch<T, A>::size];
0875 for (size_t i = 0; i < batch<T, A>::size; ++i)
0876 {
0877 matrix_begin[i].store_aligned(&scratch_buffer[i * batch<T, A>::size]);
0878 }
0879
0880 for (size_t i = 0; i < batch<T, A>::size; ++i)
0881 {
0882 for (size_t j = 0; j < i; ++j)
0883 {
0884 std::swap(scratch_buffer[i * batch<T, A>::size + j],
0885 scratch_buffer[j * batch<T, A>::size + i]);
0886 }
0887 }
0888 for (size_t i = 0; i < batch<T, A>::size; ++i)
0889 {
0890 matrix_begin[i] = batch<T, A>::load_aligned(&scratch_buffer[i * batch<T, A>::size]);
0891 }
0892 }
0893
0894
0895 template <class A, class = std::enable_if_t<batch<int16_t, A>::size == 8>>
0896 XSIMD_INLINE void transpose(batch<int16_t, A>* matrix_begin, batch<int16_t, A>* matrix_end, requires_arch<common>) noexcept
0897 {
0898 assert((matrix_end - matrix_begin == batch<int16_t, A>::size) && "correctly sized matrix");
0899 (void)matrix_end;
0900 auto l0 = zip_lo(matrix_begin[0], matrix_begin[1]);
0901 auto l1 = zip_lo(matrix_begin[2], matrix_begin[3]);
0902 auto l2 = zip_lo(matrix_begin[4], matrix_begin[5]);
0903 auto l3 = zip_lo(matrix_begin[6], matrix_begin[7]);
0904
0905 auto l4 = zip_lo(bit_cast<batch<int32_t, A>>(l0), bit_cast<batch<int32_t, A>>(l1));
0906 auto l5 = zip_lo(bit_cast<batch<int32_t, A>>(l2), bit_cast<batch<int32_t, A>>(l3));
0907
0908 auto l6 = zip_hi(bit_cast<batch<int32_t, A>>(l0), bit_cast<batch<int32_t, A>>(l1));
0909 auto l7 = zip_hi(bit_cast<batch<int32_t, A>>(l2), bit_cast<batch<int32_t, A>>(l3));
0910
0911 auto h0 = zip_hi(matrix_begin[0], matrix_begin[1]);
0912 auto h1 = zip_hi(matrix_begin[2], matrix_begin[3]);
0913 auto h2 = zip_hi(matrix_begin[4], matrix_begin[5]);
0914 auto h3 = zip_hi(matrix_begin[6], matrix_begin[7]);
0915
0916 auto h4 = zip_lo(bit_cast<batch<int32_t, A>>(h0), bit_cast<batch<int32_t, A>>(h1));
0917 auto h5 = zip_lo(bit_cast<batch<int32_t, A>>(h2), bit_cast<batch<int32_t, A>>(h3));
0918
0919 auto h6 = zip_hi(bit_cast<batch<int32_t, A>>(h0), bit_cast<batch<int32_t, A>>(h1));
0920 auto h7 = zip_hi(bit_cast<batch<int32_t, A>>(h2), bit_cast<batch<int32_t, A>>(h3));
0921
0922 matrix_begin[0] = bit_cast<batch<int16_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(l4), bit_cast<batch<int64_t, A>>(l5)));
0923 matrix_begin[1] = bit_cast<batch<int16_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(l4), bit_cast<batch<int64_t, A>>(l5)));
0924 matrix_begin[2] = bit_cast<batch<int16_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(l6), bit_cast<batch<int64_t, A>>(l7)));
0925 matrix_begin[3] = bit_cast<batch<int16_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(l6), bit_cast<batch<int64_t, A>>(l7)));
0926
0927 matrix_begin[4] = bit_cast<batch<int16_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(h4), bit_cast<batch<int64_t, A>>(h5)));
0928 matrix_begin[5] = bit_cast<batch<int16_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(h4), bit_cast<batch<int64_t, A>>(h5)));
0929 matrix_begin[6] = bit_cast<batch<int16_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(h6), bit_cast<batch<int64_t, A>>(h7)));
0930 matrix_begin[7] = bit_cast<batch<int16_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(h6), bit_cast<batch<int64_t, A>>(h7)));
0931 }
0932
0933 template <class A>
0934 XSIMD_INLINE void transpose(batch<uint16_t, A>* matrix_begin, batch<uint16_t, A>* matrix_end, requires_arch<common>) noexcept
0935 {
0936 transpose(reinterpret_cast<batch<int16_t, A>*>(matrix_begin), reinterpret_cast<batch<int16_t, A>*>(matrix_end), A {});
0937 }
0938
0939 template <class A, class = std::enable_if_t<batch<int8_t, A>::size == 16>>
0940 XSIMD_INLINE void transpose(batch<int8_t, A>* matrix_begin, batch<int8_t, A>* matrix_end, requires_arch<common>) noexcept
0941 {
0942 assert((matrix_end - matrix_begin == batch<int8_t, A>::size) && "correctly sized matrix");
0943 (void)matrix_end;
0944 auto l0 = zip_lo(matrix_begin[0], matrix_begin[1]);
0945 auto l1 = zip_lo(matrix_begin[2], matrix_begin[3]);
0946 auto l2 = zip_lo(matrix_begin[4], matrix_begin[5]);
0947 auto l3 = zip_lo(matrix_begin[6], matrix_begin[7]);
0948 auto l4 = zip_lo(matrix_begin[8], matrix_begin[9]);
0949 auto l5 = zip_lo(matrix_begin[10], matrix_begin[11]);
0950 auto l6 = zip_lo(matrix_begin[12], matrix_begin[13]);
0951 auto l7 = zip_lo(matrix_begin[14], matrix_begin[15]);
0952
0953 auto h0 = zip_hi(matrix_begin[0], matrix_begin[1]);
0954 auto h1 = zip_hi(matrix_begin[2], matrix_begin[3]);
0955 auto h2 = zip_hi(matrix_begin[4], matrix_begin[5]);
0956 auto h3 = zip_hi(matrix_begin[6], matrix_begin[7]);
0957 auto h4 = zip_hi(matrix_begin[8], matrix_begin[9]);
0958 auto h5 = zip_hi(matrix_begin[10], matrix_begin[11]);
0959 auto h6 = zip_hi(matrix_begin[12], matrix_begin[13]);
0960 auto h7 = zip_hi(matrix_begin[14], matrix_begin[15]);
0961
0962 auto L0 = zip_lo(bit_cast<batch<int16_t, A>>(l0), bit_cast<batch<int16_t, A>>(l1));
0963 auto L1 = zip_lo(bit_cast<batch<int16_t, A>>(l2), bit_cast<batch<int16_t, A>>(l3));
0964 auto L2 = zip_lo(bit_cast<batch<int16_t, A>>(l4), bit_cast<batch<int16_t, A>>(l5));
0965 auto L3 = zip_lo(bit_cast<batch<int16_t, A>>(l6), bit_cast<batch<int16_t, A>>(l7));
0966
0967 auto m0 = zip_lo(bit_cast<batch<int32_t, A>>(L0), bit_cast<batch<int32_t, A>>(L1));
0968 auto m1 = zip_lo(bit_cast<batch<int32_t, A>>(L2), bit_cast<batch<int32_t, A>>(L3));
0969 auto m2 = zip_hi(bit_cast<batch<int32_t, A>>(L0), bit_cast<batch<int32_t, A>>(L1));
0970 auto m3 = zip_hi(bit_cast<batch<int32_t, A>>(L2), bit_cast<batch<int32_t, A>>(L3));
0971
0972 matrix_begin[0] = bit_cast<batch<int8_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(m0), bit_cast<batch<int64_t, A>>(m1)));
0973 matrix_begin[1] = bit_cast<batch<int8_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(m0), bit_cast<batch<int64_t, A>>(m1)));
0974 matrix_begin[2] = bit_cast<batch<int8_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(m2), bit_cast<batch<int64_t, A>>(m3)));
0975 matrix_begin[3] = bit_cast<batch<int8_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(m2), bit_cast<batch<int64_t, A>>(m3)));
0976
0977 auto L4 = zip_hi(bit_cast<batch<int16_t, A>>(l0), bit_cast<batch<int16_t, A>>(l1));
0978 auto L5 = zip_hi(bit_cast<batch<int16_t, A>>(l2), bit_cast<batch<int16_t, A>>(l3));
0979 auto L6 = zip_hi(bit_cast<batch<int16_t, A>>(l4), bit_cast<batch<int16_t, A>>(l5));
0980 auto L7 = zip_hi(bit_cast<batch<int16_t, A>>(l6), bit_cast<batch<int16_t, A>>(l7));
0981
0982 auto m4 = zip_lo(bit_cast<batch<int32_t, A>>(L4), bit_cast<batch<int32_t, A>>(L5));
0983 auto m5 = zip_lo(bit_cast<batch<int32_t, A>>(L6), bit_cast<batch<int32_t, A>>(L7));
0984 auto m6 = zip_hi(bit_cast<batch<int32_t, A>>(L4), bit_cast<batch<int32_t, A>>(L5));
0985 auto m7 = zip_hi(bit_cast<batch<int32_t, A>>(L6), bit_cast<batch<int32_t, A>>(L7));
0986
0987 matrix_begin[4] = bit_cast<batch<int8_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(m4), bit_cast<batch<int64_t, A>>(m5)));
0988 matrix_begin[5] = bit_cast<batch<int8_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(m4), bit_cast<batch<int64_t, A>>(m5)));
0989 matrix_begin[6] = bit_cast<batch<int8_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(m6), bit_cast<batch<int64_t, A>>(m7)));
0990 matrix_begin[7] = bit_cast<batch<int8_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(m6), bit_cast<batch<int64_t, A>>(m7)));
0991
0992 auto H0 = zip_lo(bit_cast<batch<int16_t, A>>(h0), bit_cast<batch<int16_t, A>>(h1));
0993 auto H1 = zip_lo(bit_cast<batch<int16_t, A>>(h2), bit_cast<batch<int16_t, A>>(h3));
0994 auto H2 = zip_lo(bit_cast<batch<int16_t, A>>(h4), bit_cast<batch<int16_t, A>>(h5));
0995 auto H3 = zip_lo(bit_cast<batch<int16_t, A>>(h6), bit_cast<batch<int16_t, A>>(h7));
0996
0997 auto M0 = zip_lo(bit_cast<batch<int32_t, A>>(H0), bit_cast<batch<int32_t, A>>(H1));
0998 auto M1 = zip_lo(bit_cast<batch<int32_t, A>>(H2), bit_cast<batch<int32_t, A>>(H3));
0999 auto M2 = zip_hi(bit_cast<batch<int32_t, A>>(H0), bit_cast<batch<int32_t, A>>(H1));
1000 auto M3 = zip_hi(bit_cast<batch<int32_t, A>>(H2), bit_cast<batch<int32_t, A>>(H3));
1001
1002 matrix_begin[8] = bit_cast<batch<int8_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(M0), bit_cast<batch<int64_t, A>>(M1)));
1003 matrix_begin[9] = bit_cast<batch<int8_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(M0), bit_cast<batch<int64_t, A>>(M1)));
1004 matrix_begin[10] = bit_cast<batch<int8_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(M2), bit_cast<batch<int64_t, A>>(M3)));
1005 matrix_begin[11] = bit_cast<batch<int8_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(M2), bit_cast<batch<int64_t, A>>(M3)));
1006
1007 auto H4 = zip_hi(bit_cast<batch<int16_t, A>>(h0), bit_cast<batch<int16_t, A>>(h1));
1008 auto H5 = zip_hi(bit_cast<batch<int16_t, A>>(h2), bit_cast<batch<int16_t, A>>(h3));
1009 auto H6 = zip_hi(bit_cast<batch<int16_t, A>>(h4), bit_cast<batch<int16_t, A>>(h5));
1010 auto H7 = zip_hi(bit_cast<batch<int16_t, A>>(h6), bit_cast<batch<int16_t, A>>(h7));
1011
1012 auto M4 = zip_lo(bit_cast<batch<int32_t, A>>(H4), bit_cast<batch<int32_t, A>>(H5));
1013 auto M5 = zip_lo(bit_cast<batch<int32_t, A>>(H6), bit_cast<batch<int32_t, A>>(H7));
1014 auto M6 = zip_hi(bit_cast<batch<int32_t, A>>(H4), bit_cast<batch<int32_t, A>>(H5));
1015 auto M7 = zip_hi(bit_cast<batch<int32_t, A>>(H6), bit_cast<batch<int32_t, A>>(H7));
1016
1017 matrix_begin[12] = bit_cast<batch<int8_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(M4), bit_cast<batch<int64_t, A>>(M5)));
1018 matrix_begin[13] = bit_cast<batch<int8_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(M4), bit_cast<batch<int64_t, A>>(M5)));
1019 matrix_begin[14] = bit_cast<batch<int8_t, A>>(zip_lo(bit_cast<batch<int64_t, A>>(M6), bit_cast<batch<int64_t, A>>(M7)));
1020 matrix_begin[15] = bit_cast<batch<int8_t, A>>(zip_hi(bit_cast<batch<int64_t, A>>(M6), bit_cast<batch<int64_t, A>>(M7)));
1021 }
1022
1023 template <class A>
1024 XSIMD_INLINE void transpose(batch<uint8_t, A>* matrix_begin, batch<uint8_t, A>* matrix_end, requires_arch<common>) noexcept
1025 {
1026 transpose(reinterpret_cast<batch<int8_t, A>*>(matrix_begin), reinterpret_cast<batch<int8_t, A>*>(matrix_end), A {});
1027 }
1028
1029 }
1030
1031 }
1032
1033 #endif