usearch 2.15.1 → 2.15.3

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.
@@ -44,7 +44,7 @@
44
44
  * @section Choosing Arm Target Generations
45
45
  *
46
46
  * 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.
47
+ * even more fragmented than x86. There are 2 important families of SIMD extensions - NEON and SVE.
48
48
  *
49
49
  * - Armv8-A: +fp, +simd
50
50
  * - Armv8.1-A: armv8-a, +crc, +lse, +rdma
@@ -68,7 +68,7 @@
68
68
  * Here are the most important recent families of CPU cores designed by Arm:
69
69
  *
70
70
  * - Neoverse N1: armv8.2-a, extended with Armv8.4 "dotprod" instructions.
71
- * Used in AWS @b Graviton2 and Amere @b Altra.
71
+ * Used in AWS @b Graviton2 and Ampere @b Altra.
72
72
  * https://developer.arm.com/Processors/Neoverse%20N1
73
73
  * - Neoverse V1: armv8.4-a, extended with Armv8.6 bfloat/int8 "matmul" instructions.
74
74
  * Used in AWS @b Graviton3, which also enables `sve`, `svebf16`, and `svei8mm`.
@@ -87,8 +87,8 @@
87
87
  #define SIMSIMD_H
88
88
 
89
89
  #define SIMSIMD_VERSION_MAJOR 5
90
- #define SIMSIMD_VERSION_MINOR 0
91
- #define SIMSIMD_VERSION_PATCH 0
90
+ #define SIMSIMD_VERSION_MINOR 6
91
+ #define SIMSIMD_VERSION_PATCH 4
92
92
 
93
93
  /**
94
94
  * @brief Removes compile-time dispatching, and replaces it with runtime dispatching.
@@ -101,9 +101,11 @@
101
101
  #endif
102
102
 
103
103
  #include "binary.h" // Hamming, Jaccard
104
+ #include "curved.h" // Mahalanobis, Bilinear Forms
104
105
  #include "dot.h" // Inner (dot) product, and its conjugate
105
106
  #include "geospatial.h" // Haversine and Vincenty
106
107
  #include "probability.h" // Kullback-Leibler, Jensen–Shannon
108
+ #include "sparse.h" // Intersect
107
109
  #include "spatial.h" // L2, Cosine
108
110
 
109
111
  // On Apple Silicon, `mrs` is not allowed in user-space, so we need to use the `sysctl` API.
@@ -132,6 +134,8 @@ typedef enum {
132
134
  simsimd_metric_cosine_k = 'c', ///< Cosine similarity alias
133
135
  simsimd_metric_angular_k = 'c', ///< Cosine similarity alias
134
136
 
137
+ simsimd_metric_l2_k = '2', ///< Euclidean distance alias
138
+ simsimd_metric_euclidean_k = '2', ///< Euclidean distance alias
135
139
  simsimd_metric_l2sq_k = 'e', ///< Squared Euclidean distance
136
140
  simsimd_metric_sqeuclidean_k = 'e', ///< Squared Euclidean distance alias
137
141
 
@@ -142,6 +146,13 @@ typedef enum {
142
146
  simsimd_metric_jaccard_k = 'j', ///< Jaccard coefficient
143
147
  simsimd_metric_tanimoto_k = 'j', ///< Tanimoto coefficient is same as Jaccard
144
148
 
149
+ // Sets:
150
+ simsimd_metric_intersect_k = 'x', ///< Equivalent to unnormalized Jaccard
151
+
152
+ // Curved Spaces:
153
+ simsimd_metric_bilinear_k = 'b', ///< Bilinear form
154
+ simsimd_metric_mahalanobis_k = 'm', ///< Mahalanobis distance
155
+
145
156
  // Probability:
146
157
  simsimd_metric_kl_k = 'k', ///< Kullback-Leibler divergence
147
158
  simsimd_metric_kullback_leibler_k = 'k', ///< Kullback-Leibler divergence alias
@@ -164,14 +175,16 @@ typedef enum {
164
175
  simsimd_cap_genoa_k = 1 << 13, ///< x86 AVX512 capability with `bf16` support
165
176
  simsimd_cap_sapphire_k = 1 << 14, ///< x86 AVX512 capability with `f16` support
166
177
 
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
178
+ simsimd_cap_neon_k = 1 << 20, ///< ARM NEON baseline capability
179
+ simsimd_cap_neon_f16_k = 1 << 21, ///< ARM NEON `f16` capability
180
+ simsimd_cap_neon_bf16_k = 1 << 22, ///< ARM NEON `bf16` capability
181
+ simsimd_cap_neon_i8_k = 1 << 23, ///< ARM NEON `i8` capability
182
+ simsimd_cap_sve_k = 1 << 24, ///< ARM SVE baseline capability
183
+ simsimd_cap_sve_f16_k = 1 << 25, ///< ARM SVE `f16` capability
184
+ simsimd_cap_sve_bf16_k = 1 << 26, ///< ARM SVE `bf16` capability
185
+ simsimd_cap_sve_i8_k = 1 << 27, ///< ARM SVE `i8` capability
186
+ simsimd_cap_sve2_k = 1 << 28, ///< ARM SVE2 capability
187
+ simsimd_cap_sve2p1_k = 1 << 29, ///< ARM SVE2p1 capability
175
188
 
176
189
  } simsimd_capability_t;
177
190
 
@@ -184,9 +197,17 @@ typedef enum {
184
197
  */
185
198
  typedef enum {
186
199
  simsimd_datatype_unknown_k = 0, ///< Unknown data type
187
-
188
200
  simsimd_datatype_b8_k = 1 << 1, ///< Single-bit values packed into 8-bit words
189
- simsimd_datatype_i8_k = 1 << 2, ///< 8-bit integer
201
+
202
+ simsimd_datatype_i8_k = 1 << 2, ///< 8-bit signed integer
203
+ simsimd_datatype_i16_k = 1 << 3, ///< 16-bit signed integer
204
+ simsimd_datatype_i32_k = 1 << 4, ///< 32-bit signed integer
205
+ simsimd_datatype_i64_k = 1 << 5, ///< 64-bit signed integer
206
+
207
+ simsimd_datatype_u8_k = 1 << 6, ///< 8-bit unsigned integer
208
+ simsimd_datatype_u16_k = 1 << 7, ///< 16-bit unsigned integer
209
+ simsimd_datatype_u32_k = 1 << 8, ///< 32-bit unsigned integer
210
+ simsimd_datatype_u64_k = 1 << 9, ///< 64-bit unsigned integer
190
211
 
191
212
  simsimd_datatype_f64_k = 1 << 10, ///< Double precision floating point
192
213
  simsimd_datatype_f32_k = 1 << 11, ///< Single precision floating point
@@ -200,21 +221,67 @@ typedef enum {
200
221
  } simsimd_datatype_t;
201
222
 
202
223
  /**
203
- * @brief Type-punned function pointer accepting two vectors and outputting their similarity/distance.
224
+ * @brief Type-punned function pointer for dense vector representations and simplest similarity measures.
225
+ *
226
+ * @param[in] a Pointer to the first data array.
227
+ * @param[in] b Pointer to the second data array.
228
+ * @param[in] n Number of scalar words in the input arrays.
229
+ * When dealing with sub-byte data types, the number of scalar words is the number of bytes.
230
+ * @param[out] d Output value as a double-precision float.
231
+ * In complex dot-products @b two scalars are exported for the real and imaginary parts.
232
+ */
233
+ typedef void (*simsimd_metric_dense_punned_t)(void const* a, void const* b, simsimd_size_t n, simsimd_distance_t* d);
234
+
235
+ /**
236
+ * @brief Type-punned function pointer for sparse vector representations and similarity measures.
237
+ *
238
+ * @param[in] a Pointer to the first data array, generally a sorted array of integers.
239
+ * @param[in] b Pointer to the second data array, generally a sorted array of integers.
240
+ * @param[in] a_length Number of scalar words in the first input array.
241
+ * @param[in] b_length Number of scalar words in the second input array.
242
+ * @param[out] d Output value as a double-precision float, generally without decimals.
243
+ */
244
+ typedef void (*simsimd_metric_sparse_punned_t)(void const* a, void const* b, //
245
+ simsimd_size_t a_length, simsimd_size_t b_length, //
246
+ simsimd_distance_t* d);
247
+
248
+ /**
249
+ * @brief Type-punned function pointer for curved vector spaces and similarity measures.
204
250
  *
205
251
  * @param[in] a Pointer to the first data array.
206
252
  * @param[in] b Pointer to the second data array.
253
+ * @param[in] c Pointer to the metric tensor array or some covariance matrix.
207
254
  * @param[in] n Number of scalar words in the input arrays.
208
255
  * @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.
211
256
  */
212
- typedef void (*simsimd_metric_punned_t)(void const* a, void const* b, simsimd_size_t n, simsimd_distance_t* d);
257
+ typedef void (*simsimd_metric_curved_punned_t)(void const* a, void const* b, void const* c, //
258
+ simsimd_size_t n, simsimd_distance_t* d);
259
+
260
+ /**
261
+ * @brief Type-punned function pointer for a SimSIMD public interface.
262
+ * Can be a `simsimd_metric_dense_punned_t`, `simsimd_metric_sparse_punned_t`,
263
+ * or `simsimd_metric_curved_punned_t`.
264
+ */
265
+ typedef simsimd_metric_dense_punned_t simsimd_metric_punned_t;
213
266
 
214
267
  #if SIMSIMD_DYNAMIC_DISPATCH
215
268
  SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
269
+ SIMSIMD_DYNAMIC void simsimd_find_metric_punned( //
270
+ simsimd_metric_kind_t kind, //
271
+ simsimd_datatype_t datatype, //
272
+ simsimd_capability_t supported, //
273
+ simsimd_capability_t allowed, //
274
+ simsimd_metric_punned_t* metric_output, //
275
+ simsimd_capability_t* capability_output);
216
276
  #else
217
277
  SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void);
