usearch 2.15.2 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +1 -1
  2. package/binding.gyp +1 -1
  3. package/c/CMakeLists.txt +10 -3
  4. package/cpp/README.md +1 -1
  5. package/cpp/bench.cpp +2 -2
  6. package/cpp/test.cpp +16 -0
  7. package/include/usearch/index.hpp +44 -17
  8. package/include/usearch/index_dense.hpp +51 -89
  9. package/include/usearch/index_plugins.hpp +468 -43
  10. package/javascript/lib.cpp +17 -5
  11. package/javascript/usearch.test.js +205 -34
  12. package/package.json +1 -1
  13. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  14. package/prebuilds/linux-arm64/usearch.node +0 -0
  15. package/prebuilds/linux-x64/usearch.node +0 -0
  16. package/simsimd/.git-blame-ignore-revs +2 -0
  17. package/simsimd/CODE_OF_CONDUCT.md +128 -0
  18. package/simsimd/CONTRIBUTING.md +84 -26
  19. package/simsimd/Cargo.lock +1 -1
  20. package/simsimd/Cargo.toml +4 -4
  21. package/simsimd/MANIFEST.in +3 -0
  22. package/simsimd/Package.swift +15 -3
  23. package/simsimd/README.md +534 -177
  24. package/simsimd/binding.gyp +6 -2
  25. package/simsimd/build.rs +13 -1
  26. package/simsimd/c/lib.c +266 -53
  27. package/simsimd/include/simsimd/binary.h +321 -112
  28. package/simsimd/include/simsimd/curved.h +803 -0
  29. package/simsimd/include/simsimd/dot.h +694 -521
  30. package/simsimd/include/simsimd/elementwise.h +1437 -0
  31. package/simsimd/include/simsimd/geospatial.h +9 -3
  32. package/simsimd/include/simsimd/mesh.h +69 -0
  33. package/simsimd/include/simsimd/probability.h +234 -206
  34. package/simsimd/include/simsimd/simsimd.h +1476 -617
  35. package/simsimd/include/simsimd/sparse.h +1428 -0
  36. package/simsimd/include/simsimd/spatial.h +1556 -742
  37. package/simsimd/include/simsimd/types.h +276 -132
  38. package/simsimd/javascript/lib.c +1 -2
  39. package/simsimd/javascript/simsimd.ts +4 -4
  40. package/simsimd/package.json +4 -4
  41. package/simsimd/rust/lib.rs +179 -1
  42. package/simsimd/scripts/bench.cxx +1070 -0
  43. package/simsimd/scripts/bench.ipynb +187 -0
  44. package/simsimd/{javascript → scripts}/bench.js +1 -1
  45. package/simsimd/{rust/benches/sqeuclidean.rs → scripts/bench_sqeuclidean.rs} +6 -6
  46. package/simsimd/scripts/bench_vectors.py +700 -0
  47. package/simsimd/scripts/bench_vectors_live.py +213 -0
  48. package/simsimd/scripts/test.c +155 -0
  49. package/simsimd/{javascript → scripts}/test.mjs +2 -2
  50. package/simsimd/scripts/test.py +1410 -0
  51. package/simsimd/swift/{source/SimSIMD.swift → SimSIMD.swift} +6 -2
  52. package/simsimd/swift/{tests/SimSIMDTests.swift → Test.swift} +1 -1
  53. /package/simsimd/{rust/benches/cosine.rs → scripts/bench_cosine.rs} +0 -0
  54. /package/simsimd/{rust/benches/native.rs → scripts/bench_native.rs} +0 -0
@@ -23,6 +23,7 @@
23
23
  * F, CD, VL, DQ, BW, VNNI, VPOPCNTDQ, IFMA, VBMI, VAES, GFNI, VBMI2, BITALG, VPCLMULQDQ, BF16
24
24
  * > In other words, it extends Sunny Cove with BF16 support
25
25
  * - Intel Golden Cove (Sapphire Rapids): extends Zen4 and Sunny Cove with FP16 support
26
+ * - AMD Zen5 (Turin): makes VP2INTERSECT cool again
26
27
  *
27
28
  * Intel Palm Cove was an irrelevant intermediate release extending Skylake with IFMA and VBMI.
28
29
  * Intel Willow Cove was an irrelevant intermediate release extending Sunny Cove with VP2INTERSECT,
@@ -35,6 +36,7 @@
35
36
  * 2. Intel Ice Lake (2019-2021): advanced integer algorithms.
36
37
  * 3. AMD Genoa (2023+): brain-floating point support.
37
38
  * 4. Intel Sapphire Rapids (2023+): advanced mixed-precision float processing.
39
+ * 5. AMD Turin (2024+): advanced sparse algorithms.
38
40
  *
39
41
  * To list all available macros for x86, take a recent compiler, like GCC 12 and run:
40
42
  * gcc-12 -march=sapphirerapids -dM -E - < /dev/null | egrep "SSE|AVX" | sort
@@ -44,7 +46,7 @@
44
46
  * @section Choosing Arm Target Generations
45
47
  *
46
48
  * Arm CPUs share design IP, but are produced by different vendors, potentially making the platform
47
- * even more fragmented than x86. There are 2 imporatant families of SIMD extensions - NEON and SVE.
49
+ * even more fragmented than x86. There are 2 important families of SIMD extensions - NEON and SVE.
48
50
  *
49
51
  * - Armv8-A: +fp, +simd
50
52
  * - Armv8.1-A: armv8-a, +crc, +lse, +rdma
@@ -68,7 +70,7 @@
68
70
  * Here are the most important recent families of CPU cores designed by Arm:
69
71
  *
70
72
  * - Neoverse N1: armv8.2-a, extended with Armv8.4 "dotprod" instructions.
71
- * Used in AWS @b Graviton2 and Amere @b Altra.
73
+ * Used in AWS @b Graviton2 and Ampere @b Altra.
72
74
  * https://developer.arm.com/Processors/Neoverse%20N1
73
75
  * - Neoverse V1: armv8.4-a, extended with Armv8.6 bfloat/int8 "matmul" instructions.
74
76
  * Used in AWS @b Graviton3, which also enables `sve`, `svebf16`, and `svei8mm`.
@@ -87,8 +89,8 @@
87
89
  #define SIMSIMD_H
88
90
 
89
91
  #define SIMSIMD_VERSION_MAJOR 5
90
- #define SIMSIMD_VERSION_MINOR 0
91
- #define SIMSIMD_VERSION_PATCH 0
92
+ #define SIMSIMD_VERSION_MINOR 9
93
+ #define SIMSIMD_VERSION_PATCH 6
92
94
 
93
95
  /**
94
96
  * @brief Removes compile-time dispatching, and replaces it with runtime dispatching.
@@ -101,13 +103,16 @@
101
103
  #endif
102
104
 
103
105
  #include "binary.h" // Hamming, Jaccard
106
+ #include "curved.h" // Mahalanobis, Bilinear Forms
104
107
  #include "dot.h" // Inner (dot) product, and its conjugate
108
+ #include "elementwise.h" // Weighted Sum, Fused-Multiply-Add
105
109
  #include "geospatial.h" // Haversine and Vincenty
106
110
  #include "probability.h" // Kullback-Leibler, Jensen–Shannon
111
+ #include "sparse.h" // Intersect
107
112
  #include "spatial.h" // L2, Cosine
108
113
 
109
114
  // On Apple Silicon, `mrs` is not allowed in user-space, so we need to use the `sysctl` API.
110
- #if defined(SIMSIMD_DEFINED_APPLE)
115
+ #if defined(_SIMSIMD_DEFINED_APPLE)
111
116
  #include <sys/sysctl.h>
112
117
  #endif
113
118
 
@@ -132,6 +137,8 @@ typedef enum {
132
137
  simsimd_metric_cosine_k = 'c', ///< Cosine similarity alias
133
138
  simsimd_metric_angular_k = 'c', ///< Cosine similarity alias
134
139
 
140
+ simsimd_metric_l2_k = '2', ///< Euclidean distance alias
141
+ simsimd_metric_euclidean_k = '2', ///< Euclidean distance alias
135
142
  simsimd_metric_l2sq_k = 'e', ///< Squared Euclidean distance
136
143
  simsimd_metric_sqeuclidean_k = 'e', ///< Squared Euclidean distance alias
137
144
 
@@ -142,6 +149,15 @@ typedef enum {
142
149
  simsimd_metric_jaccard_k = 'j', ///< Jaccard coefficient
143
150
  simsimd_metric_tanimoto_k = 'j', ///< Tanimoto coefficient is same as Jaccard
144
151
 
152
+ // Sets:
153
+ simsimd_metric_intersect_k = 'x', ///< Equivalent to unnormalized Jaccard
154
+ simsimd_metric_spdot_counts_k = 'y', ///< Sparse sets with integer weights
155
+ simsimd_metric_spdot_weights_k = 'z', ///< Sparse sets with brain floating-point weights
156
+
157
+ // Curved Spaces:
158
+ simsimd_metric_bilinear_k = 'b', ///< Bilinear form
159
+ simsimd_metric_mahalanobis_k = 'm', ///< Mahalanobis distance
160
+
145
161
  // Probability:
146
162
  simsimd_metric_kl_k = 'k', ///< Kullback-Leibler divergence
147
163
  simsimd_metric_kullback_leibler_k = 'k', ///< Kullback-Leibler divergence alias
@@ -149,6 +165,10 @@ typedef enum {
149
165
  simsimd_metric_js_k = 's', ///< Jensen-Shannon divergence
150
166
  simsimd_metric_jensen_shannon_k = 's', ///< Jensen-Shannon divergence alias
151
167
 
168
+ // BLAS-like operations:
169
+ simsimd_metric_fma_k = 'f', ///< Fused Multiply-Add
170
+ simsimd_metric_wsum_k = 'w', ///< Weighted Sum
171
+
152
172
  } simsimd_metric_kind_t;
153
173
 
154
174
  /**
@@ -163,15 +183,19 @@ typedef enum {
163
183
  simsimd_cap_ice_k = 1 << 12, ///< x86 AVX512 capability with advanced integer algos
164
184
  simsimd_cap_genoa_k = 1 << 13, ///< x86 AVX512 capability with `bf16` support
165
185
  simsimd_cap_sapphire_k = 1 << 14, ///< x86 AVX512 capability with `f16` support
166
-
167
- simsimd_cap_neon_k = 1 << 20, ///< ARM NEON capability
168
- simsimd_cap_neon_f16_k = 1 << 21, ///< ARM NEON FP16 capability
169
- simsimd_cap_neon_bf16_k = 1 << 22, ///< ARM NEON BF16 capability
170
- simsimd_cap_neon_i8_k = 1 << 23, ///< ARM NEON Int8 capability
171
- simsimd_cap_sve_k = 1 << 24, ///< ARM SVE capability
172
- simsimd_cap_sve_f16_k = 1 << 25, ///< ARM SVE FP16 capability
173
- simsimd_cap_sve_bf16_k = 1 << 26, ///< ARM SVE BF16 capability
174
- simsimd_cap_sve_i8_k = 1 << 27, ///< ARM SVE Int8 capability
186
+ simsimd_cap_turin_k = 1 << 15, ///< x86 AVX512 capability with conflict detection
187
+ simsimd_cap_sierra_k = 1 << 16, ///< x86 AVX2+VNNI capability with `i8` dot-products
188
+
189
+ simsimd_cap_neon_k = 1 << 20, ///< ARM NEON baseline capability
190
+ simsimd_cap_neon_f16_k = 1 << 21, ///< ARM NEON `f16` capability
191
+ simsimd_cap_neon_bf16_k = 1 << 22, ///< ARM NEON `bf16` capability
192
+ simsimd_cap_neon_i8_k = 1 << 23, ///< ARM NEON `i8` capability
193
+ simsimd_cap_sve_k = 1 << 24, ///< ARM SVE baseline capability
194
+ simsimd_cap_sve_f16_k = 1 << 25, ///< ARM SVE `f16` capability
195
+ simsimd_cap_sve_bf16_k = 1 << 26, ///< ARM SVE `bf16` capability
196
+ simsimd_cap_sve_i8_k = 1 << 27, ///< ARM SVE `i8` capability
197
+ simsimd_cap_sve2_k = 1 << 28, ///< ARM SVE2 capability
198
+ simsimd_cap_sve2p1_k = 1 << 29, ///< ARM SVE2p1 capability
175
199
 
176
200
  } simsimd_capability_t;
177
201
 
@@ -183,10 +207,20 @@ typedef enum {
183
207
  * interfaces.
184
208
  */
185
209
  typedef enum {
186
- simsimd_datatype_unknown_k = 0, ///< Unknown data type
210
+ simsimd_datatype_unknown_k = 0, ///< Unknown data type
211
+ simsimd_datatype_b8_k = 1 << 1, ///< Single-bit values packed into 8-bit words
212
+ simsimd_datatype_b1x8_k = simsimd_datatype_b8_k, ///< Single-bit values packed into 8-bit words
213
+ simsimd_datatype_i4x2_k = 1 << 19, ///< 4-bit signed integers packed into 8-bit words
187
214
 
188
- simsimd_datatype_b8_k = 1 << 1, ///< Single-bit values packed into 8-bit words
189
- simsimd_datatype_i8_k = 1 << 2, ///< 8-bit integer
215
+ simsimd_datatype_i8_k = 1 << 2, ///< 8-bit signed integer
216
+ simsimd_datatype_i16_k = 1 << 3, ///< 16-bit signed integer
217
+ simsimd_datatype_i32_k = 1 << 4, ///< 32-bit signed integer
218
+ simsimd_datatype_i64_k = 1 << 5, ///< 64-bit signed integer
219
+
220
+ simsimd_datatype_u8_k = 1 << 6, ///< 8-bit unsigned integer
221
+ simsimd_datatype_u16_k = 1 << 7, ///< 16-bit unsigned integer
222
+ simsimd_datatype_u32_k = 1 << 8, ///< 32-bit unsigned integer
223
+ simsimd_datatype_u64_k = 1 << 9, ///< 64-bit unsigned integer
190
224
 
191
225
  simsimd_datatype_f64_k = 1 << 10, ///< Double precision floating point
192
226
  simsimd_datatype_f32_k = 1 << 11, ///< Single precision floating point
@@ -200,30 +234,108 @@ typedef enum {
200
234
  } simsimd_datatype_t;
201
235
 
202
236
  /**
203
- * @brief Type-punned function pointer accepting two vectors and outputting their similarity/distance.
237
+ * @brief Type-punned function pointer for dense vector representations and simplest similarity measures.
204
238
  *
205
239
  * @param[in] a Pointer to the first data array.
206
240
  * @param[in] b Pointer to the second data array.
207
241
  * @param[in] n Number of scalar words in the input arrays.
242
+ * When dealing with sub-byte types, the number of scalar words is the number of bytes.
243
+ * When dealing with complex types, the number of scalar words is the sum of real and imaginary parts.
208
244
  * @param[out] d Output value as a double-precision float.
209
- * In complex dot-products @b two double-precision scalars are exported
210
- * for the real and imaginary parts.
245
+ * In complex dot-products @b two scalars are exported for the real and imaginary parts.
246
+ */
247
+ typedef void (*simsimd_metric_dense_punned_t)(void const *a, void const *b, simsimd_size_t n, simsimd_distance_t *d);
248
+
249
+ /**
250
+ * @brief Type-punned function pointer for sparse vector representations and similarity measures.
251
+ *
252
+ * @param[in] a Pointer to the first data array, generally a sorted array of integers.
253
+ * @param[in] b Pointer to the second data array, generally a sorted array of integers.
254
+ * @param[in] a_length Number of scalar words in the first input array.
255
+ * @param[in] b_length Number of scalar words in the second input array.
256
+ * @param[out] d Output value as a double-precision float, generally without decimals.
257
+ */
258
+ typedef void (*simsimd_metric_sparse_punned_t)(void const *a, void const *b, //
259
+ simsimd_size_t a_length, simsimd_size_t b_length, //
260
+ simsimd_distance_t *d);
261
+
262
+ /**
263
+ * @brief Type-punned function pointer for curved vector spaces and similarity measures.
264
+ *
265
+ * @param[in] a Pointer to the first data array.
266
+ * @param[in] b Pointer to the second data array.
267
+ * @param[in] c Pointer to the metric tensor array or some covariance matrix.
268
+ * @param[in] n Number of scalar words in the input arrays.
269
+ * @param[out] d Output value as a double-precision float.
270
+ */
271
+ typedef void (*simsimd_metric_curved_punned_t)(void const *a, void const *b, void const *c, //
272
+ simsimd_size_t n, simsimd_distance_t *d);
273
+
274
+ /**
275
+ * @brief Type-punned function pointer for FMA operations on dense vector representations.
276
+ * Implements the `y = alpha * a * b + beta * c` operation.
277
+ *
278
+ * @param[in] a Pointer to the first data array.
279
+ * @param[in] b Pointer to the second data array.
280
+ * @param[in] c Pointer to the third data array.
281
+ * @param[in] n Number of scalar words in the input arrays.
282
+ * @param[in] alpha Scaling factor for the first two arrays.
283
+ * @param[in] beta Scaling factor for the third array.
284
+ * @param[out] y Output value in the same precision as the input arrays.
285
+ */
286
+ typedef void (*simsimd_kernel_fma_punned_t)(void const *a, void const *b, void const *c, //
287
+ simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta,
288
+ void *y);
289
+
290
+ /**
291
+ * @brief Type-punned function pointer for Weighted Sum operations on dense vector representations.
292
+ * Implements the `y = alpha * a + beta * b` operation.
293
+ *
294
+ * @param[in] a Pointer to the first data array.
295
+ * @param[in] b Pointer to the second data array.
296
+ * @param[in] n Number of scalar words in the input arrays.
297
+ * @param[in] alpha Scaling factor for the first array.
298
+ * @param[in] beta Scaling factor for the second array.
299
+ * @param[out] y Output value in the same precision as the input arrays.
211
300
  */
212
- typedef void (*simsimd_metric_punned_t)(void const* a, void const* b, simsimd_size_t n, simsimd_distance_t* d);
301
+ typedef void (*simsimd_kernel_wsum_punned_t)(void const *a, void const *b, //
302
+ simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta,
303
+ void *y);
304
+
305
+ /**
306
+ * @brief Type-punned function pointer for a SimSIMD public interface.
307
+ * Can be a `simsimd_metric_dense_punned_t`, `simsimd_metric_sparse_punned_t`,
308
+ * or `simsimd_metric_curved_punned_t`.
309
+ */
310
+ typedef simsimd_metric_dense_punned_t simsimd_metric_punned_t;
213
311
 
214
312
  #if SIMSIMD_DYNAMIC_DISPATCH
215
313
  SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
314
+ SIMSIMD_DYNAMIC void simsimd_find_metric_punned( //
315
+ simsimd_metric_kind_t kind, //
316
+ simsimd_datatype_t datatype, //
317
+ simsimd_capability_t supported, //
318
+ simsimd_capability_t allowed, //
319
+ simsimd_metric_punned_t *metric_output, //
320
+ simsimd_capability_t *capability_output);
216
321
  #else
217
322
  SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void);
