File indexing completed on 2025-08-28 09:11:40
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #ifndef XSIMD_UTILS_HPP
0013 #define XSIMD_UTILS_HPP
0014
0015 #include <complex>
0016 #include <cstdint>
0017 #include <cstring>
0018 #include <tuple>
0019 #include <type_traits>
0020
0021 #ifdef XSIMD_ENABLE_XTL_COMPLEX
0022 #include "xtl/xcomplex.hpp"
0023 #endif
0024
0025 namespace xsimd
0026 {
0027
0028 template <class T, class A>
0029 class batch;
0030
0031 template <class T, class A>
0032 class batch_bool;
0033
0034
0035
0036
0037
0038 template <size_t I>
0039 using index = std::integral_constant<size_t, I>;
0040
0041
0042
0043
0044
0045 template <class T>
0046 struct as_integer : std::make_signed<T>
0047 {
0048 };
0049
0050 template <>
0051 struct as_integer<float>
0052 {
0053 using type = int32_t;
0054 };
0055
0056 template <>
0057 struct as_integer<double>
0058 {
0059 using type = int64_t;
0060 };
0061
0062 template <class T, class A>
0063 struct as_integer<batch<T, A>>
0064 {
0065 using type = batch<typename as_integer<T>::type, A>;
0066 };
0067
0068 template <class B>
0069 using as_integer_t = typename as_integer<B>::type;
0070
0071
0072
0073
0074
0075 template <class T>
0076 struct as_unsigned_integer : std::make_unsigned<T>
0077 {
0078 };
0079
0080 template <>
0081 struct as_unsigned_integer<float>
0082 {
0083 using type = uint32_t;
0084 };
0085
0086 template <>
0087 struct as_unsigned_integer<double>
0088 {
0089 using type = uint64_t;
0090 };
0091
0092 template <class T, class A>
0093 struct as_unsigned_integer<batch<T, A>>
0094 {
0095 using type = batch<typename as_unsigned_integer<T>::type, A>;
0096 };
0097
0098 template <class T>
0099 using as_unsigned_integer_t = typename as_unsigned_integer<T>::type;
0100
0101
0102
0103
0104
0105 template <class T>
0106 struct as_signed_integer : std::make_signed<T>
0107 {
0108 };
0109
0110 template <class T>
0111 using as_signed_integer_t = typename as_signed_integer<T>::type;
0112
0113
0114
0115
0116
0117 namespace detail
0118 {
0119 template <class T, bool is_signed>
0120 struct flipped_sign_type_impl : std::make_signed<T>
0121 {
0122 };
0123
0124 template <class T>
0125 struct flipped_sign_type_impl<T, true> : std::make_unsigned<T>
0126 {
0127 };
0128 }
0129
0130 template <class T>
0131 struct flipped_sign_type
0132 : detail::flipped_sign_type_impl<T, std::is_signed<T>::value>
0133 {
0134 };
0135
0136 template <class T>
0137 using flipped_sign_type_t = typename flipped_sign_type<T>::type;
0138
0139
0140
0141
0142
0143 template <class T>
0144 struct as_float;
0145
0146 template <>
0147 struct as_float<int32_t>
0148 {
0149 using type = float;
0150 };
0151
0152 template <>
0153 struct as_float<int64_t>
0154 {
0155 using type = double;
0156 };
0157
0158 template <class T, class A>
0159 struct as_float<batch<T, A>>
0160 {
0161 using type = batch<typename as_float<T>::type, A>;
0162 };
0163
0164 template <class T>
0165 using as_float_t = typename as_float<T>::type;
0166
0167
0168
0169
0170
0171 template <class T>
0172 struct as_logical;
0173
0174 template <class T, class A>
0175 struct as_logical<batch<T, A>>
0176 {
0177 using type = batch_bool<T, A>;
0178 };
0179
0180 template <class T>
0181 using as_logical_t = typename as_logical<T>::type;
0182
0183
0184
0185
0186
0187 template <class To, class From>
0188 inline To bit_cast(From val) noexcept
0189 {
0190 static_assert(sizeof(From) == sizeof(To), "casting between compatible layout");
0191
0192
0193
0194 To res;
0195 std::memcpy(&res, &val, sizeof(val));
0196 return res;
0197 }
0198
0199 namespace kernel
0200 {
0201 namespace detail
0202 {
0203
0204
0205
0206
0207 template <class T>
0208 using enable_integral_t = typename std::enable_if<std::is_integral<T>::value, int>::type;
0209
0210 template <class T, size_t S>
0211 using enable_sized_signed_t = typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value && sizeof(T) == S, int>::type;
0212
0213 template <class T, size_t S>
0214 using enable_sized_unsigned_t = typename std::enable_if<std::is_integral<T>::value && !std::is_signed<T>::value && sizeof(T) == S, int>::type;
0215
0216 template <class T, size_t S>
0217 using enable_sized_integral_t = typename std::enable_if<std::is_integral<T>::value && sizeof(T) == S, int>::type;
0218
0219 template <class T, size_t S>
0220 using enable_sized_t = typename std::enable_if<sizeof(T) == S, int>::type;
0221
0222 template <class T, size_t S>
0223 using enable_max_sized_integral_t = typename std::enable_if<std::is_integral<T>::value && sizeof(T) <= S, int>::type;
0224
0225
0226
0227
0228
0229 template <class T, class U, class B = int>
0230 using sizes_match_t = typename std::enable_if<sizeof(T) == sizeof(U), B>::type;
0231
0232 template <class T, class U, class B = int>
0233 using sizes_mismatch_t = typename std::enable_if<sizeof(T) != sizeof(U), B>::type;
0234
0235 template <class T, class U, class B = int>
0236 using stride_match_t = typename std::enable_if<!std::is_same<T, U>::value && sizeof(T) == sizeof(U), B>::type;
0237 }
0238 }
0239
0240
0241
0242
0243
0244
0245 namespace detail
0246 {
0247 template <typename T>
0248 struct identity
0249 {
0250 using type = T;
0251 };
0252
0253 #ifdef __cpp_lib_integer_sequence
0254 using std::index_sequence;
0255 using std::integer_sequence;
0256 using std::make_index_sequence;
0257 using std::make_integer_sequence;
0258
0259 using std::index_sequence_for;
0260 #else
0261 template <typename T, T... Is>
0262 struct integer_sequence
0263 {
0264 using value_type = T;
0265 static constexpr std::size_t size() noexcept { return sizeof...(Is); }
0266 };
0267
0268 template <typename Lhs, typename Rhs>
0269 struct make_integer_sequence_concat;
0270
0271 template <typename T, T... Lhs, T... Rhs>
0272 struct make_integer_sequence_concat<integer_sequence<T, Lhs...>,
0273 integer_sequence<T, Rhs...>>
0274 : identity<integer_sequence<T, Lhs..., (sizeof...(Lhs) + Rhs)...>>
0275 {
0276 };
0277
0278 template <typename T>
0279 struct make_integer_sequence_impl;
0280
0281 template <typename T>
0282 struct make_integer_sequence_impl<std::integral_constant<T, (T)0>> : identity<integer_sequence<T>>
0283 {
0284 };
0285
0286 template <typename T>
0287 struct make_integer_sequence_impl<std::integral_constant<T, (T)1>> : identity<integer_sequence<T, 0>>
0288 {
0289 };
0290
0291 template <typename T, T N>
0292 struct make_integer_sequence_impl<std::integral_constant<T, N>>
0293 : make_integer_sequence_concat<typename make_integer_sequence_impl<std::integral_constant<T, N / 2>>::type,
0294 typename make_integer_sequence_impl<std::integral_constant<T, N - (N / 2)>>::type>
0295 {
0296 };
0297
0298 template <typename T, T N>
0299 using make_integer_sequence = typename make_integer_sequence_impl<std::integral_constant<T, N>>::type;
0300
0301 template <std::size_t... Is>
0302 using index_sequence = integer_sequence<std::size_t, Is...>;
0303
0304 template <std::size_t N>
0305 using make_index_sequence = make_integer_sequence<std::size_t, N>;
0306
0307 template <typename... Ts>
0308 using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
0309
0310 #endif
0311
0312 template <int... Is>
0313 using int_sequence = integer_sequence<int, Is...>;
0314
0315 template <int N>
0316 using make_int_sequence = make_integer_sequence<int, N>;
0317
0318 template <typename... Ts>
0319 using int_sequence_for = make_int_sequence<(int)sizeof...(Ts)>;
0320
0321
0322 template <class P, size_t... Is>
0323 inline P indexes_from(index_sequence<Is...>) noexcept
0324 {
0325 return { static_cast<typename P::value_type>(Is)... };
0326 }
0327
0328 template <class P>
0329 inline P make_sequence_as_batch() noexcept
0330 {
0331 return indexes_from<P>(make_index_sequence<P::size>());
0332 }
0333 }
0334
0335
0336
0337
0338
0339 namespace detail
0340 {
0341 template <class T, class... Types, size_t I, size_t... Is>
0342 inline const T& get_impl(const std::tuple<Types...>& t, std::is_same<T, T>, index_sequence<I, Is...>) noexcept
0343 {
0344 return std::get<I>(t);
0345 }
0346
0347 template <class T, class U, class... Types, size_t I, size_t... Is>
0348 inline const T& get_impl(const std::tuple<Types...>& t, std::is_same<T, U>, index_sequence<I, Is...>) noexcept
0349 {
0350 using tuple_elem = typename std::tuple_element<I + 1, std::tuple<Types...>>::type;
0351 return get_impl<T>(t, std::is_same<T, tuple_elem>(), index_sequence<Is...>());
0352 }
0353
0354 template <class T, class... Types>
0355 inline const T& get(const std::tuple<Types...>& t) noexcept
0356 {
0357 using tuple_elem = typename std::tuple_element<0, std::tuple<Types...>>::type;
0358 return get_impl<T>(t, std::is_same<T, tuple_elem>(), make_index_sequence<sizeof...(Types)>());
0359 }
0360 }
0361
0362
0363
0364
0365
0366 namespace detail
0367 {
0368 template <class... T>
0369 struct make_void
0370 {
0371 using type = void;
0372 };
0373
0374 template <class... T>
0375 using void_t = typename make_void<T...>::type;
0376 }
0377
0378
0379
0380
0381
0382 namespace detail
0383 {
0384 template <std::size_t>
0385 struct check_size
0386 {
0387 using type = void;
0388 };
0389
0390 template <std::size_t S>
0391 using check_size_t = typename check_size<S>::type;
0392 }
0393
0394
0395
0396
0397
0398 namespace detail
0399 {
0400
0401 template <typename T, std::size_t... Is>
0402 inline constexpr std::array<T, sizeof...(Is)>
0403 array_from_scalar_impl(const T& scalar, index_sequence<Is...>) noexcept
0404 {
0405
0406
0407 return std::array<T, sizeof...(Is)> { (Is + 1) ? scalar : T()... };
0408 }
0409
0410 template <typename T, std::size_t N>
0411 inline constexpr std::array<T, N>
0412 array_from_scalar(const T& scalar) noexcept
0413 {
0414 return array_from_scalar_impl(scalar, make_index_sequence<N>());
0415 }
0416
0417
0418 template <typename T, std::size_t... Is>
0419 inline constexpr std::array<T, sizeof...(Is)>
0420 array_from_pointer_impl(const T* c_array, index_sequence<Is...>) noexcept
0421 {
0422 return std::array<T, sizeof...(Is)> { c_array[Is]... };
0423 }
0424
0425 template <typename T, std::size_t N>
0426 inline constexpr std::array<T, N>
0427 array_from_pointer(const T* c_array) noexcept
0428 {
0429 return array_from_pointer_impl(c_array, make_index_sequence<N>());
0430 }
0431 }
0432
0433
0434
0435
0436
0437 namespace detail
0438 {
0439 template <bool...>
0440 struct bool_pack;
0441
0442 template <bool... bs>
0443 using all_true = std::is_same<
0444 bool_pack<bs..., true>, bool_pack<true, bs...>>;
0445
0446 template <typename T, typename... Args>
0447 using is_all_convertible = all_true<std::is_convertible<Args, T>::value...>;
0448
0449 template <typename T, std::size_t N, typename... Args>
0450 using is_array_initializer = std::enable_if<
0451 (sizeof...(Args) == N) && is_all_convertible<T, Args...>::value>;
0452
0453
0454
0455 template <typename T, std::size_t N, typename... Args>
0456 using is_array_initializer_t = typename is_array_initializer<T, N, Args...>::type;
0457 }
0458
0459
0460
0461
0462
0463
0464
0465
0466
0467
0468
0469 namespace detail
0470 {
0471 template <class T>
0472 struct is_complex : std::false_type
0473 {
0474 };
0475
0476 template <class T>
0477 struct is_complex<std::complex<T>> : std::true_type
0478 {
0479 };
0480
0481 #ifdef XSIMD_ENABLE_XTL_COMPLEX
0482 template <class T, bool i3ec>
0483 struct is_complex<xtl::xcomplex<T, T, i3ec>> : std::true_type
0484 {
0485 };
0486 #endif
0487 }
0488
0489
0490
0491
0492
0493 template <class B>
0494 struct real_batch_type
0495 {
0496 using type = B;
0497 };
0498
0499 template <class T, class A>
0500 struct real_batch_type<batch<std::complex<T>, A>>
0501 {
0502 using type = batch<T, A>;
0503 };
0504
0505 template <class B>
0506 using real_batch_type_t = typename real_batch_type<B>::type;
0507
0508
0509
0510
0511
0512 template <class B>
0513 struct complex_batch_type
0514 {
0515 using real_value_type = typename B::value_type;
0516 using arch_type = typename B::arch_type;
0517 using type = batch<std::complex<real_value_type>, arch_type>;
0518 };
0519
0520 template <class T, class A>
0521 struct complex_batch_type<batch<std::complex<T>, A>>
0522 {
0523 using type = batch<std::complex<T>, A>;
0524 };
0525
0526 template <class B>
0527 using complex_batch_type_t = typename complex_batch_type<B>::type;
0528 }
0529
0530 #endif