278
+ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
279
+ simsimd_metric_kind_t kind, //
280
+ simsimd_datatype_t datatype, //
281
+ simsimd_capability_t supported, //
282
+ simsimd_capability_t allowed, //
283
+ simsimd_metric_punned_t* metric_output, //
284
+ simsimd_capability_t* capability_output);
218
285
  #endif
219
286
 
220
287
  #if SIMSIMD_TARGET_X86
@@ -223,7 +290,7 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void);
223
290
  * @brief Function to determine the SIMD capabilities of the current 64-bit x86 machine at @b runtime.
224
291
  * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
225
292
  */
226
- SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_x86(void) {
293
+ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_x86(void) {
227
294
 
228
295
  /// The states of 4 registers populated for a specific "cpuid" assembly call
229
296
  union four_registers_t {
@@ -300,7 +367,7 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_x86(void) {
300
367
 
301
368
  /* Compiling the next section one may get: selected processor does not support system register name 'id_aa64zfr0_el1'.
302
369
  * Suppressing assembler errors is very complicated, so when dealing with older ARM CPUs it's simpler to compile this
303
- * function targetting newer ones.
370
+ * function targeting newer ones.
304
371
  */
305
372
  #pragma GCC push_options
306
373
  #pragma GCC target("arch=armv8.5-a+sve")
@@ -310,7 +377,7 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_x86(void) {
310
377
  * @brief Function to determine the SIMD capabilities of the current 64-bit Arm machine at @b runtime.
311
378
  * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
312
379
  */
313
- SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_arm(void) {
380
+ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_arm(void) {
314
381
  #if defined(SIMSIMD_DEFINED_APPLE)
315
382
  // On Apple Silicon, `mrs` is not allowed in user-space, so we need to use the `sysctl` API.
316
383
  uint32_t supports_neon = 0, supports_fp16 = 0, supports_bf16 = 0, supports_i8mm = 0;
@@ -388,8 +455,8 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_arm(void) {
388
455
  // - 0b0001: SVE2 is implemented
389
456
  // - 0b0010: SVE2.1 is implemented
390
457
  // 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;
458
+ unsigned supports_sve2 = ((id_aa64zfr0_el1) & 0xF) >= 1;
459
+ unsigned supports_sve2p1 = ((id_aa64zfr0_el1) & 0xF) >= 2;
393
460
  unsigned supports_neon = 1; // NEON is always supported
394
461
 
395
462
  return (simsimd_capability_t)( //
@@ -401,6 +468,8 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_arm(void) {
401
468
  (simsimd_cap_sve_f16_k * (supports_sve && supports_fp16)) | //
402
469
  (simsimd_cap_sve_bf16_k * (supports_sve && supports_sve_bf16)) | //
403
470
  (simsimd_cap_sve_i8_k * (supports_sve && supports_sve_i8mm)) | //
471
+ (simsimd_cap_sve2_k * (supports_sve2)) | //
472
+ (simsimd_cap_sve2p1_k * (supports_sve2p1)) | //
404
473
  (simsimd_cap_serial_k));
405
474
  #else // SIMSIMD_DEFINED_LINUX
406
475
  return simsimd_cap_serial_k;
@@ -409,18 +478,19 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_arm(void) {
409
478
 
410
479
  #pragma clang attribute pop
411
480
  #pragma GCC pop_options
481
+
412
482
  #endif
413
483
 
414
484
  /**
415
485
  * @brief Function to determine the SIMD capabilities of the current 64-bit x86 machine at @b runtime.
416
486
  * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
417
487
  */
418
- SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_implementation(void) {
488
+ SIMSIMD_PUBLIC simsimd_capability_t _simsimd_capabilities_implementation(void) {
419
489
  #if SIMSIMD_TARGET_X86
420
- return simsimd_capabilities_x86();
490
+ return _simsimd_capabilities_x86();
421
491
  #endif // SIMSIMD_TARGET_X86
422
492
  #if SIMSIMD_TARGET_ARM
423
- return simsimd_capabilities_arm();
493
+ return _simsimd_capabilities_arm();
424
494
  #endif // SIMSIMD_TARGET_ARM
425
495
  return simsimd_cap_serial_k;
426
496
  }
@@ -430,6 +500,11 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_implementation(void) {
430
500
  #pragma clang diagnostic push
431
501
  #pragma clang diagnostic ignored "-Wcast-function-type"
432
502
 
503
+ #ifdef __cplusplus //! option ‘-Wvolatile’ is valid for C++/ObjC++ but not for C
504
+ #pragma GCC diagnostic ignored "-Wvolatile"
505
+ #pragma clang diagnostic ignored "-Wvolatile"
506
+ #endif
507
+
433
508
  /**
434
509
  * @brief Determines the best suited metric implementation based on the given datatype,
435
510
  * supported and allowed by hardware capabilities.
@@ -441,17 +516,26 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_implementation(void) {
441
516
  * @param metric_output Output variable for the selected similarity function.
442
517
  * @param capability_output Output variable for the utilized hardware capabilities.
443
518
  */
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, //
519
+ SIMSIMD_INTERNAL void _simsimd_find_metric_punned_implementation( //
520
+ simsimd_metric_kind_t kind, //
521
+ simsimd_datatype_t datatype, //
522
+ simsimd_capability_t supported, //
523
+ simsimd_capability_t allowed, //
524
+ simsimd_metric_punned_t* metric_output, //
450
525
  simsimd_capability_t* capability_output) {
451
526
 
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);
527
+ // Modern compilers abso-freaking-lutely love optimizing-out my logic!
528
+ // Just marking the variables as `volatile` is not enough, so we have
529
+ // to add inline assembly to further discourage them!
530
+ #if defined(_MSC_VER)
531
+ _ReadWriteBarrier();
532
+ #else
533
+ __asm__ __volatile__("" ::: "memory");
534
+ #endif
535
+
536
+ volatile simsimd_metric_punned_t* m = metric_output;
537
+ volatile simsimd_capability_t* c = capability_output;
538
+ volatile simsimd_capability_t viable = (simsimd_capability_t)(supported & allowed);
455
539
  *m = (simsimd_metric_punned_t)0;
456
540
  *c = (simsimd_capability_t)0;
457
541
 
@@ -460,15 +544,31 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
460
544
 
461
545
  case simsimd_datatype_unknown_k: break;
462
546
 
463
- // Double-precision floating-point vectors
464
- case simsimd_datatype_f64_k:
547
+ // These data-types are not supported yet
548
+ case simsimd_datatype_i16_k: break;
549
+ case simsimd_datatype_i32_k: break;
550
+ case simsimd_datatype_i64_k: break;
551
+ case simsimd_datatype_u8_k: break;
552
+ case simsimd_datatype_u64_k: break;
465
553
 
554
+ // Double-precision floating-point vectors
555
+ case simsimd_datatype_f64_k: {
466
556
  #if SIMSIMD_TARGET_SVE
467
- if (viable & simsimd_cap_skylake_k)
557
+ if (viable & simsimd_cap_sve_k)
468
558
  switch (kind) {
469
559
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_sve, *c = simsimd_cap_sve_k; return;
470
560
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_sve, *c = simsimd_cap_sve_k; return;
471
561
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_sve, *c = simsimd_cap_sve_k; return;
562
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f64_sve, *c = simsimd_cap_sve_k; return;
563
+ default: break;
564
+ }
565
+ #endif
566
+ #if SIMSIMD_TARGET_NEON
567
+ if (viable & simsimd_cap_neon_k)
568
+ switch (kind) {
569
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_neon, *c = simsimd_cap_neon_k; return;
570
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_neon, *c = simsimd_cap_neon_k; return;
571
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f64_neon, *c = simsimd_cap_neon_k; return;
472
572
  default: break;
473
573
  }
474
574
  #endif
@@ -478,6 +578,7 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
478
578
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_skylake, *c = simsimd_cap_skylake_k; return;
479
579
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_skylake, *c = simsimd_cap_skylake_k; return;
480
580
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_skylake, *c = simsimd_cap_skylake_k; return;
581
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f64_skylake, *c = simsimd_cap_skylake_k; return;
481
582
  default: break;
482
583
  }
483
584
  #endif
@@ -486,15 +587,21 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
486
587
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_serial, *c = simsimd_cap_serial_k; return;
487
588
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_serial, *c = simsimd_cap_serial_k; return;
488
589
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_serial, *c = simsimd_cap_serial_k; return;
590
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f64_serial, *c = simsimd_cap_serial_k; return;
489
591
  case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f64_serial, *c = simsimd_cap_serial_k; return;
490
592
  case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f64_serial, *c = simsimd_cap_serial_k; return;
593
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f64_serial, *c = simsimd_cap_serial_k; return;
594
+ case simsimd_metric_mahalanobis_k:
595
+ *m = (m_t)&simsimd_mahalanobis_f64_serial, *c = simsimd_cap_serial_k;
596
+ return;
491
597
  default: break;
492
598
  }
493
599
 
494
600
  break;
601
+ }
495
602
 
496
603
  // Single-precision floating-point vectors
497
- case simsimd_datatype_f32_k:
604
+ case simsimd_datatype_f32_k: {
498
605
 
499
606
  #if SIMSIMD_TARGET_SVE
500
607
  if (viable & simsimd_cap_sve_k)
@@ -502,6 +609,7 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
502
609
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_sve, *c = simsimd_cap_sve_k; return;
503
610
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_sve, *c = simsimd_cap_sve_k; return;
504
611
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_sve, *c = simsimd_cap_sve_k; return;
612
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f32_sve, *c = simsimd_cap_sve_k; return;
505
613
  default: break;
506
614
  }
507
615
  #endif
@@ -511,6 +619,7 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
511
619
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_neon, *c = simsimd_cap_neon_k; return;
512
620
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_neon, *c = simsimd_cap_neon_k; return;
513
621
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_neon, *c = simsimd_cap_neon_k; return;
622
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f32_neon, *c = simsimd_cap_neon_k; return;
514
623
  case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_neon, *c = simsimd_cap_neon_k; return;
515
624
  case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_neon, *c = simsimd_cap_neon_k; return;
516
625
  default: break;
@@ -522,8 +631,13 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
522
631
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_skylake, *c = simsimd_cap_skylake_k; return;
523
632
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_skylake, *c = simsimd_cap_skylake_k; return;
524
633
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_skylake, *c = simsimd_cap_skylake_k; return;
634
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f32_skylake, *c = simsimd_cap_skylake_k; return;
525
635
  case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_skylake, *c = simsimd_cap_skylake_k; return;
526
636
  case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_skylake, *c = simsimd_cap_skylake_k; return;
637
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f32_skylake, *c = simsimd_cap_skylake_k; return;
638
+ case simsimd_metric_mahalanobis_k:
639
+ *m = (m_t)&simsimd_mahalanobis_f32_skylake, *c = simsimd_cap_skylake_k;
640
+ return;
527
641
  default: break;
528
642
  }
529
643
  #endif
@@ -533,6 +647,7 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
533
647
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_haswell, *c = simsimd_cap_haswell_k; return;
534
648
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_haswell, *c = simsimd_cap_haswell_k; return;
535
649
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_haswell, *c = simsimd_cap_haswell_k; return;
650
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f32_haswell, *c = simsimd_cap_haswell_k; return;
536
651
  default: break;
537
652
  }
538
653
  #endif
@@ -542,33 +657,44 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
542
657
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_serial, *c = simsimd_cap_serial_k; return;
543
658
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_serial, *c = simsimd_cap_serial_k; return;
544
659
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_serial, *c = simsimd_cap_serial_k; return;
660
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f32_serial, *c = simsimd_cap_serial_k; return;
545
661
  case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_serial, *c = simsimd_cap_serial_k; return;
546
662
  case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_serial, *c = simsimd_cap_serial_k; return;
663
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f32_serial, *c = simsimd_cap_serial_k; return;
664
+ case simsimd_metric_mahalanobis_k:
665
+ *m = (m_t)&simsimd_mahalanobis_f32_serial, *c = simsimd_cap_serial_k;
666
+ return;
547
667
  default: break;
548
668
  }
549
669
 
550
670
  break;
551
-
671
+ }
552
672
  // Half-precision floating-point vectors
553
- case simsimd_datatype_f16_k:
673
+ case simsimd_datatype_f16_k: {
554
674
 
555
- #if SIMSIMD_TARGET_SVE
675
+ #if SIMSIMD_TARGET_SVE_F16
556
676
  if (viable & simsimd_cap_sve_k)
557
677
  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;
678
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_sve, *c = simsimd_cap_sve_f16_k; return;
679
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_sve, *c = simsimd_cap_sve_f16_k; return;
680
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_sve, *c = simsimd_cap_sve_f16_k; return;
681
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f16_sve, *c = simsimd_cap_sve_f16_k; return;
561
682
  default: break;
562
683
  }
563
684
  #endif
564
- #if SIMSIMD_TARGET_NEON
685
+ #if SIMSIMD_TARGET_NEON_F16
565
686
  if (viable & simsimd_cap_neon_f16_k)
566
687
  switch (kind) {
567
688
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_neon, *c = simsimd_cap_neon_f16_k; return;
568
689
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_neon, *c = simsimd_cap_neon_f16_k; return;
569
690
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_neon, *c = simsimd_cap_neon_f16_k; return;
691
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f16_neon, *c = simsimd_cap_neon_f16_k; return;
570
692
  case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_neon, *c = simsimd_cap_neon_f16_k; return;
571
693
  case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_neon, *c = simsimd_cap_neon_f16_k; return;
694
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f16_neon, *c = simsimd_cap_neon_f16_k; return;
695
+ case simsimd_metric_mahalanobis_k:
696
+ *m = (m_t)&simsimd_mahalanobis_f16_neon, *c = simsimd_cap_neon_f16_k;
697
+ return;
572
698
  default: break;
573
699
  }
574
700
  #endif
@@ -578,8 +704,15 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
578
704
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
579
705
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
580
706
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
707
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
581
708
  case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
582
709
  case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
710
+ case simsimd_metric_bilinear_k:
711
+ *m = (m_t)&simsimd_bilinear_f16_sapphire, *c = simsimd_cap_sapphire_k;
712
+ return;
713
+ case simsimd_metric_mahalanobis_k:
714
+ *m = (m_t)&simsimd_mahalanobis_f16_sapphire, *c = simsimd_cap_sapphire_k;
715
+ return;
583
716
  default: break;
584
717
  }
585
718
  #endif
@@ -589,8 +722,13 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
589
722
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_haswell, *c = simsimd_cap_haswell_k; return;
590
723
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_haswell, *c = simsimd_cap_haswell_k; return;
591
724
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_haswell, *c = simsimd_cap_haswell_k; return;
725
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f16_haswell, *c = simsimd_cap_haswell_k; return;
592
726
  case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_haswell, *c = simsimd_cap_haswell_k; return;
593
727
  case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_haswell, *c = simsimd_cap_haswell_k; return;
728
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f16_haswell, *c = simsimd_cap_haswell_k; return;
729
+ case simsimd_metric_mahalanobis_k:
730
+ *m = (m_t)&simsimd_mahalanobis_f16_haswell, *c = simsimd_cap_haswell_k;
731
+ return;
594
732
  default: break;
595
733
  }
596
734
  #endif
@@ -600,30 +738,36 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
600
738
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_serial, *c = simsimd_cap_serial_k; return;
601
739
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_serial, *c = simsimd_cap_serial_k; return;
602
740
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_serial, *c = simsimd_cap_serial_k; return;
741
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_f16_serial, *c = simsimd_cap_serial_k; return;
603
742
  case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_serial, *c = simsimd_cap_serial_k; return;
604
743
  case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_serial, *c = simsimd_cap_serial_k; return;
744
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_f16_serial, *c = simsimd_cap_serial_k; return;
745
+ case simsimd_metric_mahalanobis_k:
746
+ *m = (m_t)&simsimd_mahalanobis_f16_serial, *c = simsimd_cap_serial_k;
747
+ return;
605
748
  default: break;
606
749
  }
607
750
 
608
751
  break;
609
-
752
+ }
610
753
  // Brain floating-point vectors
611
- case simsimd_datatype_bf16_k:
754
+ case simsimd_datatype_bf16_k: {
755
+ #if SIMSIMD_TARGET_SVE_BF16
756
+ if (viable & simsimd_cap_sve_bf16_k)
757
+ switch (kind) {
758
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_sve, *c = simsimd_cap_sve_bf16_k; return;
759
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_sve, *c = simsimd_cap_sve_bf16_k; return;
760
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_bf16_sve, *c = simsimd_cap_sve_bf16_k; return;
761
+ default: break;
762
+ }
763
+ #endif
612
764
  #if SIMSIMD_TARGET_NEON_BF16
613
765
  if (viable & simsimd_cap_neon_bf16_k)
614
766
  switch (kind) {
615
767
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
616
768
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
617
769
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
618
- default: break;
619
- }
620
- #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;
770
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_bf16_neon, *c = simsimd_cap_neon_bf16_k; return;
627
771
  default: break;
628
772
  }
629
773
  #endif
@@ -633,6 +777,27 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
633
777
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_genoa, *c = simsimd_cap_genoa_k; return;
634
778
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_genoa, *c = simsimd_cap_genoa_k; return;
635
779
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_genoa, *c = simsimd_cap_genoa_k; return;
780
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_bf16_genoa, *c = simsimd_cap_genoa_k; return;
781
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_bf16_genoa, *c = simsimd_cap_genoa_k; return;
782
+ case simsimd_metric_mahalanobis_k:
783
+ *m = (m_t)&simsimd_mahalanobis_bf16_genoa, *c = simsimd_cap_genoa_k;
784
+ return;
785
+ default: break;
786
+ }
787
+ #endif
788
+ #if SIMSIMD_TARGET_HASWELL
789
+ if (viable & simsimd_cap_haswell_k)
790
+ switch (kind) {
791
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_haswell, *c = simsimd_cap_haswell_k; return;
792
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_haswell, *c = simsimd_cap_haswell_k; return;
793
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_haswell, *c = simsimd_cap_haswell_k; return;
794
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_bf16_haswell, *c = simsimd_cap_haswell_k; return;
795
+ case simsimd_metric_bilinear_k:
796
+ *m = (m_t)&simsimd_bilinear_bf16_haswell, *c = simsimd_cap_haswell_k;
797
+ return;
798
+ case simsimd_metric_mahalanobis_k:
799
+ *m = (m_t)&simsimd_mahalanobis_bf16_haswell, *c = simsimd_cap_haswell_k;
800
+ return;
636
801
  default: break;
637
802
  }
638
803
  #endif
@@ -641,21 +806,27 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
641
806
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_bf16_serial, *c = simsimd_cap_serial_k; return;
642
807
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_bf16_serial, *c = simsimd_cap_serial_k; return;
643
808
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_bf16_serial, *c = simsimd_cap_serial_k; return;
809
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_bf16_serial, *c = simsimd_cap_serial_k; return;
644
810
  case simsimd_metric_js_k: *m = (m_t)&simsimd_js_bf16_serial, *c = simsimd_cap_serial_k; return;
645
811
  case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_bf16_serial, *c = simsimd_cap_serial_k; return;
812
+ case simsimd_metric_bilinear_k: *m = (m_t)&simsimd_bilinear_bf16_serial, *c = simsimd_cap_serial_k; return;
813
+ case simsimd_metric_mahalanobis_k:
814
+ *m = (m_t)&simsimd_mahalanobis_bf16_serial, *c = simsimd_cap_serial_k;
815
+ return;
646
816
  default: break;
647
817
  }