323
+ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
324
+ simsimd_metric_kind_t kind, //
325
+ simsimd_datatype_t datatype, //
326
+ simsimd_capability_t supported, //
327
+ simsimd_capability_t allowed, //
328
+ simsimd_metric_punned_t *metric_output, //
329
+ simsimd_capability_t *capability_output);
218
330
  #endif
219
331
 
220
- #if SIMSIMD_TARGET_X86
332
+ #if _SIMSIMD_TARGET_X86
221
333
 
222
334
  /**
223
335
  * @brief Function to determine the SIMD capabilities of the current 64-bit x86 machine at @b runtime.
224
336
  * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
225
337
  */
226
- SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_x86(void) {
338
+ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_x86(void) {
227
339
 
228
340
  /// The states of 4 registers populated for a specific "cpuid" assembly call
229
341
  union four_registers_t {
@@ -276,6 +388,9 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_x86(void) {
276
388
  // Check for AVX512BF16 (Function ID 7, Sub-leaf 1, EAX register)
277
389
  // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L205
278
390
  unsigned supports_avx512bf16 = (info7sub1.named.eax & 0x00000020) != 0;
391
+ // Clang doesn't show the VP2INTERSECT flag, but we can get it from QEMU
392
+ // https://stackoverflow.com/a/68289220/2766161
393
+ unsigned supports_avx512vp2intersect = (info7.named.edx & 0x00000100) != 0;
279
394
 
280
395
  // Convert specific features into CPU generations
281
396
  unsigned supports_haswell = supports_avx2 && supports_f16c && supports_fma;
@@ -284,6 +399,9 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_x86(void) {
284
399
  supports_avx512vbmi2 && supports_avx512vpopcntdq;
285
400
  unsigned supports_genoa = supports_avx512bf16;
286
401
  unsigned supports_sapphire = supports_avx512fp16;
402
+ // We don't want to accidently enable AVX512VP2INTERSECT on Intel Tiger Lake CPUs
403
+ unsigned supports_turin = supports_avx512vp2intersect && supports_avx512bf16;
404
+ unsigned supports_sierra = 0;
287
405
 
288
406
  return (simsimd_capability_t)( //
289
407
  (simsimd_cap_haswell_k * supports_haswell) | //
@@ -291,16 +409,18 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_x86(void) {
291
409
  (simsimd_cap_ice_k * supports_ice) | //
292
410
  (simsimd_cap_genoa_k * supports_genoa) | //
293
411
  (simsimd_cap_sapphire_k * supports_sapphire) | //
412
+ (simsimd_cap_turin_k * supports_turin) | //
413
+ (simsimd_cap_sierra_k * supports_sierra) | //
294
414
  (simsimd_cap_serial_k));
295
415
  }
296
416
 
297
- #endif // SIMSIMD_TARGET_X86
417
+ #endif // _SIMSIMD_TARGET_X86
298
418
 
299
- #if SIMSIMD_TARGET_ARM
419
+ #if _SIMSIMD_TARGET_ARM
300
420
 
301
421
  /* Compiling the next section one may get: selected processor does not support system register name 'id_aa64zfr0_el1'.
302
422
  * Suppressing assembler errors is very complicated, so when dealing with older ARM CPUs it's simpler to compile this
303
- * function targetting newer ones.
423
+ * function targeting newer ones.
304
424
  */
305
425
  #pragma GCC push_options
306
426
  #pragma GCC target("arch=armv8.5-a+sve")
@@ -310,19 +430,15 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_x86(void) {
310
430
  * @brief Function to determine the SIMD capabilities of the current 64-bit Arm machine at @b runtime.
311
431
  * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
312
432
  */
313
- SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_arm(void) {
314
- #if defined(SIMSIMD_DEFINED_APPLE)
433
+ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_arm(void) {
434
+ #if defined(_SIMSIMD_DEFINED_APPLE)
315
435
  // On Apple Silicon, `mrs` is not allowed in user-space, so we need to use the `sysctl` API.
316
436
  uint32_t supports_neon = 0, supports_fp16 = 0, supports_bf16 = 0, supports_i8mm = 0;
317
437
  size_t size = sizeof(supports_neon);
318
- if (sysctlbyname("hw.optional.neon", &supports_neon, &size, NULL, 0) != 0)
319
- supports_neon = 0;
320
- if (sysctlbyname("hw.optional.arm.FEAT_FP16", &supports_fp16, &size, NULL, 0) != 0)
321
- supports_fp16 = 0;
322
- if (sysctlbyname("hw.optional.arm.FEAT_BF16", &supports_bf16, &size, NULL, 0) != 0)
323
- supports_bf16 = 0;
324
- if (sysctlbyname("hw.optional.arm.FEAT_I8MM", &supports_i8mm, &size, NULL, 0) != 0)
325
- supports_i8mm = 0;
438
+ if (sysctlbyname("hw.optional.neon", &supports_neon, &size, NULL, 0) != 0) supports_neon = 0;
439
+ if (sysctlbyname("hw.optional.arm.FEAT_FP16", &supports_fp16, &size, NULL, 0) != 0) supports_fp16 = 0;
440
+ if (sysctlbyname("hw.optional.arm.FEAT_BF16", &supports_bf16, &size, NULL, 0) != 0) supports_bf16 = 0;
441
+ if (sysctlbyname("hw.optional.arm.FEAT_I8MM", &supports_i8mm, &size, NULL, 0) != 0) supports_i8mm = 0;
326
442
 
327
443
  return (simsimd_capability_t)( //
328
444
  (simsimd_cap_neon_k * (supports_neon)) | //
@@ -331,23 +447,7 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_arm(void) {
331
447
  (simsimd_cap_neon_i8_k * (supports_neon && supports_i8mm)) | //
332
448
  (simsimd_cap_serial_k));
333
449
 
334
- #elif defined(SIMSIMD_DEFINED_LINUX)
335
- // This is how the `arm-cpusysregs` library does it:
336
- //
337
- // int ID_AA64ISAR1_EL1_BF16() const { return (int)(_aa64isar1 >> 44) & 0x0F; }
338
- // int ID_AA64ZFR0_EL1_BF16() const { return (int)(_aa64zfr0 >> 20) & 0x0F; }
339
- // int ID_AA64PFR0_EL1_FP() const { return (int)(_aa64pfr0 >> 16) & 0x0F; }
340
- // int ID_AA64ISAR0_EL1_DP() const { return (int)(_aa64isar0 >> 44) & 0x0F; }
341
- // int ID_AA64PFR0_EL1_SVE() const { return (int)(_aa64pfr0 >> 32) & 0x0F; }
342
- // int ID_AA64ZFR0_EL1_SVEver() const { return (int)(_aa64zfr0) & 0x0F; }
343
- // bool FEAT_BF16() const { return ID_AA64ISAR1_EL1_BF16() >= 1 || ID_AA64ZFR0_EL1_BF16() >= 1; }
344
- // bool FEAT_FP16() const { return ID_AA64PFR0_EL1_FP() >= 1 && ID_AA64PFR0_EL1_FP() < 15; }
345
- // bool FEAT_DotProd() const { return ID_AA64ISAR0_EL1_DP() >= 1; }
346
- // bool FEAT_SVE() const { return ID_AA64PFR0_EL1_SVE() >= 1; }
347
- // bool FEAT_SVE2() const { return ID_AA64ZFR0_EL1_SVEver() >= 1; }
348
- // bool FEAT_I8MM() const { return ID_AA64ZFR0_EL1_I8MM() >= 1; }
349
- //
350
- // https://github.com/lelegard/arm-cpusysregs/tree/4837c62e619a5e5f12bf41b16a1ee1e71d62c76d
450
+ #elif defined(_SIMSIMD_DEFINED_LINUX)
351
451
 
352
452
  // Read CPUID registers directly
353
453
  unsigned long id_aa64isar0_el1 = 0, id_aa64isar1_el1 = 0, id_aa64pfr0_el1 = 0, id_aa64zfr0_el1 = 0;
@@ -377,8 +477,7 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_arm(void) {
377
477
 
378
478
  // Now let's unpack the status flags from ID_AA64ZFR0_EL1
379
479
  // https://developer.arm.com/documentation/ddi0601/2024-03/AArch64-Registers/ID-AA64ZFR0-EL1--SVE-Feature-ID-Register-0?lang=en
380
- if (supports_sve)
381
- __asm__ __volatile__("mrs %0, ID_AA64ZFR0_EL1" : "=r"(id_aa64zfr0_el1));
480
+ if (supports_sve) __asm__ __volatile__("mrs %0, ID_AA64ZFR0_EL1" : "=r"(id_aa64zfr0_el1));
382
481
  // I8MM, bits [47:44] of ID_AA64ZFR0_EL1
383
482
  unsigned supports_sve_i8mm = ((id_aa64zfr0_el1 >> 44) & 0xF) >= 1;
384
483
  // BF16, bits [23:20] of ID_AA64ZFR0_EL1
@@ -388,8 +487,8 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_arm(void) {
388
487
  // - 0b0001: SVE2 is implemented
389
488
  // - 0b0010: SVE2.1 is implemented
390
489
  // This value must match the existing indicator obtained from ID_AA64PFR0_EL1:
391
- // unsigned supports_sve = ((id_aa64zfr0_el1) & 0xF) >= 1;
392
- // unsigned supports_sve2 = ((id_aa64zfr0_el1) & 0xF) >= 2;
490
+ unsigned supports_sve2 = ((id_aa64zfr0_el1) & 0xF) >= 1;
491
+ unsigned supports_sve2p1 = ((id_aa64zfr0_el1) & 0xF) >= 2;
393
492
  unsigned supports_neon = 1; // NEON is always supported
394
493
 
395
494
  return (simsimd_capability_t)( //
@@ -401,27 +500,30 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_arm(void) {
401
500
  (simsimd_cap_sve_f16_k * (supports_sve && supports_fp16)) | //
402
501
  (simsimd_cap_sve_bf16_k * (supports_sve && supports_sve_bf16)) | //
403
502
  (simsimd_cap_sve_i8_k * (supports_sve && supports_sve_i8mm)) | //
503
+ (simsimd_cap_sve2_k * (supports_sve2)) | //
504
+ (simsimd_cap_sve2p1_k * (supports_sve2p1)) | //
404
505
  (simsimd_cap_serial_k));
405
- #else // SIMSIMD_DEFINED_LINUX
506
+ #else // if !_SIMSIMD_DEFINED_LINUX
406
507
  return simsimd_cap_serial_k;
407
508
  #endif
408
509
  }
409
510
 
410
511
  #pragma clang attribute pop
411
512
  #pragma GCC pop_options
513
+
412
514
  #endif
413
515
 
414
516
  /**
415
517
  * @brief Function to determine the SIMD capabilities of the current 64-bit x86 machine at @b runtime.
416
518
  * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
417
519
  */
418
- SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_implementation(void) {
419
- #if SIMSIMD_TARGET_X86
420
- return simsimd_capabilities_x86();
421
- #endif // SIMSIMD_TARGET_X86
422
- #if SIMSIMD_TARGET_ARM
423
- return simsimd_capabilities_arm();
424
- #endif // SIMSIMD_TARGET_ARM
520
+ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_implementation(void) {
521
+ #if _SIMSIMD_TARGET_X86
522
+ return _simsimd_capabilities_x86();
523
+ #endif // _SIMSIMD_TARGET_X86
524
+ #if _SIMSIMD_TARGET_ARM
525
+ return _simsimd_capabilities_arm();
526
+ #endif // _SIMSIMD_TARGET_ARM
425
527
  return simsimd_cap_serial_k;
426
528
  }
427
529
 
@@ -430,452 +532,702 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_implementation(void) {
430
532
  #pragma clang diagnostic push
431
533
  #pragma clang diagnostic ignored "-Wcast-function-type"
432
534
 
433
- /**
434
- * @brief Determines the best suited metric implementation based on the given datatype,
435
- * supported and allowed by hardware capabilities.
436
- *
437
- * @param kind The kind of metric to be evaluated.
438
- * @param datatype The data type for which the metric needs to be evaluated.
439
- * @param supported The hardware capabilities supported by the CPU.
440
- * @param allowed The hardware capabilities allowed for use.
441
- * @param metric_output Output variable for the selected similarity function.
442
- * @param capability_output Output variable for the utilized hardware capabilities.
443
- */
444
- SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
445
- simsimd_metric_kind_t kind, //
446
- simsimd_datatype_t datatype, //
447
- simsimd_capability_t supported, //
448
- simsimd_capability_t allowed, //
449
- simsimd_metric_punned_t* metric_output, //
450
- simsimd_capability_t* capability_output) {
451
-
452
- simsimd_metric_punned_t* m = metric_output;
453
- simsimd_capability_t* c = capability_output;
454
- simsimd_capability_t viable = (simsimd_capability_t)(supported & allowed);
455
- *m = (simsimd_metric_punned_t)0;
456
- *c = (simsimd_capability_t)0;
535
+ #ifdef __cplusplus //! option ‘-Wvolatile’ is valid for C++/ObjC++ but not for C
536
+ #pragma GCC diagnostic ignored "-Wvolatile"
537
+ #pragma clang diagnostic ignored "-Wvolatile"
538
+ #endif
457
539
 
540
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f64(simsimd_capability_t v, simsimd_metric_kind_t k,
541
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
458
542
  typedef simsimd_metric_punned_t m_t;
459
- switch (datatype) {
460
-
461
- case simsimd_datatype_unknown_k: break;
462
-
463
- // Double-precision floating-point vectors
464
- case simsimd_datatype_f64_k:
465
-
466
543
  #if SIMSIMD_TARGET_SVE
467
- if (viable & simsimd_cap_skylake_k)
468
- switch (kind) {
469
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_sve, *c = simsimd_cap_sve_k; return;
470
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_sve, *c = simsimd_cap_sve_k; return;
471
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_sve, *c = simsimd_cap_sve_k; return;
472
- default: break;
473
- }
544
+ if (v & simsimd_cap_sve_k) switch (k) {
545
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_sve, *c = simsimd_cap_sve_k; return;
546
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_sve, *c = simsimd_cap_sve_k; return;
547
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_sve, *c = simsimd_cap_sve_k; return;
548
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f64_sve, *c = simsimd_cap_sve_k; return;
549
+ default: break;
550
+ }
551
+ #endif
552
+ #if SIMSIMD_TARGET_NEON
553
+ if (v & simsimd_cap_neon_k) switch (k) {
554
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_neon, *c = simsimd_cap_neon_k; return;
555
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_neon, *c = simsimd_cap_neon_k; return;
556
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f64_neon, *c = simsimd_cap_neon_k; return;
557
+ default: break;
558
+ }
474
559
  #endif
475
560
  #if SIMSIMD_TARGET_SKYLAKE
476
- if (viable & simsimd_cap_skylake_k)
477
- switch (kind) {
478
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_skylake, *c = simsimd_cap_skylake_k; return;
479
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_skylake, *c = simsimd_cap_skylake_k; return;
480
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_skylake, *c = simsimd_cap_skylake_k; return;
481
- default: break;
482
- }
483
- #endif
484
- if (viable & simsimd_cap_serial_k)
485
- switch (kind) {
486
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_serial, *c = simsimd_cap_serial_k; return;
487
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_serial, *c = simsimd_cap_serial_k; return;
488
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_serial, *c = simsimd_cap_serial_k; return;
489
- case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f64_serial, *c = simsimd_cap_serial_k; return;
490
- case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f64_serial, *c = simsimd_cap_serial_k; return;
491
- default: break;
492
- }
493
-
494
- break;
495
-
496
- // Single-precision floating-point vectors
497
- case simsimd_datatype_f32_k:
561
+ if (v & simsimd_cap_skylake_k) switch (k) {
562
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_skylake, *c = simsimd_cap_skylake_k; return;
563
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_skylake, *c = simsimd_cap_skylake_k; return;
564
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_skylake, *c = simsimd_cap_skylake_k; return;
565
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f64_skylake, *c = simsimd_cap_skylake_k; return;
566
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_f64_skylake, *c = simsimd_cap_skylake_k; return;
567
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_f64_skylake, *c = simsimd_cap_skylake_k; return;
568
+ default: break;
569
+ }
570
+ #endif
571
+ #if SIMSIMD_TARGET_HASWELL
572
+ if (v & simsimd_cap_haswell_k) switch (k) {
573
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_haswell, *c = simsimd_cap_haswell_k; return;
574
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_haswell, *c = simsimd_cap_haswell_k; return;
575
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f64_haswell, *c = simsimd_cap_haswell_k; return;
576
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_f64_haswell, *c = simsimd_cap_haswell_k; return;
577
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_f64_haswell, *c = simsimd_cap_haswell_k; return;
578
+ default: break;
579
+ }
580
+ #endif
581
+ if (v & simsimd_cap_serial_k) switch (k) {
582
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_serial, *c = simsimd_cap_serial_k; return;
583
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_serial, *c = simsimd_cap_serial_k; return;
584
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_serial, *c = simsimd_cap_serial_k; return;
585
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f64_serial, *c = simsimd_cap_serial_k; return;
586
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f64_serial, *c = simsimd_cap_serial_k; return;
587
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f64_serial, *c = simsimd_cap_serial_k; return;
588
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f64_serial, *c = simsimd_cap_serial_k; return;
589
+ case simsimd_metric_mahalanobis_k: *m = (m_t)&simsimd_mahalanobis_f64_serial, *c = simsimd_cap_serial_k; return;
590
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_f64_serial, *c = simsimd_cap_serial_k; return;
591
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_f64_serial, *c = simsimd_cap_serial_k; return;
592
+ default: break;
593
+ }
594
+ }
498
595
 
596
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f32(simsimd_capability_t v, simsimd_metric_kind_t k,
597
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
598
+ typedef simsimd_metric_punned_t m_t;
499
599
  #if SIMSIMD_TARGET_SVE
500
- if (viable & simsimd_cap_sve_k)
501
- switch (kind) {
502
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_sve, *c = simsimd_cap_sve_k; return;
503
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_sve, *c = simsimd_cap_sve_k; return;
504
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_sve, *c = simsimd_cap_sve_k; return;
505
- default: break;
506
- }
600
+ if (v & simsimd_cap_sve_k) switch (k) {
601
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_sve, *c = simsimd_cap_sve_k; return;
602
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_sve, *c = simsimd_cap_sve_k; return;
603
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_sve, *c = simsimd_cap_sve_k; return;
604
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f32_sve, *c = simsimd_cap_sve_k; return;
605
+ default: break;
606
+ }
507
607
  #endif
508
608
  #if SIMSIMD_TARGET_NEON
509
- if (viable & simsimd_cap_neon_k)
510
- switch (kind) {
511
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_neon, *c = simsimd_cap_neon_k; return;
512
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_neon, *c = simsimd_cap_neon_k; return;
513
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_neon, *c = simsimd_cap_neon_k; return;
514
- case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_neon, *c = simsimd_cap_neon_k; return;
515
- case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_neon, *c = simsimd_cap_neon_k; return;
516
- default: break;
517
- }
609
+ if (v & simsimd_cap_neon_k) switch (k) {
610
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_neon, *c = simsimd_cap_neon_k; return;
611
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_neon, *c = simsimd_cap_neon_k; return;
612
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_neon, *c = simsimd_cap_neon_k; return;
613
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f32_neon, *c = simsimd_cap_neon_k; return;
614
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_neon, *c = simsimd_cap_neon_k; return;
615
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_neon, *c = simsimd_cap_neon_k; return;
616
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_f32_neon, *c = simsimd_cap_neon_k; return;
617
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_f32_neon, *c = simsimd_cap_neon_k; return;
618
+ default: break;
619
+ }
518
620
  #endif
519
621
  #if SIMSIMD_TARGET_SKYLAKE
520
- if (viable & simsimd_cap_skylake_k)
521
- switch (kind) {
522
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_skylake, *c = simsimd_cap_skylake_k; return;
523
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_skylake, *c = simsimd_cap_skylake_k; return;
524
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_skylake, *c = simsimd_cap_skylake_k; return;
525
- case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_skylake, *c = simsimd_cap_skylake_k; return;
526
- case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_skylake, *c = simsimd_cap_skylake_k; return;
527
- default: break;
528
- }
622
+ if (v & simsimd_cap_skylake_k) switch (k) {
623
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_skylake, *c = simsimd_cap_skylake_k; return;
624
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_skylake, *c = simsimd_cap_skylake_k; return;
625
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_skylake, *c = simsimd_cap_skylake_k; return;
626
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f32_skylake, *c = simsimd_cap_skylake_k; return;
627
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_skylake, *c = simsimd_cap_skylake_k; return;
628
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_skylake, *c = simsimd_cap_skylake_k; return;
629
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f32_skylake, *c = simsimd_cap_skylake_k; return;
630
+ case simsimd_metric_mahalanobis_k:
631
+ *m = (m_t)&simsimd_mahalanobis_f32_skylake, *c = simsimd_cap_skylake_k;
632
+ return;
633
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_f32_skylake, *c = simsimd_cap_skylake_k; return;
634
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_f32_skylake, *c = simsimd_cap_skylake_k; return;
635
+ default: break;
636
+ }
529
637
  #endif
530
638
  #if SIMSIMD_TARGET_HASWELL
531
- if (viable & simsimd_cap_haswell_k)
532
- switch (kind) {
533
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_haswell, *c = simsimd_cap_haswell_k; return;
534
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_haswell, *c = simsimd_cap_haswell_k; return;
535
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_haswell, *c = simsimd_cap_haswell_k; return;
536
- default: break;
537
- }
538
- #endif
539
-
540
- if (viable & simsimd_cap_serial_k)
541
- switch (kind) {
542
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_serial, *c = simsimd_cap_serial_k; return;
543
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_serial, *c = simsimd_cap_serial_k; return;
544
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_serial, *c = simsimd_cap_serial_k; return;
545
- case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_serial, *c = simsimd_cap_serial_k; return;
546
- case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_serial, *c = simsimd_cap_serial_k; return;
547
- default: break;
548
- }
549
-
550
- break;
551
-
552
- // Half-precision floating-point vectors
553
- case simsimd_datatype_f16_k:
639
+ if (v & simsimd_cap_haswell_k) switch (k) {
640
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_haswell, *c = simsimd_cap_haswell_k; return;
641
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_haswell, *c = simsimd_cap_haswell_k; return;
642
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_haswell, *c = simsimd_cap_haswell_k; return;
643
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f32_haswell, *c = simsimd_cap_haswell_k; return;
644
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_f32_haswell, *c = simsimd_cap_haswell_k; return;
645
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_f32_haswell, *c = simsimd_cap_haswell_k; return;
646
+ default: break;
647
+ }
648
+ #endif
649
+ if (v & simsimd_cap_serial_k) switch (k) {
650
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_serial, *c = simsimd_cap_serial_k; return;
651
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_serial, *c = simsimd_cap_serial_k; return;
652
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_serial, *c = simsimd_cap_serial_k; return;
653
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f32_serial, *c = simsimd_cap_serial_k; return;
654
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_serial, *c = simsimd_cap_serial_k; return;
655
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_serial, *c = simsimd_cap_serial_k; return;
656
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f32_serial, *c = simsimd_cap_serial_k; return;
657
+ case simsimd_metric_mahalanobis_k: *m = (m_t)&simsimd_mahalanobis_f32_serial, *c = simsimd_cap_serial_k; return;
658
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_f32_serial, *c = simsimd_cap_serial_k; return;
659
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_f32_serial, *c = simsimd_cap_serial_k; return;
660
+ default: break;
661
+ }
662
+ }
554
663
 
555
- #if SIMSIMD_TARGET_SVE
556
- if (viable & simsimd_cap_sve_k)
557
- switch (kind) {
558
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_sve, *c = simsimd_cap_sve_k; return;
559
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_sve, *c = simsimd_cap_sve_k; return;
560
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_sve, *c = simsimd_cap_sve_k; return;
561
- default: break;
562
- }
563
- #endif
564
- #if SIMSIMD_TARGET_NEON
565
- if (viable & simsimd_cap_neon_f16_k)
566
- switch (kind) {
567
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_neon, *c = simsimd_cap_neon_f16_k; return;
568
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_neon, *c = simsimd_cap_neon_f16_k; return;
569
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_neon, *c = simsimd_cap_neon_f16_k; return;
570
- case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_neon, *c = simsimd_cap_neon_f16_k; return;
571
- case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_neon, *c = simsimd_cap_neon_f16_k; return;
572
- default: break;
573
- }
664
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f16(simsimd_capability_t v, simsimd_metric_kind_t k,
665
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
666
+ typedef simsimd_metric_punned_t m_t;
667
+ #if SIMSIMD_TARGET_SVE_F16
668
+ if (v & simsimd_cap_sve_k) switch (k) {
669
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_sve, *c = simsimd_cap_sve_f16_k; return;
670
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_sve, *c = simsimd_cap_sve_f16_k; return;
671
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_sve, *c = simsimd_cap_sve_f16_k; return;
672
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f16_sve, *c = simsimd_cap_sve_f16_k; return;
673
+ default: break;
674
+ }
675
+ #endif
676
+ #if SIMSIMD_TARGET_NEON_F16
677
+ if (v & simsimd_cap_neon_f16_k) switch (k) {
678
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_neon, *c = simsimd_cap_neon_f16_k; return;
679
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_neon, *c = simsimd_cap_neon_f16_k; return;
680
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_neon, *c = simsimd_cap_neon_f16_k; return;
681
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f16_neon, *c = simsimd_cap_neon_f16_k; return;
682
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_neon, *c = simsimd_cap_neon_f16_k; return;
683
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_neon, *c = simsimd_cap_neon_f16_k; return;
684
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f16_neon, *c = simsimd_cap_neon_f16_k; return;
685
+ case simsimd_metric_mahalanobis_k: *m = (m_t)&simsimd_mahalanobis_f16_neon, *c = simsimd_cap_neon_f16_k; return;
686
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_f16_neon, *c = simsimd_cap_neon_f16_k; return;
687
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_f16_neon, *c = simsimd_cap_neon_f16_k; return;
688
+ default: break;
689
+ }
574
690
  #endif
575
691
  #if SIMSIMD_TARGET_SAPPHIRE
576
- if (viable & simsimd_cap_sapphire_k)
577
- switch (kind) {
578
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
579
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
580
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
581
- case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
582
- case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
583
- default: break;
584
- }
692
+ if (v & simsimd_cap_sapphire_k) switch (k) {
693
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
694
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
695
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
696
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
697
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
698
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
699
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
700
+ case simsimd_metric_mahalanobis_k:
701
+ *m = (m_t)&simsimd_mahalanobis_f16_sapphire, *c = simsimd_cap_sapphire_k;
702
+ return;
703
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
704
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
705
+ default: break;
706
+ }
585
707
  #endif
586
708
  #if SIMSIMD_TARGET_HASWELL
587
- if (viable & simsimd_cap_haswell_k)
588
- switch (kind) {
589
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_haswell, *c = simsimd_cap_haswell_k; return;
590
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_haswell, *c = simsimd_cap_haswell_k; return;
591
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_haswell, *c = simsimd_cap_haswell_k; return;
592
- case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_haswell, *c = simsimd_cap_haswell_k; return;
593
- case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_haswell, *c = simsimd_cap_haswell_k; return;
594
- default: break;
595
- }
596
- #endif
597
-
598
- if (viable & simsimd_cap_serial_k)
599
- switch (kind) {
600
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_serial, *c = simsimd_cap_serial_k; return;
601
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_serial, *c = simsimd_cap_serial_k; return;
602
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_serial, *c = simsimd_cap_serial_k; return;
603
- case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_serial, *c = simsimd_cap_serial_k; return;
604
- case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_serial, *c = simsimd_cap_serial_k; return;
605
- default: break;
606
- }
607
-
608
- break;
609
-
610
- // Brain floating-point vectors
611
- case simsimd_datatype_bf16_k:
612
- #if SIMSIMD_TARGET_NEON_BF16
613
- if (viable & simsimd_cap_neon_bf16_k)
614
- switch (kind) {
615
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
616
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
617
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
618
- default: break;
619
- }
709
+ if (v & simsimd_cap_haswell_k) switch (k) {
710
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_haswell, *c = simsimd_cap_haswell_k; return;
711
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_haswell, *c = simsimd_cap_haswell_k; return;
712
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_haswell, *c = simsimd_cap_haswell_k; return;
713
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f16_haswell, *c = simsimd_cap_haswell_k; return;
714
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_haswell, *c = simsimd_cap_haswell_k; return;
715
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_haswell, *c = simsimd_cap_haswell_k; return;
716
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f16_haswell, *c = simsimd_cap_haswell_k; return;
717
+ case simsimd_metric_mahalanobis_k:
718
+ *m = (m_t)&simsimd_mahalanobis_f16_haswell, *c = simsimd_cap_haswell_k;
719
+ return;
720
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_f16_haswell, *c = simsimd_cap_haswell_k; return;
721
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_f16_haswell, *c = simsimd_cap_haswell_k; return;
722
+ default: break;
723
+ }
724
+ #endif
725
+ if (v & simsimd_cap_serial_k) switch (k) {
726
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_serial, *c = simsimd_cap_serial_k; return;
727
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_serial, *c = simsimd_cap_serial_k; return;
728
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_serial, *c = simsimd_cap_serial_k; return;
729
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f16_serial, *c = simsimd_cap_serial_k; return;
730
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_serial, *c = simsimd_cap_serial_k; return;
731
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_serial, *c = simsimd_cap_serial_k; return;
732
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f16_serial, *c = simsimd_cap_serial_k; return;
733
+ case simsimd_metric_mahalanobis_k: *m = (m_t)&simsimd_mahalanobis_f16_serial, *c = simsimd_cap_serial_k; return;
734
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_f16_serial, *c = simsimd_cap_serial_k; return;
735
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_f16_serial, *c = simsimd_cap_serial_k; return;
736
+ default: break;
737
+ }
738
+ }
739
+
740
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_bf16(simsimd_capability_t v, simsimd_metric_kind_t k,
741
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
742
+ typedef simsimd_metric_punned_t m_t;
743
+ #if SIMSIMD_TARGET_SVE_BF16
744
+ if (v & simsimd_cap_sve_bf16_k) switch (k) {
745
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_sve, *c = simsimd_cap_sve_bf16_k; return;
746
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_sve, *c = simsimd_cap_sve_bf16_k; return;
747
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_bf16_sve, *c = simsimd_cap_sve_bf16_k; return;
748
+ default: break;
749
+ }
620
750
  #endif
621
- #if SIMSIMD_TARGET_HASWELL
622
- if (viable & simsimd_cap_haswell_k)
623
- switch (kind) {
624
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_haswell, *c = simsimd_cap_haswell_k; return;
625
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_haswell, *c = simsimd_cap_haswell_k; return;
626
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_haswell, *c = simsimd_cap_haswell_k; return;
627
- default: break;
628
- }
751
+ #if SIMSIMD_TARGET_NEON_BF16
752
+ if (v & simsimd_cap_neon_bf16_k) switch (k) {
753
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
754
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
755
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
756
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
757
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
758
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
759
+ default: break;
760
+ }
629
761
  #endif
630
762
  #if SIMSIMD_TARGET_GENOA
631
- if (viable & simsimd_cap_genoa_k)
632
- switch (kind) {
633
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_genoa, *c = simsimd_cap_genoa_k; return;
634
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_genoa, *c = simsimd_cap_genoa_k; return;
635
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_genoa, *c = simsimd_cap_genoa_k; return;
636
- default: break;
637
- }
638
- #endif
639
- if (viable & simsimd_cap_serial_k)
640
- switch (kind) {
641
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_serial, *c = simsimd_cap_serial_k; return;
642
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_serial, *c = simsimd_cap_serial_k; return;
643
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_serial, *c = simsimd_cap_serial_k; return;
644
- case simsimd_metric_js_k: *m = (m_t)&simsimd_js_bf16_serial, *c = simsimd_cap_serial_k; return;
645
- case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_bf16_serial, *c = simsimd_cap_serial_k; return;
646
- default: break;
647
- }
648
-
649
- break;
650
-
651
- // Single-byte integer vectors
652
- case simsimd_datatype_i8_k:
653
- #if SIMSIMD_TARGET_NEON
654
- if (viable & simsimd_cap_neon_i8_k)
655
- switch (kind) {
656
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_neon, *c = simsimd_cap_neon_i8_k; return;
657
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_neon, *c = simsimd_cap_neon_i8_k; return;
658
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_neon, *c = simsimd_cap_neon_i8_k; return;
659
- default: break;
660
- }
763
+ if (v & simsimd_cap_genoa_k) switch (k) {
764
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_genoa, *c = simsimd_cap_genoa_k; return;
765
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_genoa, *c = simsimd_cap_genoa_k; return;
766
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_genoa, *c = simsimd_cap_genoa_k; return;
767
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_bf16_genoa, *c = simsimd_cap_genoa_k; return;
768
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_bf16_genoa, *c = simsimd_cap_genoa_k; return;
769
+ case simsimd_metric_mahalanobis_k: *m = (m_t)&simsimd_mahalanobis_bf16_genoa, *c = simsimd_cap_genoa_k; return;
770
+ default: break;
771
+ }
772
+ #endif
773
+ #if SIMSIMD_TARGET_SKYLAKE
774
+ if (v & simsimd_cap_skylake_k) switch (k) {
775
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_bf16_skylake, *c = simsimd_cap_skylake_k; return;
776
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_bf16_skylake, *c = simsimd_cap_skylake_k; return;
777
+ default: break;
778
+ }
779
+ #endif
780
+ #if SIMSIMD_TARGET_HASWELL
781
+ if (v & simsimd_cap_haswell_k) switch (k) {
782
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_haswell, *c = simsimd_cap_haswell_k; return;
783
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_haswell, *c = simsimd_cap_haswell_k; return;
784
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_haswell, *c = simsimd_cap_haswell_k; return;
785
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_bf16_haswell, *c = simsimd_cap_haswell_k; return;
786
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_bf16_haswell, *c = simsimd_cap_haswell_k; return;
787
+ case simsimd_metric_mahalanobis_k:
788
+ *m = (m_t)&simsimd_mahalanobis_bf16_haswell, *c = simsimd_cap_haswell_k;
789
+ return;
790
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_bf16_haswell, *c = simsimd_cap_haswell_k; return;
791
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_bf16_haswell, *c = simsimd_cap_haswell_k; return;
792
+ default: break;
793
+ }
794
+ #endif
795
+ if (v & simsimd_cap_serial_k) switch (k) {
796
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_serial, *c = simsimd_cap_serial_k; return;
797
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_serial, *c = simsimd_cap_serial_k; return;
798
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_serial, *c = simsimd_cap_serial_k; return;
799
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_bf16_serial, *c = simsimd_cap_serial_k; return;
800
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_bf16_serial, *c = simsimd_cap_serial_k; return;
801
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_bf16_serial, *c = simsimd_cap_serial_k; return;
802
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_bf16_serial, *c = simsimd_cap_serial_k; return;
803
+ case simsimd_metric_mahalanobis_k:
804
+ *m = (m_t)&simsimd_mahalanobis_bf16_serial, *c = simsimd_cap_serial_k;
805
+ return;
806
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_bf16_serial, *c = simsimd_cap_serial_k; return;
807
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_bf16_serial, *c = simsimd_cap_serial_k; return;
808
+ default: break;
809
+ }
810
+ }
811
+
812
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_i8(simsimd_capability_t v, simsimd_metric_kind_t k,
813
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
814
+ typedef simsimd_metric_punned_t m_t;
815
+ #if SIMSIMD_TARGET_NEON_I8
816
+ if (v & simsimd_cap_neon_i8_k) switch (k) {
817
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_neon, *c = simsimd_cap_neon_i8_k; return;
818
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_neon, *c = simsimd_cap_neon_i8_k; return;
819
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_neon, *c = simsimd_cap_neon_i8_k; return;
820
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_i8_neon, *c = simsimd_cap_neon_i8_k; return;
821
+ default: break;
822
+ }
823
+ #endif
824
+ #if SIMSIMD_TARGET_NEON_F16 //! Scaling of 8-bit integers is performed using 16-bit floats.
825
+ if (v & simsimd_cap_neon_f16_k) switch (k) {
826
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_i8_neon, *c = simsimd_cap_neon_f16_k; return;
827
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_i8_neon, *c = simsimd_cap_neon_f16_k; return;
828
+ default: break;
829
+ }
830
+ #endif
831
+ #if SIMSIMD_TARGET_SAPPHIRE //! Scaling of 8-bit integers is performed using 16-bit floats.
832
+ if (v & simsimd_cap_sapphire_k) switch (k) {
833
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_i8_sapphire, *c = simsimd_cap_sapphire_k; return;
834
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_i8_sapphire, *c = simsimd_cap_sapphire_k; return;
835
+ default: break;
836
+ }
661
837
  #endif
662
838
  #if SIMSIMD_TARGET_ICE
663
- if (viable & simsimd_cap_ice_k)
664
- switch (kind) {
665
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_ice, *c = simsimd_cap_ice_k; return;
666
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_ice, *c = simsimd_cap_ice_k; return;
667
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_ice, *c = simsimd_cap_ice_k; return;
668
- default: break;
669
- }
839
+ if (v & simsimd_cap_ice_k) switch (k) {
840
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_ice, *c = simsimd_cap_ice_k; return;
841
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_ice, *c = simsimd_cap_ice_k; return;
842
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_ice, *c = simsimd_cap_ice_k; return;
843
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_i8_ice, *c = simsimd_cap_ice_k; return;
844
+ default: break;
845
+ }
670
846
  #endif
671
847
  #if SIMSIMD_TARGET_HASWELL
672
- if (viable & simsimd_cap_haswell_k)
673
- switch (kind) {
674
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_haswell, *c = simsimd_cap_haswell_k; return;
675
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_haswell, *c = simsimd_cap_haswell_k; return;
676
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_haswell, *c = simsimd_cap_haswell_k; return;
677
- default: break;
678
- }
848
+ if (v & simsimd_cap_haswell_k) switch (k) {
849
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_haswell, *c = simsimd_cap_haswell_k; return;
850
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_haswell, *c = simsimd_cap_haswell_k; return;
851
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_haswell, *c = simsimd_cap_haswell_k; return;
852
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_i8_haswell, *c = simsimd_cap_haswell_k; return;
853
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_i8_haswell, *c = simsimd_cap_haswell_k; return;
854
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_i8_haswell, *c = simsimd_cap_haswell_k; return;
855
+ default: break;
856
+ }
857
+ #endif
858
+ if (v & simsimd_cap_serial_k) switch (k) {
859
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_serial, *c = simsimd_cap_serial_k; return;
860
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_serial, *c = simsimd_cap_serial_k; return;
861
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_serial, *c = simsimd_cap_serial_k; return;
862
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_i8_serial, *c = simsimd_cap_serial_k; return;
863
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_i8_serial, *c = simsimd_cap_serial_k; return;
864
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_i8_serial, *c = simsimd_cap_serial_k; return;
865
+ default: break;
866
+ }
867
+ }
868
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_u8(simsimd_capability_t v, simsimd_metric_kind_t k,
869
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
870
+ typedef simsimd_metric_punned_t m_t;
871
+ #if SIMSIMD_TARGET_NEON_I8
872
+ if (v & simsimd_cap_neon_i8_k) switch (k) {
873
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_u8_neon, *c = simsimd_cap_neon_i8_k; return;
874
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_u8_neon, *c = simsimd_cap_neon_i8_k; return;
875
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_u8_neon, *c = simsimd_cap_neon_i8_k; return;
876
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_u8_neon, *c = simsimd_cap_neon_i8_k; return;
877
+ default: break;
878
+ }
879
+ #endif
880
+ #if SIMSIMD_TARGET_NEON_F16 //! Scaling of 8-bit integers is performed using 16-bit floats.
881
+ if (v & simsimd_cap_neon_f16_k) switch (k) {
882
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_u8_neon, *c = simsimd_cap_neon_f16_k; return;
883
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_u8_neon, *c = simsimd_cap_neon_f16_k; return;
884
+ default: break;
885
+ }
886
+ #endif
887
+ #if SIMSIMD_TARGET_SAPPHIRE //! Scaling of 8-bit integers is performed using 16-bit floats.
888
+ if (v & simsimd_cap_sapphire_k) switch (k) {
889
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_u8_sapphire, *c = simsimd_cap_sapphire_k; return;
890
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_u8_sapphire, *c = simsimd_cap_sapphire_k; return;
891
+ default: break;
892
+ }
679
893
  #endif
894
+ #if SIMSIMD_TARGET_ICE
895
+ if (v & simsimd_cap_ice_k) switch (k) {
896
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_u8_ice, *c = simsimd_cap_ice_k; return;
897
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_u8_ice, *c = simsimd_cap_ice_k; return;
898
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_u8_ice, *c = simsimd_cap_ice_k; return;
899
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_u8_ice, *c = simsimd_cap_ice_k; return;
900
+ default: break;
901
+ }
902
+ #endif
903
+ #if SIMSIMD_TARGET_HASWELL
904
+ if (v & simsimd_cap_haswell_k) switch (k) {
905
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_u8_haswell, *c = simsimd_cap_haswell_k; return;
906
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_u8_haswell, *c = simsimd_cap_haswell_k; return;
907
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_u8_haswell, *c = simsimd_cap_haswell_k; return;
908
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_u8_haswell, *c = simsimd_cap_haswell_k; return;
909
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_u8_haswell, *c = simsimd_cap_haswell_k; return;
910
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_u8_haswell, *c = simsimd_cap_haswell_k; return;
911
+ default: break;
912
+ }
913
+ #endif
914
+ if (v & simsimd_cap_serial_k) switch (k) {
915
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_u8_serial, *c = simsimd_cap_serial_k; return;
916
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_u8_serial, *c = simsimd_cap_serial_k; return;
917
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_u8_serial, *c = simsimd_cap_serial_k; return;
918
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_u8_serial, *c = simsimd_cap_serial_k; return;
919
+ case simsimd_metric_fma_k: *m = (m_t)&simsimd_fma_u8_serial, *c = simsimd_cap_serial_k; return;
920
+ case simsimd_metric_wsum_k: *m = (m_t)&simsimd_wsum_u8_serial, *c = simsimd_cap_serial_k; return;
921
+ default: break;
922
+ }
923
+ }
680
924
 
681
- if (viable & simsimd_cap_serial_k)
682
- switch (kind) {
683
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_serial, *c = simsimd_cap_serial_k; return;
684
- case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_serial, *c = simsimd_cap_serial_k; return;
685
- case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_serial, *c = simsimd_cap_serial_k; return;
686
- default: break;
687
- }
688
-
689
- break;
690
-
691
- // Binary vectors
692
- case simsimd_datatype_b8_k:
693
-
925
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_b8(simsimd_capability_t v, simsimd_metric_kind_t k,
926
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
927
+ typedef simsimd_metric_punned_t m_t;
694
928
  #if SIMSIMD_TARGET_SVE
695
- if (viable & simsimd_cap_sve_k)
696
- switch (kind) {
697
- case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_sve, *c = simsimd_cap_sve_k; return;
698
- case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_sve, *c = simsimd_cap_sve_k; return;
699
- default: break;
700
- }
929
+ if (v & simsimd_cap_sve_k) switch (k) {
930
+ case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_sve, *c = simsimd_cap_sve_k; return;
931
+ case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_sve, *c = simsimd_cap_sve_k; return;
932
+ default: break;
933
+ }
701
934
  #endif
702
935
  #if SIMSIMD_TARGET_NEON
703
- if (viable & simsimd_cap_neon_k)
704
- switch (kind) {
705
- case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_neon, *c = simsimd_cap_neon_k; return;
706
- case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_neon, *c = simsimd_cap_neon_k; return;
707
- default: break;
708
- }
936
+ if (v & simsimd_cap_neon_k) switch (k) {
937
+ case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_neon, *c = simsimd_cap_neon_k; return;
938
+ case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_neon, *c = simsimd_cap_neon_k; return;
939
+ default: break;
940
+ }
709
941
  #endif
710
942
  #if SIMSIMD_TARGET_ICE
711
- if (viable & simsimd_cap_ice_k)
712
- switch (kind) {
713
- case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_ice, *c = simsimd_cap_ice_k; return;
714
- case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_ice, *c = simsimd_cap_ice_k; return;
715
- default: break;
716
- }
943
+ if (v & simsimd_cap_ice_k) switch (k) {
944
+ case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_ice, *c = simsimd_cap_ice_k; return;
945
+ case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_ice, *c = simsimd_cap_ice_k; return;
946
+ default: break;
947
+ }
717
948
  #endif
718
949
  #if SIMSIMD_TARGET_HASWELL
719
- if (viable & simsimd_cap_haswell_k)
720
- switch (kind) {
721
- case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_haswell, *c = simsimd_cap_haswell_k; return;
722
- case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_haswell, *c = simsimd_cap_haswell_k; return;
723
- default: break;
724
- }
725
- #endif
726
-
727
- if (viable & simsimd_cap_serial_k)
728
- switch (kind) {
729
- case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_serial, *c = simsimd_cap_serial_k; return;
730
- case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_serial, *c = simsimd_cap_serial_k; return;
731
- default: break;
732
- }
733
-
734
- break;
950
+ if (v & simsimd_cap_haswell_k) switch (k) {
951
+ case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_haswell, *c = simsimd_cap_haswell_k; return;
952
+ case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_haswell, *c = simsimd_cap_haswell_k; return;
953
+ default: break;
954
+ }
955
+ #endif
956
+ if (v & simsimd_cap_serial_k) switch (k) {
957
+ case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_serial, *c = simsimd_cap_serial_k; return;
958
+ case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_serial, *c = simsimd_cap_serial_k; return;
959
+ default: break;
960
+ }
961
+ }
735
962
 
736
- case simsimd_datatype_f32c_k:
963
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f64c(simsimd_capability_t v, simsimd_metric_kind_t k,
964
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
965
+ typedef simsimd_metric_punned_t m_t;
966
+ #if SIMSIMD_TARGET_SVE
967
+ if (v & simsimd_cap_sve_k) switch (k) {
968
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_sve, *c = simsimd_cap_sve_k; return;
969
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f64c_sve, *c = simsimd_cap_sve_k; return;
970
+ default: break;
971
+ }
972
+ #endif
973
+ #if SIMSIMD_TARGET_SKYLAKE
974
+ if (v & simsimd_cap_skylake_k) switch (k) {
975
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_skylake, *c = simsimd_cap_skylake_k; return;
976
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f64c_skylake, *c = simsimd_cap_skylake_k; return;
977
+ default: break;
978
+ }
979
+ #endif
980
+ if (v & simsimd_cap_serial_k) switch (k) {
981
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_serial, *c = simsimd_cap_serial_k; return;
982
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f64c_serial, *c = simsimd_cap_serial_k; return;
983
+ default: break;
984
+ }
985
+ }
737
986
 
987
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f32c(simsimd_capability_t v, simsimd_metric_kind_t k,
988
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
989
+ typedef simsimd_metric_punned_t m_t;
738
990
  #if SIMSIMD_TARGET_SVE
739
- if (viable & simsimd_cap_sve_k)
740
- switch (kind) {
741
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_sve, *c = simsimd_cap_sve_k; return;
742
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_sve, *c = simsimd_cap_sve_k; return;
743
- default: break;
744
- }
991
+ if (v & simsimd_cap_sve_k) switch (k) {
992
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_sve, *c = simsimd_cap_sve_k; return;
993
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_sve, *c = simsimd_cap_sve_k; return;
994
+ default: break;
995
+ }
745
996
  #endif
746
997
  #if SIMSIMD_TARGET_NEON
747
- if (viable & simsimd_cap_neon_k)
748
- switch (kind) {
749
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_neon, *c = simsimd_cap_neon_k; return;
750
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_neon, *c = simsimd_cap_neon_k; return;
751
- default: break;
752
- }
998
+ if (v & simsimd_cap_neon_k) switch (k) {
999
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_neon, *c = simsimd_cap_neon_k; return;
1000
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_neon, *c = simsimd_cap_neon_k; return;
1001
+ default: break;
1002
+ }
753
1003
  #endif
754
1004
  #if SIMSIMD_TARGET_SKYLAKE
755
- if (viable & simsimd_cap_skylake_k)
756
- switch (kind) {
757
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_skylake, *c = simsimd_cap_skylake_k; return;
758
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_skylake, *c = simsimd_cap_skylake_k; return;
759
- default: break;
760
- }
1005
+ if (v & simsimd_cap_skylake_k) switch (k) {
1006
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_skylake, *c = simsimd_cap_skylake_k; return;
1007
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_skylake, *c = simsimd_cap_skylake_k; return;
1008
+ default: break;
1009
+ }
761
1010
  #endif
762
1011
  #if SIMSIMD_TARGET_HASWELL
763
- if (viable & simsimd_cap_haswell_k)
764
- switch (kind) {
765
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_haswell, *c = simsimd_cap_haswell_k; return;
766
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_haswell, *c = simsimd_cap_haswell_k; return;
767
- default: break;
768
- }
769
- #endif
770
-
771
- if (viable & simsimd_cap_serial_k)
772
- switch (kind) {
773
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_serial, *c = simsimd_cap_serial_k; return;
774
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_serial, *c = simsimd_cap_serial_k; return;
775
- default: break;
776
- }
777
-
778
- break;
779
-
780
- case simsimd_datatype_f64c_k:
1012
+ if (v & simsimd_cap_haswell_k) switch (k) {
1013
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_haswell, *c = simsimd_cap_haswell_k; return;
1014
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_haswell, *c = simsimd_cap_haswell_k; return;
1015
+ default: break;
1016
+ }
1017
+ #endif
1018
+ if (v & simsimd_cap_serial_k) switch (k) {
1019
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_serial, *c = simsimd_cap_serial_k; return;
1020
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_serial, *c = simsimd_cap_serial_k; return;
1021
+ default: break;
1022
+ }
1023
+ }
781
1024
 
782
- #if SIMSIMD_TARGET_SVE
783
- if (viable & simsimd_cap_sve_k)
784
- switch (kind) {
785
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_sve, *c = simsimd_cap_sve_k; return;
786
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f64c_sve, *c = simsimd_cap_sve_k; return;
787
- default: break;
788
- }
1025
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_f16c(simsimd_capability_t v, simsimd_metric_kind_t k,
1026
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
1027
+ typedef simsimd_metric_punned_t m_t;
1028
+ #if SIMSIMD_TARGET_SVE_F16
1029
+ if (v & simsimd_cap_sve_k) switch (k) {
1030
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_sve, *c = simsimd_cap_sve_f16_k; return;
1031
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_sve, *c = simsimd_cap_sve_f16_k; return;
1032
+ default: break;
1033
+ }
1034
+ #endif
1035
+ #if SIMSIMD_TARGET_NEON_F16
1036
+ if (v & simsimd_cap_neon_f16_k) switch (k) {
1037
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_neon, *c = simsimd_cap_neon_f16_k; return;
1038
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_neon, *c = simsimd_cap_neon_f16_k; return;
1039
+ default: break;
1040
+ }
789
1041
  #endif
790
- #if SIMSIMD_TARGET_SKYLAKE
791
- if (viable & simsimd_cap_skylake_k)
792
- switch (kind) {
793
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_skylake, *c = simsimd_cap_skylake_k; return;
794
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f64c_skylake, *c = simsimd_cap_skylake_k; return;
795
- default: break;
796
- }
1042
+ #if SIMSIMD_TARGET_SAPPHIRE
1043
+ if (v & simsimd_cap_sapphire_k) switch (k) {
1044
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
1045
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
1046
+ default: break;
1047
+ }
797
1048
  #endif
1049
+ #if SIMSIMD_TARGET_HASWELL
1050
+ if (v & simsimd_cap_haswell_k) switch (k) {
1051
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_haswell, *c = simsimd_cap_haswell_k; return;
1052
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_haswell, *c = simsimd_cap_haswell_k; return;
1053
+ default: break;
1054
+ }
1055
+ #endif
1056
+ if (v & simsimd_cap_serial_k) switch (k) {
1057
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_serial, *c = simsimd_cap_serial_k; return;
1058
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_serial, *c = simsimd_cap_serial_k; return;
1059
+ default: break;
1060
+ }
1061
+ }
798
1062
 
799
- if (viable & simsimd_cap_serial_k)
800
- switch (kind) {
801
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_serial, *c = simsimd_cap_serial_k; return;
802
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f64c_serial, *c = simsimd_cap_serial_k; return;
803
- default: break;
804
- }
805
-
806
- break;
1063
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_bf16c(simsimd_capability_t v, simsimd_metric_kind_t k,
1064
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
1065
+ typedef simsimd_metric_punned_t m_t;
1066
+ #if SIMSIMD_TARGET_NEON_BF16
1067
+ if (v & simsimd_cap_neon_bf16_k) switch (k) {
1068
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_neon, *c = simsimd_cap_neon_bf16_k; return;
1069
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_neon, *c = simsimd_cap_neon_bf16_k; return;
1070
+ default: break;
1071
+ }
1072
+ #endif
1073
+ #if SIMSIMD_TARGET_GENOA
1074
+ if (v & simsimd_cap_genoa_k) switch (k) {
1075
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_genoa, *c = simsimd_cap_genoa_k; return;
1076
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_genoa, *c = simsimd_cap_genoa_k; return;
1077
+ default: break;
1078
+ }
1079
+ #endif
1080
+ if (v & simsimd_cap_serial_k) switch (k) {
1081
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_serial, *c = simsimd_cap_serial_k; return;
1082
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_serial, *c = simsimd_cap_serial_k; return;
1083
+ default: break;
1084
+ }
1085
+ }
807
1086
 
808
- case simsimd_datatype_f16c_k:
1087
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_u16(simsimd_capability_t v, simsimd_metric_kind_t k,
1088
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
1089
+ typedef simsimd_metric_punned_t m_t;
1090
+ #if SIMSIMD_TARGET_SVE2
1091
+ if (v & simsimd_cap_sve2_k) switch (k) {
1092
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u16_sve2, *c = simsimd_cap_sve2_k; return;
1093
+ case simsimd_metric_spdot_counts_k: *m = (m_t)&simsimd_spdot_counts_u16_sve2, *c = simsimd_cap_sve2_k; return;
1094
+ #if SIMSIMD_TARGET_SVE_BF16 //! We also need `bf16` support for weights
1095
+ case simsimd_metric_spdot_weights_k: *m = (m_t)&simsimd_spdot_weights_u16_sve2, *c = simsimd_cap_sve2_k; return;
1096
+ #endif
1097
+ default: break;
1098
+ }
1099
+ #endif
1100
+ #if SIMSIMD_TARGET_NEON
1101
+ if (v & simsimd_cap_neon_k) switch (k) {
1102
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u16_neon, *c = simsimd_cap_neon_k; return;
1103
+ default: break;
1104
+ }
1105
+ #endif
1106
+ #if SIMSIMD_TARGET_TURIN
1107
+ if (v & simsimd_cap_turin_k) switch (k) {
1108
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u16_turin, *c = simsimd_cap_turin_k; return;
1109
+ case simsimd_metric_spdot_counts_k: *m = (m_t)&simsimd_spdot_counts_u16_turin, *c = simsimd_cap_turin_k; return;
1110
+ case simsimd_metric_spdot_weights_k:
1111
+ *m = (m_t)&simsimd_spdot_weights_u16_turin, *c = simsimd_cap_turin_k;
1112
+ return;
1113
+ default: break;
1114
+ }
1115
+ #endif
1116
+ #if SIMSIMD_TARGET_ICE
1117
+ if (v & simsimd_cap_ice_k) switch (k) {
1118
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u16_ice, *c = simsimd_cap_skylake_k; return;
1119
+ default: break;
1120
+ }
1121
+ #endif
1122
+ if (v & simsimd_cap_serial_k) switch (k) {
1123
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u16_serial, *c = simsimd_cap_serial_k; return;
1124
+ default: break;
1125
+ }
1126
+ }
809
1127
 
810
- #if SIMSIMD_TARGET_SVE
811
- if (viable & simsimd_cap_sve_k)
812
- switch (kind) {
813
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_sve, *c = simsimd_cap_sve_k; return;
814
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_sve, *c = simsimd_cap_sve_k; return;
815
- default: break;
816
- }
1128
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_u32(simsimd_capability_t v, simsimd_metric_kind_t k,
1129
+ simsimd_metric_punned_t *m, simsimd_capability_t *c) {
1130
+ typedef simsimd_metric_punned_t m_t;
1131
+ #if SIMSIMD_TARGET_SVE2
1132
+ if (v & simsimd_cap_sve2_k) switch (k) {
1133
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u32_sve2, *c = simsimd_cap_sve2_k; return;
1134
+ default: break;
1135
+ }
817
1136
  #endif
818
1137
  #if SIMSIMD_TARGET_NEON
819
- if (viable & simsimd_cap_neon_f16_k)
820
- switch (kind) {
821
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_neon, *c = simsimd_cap_neon_f16_k; return;
822
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_neon, *c = simsimd_cap_neon_f16_k; return;
823
- default: break;
824
- }
1138
+ if (v & simsimd_cap_neon_k) switch (k) {
1139
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u32_neon, *c = simsimd_cap_neon_k; return;
1140
+ default: break;
1141
+ }
825
1142
  #endif
826
- #if SIMSIMD_TARGET_SAPPHIRE
827
- if (viable & simsimd_cap_sapphire_k)
828
- switch (kind) {
829
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
830
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
831
- default: break;
832
- }
1143
+ #if SIMSIMD_TARGET_TURIN
1144
+ if (v & simsimd_cap_turin_k) switch (k) {
1145
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u32_turin, *c = simsimd_cap_skylake_k; return;
1146
+ default: break;
1147
+ }
833
1148
  #endif
834
- #if SIMSIMD_TARGET_HASWELL
835
- if (viable & simsimd_cap_haswell_k)
836
- switch (kind) {
837
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_haswell, *c = simsimd_cap_haswell_k; return;
838
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_haswell, *c = simsimd_cap_haswell_k; return;
839
- default: break;
840
- }
1149
+ #if SIMSIMD_TARGET_ICE
1150
+ if (v & simsimd_cap_ice_k) switch (k) {
1151
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u32_ice, *c = simsimd_cap_skylake_k; return;
1152
+ default: break;
1153
+ }
1154
+ #endif
1155
+ if (v & simsimd_cap_serial_k) switch (k) {
1156
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u32_serial, *c = simsimd_cap_serial_k; return;
1157
+ default: break;
1158
+ }
1159
+ }
1160
+
1161
+ /**
1162
+ * @brief Determines the best suited metric implementation based on the given datatype,
1163
+ * supported and allowed by hardware capabilities.
1164
+ *
1165
+ * @param kind The kind of metric to be evaluated.
1166
+ * @param datatype The data type for which the metric needs to be evaluated.
1167
+ * @param supported The hardware capabilities supported by the CPU.
1168
+ * @param allowed The hardware capabilities allowed for use.
1169
+ * @param metric_output Output variable for the selected similarity function.
1170
+ * @param capability_output Output variable for the utilized hardware capabilities.
1171
+ */
1172
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_implementation( //
1173
+ simsimd_metric_kind_t kind, //
1174
+ simsimd_datatype_t datatype, //
1175
+ simsimd_capability_t supported, //
1176
+ simsimd_capability_t allowed, //
1177
+ simsimd_metric_punned_t *metric_output, //
1178
+ simsimd_capability_t *capability_output) {
1179
+
1180
+ // Modern compilers abso-freaking-lutely love optimizing-out my logic!
1181
+ // Just marking the variables as `volatile` is not enough, so we have
1182
+ // to add inline assembly to further discourage them!
1183
+ #if defined(_MSC_VER)
1184
+ _ReadWriteBarrier();
1185
+ #else
1186
+ __asm__ __volatile__("" ::: "memory");
841
1187
  #endif
842
1188
 
843
- if (viable & simsimd_cap_serial_k)
844
- switch (kind) {
845
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_serial, *c = simsimd_cap_serial_k; return;
846
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_serial, *c = simsimd_cap_serial_k; return;
847
- default: break;
848
- }
1189
+ simsimd_metric_punned_t *m = metric_output;
1190
+ simsimd_capability_t *c = capability_output;
1191
+ simsimd_capability_t viable = (simsimd_capability_t)(supported & allowed);
849
1192
 
850
- break;
851
- case simsimd_datatype_bf16c_k:
1193
+ switch (datatype) {
852
1194
 
853
- #if SIMSIMD_TARGET_NEON_BF16
854
- if (viable & simsimd_cap_neon_bf16_k)
855
- switch (kind) {
856
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_neon, *c = simsimd_cap_neon_bf16_k; return;
857
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_neon, *c = simsimd_cap_neon_bf16_k; return;
858
- default: break;
859
- }
860
- #endif
861
- #if SIMSIMD_TARGET_GENOA
862
- if (viable & simsimd_cap_genoa_k)
863
- switch (kind) {
864
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_genoa, *c = simsimd_cap_genoa_k; return;
865
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_genoa, *c = simsimd_cap_genoa_k; return;
866
- default: break;
867
- }
868
- #endif
869
-
870
- if (viable & simsimd_cap_serial_k)
871
- switch (kind) {
872
- case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16c_serial, *c = simsimd_cap_serial_k; return;
873
- case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_bf16c_serial, *c = simsimd_cap_serial_k; return;
874
- default: break;
875
- }
876
-
877
- break;
1195
+ case simsimd_datatype_f64_k: _simsimd_find_metric_punned_f64(viable, kind, m, c); return;
1196
+ case simsimd_datatype_f32_k: _simsimd_find_metric_punned_f32(viable, kind, m, c); return;
1197
+ case simsimd_datatype_f16_k: _simsimd_find_metric_punned_f16(viable, kind, m, c); return;
1198
+ case simsimd_datatype_bf16_k: _simsimd_find_metric_punned_bf16(viable, kind, m, c); return;
1199
+ case simsimd_datatype_i8_k: _simsimd_find_metric_punned_i8(viable, kind, m, c); return;
1200
+ case simsimd_datatype_u8_k: _simsimd_find_metric_punned_u8(viable, kind, m, c); return;
1201
+ case simsimd_datatype_b8_k: _simsimd_find_metric_punned_b8(viable, kind, m, c); return;
1202
+ case simsimd_datatype_f32c_k: _simsimd_find_metric_punned_f32c(viable, kind, m, c); return;
1203
+ case simsimd_datatype_f64c_k: _simsimd_find_metric_punned_f64c(viable, kind, m, c); return;
1204
+ case simsimd_datatype_f16c_k: _simsimd_find_metric_punned_f16c(viable, kind, m, c); return;
1205
+ case simsimd_datatype_bf16c_k: _simsimd_find_metric_punned_bf16c(viable, kind, m, c); return;
1206
+ case simsimd_datatype_u16_k: _simsimd_find_metric_punned_u16(viable, kind, m, c); return;
1207
+ case simsimd_datatype_u32_k: _simsimd_find_metric_punned_u32(viable, kind, m, c); return;
1208
+
1209
+ // These data-types are not supported yet
1210
+ case simsimd_datatype_i4x2_k: break;
1211
+ case simsimd_datatype_i16_k: break;
1212
+ case simsimd_datatype_i32_k: break;
1213
+ case simsimd_datatype_i64_k: break;
1214
+ case simsimd_datatype_u64_k: break;
1215
+ case simsimd_datatype_unknown_k: break;
1216
+ default: break;
878
1217
  }
1218
+
1219
+ // Replace with zeros if no suitable implementation was found
1220
+ *m = (simsimd_metric_punned_t)0;
1221
+ *c = (simsimd_capability_t)0;
1222
+
1223
+ // Modern compilers abso-freaking-lutely love optimizing-out my logic!
1224
+ // Just marking the variables as `volatile` is not enough, so we have
1225
+ // to add inline assembly to further discourage them!
1226
+ #if defined(_MSC_VER)
1227
+ _ReadWriteBarrier();
1228
+ #else
1229
+ __asm__ __volatile__("" ::: "memory");
1230
+ #endif
879
1231
  }
880
1232
 
881
1233
  #pragma GCC diagnostic pop
@@ -910,10 +1262,14 @@ SIMSIMD_PUBLIC simsimd_metric_punned_t simsimd_metric_punned( //
910
1262
  * - Check if the CPU supports AVX512F and AVX512BW extensions on Skylake x86 CPUs and newer
911
1263
  * - Check if the CPU supports AVX512VNNI, AVX512IFMA, AVX512BITALG, AVX512VBMI2, and AVX512VPOPCNTDQ
912
1264
  * extensions on Ice Lake x86 CPUs and newer
1265
+ * - Check if the CPU supports AVX512BF16 extensions on Genoa x86 CPUs and newer
913
1266
  * - Check if the CPU supports AVX512FP16 extensions on Sapphire Rapids x86 CPUs and newer
1267
+ * - Check if the CPU supports AVX2VP2INTERSECT extensions on Turin x86 CPUs and newer
914
1268
  *
915
1269
  * @return 1 if the CPU supports the SIMD instruction set, 0 otherwise.
916
1270
  */
1271
+ SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
1272
+ SIMSIMD_DYNAMIC int simsimd_uses_dynamic_dispatch(void);
917
1273
  SIMSIMD_DYNAMIC int simsimd_uses_neon(void);
918
1274
  SIMSIMD_DYNAMIC int simsimd_uses_neon_f16(void);
919
1275
  SIMSIMD_DYNAMIC int simsimd_uses_neon_bf16(void);
@@ -922,12 +1278,14 @@ SIMSIMD_DYNAMIC int simsimd_uses_sve(void);
922
1278
  SIMSIMD_DYNAMIC int simsimd_uses_sve_f16(void);
923
1279
  SIMSIMD_DYNAMIC int simsimd_uses_sve_bf16(void);
924
1280
  SIMSIMD_DYNAMIC int simsimd_uses_sve_i8(void);
1281
+ SIMSIMD_DYNAMIC int simsimd_uses_sve2(void);
925
1282
  SIMSIMD_DYNAMIC int simsimd_uses_haswell(void);
926
1283
  SIMSIMD_DYNAMIC int simsimd_uses_skylake(void);
927
1284
  SIMSIMD_DYNAMIC int simsimd_uses_ice(void);
928
- SIMSIMD_DYNAMIC int simsimd_uses_sapphire(void);
929
1285
  SIMSIMD_DYNAMIC int simsimd_uses_genoa(void);
930
- SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
1286
+ SIMSIMD_DYNAMIC int simsimd_uses_sapphire(void);
1287
+ SIMSIMD_DYNAMIC int simsimd_uses_turin(void);
1288
+ SIMSIMD_DYNAMIC int simsimd_uses_sierra(void);
931
1289
 
932
1290
  /* Inner products
933
1291
  * - Dot product: the sum of the products of the corresponding elements of two vectors.
@@ -943,30 +1301,30 @@ SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
943
1301
  * @note The dot product is zero if and only if the two vectors are orthogonal.
944
1302
  * @note Defined only for floating-point and integer data types.
945
1303
  */
946
- SIMSIMD_DYNAMIC void simsimd_dot_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
947
- simsimd_distance_t* d);
948
- SIMSIMD_DYNAMIC void simsimd_dot_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
949
- simsimd_distance_t* d);
950
- SIMSIMD_DYNAMIC void simsimd_dot_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
951
- simsimd_distance_t* d);
952
- SIMSIMD_DYNAMIC void simsimd_dot_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
953
- simsimd_distance_t* d);
954
- SIMSIMD_DYNAMIC void simsimd_dot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
955
- simsimd_distance_t* d);
956
- SIMSIMD_DYNAMIC void simsimd_dot_bf16c(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
957
- simsimd_distance_t* d);
958
- SIMSIMD_DYNAMIC void simsimd_dot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
959
- simsimd_distance_t* d);
960
- SIMSIMD_DYNAMIC void simsimd_dot_f64c(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
961
- simsimd_distance_t* d);
962
- SIMSIMD_DYNAMIC void simsimd_vdot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
963
- simsimd_distance_t* d);
964
- SIMSIMD_DYNAMIC void simsimd_vdot_bf16c(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
965
- simsimd_distance_t* d);
966
- SIMSIMD_DYNAMIC void simsimd_vdot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
967
- simsimd_distance_t* d);
968
- SIMSIMD_DYNAMIC void simsimd_vdot_f64c(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
969
- simsimd_distance_t* d);
1304
+ SIMSIMD_DYNAMIC void simsimd_dot_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1305
+ simsimd_distance_t *d);
1306
+ SIMSIMD_DYNAMIC void simsimd_dot_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1307
+ simsimd_distance_t *d);
1308
+ SIMSIMD_DYNAMIC void simsimd_dot_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1309
+ simsimd_distance_t *d);
1310
+ SIMSIMD_DYNAMIC void simsimd_dot_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1311
+ simsimd_distance_t *d);
1312
+ SIMSIMD_DYNAMIC void simsimd_dot_f16c(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1313
+ simsimd_distance_t *d);
1314
+ SIMSIMD_DYNAMIC void simsimd_dot_bf16c(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1315
+ simsimd_distance_t *d);
1316
+ SIMSIMD_DYNAMIC void simsimd_dot_f32c(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1317
+ simsimd_distance_t *d);
1318
+ SIMSIMD_DYNAMIC void simsimd_dot_f64c(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1319
+ simsimd_distance_t *d);
1320
+ SIMSIMD_DYNAMIC void simsimd_vdot_f16c(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1321
+ simsimd_distance_t *d);
1322
+ SIMSIMD_DYNAMIC void simsimd_vdot_bf16c(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1323
+ simsimd_distance_t *d);
1324
+ SIMSIMD_DYNAMIC void simsimd_vdot_f32c(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1325
+ simsimd_distance_t *d);
1326
+ SIMSIMD_DYNAMIC void simsimd_vdot_f64c(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1327
+ simsimd_distance_t *d);
970
1328
 
971
1329
  /* Spatial distances
972
1330
  * - Cosine distance: the cosine of the angle between two vectors.
@@ -981,26 +1339,42 @@ SIMSIMD_DYNAMIC void simsimd_vdot_f64c(simsimd_f64_t const* a, simsimd_f64_t con
981
1339
  * @note The output distance value is zero if and only if the two vectors are identical.
982
1340
  * @note Defined only for floating-point and integer data types.
983
1341
  */
984
- SIMSIMD_DYNAMIC void simsimd_cos_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
985
- simsimd_distance_t* d);
986
- SIMSIMD_DYNAMIC void simsimd_cos_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
987
- simsimd_distance_t* d);
988
- SIMSIMD_DYNAMIC void simsimd_cos_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
989
- simsimd_distance_t* d);
990
- SIMSIMD_DYNAMIC void simsimd_cos_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
991
- simsimd_distance_t* d);
992
- SIMSIMD_DYNAMIC void simsimd_cos_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
993
- simsimd_distance_t* d);
994
- SIMSIMD_DYNAMIC void simsimd_l2sq_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
995
- simsimd_distance_t* d);
996
- SIMSIMD_DYNAMIC void simsimd_l2sq_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
997
- simsimd_distance_t* d);
998
- SIMSIMD_DYNAMIC void simsimd_l2sq_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
999
- simsimd_distance_t* d);
1000
- SIMSIMD_DYNAMIC void simsimd_l2sq_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1001
- simsimd_distance_t* d);
1002
- SIMSIMD_DYNAMIC void simsimd_l2sq_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1003
- simsimd_distance_t* d);
1342
+ SIMSIMD_DYNAMIC void simsimd_cos_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1343
+ simsimd_distance_t *d);
1344
+ SIMSIMD_DYNAMIC void simsimd_cos_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1345
+ simsimd_distance_t *d);
1346
+ SIMSIMD_DYNAMIC void simsimd_cos_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1347
+ simsimd_distance_t *d);
1348
+ SIMSIMD_DYNAMIC void simsimd_cos_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1349
+ simsimd_distance_t *d);
1350
+ SIMSIMD_DYNAMIC void simsimd_cos_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1351
+ simsimd_distance_t *d);
1352
+ SIMSIMD_DYNAMIC void simsimd_cos_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1353
+ simsimd_distance_t *d);
1354
+ SIMSIMD_DYNAMIC void simsimd_l2sq_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1355
+ simsimd_distance_t *d);
1356
+ SIMSIMD_DYNAMIC void simsimd_l2sq_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1357
+ simsimd_distance_t *d);
1358
+ SIMSIMD_DYNAMIC void simsimd_l2sq_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1359
+ simsimd_distance_t *d);
1360
+ SIMSIMD_DYNAMIC void simsimd_l2sq_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1361
+ simsimd_distance_t *d);
1362
+ SIMSIMD_DYNAMIC void simsimd_l2sq_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1363
+ simsimd_distance_t *d);
1364
+ SIMSIMD_DYNAMIC void simsimd_l2sq_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1365
+ simsimd_distance_t *d);
1366
+ SIMSIMD_DYNAMIC void simsimd_l2_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1367
+ simsimd_distance_t *d);
1368
+ SIMSIMD_DYNAMIC void simsimd_l2_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1369
+ simsimd_distance_t *d);
1370
+ SIMSIMD_DYNAMIC void simsimd_l2_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1371
+ simsimd_distance_t *d);
1372
+ SIMSIMD_DYNAMIC void simsimd_l2_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1373
+ simsimd_distance_t *d);
1374
+ SIMSIMD_DYNAMIC void simsimd_l2_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1375
+ simsimd_distance_t *d);
1376
+ SIMSIMD_DYNAMIC void simsimd_l2_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1377
+ simsimd_distance_t *d);
1004
1378
 
