usearch 2.10.3 → 2.10.5

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 (112) hide show
  1. package/README.md +1 -1
  2. package/binding.gyp +101 -0
  3. package/c/CMakeLists.txt +27 -0
  4. package/c/README.md +34 -0
  5. package/c/lib.cpp +362 -0
  6. package/c/test.c +382 -0
  7. package/c/usearch.h +356 -0
  8. package/cmake/clipp.cmake +7 -0
  9. package/cmake/config.cmake.in +15 -0
  10. package/cmake/emscripten.cmake +8 -0
  11. package/cmake/jemalloc.cmake +62 -0
  12. package/cmake/pkg-config.pc.in +4 -0
  13. package/cmake/usearchConfigVersion.cmake.in +20 -0
  14. package/cpp/CMakeLists.txt +14 -0
  15. package/cpp/README.md +161 -0
  16. package/cpp/bench.cpp +616 -0
  17. package/cpp/test.cpp +324 -0
  18. package/fp16/CMakeLists.txt +176 -0
  19. package/fp16/LICENSE +11 -0
  20. package/fp16/README.md +20 -0
  21. package/fp16/bench/alt-element.cc +89 -0
  22. package/fp16/bench/from-alt-array.cc +175 -0
  23. package/fp16/bench/from-ieee-array.cc +399 -0
  24. package/fp16/bench/ieee-element.cc +272 -0
  25. package/fp16/bench/to-alt-array.cc +91 -0
  26. package/fp16/bench/to-ieee-array.cc +303 -0
  27. package/fp16/cmake/DownloadGoogleBenchmark.cmake +15 -0
  28. package/fp16/cmake/DownloadGoogleTest.cmake +15 -0
  29. package/fp16/cmake/DownloadPSimd.cmake +15 -0
  30. package/fp16/configure.py +65 -0
  31. package/fp16/confu.yaml +8 -0
  32. package/fp16/include/fp16/__init__.py +0 -0
  33. package/fp16/include/fp16/avx.py +50 -0
  34. package/fp16/include/fp16/avx2.py +53 -0
  35. package/fp16/include/fp16/bitcasts.h +92 -0
  36. package/fp16/include/fp16/fp16.h +451 -0
  37. package/fp16/include/fp16/psimd.h +131 -0
  38. package/fp16/include/fp16.h +11 -0
  39. package/fp16/jni/Android.mk +6 -0
  40. package/fp16/jni/Application.mk +4 -0
  41. package/fp16/test/alt-from-fp32-value.cc +498 -0
  42. package/fp16/test/alt-to-fp32-bits.cc +263 -0
  43. package/fp16/test/alt-to-fp32-psimd.cc +145 -0
  44. package/fp16/test/alt-to-fp32-value.cc +348 -0
  45. package/fp16/test/alt-to-fp32x2-psimd.cc +245 -0
  46. package/fp16/test/bitcasts.cc +56 -0
  47. package/fp16/test/ieee-from-fp32-value.cc +523 -0
  48. package/fp16/test/ieee-to-fp32-bits.cc +333 -0
  49. package/fp16/test/ieee-to-fp32-psimd.cc +312 -0
  50. package/fp16/test/ieee-to-fp32-value.cc +420 -0
  51. package/fp16/test/ieee-to-fp32x2-psimd.cc +541 -0
  52. package/fp16/test/peachpy/alt-xmm-to-fp32-xmm-avx.cc +96 -0
  53. package/fp16/test/peachpy/alt-xmm-to-fp32-ymm-avx2.cc +96 -0
  54. package/fp16/test/peachpy/stubs.py +38 -0
  55. package/fp16/test/tables.cc +530 -0
  56. package/fp16/test/tables.h +13 -0
  57. package/fp16/third-party/THHalf.h +92 -0
  58. package/fp16/third-party/eigen-half.h +170 -0
  59. package/fp16/third-party/float16-compressor.h +80 -0
  60. package/fp16/third-party/half.hpp +2916 -0
  61. package/fp16/third-party/npy-halffloat.h +328 -0
  62. package/include/usearch/index.hpp +3838 -0
  63. package/include/usearch/index_dense.hpp +2004 -0
  64. package/include/usearch/index_plugins.hpp +2255 -0
  65. package/javascript/dist-package-cjs.json +3 -0
  66. package/javascript/dist-package-esm.json +3 -0
  67. package/javascript/lib.cpp +350 -0
  68. package/javascript/node-gyp-build.d.ts +1 -0
  69. package/javascript/tsconfig-base.json +25 -0
  70. package/javascript/tsconfig-cjs.json +8 -0
  71. package/javascript/tsconfig-esm.json +8 -0
  72. package/javascript/usearch.test.js +84 -0
  73. package/javascript/usearch.ts +640 -0
  74. package/package.json +10 -3
  75. package/prebuilds/darwin-arm64+x64/usearch.node +0 -0
  76. package/prebuilds/linux-arm64/usearch.node +0 -0
  77. package/prebuilds/linux-x64/usearch.node +0 -0
  78. package/prebuilds/win32-ia32/usearch.node +0 -0
  79. package/prebuilds/win32-x64/usearch.node +0 -0
  80. package/simsimd/.releaserc +87 -0
  81. package/simsimd/CONTRIBUTING.md +114 -0
  82. package/simsimd/Cargo.lock +709 -0
  83. package/simsimd/Cargo.toml +44 -0
  84. package/simsimd/LICENSE +201 -0
  85. package/simsimd/README.md +708 -0
  86. package/simsimd/binding.gyp +36 -0
  87. package/simsimd/build.rs +51 -0
  88. package/simsimd/c/lib.c +110 -0
  89. package/simsimd/include/simsimd/binary.h +258 -0
  90. package/simsimd/include/simsimd/dot.h +1237 -0
  91. package/simsimd/include/simsimd/geospatial.h +37 -0
  92. package/simsimd/include/simsimd/probability.h +563 -0
  93. package/simsimd/include/simsimd/simsimd.h +1129 -0
  94. package/simsimd/include/simsimd/spatial.h +1019 -0
  95. package/simsimd/include/simsimd/types.h +309 -0
  96. package/simsimd/javascript/bench.js +127 -0
  97. package/simsimd/javascript/dist-package-cjs.json +3 -0
  98. package/simsimd/javascript/dist-package-esm.json +3 -0
  99. package/simsimd/javascript/fallback.ts +203 -0
  100. package/simsimd/javascript/lib.c +107 -0
  101. package/simsimd/javascript/node-gyp-build.d.ts +1 -0
  102. package/simsimd/javascript/simsimd.ts +135 -0
  103. package/simsimd/javascript/test.mjs +179 -0
  104. package/simsimd/javascript/tsconfig-base.json +37 -0
  105. package/simsimd/javascript/tsconfig-cjs.json +8 -0
  106. package/simsimd/javascript/tsconfig-esm.json +8 -0
  107. package/simsimd/package-lock.json +1709 -0
  108. package/simsimd/package.json +61 -0
  109. package/simsimd/rust/benches/cosine.rs +27 -0
  110. package/simsimd/rust/benches/native.rs +30 -0
  111. package/simsimd/rust/benches/sqeuclidean.rs +27 -0
  112. package/simsimd/rust/lib.rs +778 -0