648
818
 
649
819
  break;
650
-
820
+ }
651
821
  // Single-byte integer vectors
652
- case simsimd_datatype_i8_k:
653
- #if SIMSIMD_TARGET_NEON
822
+ case simsimd_datatype_i8_k: {
823
+ #if SIMSIMD_TARGET_NEON_I8
654
824
  if (viable & simsimd_cap_neon_i8_k)
655
825
  switch (kind) {
656
826
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_neon, *c = simsimd_cap_neon_i8_k; return;
657
827
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_neon, *c = simsimd_cap_neon_i8_k; return;
658
828
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_neon, *c = simsimd_cap_neon_i8_k; return;
829
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_i8_neon, *c = simsimd_cap_neon_i8_k; return;
659
830
  default: break;
660
831
  }
661
832
  #endif
@@ -665,6 +836,7 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
665
836
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_ice, *c = simsimd_cap_ice_k; return;
666
837
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_ice, *c = simsimd_cap_ice_k; return;
667
838
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_ice, *c = simsimd_cap_ice_k; return;
839
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_i8_ice, *c = simsimd_cap_ice_k; return;
668
840
  default: break;
669
841
  }
670
842
  #endif
@@ -674,6 +846,7 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
674
846
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_haswell, *c = simsimd_cap_haswell_k; return;
675
847
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_haswell, *c = simsimd_cap_haswell_k; return;
676
848
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_haswell, *c = simsimd_cap_haswell_k; return;
849
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_i8_haswell, *c = simsimd_cap_haswell_k; return;
677
850
  default: break;