1005
1379
  /* Binary distances
1006
1380
  * - Hamming distance: the number of positions at which the corresponding bits are different.
@@ -1015,18 +1389,18 @@ SIMSIMD_DYNAMIC void simsimd_l2sq_f64(simsimd_f64_t const* a, simsimd_f64_t cons
1015
1389
  * @note The output distance value is zero if and only if the two vectors are identical.
1016
1390
  * @note Defined only for binary data.
1017
1391
  */
1018
- SIMSIMD_DYNAMIC void simsimd_hamming_b8(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n,
1019
- simsimd_distance_t* d);
1020
- SIMSIMD_DYNAMIC void simsimd_jaccard_b8(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n,
1021
- simsimd_distance_t* d);
1392
+ SIMSIMD_DYNAMIC void simsimd_hamming_b8(simsimd_b8_t const *a, simsimd_b8_t const *b, simsimd_size_t n,
1393
+ simsimd_distance_t *d);
1394
+ SIMSIMD_DYNAMIC void simsimd_jaccard_b8(simsimd_b8_t const *a, simsimd_b8_t const *b, simsimd_size_t n,
1395
+ simsimd_distance_t *d);
1022
1396
 
1023
1397
  /* Probability distributions
1024
1398
  * - Jensen-Shannon divergence: a measure of similarity between two probability distributions.
1025
1399
  * - Kullback-Leibler divergence: a measure of how one probability distribution diverges from a second.
1026
1400
  *
1027
- * @param a The first descrete probability distribution.
1028
- * @param b The second descrete probability distribution.
1029
- * @param n The number of elements in the descrete distributions.
1401
+ * @param a The first discrete probability distribution.
1402
+ * @param b The second discrete probability distribution.
1403
+ * @param n The number of elements in the discrete distributions.
1030
1404
  * @param d The output divergence value.
1031
1405
  *
1032
1406
  * @note The distributions are assumed to be normalized.
@@ -1034,22 +1408,22 @@ SIMSIMD_DYNAMIC void simsimd_jaccard_b8(simsimd_b8_t const* a, simsimd_b8_t cons
1034
1408
  * @note The output divergence value is zero if and only if the two distributions are identical.
1035
1409
  * @note Defined only for floating-point data types.
1036
1410
  */