@@ -0,0 +1,1129 @@
1
+ /**
2
+ * @file simsimd.h
3
+ * @brief SIMD-accelerated Similarity Measures and Distance Functions.
4
+ * @author Ash Vardanian
5
+ * @date March 14, 2023
6
+ * @copyright Copyright (c) 2023
7
+ *
8
+ * References:
9
+ * x86 intrinsics: https://www.intel.com/content/www/us/en/docs/intrinsics-guide/
10
+ * Arm intrinsics: https://developer.arm.com/architectures/instruction-sets/intrinsics/
11
+ * Detecting target CPU features at compile time: https://stackoverflow.com/a/28939692/2766161
12
+ */
13
+
14
+ #ifndef SIMSIMD_H
15
+ #define SIMSIMD_H
16
+
17
+ #define SIMSIMD_VERSION_MAJOR 4
18
+ #define SIMSIMD_VERSION_MINOR 2
19
+ #define SIMSIMD_VERSION_PATCH 2
20
+
21
+ /**
22
+ * @brief Removes compile-time dispatching, and replaces it with runtime dispatching.
23
+ * So the `simsimd_dot_f32` function will invoke the most advanced backend supported by the CPU,
24
+ * that runs the program, rather than the most advanced backend supported by the CPU
25
+ * used to compile the library or the downstream application.
26
+ */
27
+ #ifndef SIMSIMD_DYNAMIC_DISPATCH
28
+ #define SIMSIMD_DYNAMIC_DISPATCH (0) // true or false
29
+ #endif
30
+
31
+ #include "binary.h" // Hamming, Jaccard
32
+ #include "dot.h" // Inner (dot) product, and its conjugate
33
+ #include "geospatial.h" // Haversine and Vincenty
34
+ #include "probability.h" // Kullback-Leibler, Jensen–Shannon
35
+ #include "spatial.h" // L2, Cosine
36
+
37
+ #if SIMSIMD_TARGET_ARM
38
+ #ifdef __linux__
39
+ #include <asm/hwcap.h>
40
+ #include <sys/auxv.h>
41
+ #endif
42
+ #endif
43
+
44
+ #ifdef __cplusplus
45
+ extern "C" {
46
+ #endif
47
+
48
+ /**
49
+ * @brief Enumeration of supported metric kinds.
50
+ * Some have aliases for convenience.
51
+ */
52
+ typedef enum {
53
+ simsimd_metric_unknown_k = 0, ///< Unknown metric kind
54
+
55
+ // Classics:
56
+ simsimd_metric_dot_k = 'i', ///< Inner product
57
+ simsimd_metric_inner_k = 'i', ///< Inner product alias
58
+
59
+ simsimd_metric_vdot_k = 'v', ///< Complex inner product
60
+
61
+ simsimd_metric_cos_k = 'c', ///< Cosine similarity
62
+ simsimd_metric_cosine_k = 'c', ///< Cosine similarity alias
63
+ simsimd_metric_angular_k = 'c', ///< Cosine similarity alias
64
+
65
+ simsimd_metric_l2sq_k = 'e', ///< Squared Euclidean distance
66
+ simsimd_metric_sqeuclidean_k = 'e', ///< Squared Euclidean distance alias
67
+
68
+ // Binary:
69
+ simsimd_metric_hamming_k = 'h', ///< Hamming distance
70
+ simsimd_metric_manhattan_k = 'h', ///< Manhattan distance is same as Hamming
71
+
72
+ simsimd_metric_jaccard_k = 'j', ///< Jaccard coefficient
73
+ simsimd_metric_tanimoto_k = 'j', ///< Tanimoto coefficient is same as Jaccard
74
+
75
+ // Probability:
76
+ simsimd_metric_kl_k = 'k', ///< Kullback-Leibler divergence
77
+ simsimd_metric_kullback_leibler_k = 'k', ///< Kullback-Leibler divergence alias
78
+
79
+ simsimd_metric_js_k = 's', ///< Jensen-Shannon divergence
80
+ simsimd_metric_jensen_shannon_k = 's', ///< Jensen-Shannon divergence alias
81
+
82
+ } simsimd_metric_kind_t;
83
+
84
+ /**
85
+ * @brief Enumeration of SIMD capabilities of the target architecture.
86
+ */
87
+ typedef enum {
88
+ simsimd_cap_serial_k = 1, ///< Serial (non-SIMD) capability
89
+ simsimd_cap_any_k = 0x7FFFFFFF, ///< Mask representing any capability with `INT_MAX`
90
+
91
+ simsimd_cap_neon_k = 1 << 10, ///< ARM NEON capability
92
+ simsimd_cap_sve_k = 1 << 11, ///< ARM SVE capability
93
+ simsimd_cap_sve2_k = 1 << 12, ///< ARM SVE2 capability
94
+
95
+ simsimd_cap_haswell_k = 1 << 20, ///< x86 AVX2 capability with FMA and F16C extensions
96
+ simsimd_cap_skylake_k = 1 << 21, ///< x86 AVX512 baseline capability
97
+ simsimd_cap_ice_k = 1 << 22, ///< x86 AVX512 capability with advanced integer algos
98
+ simsimd_cap_sapphire_k = 1 << 23, ///< x86 AVX512 capability with `f16` support
99
+
100
+ } simsimd_capability_t;
101
+
102
+ /**
103
+ * @brief Enumeration of supported data types.
104
+ *
105
+ * Includes complex type descriptors which in C code would use the real counterparts,
106
+ * but the independent flags contain metadata to be passed between programming language
107
+ * interfaces.
108
+ */
109
+ typedef enum {
110
+ simsimd_datatype_unknown_k, ///< Unknown data type
111
+ simsimd_datatype_f64_k, ///< Double precision floating point
112
+ simsimd_datatype_f32_k, ///< Single precision floating point
113
+ simsimd_datatype_f16_k, ///< Half precision floating point
114
+ simsimd_datatype_i8_k, ///< 8-bit integer
115
+ simsimd_datatype_b8_k, ///< Single-bit values packed into 8-bit words
116
+
117
+ simsimd_datatype_f64c_k, ///< Complex double precision floating point
118
+ simsimd_datatype_f32c_k, ///< Complex single precision floating point
119
+ simsimd_datatype_f16c_k, ///< Complex half precision floating point
120
+ } simsimd_datatype_t;
121
+
122
+ /**
123
+ * @brief Type-punned function pointer accepting two vectors and outputting their similarity/distance.
124
+ *
125
+ * @param[in] a Pointer to the first data array.
126
+ * @param[in] b Pointer to the second data array.
127
+ * @param[in] n Number of scalar words in the input arrays.
128
+ * @param[out] d Output value as a double-precision float.
129
+ * In complex dot-products @b two double-precision scalars are exported
130
+ * for the real and imaginary parts.
131
+ */
132
+ typedef void (*simsimd_metric_punned_t)(void const* a, void const* b, simsimd_size_t n, simsimd_distance_t* d);
133
+
134
+ #if SIMSIMD_DYNAMIC_DISPATCH
135
+ SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
136
+ #else
137
+ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void);
138
+ #endif
139
+
140
+ /**
141
+ * @brief Function to determine the SIMD capabilities of the current machine at @b runtime.
142
+ * @return A bitmask of the SIMD capabilities represented as a `simsimd_capability_t` enum value.
143
+ */
144
+ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities_implementation(void) {
145
+
146
+ #if SIMSIMD_TARGET_X86
147
+
148
+ /// The states of 4 registers populated for a specific "cpuid" assembly call
149
+ union four_registers_t {
150
+ int array[4];
151
+ struct separate_t {
152
+ unsigned eax, ebx, ecx, edx;
153
+ } named;
154
+ } info1, info7;
155
+
156
+ #ifdef _MSC_VER
157
+ __cpuidex(info1.array, 1, 0);
158
+ __cpuidex(info7.array, 7, 0);
159
+ #else
160
+ __asm__ __volatile__("cpuid"
161
+ : "=a"(info1.named.eax), "=b"(info1.named.ebx), "=c"(info1.named.ecx), "=d"(info1.named.edx)
162
+ : "a"(1), "c"(0));
163
+ __asm__ __volatile__("cpuid"
164
+ : "=a"(info7.named.eax), "=b"(info7.named.ebx), "=c"(info7.named.ecx), "=d"(info7.named.edx)
165
+ : "a"(7), "c"(0));
166
+ #endif
167
+
168
+ // Check for AVX2 (Function ID 7, EBX register)
169
+ // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L148
170
+ unsigned supports_avx2 = (info7.named.ebx & 0x00000020) != 0;
171
+ // Check for F16C (Function ID 1, ECX register)
172
+ // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L107
173
+ unsigned supports_f16c = (info1.named.ecx & 0x20000000) != 0;
174
+ unsigned supports_fma = (info1.named.ecx & 0x00001000) != 0;
175
+ // Check for AVX512F (Function ID 7, EBX register)
176
+ // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L155
177
+ unsigned supports_avx512f = (info7.named.ebx & 0x00010000) != 0;
178
+ // Check for AVX512FP16 (Function ID 7, EDX register)
179
+ // https://github.com/llvm/llvm-project/blob/50598f0ff44f3a4e75706f8c53f3380fe7faa896/clang/lib/Headers/cpuid.h#L198C9-L198C23
180
+ unsigned supports_avx512fp16 = (info7.named.edx & 0x00800000) != 0;
181
+ // Check for AVX512VNNI (Function ID 7, ECX register)
182
+ unsigned supports_avx512vnni = (info7.named.ecx & 0x00000800) != 0;
183
+ // Check for AVX512IFMA (Function ID 7, EBX register)
184
+ unsigned supports_avx512ifma = (info7.named.ebx & 0x00200000) != 0;
185
+ // Check for AVX512BITALG (Function ID 7, ECX register)
186
+ unsigned supports_avx512bitalg = (info7.named.ecx & 0x00001000) != 0;
187
+ // Check for AVX512VBMI2 (Function ID 7, ECX register)
188
+ unsigned supports_avx512vbmi2 = (info7.named.ecx & 0x00000040) != 0;
189
+ // Check for AVX512VPOPCNTDQ (Function ID 7, ECX register)
190
+ unsigned supports_avx512vpopcntdq = (info7.named.ecx & 0x00004000) != 0;
191
+
192
+ // Convert specific features into CPU generations
193
+ unsigned supports_haswell = supports_avx2 && supports_f16c && supports_fma;
194
+ unsigned supports_skylake = supports_avx512f;
195
+ unsigned supports_ice = supports_avx512vnni && supports_avx512ifma && supports_avx512bitalg &&
196
+ supports_avx512vbmi2 && supports_avx512vpopcntdq;
197
+ unsigned supports_sapphire = supports_avx512fp16;
198
+
199
+ return (simsimd_capability_t)( //
200
+ (simsimd_cap_haswell_k * supports_haswell) | //
201
+ (simsimd_cap_skylake_k * supports_skylake) | //
202
+ (simsimd_cap_ice_k * supports_ice) | //
203
+ (simsimd_cap_sapphire_k * supports_sapphire) | //
204
+ (simsimd_cap_serial_k));
205
+
206
+ #endif // SIMSIMD_TARGET_X86
207
+
208
+ #if SIMSIMD_TARGET_ARM
209
+
210
+ // Every 64-bit Arm CPU supports NEON
211
+ unsigned supports_neon = 1;
212
+ unsigned supports_sve = 0;
213
+ unsigned supports_sve2 = 0;
214
+
215
+ #ifdef __linux__
216
+ unsigned long hwcap = getauxval(AT_HWCAP);
217
+ unsigned long hwcap2 = getauxval(AT_HWCAP2);
218
+ supports_sve = (hwcap & HWCAP_SVE) != 0;
219
+ supports_sve2 = (hwcap2 & HWCAP2_SVE2) != 0;
220
+ #endif
221
+
222
+ return (simsimd_capability_t)( //
223
+ (simsimd_cap_neon_k * supports_neon) | //
224
+ (simsimd_cap_sve_k * supports_sve) | //
225
+ (simsimd_cap_sve2_k * supports_sve2) | //
226
+ (simsimd_cap_serial_k));
227
+
228
+ #endif // SIMSIMD_TARGET_ARM
229
+
230
+ return simsimd_cap_serial_k;
231
+ }
232
+
233
+ #pragma GCC diagnostic push
234
+ #pragma GCC diagnostic ignored "-Wcast-function-type"
235
+ #pragma clang diagnostic push
236
+ #pragma clang diagnostic ignored "-Wcast-function-type"
237
+
238
+ /**
239
+ * @brief Determines the best suited metric implementation based on the given datatype,
240
+ * supported and allowed by hardware capabilities.
241
+ *
242
+ * @param kind The kind of metric to be evaluated.
243
+ * @param datatype The data type for which the metric needs to be evaluated.
244
+ * @param supported The hardware capabilities supported by the CPU.
245
+ * @param allowed The hardware capabilities allowed for use.
246
+ * @param metric_output Output variable for the selected similarity function.
247
+ * @param capability_output Output variable for the utilized hardware capabilities.
248
+ */
249
+ SIMSIMD_PUBLIC void simsimd_find_metric_punned( //
250
+ simsimd_metric_kind_t kind, //
251
+ simsimd_datatype_t datatype, //
252
+ simsimd_capability_t supported, //
253
+ simsimd_capability_t allowed, //
254
+ simsimd_metric_punned_t* metric_output, //
255
+ simsimd_capability_t* capability_output) {
256
+
257
+ simsimd_metric_punned_t* m = metric_output;
258
+ simsimd_capability_t* c = capability_output;
259
+ simsimd_capability_t viable = (simsimd_capability_t)(supported & allowed);
260
+ *m = (simsimd_metric_punned_t)0;
261
+ *c = (simsimd_capability_t)0;
262
+
263
+ typedef simsimd_metric_punned_t m_t;
264
+ switch (datatype) {
265
+
266
+ case simsimd_datatype_unknown_k: break;
267
+
268
+ // Double-precision floating-point vectors
269
+ case simsimd_datatype_f64_k:
270
+
271
+ #if SIMSIMD_TARGET_SVE
272
+ if (viable & simsimd_cap_skylake_k)
273
+ switch (kind) {
274
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_sve, *c = simsimd_cap_sve_k; return;
275
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_sve, *c = simsimd_cap_sve_k; return;
276
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_sve, *c = simsimd_cap_sve_k; return;
277
+ default: break;
278
+ }
279
+ #endif
280
+ #if SIMSIMD_TARGET_SKYLAKE
281
+ if (viable & simsimd_cap_skylake_k)
282
+ switch (kind) {
283
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_skylake, *c = simsimd_cap_skylake_k; return;
284
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_skylake, *c = simsimd_cap_skylake_k; return;
285
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_skylake, *c = simsimd_cap_skylake_k; return;
286
+ default: break;
287
+ }
288
+ #endif
289
+ if (viable & simsimd_cap_serial_k)
290
+ switch (kind) {
291
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64_serial, *c = simsimd_cap_serial_k; return;
292
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f64_serial, *c = simsimd_cap_serial_k; return;
293
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f64_serial, *c = simsimd_cap_serial_k; return;
294
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f64_serial, *c = simsimd_cap_serial_k; return;
295
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f64_serial, *c = simsimd_cap_serial_k; return;
296
+ default: break;
297
+ }
298
+
299
+ break;
300
+
301
+ // Single-precision floating-point vectors
302
+ case simsimd_datatype_f32_k:
303
+
304
+ #if SIMSIMD_TARGET_SVE
305
+ if (viable & simsimd_cap_sve_k)
306
+ switch (kind) {
307
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_sve, *c = simsimd_cap_sve_k; return;
308
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_sve, *c = simsimd_cap_sve_k; return;
309
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_sve, *c = simsimd_cap_sve_k; return;
310
+ default: break;
311
+ }
312
+ #endif
313
+ #if SIMSIMD_TARGET_NEON
314
+ if (viable & simsimd_cap_neon_k)
315
+ switch (kind) {
316
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_neon, *c = simsimd_cap_neon_k; return;
317
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_neon, *c = simsimd_cap_neon_k; return;
318
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_neon, *c = simsimd_cap_neon_k; return;
319
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_neon, *c = simsimd_cap_neon_k; return;
320
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_neon, *c = simsimd_cap_neon_k; return;
321
+ default: break;
322
+ }
323
+ #endif
324
+ #if SIMSIMD_TARGET_SKYLAKE
325
+ if (viable & simsimd_cap_skylake_k)
326
+ switch (kind) {
327
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_skylake, *c = simsimd_cap_skylake_k; return;
328
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_skylake, *c = simsimd_cap_skylake_k; return;
329
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_skylake, *c = simsimd_cap_skylake_k; return;
330
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_skylake, *c = simsimd_cap_skylake_k; return;
331
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_skylake, *c = simsimd_cap_skylake_k; return;
332
+ default: break;
333
+ }
334
+ #endif
335
+ if (viable & simsimd_cap_serial_k)
336
+ switch (kind) {
337
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32_serial, *c = simsimd_cap_serial_k; return;
338
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f32_serial, *c = simsimd_cap_serial_k; return;
339
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f32_serial, *c = simsimd_cap_serial_k; return;
340
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f32_serial, *c = simsimd_cap_serial_k; return;
341
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f32_serial, *c = simsimd_cap_serial_k; return;
342
+ default: break;
343
+ }
344
+
345
+ break;
346
+
347
+ // Half-precision floating-point vectors
348
+ case simsimd_datatype_f16_k:
349
+
350
+ #if SIMSIMD_TARGET_SVE
351
+ if (viable & simsimd_cap_sve_k)
352
+ switch (kind) {
353
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_sve, *c = simsimd_cap_sve_k; return;
354
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_sve, *c = simsimd_cap_sve_k; return;
355
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_sve, *c = simsimd_cap_sve_k; return;
356
+ default: break;
357
+ }
358
+ #endif
359
+ #if SIMSIMD_TARGET_NEON
360
+ if (viable & simsimd_cap_neon_k)
361
+ switch (kind) {
362
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_neon, *c = simsimd_cap_neon_k; return;
363
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_neon, *c = simsimd_cap_neon_k; return;
364
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_neon, *c = simsimd_cap_neon_k; return;
365
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_neon, *c = simsimd_cap_neon_k; return;
366
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_neon, *c = simsimd_cap_neon_k; return;
367
+ default: break;
368
+ }
369
+ #endif
370
+ #if SIMSIMD_TARGET_SAPPHIRE
371
+ if (viable & simsimd_cap_sapphire_k)
372
+ switch (kind) {
373
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
374
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
375
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
376
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
377
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_sapphire, *c = simsimd_cap_sapphire_k; return;
378
+ default: break;
379
+ }
380
+ #endif
381
+ #if SIMSIMD_TARGET_HASWELL
382
+ if (viable & simsimd_cap_haswell_k)
383
+ switch (kind) {
384
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_haswell, *c = simsimd_cap_haswell_k; return;
385
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_haswell, *c = simsimd_cap_haswell_k; return;
386
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_haswell, *c = simsimd_cap_haswell_k; return;
387
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_haswell, *c = simsimd_cap_haswell_k; return;
388
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_haswell, *c = simsimd_cap_haswell_k; return;
389
+ default: break;
390
+ }
391
+ #endif
392
+
393
+ if (viable & simsimd_cap_serial_k)
394
+ switch (kind) {
395
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16_serial, *c = simsimd_cap_serial_k; return;
396
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_f16_serial, *c = simsimd_cap_serial_k; return;
397
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_f16_serial, *c = simsimd_cap_serial_k; return;
398
+ case simsimd_metric_js_k: *m = (m_t)&simsimd_js_f16_serial, *c = simsimd_cap_serial_k; return;
399
+ case simsimd_metric_kl_k: *m = (m_t)&simsimd_kl_f16_serial, *c = simsimd_cap_serial_k; return;
400
+ default: break;
401
+ }
402
+
403
+ break;
404
+
405
+ // Single-byte integer vectors
406
+ case simsimd_datatype_i8_k:
407
+ #if SIMSIMD_TARGET_NEON
408
+ if (viable & simsimd_cap_neon_k)
409
+ switch (kind) {
410
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_cos_i8_neon, *c = simsimd_cap_neon_k; return;
411
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_neon, *c = simsimd_cap_neon_k; return;
412
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_neon, *c = simsimd_cap_neon_k; return;
413
+ default: break;
414
+ }
415
+ #endif
416
+ #if SIMSIMD_TARGET_ICE
417
+ if (viable & simsimd_cap_ice_k)
418
+ switch (kind) {
419
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_cos_i8_ice, *c = simsimd_cap_ice_k; return;
420
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_ice, *c = simsimd_cap_ice_k; return;
421
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_ice, *c = simsimd_cap_ice_k; return;
422
+ default: break;
423
+ }
424
+ #endif
425
+ #if SIMSIMD_TARGET_HASWELL
426
+ if (viable & simsimd_cap_haswell_k)
427
+ switch (kind) {
428
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_cos_i8_haswell, *c = simsimd_cap_haswell_k; return;
429
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_haswell, *c = simsimd_cap_haswell_k; return;
430
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_haswell, *c = simsimd_cap_haswell_k; return;
431
+ default: break;
432
+ }
433
+ #endif
434
+
435
+ if (viable & simsimd_cap_serial_k)
436
+ switch (kind) {
437
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_cos_i8_serial, *c = simsimd_cap_serial_k; return;
438
+ case simsimd_metric_cos_k: *m = (m_t)&simsimd_cos_i8_serial, *c = simsimd_cap_serial_k; return;
439
+ case simsimd_metric_l2sq_k: *m = (m_t)&simsimd_l2sq_i8_serial, *c = simsimd_cap_serial_k; return;
440
+ default: break;
441
+ }
442
+
443
+ break;
444
+
445
+ // Binary vectors
446
+ case simsimd_datatype_b8_k:
447
+
448
+ #if SIMSIMD_TARGET_SVE
449
+ if (viable & simsimd_cap_sve_k)
450
+ switch (kind) {
451
+ case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_sve, *c = simsimd_cap_sve_k; return;
452
+ case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_sve, *c = simsimd_cap_sve_k; return;
453
+ default: break;
454
+ }
455
+ #endif
456
+ #if SIMSIMD_TARGET_NEON
457
+ if (viable & simsimd_cap_neon_k)
458
+ switch (kind) {
459
+ case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_neon, *c = simsimd_cap_neon_k; return;
460
+ case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_neon, *c = simsimd_cap_neon_k; return;
461
+ default: break;
462
+ }
463
+ #endif
464
+ #if SIMSIMD_TARGET_ICE
465
+ if (viable & simsimd_cap_ice_k)
466
+ switch (kind) {
467
+ case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_ice, *c = simsimd_cap_ice_k; return;
468
+ case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_ice, *c = simsimd_cap_ice_k; return;
469
+ default: break;
470
+ }
471
+ #endif
472
+ #if SIMSIMD_TARGET_HASWELL
473
+ if (viable & simsimd_cap_haswell_k)
474
+ switch (kind) {
475
+ case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_haswell, *c = simsimd_cap_haswell_k; return;
476
+ case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_haswell, *c = simsimd_cap_haswell_k; return;
477
+ default: break;
478
+ }
479
+ #endif
480
+
481
+ if (viable & simsimd_cap_serial_k)
482
+ switch (kind) {
483
+ case simsimd_metric_hamming_k: *m = (m_t)&simsimd_hamming_b8_serial, *c = simsimd_cap_serial_k; return;
484
+ case simsimd_metric_jaccard_k: *m = (m_t)&simsimd_jaccard_b8_serial, *c = simsimd_cap_serial_k; return;
485
+ default: break;
486
+ }
487
+
488
+ break;
489
+
490
+ case simsimd_datatype_f32c_k:
491
+
492
+ #if SIMSIMD_TARGET_SVE
493
+ if (viable & simsimd_cap_sve_k)
494
+ switch (kind) {
495
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_sve, *c = simsimd_cap_sve_k; return;
496
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_sve, *c = simsimd_cap_sve_k; return;
497
+ default: break;
498
+ }
499
+ #endif
500
+ #if SIMSIMD_TARGET_NEON
501
+ if (viable & simsimd_cap_neon_k)
502
+ switch (kind) {
503
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_neon, *c = simsimd_cap_neon_k; return;
504
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_neon, *c = simsimd_cap_neon_k; return;
505
+ default: break;
506
+ }
507
+ #endif
508
+ #if SIMSIMD_TARGET_HASWELL
509
+ if (viable & simsimd_cap_haswell_k)
510
+ switch (kind) {
511
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_haswell, *c = simsimd_cap_haswell_k; return;
512
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_haswell, *c = simsimd_cap_haswell_k; return;
513
+ default: break;
514
+ }
515
+ #endif
516
+ #if SIMSIMD_TARGET_SKYLAKE
517
+ if (viable & simsimd_cap_skylake_k)
518
+ switch (kind) {
519
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_skylake, *c = simsimd_cap_skylake_k; return;
520
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_skylake, *c = simsimd_cap_skylake_k; return;
521
+ default: break;
522
+ }
523
+ #endif
524
+
525
+ if (viable & simsimd_cap_serial_k)
526
+ switch (kind) {
527
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f32c_serial, *c = simsimd_cap_serial_k; return;
528
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f32c_serial, *c = simsimd_cap_serial_k; return;
529
+ default: break;
530
+ }
531
+
532
+ break;
533
+
534
+ case simsimd_datatype_f64c_k:
535
+
536
+ #if SIMSIMD_TARGET_SVE
537
+ if (viable & simsimd_cap_sve_k)
538
+ switch (kind) {
539
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_sve, *c = simsimd_cap_sve_k; return;
540
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f64c_sve, *c = simsimd_cap_sve_k; return;
541
+ default: break;
542
+ }
543
+ #endif
544
+ #if SIMSIMD_TARGET_SKYLAKE
545
+ if (viable & simsimd_cap_skylake_k)
546
+ switch (kind) {
547
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_skylake, *c = simsimd_cap_skylake_k; return;
548
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f64c_skylake, *c = simsimd_cap_skylake_k; return;
549
+ default: break;
550
+ }
551
+ #endif
552
+
553
+ if (viable & simsimd_cap_serial_k)
554
+ switch (kind) {
555
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f64c_serial, *c = simsimd_cap_serial_k; return;
556
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f64c_serial, *c = simsimd_cap_serial_k; return;
557
+ default: break;
558
+ }
559
+
560
+ break;
561
+
562
+ case simsimd_datatype_f16c_k:
563
+
564
+ #if SIMSIMD_TARGET_SVE
565
+ if (viable & simsimd_cap_sve_k)
566
+ switch (kind) {
567
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_sve, *c = simsimd_cap_sve_k; return;
568
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_sve, *c = simsimd_cap_sve_k; return;
569
+ default: break;
570
+ }
571
+ #endif
572
+ #if SIMSIMD_TARGET_NEON
573
+ if (viable & simsimd_cap_neon_k)
574
+ switch (kind) {
575
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_neon, *c = simsimd_cap_neon_k; return;
576
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_neon, *c = simsimd_cap_neon_k; return;
577
+ default: break;
578
+ }
579
+ #endif
580
+ #if SIMSIMD_TARGET_HASWELL
581
+ if (viable & simsimd_cap_haswell_k)
582
+ switch (kind) {
583
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_haswell, *c = simsimd_cap_haswell_k; return;
584
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_haswell, *c = simsimd_cap_haswell_k; return;
585
+ default: break;
586
+ }
587
+ #endif
588
+ #if SIMSIMD_TARGET_SAPPHIRE
589
+ if (viable & simsimd_cap_sapphire_k)
590
+ switch (kind) {
591
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
592
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_sapphire, *c = simsimd_cap_sapphire_k; return;
593
+ default: break;
594
+ }
595
+ #endif
596
+
597
+ if (viable & simsimd_cap_serial_k)
598
+ switch (kind) {
599
+ case simsimd_metric_dot_k: *m = (m_t)&simsimd_dot_f16c_serial, *c = simsimd_cap_serial_k; return;
600
+ case simsimd_metric_vdot_k: *m = (m_t)&simsimd_vdot_f16c_serial, *c = simsimd_cap_serial_k; return;
601
+ default: break;
602
+ }
603
+
604
+ break;
605
+ }
606
+ }
607
+
608
+ #pragma clang diagnostic pop
609
+ #pragma GCC diagnostic pop
610
+
611
+ /**
612
+ * @brief Selects the most suitable metric implementation based on the given metric kind, datatype,
613
+ * and allowed capabilities. @b Don't call too often and prefer caching the `simsimd_capabilities()`.
614
+ *
615
+ * @param kind The kind of metric to be evaluated.
616
+ * @param datatype The data type for which the metric needs to be evaluated.
617
+ * @param allowed The hardware capabilities allowed for use.
618
+ * @return A function pointer to the selected metric implementation.
619
+ */
620
+ SIMSIMD_PUBLIC simsimd_metric_punned_t simsimd_metric_punned( //
621
+ simsimd_metric_kind_t kind, //
622
+ simsimd_datatype_t datatype, //
623
+ simsimd_capability_t allowed) {
624
+
625
+ simsimd_metric_punned_t result = 0;
626
+ simsimd_capability_t c = simsimd_cap_serial_k;
627
+ simsimd_capability_t supported = simsimd_capabilities();
628
+ simsimd_find_metric_punned(kind, datatype, supported, allowed, &result, &c);
629
+ return result;
630
+ }
631
+
632
+ #if SIMSIMD_DYNAMIC_DISPATCH
633
+
634
+ /* Run-time feature-testing functions
635
+ * - Check if the CPU supports NEON or SVE extensions on Arm
636
+ * - Check if the CPU supports AVX2 and F16C extensions on Haswell x86 CPUs and newer
637
+ * - Check if the CPU supports AVX512F and AVX512BW extensions on Skylake x86 CPUs and newer
638
+ * - Check if the CPU supports AVX512VNNI, AVX512IFMA, AVX512BITALG, AVX512VBMI2, and AVX512VPOPCNTDQ
639
+ * extensions on Ice Lake x86 CPUs and newer
640
+ * - Check if the CPU supports AVX512FP16 extensions on Sapphire Rapids x86 CPUs and newer
641
+ *
642
+ * @return 1 if the CPU supports the SIMD instruction set, 0 otherwise.
643
+ */
644
+ SIMSIMD_DYNAMIC int simsimd_uses_neon(void);
645
+ SIMSIMD_DYNAMIC int simsimd_uses_sve(void);
646
+ SIMSIMD_DYNAMIC int simsimd_uses_haswell(void);
647
+ SIMSIMD_DYNAMIC int simsimd_uses_skylake(void);
648
+ SIMSIMD_DYNAMIC int simsimd_uses_ice(void);
649
+ SIMSIMD_DYNAMIC int simsimd_uses_sapphire(void);
650
+ SIMSIMD_DYNAMIC simsimd_capability_t simsimd_capabilities(void);
651
+
652
+ /* Inner products
653
+ * - Dot product: the sum of the products of the corresponding elements of two vectors.
654
+ * - Complex Dot product: dot product with a conjugate first argument.
655
+ * - Complex Conjugate Dot product: dot product with a conjugate first argument.
656
+ *
657
+ * @param a The first vector.
658
+ * @param b The second vector.
659
+ * @param n The number of elements in the vectors. Even for complex variants (the number of scalars).
660
+ * @param d The output distance value.
661
+ *
662
+ * @note The dot product can be negative, to use as a distance, take `1 - a * b`.
663
+ * @note The dot product is zero if and only if the two vectors are orthogonal.
664
+ * @note Defined only for floating-point and integer data types.
665
+ */
666
+ SIMSIMD_DYNAMIC void simsimd_dot_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
667
+ simsimd_distance_t* d);
668
+ SIMSIMD_DYNAMIC void simsimd_dot_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
669
+ simsimd_distance_t* d);
670
+ SIMSIMD_DYNAMIC void simsimd_dot_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
671
+ simsimd_distance_t* d);
672
+ SIMSIMD_DYNAMIC void simsimd_dot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
673
+ simsimd_distance_t* d);
674
+ SIMSIMD_DYNAMIC void simsimd_dot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
675
+ simsimd_distance_t* d);
676
+ SIMSIMD_DYNAMIC void simsimd_dot_f64c(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
677
+ simsimd_distance_t* d);
678
+ SIMSIMD_DYNAMIC void simsimd_vdot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
679
+ simsimd_distance_t* d);
680
+ SIMSIMD_DYNAMIC void simsimd_vdot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
681
+ simsimd_distance_t* d);
682
+ SIMSIMD_DYNAMIC void simsimd_vdot_f64c(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
683
+ simsimd_distance_t* d);
684
+
685
+ /* Spatial distances
686
+ * - Cosine distance: the cosine of the angle between two vectors.
687
+ * - L2 squared distance: the squared Euclidean distance between two vectors.
688
+ *
689
+ * @param a The first vector.
690
+ * @param b The second vector.
691
+ * @param n The number of elements in the vectors.
692
+ * @param d The output distance value.
693
+ *
694
+ * @note The output distance value is non-negative.
695
+ * @note The output distance value is zero if and only if the two vectors are identical.
696
+ * @note Defined only for floating-point and integer data types.
697
+ */
698
+ SIMSIMD_DYNAMIC void simsimd_cos_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
699
+ simsimd_distance_t* d);
700
+ SIMSIMD_DYNAMIC void simsimd_cos_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
701
+ simsimd_distance_t* d);
702
+ SIMSIMD_DYNAMIC void simsimd_cos_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
703
+ simsimd_distance_t* d);
704
+ SIMSIMD_DYNAMIC void simsimd_cos_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
705
+ simsimd_distance_t* d);
706
+ SIMSIMD_DYNAMIC void simsimd_l2sq_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
707
+ simsimd_distance_t* d);
708
+ SIMSIMD_DYNAMIC void simsimd_l2sq_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
709
+ simsimd_distance_t* d);
710
+ SIMSIMD_DYNAMIC void simsimd_l2sq_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
711
+ simsimd_distance_t* d);
712
+ SIMSIMD_DYNAMIC void simsimd_l2sq_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
713
+ simsimd_distance_t* d);
714
+
715
+ /* Binary distances
716
+ * - Hamming distance: the number of positions at which the corresponding bits are different.
717
+ * - Jaccard distance: ratio of bit-level matching positions (intersection) to the total number of positions (union).
718
+ *
719
+ * @param a The first binary vector.
720
+ * @param b The second binary vector.
721
+ * @param n The number of 8-bit words in the vectors.
722
+ * @param d The output distance value.
723
+ *
724
+ * @note The output distance value is non-negative.
725
+ * @note The output distance value is zero if and only if the two vectors are identical.
726
+ * @note Defined only for binary data.
727
+ */
728
+ SIMSIMD_DYNAMIC void simsimd_hamming_b8(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n,
729
+ simsimd_distance_t* d);
730
+ SIMSIMD_DYNAMIC void simsimd_jaccard_b8(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n,
731
+ simsimd_distance_t* d);
732
+
733
+ /* Probability distributions
734
+ * - Jensen-Shannon divergence: a measure of similarity between two probability distributions.
735
+ * - Kullback-Leibler divergence: a measure of how one probability distribution diverges from a second.
736
+ *
737
+ * @param a The first descrete probability distribution.
738
+ * @param b The second descrete probability distribution.
739
+ * @param n The number of elements in the descrete distributions.
740
+ * @param d The output divergence value.
741
+ *
742
+ * @note The distributions are assumed to be normalized.
743
+ * @note The output divergence value is non-negative.
744
+ * @note The output divergence value is zero if and only if the two distributions are identical.
745
+ * @note Defined only for floating-point data types.
746
+ */
747
+ SIMSIMD_DYNAMIC void simsimd_kl_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
748
+ simsimd_distance_t* d);
749
+ SIMSIMD_DYNAMIC void simsimd_kl_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
750
+ simsimd_distance_t* d);
751
+ SIMSIMD_DYNAMIC void simsimd_kl_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
752
+ simsimd_distance_t* d);
753
+ SIMSIMD_DYNAMIC void simsimd_js_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
754
+ simsimd_distance_t* d);
755
+ SIMSIMD_DYNAMIC void simsimd_js_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
756
+ simsimd_distance_t* d);
757
+ SIMSIMD_DYNAMIC void simsimd_js_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
758
+ simsimd_distance_t* d);
759
+
760
+ #else
761
+
762
+ /* Compile-time feature-testing functions
763
+ * - Check if the CPU supports NEON or SVE extensions on Arm
764
+ * - Check if the CPU supports AVX2 and F16C extensions on Haswell x86 CPUs and newer
765
+ * - Check if the CPU supports AVX512F and AVX512BW extensions on Skylake x86 CPUs and newer
766
+ * - Check if the CPU supports AVX512VNNI, AVX512IFMA, AVX512BITALG, AVX512VBMI2, and AVX512VPOPCNTDQ
767
+ * extensions on Ice Lake x86 CPUs and newer
768
+ * - Check if the CPU supports AVX512FP16 extensions on Sapphire Rapids x86 CPUs and newer
769
+ *
770
+ * @return 1 if the CPU supports the SIMD instruction set, 0 otherwise.
771
+ */
772
+ SIMSIMD_PUBLIC int simsimd_uses_neon(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_NEON; }
773
+ SIMSIMD_PUBLIC int simsimd_uses_sve(void) { return SIMSIMD_TARGET_ARM && SIMSIMD_TARGET_SVE; }
774
+ SIMSIMD_PUBLIC int simsimd_uses_haswell(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_HASWELL; }
775
+ SIMSIMD_PUBLIC int simsimd_uses_skylake(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SKYLAKE; }
776
+ SIMSIMD_PUBLIC int simsimd_uses_ice(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_ICE; }
777
+ SIMSIMD_PUBLIC int simsimd_uses_sapphire(void) { return SIMSIMD_TARGET_X86 && SIMSIMD_TARGET_SAPPHIRE; }
778
+ SIMSIMD_PUBLIC simsimd_capability_t simsimd_capabilities(void) { return simsimd_capabilities_implementation(); }
779
+
780
+ /* Inner products
781
+ * - Dot product: the sum of the products of the corresponding elements of two vectors.
782
+ * - Complex Dot product: dot product with a conjugate first argument.
783
+ * - Complex Conjugate Dot product: dot product with a conjugate first argument.
784
+ *
785
+ * @param a The first vector.
786
+ * @param b The second vector.
787
+ * @param n The number of elements in the vectors. Even for complex variants (the number of scalars).
788
+ * @param d The output distance value.
789
+ *
790
+ * @note The dot product can be negative, to use as a distance, take `1 - a * b`.
791
+ * @note The dot product is zero if and only if the two vectors are orthogonal.
792
+ * @note Defined only for floating-point and integer data types.
793
+ */
794
+ SIMSIMD_PUBLIC void simsimd_dot_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
795
+ simsimd_distance_t* d) {
796
+ #if SIMSIMD_TARGET_SVE
797
+ simsimd_dot_f16_sve(a, b, n, d);
798
+ #elif SIMSIMD_TARGET_NEON
799
+ simsimd_dot_f16_neon(a, b, n, d);
800
+ #elif SIMSIMD_TARGET_HASWELL
801
+ simsimd_dot_f16_haswell(a, b, n, d);
802
+ #elif SIMSIMD_TARGET_SAPPHIRE
803
+ simsimd_dot_f16_sapphire(a, b, n, d);
804
+ #else
805
+ simsimd_dot_f16_serial(a, b, n, d);
806
+ #endif
807
+ }
808
+
809
+ SIMSIMD_PUBLIC void simsimd_dot_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
810
+ simsimd_distance_t* d) {
811
+ #if SIMSIMD_TARGET_SVE
812
+ simsimd_dot_f32_sve(a, b, n, d);
813
+ #elif SIMSIMD_TARGET_NEON
814
+ simsimd_dot_f32_neon(a, b, n, d);
815
+ #elif SIMSIMD_TARGET_SKYLAKE
816
+ simsimd_dot_f32_skylake(a, b, n, d);
817
+ #else
818
+ simsimd_dot_f32_serial(a, b, n, d);
819
+ #endif
820
+ }
821
+ SIMSIMD_PUBLIC void simsimd_dot_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
822
+ simsimd_distance_t* d) {
823
+ #if SIMSIMD_TARGET_SVE
824
+ simsimd_dot_f64_sve(a, b, n, d);
825
+ #elif SIMSIMD_TARGET_SKYLAKE
826
+ simsimd_dot_f64_skylake(a, b, n, d);
827
+ #else
828
+ simsimd_dot_f64_serial(a, b, n, d);
829
+ #endif
830
+ }
831
+ SIMSIMD_PUBLIC void simsimd_dot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
832
+ simsimd_distance_t* d) {
833
+ #if SIMSIMD_TARGET_SVE
834
+ simsimd_dot_f16c_sve(a, b, n, d);
835
+ #elif SIMSIMD_TARGET_NEON
836
+ simsimd_dot_f16c_neon(a, b, n, d);
837
+ #elif SIMSIMD_TARGET_HASWELL
838
+ simsimd_dot_f16c_haswell(a, b, n, d);
839
+ #elif SIMSIMD_TARGET_sapphire
840
+ simsimd_dot_f16c_sapphire(a, b, n, d);
841
+ #else
842
+ simsimd_dot_f16c_serial(a, b, n, d);
843
+ #endif
844
+ }
845
+ SIMSIMD_PUBLIC void simsimd_dot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
846
+ simsimd_distance_t* d) {
847
+ #if SIMSIMD_TARGET_SVE
848
+ simsimd_dot_f32c_sve(a, b, n, d);
849
+ #elif SIMSIMD_TARGET_NEON
850
+ simsimd_dot_f32c_neon(a, b, n, d);
851
+ #elif SIMSIMD_TARGET_HASWELL
852
+ simsimd_dot_f32c_haswell(a, b, n, d);
853
+ #elif SIMSIMD_TARGET_SKYLAKE
854
+ simsimd_dot_f32c_skylake(a, b, n, d);
855
+ #else
856
+ simsimd_dot_f32c_serial(a, b, n, d);
857
+ #endif
858
+ }
859
+ SIMSIMD_PUBLIC void simsimd_dot_f64c(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
860
+ simsimd_distance_t* d) {
861
+ #if SIMSIMD_TARGET_SVE
862
+ simsimd_dot_f64c_sve(a, b, n, d);
863
+ #elif SIMSIMD_TARGET_SKYLAKE
864
+ simsimd_dot_f64c_skylake(a, b, n, d);
865
+ #else
866
+ simsimd_dot_f64c_serial(a, b, n, d);
867
+ #endif
868
+ }
869
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
870
+ simsimd_distance_t* d) {
871
+ #if SIMSIMD_TARGET_SVE
872
+ simsimd_dot_f16c_sve(a, b, n, d);
873
+ #elif SIMSIMD_TARGET_NEON
874
+ simsimd_dot_f16c_neon(a, b, n, d);
875
+ #elif SIMSIMD_TARGET_HASWELL
876
+ simsimd_dot_f16c_haswell(a, b, n, d);
877
+ #elif SIMSIMD_TARGET_sapphire
878
+ simsimd_dot_f16c_sapphire(a, b, n, d);
879
+ #else
880
+ simsimd_dot_f16c_serial(a, b, n, d);
881
+ #endif
882
+ }
883
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
884
+ simsimd_distance_t* d) {
885
+ #if SIMSIMD_TARGET_SVE
886
+ simsimd_dot_f32c_sve(a, b, n, d);
887
+ #elif SIMSIMD_TARGET_NEON
888
+ simsimd_dot_f32c_neon(a, b, n, d);
889
+ #elif SIMSIMD_TARGET_HASWELL
890
+ simsimd_dot_f32c_haswell(a, b, n, d);
891
+ #elif SIMSIMD_TARGET_SKYLAKE
892
+ simsimd_dot_f32c_skylake(a, b, n, d);
893
+ #else
894
+ simsimd_dot_f32c_serial(a, b, n, d);
895
+ #endif
896
+ }
897
+ SIMSIMD_PUBLIC void simsimd_vdot_f64c(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
898
+ simsimd_distance_t* d) {
899
+ #if SIMSIMD_TARGET_SVE
900
+ simsimd_dot_f64c_sve(a, b, n, d);
901
+ #elif SIMSIMD_TARGET_SKYLAKE
902
+ simsimd_dot_f64c_skylake(a, b, n, d);
903
+ #else
904
+ simsimd_dot_f64c_serial(a, b, n, d);
905
+ #endif
906
+ }
907
+
908
+ /* Spatial distances
909
+ * - Cosine distance: the cosine of the angle between two vectors.
910
+ * - L2 squared distance: the squared Euclidean distance between two vectors.
911
+ *
912
+ * @param a The first vector.
913
+ * @param b The second vector.
914
+ * @param n The number of elements in the vectors.
915
+ * @param d The output distance value.
916
+ *
917
+ * @note The output distance value is non-negative.
918
+ * @note The output distance value is zero if and only if the two vectors are identical.
919
+ * @note Defined only for floating-point and integer data types.
920
+ */
921
+ SIMSIMD_PUBLIC void simsimd_cos_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
922
+ simsimd_distance_t* d) {
923
+ #if SIMSIMD_TARGET_NEON
924
+ simsimd_cos_i8_neon(a, b, n, d);
925
+ #elif SIMSIMD_TARGET_ICE
926
+ simsimd_cos_i8_ice(a, b, n, d);
927
+ #elif SIMSIMD_TARGET_HASWELL
928
+ simsimd_cos_i8_haswell(a, b, n, d);
929
+ #else
930
+ simsimd_cos_i8_serial(a, b, n, d);
931
+ #endif
932
+ }
933
+ SIMSIMD_PUBLIC void simsimd_l2sq_i8(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n,
934
+ simsimd_distance_t* d) {
935
+ #if SIMSIMD_TARGET_NEON
936
+ simsimd_l2sq_i8_neon(a, b, n, d);
937
+ #elif SIMSIMD_TARGET_ICE
938
+ simsimd_l2sq_i8_ice(a, b, n, d);
939
+ #elif SIMSIMD_TARGET_HASWELL
940
+ simsimd_l2sq_i8_haswell(a, b, n, d);
941
+ #else
942
+ simsimd_l2sq_i8_serial(a, b, n, d);
943
+ #endif
944
+ }
945
+ SIMSIMD_PUBLIC void simsimd_cos_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
946
+ simsimd_distance_t* d) {
947
+ #if SIMSIMD_TARGET_SVE
948
+ simsimd_cos_f16_sve(a, b, n, d);
949
+ #elif SIMSIMD_TARGET_NEON
950
+ simsimd_cos_f16_neon(a, b, n, d);
951
+ #elif SIMSIMD_TARGET_SAPPHIRE
952
+ simsimd_cos_f16_sapphire(a, b, n, d);
953
+ #elif SIMSIMD_TARGET_HASWELL
954
+ simsimd_cos_f16_haswell(a, b, n, d);
955
+ #else
956
+ simsimd_cos_f16_serial(a, b, n, d);
957
+ #endif
958
+ }
959
+ SIMSIMD_PUBLIC void simsimd_cos_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
960
+ simsimd_distance_t* d) {
961
+ #if SIMSIMD_TARGET_SVE
962
+ simsimd_cos_f32_sve(a, b, n, d);
963
+ #elif SIMSIMD_TARGET_NEON
964
+ simsimd_cos_f32_neon(a, b, n, d);
965
+ #elif SIMSIMD_TARGET_SKYLAKE
966
+ simsimd_cos_f32_skylake(a, b, n, d);
967
+ #else
968
+ simsimd_cos_f32_serial(a, b, n, d);
969
+ #endif
970
+ }
971
+ SIMSIMD_PUBLIC void simsimd_cos_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
972
+ simsimd_distance_t* d) {
973
+ #if SIMSIMD_TARGET_SVE
974
+ simsimd_cos_f64_sve(a, b, n, d);
975
+ #elif SIMSIMD_TARGET_SKYLAKE
976
+ simsimd_cos_f64_skylake(a, b, n, d);
977
+ #else
978
+ simsimd_cos_f64_serial(a, b, n, d);
979
+ #endif
980
+ }
981
+ SIMSIMD_PUBLIC void simsimd_l2sq_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
982
+ simsimd_distance_t* d) {
983
+ #if SIMSIMD_TARGET_SVE
984
+ simsimd_l2sq_f16_sve(a, b, n, d);
985
+ #elif SIMSIMD_TARGET_NEON
986
+ simsimd_l2sq_f16_neon(a, b, n, d);
987
+ #elif SIMSIMD_TARGET_sapphire
988
+ simsimd_l2sq_f16_sapphire(a, b, n, d);
989
+ #elif SIMSIMD_TARGET_HASWELL
990
+ simsimd_l2sq_f16_haswell(a, b, n, d);
991
+ #else
992
+ simsimd_l2sq_f16_serial(a, b, n, d);
993
+ #endif
994
+ }
995
+ SIMSIMD_PUBLIC void simsimd_l2sq_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
996
+ simsimd_distance_t* d) {
997
+ #if SIMSIMD_TARGET_SVE
998
+ simsimd_l2sq_f32_sve(a, b, n, d);
999
+ #elif SIMSIMD_TARGET_NEON
1000
+ simsimd_l2sq_f32_neon(a, b, n, d);
1001
+ #elif SIMSIMD_TARGET_SKYLAKE
1002
+ simsimd_l2sq_f32_skylake(a, b, n, d);
1003
+ #else
1004
+ simsimd_l2sq_f32_serial(a, b, n, d);
1005
+ #endif
1006
+ }
1007
+ SIMSIMD_PUBLIC void simsimd_l2sq_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1008
+ simsimd_distance_t* d) {
1009
+ #if SIMSIMD_TARGET_SVE
1010
+ simsimd_l2sq_f64_sve(a, b, n, d);
1011
+ #elif SIMSIMD_TARGET_SKYLAKE
1012
+ simsimd_l2sq_f64_skylake(a, b, n, d);
1013
+ #else
1014
+ simsimd_l2sq_f64_serial(a, b, n, d);
1015
+ #endif
1016
+ }
1017
+
1018
+ /* Binary distances
1019
+ * - Hamming distance: the number of positions at which the corresponding bits are different.
1020
+ * - Jaccard distance: ratio of bit-level matching positions (intersection) to the total number of positions (union).
1021
+ *
1022
+ * @param a The first binary vector.
1023
+ * @param b The second binary vector.
1024
+ * @param n The number of 8-bit words in the vectors.
1025
+ * @param d The output distance value.
1026
+ *
1027
+ * @note The output distance value is non-negative.
1028
+ * @note The output distance value is zero if and only if the two vectors are identical.
1029
+ * @note Defined only for binary data.
1030
+ */
1031
+ SIMSIMD_PUBLIC void simsimd_hamming_b8(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n,
1032
+ simsimd_distance_t* d) {
1033
+ #if SIMSIMD_TARGET_SVE
1034
+ simsimd_hamming_b8_sve(a, b, n, d);
1035
+ #elif SIMSIMD_TARGET_NEON
1036
+ simsimd_hamming_b8_neon(a, b, n, d);
1037
+ #elif SIMSIMD_TARGET_ICE
1038
+ simsimd_hamming_b8_ice(a, b, n, d);
1039
+ #elif SIMSIMD_TARGET_HASWELL
1040
+ simsimd_hamming_b8_haswell(a, b, n, d);
1041
+ #else
1042
+ simsimd_hamming_b8_serial(a, b, n, d);
1043
+ #endif
1044
+ }
1045
+ SIMSIMD_PUBLIC void simsimd_jaccard_b8(simsimd_b8_t const* a, simsimd_b8_t const* b, simsimd_size_t n,
1046
+ simsimd_distance_t* d) {
1047
+ #if SIMSIMD_TARGET_SVE
1048
+ simsimd_jaccard_b8_sve(a, b, n, d);
1049
+ #elif SIMSIMD_TARGET_NEON
1050
+ simsimd_jaccard_b8_neon(a, b, n, d);
1051
+ #elif SIMSIMD_TARGET_ICE
1052
+ simsimd_jaccard_b8_ice(a, b, n, d);
1053
+ #elif SIMSIMD_TARGET_HASWELL
1054
+ simsimd_jaccard_b8_haswell(a, b, n, d);
1055
+ #else
1056
+ simsimd_jaccard_b8_serial(a, b, n, d);
1057
+ #endif
1058
+ }
1059
+
1060
+ /* Probability distributions
1061
+ * - Jensen-Shannon divergence: a measure of similarity between two probability distributions.
1062
+ * - Kullback-Leibler divergence: a measure of how one probability distribution diverges from a second.
1063
+ *
1064
+ * @param a The first descrete probability distribution.
1065
+ * @param b The second descrete probability distribution.
1066
+ * @param n The number of elements in the descrete distributions.
1067
+ * @param d The output divergence value.
1068
+ *
1069
+ * @note The distributions are assumed to be normalized.
1070
+ * @note The output divergence value is non-negative.
1071
+ * @note The output divergence value is zero if and only if the two distributions are identical.
1072
+ * @note Defined only for floating-point data types.
1073
+ */
1074
+ SIMSIMD_PUBLIC void simsimd_kl_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1075
+ simsimd_distance_t* d) {
1076
+ #if SIMSIMD_TARGET_NEON
1077
+ simsimd_kl_f16_neon(a, b, n, d);
1078
+ #elif SIMSIMD_TARGET_HASWELL
1079
+ simsimd_kl_f16_haswell(a, b, n, d);
1080
+ #else
1081
+ simsimd_kl_f16_serial(a, b, n, d);
1082
+ #endif
1083
+ }
1084
+ SIMSIMD_PUBLIC void simsimd_kl_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1085
+ simsimd_distance_t* d) {
1086
+ #if SIMSIMD_TARGET_NEON
1087
+ simsimd_kl_f32_neon(a, b, n, d);
1088
+ #elif SIMSIMD_TARGET_SKYLAKE
1089
+ simsimd_kl_f32_skylake(a, b, n, d);
1090
+ #else
1091
+ simsimd_kl_f32_serial(a, b, n, d);
1092
+ #endif
1093
+ }
1094
+ SIMSIMD_PUBLIC void simsimd_kl_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1095
+ simsimd_distance_t* d) {
1096
+ simsimd_kl_f64_serial(a, b, n, d);
1097
+ }
1098
+ SIMSIMD_PUBLIC void simsimd_js_f16(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n,
1099
+ simsimd_distance_t* d) {
1100
+ #if SIMSIMD_TARGET_NEON
1101
+ simsimd_js_f16_neon(a, b, n, d);
1102
+ #elif SIMSIMD_TARGET_HASWELL
1103
+ simsimd_js_f16_haswell(a, b, n, d);
1104
+ #else
1105
+ simsimd_js_f16_serial(a, b, n, d);
1106
+ #endif
1107
+ }
1108
+ SIMSIMD_PUBLIC void simsimd_js_f32(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n,
1109
+ simsimd_distance_t* d) {
1110
+ #if SIMSIMD_TARGET_NEON
1111
+ simsimd_js_f32_neon(a, b, n, d);
1112
+ #elif SIMSIMD_TARGET_SKYLAKE
1113
+ simsimd_js_f32_skylake(a, b, n, d);
1114
+ #else
1115
+ simsimd_js_f32_serial(a, b, n, d);
1116
+ #endif
1117
+ }
1118
+ SIMSIMD_PUBLIC void simsimd_js_f64(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n,
1119
+ simsimd_distance_t* d) {
1120
+ simsimd_js_f64_serial(a, b, n, d);
1121
+ }
1122
+
1123
+ #endif
1124
+
1125
+ #ifdef __cplusplus
1126
+ }
1127
+ #endif
1128
+
1129
+ #endif // SIMSIMD_H