678
851
  }
679
852
  #endif
@@ -683,13 +856,14 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
683
856
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_i8_serial, *c = simsimd_cap_serial_k; return;
684
857
  case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_serial, *c = simsimd_cap_serial_k; return;
685
858
  case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_serial, *c = simsimd_cap_serial_k; return;
859
+ case simsimd_metric_l2_k: *m = (m_t)&simsimd_l2_i8_serial, *c = simsimd_cap_serial_k; return;
686
860
  default: break;
687
861
  }
688
862
 
689
863
  break;
690
-
864
+ }
691
865
  // Binary vectors
692
- case simsimd_datatype_b8_k:
866
+ case simsimd_datatype_b8_k: {
693
867
 
694
868
  #if SIMSIMD_TARGET_SVE
695
869
  if (viable & simsimd_cap_sve_k)
@@ -732,8 +906,9 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
732
906
  }
733
907
 
734
908
  break;
735
-
736
- case simsimd_datatype_f32c_k:
909
+ }
910
+ // Complex floating-point vectors
911
+ case simsimd_datatype_f32c_k: {
737
912
 
738
913
  #if SIMSIMD_TARGET_SVE
739
914
  if (viable & simsimd_cap_sve_k)
@@ -776,8 +951,9 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
776
951
  }
777
952
 