1037
- SIMSIMD_DYNAMIC void simsimd_kl_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1038
- simsimd_distance_t* d);
1039
- SIMSIMD_DYNAMIC void simsimd_kl_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1040
- simsimd_distance_t* d);
1041
- SIMSIMD_DYNAMIC void simsimd_kl_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1042
- simsimd_distance_t* d);
1043
- SIMSIMD_DYNAMIC void simsimd_kl_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1044
- simsimd_distance_t* d);
1045
- SIMSIMD_DYNAMIC void simsimd_js_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1046
- simsimd_distance_t* d);
1047
- SIMSIMD_DYNAMIC void simsimd_js_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1048
- simsimd_distance_t* d);
1049
- SIMSIMD_DYNAMIC void simsimd_js_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1050
- simsimd_distance_t* d);
1051
- SIMSIMD_DYNAMIC void simsimd_js_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1052
- simsimd_distance_t* d);
1411
+ SIMSIMD_DYNAMIC void simsimd_kl_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1412
+ simsimd_distance_t *d);
1413
+ SIMSIMD_DYNAMIC void simsimd_kl_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1414
+ simsimd_distance_t *d);
1415
+ SIMSIMD_DYNAMIC void simsimd_kl_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1416
+ simsimd_distance_t *d);
1417
+ SIMSIMD_DYNAMIC void simsimd_kl_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1418
+ simsimd_distance_t *d);
1419
+ SIMSIMD_DYNAMIC void simsimd_js_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1420
+ simsimd_distance_t *d);
1421
+ SIMSIMD_DYNAMIC void simsimd_js_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1422
+ simsimd_distance_t *d);
1423
+ SIMSIMD_DYNAMIC void simsimd_js_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1424
+ simsimd_distance_t *d);
1425
+ SIMSIMD_DYNAMIC void simsimd_js_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1426
+ simsimd_distance_t *d);
1053
1427
 