778
953
  break;
779
-
780
- case simsimd_datatype_f64c_k:
954
+ }
955
+ // Complex double-precision floating-point vectors
956
+ case simsimd_datatype_f64c_k: {
781
957
 
782
958
  #if SIMSIMD_TARGET_SVE
783
959
  if (viable & simsimd_cap_sve_k)
@@ -804,18 +980,19 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
804
980
  }
805
981
 
806
982
  break;
983
+ }
984
+ // Complex half-precision floating-point vectors
985
+ case simsimd_datatype_f16c_k: {
807
986
 
808
- case simsimd_datatype_f16c_k:
809
-
810
- #if SIMSIMD_TARGET_SVE
987
+ #if SIMSIMD_TARGET_SVE_F16
811
988
  if (viable & simsimd_cap_sve_k)
812
989
  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;
990
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_sve, *c = simsimd_cap_sve_f16_k; return;
991
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_sve, *c = simsimd_cap_sve_f16_k; return;
815
992
  default: break;
816
993
  }
817
994
  #endif
818
- #if SIMSIMD_TARGET_NEON
995
+ #if SIMSIMD_TARGET_NEON_F16
819
996
  if (viable & simsimd_cap_neon_f16_k)
820
997
  switch (kind) {
821
998
  case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_neon, *c = simsimd_cap_neon_f16_k; return;
@@ -848,7 +1025,9 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
848
1025
  }
849
1026
 
850
1027
  break;
851
- case simsimd_datatype_bf16c_k:
1028
+ }
1029
+ // Complex Brain floating-point vectors
1030
+ case simsimd_datatype_bf16c_k: {
852
1031
 
853
1032
  #if SIMSIMD_TARGET_NEON_BF16
854
1033
  if (viable & simsimd_cap_neon_bf16_k)
@@ -876,6 +1055,81 @@ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
876
1055
 
877
1056
  break;
878
1057
  }
1058
+
1059
+ // Unsigned 16-bit integer vectors
1060
+ case simsimd_datatype_u16_k: {
1061
+
1062
+ #if SIMSIMD_TARGET_SVE2
1063
+ if (viable & simsimd_cap_sve_k)
1064
+ switch (kind) {
1065
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u16_sve2, *c = simsimd_cap_sve_k; return;
1066
+ default: break;
1067
+ }
1068
+ #endif
1069
+ #if SIMSIMD_TARGET_NEON
1070
+ if (viable & simsimd_cap_neon_k)
1071
+ switch (kind) {
1072
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u16_neon, *c = simsimd_cap_neon_k; return;
1073
+ default: break;
1074
+ }
1075
+ #endif
1076
+ #if SIMSIMD_TARGET_ICE
1077
+ if (viable & simsimd_cap_ice_k)
1078
+ switch (kind) {
1079
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u16_ice, *c = simsimd_cap_skylake_k; return;
1080
+ default: break;
1081
+ }
1082
+ #endif
1083
+ if (viable & simsimd_cap_serial_k)
1084
+ switch (kind) {
1085
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u16_serial, *c = simsimd_cap_serial_k; return;
1086
+ default: break;
1087
+ }
1088
+
1089
+ break;
1090
+ }
1091
+ // Unsigned 32-bit integer vectors
1092
+ case simsimd_datatype_u32_k: {
1093
+
1094
+ #if SIMSIMD_TARGET_SVE2
1095
+ if (viable & simsimd_cap_sve_k)
1096
+ switch (kind) {
1097
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u32_sve2, *c = simsimd_cap_sve_k; return;
1098
+ default: break;
1099
+ }
1100
+ #endif
1101
+ #if SIMSIMD_TARGET_NEON
1102
+ if (viable & simsimd_cap_neon_k)
1103
+ switch (kind) {
1104
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u32_neon, *c = simsimd_cap_neon_k; return;
1105
+ default: break;
1106
+ }
1107
+ #endif
1108
+ #if SIMSIMD_TARGET_ICE
1109
+ if (viable & simsimd_cap_ice_k)
1110
+ switch (kind) {
1111
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u32_ice, *c = simsimd_cap_skylake_k; return;
1112
+ default: break;
1113
+ }
1114
+ #endif
1115
+ if (viable & simsimd_cap_serial_k)
1116
+ switch (kind) {
1117
+ case simsimd_metric_intersect_k: *m = (m_t)&simsimd_intersect_u32_serial, *c = simsimd_cap_serial_k; return;
1118
+ default: break;
1119
+ }
1120
+
1121
+ break;
1122
+ }
1123
+ }
1124
+
1125
+ // Modern compilers abso-freaking-lutely love optimizing-out my logic!
1126
+ // Just marking the variables as `volatile` is not enough, so we have
1127
+ // to add inline assembly to further discourage them!
1128
+ #if defined(_MSC_VER)
1129
+ _ReadWriteBarrier();
1130
+ #else
1131
+ __asm__ __volatile__("" ::: "memory");
1132
+ #endif
879
1133
  }
880
1134
 
881
1135
  #pragma GCC diagnostic pop
@@ -922,12 +1176,13 @@ SIMSIMD_DYNAMIC int simsimd_uses_sve(void);
922
1176
  SIMSIMD_DYNAMIC int simsimd_uses_sve_f16(void);
923
1177
  SIMSIMD_DYNAMIC int simsimd_uses_sve_bf16(void);
924
1178
  SIMSIMD_DYNAMIC int simsimd_uses_sve_i8(void);
1179
+ SIMSIMD_DYNAMIC int simsimd_uses_sve2(void);
925
1180
  SIMSIMD_DYNAMIC int simsimd_uses_haswell(void);
926
1181
  SIMSIMD_DYNAMIC int simsimd_uses_skylake(void);
927
1182
  SIMSIMD_DYNAMIC int simsimd_uses_ice(void);
928
1183
  SIMSIMD_DYNAMIC int simsimd_uses_sapphire(void);
929
1184
  SIMSIMD_DYNAMIC int simsimd_uses_genoa(void);
930
- SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
1185
+ SIMSIMD_DYNAMIC int simsimd_uses_dynamic_dispatch(void);
931
1186
 
932
1187
  /* Inner products
933
1188
  * - Dot product: the sum of the products of the corresponding elements of two vectors.
@@ -1024,9 +1279,9 @@ SIMSIMD_DYNAMIC void simsimd_jaccard_b8(simsimd_b8_t const* a, simsimd_b8_t cons
1024
1279
  * - Jensen-Shannon divergence: a measure of similarity between two probability distributions.
1025
1280
  * - Kullback-Leibler divergence: a measure of how one probability distribution diverges from a second.
1026
1281
  *
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.
1282
+ * @param a The first discrete probability distribution.
1283
+ * @param b The second discrete probability distribution.
1284
+ * @param n The number of elements in the discrete distributions.
1030
1285
  * @param d The output divergence value.
1031
1286
  *
1032
1287
  * @note The distributions are assumed to be normalized.
@@ -1066,20 +1321,31 @@ SIMSIMD_DYNAMIC void simsimd_js_f64(simsimd_f64_t const* a, simsimd_f64_t const*
1066
1321
  */
1067
1322
 
1068
1323
  // clang-format off
1324
+ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void) { return _simsimd_capabilities_implementation(); }
1325
+ SIMSIMD_PUBLIC int simsimd_uses_dynamic_dispatch(void) { return 0; }
1069
1326
  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; }
1327
+ SIMSIMD_PUBLIC int simsimd_uses_neon_f16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON_F16 ; }
1328
+ SIMSIMD_PUBLIC int simsimd_uses_neon_bf16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON_BF16; }
1329
+ SIMSIMD_PUBLIC int simsimd_uses_neon_i8(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON_I8; }
1073
1330
  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; }
1331
+ SIMSIMD_PUBLIC int simsimd_uses_sve_f16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE_F16; }
1332
+ SIMSIMD_PUBLIC int simsimd_uses_sve_bf16(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE_BF16; }
1333
+ SIMSIMD_PUBLIC int simsimd_uses_sve_i8(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE_I8; }
1334
+ SIMSIMD_PUBLIC int simsimd_uses_sve2(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE2; }
1077
1335
  SIMSIMD_PUBLIC int simsimd_uses_haswell(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_HASWELL; }
1078
1336
  SIMSIMD_PUBLIC int simsimd_uses_skylake(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SKYLAKE; }
1079
1337
  SIMSIMD_PUBLIC int simsimd_uses_ice(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_ICE; }
1080
1338
  SIMSIMD_PUBLIC int simsimd_uses_sapphire(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SAPPHIRE; }
1081
1339
  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(); }
1340
+ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
1341
+ simsimd_metric_kind_t kind, //
1342
+ simsimd_datatype_t datatype, //
1343
+ simsimd_capability_t supported, //
1344
+ simsimd_capability_t allowed, //
1345
+ simsimd_metric_punned_t* metric_output, //
1346
+ simsimd_capability_t* capability_output) {
1347
+ _simsimd_find_metric_punned_implementation(kind, datatype, supported, allowed, metric_output, capability_output);
1348
+ }
1083
1349
  // clang-format on
1084
1350
 
1085
1351
  /* Inner products
@@ -1098,9 +1364,9 @@ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void) { return simsimd_
1098
1364
  */
1099
1365
  SIMSIMD_PUBLIC void simsimd_dot_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1100