1054
1428
  #else
1055
1429
 
@@ -1066,20 +1440,33 @@ SIMSIMD_DYNAMIC void simsimd_js_f64(simsimd_f64_t const* a, simsimd_f64_t const*
1066
1440
  */
1067
1441
 
1068
1442
  // clang-format off
1069
- SIMSIMD_PUBLIC int simsimd_uses_neon(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON; }
1070
- SIMSIMD_PUBLIC int simsimd_uses_neon_f16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON && SIMSIMD_NATIVE_F16; }
1071
- SIMSIMD_PUBLIC int simsimd_uses_neon_bf16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON && SIMSIMD_NATIVE_BF16; }
1072
- SIMSIMD_PUBLIC int simsimd_uses_neon_i8(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON; }
1073
- SIMSIMD_PUBLIC int simsimd_uses_sve(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE; }
1074
- SIMSIMD_PUBLIC int simsimd_uses_sve_f16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE && SIMSIMD_NATIVE_F16; }
1075
- SIMSIMD_PUBLIC int simsimd_uses_sve_bf16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE && SIMSIMD_NATIVE_BF16; }
1076
- SIMSIMD_PUBLIC int simsimd_uses_sve_i8(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE; }
1077
- SIMSIMD_PUBLIC int simsimd_uses_haswell(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_HASWELL; }
1078
- SIMSIMD_PUBLIC int simsimd_uses_skylake(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SKYLAKE; }
1079
- SIMSIMD_PUBLIC int simsimd_uses_ice(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_ICE; }
1080
- SIMSIMD_PUBLIC int simsimd_uses_sapphire(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SAPPHIRE; }
1081
- SIMSIMD_PUBLIC int simsimd_uses_genoa(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_GENOA; }
1082
- SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void) { return simsimd_capabilities_implementation(); }
1443
+ SIMSIMD_PUBLIC int simsimd_uses_neon(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON; }
1444
+ SIMSIMD_PUBLIC int simsimd_uses_neon_f16(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON_F16 ; }
1445
+ SIMSIMD_PUBLIC int simsimd_uses_neon_bf16(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON_BF16; }
1446
+ SIMSIMD_PUBLIC int simsimd_uses_neon_i8(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON_I8; }
1447
+ SIMSIMD_PUBLIC int simsimd_uses_sve(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE; }
1448
+ SIMSIMD_PUBLIC int simsimd_uses_sve_f16(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE_F16; }
1449
+ SIMSIMD_PUBLIC int simsimd_uses_sve_bf16(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE_BF16; }
1450
+ SIMSIMD_PUBLIC int simsimd_uses_sve_i8(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE_I8; }
1451
+ SIMSIMD_PUBLIC int simsimd_uses_sve2(void) { return _SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE2; }
1452
+ SIMSIMD_PUBLIC int simsimd_uses_haswell(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_HASWELL; }
1453
+ SIMSIMD_PUBLIC int simsimd_uses_skylake(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SKYLAKE; }
1454
+ SIMSIMD_PUBLIC int simsimd_uses_ice(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_ICE; }
1455
+ SIMSIMD_PUBLIC int simsimd_uses_genoa(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_GENOA; }
1456
+ SIMSIMD_PUBLIC int simsimd_uses_sapphire(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SAPPHIRE; }
1457
+ SIMSIMD_PUBLIC int simsimd_uses_turin(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_TURIN; }
1458
+ SIMSIMD_PUBLIC int simsimd_uses_sierra(void) { return _SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SIERRA; }
1459
+ SIMSIMD_PUBLIC int simsimd_uses_dynamic_dispatch(void) { return 0; }
1460
+ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void) { return _simsimd_capabilities_implementation(); }
1461
+ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
1462
+ simsimd_metric_kind_t kind, //
1463
+ simsimd_datatype_t datatype, //
1464
+ simsimd_capability_t supported, //
1465
+ simsimd_capability_t allowed, //
1466
+ simsimd_metric_punned_t* metric_output, //
1467
+ simsimd_capability_t* capability_output) {
1468
+ _simsimd_find_metric_punned_implementation(kind, datatype, supported, allowed, metric_output, capability_output);
1469
+ }
1083
1470
  // clang-format on
1084
1471
 
1085
1472
  /* Inner products
@@ -1096,11 +1483,35 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void) { return simsimd_
1096
1483
  * @note The dot product is zero if and only if the two vectors are orthogonal.
1097
1484
  * @note Defined only for floating-point and integer data types.
1098
1485
  */
1099
- SIMSIMD_PUBLIC void simsimd_dot_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1100
- simsimd_distance_t* d) {
1101
- #if SIMSIMD_TARGET_SVE
1486
+ SIMSIMD_PUBLIC void simsimd_dot_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1487
+ simsimd_distance_t *d) {
1488
+ #if SIMSIMD_TARGET_NEON_F16
1489
+ simsimd_dot_i8_neon(a, b, n, d);
1490
+ #elif SIMSIMD_TARGET_ICE
1491
+ simsimd_dot_i8_ice(a, b, n, d);
1492
+ #elif SIMSIMD_TARGET_HASWELL
1493
+ simsimd_dot_i8_haswell(a, b, n, d);
1494
+ #else
1495
+ simsimd_dot_i8_serial(a, b, n, d);
1496
+ #endif
1497
+ }
1498
+ SIMSIMD_PUBLIC void simsimd_dot_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1499
+ simsimd_distance_t *d) {
1500
+ #if SIMSIMD_TARGET_NEON_F16
1501
+ simsimd_dot_u8_neon(a, b, n, d);
1502
+ #elif SIMSIMD_TARGET_ICE
1503
+ simsimd_dot_u8_ice(a, b, n, d);
1504
+ #elif SIMSIMD_TARGET_HASWELL
1505
+ simsimd_dot_u8_haswell(a, b, n, d);
1506
+ #else
1507
+ simsimd_dot_u8_serial(a, b, n, d);
1508
+ #endif
1509
+ }
1510
+ SIMSIMD_PUBLIC void simsimd_dot_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1511
+ simsimd_distance_t *d) {
1512
+ #if SIMSIMD_TARGET_SVE_F16
1102
1513
  simsimd_dot_f16_sve(a, b, n, d);
1103
- #elif SIMSIMD_TARGET_NEON
1514
+ #elif SIMSIMD_TARGET_NEON_F16
1104
1515
  simsimd_dot_f16_neon(a, b, n, d);
1105
1516
  #elif SIMSIMD_TARGET_SAPPHIRE
1106
1517
  simsimd_dot_f16_sapphire(a, b, n, d);
@@ -1110,20 +1521,20 @@ SIMSIMD_PUBLIC void simsimd_dot_f16(simsimd_f16_t const* a, simsimd_f16_t const*
1110
1521
  simsimd_dot_f16_serial(a, b, n, d);
1111
1522
  #endif
1112
1523
  }
1113
-
1114
- SIMSIMD_PUBLIC void simsimd_dot_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1115
- simsimd_distance_t* d) {
1524
+ SIMSIMD_PUBLIC void simsimd_dot_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1525
+ simsimd_distance_t *d) {
1116
1526
  #if SIMSIMD_TARGET_GENOA
1117
1527
  simsimd_dot_bf16_genoa(a, b, n, d);
1118
1528
  #elif SIMSIMD_TARGET_HASWELL
1119
1529
  simsimd_dot_bf16_haswell(a, b, n, d);
1530
+ #elif SIMSIMD_TARGET_NEON_BF16
1531
+ simsimd_dot_bf16_neon(a, b, n, d);
1120
1532
  #else
1121
1533
  simsimd_dot_bf16_serial(a, b, n, d);
1122
1534
  #endif
1123
1535
  }