1366
  simsimd_distance_t* d) {
1101
- #if SIMSIMD_TARGET_SVE
1367
+ #if SIMSIMD_TARGET_SVE_F16
1102
1368
  simsimd_dot_f16_sve(a, b, n, d);
1103
- #elif SIMSIMD_TARGET_NEON
1369
+ #elif SIMSIMD_TARGET_NEON_F16
1104
1370
  simsimd_dot_f16_neon(a, b, n, d);
1105
1371
  #elif SIMSIMD_TARGET_SAPPHIRE
1106
1372
  simsimd_dot_f16_sapphire(a, b, n, d);
@@ -1117,6 +1383,8 @@ SIMSIMD_PUBLIC void simsimd_dot_bf16(simsimd_bf16_t const* a, simsimd_bf16_t con
1117
1383
  simsimd_dot_bf16_genoa(a, b, n, d);
1118
1384
  #elif SIMSIMD_TARGET_HASWELL
1119
1385
  simsimd_dot_bf16_haswell(a, b, n, d);
1386
+ #elif SIMSIMD_TARGET_NEON_BF16
1387
+ simsimd_dot_bf16_neon(a, b, n, d);
1120
1388
  #else
1121
1389
  simsimd_dot_bf16_serial(a, b, n, d);
1122
1390
  #endif
@@ -1148,9 +1416,9 @@ SIMSIMD_PUBLIC void simsimd_dot_f64(simsimd_f64_t const* a, simsimd_f64_t const*
1148
1416
  }
1149
1417
  SIMSIMD_PUBLIC void simsimd_dot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1150
1418
  simsimd_distance_t* d) {
1151
- #if SIMSIMD_TARGET_SVE
1419
+ #if SIMSIMD_TARGET_SVE_F16
1152
1420
  simsimd_dot_f16c_sve(a, b, n, d);
1153
- #elif SIMSIMD_TARGET_NEON
1421
+ #elif SIMSIMD_TARGET_NEON_F16
1154
1422
  simsimd_dot_f16c_neon(a, b, n, d);
1155
1423
  #elif SIMSIMD_TARGET_SAPPHIRE
1156
1424
  simsimd_dot_f16c_sapphire(a, b, n, d);
@@ -1164,6 +1432,8 @@ SIMSIMD_PUBLIC void simsimd_dot_bf16c(simsimd_bf16_t const* a, simsimd_bf16_t co
1164
1432
  simsimd_distance_t* d) {
1165
1433
  #if SIMSIMD_TARGET_GENOA
1166
1434
  simsimd_dot_bf16c_genoa(a, b, n, d);
1435
+ #elif SIMSIMD_TARGET_NEON_BF16
1436
+ simsimd_dot_bf16c_neon(a, b, n, d);
1167
1437
  #else
1168
1438
  simsimd_dot_bf16c_serial(a, b, n, d);
1169
1439
  #endif
@@ -1208,7 +1478,13 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c(simsimd_f16_t const* a, simsimd_f16_t cons
1208
1478
  }
1209
1479
  SIMSIMD_PUBLIC void simsimd_vdot_bf16c(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1210
1480
  simsimd_distance_t* d) {
1481
+ #if SIMSIMD_TARGET_GENOA
1482
+ simsimd_vdot_bf16c_genoa(a, b, n, d);
1483
+ #elif SIMSIMD_TARGET_NEON_BF16
1484
+ simsimd_dot_bf16c_neon(a, b, n, d);
1485
+ #else
1211
1486
  simsimd_vdot_bf16c_serial(a, b, n, d);
1487
+ #endif
1212
1488
  }
1213
1489
  SIMSIMD_PUBLIC void simsimd_vdot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1214
1490
  simsimd_distance_t* d) {
@@ -1260,23 +1536,11 @@ SIMSIMD_PUBLIC void simsimd_cos_i8(simsimd_i8_t const* a, simsimd_i8_t const* b,
1260
1536
  simsimd_cos_i8_serial(a, b, n, d);
1261
1537
  #endif
1262
1538
  }
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) {
1265
- #if SIMSIMD_TARGET_NEON
1266
- simsimd_l2sq_i8_neon(a, b, n, d);
1267
- #elif SIMSIMD_TARGET_ICE
1268
- simsimd_l2sq_i8_ice(a, b, n, d);
1269
- #elif SIMSIMD_TARGET_HASWELL
1270
- simsimd_l2sq_i8_haswell(a, b, n, d);
1271
- #else
1272
- simsimd_l2sq_i8_serial(a, b, n, d);
1273
- #endif
1274
- }
1275
1539
  SIMSIMD_PUBLIC void simsimd_cos_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1276
1540
  simsimd_distance_t* d) {
1277
- #if SIMSIMD_TARGET_SVE
1541
+ #if SIMSIMD_TARGET_SVE_F16
1278
1542
  simsimd_cos_f16_sve(a, b, n, d);
1279
- #elif SIMSIMD_TARGET_NEON
1543
+ #elif SIMSIMD_TARGET_NEON_F16
1280
1544
  simsimd_cos_f16_neon(a, b, n, d);
1281
1545
  #elif SIMSIMD_TARGET_SAPPHIRE
1282
1546
  simsimd_cos_f16_sapphire(a, b, n, d);
@@ -1292,6 +1556,10 @@ SIMSIMD_PUBLIC void simsimd_cos_bf16(simsimd_bf16_t const* a, simsimd_bf16_t con
1292
1556
  simsimd_cos_bf16_genoa(a, b, n, d);
1293
1557
  #elif SIMSIMD_TARGET_HASWELL
1294
1558
  simsimd_cos_bf16_haswell(a, b, n, d);
1559
+ #elif SIMSIMD_TARGET_SVE_BF16
1560
+ simsimd_cos_bf16_sve(a, b, n, d);
1561
+ #elif SIMSIMD_TARGET_NEON_BF16
1562
+ simsimd_cos_bf16_neon(a, b, n, d);
1295
1563
  #else
1296
1564
  simsimd_cos_bf16_serial(a, b, n, d);
1297
1565
  #endif
@@ -1314,17 +1582,31 @@ SIMSIMD_PUBLIC void simsimd_cos_f64(simsimd_f64_t const* a, simsimd_f64_t const*
1314
1582
  simsimd_distance_t* d) {
1315
1583
  #if SIMSIMD_TARGET_SVE
1316
1584
  simsimd_cos_f64_sve(a, b, n, d);
1585
+ #elif SIMSIMD_TARGET_NEON
1586
+ simsimd_cos_f64_neon(a, b, n, d);
1317
1587
  #elif SIMSIMD_TARGET_SKYLAKE
1318
1588
  simsimd_cos_f64_skylake(a, b, n, d);
1319
1589
  #else
1320
1590
  simsimd_cos_f64_serial(a, b, n, d);
1321
1591
  #endif
1322
1592
  }
1593
+ SIMSIMD_PUBLIC void simsimd_l2sq_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
1594
+ simsimd_distance_t* d) {
1595
+ #if SIMSIMD_TARGET_NEON
1596
+ simsimd_l2sq_i8_neon(a, b, n, d);
1597
+ #elif SIMSIMD_TARGET_ICE
1598
+ simsimd_l2sq_i8_ice(a, b, n, d);
1599
+ #elif SIMSIMD_TARGET_HASWELL
1600
+ simsimd_l2sq_i8_haswell(a, b, n, d);
1601
+ #else
1602
+ simsimd_l2sq_i8_serial(a, b, n, d);
1603
+ #endif
1604
+ }
1323
1605
  SIMSIMD_PUBLIC void simsimd_l2sq_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1324
1606
  simsimd_distance_t* d) {
1325
- #if SIMSIMD_TARGET_SVE
1607
+ #if SIMSIMD_TARGET_SVE_F16
1326
1608
  simsimd_l2sq_f16_sve(a, b, n, d);
1327
- #elif SIMSIMD_TARGET_NEON
1609
+ #elif SIMSIMD_TARGET_NEON_F16
1328
1610
  simsimd_l2sq_f16_neon(a, b, n, d);
1329
1611
  #elif SIMSIMD_TARGET_SAPPHIRE
1330
1612
  simsimd_l2sq_f16_sapphire(a, b, n, d);
@@ -1340,6 +1622,10 @@ SIMSIMD_PUBLIC void simsimd_l2sq_bf16(simsimd_bf16_t const* a, simsimd_bf16_t co
1340
1622
  simsimd_l2sq_bf16_genoa(a, b, n, d);
1341
1623
  #elif SIMSIMD_TARGET_HASWELL
1342
1624
  simsimd_l2sq_bf16_haswell(a, b, n, d);
1625
+ #elif SIMSIMD_TARGET_SVE_BF16
1626
+ simsimd_l2sq_bf16_sve(a, b, n, d);
1627
+ #elif SIMSIMD_TARGET_NEON_BF16
1628
+ simsimd_l2sq_bf16_neon(a, b, n, d);
1343
1629
  #else
1344
1630
  simsimd_l2sq_bf16_serial(a, b, n, d);
1345
1631
  #endif
@@ -1362,12 +1648,80 @@ SIMSIMD_PUBLIC void simsimd_l2sq_f64(simsimd_f64_t const* a, simsimd_f64_t const
1362
1648
  simsimd_distance_t* d) {
1363
1649
  #if SIMSIMD_TARGET_SVE
1364
1650
  simsimd_l2sq_f64_sve(a, b, n, d);
1651
+ #elif SIMSIMD_TARGET_NEON
1652
+ simsimd_l2sq_f64_neon(a, b, n, d);
1365
1653
  #elif SIMSIMD_TARGET_SKYLAKE
1366
1654
  simsimd_l2sq_f64_skylake(a, b, n, d);
1367
1655
  #else
1368
1656
  simsimd_l2sq_f64_serial(a, b, n, d);
1369
1657
  #endif
1370
1658
  }
1659
+ SIMSIMD_PUBLIC void simsimd_l2_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
1660
+ simsimd_distance_t* d) {
1661
+ #if SIMSIMD_TARGET_NEON
1662
+ simsimd_l2_i8_neon(a, b, n, d);
1663
+ #elif SIMSIMD_TARGET_ICE
1664
+ simsimd_l2_i8_ice(a, b, n, d);
1665
+ #elif SIMSIMD_TARGET_HASWELL
1666
+ simsimd_l2_i8_haswell(a, b, n, d);
1667
+ #else
1668
+ simsimd_l2_i8_serial(a, b, n, d);
1669
+ #endif
1670
+ }
1671
+ SIMSIMD_PUBLIC void simsimd_l2_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1672
+ simsimd_distance_t* d) {
1673
+ #if SIMSIMD_TARGET_SVE_F16
1674
+ simsimd_l2_f16_sve(a, b, n, d);
1675
+ #elif SIMSIMD_TARGET_NEON_F16
1676
+ simsimd_l2_f16_neon(a, b, n, d);
1677
+ #elif SIMSIMD_TARGET_SAPPHIRE
1678
+ simsimd_l2_f16_sapphire(a, b, n, d);
1679
+ #elif SIMSIMD_TARGET_HASWELL
1680
+ simsimd_l2_f16_haswell(a, b, n, d);
1681
+ #else
1682
+ simsimd_l2_f16_serial(a, b, n, d);
1683
+ #endif
1684
+ }
1685
+ SIMSIMD_PUBLIC void simsimd_l2_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n,
1686
+ simsimd_distance_t* d) {
1687
+ #if SIMSIMD_TARGET_GENOA
1688
+ simsimd_l2_bf16_genoa(a, b, n, d);
1689
+ #elif SIMSIMD_TARGET_HASWELL
1690
+ simsimd_l2_bf16_haswell(a, b, n, d);
1691
+ #elif SIMSIMD_TARGET_SVE_BF16
1692
+ simsimd_l2_bf16_sve(a, b, n, d);
1693
+ #elif SIMSIMD_TARGET_NEON_BF16
1694
+ simsimd_l2_bf16_neon(a, b, n, d);
1695
+ #else
1696
+ simsimd_l2_bf16_serial(a, b, n, d);
1697
+ #endif
1698
+ }
1699
+ SIMSIMD_PUBLIC void simsimd_l2_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1700
+ simsimd_distance_t* d) {
1701
+ #if SIMSIMD_TARGET_SVE
1702
+ simsimd_l2_f32_sve(a, b, n, d);
1703
+ #elif SIMSIMD_TARGET_NEON
1704
+ simsimd_l2_f32_neon(a, b, n, d);
1705
+ #elif SIMSIMD_TARGET_SKYLAKE
1706
+ simsimd_l2_f32_skylake(a, b, n, d);
1707
+ #elif SIMSIMD_TARGET_HASWELL
1708
+ simsimd_l2_f32_haswell(a, b, n, d);
1709
+ #else
1710
+ simsimd_l2_f32_serial(a, b, n, d);
1711
+ #endif
1712
+ }
1713
+ SIMSIMD_PUBLIC void simsimd_l2_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1714
+ simsimd_distance_t* d) {
1715
+ #if SIMSIMD_TARGET_SVE
1716
+ simsimd_l2_f64_sve(a, b, n, d);
1717
+ #elif SIMSIMD_TARGET_NEON
1718
+ simsimd_l2_f64_neon(a, b, n, d);
1719
+ #elif SIMSIMD_TARGET_SKYLAKE
1720
+ simsimd_l2_f64_skylake(a, b, n, d);
1721
+ #else
1722
+ simsimd_l2_f64_serial(a, b, n, d);
1723
+ #endif
1724
+ }
1371
1725
 