1124
-
1125
- SIMSIMD_PUBLIC void simsimd_dot_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1126
- simsimd_distance_t* d) {
1536
+ SIMSIMD_PUBLIC void simsimd_dot_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1537
+ simsimd_distance_t *d) {
1127
1538
  #if SIMSIMD_TARGET_SVE
1128
1539
  simsimd_dot_f32_sve(a, b, n, d);
1129
1540
  #elif SIMSIMD_TARGET_NEON
@@ -1136,8 +1547,8 @@ SIMSIMD_PUBLIC void simsimd_dot_f32(simsimd_f32_t const* a, simsimd_f32_t const*
1136
1547
  simsimd_dot_f32_serial(a, b, n, d);
1137
1548
  #endif
1138
1549
  }
1139
- SIMSIMD_PUBLIC void simsimd_dot_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1140
- simsimd_distance_t* d) {
1550
+ SIMSIMD_PUBLIC void simsimd_dot_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1551
+ simsimd_distance_t *d) {
1141
1552
  #if SIMSIMD_TARGET_SVE
1142
1553
  simsimd_dot_f64_sve(a, b, n, d);
1143
1554
  #elif SIMSIMD_TARGET_SKYLAKE
@@ -1146,11 +1557,11 @@ SIMSIMD_PUBLIC void simsimd_dot_f64(simsimd_f64_t const* a, simsimd_f64_t const*
1146
1557
  simsimd_dot_f64_serial(a, b, n, d);
1147
1558
  #endif
1148
1559
  }
1149
- SIMSIMD_PUBLIC void simsimd_dot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1150
- simsimd_distance_t* d) {
1151
- #if SIMSIMD_TARGET_SVE
1560
+ SIMSIMD_PUBLIC void simsimd_dot_f16c(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1561
+ simsimd_distance_t *d) {
1562
+ #if SIMSIMD_TARGET_SVE_F16
1152
1563
  simsimd_dot_f16c_sve(a, b, n, d);
1153
- #elif SIMSIMD_TARGET_NEON
1564
+ #elif SIMSIMD_TARGET_NEON_F16
1154
1565
  simsimd_dot_f16c_neon(a, b, n, d);
1155
1566
  #elif SIMSIMD_TARGET_SAPPHIRE
1156
1567
  simsimd_dot_f16c_sapphire(a, b, n, d);
@@ -1160,16 +1571,18 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c(simsimd_f16_t const* a, simsimd_f16_t const
1160
1571
  simsimd_dot_f16c_serial(a, b, n, d);
1161
1572
  #endif
1162
1573
  }
1163
- SIMSIMD_PUBLIC void simsimd_dot_bf16c(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1164
- simsimd_distance_t* d) {
1574
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1575
+ simsimd_distance_t *d) {
1165
1576
  #if SIMSIMD_TARGET_GENOA
1166
1577
  simsimd_dot_bf16c_genoa(a, b, n, d);
1578
+ #elif SIMSIMD_TARGET_NEON_BF16
1579
+ simsimd_dot_bf16c_neon(a, b, n, d);
1167
1580
  #else
1168
1581
  simsimd_dot_bf16c_serial(a, b, n, d);
1169
1582
  #endif
1170
1583
  }
1171
- SIMSIMD_PUBLIC void simsimd_dot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1172
- simsimd_distance_t* d) {
1584
+ SIMSIMD_PUBLIC void simsimd_dot_f32c(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1585
+ simsimd_distance_t *d) {
1173
1586
  #if SIMSIMD_TARGET_SVE
1174
1587
  simsimd_dot_f32c_sve(a, b, n, d);
1175
1588
  #elif SIMSIMD_TARGET_NEON
@@ -1182,8 +1595,8 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c(simsimd_f32_t const* a, simsimd_f32_t const
1182
1595
  simsimd_dot_f32c_serial(a, b, n, d);
1183
1596
  #endif
1184
1597
  }
1185
- SIMSIMD_PUBLIC void simsimd_dot_f64c(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1186
- simsimd_distance_t* d) {
1598
+ SIMSIMD_PUBLIC void simsimd_dot_f64c(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1599
+ simsimd_distance_t *d) {
1187
1600
  #if SIMSIMD_TARGET_SVE
1188
1601
  simsimd_dot_f64c_sve(a, b, n, d);
1189
1602
  #elif SIMSIMD_TARGET_SKYLAKE
@@ -1192,8 +1605,8 @@ SIMSIMD_PUBLIC void simsimd_dot_f64c(simsimd_f64_t const* a, simsimd_f64_t const
1192
1605
  simsimd_dot_f64c_serial(a, b, n, d);
1193
1606
  #endif
1194
1607
  }
1195
- SIMSIMD_PUBLIC void simsimd_vdot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1196
- simsimd_distance_t* d) {
1608
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1609
+ simsimd_distance_t *d) {
1197
1610
  #if SIMSIMD_TARGET_SVE
1198
1611
  simsimd_vdot_f16c_sve(a, b, n, d);
1199
1612
  #elif SIMSIMD_TARGET_NEON
@@ -1206,12 +1619,18 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c(simsimd_f16_t const* a, simsimd_f16_t cons
1206
1619
  simsimd_vdot_f16c_serial(a, b, n, d);
1207
1620
  #endif
1208
1621
  }
1209
- SIMSIMD_PUBLIC void simsimd_vdot_bf16c(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1210
- simsimd_distance_t* d) {
1622
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1623
+ simsimd_distance_t *d) {
1624
+ #if SIMSIMD_TARGET_GENOA
1625
+ simsimd_vdot_bf16c_genoa(a, b, n, d);
1626
+ #elif SIMSIMD_TARGET_NEON_BF16
1627
+ simsimd_dot_bf16c_neon(a, b, n, d);
1628
+ #else
1211
1629
  simsimd_vdot_bf16c_serial(a, b, n, d);
1630
+ #endif
1212
1631
  }
1213
- SIMSIMD_PUBLIC void simsimd_vdot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1214
- simsimd_distance_t* d) {
1632
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1633
+ simsimd_distance_t *d) {
1215
1634
  #if SIMSIMD_TARGET_SVE
1216
1635
  simsimd_vdot_f32c_sve(a, b, n, d);
1217
1636
  #elif SIMSIMD_TARGET_NEON
@@ -1224,8 +1643,8 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c(simsimd_f32_t const* a, simsimd_f32_t cons
1224
1643
  simsimd_vdot_f32c_serial(a, b, n, d);
1225
1644
  #endif
1226
1645
  }
1227
- SIMSIMD_PUBLIC void simsimd_vdot_f64c(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1228
- simsimd_distance_t* d) {
1646
+ SIMSIMD_PUBLIC void simsimd_vdot_f64c(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1647
+ simsimd_distance_t *d) {
1229
1648
  #if SIMSIMD_TARGET_SVE
1230
1649
  simsimd_vdot_f64c_sve(a, b, n, d);
1231
1650
  #elif SIMSIMD_TARGET_SKYLAKE
@@ -1248,8 +1667,8 @@ SIMSIMD_PUBLIC void simsimd_vdot_f64c(simsimd_f64_t const* a, simsimd_f64_t cons
1248
1667
  * @note The output distance value is zero if and only if the two vectors are identical.
1249
1668
  * @note Defined only for floating-point and integer data types.
1250
1669
  */
1251
- SIMSIMD_PUBLIC void simsimd_cos_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
1252
- simsimd_distance_t* d) {
1670
+ SIMSIMD_PUBLIC void simsimd_cos_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1671
+ simsimd_distance_t *d) {
1253
1672
  #if SIMSIMD_TARGET_NEON
1254
1673
  simsimd_cos_i8_neon(a, b, n, d);
1255
1674
  #elif SIMSIMD_TARGET_ICE
@@ -1260,23 +1679,23 @@ SIMSIMD_PUBLIC void simsimd_cos_i8(simsimd_i8_t const* a, simsimd_i8_t const* b,
1260
1679
  simsimd_cos_i8_serial(a, b, n, d);
1261
1680
  #endif
1262
1681
  }
1263
- SIMSIMD_PUBLIC void simsimd_l2sq_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
1264
- simsimd_distance_t* d) {
1682
+ SIMSIMD_PUBLIC void simsimd_cos_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1683
+ simsimd_distance_t *d) {
1265
1684
  #if SIMSIMD_TARGET_NEON
1266
- simsimd_l2sq_i8_neon(a, b, n, d);
1685
+ simsimd_cos_u8_neon(a, b, n, d);
1267
1686
  #elif SIMSIMD_TARGET_ICE
1268
- simsimd_l2sq_i8_ice(a, b, n, d);
1687
+ simsimd_cos_u8_ice(a, b, n, d);
1269
1688
  #elif SIMSIMD_TARGET_HASWELL
1270
- simsimd_l2sq_i8_haswell(a, b, n, d);
1689
+ simsimd_cos_u8_haswell(a, b, n, d);
1271
1690
  #else
1272
- simsimd_l2sq_i8_serial(a, b, n, d);
1691
+ simsimd_cos_u8_serial(a, b, n, d);
1273
1692
  #endif
1274
1693
  }
1275
- SIMSIMD_PUBLIC void simsimd_cos_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1276
- simsimd_distance_t* d) {
1277
- #if SIMSIMD_TARGET_SVE
1694
+ SIMSIMD_PUBLIC void simsimd_cos_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1695
+ simsimd_distance_t *d) {
1696
+ #if SIMSIMD_TARGET_SVE_F16
1278
1697
  simsimd_cos_f16_sve(a, b, n, d);
1279
- #elif SIMSIMD_TARGET_NEON
1698
+ #elif SIMSIMD_TARGET_NEON_F16
1280
1699
  simsimd_cos_f16_neon(a, b, n, d);
1281
1700
  #elif SIMSIMD_TARGET_SAPPHIRE
1282
1701
  simsimd_cos_f16_sapphire(a, b, n, d);
@@ -1286,18 +1705,22 @@ SIMSIMD_PUBLIC void simsimd_cos_f16(simsimd_f16_t const* a, simsimd_f16_t const*
1286
1705
  simsimd_cos_f16_serial(a, b, n, d);
1287
1706
  #endif
1288
1707
  }
1289
- SIMSIMD_PUBLIC void simsimd_cos_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1290
- simsimd_distance_t* d) {
1708
+ SIMSIMD_PUBLIC void simsimd_cos_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1709
+ simsimd_distance_t *d) {
1291
1710
  #if SIMSIMD_TARGET_GENOA
1292
1711
  simsimd_cos_bf16_genoa(a, b, n, d);
1293
1712
  #elif SIMSIMD_TARGET_HASWELL
1294
1713
  simsimd_cos_bf16_haswell(a, b, n, d);
1714
+ #elif SIMSIMD_TARGET_SVE_BF16
1715
+ simsimd_cos_bf16_sve(a, b, n, d);
1716
+ #elif SIMSIMD_TARGET_NEON_BF16
1717
+ simsimd_cos_bf16_neon(a, b, n, d);
1295
1718
  #else
1296
1719
  simsimd_cos_bf16_serial(a, b, n, d);
1297
1720
  #endif
1298
1721
  }
1299
- SIMSIMD_PUBLIC void simsimd_cos_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1300
- simsimd_distance_t* d) {
1722
+ SIMSIMD_PUBLIC void simsimd_cos_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1723
+ simsimd_distance_t *d) {
1301
1724
  #if SIMSIMD_TARGET_SVE
1302
1725
  simsimd_cos_f32_sve(a, b, n, d);
1303
1726
  #elif SIMSIMD_TARGET_NEON
@@ -1310,21 +1733,47 @@ SIMSIMD_PUBLIC void simsimd_cos_f32(simsimd_f32_t const* a, simsimd_f32_t const*
1310
1733
  simsimd_cos_f32_serial(a, b, n, d);
1311
1734
  #endif
1312
1735
  }
1313
- SIMSIMD_PUBLIC void simsimd_cos_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1314
- simsimd_distance_t* d) {
1736
+ SIMSIMD_PUBLIC void simsimd_cos_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1737
+ simsimd_distance_t *d) {
1315
1738
  #if SIMSIMD_TARGET_SVE
1316
1739
  simsimd_cos_f64_sve(a, b, n, d);
1740
+ #elif SIMSIMD_TARGET_NEON
1741
+ simsimd_cos_f64_neon(a, b, n, d);
1317
1742
  #elif SIMSIMD_TARGET_SKYLAKE
1318
1743
  simsimd_cos_f64_skylake(a, b, n, d);
1319
1744
  #else
1320
1745
  simsimd_cos_f64_serial(a, b, n, d);
1321
1746
  #endif
1322
1747
  }
1323
- SIMSIMD_PUBLIC void simsimd_l2sq_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1324
- simsimd_distance_t* d) {
1325
- #if SIMSIMD_TARGET_SVE
1748
+ SIMSIMD_PUBLIC void simsimd_l2sq_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1749
+ simsimd_distance_t *d) {
1750
+ #if SIMSIMD_TARGET_NEON
1751
+ simsimd_l2sq_i8_neon(a, b, n, d);
1752
+ #elif SIMSIMD_TARGET_ICE
1753
+ simsimd_l2sq_i8_ice(a, b, n, d);
1754
+ #elif SIMSIMD_TARGET_HASWELL
1755
+ simsimd_l2sq_i8_haswell(a, b, n, d);
1756
+ #else
1757
+ simsimd_l2sq_i8_serial(a, b, n, d);
1758
+ #endif
1759
+ }
1760
+ SIMSIMD_PUBLIC void simsimd_l2sq_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1761
+ simsimd_distance_t *d) {
1762
+ #if SIMSIMD_TARGET_NEON
1763
+ simsimd_l2sq_u8_neon(a, b, n, d);
1764
+ #elif SIMSIMD_TARGET_ICE
1765
+ simsimd_l2sq_u8_ice(a, b, n, d);
1766
+ #elif SIMSIMD_TARGET_HASWELL
1767
+ simsimd_l2sq_u8_haswell(a, b, n, d);
1768
+ #else
1769
+ simsimd_l2sq_u8_serial(a, b, n, d);
1770
+ #endif
1771
+ }
1772
+ SIMSIMD_PUBLIC void simsimd_l2sq_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1773
+ simsimd_distance_t *d) {
1774
+ #if SIMSIMD_TARGET_SVE_F16
1326
1775
  simsimd_l2sq_f16_sve(a, b, n, d);
1327
- #elif SIMSIMD_TARGET_NEON
1776
+ #elif SIMSIMD_TARGET_NEON_F16
1328
1777
  simsimd_l2sq_f16_neon(a, b, n, d);
1329
1778
  #elif SIMSIMD_TARGET_SAPPHIRE
1330
1779
  simsimd_l2sq_f16_sapphire(a, b, n, d);
@@ -1334,18 +1783,22 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f16(simsimd_f16_t const* a, simsimd_f16_t const
1334
1783
  simsimd_l2sq_f16_serial(a, b, n, d);
1335
1784
  #endif
1336
1785
  }
1337
- SIMSIMD_PUBLIC void simsimd_l2sq_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1338
- simsimd_distance_t* d) {
1786
+ SIMSIMD_PUBLIC void simsimd_l2sq_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1787
+ simsimd_distance_t *d) {
1339
1788
  #if SIMSIMD_TARGET_GENOA
1340
1789
  simsimd_l2sq_bf16_genoa(a, b, n, d);
1341
1790
  #elif SIMSIMD_TARGET_HASWELL
1342
1791
  simsimd_l2sq_bf16_haswell(a, b, n, d);
1792
+ #elif SIMSIMD_TARGET_SVE_BF16
1793
+ simsimd_l2sq_bf16_sve(a, b, n, d);
1794
+ #elif SIMSIMD_TARGET_NEON_BF16
1795
+ simsimd_l2sq_bf16_neon(a, b, n, d);
1343
1796
  #else
1344
1797
  simsimd_l2sq_bf16_serial(a, b, n, d);
1345
1798
  #endif
1346
1799
  }
1347
- SIMSIMD_PUBLIC void simsimd_l2sq_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1348
- simsimd_distance_t* d) {
1800
+ SIMSIMD_PUBLIC void simsimd_l2sq_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1801
+ simsimd_distance_t *d) {
1349
1802
  #if SIMSIMD_TARGET_SVE
1350
1803
  simsimd_l2sq_f32_sve(a, b, n, d);
1351
1804
  #elif SIMSIMD_TARGET_NEON
@@ -1358,16 +1811,96 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f32(simsimd_f32_t const* a, simsimd_f32_t const
1358
1811
  simsimd_l2sq_f32_serial(a, b, n, d);
1359
1812
  #endif
1360
1813
  }
1361
- SIMSIMD_PUBLIC void simsimd_l2sq_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1362
- simsimd_distance_t* d) {
1814
+ SIMSIMD_PUBLIC void simsimd_l2sq_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1815
+ simsimd_distance_t *d) {
1363
1816
  #if SIMSIMD_TARGET_SVE
1364
1817
  simsimd_l2sq_f64_sve(a, b, n, d);
1818
+ #elif SIMSIMD_TARGET_NEON
1819
+ simsimd_l2sq_f64_neon(a, b, n, d);
1365
1820
  #elif SIMSIMD_TARGET_SKYLAKE
1366
1821
  simsimd_l2sq_f64_skylake(a, b, n, d);
1367
1822
  #else
1368
1823
  simsimd_l2sq_f64_serial(a, b, n, d);
1369
1824
  #endif
1370
1825
  }
1826
+ SIMSIMD_PUBLIC void simsimd_l2_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1827
+ simsimd_distance_t *d) {
1828
+ #if SIMSIMD_TARGET_NEON
1829
+ simsimd_l2_i8_neon(a, b, n, d);
1830
+ #elif SIMSIMD_TARGET_ICE
1831
+ simsimd_l2_i8_ice(a, b, n, d);
1832
+ #elif SIMSIMD_TARGET_HASWELL
1833
+ simsimd_l2_i8_haswell(a, b, n, d);
1834
+ #else
1835
+ simsimd_l2_i8_serial(a, b, n, d);
1836
+ #endif
1837
+ }
1838
+ SIMSIMD_PUBLIC void simsimd_l2_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1839
+ simsimd_distance_t *d) {
1840
+ #if SIMSIMD_TARGET_NEON
1841
+ simsimd_l2_u8_neon(a, b, n, d);
1842
+ #elif SIMSIMD_TARGET_ICE
1843
+ simsimd_l2_u8_ice(a, b, n, d);
1844
+ #elif SIMSIMD_TARGET_HASWELL
1845
+ simsimd_l2_u8_haswell(a, b, n, d);
1846
+ #else
1847
+ simsimd_l2_u8_serial(a, b, n, d);
1848
+ #endif
1849
+ }
1850
+ SIMSIMD_PUBLIC void simsimd_l2_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1851
+ simsimd_distance_t *d) {
1852
+ #if SIMSIMD_TARGET_SVE_F16
1853
+ simsimd_l2_f16_sve(a, b, n, d);
1854
+ #elif SIMSIMD_TARGET_NEON_F16
1855
+ simsimd_l2_f16_neon(a, b, n, d);
1856
+ #elif SIMSIMD_TARGET_SAPPHIRE
1857
+ simsimd_l2_f16_sapphire(a, b, n, d);
1858
+ #elif SIMSIMD_TARGET_HASWELL
1859
+ simsimd_l2_f16_haswell(a, b, n, d);
1860
+ #else
1861
+ simsimd_l2_f16_serial(a, b, n, d);
1862
+ #endif
1863
+ }
1864
+ SIMSIMD_PUBLIC void simsimd_l2_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1865
+ simsimd_distance_t *d) {
1866
+ #if SIMSIMD_TARGET_GENOA
1867
+ simsimd_l2_bf16_genoa(a, b, n, d);
1868
+ #elif SIMSIMD_TARGET_HASWELL
1869
+ simsimd_l2_bf16_haswell(a, b, n, d);
1870
+ #elif SIMSIMD_TARGET_SVE_BF16
1871
+ simsimd_l2_bf16_sve(a, b, n, d);
1872
+ #elif SIMSIMD_TARGET_NEON_BF16
1873
+ simsimd_l2_bf16_neon(a, b, n, d);
1874
+ #else
1875
+ simsimd_l2_bf16_serial(a, b, n, d);
1876
+ #endif
1877
+ }
1878
+ SIMSIMD_PUBLIC void simsimd_l2_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1879
+ simsimd_distance_t *d) {
1880
+ #if SIMSIMD_TARGET_SVE
1881
+ simsimd_l2_f32_sve(a, b, n, d);
1882
+ #elif SIMSIMD_TARGET_NEON
1883
+ simsimd_l2_f32_neon(a, b, n, d);
1884
+ #elif SIMSIMD_TARGET_SKYLAKE
1885
+ simsimd_l2_f32_skylake(a, b, n, d);
1886
+ #elif SIMSIMD_TARGET_HASWELL
1887
+ simsimd_l2_f32_haswell(a, b, n, d);
1888
+ #else
1889
+ simsimd_l2_f32_serial(a, b, n, d);
1890
+ #endif
1891
+ }
1892
+ SIMSIMD_PUBLIC void simsimd_l2_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1893
+ simsimd_distance_t *d) {
1894
+ #if SIMSIMD_TARGET_SVE
1895
+ simsimd_l2_f64_sve(a, b, n, d);
1896
+ #elif SIMSIMD_TARGET_NEON
1897
+ simsimd_l2_f64_neon(a, b, n, d);
1898
+ #elif SIMSIMD_TARGET_SKYLAKE
1899
+ simsimd_l2_f64_skylake(a, b, n, d);
1900
+ #else
1901
+ simsimd_l2_f64_serial(a, b, n, d);
1902
+ #endif
1903
+ }
1371
1904
 
1372
1905
  /* Binary distances
1373
1906
  * - Hamming distance: the number of positions at which the corresponding bits are different.
@@ -1382,8 +1915,8 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f64(simsimd_f64_t const* a, simsimd_f64_t const
1382
1915
  * @note The output distance value is zero if and only if the two vectors are identical.
1383
1916
  * @note Defined only for binary data.
1384
1917
  */