1372
1726
  /* Binary distances
1373
1727
  * - Hamming distance: the number of positions at which the corresponding bits are different.
@@ -1415,9 +1769,9 @@ SIMSIMD_PUBLIC void simsimd_jaccard_b8(simsimd_b8_t const* a, simsimd_b8_t const
1415
1769
  * - Jensen-Shannon divergence: a measure of similarity between two probability distributions.
1416
1770
  * - Kullback-Leibler divergence: a measure of how one probability distribution diverges from a second.
1417
1771
  *
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.
1772
+ * @param a The first discrete probability distribution.
1773
+ * @param b The second discrete probability distribution.
1774
+ * @param n The number of elements in the discrete distributions.
1421
1775
  * @param d The output divergence value.
1422
1776
  *
1423
1777
  * @note The distributions are assumed to be normalized.
@@ -1482,6 +1836,126 @@ SIMSIMD_PUBLIC void simsimd_js_f64(simsimd_f64_t const* a, simsimd_f64_t const*
1482
1836
  simsimd_js_f64_serial(a, b, n, d);
1483
1837
  }
1484
1838
 
1839
+ /* Set operations
1840
+ *
1841
+ * @param a The first sorted array of integers.
1842
+ * @param b The second sorted array of integers.
1843
+ * @param a_length The number of elements in the first array.
1844
+ * @param b_length The number of elements in the second array.
1845
+ * @param d The output for the number of elements in the intersection.
1846
+ */
1847
+ SIMSIMD_PUBLIC void simsimd_intersect_u16(simsimd_u16_t const* a, simsimd_u16_t const* b, simsimd_size_t a_length,
1848
+ simsimd_size_t b_length, simsimd_distance_t* d) {
1849
+ #if SIMSIMD_TARGET_SVE2
1850
+ simsimd_intersect_u16_sve2(a, b, a_length, b_length, d);
1851
+ #elif SIMSIMD_TARGET_NEON
1852
+ simsimd_intersect_u16_neon(a, b, a_length, b_length, d);
1853
+ #elif SIMSIMD_TARGET_SKYLAKE
1854
+ simsimd_intersect_u16_ice(a, b, a_length, b_length, d);
1855
+ #else
1856
+ simsimd_intersect_u16_serial(a, b, a_length, b_length, d);
1857
+ #endif
1858
+ }
1859
+
1860
+ SIMSIMD_PUBLIC void simsimd_intersect_u32(simsimd_u32_t const* a, simsimd_u32_t const* b, simsimd_size_t a_length,
1861
+ simsimd_size_t b_length, simsimd_distance_t* d) {
1862
+ #if SIMSIMD_TARGET_SVE2
1863
+ simsimd_intersect_u32_sve2(a, b, a_length, b_length, d);
1864
+ #elif SIMSIMD_TARGET_NEON
1865
+ simsimd_intersect_u32_neon(a, b, a_length, b_length, d);
1866
+ #elif SIMSIMD_TARGET_SKYLAKE
1867
+ simsimd_intersect_u32_ice(a, b, a_length, b_length, d);
1868
+ #else
1869
+ simsimd_intersect_u32_serial(a, b, a_length, b_length, d);
1870
+ #endif
1871
+ }
1872
+
1873
+ /* Curved space distances
1874
+ *
1875
+ * @param a The first vector of floating point values.
1876
+ * @param b The second vector of floating point values.
1877
+ * @param c The metric tensor or covariance matrix.
1878
+ * @param n The number of dimensions in the vectors.
1879
+ * @param d The output for the number of elements in the intersection.
1880
+ */
1881
+ SIMSIMD_PUBLIC void simsimd_bilinear_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_f64_t const* c,
1882
+ simsimd_size_t n, simsimd_distance_t* d) {
1883
+
1884
+ simsimd_bilinear_f64_serial(a, b, c, n, d);
1885
+ }
1886
+ SIMSIMD_PUBLIC void simsimd_mahalanobis_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_f64_t const* c,
1887
+ simsimd_size_t n, simsimd_distance_t* d) {
1888
+ simsimd_mahalanobis_f64_serial(a, b, c, n, d);
1889
+ }
1890
+ SIMSIMD_PUBLIC void simsimd_bilinear_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_f32_t const* c,
1891
+ simsimd_size_t n, simsimd_distance_t* d) {
1892
+ #if SIMSIMD_TARGET_SKYLAKE
1893
+ simsimd_bilinear_f32_skylake(a, b, c, n, d);
1894
+ #elif SIMSIMD_TARGET_NEON
1895
+ simsimd_bilinear_f32_neon(a, b, c, n, d);
1896
+ #else
1897
+ simsimd_bilinear_f32_serial(a, b, c, n, d);
1898
+ #endif
1899
+ }
1900
+ SIMSIMD_PUBLIC void simsimd_mahalanobis_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_f32_t const* c,
1901
+ simsimd_size_t n, simsimd_distance_t* d) {
1902
+ #if SIMSIMD_TARGET_SKYLAKE
1903
+ simsimd_mahalanobis_f32_skylake(a, b, c, n, d);
1904
+ #elif SIMSIMD_TARGET_NEON
1905
+ simsimd_mahalanobis_f32_neon(a, b, c, n, d);
1906
+ #else
1907
+ simsimd_mahalanobis_f32_serial(a, b, c, n, d);
1908
+ #endif
1909
+ }
1910
+ SIMSIMD_PUBLIC void simsimd_bilinear_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_f16_t const* c,
1911
+ simsimd_size_t n, simsimd_distance_t* d) {
1912
+ #if SIMSIMD_TARGET_SAPPHIRE
1913
+ simsimd_bilinear_f16_sapphire(a, b, c, n, d);
1914
+ #elif SIMSIMD_TARGET_HASWELL
1915
+ simsimd_bilinear_f16_haswell(a, b, c, n, d);
1916
+ #elif SIMSIMD_TARGET_NEON
1917
+ simsimd_bilinear_f16_neon(a, b, c, n, d);
1918
+ #else
1919
+ simsimd_bilinear_f16_serial(a, b, c, n, d);
1920
+ #endif
1921
+ }
1922
+ SIMSIMD_PUBLIC void simsimd_mahalanobis_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_f16_t const* c,
1923
+ simsimd_size_t n, simsimd_distance_t* d) {
1924
+ #if SIMSIMD_TARGET_SAPPHIRE
1925
+ simsimd_mahalanobis_f16_sapphire(a, b, c, n, d);
1926
+ #elif SIMSIMD_TARGET_HASWELL
1927
+ simsimd_mahalanobis_f16_haswell(a, b, c, n, d);
1928
+ #elif SIMSIMD_TARGET_NEON
1929
+ simsimd_mahalanobis_f16_neon(a, b, c, n, d);
1930
+ #else
1931
+ simsimd_mahalanobis_f16_serial(a, b, c, n, d);
1932
+ #endif
1933
+ }
1934
+ SIMSIMD_PUBLIC void simsimd_bilinear_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_bf16_t const* c,
1935
+ simsimd_size_t n, simsimd_distance_t* d) {
1936
+ #if SIMSIMD_TARGET_GENOA
1937
+ simsimd_bilinear_bf16_genoa(a, b, c, n, d);
1938
+ #elif SIMSIMD_TARGET_HASWELL
1939
+ simsimd_bilinear_bf16_haswell(a, b, c, n, d);
1940
+ #elif SIMSIMD_TARGET_NEON
1941
+ simsimd_bilinear_bf16_neon(a, b, c, n, d);
1942
+ #else
1943
+ simsimd_bilinear_bf16_serial(a, b, c, n, d);
1944
+ #endif
1945
+ }
1946
+ SIMSIMD_PUBLIC void simsimd_mahalanobis_bf16(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_bf16_t const* c,
1947
+ simsimd_size_t n, simsimd_distance_t* d) {
1948
+ #if SIMSIMD_TARGET_GENOA
1949
+ simsimd_mahalanobis_bf16_genoa(a, b, c, n, d);
1950
+ #elif SIMSIMD_TARGET_HASWELL
1951
+ simsimd_mahalanobis_bf16_haswell(a, b, c, n, d);
1952
+ #elif SIMSIMD_TARGET_NEON
1953
+ simsimd_mahalanobis_bf16_neon(a, b, c, n, d);
1954
+ #else
1955
+ simsimd_mahalanobis_bf16_serial(a, b, c, n, d);
1956
+ #endif
1957
+ }
1958
+
1485
1959
  #endif
1486
1960
 
1487
1961
  #ifdef __cplusplus