1385
- SIMSIMD_PUBLIC void simsimd_hamming_b8(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n,
1386
- simsimd_distance_t* d) {
1918
+ SIMSIMD_PUBLIC void simsimd_hamming_b8(simsimd_b8_t const *a, simsimd_b8_t const *b, simsimd_size_t n,
1919
+ simsimd_distance_t *d) {
1387
1920
  #if SIMSIMD_TARGET_SVE
1388
1921
  simsimd_hamming_b8_sve(a, b, n, d);
1389
1922
  #elif SIMSIMD_TARGET_NEON
@@ -1396,8 +1929,8 @@ SIMSIMD_PUBLIC void simsimd_hamming_b8(simsimd_b8_t const* a, simsimd_b8_t const
1396
1929
  simsimd_hamming_b8_serial(a, b, n, d);
1397
1930
  #endif
1398
1931
  }
1399
- SIMSIMD_PUBLIC void simsimd_jaccard_b8(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n,
1400
- simsimd_distance_t* d) {
1932
+ SIMSIMD_PUBLIC void simsimd_jaccard_b8(simsimd_b8_t const *a, simsimd_b8_t const *b, simsimd_size_t n,
1933
+ simsimd_distance_t *d) {
1401
1934
  #if SIMSIMD_TARGET_SVE
1402
1935
  simsimd_jaccard_b8_sve(a, b, n, d);
1403
1936
  #elif SIMSIMD_TARGET_NEON
@@ -1415,9 +1948,9 @@ SIMSIMD_PUBLIC void simsimd_jaccard_b8(simsimd_b8_t const* a, simsimd_b8_t const
1415
1948
  * - Jensen-Shannon divergence: a measure of similarity between two probability distributions.
1416
1949
  * - Kullback-Leibler divergence: a measure of how one probability distribution diverges from a second.
1417
1950
  *
1418
- * @param a The first descrete probability distribution.
1419
- * @param b The second descrete probability distribution.
1420
- * @param n The number of elements in the descrete distributions.
1951
+ * @param a The first discrete probability distribution.
1952
+ * @param b The second discrete probability distribution.
1953
+ * @param n The number of elements in the discrete distributions.
1421
1954
  * @param d The output divergence value.
1422
1955
  *
1423
1956
  * @note The distributions are assumed to be normalized.
@@ -1425,8 +1958,8 @@ SIMSIMD_PUBLIC void simsimd_jaccard_b8(simsimd_b8_t const* a, simsimd_b8_t const
1425
1958
  * @note The output divergence value is zero if and only if the two distributions are identical.
1426
1959
  * @note Defined only for floating-point data types.
1427
1960
  */
1428
- SIMSIMD_PUBLIC void simsimd_kl_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1429
- simsimd_distance_t* d) {
1961
+ SIMSIMD_PUBLIC void simsimd_kl_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1962
+ simsimd_distance_t *d) {
1430
1963
  #if SIMSIMD_TARGET_NEON
1431
1964
  simsimd_kl_f16_neon(a, b, n, d);
1432
1965
  #elif SIMSIMD_TARGET_HASWELL
@@ -1435,12 +1968,12 @@ SIMSIMD_PUBLIC void simsimd_kl_f16(simsimd_f16_t const* a, simsimd_f16_t const*
1435
1968
  simsimd_kl_f16_serial(a, b, n, d);
1436
1969
  #endif
1437
1970
  }
1438
- SIMSIMD_PUBLIC void simsimd_kl_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1439
- simsimd_distance_t* d) {
1971
+ SIMSIMD_PUBLIC void simsimd_kl_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1972
+ simsimd_distance_t *d) {
1440
1973
  simsimd_kl_bf16_serial(a, b, n, d);
1441
1974
  }
1442
- SIMSIMD_PUBLIC void simsimd_kl_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1443
- simsimd_distance_t* d) {
1975
+ SIMSIMD_PUBLIC void simsimd_kl_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1976
+ simsimd_distance_t *d) {
1444
1977
  #if SIMSIMD_TARGET_NEON
1445
1978
  simsimd_kl_f32_neon(a, b, n, d);
1446
1979
  #elif SIMSIMD_TARGET_SKYLAKE
@@ -1449,12 +1982,12 @@ SIMSIMD_PUBLIC void simsimd_kl_f32(simsimd_f32_t const* a, simsimd_f32_t const*
1449
1982
  simsimd_kl_f32_serial(a, b, n, d);
1450
1983
  #endif
1451
1984
  }
1452
- SIMSIMD_PUBLIC void simsimd_kl_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1453
- simsimd_distance_t* d) {
1985
+ SIMSIMD_PUBLIC void simsimd_kl_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1986
+ simsimd_distance_t *d) {
1454
1987
  simsimd_kl_f64_serial(a, b, n, d);
1455
1988
  }
1456
- SIMSIMD_PUBLIC void simsimd_js_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1457
- simsimd_distance_t* d) {
1989
+ SIMSIMD_PUBLIC void simsimd_js_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1990
+ simsimd_distance_t *d) {
1458
1991
  #if SIMSIMD_TARGET_NEON
1459
1992
  simsimd_js_f16_neon(a, b, n, d);
1460
1993
  #elif SIMSIMD_TARGET_HASWELL
@@ -1463,12 +1996,12 @@ SIMSIMD_PUBLIC void simsimd_js_f16(simsimd_f16_t const* a, simsimd_f16_t const*
1463
1996
  simsimd_js_f16_serial(a, b, n, d);
1464
1997
  #endif
1465
1998
  }
1466
- SIMSIMD_PUBLIC void simsimd_js_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1467
- simsimd_distance_t* d) {
1999
+ SIMSIMD_PUBLIC void simsimd_js_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
2000
+ simsimd_distance_t *d) {
1468
2001
  simsimd_js_bf16_serial(a, b, n, d);
1469
2002
  }
1470
- SIMSIMD_PUBLIC void simsimd_js_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1471
- simsimd_distance_t* d) {
2003
+ SIMSIMD_PUBLIC void simsimd_js_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
2004
+ simsimd_distance_t *d) {
1472
2005
  #if SIMSIMD_TARGET_NEON
1473
2006
  simsimd_js_f32_neon(a, b, n, d);
1474
2007
  #elif SIMSIMD_TARGET_SKYLAKE
@@ -1477,11 +2010,337 @@ SIMSIMD_PUBLIC void simsimd_js_f32(simsimd_f32_t const* a, simsimd_f32_t const*
1477
2010
  simsimd_js_f32_serial(a, b, n, d);
1478
2011
  #endif
1479
2012
  }
1480
- SIMSIMD_PUBLIC void simsimd_js_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1481
- simsimd_distance_t* d) {
2013
+ SIMSIMD_PUBLIC void simsimd_js_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
2014
+ simsimd_distance_t *d) {
1482
2015
  simsimd_js_f64_serial(a, b, n, d);
1483
2016
  }
1484
2017
 
2018
+ /* Set operations
2019
+ *
2020
+ * @param a The first sorted array of integers.
2021
+ * @param b The second sorted array of integers.
2022
+ * @param a_length The number of elements in the first array.
2023
+ * @param b_length The number of elements in the second array.
2024
+ * @param d The output for the number of elements in the intersection.
2025
+ */
2026
+ SIMSIMD_PUBLIC void simsimd_intersect_u16(simsimd_u16_t const *a, simsimd_u16_t const *b, simsimd_size_t a_length,
2027
+ simsimd_size_t b_length, simsimd_distance_t *d) {
2028
+ #if SIMSIMD_TARGET_SVE2
2029
+ simsimd_intersect_u16_sve2(a, b, a_length, b_length, d);
2030
+ #elif SIMSIMD_TARGET_NEON
2031
+ simsimd_intersect_u16_neon(a, b, a_length, b_length, d);
2032
+ #elif SIMSIMD_TARGET_TURIN
2033
+ simsimd_intersect_u16_turin(a, b, a_length, b_length, d);
2034
+ #elif SIMSIMD_TARGET_ICE
2035
+ simsimd_intersect_u16_ice(a, b, a_length, b_length, d);
2036
+ #else
2037
+ simsimd_intersect_u16_serial(a, b, a_length, b_length, d);
2038
+ #endif
2039
+ }
2040
+
2041
+ SIMSIMD_PUBLIC void simsimd_intersect_u32(simsimd_u32_t const *a, simsimd_u32_t const *b, simsimd_size_t a_length,
2042
+ simsimd_size_t b_length, simsimd_distance_t *d) {
2043
+ #if SIMSIMD_TARGET_SVE2
2044
+ simsimd_intersect_u32_sve2(a, b, a_length, b_length, d);
2045
+ #elif SIMSIMD_TARGET_NEON
2046
+ simsimd_intersect_u32_neon(a, b, a_length, b_length, d);
2047
+ #elif SIMSIMD_TARGET_TURIN
2048
+ simsimd_intersect_u32_turin(a, b, a_length, b_length, d);
2049
+ #elif SIMSIMD_TARGET_ICE
2050
+ simsimd_intersect_u32_ice(a, b, a_length, b_length, d);
2051
+ #else
2052
+ simsimd_intersect_u32_serial(a, b, a_length, b_length, d);
2053
+ #endif
2054
+ }
2055
+
2056
+ /* Weighted set operations
2057
+ *
2058
+ * @param a The first sorted array of integers.
2059
+ * @param b The second sorted array of integers.
2060
+ * @param a_weights The weights for the first array.
2061
+ * @param b_weights The weights for the second array.
2062
+ * @param a_length The number of elements in the first array.
2063
+ * @param b_length The number of elements in the second array.
2064
+ * @param d The output for the number of elements in the intersection.
2065
+ */
2066
+ SIMSIMD_PUBLIC void simsimd_spdot_counts_u16(simsimd_u16_t const *a, simsimd_u16_t const *b,
2067
+ simsimd_i16_t const *a_weights, simsimd_i16_t const *b_weights,
2068
+ simsimd_size_t a_length, simsimd_size_t b_length, simsimd_distance_t *d) {
2069
+ #if SIMSIMD_TARGET_SVE2
2070
+ simsimd_spdot_counts_u16_sve2(a, b, a_weights, b_weights, a_length, b_length, d);
2071
+ #elif SIMSIMD_TARGET_TURIN
2072
+ simsimd_spdot_counts_u16_turin(a, b, a_weights, b_weights, a_length, b_length, d);
2073
+ #else
2074
+ simsimd_spdot_counts_u16_serial(a, b, a_weights, b_weights, a_length, b_length, d);
2075
+ #endif
2076
+ }
2077
+
2078
+ SIMSIMD_PUBLIC void simsimd_spdot_weights_u16(simsimd_u16_t const *a, simsimd_u16_t const *b,
2079
+ simsimd_bf16_t const *a_weights, simsimd_bf16_t const *b_weights,
2080
+ simsimd_size_t a_length, simsimd_size_t b_length, simsimd_distance_t *d) {
2081
+ #if SIMSIMD_TARGET_SVE2
2082
+ simsimd_spdot_weights_u16_sve2(a, b, a_weights, b_weights, a_length, b_length, d);
2083
+ #elif SIMSIMD_TARGET_TURIN
2084
+ simsimd_spdot_weights_u16_turin(a, b, a_weights, b_weights, a_length, b_length, d);
2085
+ #else
2086
+ simsimd_spdot_weights_u16_serial(a, b, a_weights, b_weights, a_length, b_length, d);
2087
+ #endif
2088
+ }
2089
+
2090
+ /* Curved space distances
2091
+ *
2092
+ * @param a The first vector of floating point values.
2093
+ * @param b The second vector of floating point values.
2094
+ * @param c The metric tensor or covariance matrix.
2095
+ * @param n The number of dimensions in the vectors.
2096
+ * @param d The output for the number of elements in the intersection.
2097
+ */
2098
+ SIMSIMD_PUBLIC void simsimd_bilinear_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_f64_t const *c,
2099
+ simsimd_size_t n, simsimd_distance_t *d) {
2100
+
2101
+ simsimd_bilinear_f64_serial(a, b, c, n, d);
2102
+ }
2103
+ SIMSIMD_PUBLIC void simsimd_mahalanobis_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_f64_t const *c,
2104
+ simsimd_size_t n, simsimd_distance_t *d) {
2105
+ simsimd_mahalanobis_f64_serial(a, b, c, n, d);
2106
+ }
2107
+ SIMSIMD_PUBLIC void simsimd_bilinear_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_f32_t const *c,
2108
+ simsimd_size_t n, simsimd_distance_t *d) {
2109
+ #if SIMSIMD_TARGET_SKYLAKE
2110
+ simsimd_bilinear_f32_skylake(a, b, c, n, d);
2111
+ #elif SIMSIMD_TARGET_NEON
2112
+ simsimd_bilinear_f32_neon(a, b, c, n, d);
2113
+ #else
2114
+ simsimd_bilinear_f32_serial(a, b, c, n, d);
2115
+ #endif
2116
+ }
2117
+ SIMSIMD_PUBLIC void simsimd_mahalanobis_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_f32_t const *c,
2118
+ simsimd_size_t n, simsimd_distance_t *d) {
2119
+ #if SIMSIMD_TARGET_SKYLAKE
2120
+ simsimd_mahalanobis_f32_skylake(a, b, c, n, d);
2121
+ #elif SIMSIMD_TARGET_NEON
2122
+ simsimd_mahalanobis_f32_neon(a, b, c, n, d);
2123
+ #else
2124
+ simsimd_mahalanobis_f32_serial(a, b, c, n, d);
2125
+ #endif
2126
+ }
2127
+ SIMSIMD_PUBLIC void simsimd_bilinear_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_f16_t const *c,
2128
+ simsimd_size_t n, simsimd_distance_t *d) {
2129
+ #if SIMSIMD_TARGET_SAPPHIRE
2130
+ simsimd_bilinear_f16_sapphire(a, b, c, n, d);
2131
+ #elif SIMSIMD_TARGET_HASWELL
2132
+ simsimd_bilinear_f16_haswell(a, b, c, n, d);
2133
+ #elif SIMSIMD_TARGET_NEON
2134
+ simsimd_bilinear_f16_neon(a, b, c, n, d);
2135
+ #else
2136
+ simsimd_bilinear_f16_serial(a, b, c, n, d);
2137
+ #endif
2138
+ }
2139
+ SIMSIMD_PUBLIC void simsimd_mahalanobis_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_f16_t const *c,
2140
+ simsimd_size_t n, simsimd_distance_t *d) {
2141
+ #if SIMSIMD_TARGET_SAPPHIRE
2142
+ simsimd_mahalanobis_f16_sapphire(a, b, c, n, d);
2143
+ #elif SIMSIMD_TARGET_HASWELL
2144
+ simsimd_mahalanobis_f16_haswell(a, b, c, n, d);
2145
+ #elif SIMSIMD_TARGET_NEON
2146
+ simsimd_mahalanobis_f16_neon(a, b, c, n, d);
2147
+ #else
2148
+ simsimd_mahalanobis_f16_serial(a, b, c, n, d);
2149
+ #endif
2150
+ }
2151
+ SIMSIMD_PUBLIC void simsimd_bilinear_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_bf16_t const *c,
2152
+ simsimd_size_t n, simsimd_distance_t *d) {
2153
+ #if SIMSIMD_TARGET_GENOA
2154
+ simsimd_bilinear_bf16_genoa(a, b, c, n, d);
2155
+ #elif SIMSIMD_TARGET_HASWELL
2156
+ simsimd_bilinear_bf16_haswell(a, b, c, n, d);
2157
+ #elif SIMSIMD_TARGET_NEON
2158
+ simsimd_bilinear_bf16_neon(a, b, c, n, d);
2159
+ #else
2160
+ simsimd_bilinear_bf16_serial(a, b, c, n, d);
2161
+ #endif
2162
+ }
2163
+ SIMSIMD_PUBLIC void simsimd_mahalanobis_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_bf16_t const *c,
2164
+ simsimd_size_t n, simsimd_distance_t *d) {
2165
+ #if SIMSIMD_TARGET_GENOA
2166
+ simsimd_mahalanobis_bf16_genoa(a, b, c, n, d);
2167
+ #elif SIMSIMD_TARGET_HASWELL
2168
+ simsimd_mahalanobis_bf16_haswell(a, b, c, n, d);
2169
+ #elif SIMSIMD_TARGET_NEON
2170
+ simsimd_mahalanobis_bf16_neon(a, b, c, n, d);
2171
+ #else
2172
+ simsimd_mahalanobis_bf16_serial(a, b, c, n, d);
2173
+ #endif
2174
+ }
2175
+
2176
+ /* Elementwise operations
2177
+ *
2178
+ * @param a The first vector of integral or floating point values.
2179
+ * @param b The second vector of integral or floating point values.
2180
+ * @param c The third vector of integral or floating point values.
2181
+ * @param n The number of dimensions in the vectors.
2182
+ * @param alpha The first scaling factor.
2183
+ * @param beta The first scaling factor.
2184
+ * @param r The output vector or integral or floating point values.
2185
+ */
2186
+ SIMSIMD_PUBLIC void simsimd_wsum_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
2187
+ simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_f64_t *r) {
2188
+ #if SIMSIMD_TARGET_SKYLAKE
2189
+ simsimd_wsum_f64_skylake(a, b, n, alpha, beta, r);
2190
+ #elif SIMSIMD_TARGET_HASWELL
2191
+ simsimd_wsum_f64_haswell(a, b, n, alpha, beta, r);
2192
+ #else
2193
+ simsimd_wsum_f64_serial(a, b, n, alpha, beta, r);
2194
+ #endif
2195
+ }
2196
+
2197
+ SIMSIMD_PUBLIC void simsimd_wsum_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
2198
+ simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_f32_t *r) {
2199
+ #if SIMSIMD_TARGET_SKYLAKE
2200
+ simsimd_wsum_f32_skylake(a, b, n, alpha, beta, r);
2201
+ #elif SIMSIMD_TARGET_HASWELL
2202
+ simsimd_wsum_f32_haswell(a, b, n, alpha, beta, r);
2203
+ #elif SIMSIMD_TARGET_NEON
2204
+ simsimd_wsum_f32_neon(a, b, n, alpha, beta, r);
2205
+ #else
2206
+ simsimd_wsum_f32_serial(a, b, n, alpha, beta, r);
2207
+ #endif
2208
+ }
2209
+
2210
+ SIMSIMD_PUBLIC void simsimd_wsum_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
2211
+ simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_bf16_t *r) {
2212
+ #if SIMSIMD_TARGET_SKYLAKE
2213
+ simsimd_wsum_bf16_skylake(a, b, n, alpha, beta, r);
2214
+ #elif SIMSIMD_TARGET_HASWELL
2215
+ simsimd_wsum_bf16_haswell(a, b, n, alpha, beta, r);
2216
+ #elif SIMSIMD_TARGET_NEON
2217
+ simsimd_wsum_bf16_neon(a, b, n, alpha, beta, r);
2218
+ #else
2219
+ simsimd_wsum_bf16_serial(a, b, n, alpha, beta, r);
2220
+ #endif
2221
+ }
2222
+
2223
+ SIMSIMD_PUBLIC void simsimd_wsum_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
2224
+ simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_f16_t *r) {
2225
+ #if SIMSIMD_TARGET_SAPPHIRE
2226
+ simsimd_wsum_f16_sapphire(a, b, n, alpha, beta, r);
2227
+ #elif SIMSIMD_TARGET_HASWELL
2228
+ simsimd_wsum_f16_haswell(a, b, n, alpha, beta, r);
2229
+ #elif SIMSIMD_TARGET_NEON_F16
2230
+ simsimd_wsum_f16_neon(a, b, n, alpha, beta, r);
2231
+ #else
2232
+ simsimd_wsum_f16_serial(a, b, n, alpha, beta, r);
2233
+ #endif
2234
+ }
2235
+
2236
+ SIMSIMD_PUBLIC void simsimd_wsum_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
2237
+ simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_i8_t *r) {
2238
+ #if SIMSIMD_TARGET_SAPPHIRE
2239
+ simsimd_wsum_i8_sapphire(a, b, n, alpha, beta, r);
2240
+ #elif SIMSIMD_TARGET_HASWELL
2241
+ simsimd_wsum_i8_haswell(a, b, n, alpha, beta, r);
2242
+ #elif SIMSIMD_TARGET_NEON_F16
2243
+ simsimd_wsum_i8_neon(a, b, n, alpha, beta, r);
2244
+ #else
2245
+ simsimd_wsum_i8_serial(a, b, n, alpha, beta, r);
2246
+ #endif
2247
+ }
2248
+
2249
+ SIMSIMD_PUBLIC void simsimd_wsum_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
2250
+ simsimd_distance_t alpha, simsimd_distance_t beta, simsimd_u8_t *r) {
2251
+ #if SIMSIMD_TARGET_SAPPHIRE
2252
+ simsimd_wsum_u8_sapphire(a, b, n, alpha, beta, r);
2253
+ #elif SIMSIMD_TARGET_HASWELL
2254
+ simsimd_wsum_u8_haswell(a, b, n, alpha, beta, r);
2255
+ #elif SIMSIMD_TARGET_NEON_F16
2256
+ simsimd_wsum_u8_neon(a, b, n, alpha, beta, r);
2257
+ #else
2258
+ simsimd_wsum_u8_serial(a, b, n, alpha, beta, r);
2259
+ #endif
2260
+ }
2261
+
2262
+ SIMSIMD_PUBLIC void simsimd_fma_f64(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_f64_t const *c,
2263
+ simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta,
2264
+ simsimd_f64_t *r) {
2265
+ #if SIMSIMD_TARGET_SKYLAKE
2266
+ simsimd_fma_f64_skylake(a, b, c, n, alpha, beta, r);
2267
+ #elif SIMSIMD_TARGET_HASWELL
2268
+ simsimd_fma_f64_haswell(a, b, c, n, alpha, beta, r);
2269
+ #else
2270
+ simsimd_fma_f64_serial(a, b, c, n, alpha, beta, r);
2271
+ #endif
2272
+ }
2273
+
2274
+ SIMSIMD_PUBLIC void simsimd_fma_f32(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_f32_t const *c,
2275
+ simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta,
2276
+ simsimd_f32_t *r) {
2277
+ #if SIMSIMD_TARGET_SKYLAKE
2278
+ simsimd_fma_f32_skylake(a, b, c, n, alpha, beta, r);
2279
+ #elif SIMSIMD_TARGET_HASWELL
2280
+ simsimd_fma_f32_haswell(a, b, c, n, alpha, beta, r);
2281
+ #elif SIMSIMD_TARGET_NEON
2282
+ simsimd_fma_f32_neon(a, b, c, n, alpha, beta, r);
2283
+ #else
2284
+ simsimd_fma_f32_serial(a, b, c, n, alpha, beta, r);
2285
+ #endif
2286
+ }
2287
+
2288
+ SIMSIMD_PUBLIC void simsimd_fma_bf16(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_bf16_t const *c,
2289
+ simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta,
2290
+ simsimd_bf16_t *r) {
2291
+ #if SIMSIMD_TARGET_SKYLAKE
2292
+ simsimd_fma_bf16_skylake(a, b, c, n, alpha, beta, r);
2293
+ #elif SIMSIMD_TARGET_HASWELL
2294
+ simsimd_fma_bf16_haswell(a, b, c, n, alpha, beta, r);
2295
+ #elif SIMSIMD_TARGET_NEON
2296
+ simsimd_fma_bf16_neon(a, b, c, n, alpha, beta, r);
2297
+ #else
2298
+ simsimd_fma_bf16_serial(a, b, c, n, alpha, beta, r);
2299
+ #endif
2300
+ }
2301
+
2302
+ SIMSIMD_PUBLIC void simsimd_fma_f16(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_f16_t const *c,
2303
+ simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta,
2304
+ simsimd_f16_t *r) {
2305
+ #if SIMSIMD_TARGET_SAPPHIRE
2306
+ simsimd_fma_f16_sapphire(a, b, c, n, alpha, beta, r);
2307
+ #elif SIMSIMD_TARGET_HASWELL
2308
+ simsimd_fma_f16_haswell(a, b, c, n, alpha, beta, r);
2309
+ #elif SIMSIMD_TARGET_NEON_F16
2310
+ simsimd_fma_f16_neon(a, b, c, n, alpha, beta, r);
2311
+ #else
2312
+ simsimd_fma_f16_serial(a, b, c, n, alpha, beta, r);
2313
+ #endif
2314
+ }
2315
+
2316
+ SIMSIMD_PUBLIC void simsimd_fma_i8(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_i8_t const *c,
2317
+ simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta,
2318
+ simsimd_i8_t *r) {
2319
+ #if SIMSIMD_TARGET_SAPPHIRE
2320
+ simsimd_fma_i8_sapphire(a, b, c, n, alpha, beta, r);
2321
+ #elif SIMSIMD_TARGET_HASWELL
2322
+ simsimd_fma_i8_haswell(a, b, c, n, alpha, beta, r);
2323
+ #elif SIMSIMD_TARGET_NEON_F16
2324
+ simsimd_fma_i8_neon(a, b, c, n, alpha, beta, r);
2325
+ #else
2326
+ simsimd_fma_i8_serial(a, b, c, n, alpha, beta, r);
2327
+ #endif
2328
+ }
2329
+
2330
+ SIMSIMD_PUBLIC void simsimd_fma_u8(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_u8_t const *c,
2331
+ simsimd_size_t n, simsimd_distance_t alpha, simsimd_distance_t beta,
2332
+ simsimd_u8_t *r) {
2333
+ #if SIMSIMD_TARGET_SAPPHIRE
2334
+ simsimd_fma_u8_sapphire(a, b, c, n, alpha, beta, r);
2335
+ #elif SIMSIMD_TARGET_HASWELL
2336
+ simsimd_fma_u8_haswell(a, b, c, n, alpha, beta, r);
2337
+ #elif SIMSIMD_TARGET_NEON_F16
2338
+ simsimd_fma_u8_neon(a, b, c, n, alpha, beta, r);
2339
+ #else
2340
+ simsimd_fma_u8_serial(a, b, c, n, alpha, beta, r);
2341
+ #endif
2342
+ }
2343
+
1485
2344
  #endif
1486
2345
 
1487
2346
  #ifdef __cplusplus