usearch 2.16.7 → 2.16.9

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.
@@ -39,20 +39,20 @@ extern "C" {
39
39
  * For double-precision computation check out the "*_accurate" variants of those "*_serial" functions.
40
40
  */
41
41
  SIMSIMD_PUBLIC void simsimd_dot_f64_serial(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
42
- SIMSIMD_PUBLIC void simsimd_dot_f64c_serial(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* results);
43
- SIMSIMD_PUBLIC void simsimd_vdot_f64c_serial(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* results);
42
+ SIMSIMD_PUBLIC void simsimd_dot_f64c_serial(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
43
+ SIMSIMD_PUBLIC void simsimd_vdot_f64c_serial(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
44
44
 
45
45
  SIMSIMD_PUBLIC void simsimd_dot_f32_serial(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
46
- SIMSIMD_PUBLIC void simsimd_dot_f32c_serial(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
47
- SIMSIMD_PUBLIC void simsimd_vdot_f32c_serial(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
46
+ SIMSIMD_PUBLIC void simsimd_dot_f32c_serial(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
47
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c_serial(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
48
48
 
49
49
  SIMSIMD_PUBLIC void simsimd_dot_f16_serial(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
50
- SIMSIMD_PUBLIC void simsimd_dot_f16c_serial(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
51
- SIMSIMD_PUBLIC void simsimd_vdot_f16c_serial(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
50
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_serial(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
51
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c_serial(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
52
52
 
53
53
  SIMSIMD_PUBLIC void simsimd_dot_bf16_serial(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
54
- SIMSIMD_PUBLIC void simsimd_dot_bf16c_serial(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
55
- SIMSIMD_PUBLIC void simsimd_vdot_bf16c_serial(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
54
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c_serial(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
55
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_serial(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
56
56
 
57
57
  SIMSIMD_PUBLIC void simsimd_dot_i8_serial(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
58
58
  SIMSIMD_PUBLIC void simsimd_dot_u8_serial(simsimd_u8_t const* a, simsimd_u8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
@@ -61,50 +61,50 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_serial(simsimd_u8_t const* a, simsimd_u8_t co
61
61
  * For single-precision computation check out the "*_serial" counterparts of those "*_accurate" functions.
62
62
  */
63
63
  SIMSIMD_PUBLIC void simsimd_dot_f32_accurate(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
64
- SIMSIMD_PUBLIC void simsimd_dot_f32c_accurate(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
65
- SIMSIMD_PUBLIC void simsimd_vdot_f32c_accurate(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
64
+ SIMSIMD_PUBLIC void simsimd_dot_f32c_accurate(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
65
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c_accurate(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
66
66
 
67
67
  SIMSIMD_PUBLIC void simsimd_dot_f16_accurate(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
68
- SIMSIMD_PUBLIC void simsimd_dot_f16c_accurate(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
69
- SIMSIMD_PUBLIC void simsimd_vdot_f16c_accurate(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
68
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_accurate(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
69
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c_accurate(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
70
70
 
71
71
  SIMSIMD_PUBLIC void simsimd_dot_bf16_accurate(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
72
- SIMSIMD_PUBLIC void simsimd_dot_bf16c_accurate(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
73
- SIMSIMD_PUBLIC void simsimd_vdot_bf16c_accurate(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
72
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c_accurate(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
73
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_accurate(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
74
74
 
75
75
  /* SIMD-powered backends for Arm NEON, mostly using 32-bit arithmetic over 128-bit words.
76
76
  * By far the most portable backend, covering most Arm v8 devices, over a billion phones, and almost all
77
77
  * server CPUs produced before 2023.
78
78
  */
79
79
  SIMSIMD_PUBLIC void simsimd_dot_f32_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
80
- SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
81
- SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
80
+ SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
81
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
82
82
 
83
83
  SIMSIMD_PUBLIC void simsimd_dot_f16_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
84
- SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
85
- SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
84
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
85
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
86
86
 
87
87
  SIMSIMD_PUBLIC void simsimd_dot_i8_neon(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
88
88
  SIMSIMD_PUBLIC void simsimd_dot_u8_neon(simsimd_u8_t const* a, simsimd_u8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
89
89
 
90
90
  SIMSIMD_PUBLIC void simsimd_dot_bf16_neon(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
91
- SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
92
- SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
91
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
92
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
93
93
 
94
94
  /* SIMD-powered backends for Arm SVE, mostly using 32-bit arithmetic over variable-length platform-defined word sizes.
95
95
  * Designed for Arm Graviton 3, Microsoft Cobalt, as well as Nvidia Grace and newer Ampere Altra CPUs.
96
96
  */
97
97
  SIMSIMD_PUBLIC void simsimd_dot_f32_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
98
- SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
99
- SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
98
+ SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
99
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
100
100
 
101
101
  SIMSIMD_PUBLIC void simsimd_dot_f16_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
102
- SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
103
- SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
102
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
103
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
104
104
 
105
105
  SIMSIMD_PUBLIC void simsimd_dot_f64_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
106
- SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* results);
107
- SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* results);
106
+ SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
107
+ SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
108
108
 
109
109
  /* SIMD-powered backends for AVX2 CPUs of Haswell generation and newer, using 32-bit arithmetic over 256-bit words.
110
110
  * First demonstrated in 2011, at least one Haswell-based processor was still being sold in 2022 — the Pentium G3420.
@@ -113,12 +113,12 @@ SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const* a, simsimd_f64_t
113
113
  * properly vectorized by recent compilers.
114
114
  */
115
115
  SIMSIMD_PUBLIC void simsimd_dot_f32_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
116
- SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
117
- SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* results);
116
+ SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
117
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
118
118
 
119
119
  SIMSIMD_PUBLIC void simsimd_dot_f16_haswell(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
120
- SIMSIMD_PUBLIC void simsimd_dot_f16c_haswell(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
121
- SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* results);
120
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_haswell(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
121
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
122
122
 
123
123
  SIMSIMD_PUBLIC void simsimd_dot_bf16_haswell(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
124
124
 
@@ -130,25 +130,35 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_haswell(simsimd_u8_t const* a, simsimd_u8_t c
130
130
  * Ice Lake added VNNI, VPOPCNTDQ, IFMA, VBMI, VAES, GFNI, VBMI2, BITALG, VPCLMULQDQ, and other extensions for integral operations.
131
131
  * Genoa added only BF16.
132
132
  * Sapphire Rapids added tiled matrix operations, but we are most interested in the new mixed-precision FMA instructions.
133
+ *
134
+ * Sadly, we can't effectively interleave different kinds of arithmetic instructions to utilize more ports:
135
+ *
136
+ * > Like Intel server architectures since Skylake-X, SPR cores feature two 512-bit FMA units, and organize them in a similar fashion.
137
+ * > One 512-bit FMA unit is created by fusing two 256-bit ones on port 0 and port 1. The other is added to port 5, as a server-specific
138
+ * > core extension. The FMA units on port 0 and 1 are configured into 2×256-bit or 1×512-bit mode depending on whether 512-bit FMA
139
+ * > instructions are present in the scheduler. That means a mix of 256-bit and 512-bit FMA instructions will not achieve higher IPC
140
+ * > than executing 512-bit instructions alone.
141
+ *
142
+ * Source: https://chipsandcheese.com/p/a-peek-at-sapphire-rapids
133
143
  */
134
144
  SIMSIMD_PUBLIC void simsimd_dot_f64_skylake(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
135
- SIMSIMD_PUBLIC void simsimd_dot_f64c_skylake(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
136
- SIMSIMD_PUBLIC void simsimd_vdot_f64c_skylake(simsimd_f64_t const* a, simsimd_f64_t const* b, simsimd_size_t n, simsimd_distance_t* result);
145
+ SIMSIMD_PUBLIC void simsimd_dot_f64c_skylake(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
146
+ SIMSIMD_PUBLIC void simsimd_vdot_f64c_skylake(simsimd_f64c_t const* a, simsimd_f64c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
137
147
 
138
148
  SIMSIMD_PUBLIC void simsimd_dot_f32_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
139
- SIMSIMD_PUBLIC void simsimd_dot_f32c_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
140
- SIMSIMD_PUBLIC void simsimd_vdot_f32c_skylake(simsimd_f32_t const* a, simsimd_f32_t const* b, simsimd_size_t n, simsimd_distance_t* result);
149
+ SIMSIMD_PUBLIC void simsimd_dot_f32c_skylake(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
150
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c_skylake(simsimd_f32c_t const* a, simsimd_f32c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
141
151
 
142
152
  SIMSIMD_PUBLIC void simsimd_dot_i8_ice(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
143
153
  SIMSIMD_PUBLIC void simsimd_dot_u8_ice(simsimd_u8_t const* a, simsimd_u8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
144
154
 
145
155
  SIMSIMD_PUBLIC void simsimd_dot_bf16_genoa(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
146
- SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
147
- SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(simsimd_bf16_t const* a, simsimd_bf16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
156
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
157
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(simsimd_bf16c_t const* a, simsimd_bf16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
148
158
 
149
159
  SIMSIMD_PUBLIC void simsimd_dot_f16_sapphire(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
150
- SIMSIMD_PUBLIC void simsimd_dot_f16c_sapphire(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
151
- SIMSIMD_PUBLIC void simsimd_vdot_f16c_sapphire(simsimd_f16_t const* a, simsimd_f16_t const* b, simsimd_size_t n, simsimd_distance_t* result);
160
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_sapphire(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
161
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c_sapphire(simsimd_f16c_t const* a, simsimd_f16c_t const* b, simsimd_size_t n, simsimd_distance_t* results);
152
162
 
153
163
  SIMSIMD_PUBLIC void simsimd_dot_i8_sierra(simsimd_i8_t const* a, simsimd_i8_t const* b, simsimd_size_t n, simsimd_distance_t* result);
154
164
  // clang-format on
@@ -166,70 +176,70 @@ SIMSIMD_PUBLIC void simsimd_dot_i8_sierra(simsimd_i8_t const* a, simsimd_i8_t co
166
176
  *result = ab; \
167
177
  }
168
178
 
169
- #define SIMSIMD_MAKE_COMPLEX_DOT(name, input_type, accumulator_type, load_and_convert) \
170
- SIMSIMD_PUBLIC void simsimd_dot_##input_type##c_##name(simsimd_##input_type##_t const *a, \
171
- simsimd_##input_type##_t const *b, simsimd_size_t n, \
172
- simsimd_distance_t *results) { \
173
- simsimd_##accumulator_type##_t ab_real = 0, ab_imag = 0; \
174
- for (simsimd_size_t i = 0; i + 2 <= n; i += 2) { \
175
- simsimd_##accumulator_type##_t ar = load_and_convert(a + i); \
176
- simsimd_##accumulator_type##_t br = load_and_convert(b + i); \
177
- simsimd_##accumulator_type##_t ai = load_and_convert(a + i + 1); \
178
- simsimd_##accumulator_type##_t bi = load_and_convert(b + i + 1); \
179
- ab_real += ar * br - ai * bi; \
180
- ab_imag += ar * bi + ai * br; \
181
- } \
182
- results[0] = ab_real; \
183
- results[1] = ab_imag; \
179
+ #define SIMSIMD_MAKE_COMPLEX_DOT(name, input_type, accumulator_type, load_and_convert) \
180
+ SIMSIMD_PUBLIC void simsimd_dot_##input_type##_##name(simsimd_##input_type##_t const *a_pairs, \
181
+ simsimd_##input_type##_t const *b_pairs, \
182
+ simsimd_size_t count_pairs, simsimd_distance_t *results) { \
183
+ simsimd_##accumulator_type##_t ab_real = 0, ab_imag = 0; \
184
+ for (simsimd_size_t i = 0; i != count_pairs; ++i) { \
185
+ simsimd_##accumulator_type##_t ar = load_and_convert(&(a_pairs + i)->real); \
186
+ simsimd_##accumulator_type##_t br = load_and_convert(&(b_pairs + i)->real); \
187
+ simsimd_##accumulator_type##_t ai = load_and_convert(&(a_pairs + i)->imag); \
188
+ simsimd_##accumulator_type##_t bi = load_and_convert(&(b_pairs + i)->imag); \
189
+ ab_real += ar * br - ai * bi; \
190
+ ab_imag += ar * bi + ai * br; \
191
+ } \
192
+ results[0] = ab_real; \
193
+ results[1] = ab_imag; \
184
194
  }
185
195
 
186
- #define SIMSIMD_MAKE_COMPLEX_VDOT(name, input_type, accumulator_type, load_and_convert) \
187
- SIMSIMD_PUBLIC void simsimd_vdot_##input_type##c_##name(simsimd_##input_type##_t const *a, \
188
- simsimd_##input_type##_t const *b, simsimd_size_t n, \
189
- simsimd_distance_t *results) { \
190
- simsimd_##accumulator_type##_t ab_real = 0, ab_imag = 0; \
191
- for (simsimd_size_t i = 0; i + 2 <= n; i += 2) { \
192
- simsimd_##accumulator_type##_t ar = load_and_convert(a + i); \
193
- simsimd_##accumulator_type##_t br = load_and_convert(b + i); \
194
- simsimd_##accumulator_type##_t ai = load_and_convert(a + i + 1); \
195
- simsimd_##accumulator_type##_t bi = load_and_convert(b + i + 1); \
196
- ab_real += ar * br + ai * bi; \
197
- ab_imag += ar * bi - ai * br; \
198
- } \
199
- results[0] = ab_real; \
200
- results[1] = ab_imag; \
196
+ #define SIMSIMD_MAKE_COMPLEX_VDOT(name, input_type, accumulator_type, load_and_convert) \
197
+ SIMSIMD_PUBLIC void simsimd_vdot_##input_type##_##name(simsimd_##input_type##_t const *a_pairs, \
198
+ simsimd_##input_type##_t const *b_pairs, \
199
+ simsimd_size_t count_pairs, simsimd_distance_t *results) { \
200
+ simsimd_##accumulator_type##_t ab_real = 0, ab_imag = 0; \
201
+ for (simsimd_size_t i = 0; i != count_pairs; ++i) { \
202
+ simsimd_##accumulator_type##_t ar = load_and_convert(&(a_pairs + i)->real); \
203
+ simsimd_##accumulator_type##_t br = load_and_convert(&(b_pairs + i)->real); \
204
+ simsimd_##accumulator_type##_t ai = load_and_convert(&(a_pairs + i)->imag); \
205
+ simsimd_##accumulator_type##_t bi = load_and_convert(&(b_pairs + i)->imag); \
206
+ ab_real += ar * br + ai * bi; \
207
+ ab_imag += ar * bi - ai * br; \
208
+ } \
209
+ results[0] = ab_real; \
210
+ results[1] = ab_imag; \
201
211
  }
202
212
 
203
- SIMSIMD_MAKE_DOT(serial, f64, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f64_serial
204
- SIMSIMD_MAKE_COMPLEX_DOT(serial, f64, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f64c_serial
205
- SIMSIMD_MAKE_COMPLEX_VDOT(serial, f64, f64, SIMSIMD_DEREFERENCE) // simsimd_vdot_f64c_serial
213
+ SIMSIMD_MAKE_DOT(serial, f64, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f64_serial
214
+ SIMSIMD_MAKE_COMPLEX_DOT(serial, f64c, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f64c_serial
215
+ SIMSIMD_MAKE_COMPLEX_VDOT(serial, f64c, f64, SIMSIMD_DEREFERENCE) // simsimd_vdot_f64c_serial
206
216
 
207
- SIMSIMD_MAKE_DOT(serial, f32, f32, SIMSIMD_DEREFERENCE) // simsimd_dot_f32_serial
208
- SIMSIMD_MAKE_COMPLEX_DOT(serial, f32, f32, SIMSIMD_DEREFERENCE) // simsimd_dot_f32c_serial
209
- SIMSIMD_MAKE_COMPLEX_VDOT(serial, f32, f32, SIMSIMD_DEREFERENCE) // simsimd_vdot_f32c_serial
217
+ SIMSIMD_MAKE_DOT(serial, f32, f32, SIMSIMD_DEREFERENCE) // simsimd_dot_f32_serial
218
+ SIMSIMD_MAKE_COMPLEX_DOT(serial, f32c, f32, SIMSIMD_DEREFERENCE) // simsimd_dot_f32c_serial
219
+ SIMSIMD_MAKE_COMPLEX_VDOT(serial, f32c, f32, SIMSIMD_DEREFERENCE) // simsimd_vdot_f32c_serial
210
220
 
211
- SIMSIMD_MAKE_DOT(serial, f16, f32, SIMSIMD_F16_TO_F32) // simsimd_dot_f16_serial
212
- SIMSIMD_MAKE_COMPLEX_DOT(serial, f16, f32, SIMSIMD_F16_TO_F32) // simsimd_dot_f16c_serial
213
- SIMSIMD_MAKE_COMPLEX_VDOT(serial, f16, f32, SIMSIMD_F16_TO_F32) // simsimd_vdot_f16c_serial
221
+ SIMSIMD_MAKE_DOT(serial, f16, f32, SIMSIMD_F16_TO_F32) // simsimd_dot_f16_serial
222
+ SIMSIMD_MAKE_COMPLEX_DOT(serial, f16c, f32, SIMSIMD_F16_TO_F32) // simsimd_dot_f16c_serial
223
+ SIMSIMD_MAKE_COMPLEX_VDOT(serial, f16c, f32, SIMSIMD_F16_TO_F32) // simsimd_vdot_f16c_serial
214
224
 
215
- SIMSIMD_MAKE_DOT(serial, bf16, f32, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16_serial
216
- SIMSIMD_MAKE_COMPLEX_DOT(serial, bf16, f32, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16c_serial
217
- SIMSIMD_MAKE_COMPLEX_VDOT(serial, bf16, f32, SIMSIMD_BF16_TO_F32) // simsimd_vdot_bf16c_serial
225
+ SIMSIMD_MAKE_DOT(serial, bf16, f32, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16_serial
226
+ SIMSIMD_MAKE_COMPLEX_DOT(serial, bf16c, f32, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16c_serial
227
+ SIMSIMD_MAKE_COMPLEX_VDOT(serial, bf16c, f32, SIMSIMD_BF16_TO_F32) // simsimd_vdot_bf16c_serial
218
228
 
219
229
  SIMSIMD_MAKE_DOT(serial, i8, i64, SIMSIMD_DEREFERENCE) // simsimd_dot_i8_serial
220
230
  SIMSIMD_MAKE_DOT(serial, u8, i64, SIMSIMD_DEREFERENCE) // simsimd_dot_u8_serial
221
231
 
222
- SIMSIMD_MAKE_DOT(accurate, f32, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f32_accurate
223
- SIMSIMD_MAKE_COMPLEX_DOT(accurate, f32, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f32c_accurate
224
- SIMSIMD_MAKE_COMPLEX_VDOT(accurate, f32, f64, SIMSIMD_DEREFERENCE) // simsimd_vdot_f32c_accurate
232
+ SIMSIMD_MAKE_DOT(accurate, f32, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f32_accurate
233
+ SIMSIMD_MAKE_COMPLEX_DOT(accurate, f32c, f64, SIMSIMD_DEREFERENCE) // simsimd_dot_f32c_accurate
234
+ SIMSIMD_MAKE_COMPLEX_VDOT(accurate, f32c, f64, SIMSIMD_DEREFERENCE) // simsimd_vdot_f32c_accurate
225
235
 
226
- SIMSIMD_MAKE_DOT(accurate, f16, f64, SIMSIMD_F16_TO_F32) // simsimd_dot_f16_accurate
227
- SIMSIMD_MAKE_COMPLEX_DOT(accurate, f16, f64, SIMSIMD_F16_TO_F32) // simsimd_dot_f16c_accurate
228
- SIMSIMD_MAKE_COMPLEX_VDOT(accurate, f16, f64, SIMSIMD_F16_TO_F32) // simsimd_vdot_f16c_accurate
236
+ SIMSIMD_MAKE_DOT(accurate, f16, f64, SIMSIMD_F16_TO_F32) // simsimd_dot_f16_accurate
237
+ SIMSIMD_MAKE_COMPLEX_DOT(accurate, f16c, f64, SIMSIMD_F16_TO_F32) // simsimd_dot_f16c_accurate
238
+ SIMSIMD_MAKE_COMPLEX_VDOT(accurate, f16c, f64, SIMSIMD_F16_TO_F32) // simsimd_vdot_f16c_accurate
229
239
 
230
- SIMSIMD_MAKE_DOT(accurate, bf16, f64, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16_accurate
231
- SIMSIMD_MAKE_COMPLEX_DOT(accurate, bf16, f64, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16c_accurate
232
- SIMSIMD_MAKE_COMPLEX_VDOT(accurate, bf16, f64, SIMSIMD_BF16_TO_F32) // simsimd_vdot_bf16c_accurate
240
+ SIMSIMD_MAKE_DOT(accurate, bf16, f64, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16_accurate
241
+ SIMSIMD_MAKE_COMPLEX_DOT(accurate, bf16c, f64, SIMSIMD_BF16_TO_F32) // simsimd_dot_bf16c_accurate
242
+ SIMSIMD_MAKE_COMPLEX_VDOT(accurate, bf16c, f64, SIMSIMD_BF16_TO_F32) // simsimd_vdot_bf16c_accurate
233
243
 
234
244
  #if _SIMSIMD_TARGET_ARM
235
245
  #if SIMSIMD_TARGET_NEON
@@ -237,40 +247,40 @@ SIMSIMD_MAKE_COMPLEX_VDOT(accurate, bf16, f64, SIMSIMD_BF16_TO_F32) // simsimd_v
237
247
  #pragma GCC target("arch=armv8.2-a+simd")
238
248
  #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd"))), apply_to = function)
239
249
 
240
- SIMSIMD_INTERNAL float32x4_t _simsimd_partial_load_f32x4_neon(simsimd_f32_t const *a, simsimd_size_t n) {
250
+ SIMSIMD_INTERNAL float32x4_t _simsimd_partial_load_f32x4_neon(simsimd_f32_t const *x, simsimd_size_t n) {
241
251
  union {
242
252
  float32x4_t vec;
243
253
  simsimd_f32_t scalars[4];
244
254
  } result;
245
255
  simsimd_size_t i = 0;
246
- for (; i < n; ++i) result.scalars[i] = a[i];
256
+ for (; i < n; ++i) result.scalars[i] = x[i];
247
257
  for (; i < 4; ++i) result.scalars[i] = 0;
248
258
  return result.vec;
249
259
  }
250
260
 
251
- SIMSIMD_PUBLIC void simsimd_dot_f32_neon(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
252
- simsimd_distance_t *result) {
261
+ SIMSIMD_PUBLIC void simsimd_dot_f32_neon(simsimd_f32_t const *a_scalars, simsimd_f32_t const *b_scalars,
262
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
253
263
  float32x4_t ab_vec = vdupq_n_f32(0);
254
- simsimd_size_t i = 0;
255
- for (; i + 4 <= n; i += 4) {
256
- float32x4_t a_vec = vld1q_f32(a + i);
257
- float32x4_t b_vec = vld1q_f32(b + i);
264
+ simsimd_size_t idx_scalars = 0;
265
+ for (; idx_scalars + 4 <= count_scalars; idx_scalars += 4) {
266
+ float32x4_t a_vec = vld1q_f32(a_scalars + idx_scalars);
267
+ float32x4_t b_vec = vld1q_f32(b_scalars + idx_scalars);
258
268
  ab_vec = vfmaq_f32(ab_vec, a_vec, b_vec);
259
269
  }
260
270
  simsimd_f32_t ab = vaddvq_f32(ab_vec);
261
- for (; i < n; ++i) ab += a[i] * b[i];
271
+ for (; idx_scalars < count_scalars; ++idx_scalars) ab += a_scalars[idx_scalars] * b_scalars[idx_scalars];
262
272
  *result = ab;
263
273
  }
264
274
 
265
- SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n, //
266
- simsimd_distance_t *results) {
275
+ SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
276
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
267
277
  float32x4_t ab_real_vec = vdupq_n_f32(0);
268
278
  float32x4_t ab_imag_vec = vdupq_n_f32(0);
269
- simsimd_size_t i = 0;
270
- for (; i + 8 <= n; i += 8) {
279
+ simsimd_size_t idx_pairs = 0;
280
+ for (; idx_pairs + 4 <= count_pairs; idx_pairs += 4) {
271
281
  // Unpack the input arrays into real and imaginary parts:
272
- float32x4x2_t a_vec = vld2q_f32(a + i);
273
- float32x4x2_t b_vec = vld2q_f32(b + i);
282
+ float32x4x2_t a_vec = vld2q_f32((simsimd_f32_t const *)(a_pairs + idx_pairs));
283
+ float32x4x2_t b_vec = vld2q_f32((simsimd_f32_t const *)(b_pairs + idx_pairs));
274
284
  float32x4_t a_real_vec = a_vec.val[0];
275
285
  float32x4_t a_imag_vec = a_vec.val[1];
276
286
  float32x4_t b_real_vec = b_vec.val[0];
@@ -288,8 +298,9 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(simsimd_f32_t const *a, simsimd_f32_t
288
298
  simsimd_f32_t ab_imag = vaddvq_f32(ab_imag_vec);
289
299
 
290
300
  // Handle the tail:
291
- for (; i + 2 <= n; i += 2) {
292
- simsimd_f32_t ar = a[i], ai = a[i + 1], br = b[i], bi = b[i + 1];
301
+ for (; idx_pairs != count_pairs; ++idx_pairs) {
302
+ simsimd_f32c_t a_pair = a_pairs[idx_pairs], b_pair = b_pairs[idx_pairs];
303
+ simsimd_f32_t ar = a_pair.real, ai = a_pair.imag, br = b_pair.real, bi = b_pair.imag;
293
304
  ab_real += ar * br - ai * bi;
294
305
  ab_imag += ar * bi + ai * br;
295
306
  }
@@ -297,15 +308,15 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_neon(simsimd_f32_t const *a, simsimd_f32_t
297
308
  results[1] = ab_imag;
298
309
  }
299
310
 
300
- SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n, //
301
- simsimd_distance_t *results) {
311
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
312
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
302
313
  float32x4_t ab_real_vec = vdupq_n_f32(0);
303
314
  float32x4_t ab_imag_vec = vdupq_n_f32(0);
304
- simsimd_size_t i = 0;
305
- for (; i + 8 <= n; i += 8) {
315
+ simsimd_size_t idx_pairs = 0;
316
+ for (; idx_pairs + 4 <= count_pairs; idx_pairs += 4) {
306
317
  // Unpack the input arrays into real and imaginary parts:
307
- float32x4x2_t a_vec = vld2q_f32(a + i);
308
- float32x4x2_t b_vec = vld2q_f32(b + i);
318
+ float32x4x2_t a_vec = vld2q_f32((simsimd_f32_t const *)(a_pairs + idx_pairs));
319
+ float32x4x2_t b_vec = vld2q_f32((simsimd_f32_t const *)(b_pairs + idx_pairs));
309
320
  float32x4_t a_real_vec = a_vec.val[0];
310
321
  float32x4_t a_imag_vec = a_vec.val[1];
311
322
  float32x4_t b_real_vec = b_vec.val[0];
@@ -323,8 +334,9 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32_t const *a, simsimd_f32_t
323
334
  simsimd_f32_t ab_imag = vaddvq_f32(ab_imag_vec);
324
335
 
325
336
  // Handle the tail:
326
- for (; i + 2 <= n; i += 2) {
327
- simsimd_f32_t ar = a[i], ai = a[i + 1], br = b[i], bi = b[i + 1];
337
+ for (; idx_pairs != count_pairs; ++idx_pairs) {
338
+ simsimd_f32c_t a_pair = a_pairs[idx_pairs], b_pair = b_pairs[idx_pairs];
339
+ simsimd_f32_t ar = a_pair.real, ai = a_pair.imag, br = b_pair.real, bi = b_pair.imag;
328
340
  ab_real += ar * br + ai * bi;
329
341
  ab_imag += ar * bi - ai * br;
330
342
  }
@@ -341,51 +353,51 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_neon(simsimd_f32_t const *a, simsimd_f32_t
341
353
  #pragma GCC target("arch=armv8.2-a+dotprod")
342
354
  #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+dotprod"))), apply_to = function)
343
355
 
344
- SIMSIMD_PUBLIC void simsimd_dot_i8_neon(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
345
- simsimd_distance_t *result) {
356
+ SIMSIMD_PUBLIC void simsimd_dot_i8_neon(simsimd_i8_t const *a_scalars, simsimd_i8_t const *b_scalars,
357
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
346
358
 
347
359
  int32x4_t ab_vec = vdupq_n_s32(0);
348
- simsimd_size_t i = 0;
360
+ simsimd_size_t idx_scalars = 0;
349
361
 
350
362
  // If the 128-bit `vdot_s32` intrinsic is unavailable, we can use the 64-bit `vdot_s32`.
351
- // for (simsimd_size_t i = 0; i != n; i += 8) {
352
- // int16x8_t a_vec = vmovl_s8(vld1_s8(a + i));
353
- // int16x8_t b_vec = vmovl_s8(vld1_s8(b + i));
363
+ // for (simsimd_size_t idx_scalars = 0; idx_scalars != n; idx_scalars += 8) {
364
+ // int16x8_t a_vec = vmovl_s8(vld1_s8(a_scalars + idx_scalars));
365
+ // int16x8_t b_vec = vmovl_s8(vld1_s8(b_scalars + idx_scalars));
354
366
  // int16x8_t ab_part_vec = vmulq_s16(a_vec, b_vec);
355
367
  // ab_vec = vaddq_s32(ab_vec, vaddq_s32(vmovl_s16(vget_high_s16(ab_part_vec)), //
356
368
  // vmovl_s16(vget_low_s16(ab_part_vec))));
357
369
  // }
358
- for (; i + 16 <= n; i += 16) {
359
- int8x16_t a_vec = vld1q_s8(a + i);
360
- int8x16_t b_vec = vld1q_s8(b + i);
370
+ for (; idx_scalars + 16 <= count_scalars; idx_scalars += 16) {
371
+ int8x16_t a_vec = vld1q_s8(a_scalars + idx_scalars);
372
+ int8x16_t b_vec = vld1q_s8(b_scalars + idx_scalars);
361
373
  ab_vec = vdotq_s32(ab_vec, a_vec, b_vec);
362
374
  }
363
375
 
364
376
  // Take care of the tail:
365
- int32_t ab = vaddvq_s32(ab_vec);
366
- for (; i < n; ++i) {
367
- int32_t ai = a[i], bi = b[i];
377
+ simsimd_i32_t ab = vaddvq_s32(ab_vec);
378
+ for (; idx_scalars < count_scalars; ++idx_scalars) {
379
+ simsimd_i32_t ai = a_scalars[idx_scalars], bi = b_scalars[idx_scalars];
368
380
  ab += ai * bi;
369
381
  }
370
382
 
371
383
  *result = ab;
372
384
  }
373
385
 
374
- SIMSIMD_PUBLIC void simsimd_dot_u8_neon(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
375
- simsimd_distance_t *result) {
386
+ SIMSIMD_PUBLIC void simsimd_dot_u8_neon(simsimd_u8_t const *a_scalars, simsimd_u8_t const *b_scalars,
387
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
376
388
 
377
389
  uint32x4_t ab_vec = vdupq_n_u32(0);
378
- simsimd_size_t i = 0;
379
- for (; i + 16 <= n; i += 16) {
380
- uint8x16_t a_vec = vld1q_u8(a + i);
381
- uint8x16_t b_vec = vld1q_u8(b + i);
390
+ simsimd_size_t idx_scalars = 0;
391
+ for (; idx_scalars + 16 <= count_scalars; idx_scalars += 16) {
392
+ uint8x16_t a_vec = vld1q_u8(a_scalars + idx_scalars);
393
+ uint8x16_t b_vec = vld1q_u8(b_scalars + idx_scalars);
382
394
  ab_vec = vdotq_u32(ab_vec, a_vec, b_vec);
383
395
  }
384
396
 
385
397
  // Take care of the tail:
386
- uint32_t ab = vaddvq_u32(ab_vec);
387
- for (; i < n; ++i) {
388
- uint32_t ai = a[i], bi = b[i];
398
+ simsimd_u32_t ab = vaddvq_u32(ab_vec);
399
+ for (; idx_scalars < count_scalars; ++idx_scalars) {
400
+ simsimd_u32_t ai = a_scalars[idx_scalars], bi = b_scalars[idx_scalars];
389
401
  ab += ai * bi;
390
402
  }
391
403
 
@@ -401,7 +413,7 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_neon(simsimd_u8_t const *a, simsimd_u8_t cons
401
413
  #pragma GCC target("arch=armv8.2-a+simd+fp16")
402
414
  #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+simd+fp16"))), apply_to = function)
403
415
 
404
- SIMSIMD_INTERNAL float16x4_t _simsimd_partial_load_f16x4_neon(simsimd_f16_t const *a, simsimd_size_t n) {
416
+ SIMSIMD_INTERNAL float16x4_t _simsimd_partial_load_f16x4_neon(simsimd_f16_t const *x, simsimd_size_t n) {
405
417
  // In case the software emulation for `f16` scalars is enabled, the `simsimd_f16_to_f32`
406
418
  // function will run. It is extremely slow, so even for the tail, let's combine serial
407
419
  // loads and stores with vectorized math.
@@ -410,35 +422,35 @@ SIMSIMD_INTERNAL float16x4_t _simsimd_partial_load_f16x4_neon(simsimd_f16_t cons
410
422
  simsimd_f16_t scalars[4];
411
423
  } result;
412
424
  simsimd_size_t i = 0;
413
- for (; i < n; ++i) result.scalars[i] = a[i];
425
+ for (; i < n; ++i) result.scalars[i] = x[i];
414
426
  for (; i < 4; ++i) result.scalars[i] = 0;
415
427
  return result.vec;
416
428
  }
417
429
 
418
- SIMSIMD_PUBLIC void simsimd_dot_f16_neon(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
419
- simsimd_distance_t *result) {
420
- float32x4_t ab_vec = vdupq_n_f32(0);
430
+ SIMSIMD_PUBLIC void simsimd_dot_f16_neon(simsimd_f16_t const *a_scalars, simsimd_f16_t const *b_scalars,
431
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
421
432
  float32x4_t a_vec, b_vec;
433
+ float32x4_t ab_vec = vdupq_n_f32(0);
422
434
  simsimd_size_t i = 0;
423
435
 
424
436
  simsimd_dot_f16_neon_cycle:
425
- if (n < 4) {
426
- a_vec = vcvt_f32_f16(_simsimd_partial_load_f16x4_neon(a, n));
427
- b_vec = vcvt_f32_f16(_simsimd_partial_load_f16x4_neon(b, n));
428
- n = 0;
437
+ if (count_scalars < 4) {
438
+ a_vec = vcvt_f32_f16(_simsimd_partial_load_f16x4_neon(a_scalars, count_scalars));
439
+ b_vec = vcvt_f32_f16(_simsimd_partial_load_f16x4_neon(b_scalars, count_scalars));
440
+ count_scalars = 0;
429
441
  }
430
442
  else {
431
- a_vec = vcvt_f32_f16(vld1_f16((simsimd_f16_for_arm_simd_t const *)a));
432
- b_vec = vcvt_f32_f16(vld1_f16((simsimd_f16_for_arm_simd_t const *)b));
433
- a += 4, b += 4, n -= 4;
443
+ a_vec = vcvt_f32_f16(vld1_f16((simsimd_f16_for_arm_simd_t const *)a_scalars));
444
+ b_vec = vcvt_f32_f16(vld1_f16((simsimd_f16_for_arm_simd_t const *)b_scalars));
445
+ a_scalars += 4, b_scalars += 4, count_scalars -= 4;
434
446
  }
435
447
  ab_vec = vfmaq_f32(ab_vec, a_vec, b_vec);
436
- if (n) goto simsimd_dot_f16_neon_cycle;
448
+ if (count_scalars) goto simsimd_dot_f16_neon_cycle;
437
449
  *result = vaddvq_f32(ab_vec);
438
450
  }
439
451
 
440
- SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n, //
441
- simsimd_distance_t *results) {
452
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
453
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
442
454
 
443
455
  // A nicer approach is to use `f16` arithmetic for the dot product, but that requires
444
456
  // FMLA extensions available on Arm v8.3 and later. That we can also process 16 entries
@@ -446,12 +458,12 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t
446
458
  float32x4_t ab_real_vec = vdupq_n_f32(0);
447
459
  float32x4_t ab_imag_vec = vdupq_n_f32(0);
448
460
 
449
- while (n >= 8) {
461
+ while (count_pairs >= 4) {
450
462
  // Unpack the input arrays into real and imaginary parts.
451
463
  // MSVC sadly doesn't recognize the `vld2_f16`, so we load the data as signed
452
464
  // integers of the same size and reinterpret with `vreinterpret_f16_s16` afterwards.
453
- int16x4x2_t a_vec = vld2_s16((short *)a);
454
- int16x4x2_t b_vec = vld2_s16((short *)b);
465
+ int16x4x2_t a_vec = vld2_s16((short *)a_pairs);
466
+ int16x4x2_t b_vec = vld2_s16((short *)b_pairs);
455
467
  float32x4_t a_real_vec = vcvt_f32_f16(vreinterpret_f16_s16(a_vec.val[0]));
456
468
  float32x4_t a_imag_vec = vcvt_f32_f16(vreinterpret_f16_s16(a_vec.val[1]));
457
469
  float32x4_t b_real_vec = vcvt_f32_f16(vreinterpret_f16_s16(b_vec.val[0]));
@@ -463,17 +475,17 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t
463
475
  ab_imag_vec = vfmaq_f32(ab_imag_vec, a_real_vec, b_imag_vec);
464
476
  ab_imag_vec = vfmaq_f32(ab_imag_vec, a_imag_vec, b_real_vec);
465
477
 
466
- n -= 8, a += 8, b += 8;
478
+ count_pairs -= 4, a_pairs += 4, b_pairs += 4;
467
479
  }
468
480
 
469
481
  // Reduce horizontal sums and aggregate with the tail:
470
- simsimd_dot_f16c_serial(a, b, n, results);
482
+ simsimd_dot_f16c_serial(a_pairs, b_pairs, count_pairs, results);
471
483
  results[0] += vaddvq_f32(ab_real_vec);
472
484
  results[1] += vaddvq_f32(ab_imag_vec);
473
485
  }
474
486
 
475
- SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n, //
476
- simsimd_distance_t *results) {
487
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
488
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
477
489
 
478
490
  // A nicer approach is to use `f16` arithmetic for the dot product, but that requires
479
491
  // FMLA extensions available on Arm v8.3 and later. That we can also process 16 entries
@@ -481,12 +493,12 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t
481
493
  float32x4_t ab_real_vec = vdupq_n_f32(0);
482
494
  float32x4_t ab_imag_vec = vdupq_n_f32(0);
483
495
 
484
- while (n >= 8) {
496
+ while (count_pairs >= 4) {
485
497
  // Unpack the input arrays into real and imaginary parts.
486
498
  // MSVC sadly doesn't recognize the `vld2_f16`, so we load the data as signed
487
499
  // integers of the same size and reinterpret with `vreinterpret_f16_s16` afterwards.
488
- int16x4x2_t a_vec = vld2_s16((short *)a);
489
- int16x4x2_t b_vec = vld2_s16((short *)b);
500
+ int16x4x2_t a_vec = vld2_s16((short *)a_pairs);
501
+ int16x4x2_t b_vec = vld2_s16((short *)b_pairs);
490
502
  float32x4_t a_real_vec = vcvt_f32_f16(vreinterpret_f16_s16(a_vec.val[0]));
491
503
  float32x4_t a_imag_vec = vcvt_f32_f16(vreinterpret_f16_s16(a_vec.val[1]));
492
504
  float32x4_t b_real_vec = vcvt_f32_f16(vreinterpret_f16_s16(b_vec.val[0]));
@@ -498,11 +510,11 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t
498
510
  ab_imag_vec = vfmaq_f32(ab_imag_vec, a_real_vec, b_imag_vec);
499
511
  ab_imag_vec = vfmsq_f32(ab_imag_vec, a_imag_vec, b_real_vec);
500
512
 
501
- n -= 8, a += 8, b += 8;
513
+ count_pairs -= 4, a_pairs += 4, b_pairs += 4;
502
514
  }
503
515
 
504
516
  // Reduce horizontal sums and aggregate with the tail:
505
- simsimd_vdot_f16c_serial(a, b, n, results);
517
+ simsimd_vdot_f16c_serial(a_pairs, b_pairs, count_pairs, results);
506
518
  results[0] += vaddvq_f32(ab_real_vec);
507
519
  results[1] += vaddvq_f32(ab_imag_vec);
508
520
  }
@@ -516,41 +528,41 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_neon(simsimd_f16_t const *a, simsimd_f16_t
516
528
  #pragma GCC target("arch=armv8.6-a+simd+bf16")
517
529
  #pragma clang attribute push(__attribute__((target("arch=armv8.6-a+simd+bf16"))), apply_to = function)
518
530
 
519
- SIMSIMD_INTERNAL bfloat16x8_t _simsimd_partial_load_bf16x8_neon(simsimd_bf16_t const *a, simsimd_size_t n) {
531
+ SIMSIMD_INTERNAL bfloat16x8_t _simsimd_partial_load_bf16x8_neon(simsimd_bf16_t const *x, simsimd_size_t n) {
520
532
  union {
521
533
  bfloat16x8_t vec;
522
534
  simsimd_bf16_t scalars[8];
523
535
  } result;
524
536
  simsimd_size_t i = 0;
525
- for (; i < n; ++i) result.scalars[i] = a[i];
537
+ for (; i < n; ++i) result.scalars[i] = x[i];
526
538
  for (; i < 8; ++i) result.scalars[i] = 0;
527
539
  return result.vec;
528
540
  }
529
541
 
530
- SIMSIMD_PUBLIC void simsimd_dot_bf16_neon(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
531
- simsimd_distance_t *result) {
532
- float32x4_t ab_vec = vdupq_n_f32(0);
542
+ SIMSIMD_PUBLIC void simsimd_dot_bf16_neon(simsimd_bf16_t const *a_scalars, simsimd_bf16_t const *b_scalars,
543
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
533
544
  bfloat16x8_t a_vec, b_vec;
545
+ float32x4_t ab_vec = vdupq_n_f32(0);
534
546
 
535
547
  simsimd_dot_bf16_neon_cycle:
536
- if (n < 8) {
537
- a_vec = _simsimd_partial_load_bf16x8_neon(a, n);
538
- b_vec = _simsimd_partial_load_bf16x8_neon(b, n);
539
- n = 0;
548
+ if (count_scalars < 4) {
549
+ a_vec = _simsimd_partial_load_bf16x8_neon(a_scalars, count_scalars);
550
+ b_vec = _simsimd_partial_load_bf16x8_neon(b_scalars, count_scalars);
551
+ count_scalars = 0;
540
552
  }
541
553
  else {
542
- a_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const *)a);
543
- b_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const *)b);
544
- a += 8, b += 8, n -= 8;
554
+ a_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const *)a_scalars);
555
+ b_vec = vld1q_bf16((simsimd_bf16_for_arm_simd_t const *)b_scalars);
556
+ a_scalars += 4, b_scalars += 4, count_scalars -= 4;
545
557
  }
546
558
  ab_vec = vbfdotq_f32(ab_vec, a_vec, b_vec);
547
- if (n) goto simsimd_dot_bf16_neon_cycle;
559
+ if (count_scalars) goto simsimd_dot_bf16_neon_cycle;
548
560
 
549
561
  *result = vaddvq_f32(ab_vec);
550
562
  }
551
563
 
552
- SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
553
- simsimd_distance_t *results) {
564
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(simsimd_bf16c_t const *a_pairs, simsimd_bf16c_t const *b_pairs,
565
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
554
566
 
555
567
  // A nicer approach is to use `bf16` arithmetic for the dot product, but that requires
556
568
  // FMLA extensions available on Arm v8.3 and later. That we can also process 16 entries
@@ -558,12 +570,12 @@ SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf16
558
570
  float32x4_t ab_real_vec = vdupq_n_f32(0);
559
571
  float32x4_t ab_imag_vec = vdupq_n_f32(0);
560
572
 
561
- while (n >= 8) {
573
+ while (count_pairs >= 4) {
562
574
  // Unpack the input arrays into real and imaginary parts.
563
575
  // MSVC sadly doesn't recognize the `vld2_bf16`, so we load the data as signed
564
576
  // integers of the same size and reinterpret with `vreinterpret_bf16_s16` afterwards.
565
- int16x4x2_t a_vec = vld2_s16((short *)a);
566
- int16x4x2_t b_vec = vld2_s16((short *)b);
577
+ int16x4x2_t a_vec = vld2_s16((short const *)a_pairs);
578
+ int16x4x2_t b_vec = vld2_s16((short const *)b_pairs);
567
579
  float32x4_t a_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[0]));
568
580
  float32x4_t a_imag_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[1]));
569
581
  float32x4_t b_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(b_vec.val[0]));
@@ -575,17 +587,17 @@ SIMSIMD_PUBLIC void simsimd_dot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf16
575
587
  ab_imag_vec = vfmaq_f32(ab_imag_vec, a_real_vec, b_imag_vec);
576
588
  ab_imag_vec = vfmaq_f32(ab_imag_vec, a_imag_vec, b_real_vec);
577
589
 
578
- n -= 8, a += 8, b += 8;
590
+ count_pairs -= 4, a_pairs += 4, b_pairs += 4;
579
591
  }
580
592
 
581
593
  // Reduce horizontal sums and aggregate with the tail:
582
- simsimd_dot_bf16c_serial(a, b, n, results);
594
+ simsimd_dot_bf16c_serial(a_pairs, b_pairs, count_pairs, results);
583
595
  results[0] += vaddvq_f32(ab_real_vec);
584
596
  results[1] += vaddvq_f32(ab_imag_vec);
585
597
  }
586
598
 
587
- SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n, //
588
- simsimd_distance_t *results) {
599
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16c_t const *a_pairs, simsimd_bf16c_t const *b_pairs,
600
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
589
601
 
590
602
  // A nicer approach is to use `bf16` arithmetic for the dot product, but that requires
591
603
  // FMLA extensions available on Arm v8.3 and later. That we can also process 16 entries
@@ -593,12 +605,12 @@ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf1
593
605
  float32x4_t ab_real_vec = vdupq_n_f32(0);
594
606
  float32x4_t ab_imag_vec = vdupq_n_f32(0);
595
607
 
596
- while (n >= 8) {
608
+ while (count_pairs >= 4) {
597
609
  // Unpack the input arrays into real and imaginary parts.
598
610
  // MSVC sadly doesn't recognize the `vld2_bf16`, so we load the data as signed
599
611
  // integers of the same size and reinterpret with `vreinterpret_bf16_s16` afterwards.
600
- int16x4x2_t a_vec = vld2_s16((short *)a);
601
- int16x4x2_t b_vec = vld2_s16((short *)b);
612
+ int16x4x2_t a_vec = vld2_s16((short const *)a_pairs);
613
+ int16x4x2_t b_vec = vld2_s16((short const *)b_pairs);
602
614
  float32x4_t a_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[0]));
603
615
  float32x4_t a_imag_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(a_vec.val[1]));
604
616
  float32x4_t b_real_vec = vcvt_f32_bf16(vreinterpret_bf16_s16(b_vec.val[0]));
@@ -610,11 +622,11 @@ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf1
610
622
  ab_imag_vec = vfmaq_f32(ab_imag_vec, a_real_vec, b_imag_vec);
611
623
  ab_imag_vec = vfmsq_f32(ab_imag_vec, a_imag_vec, b_real_vec);
612
624
 
613
- n -= 8, a += 8, b += 8;
625
+ count_pairs -= 4, a_pairs += 4, b_pairs += 4;
614
626
  }
615
627
 
616
628
  // Reduce horizontal sums and aggregate with the tail:
617
- simsimd_vdot_bf16c_serial(a, b, n, results);
629
+ simsimd_vdot_bf16c_serial(a_pairs, b_pairs, count_pairs, results);
618
630
  results[0] += vaddvq_f32(ab_real_vec);
619
631
  results[1] += vaddvq_f32(ab_imag_vec);
620
632
  }
@@ -629,29 +641,29 @@ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_neon(simsimd_bf16_t const *a, simsimd_bf1
629
641
  #pragma GCC target("arch=armv8.2-a+sve")
630
642
  #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+sve"))), apply_to = function)
631
643
 
632
- SIMSIMD_PUBLIC void simsimd_dot_f32_sve(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
633
- simsimd_distance_t *result) {
634
- simsimd_size_t i = 0;
644
+ SIMSIMD_PUBLIC void simsimd_dot_f32_sve(simsimd_f32_t const *a_scalars, simsimd_f32_t const *b_scalars,
645
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
646
+ simsimd_size_t idx_scalars = 0;
635
647
  svfloat32_t ab_vec = svdup_f32(0.f);
636
648
  do {
637
- svbool_t pg_vec = svwhilelt_b32((unsigned int)i, (unsigned int)n);
638
- svfloat32_t a_vec = svld1_f32(pg_vec, a + i);
639
- svfloat32_t b_vec = svld1_f32(pg_vec, b + i);
649
+ svbool_t pg_vec = svwhilelt_b32((unsigned int)idx_scalars, (unsigned int)count_scalars);
650
+ svfloat32_t a_vec = svld1_f32(pg_vec, a_scalars + idx_scalars);
651
+ svfloat32_t b_vec = svld1_f32(pg_vec, b_scalars + idx_scalars);
640
652
  ab_vec = svmla_f32_x(pg_vec, ab_vec, a_vec, b_vec);
641
- i += svcntw();
642
- } while (i < n);
653
+ idx_scalars += svcntw();
654
+ } while (idx_scalars < count_scalars);
643
655
  *result = svaddv_f32(svptrue_b32(), ab_vec);
644
656
  }
645
657
 
646
- SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
647
- simsimd_distance_t *results) {
648
- simsimd_size_t i = 0;
658
+ SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
659
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
660
+ simsimd_size_t idx_pairs = 0;
649
661
  svfloat32_t ab_real_vec = svdup_f32(0.f);
650
662
  svfloat32_t ab_imag_vec = svdup_f32(0.f);
651
663
  do {
652
- svbool_t pg_vec = svwhilelt_b32((unsigned int)i / 2, (unsigned int)n / 2);
653
- svfloat32x2_t a_vec = svld2_f32(pg_vec, a + i);
654
- svfloat32x2_t b_vec = svld2_f32(pg_vec, b + i);
664
+ svbool_t pg_vec = svwhilelt_b32((unsigned int)idx_pairs, (unsigned int)count_pairs);
665
+ svfloat32x2_t a_vec = svld2_f32(pg_vec, (simsimd_f32_t const *)(a_pairs + idx_pairs));
666
+ svfloat32x2_t b_vec = svld2_f32(pg_vec, (simsimd_f32_t const *)(b_pairs + idx_pairs));
655
667
  svfloat32_t a_real_vec = svget2_f32(a_vec, 0);
656
668
  svfloat32_t a_imag_vec = svget2_f32(a_vec, 1);
657
669
  svfloat32_t b_real_vec = svget2_f32(b_vec, 0);
@@ -660,21 +672,21 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_sve(simsimd_f32_t const *a, simsimd_f32_t c
660
672
  ab_real_vec = svmls_f32_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
661
673
  ab_imag_vec = svmla_f32_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
662
674
  ab_imag_vec = svmla_f32_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
663
- i += svcntw() * 2;
664
- } while (i < n);
675
+ idx_pairs += svcntw();
676
+ } while (idx_pairs < count_pairs);
665
677
  results[0] = svaddv_f32(svptrue_b32(), ab_real_vec);
666
678
  results[1] = svaddv_f32(svptrue_b32(), ab_imag_vec);
667
679
  }
668
680
 
669
- SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
670
- simsimd_distance_t *results) {
671
- simsimd_size_t i = 0;
681
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
682
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
683
+ simsimd_size_t idx_pairs = 0;
672
684
  svfloat32_t ab_real_vec = svdup_f32(0.f);
673
685
  svfloat32_t ab_imag_vec = svdup_f32(0.f);
674
686
  do {
675
- svbool_t pg_vec = svwhilelt_b32((unsigned int)i / 2, (unsigned int)n / 2);
676
- svfloat32x2_t a_vec = svld2_f32(pg_vec, a + i);
677
- svfloat32x2_t b_vec = svld2_f32(pg_vec, b + i);
687
+ svbool_t pg_vec = svwhilelt_b32((unsigned int)idx_pairs, (unsigned int)count_pairs);
688
+ svfloat32x2_t a_vec = svld2_f32(pg_vec, (simsimd_f32_t const *)(a_pairs + idx_pairs));
689
+ svfloat32x2_t b_vec = svld2_f32(pg_vec, (simsimd_f32_t const *)(b_pairs + idx_pairs));
678
690
  svfloat32_t a_real_vec = svget2_f32(a_vec, 0);
679
691
  svfloat32_t a_imag_vec = svget2_f32(a_vec, 1);
680
692
  svfloat32_t b_real_vec = svget2_f32(b_vec, 0);
@@ -683,35 +695,35 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_sve(simsimd_f32_t const *a, simsimd_f32_t
683
695
  ab_real_vec = svmla_f32_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
684
696
  ab_imag_vec = svmla_f32_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
685
697
  ab_imag_vec = svmls_f32_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
686
- i += svcntw() * 2;
687
- } while (i < n);
698
+ idx_pairs += svcntw();
699
+ } while (idx_pairs < count_pairs);
688
700
  results[0] = svaddv_f32(svptrue_b32(), ab_real_vec);
689
701
  results[1] = svaddv_f32(svptrue_b32(), ab_imag_vec);
690
702
  }
691
703
 
692
- SIMSIMD_PUBLIC void simsimd_dot_f64_sve(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
693
- simsimd_distance_t *result) {
694
- simsimd_size_t i = 0;
704
+ SIMSIMD_PUBLIC void simsimd_dot_f64_sve(simsimd_f64_t const *a_scalars, simsimd_f64_t const *b_scalars,
705
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
706
+ simsimd_size_t idx_scalars = 0;
695
707
  svfloat64_t ab_vec = svdup_f64(0.);
696
708
  do {
697
- svbool_t pg_vec = svwhilelt_b64((unsigned int)i, (unsigned int)n);
698
- svfloat64_t a_vec = svld1_f64(pg_vec, a + i);
699
- svfloat64_t b_vec = svld1_f64(pg_vec, b + i);
709
+ svbool_t pg_vec = svwhilelt_b64((unsigned int)idx_scalars, (unsigned int)count_scalars);
710
+ svfloat64_t a_vec = svld1_f64(pg_vec, a_scalars + idx_scalars);
711
+ svfloat64_t b_vec = svld1_f64(pg_vec, b_scalars + idx_scalars);
700
712
  ab_vec = svmla_f64_x(pg_vec, ab_vec, a_vec, b_vec);
701
- i += svcntd();
702
- } while (i < n);
713
+ idx_scalars += svcntd();
714
+ } while (idx_scalars < count_scalars);
703
715
  *result = svaddv_f64(svptrue_b32(), ab_vec);
704
716
  }
705
717
 
706
- SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
707
- simsimd_distance_t *results) {
708
- simsimd_size_t i = 0;
718
+ SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64c_t const *a_pairs, simsimd_f64c_t const *b_pairs,
719
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
720
+ simsimd_size_t idx_pairs = 0;
709
721
  svfloat64_t ab_real_vec = svdup_f64(0.);
710
722
  svfloat64_t ab_imag_vec = svdup_f64(0.);
711
723
  do {
712
- svbool_t pg_vec = svwhilelt_b64((unsigned int)i / 2, (unsigned int)n / 2);
713
- svfloat64x2_t a_vec = svld2_f64(pg_vec, a + i);
714
- svfloat64x2_t b_vec = svld2_f64(pg_vec, b + i);
724
+ svbool_t pg_vec = svwhilelt_b64((unsigned int)idx_pairs, (unsigned int)count_pairs);
725
+ svfloat64x2_t a_vec = svld2_f64(pg_vec, (simsimd_f64_t const *)(a_pairs + idx_pairs));
726
+ svfloat64x2_t b_vec = svld2_f64(pg_vec, (simsimd_f64_t const *)(b_pairs + idx_pairs));
715
727
  svfloat64_t a_real_vec = svget2_f64(a_vec, 0);
716
728
  svfloat64_t a_imag_vec = svget2_f64(a_vec, 1);
717
729
  svfloat64_t b_real_vec = svget2_f64(b_vec, 0);
@@ -720,21 +732,21 @@ SIMSIMD_PUBLIC void simsimd_dot_f64c_sve(simsimd_f64_t const *a, simsimd_f64_t c
720
732
  ab_real_vec = svmls_f64_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
721
733
  ab_imag_vec = svmla_f64_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
722
734
  ab_imag_vec = svmla_f64_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
723
- i += svcntd() * 2;
724
- } while (i < n);
735
+ idx_pairs += svcntd();
736
+ } while (idx_pairs < count_pairs);
725
737
  results[0] = svaddv_f64(svptrue_b64(), ab_real_vec);
726
738
  results[1] = svaddv_f64(svptrue_b64(), ab_imag_vec);
727
739
  }
728
740
 
729
- SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
730
- simsimd_distance_t *results) {
731
- simsimd_size_t i = 0;
741
+ SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64c_t const *a_pairs, simsimd_f64c_t const *b_pairs,
742
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
743
+ simsimd_size_t idx_pairs = 0;
732
744
  svfloat64_t ab_real_vec = svdup_f64(0.);
733
745
  svfloat64_t ab_imag_vec = svdup_f64(0.);
734
746
  do {
735
- svbool_t pg_vec = svwhilelt_b64((unsigned int)i / 2, (unsigned int)n / 2);
736
- svfloat64x2_t a_vec = svld2_f64(pg_vec, a + i);
737
- svfloat64x2_t b_vec = svld2_f64(pg_vec, b + i);
747
+ svbool_t pg_vec = svwhilelt_b64((unsigned int)idx_pairs, (unsigned int)count_pairs);
748
+ svfloat64x2_t a_vec = svld2_f64(pg_vec, (simsimd_f64_t const *)(a_pairs + idx_pairs));
749
+ svfloat64x2_t b_vec = svld2_f64(pg_vec, (simsimd_f64_t const *)(b_pairs + idx_pairs));
738
750
  svfloat64_t a_real_vec = svget2_f64(a_vec, 0);
739
751
  svfloat64_t a_imag_vec = svget2_f64(a_vec, 1);
740
752
  svfloat64_t b_real_vec = svget2_f64(b_vec, 0);
@@ -743,8 +755,8 @@ SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const *a, simsimd_f64_t
743
755
  ab_real_vec = svmla_f64_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
744
756
  ab_imag_vec = svmla_f64_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
745
757
  ab_imag_vec = svmls_f64_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
746
- i += svcntd() * 2;
747
- } while (i < n);
758
+ idx_pairs += svcntd();
759
+ } while (idx_pairs < count_pairs);
748
760
  results[0] = svaddv_f64(svptrue_b64(), ab_real_vec);
749
761
  results[1] = svaddv_f64(svptrue_b64(), ab_imag_vec);
750
762
  }
@@ -756,32 +768,30 @@ SIMSIMD_PUBLIC void simsimd_vdot_f64c_sve(simsimd_f64_t const *a, simsimd_f64_t
756
768
  #pragma GCC target("arch=armv8.2-a+sve+fp16")
757
769
  #pragma clang attribute push(__attribute__((target("arch=armv8.2-a+sve+fp16"))), apply_to = function)
758
770
 
759
- SIMSIMD_PUBLIC void simsimd_dot_f16_sve(simsimd_f16_t const *a_enum, simsimd_f16_t const *b_enum, simsimd_size_t n,
760
- simsimd_distance_t *result) {
761
- simsimd_size_t i = 0;
771
+ SIMSIMD_PUBLIC void simsimd_dot_f16_sve(simsimd_f16_t const *a_scalars, simsimd_f16_t const *b_scalars,
772
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
773
+ simsimd_size_t idx_scalars = 0;
762
774
  svfloat16_t ab_vec = svdup_f16(0);
763
- simsimd_f16_for_arm_simd_t const *a = (simsimd_f16_for_arm_simd_t const *)(a_enum);
764
- simsimd_f16_for_arm_simd_t const *b = (simsimd_f16_for_arm_simd_t const *)(b_enum);
765
775
  do {
766
- svbool_t pg_vec = svwhilelt_b16((unsigned int)i, (unsigned int)n);
767
- svfloat16_t a_vec = svld1_f16(pg_vec, a + i);
768
- svfloat16_t b_vec = svld1_f16(pg_vec, b + i);
776
+ svbool_t pg_vec = svwhilelt_b16((unsigned int)idx_scalars, (unsigned int)count_scalars);
777
+ svfloat16_t a_vec = svld1_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(a_scalars + idx_scalars));
778
+ svfloat16_t b_vec = svld1_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(b_scalars + idx_scalars));
769
779
  ab_vec = svmla_f16_x(pg_vec, ab_vec, a_vec, b_vec);
770
- i += svcnth();
771
- } while (i < n);
780
+ idx_scalars += svcnth();
781
+ } while (idx_scalars < count_scalars);
772
782
  simsimd_f16_for_arm_simd_t ab = svaddv_f16(svptrue_b16(), ab_vec);
773
783
  *result = ab;
774
784
  }
775
785
 
776
- SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
777
- simsimd_distance_t *results) {
778
- simsimd_size_t i = 0;
786
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
787
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
788
+ simsimd_size_t idx_pairs = 0;
779
789
  svfloat16_t ab_real_vec = svdup_f16(0);
780
790
  svfloat16_t ab_imag_vec = svdup_f16(0);
781
791
  do {
782
- svbool_t pg_vec = svwhilelt_b16((unsigned int)i / 2, (unsigned int)n / 2);
783
- svfloat16x2_t a_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)a + i);
784
- svfloat16x2_t b_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)b + i);
792
+ svbool_t pg_vec = svwhilelt_b32((unsigned int)idx_pairs, (unsigned int)count_pairs);
793
+ svfloat16x2_t a_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(a_pairs + idx_pairs));
794
+ svfloat16x2_t b_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(b_pairs + idx_pairs));
785
795
  svfloat16_t a_real_vec = svget2_f16(a_vec, 0);
786
796
  svfloat16_t a_imag_vec = svget2_f16(a_vec, 1);
787
797
  svfloat16_t b_real_vec = svget2_f16(b_vec, 0);
@@ -790,21 +800,21 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c_sve(simsimd_f16_t const *a, simsimd_f16_t c
790
800
  ab_real_vec = svmls_f16_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
791
801
  ab_imag_vec = svmla_f16_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
792
802
  ab_imag_vec = svmla_f16_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
793
- i += svcnth() * 2;
794
- } while (i < n);
803
+ idx_pairs += svcnth();
804
+ } while (idx_pairs < count_pairs);
795
805
  results[0] = svaddv_f16(svptrue_b16(), ab_real_vec);
796
806
  results[1] = svaddv_f16(svptrue_b16(), ab_imag_vec);
797
807
  }
798
808
 
799
- SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
800
- simsimd_distance_t *results) {
801
- simsimd_size_t i = 0;
809
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
810
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
811
+ simsimd_size_t idx_pairs = 0;
802
812
  svfloat16_t ab_real_vec = svdup_f16(0);
803
813
  svfloat16_t ab_imag_vec = svdup_f16(0);
804
814
  do {
805
- svbool_t pg_vec = svwhilelt_b16((unsigned int)i / 2, (unsigned int)n / 2);
806
- svfloat16x2_t a_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)a + i);
807
- svfloat16x2_t b_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)b + i);
815
+ svbool_t pg_vec = svwhilelt_b32((unsigned int)idx_pairs, (unsigned int)count_pairs);
816
+ svfloat16x2_t a_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(a_pairs + idx_pairs));
817
+ svfloat16x2_t b_vec = svld2_f16(pg_vec, (simsimd_f16_for_arm_simd_t const *)(b_pairs + idx_pairs));
808
818
  svfloat16_t a_real_vec = svget2_f16(a_vec, 0);
809
819
  svfloat16_t a_imag_vec = svget2_f16(a_vec, 1);
810
820
  svfloat16_t b_real_vec = svget2_f16(b_vec, 0);
@@ -813,8 +823,8 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_sve(simsimd_f16_t const *a, simsimd_f16_t
813
823
  ab_real_vec = svmla_f16_x(pg_vec, ab_real_vec, a_imag_vec, b_imag_vec);
814
824
  ab_imag_vec = svmla_f16_x(pg_vec, ab_imag_vec, a_real_vec, b_imag_vec);
815
825
  ab_imag_vec = svmls_f16_x(pg_vec, ab_imag_vec, a_imag_vec, b_real_vec);
816
- i += svcnth() * 2;
817
- } while (i < n);
826
+ idx_pairs += svcnth();
827
+ } while (idx_pairs < count_pairs);
818
828
  results[0] = svaddv_f16(svptrue_b16(), ab_real_vec);
819
829
  results[1] = svaddv_f16(svptrue_b16(), ab_imag_vec);
820
830
  }
@@ -867,23 +877,23 @@ SIMSIMD_INTERNAL simsimd_i32_t _simsimd_reduce_i32x8_haswell(__m256i vec) {
867
877
  return _mm_cvtsi128_si32(sum);
868
878
  }
869
879
 
870
- SIMSIMD_PUBLIC void simsimd_dot_f32_haswell(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
871
- simsimd_distance_t *results) {
880
+ SIMSIMD_PUBLIC void simsimd_dot_f32_haswell(simsimd_f32_t const *a_scalars, simsimd_f32_t const *b_scalars,
881
+ simsimd_size_t count_scalars, simsimd_distance_t *results) {
872
882
 
873
883
  __m256 ab_vec = _mm256_setzero_ps();
874
- simsimd_size_t i = 0;
875
- for (; i + 8 <= n; i += 8) {
876
- __m256 a_vec = _mm256_loadu_ps(a + i);
877
- __m256 b_vec = _mm256_loadu_ps(b + i);
884
+ simsimd_size_t idx_scalars = 0;
885
+ for (; idx_scalars + 8 <= count_scalars; idx_scalars += 8) {
886
+ __m256 a_vec = _mm256_loadu_ps(a_scalars + idx_scalars);
887
+ __m256 b_vec = _mm256_loadu_ps(b_scalars + idx_scalars);
878
888
  ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
879
889
  }
880
890
  simsimd_f64_t ab = _simsimd_reduce_f32x8_haswell(ab_vec);
881
- for (; i < n; ++i) ab += a[i] * b[i];
891
+ for (; idx_scalars < count_scalars; ++idx_scalars) ab += a_scalars[idx_scalars] * b_scalars[idx_scalars];
882
892
  *results = ab;
883
893
  }
884
894
 
885
- SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
886
- simsimd_distance_t *results) {
895
+ SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
896
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
887
897
 
888
898
  // The naive approach would be to use FMA and FMS instructions on different parts of the vectors.
889
899
  // Prior to that we would need to shuffle the input vectors to separate real and imaginary parts.
@@ -891,10 +901,10 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const *a, simsimd_f32
891
901
  // __m128 ab_real_vec = _mm_setzero_ps();
892
902
  // __m128 ab_imag_vec = _mm_setzero_ps();
893
903
  // __m256i permute_vec = _mm256_set_epi32(7, 5, 3, 1, 6, 4, 2, 0);
894
- // simsimd_size_t i = 0;
895
- // for (; i + 8 <= n; i += 8) {
896
- // __m256 a_vec = _mm256_loadu_ps(a + i);
897
- // __m256 b_vec = _mm256_loadu_ps(b + i);
904
+ // simsimd_size_t idx_pairs = 0;
905
+ // for (; idx_pairs + 4 <= count_pairs; idx_pairs += 4) {
906
+ // __m256 a_vec = _mm256_loadu_ps((simsimd_f32_t const *)(a_pairs + idx_pairs));
907
+ // __m256 b_vec = _mm256_loadu_ps((simsimd_f32_t const *)(b_pairs + idx_pairs));
898
908
  // __m256 a_shuffled = _mm256_permutevar8x32_ps(a_vec, permute_vec);
899
909
  // __m256 b_shuffled = _mm256_permutevar8x32_ps(b_vec, permute_vec);
900
910
  // __m128 a_real_vec = _mm256_extractf128_ps(a_shuffled, 0);
@@ -926,10 +936,11 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const *a, simsimd_f32
926
936
  3, 2, 1, 0, // Points to the first f32 in 128-bit lane
927
937
  7, 6, 5, 4 // Points to the second f32 in 128-bit lane
928
938
  );
929
- simsimd_size_t i = 0;
930
- for (; i + 8 <= n; i += 8) {
931
- __m256 a_vec = _mm256_loadu_ps(a + i);
932
- __m256 b_vec = _mm256_loadu_ps(b + i);
939
+
940
+ simsimd_size_t idx_pairs = 0;
941
+ for (; idx_pairs + 4 <= count_pairs; idx_pairs += 4) {
942
+ __m256 a_vec = _mm256_loadu_ps((simsimd_f32_t const *)(a_pairs + idx_pairs));
943
+ __m256 b_vec = _mm256_loadu_ps((simsimd_f32_t const *)(b_pairs + idx_pairs));
933
944
  __m256 b_swapped_vec = _mm256_castsi256_ps(_mm256_shuffle_epi8(_mm256_castps_si256(b_vec), swap_adjacent_vec));
934
945
  ab_real_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_real_vec);
935
946
  ab_imag_vec = _mm256_fmadd_ps(a_vec, b_swapped_vec, ab_imag_vec);
@@ -943,8 +954,9 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const *a, simsimd_f32
943
954
  simsimd_distance_t ab_imag = _simsimd_reduce_f32x8_haswell(ab_imag_vec);
944
955
 
945
956
  // Handle the tail:
946
- for (; i + 2 <= n; i += 2) {
947
- simsimd_f32_t ar = a[i], ai = a[i + 1], br = b[i], bi = b[i + 1];
957
+ for (; idx_pairs != count_pairs; ++idx_pairs) {
958
+ simsimd_f32c_t a_pair = a_pairs[idx_pairs], b_pair = b_pairs[idx_pairs];
959
+ simsimd_f32_t ar = a_pair.real, ai = a_pair.imag, br = b_pair.real, bi = b_pair.imag;
948
960
  ab_real += ar * br - ai * bi;
949
961
  ab_imag += ar * bi + ai * br;
950
962
  }
@@ -952,8 +964,8 @@ SIMSIMD_PUBLIC void simsimd_dot_f32c_haswell(simsimd_f32_t const *a, simsimd_f32
952
964
  results[1] = ab_imag;
953
965
  }
954
966
 
955
- SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
956
- simsimd_distance_t *results) {
967
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
968
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
957
969
 
958
970
  __m256 ab_real_vec = _mm256_setzero_ps();
959
971
  __m256 ab_imag_vec = _mm256_setzero_ps();
@@ -968,10 +980,11 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(simsimd_f32_t const *a, simsimd_f3
968
980
  3, 2, 1, 0, // Points to the first f32 in 128-bit lane
969
981
  7, 6, 5, 4 // Points to the second f32 in 128-bit lane
970
982
  );
971
- simsimd_size_t i = 0;
972
- for (; i + 8 <= n; i += 8) {
973
- __m256 a_vec = _mm256_loadu_ps(a + i);
974
- __m256 b_vec = _mm256_loadu_ps(b + i);
983
+
984
+ simsimd_size_t idx_pairs = 0;
985
+ for (; idx_pairs + 4 <= count_pairs; idx_pairs += 4) {
986
+ __m256 a_vec = _mm256_loadu_ps((simsimd_f32_t const *)(a_pairs + idx_pairs));
987
+ __m256 b_vec = _mm256_loadu_ps((simsimd_f32_t const *)(b_pairs + idx_pairs));
975
988
  ab_real_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_real_vec);
976
989
  b_vec = _mm256_castsi256_ps(_mm256_shuffle_epi8(_mm256_castps_si256(b_vec), swap_adjacent_vec));
977
990
  ab_imag_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_imag_vec);
@@ -985,8 +998,9 @@ SIMSIMD_PUBLIC void simsimd_vdot_f32c_haswell(simsimd_f32_t const *a, simsimd_f3
985
998
  simsimd_distance_t ab_imag = _simsimd_reduce_f32x8_haswell(ab_imag_vec);
986
999
 
987
1000
  // Handle the tail:
988
- for (; i + 2 <= n; i += 2) {
989
- simsimd_f32_t ar = a[i], ai = a[i + 1], br = b[i], bi = b[i + 1];
1001
+ for (; idx_pairs != count_pairs; ++idx_pairs) {
1002
+ simsimd_f32c_t a_pair = a_pairs[idx_pairs], b_pair = b_pairs[idx_pairs];
1003
+ simsimd_f32_t ar = a_pair.real, ai = a_pair.imag, br = b_pair.real, bi = b_pair.imag;
990
1004
  ab_real += ar * br + ai * bi;
991
1005
  ab_imag += ar * bi - ai * br;
992
1006
  }
@@ -1008,21 +1022,21 @@ SIMSIMD_INTERNAL __m256 _simsimd_partial_load_f16x8_haswell(simsimd_f16_t const
1008
1022
  return _mm256_cvtph_ps(result.vec);
1009
1023
  }
1010
1024
 
1011
- SIMSIMD_PUBLIC void simsimd_dot_f16_haswell(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1012
- simsimd_distance_t *result) {
1025
+ SIMSIMD_PUBLIC void simsimd_dot_f16_haswell(simsimd_f16_t const *a_scalars, simsimd_f16_t const *b_scalars,
1026
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
1013
1027
  __m256 a_vec, b_vec;
1014
1028
  __m256 ab_vec = _mm256_setzero_ps();
1015
1029
 
1016
1030
  simsimd_dot_f16_haswell_cycle:
1017
- if (n < 8) {
1018
- a_vec = _simsimd_partial_load_f16x8_haswell(a, n);
1019
- b_vec = _simsimd_partial_load_f16x8_haswell(b, n);
1020
- n = 0;
1031
+ if (count_scalars < 8) {
1032
+ a_vec = _simsimd_partial_load_f16x8_haswell(a_scalars, count_scalars);
1033
+ b_vec = _simsimd_partial_load_f16x8_haswell(b_scalars, count_scalars);
1034
+ count_scalars = 0;
1021
1035
  }
1022
1036
  else {
1023
- a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)a));
1024
- b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)b));
1025
- n -= 8, a += 8, b += 8;
1037
+ a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)a_scalars));
1038
+ b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)b_scalars));
1039
+ count_scalars -= 8, a_scalars += 8, b_scalars += 8;
1026
1040
  }
1027
1041
  // We can silence the NaNs using blends:
1028
1042
  //
@@ -1031,13 +1045,14 @@ simsimd_dot_f16_haswell_cycle:
1031
1045
  // ab_vec = _mm256_blendv_ps(_mm256_fmadd_ps(a_vec, b_vec, ab_vec), ab_vec, _mm256_or_ps(a_is_nan, b_is_nan));
1032
1046
  //
1033
1047
  ab_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_vec);
1034
- if (n) goto simsimd_dot_f16_haswell_cycle;
1048
+ if (count_scalars) goto simsimd_dot_f16_haswell_cycle;
1035
1049
 
1036
1050
  *result = _simsimd_reduce_f32x8_haswell(ab_vec);
1037
1051
  }
1038
1052
 
1039
- SIMSIMD_PUBLIC void simsimd_dot_f16c_haswell(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1040
- simsimd_distance_t *results) {
1053
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_haswell(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
1054
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
1055
+
1041
1056
  // Ideally the implementation would load 256 bits worth of vector data at a time,
1042
1057
  // shuffle those within a register, split in halfs, and only then upcast.
1043
1058
  // That way, we are stepping through 32x 16-bit vector components at a time, or 16 dimensions.
@@ -1059,28 +1074,34 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c_haswell(simsimd_f16_t const *a, simsimd_f16
1059
1074
  7, 6, 5, 4 // Points to the second f32 in 128-bit lane
1060
1075
  );
1061
1076
 
1062
- while (n >= 8) {
1063
- __m256 a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)a));
1064
- __m256 b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)b));
1077
+ while (count_pairs >= 4) {
1078
+ __m256 a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)a_pairs));
1079
+ __m256 b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)b_pairs));
1065
1080
  __m256 b_swapped_vec = _mm256_castsi256_ps(_mm256_shuffle_epi8(_mm256_castps_si256(b_vec), swap_adjacent_vec));
1066
1081
  ab_real_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_real_vec);
1067
1082
  ab_imag_vec = _mm256_fmadd_ps(a_vec, b_swapped_vec, ab_imag_vec);
1068
-
1069
- n -= 8, a += 8, b += 8;
1083
+ count_pairs -= 4, a_pairs += 4, b_pairs += 4;
1070
1084
  }
1071
1085
 
1072
1086
  // Flip the sign bit in every second scalar before accumulation:
1073
1087
  ab_real_vec = _mm256_castsi256_ps(_mm256_xor_si256(_mm256_castps_si256(ab_real_vec), sign_flip_vec));
1074
1088
 
1075
1089
  // Reduce horizontal sums and aggregate with the tail:
1076
- simsimd_dot_f16c_serial(a, b, n, results);
1090
+ simsimd_dot_f16c_serial(a_pairs, b_pairs, count_pairs, results);
1077
1091
  results[0] += _simsimd_reduce_f32x8_haswell(ab_real_vec);
1078
1092
  results[1] += _simsimd_reduce_f32x8_haswell(ab_imag_vec);
1079
1093
  }
1080
1094
 
1081
- SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1082
- simsimd_distance_t *results) {
1095
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
1096
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
1083
1097
 
1098
+ // Ideally the implementation would load 256 bits worth of vector data at a time,
1099
+ // shuffle those within a register, split in halfs, and only then upcast.
1100
+ // That way, we are stepping through 32x 16-bit vector components at a time, or 16 dimensions.
1101
+ // Sadly, shuffling 16-bit entries in a YMM register is hard to implement efficiently.
1102
+ //
1103
+ // Simpler approach is to load 128 bits at a time, upcast, and then shuffle.
1104
+ // This mostly replicates the `simsimd_vdot_f32c_haswell`.
1084
1105
  __m256 ab_real_vec = _mm256_setzero_ps();
1085
1106
  __m256 ab_imag_vec = _mm256_setzero_ps();
1086
1107
  __m256i sign_flip_vec = _mm256_set1_epi64x(0x8000000000000000);
@@ -1095,27 +1116,26 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_haswell(simsimd_f16_t const *a, simsimd_f1
1095
1116
  7, 6, 5, 4 // Points to the second f32 in 128-bit lane
1096
1117
  );
1097
1118
 
1098
- while (n >= 8) {
1099
- __m256 a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)a));
1100
- __m256 b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)b));
1119
+ while (count_pairs >= 4) {
1120
+ __m256 a_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)a_pairs));
1121
+ __m256 b_vec = _mm256_cvtph_ps(_mm_lddqu_si128((__m128i const *)b_pairs));
1101
1122
  ab_real_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_real_vec);
1102
1123
  b_vec = _mm256_castsi256_ps(_mm256_shuffle_epi8(_mm256_castps_si256(b_vec), swap_adjacent_vec));
1103
1124
  ab_imag_vec = _mm256_fmadd_ps(a_vec, b_vec, ab_imag_vec);
1104
-
1105
- n -= 8, a += 8, b += 8;
1125
+ count_pairs -= 4, a_pairs += 4, b_pairs += 4;
1106
1126
  }
1107
1127
 
1108
1128
  // Flip the sign bit in every second scalar before accumulation:
1109
1129
  ab_imag_vec = _mm256_castsi256_ps(_mm256_xor_si256(_mm256_castps_si256(ab_imag_vec), sign_flip_vec));
1110
1130
 
1111
1131
  // Reduce horizontal sums and aggregate with the tail:
1112
- simsimd_dot_f16c_serial(a, b, n, results);
1132
+ simsimd_dot_f16c_serial(a_pairs, b_pairs, count_pairs, results);
1113
1133
  results[0] += _simsimd_reduce_f32x8_haswell(ab_real_vec);
1114
1134
  results[1] += _simsimd_reduce_f32x8_haswell(ab_imag_vec);
1115
1135
  }
1116
1136
 
1117
- SIMSIMD_PUBLIC void simsimd_dot_i8_haswell(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1118
- simsimd_distance_t *result) {
1137
+ SIMSIMD_PUBLIC void simsimd_dot_i8_haswell(simsimd_i8_t const *a_scalars, simsimd_i8_t const *b_scalars,
1138
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
1119
1139
 
1120
1140
  __m256i ab_i32_low_vec = _mm256_setzero_si256();
1121
1141
  __m256i ab_i32_high_vec = _mm256_setzero_si256();
@@ -1132,10 +1152,10 @@ SIMSIMD_PUBLIC void simsimd_dot_i8_haswell(simsimd_i8_t const *a, simsimd_i8_t c
1132
1152
  // The problem with this approach, however, is the `-128` value in the second vector.
1133
1153
  // Flipping it's sign will do nothing, and the result will be incorrect.
1134
1154
  // This can easily lead to noticeable numerical errors in the final result.
1135
- simsimd_size_t i = 0;
1136
- for (; i + 32 <= n; i += 32) {
1137
- __m256i a_i8_vec = _mm256_lddqu_si256((__m256i const *)(a + i));
1138
- __m256i b_i8_vec = _mm256_lddqu_si256((__m256i const *)(b + i));
1155
+ simsimd_size_t idx_scalars = 0;
1156
+ for (; idx_scalars + 32 <= count_scalars; idx_scalars += 32) {
1157
+ __m256i a_i8_vec = _mm256_lddqu_si256((__m256i const *)(a_scalars + idx_scalars));
1158
+ __m256i b_i8_vec = _mm256_lddqu_si256((__m256i const *)(b_scalars + idx_scalars));
1139
1159
 
1140
1160
  // Upcast `int8` to `int16`
1141
1161
  __m256i a_i16_low_vec = _mm256_cvtepi8_epi16(_mm256_extracti128_si256(a_i8_vec, 0));
@@ -1152,12 +1172,12 @@ SIMSIMD_PUBLIC void simsimd_dot_i8_haswell(simsimd_i8_t const *a, simsimd_i8_t c
1152
1172
  int ab = _simsimd_reduce_i32x8_haswell(_mm256_add_epi32(ab_i32_low_vec, ab_i32_high_vec));
1153
1173
 
1154
1174
  // Take care of the tail:
1155
- for (; i < n; ++i) ab += (int)(a[i]) * b[i];
1175
+ for (; idx_scalars < count_scalars; ++idx_scalars) ab += (int)(a_scalars[idx_scalars]) * b_scalars[idx_scalars];
1156
1176
  *result = ab;
1157
1177
  }
1158
1178
 
1159
- SIMSIMD_PUBLIC void simsimd_dot_u8_haswell(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1160
- simsimd_distance_t *result) {
1179
+ SIMSIMD_PUBLIC void simsimd_dot_u8_haswell(simsimd_u8_t const *a_scalars, simsimd_u8_t const *b_scalars,
1180
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
1161
1181
 
1162
1182
  __m256i ab_i32_low_vec = _mm256_setzero_si256();
1163
1183
  __m256i ab_i32_high_vec = _mm256_setzero_si256();
@@ -1165,10 +1185,10 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_haswell(simsimd_u8_t const *a, simsimd_u8_t c
1165
1185
 
1166
1186
  // AVX2 has no instructions for unsigned 8-bit integer dot-products,
1167
1187
  // but it has a weird instruction for mixed signed-unsigned 8-bit dot-product.
1168
- simsimd_size_t i = 0;
1169
- for (; i + 32 <= n; i += 32) {
1170
- __m256i a_u8_vec = _mm256_lddqu_si256((__m256i const *)(a + i));
1171
- __m256i b_u8_vec = _mm256_lddqu_si256((__m256i const *)(b + i));
1188
+ simsimd_size_t idx_scalars = 0;
1189
+ for (; idx_scalars + 32 <= count_scalars; idx_scalars += 32) {
1190
+ __m256i a_u8_vec = _mm256_lddqu_si256((__m256i const *)(a_scalars + idx_scalars));
1191
+ __m256i b_u8_vec = _mm256_lddqu_si256((__m256i const *)(b_scalars + idx_scalars));
1172
1192
 
1173
1193
  // Upcast `uint8` to `int16`. Unlike the signed version, we can use the unpacking
1174
1194
  // instructions instead of extracts, as they are much faster and more efficient.
@@ -1186,22 +1206,22 @@ SIMSIMD_PUBLIC void simsimd_dot_u8_haswell(simsimd_u8_t const *a, simsimd_u8_t c
1186
1206
  int ab = _simsimd_reduce_i32x8_haswell(_mm256_add_epi32(ab_i32_low_vec, ab_i32_high_vec));
1187
1207
 
1188
1208
  // Take care of the tail:
1189
- for (; i < n; ++i) ab += (int)(a[i]) * b[i];
1209
+ for (; idx_scalars < count_scalars; ++idx_scalars) ab += (int)(a_scalars[idx_scalars]) * b_scalars[idx_scalars];
1190
1210
  *result = ab;
1191
1211
  }
1192
1212
 
1193
- SIMSIMD_INTERNAL __m256 _simsimd_bf16x8_to_f32x8_haswell(__m128i a) {
1213
+ SIMSIMD_INTERNAL __m256 _simsimd_bf16x8_to_f32x8_haswell(__m128i x) {
1194
1214
  // Upcasting from `bf16` to `f32` is done by shifting the `bf16` values by 16 bits to the left, like:
1195
- return _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(a), 16));
1215
+ return _mm256_castsi256_ps(_mm256_slli_epi32(_mm256_cvtepu16_epi32(x), 16));
1196
1216
  }
1197
1217
 
1198
- SIMSIMD_INTERNAL __m128i _simsimd_f32x8_to_bf16x8_haswell(__m256 a) {
1218
+ SIMSIMD_INTERNAL __m128i _simsimd_f32x8_to_bf16x8_haswell(__m256 x) {
1199
1219
  // Pack the 32-bit integers into 16-bit integers.
1200
1220
  // This is less trivial than unpacking: https://stackoverflow.com/a/77781241/2766161
1201
1221
  // The best approach is to shuffle within lanes first: https://stackoverflow.com/a/49723746/2766161
1202
1222
  // Our shuffling mask will drop the low 2-bytes from every 4-byte word.
1203
1223
  __m256i trunc_elements = _mm256_shuffle_epi8( //
1204
- _mm256_castps_si256(a), //
1224
+ _mm256_castps_si256(x), //
1205
1225
  _mm256_set_epi8( //
1206
1226
  -1, -1, -1, -1, -1, -1, -1, -1, 15, 14, 11, 10, 7, 6, 3, 2, //
1207
1227
  -1, -1, -1, -1, -1, -1, -1, -1, 15, 14, 11, 10, 7, 6, 3, 2 //
@@ -1225,24 +1245,24 @@ SIMSIMD_INTERNAL __m128i _simsimd_partial_load_bf16x8_haswell(simsimd_bf16_t con
1225
1245
  return result.vec;
1226
1246
  }
1227
1247
 
1228
- SIMSIMD_PUBLIC void simsimd_dot_bf16_haswell(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1229
- simsimd_distance_t *result) {
1248
+ SIMSIMD_PUBLIC void simsimd_dot_bf16_haswell(simsimd_bf16_t const *a_scalars, simsimd_bf16_t const *b_scalars,
1249
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
1230
1250
  __m128i a_vec, b_vec;
1231
1251
  __m256 ab_vec = _mm256_setzero_ps();
1232
1252
 
1233
1253
  simsimd_dot_bf16_haswell_cycle:
1234
- if (n < 8) {
1235
- a_vec = _simsimd_partial_load_bf16x8_haswell(a, n);
1236
- b_vec = _simsimd_partial_load_bf16x8_haswell(b, n);
1237
- n = 0;
1254
+ if (count_scalars < 8) {
1255
+ a_vec = _simsimd_partial_load_bf16x8_haswell(a_scalars, count_scalars);
1256
+ b_vec = _simsimd_partial_load_bf16x8_haswell(b_scalars, count_scalars);
1257
+ count_scalars = 0;
1238
1258
  }
1239
1259
  else {
1240
- a_vec = _mm_lddqu_si128((__m128i const *)a);
1241
- b_vec = _mm_lddqu_si128((__m128i const *)b);
1242
- a += 8, b += 8, n -= 8;
1260
+ a_vec = _mm_lddqu_si128((__m128i const *)a_scalars);
1261
+ b_vec = _mm_lddqu_si128((__m128i const *)b_scalars);
1262
+ a_scalars += 8, b_scalars += 8, count_scalars -= 8;
1243
1263
  }
1244
1264
  ab_vec = _mm256_fmadd_ps(_simsimd_bf16x8_to_f32x8_haswell(a_vec), _simsimd_bf16x8_to_f32x8_haswell(b_vec), ab_vec);
1245
- if (n) goto simsimd_dot_bf16_haswell_cycle;
1265
+ if (count_scalars) goto simsimd_dot_bf16_haswell_cycle;
1246
1266
 
1247
1267
  *result = _simsimd_reduce_f32x8_haswell(ab_vec);
1248
1268
  }
@@ -1274,87 +1294,80 @@ SIMSIMD_INTERNAL __m256i _simsimd_f32x16_to_bf16x16_skylake(__m512 a) {
1274
1294
  return _mm512_cvtepi32_epi16(x);
1275
1295
  }
1276
1296
 
1277
- SIMSIMD_PUBLIC void simsimd_dot_f32_skylake(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1278
- simsimd_distance_t *result) {
1279
- __m512 ab_vec = _mm512_setzero();
1297
+ SIMSIMD_PUBLIC void simsimd_dot_f32_skylake(simsimd_f32_t const *a_scalars, simsimd_f32_t const *b_scalars,
1298
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
1280
1299
  __m512 a_vec, b_vec;
1300
+ __m512 ab_vec = _mm512_setzero();
1281
1301
 
1282
1302
  simsimd_dot_f32_skylake_cycle:
1283
- if (n < 16) {
1284
- __mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF, n);
1285
- a_vec = _mm512_maskz_loadu_ps(mask, a);
1286
- b_vec = _mm512_maskz_loadu_ps(mask, b);
1287
- n = 0;
1303
+ if (count_scalars < 16) {
1304
+ __mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF, count_scalars);
1305
+ a_vec = _mm512_maskz_loadu_ps(mask, a_scalars);
1306
+ b_vec = _mm512_maskz_loadu_ps(mask, b_scalars);
1307
+ count_scalars = 0;
1288
1308
  }
1289
1309
  else {
1290
- a_vec = _mm512_loadu_ps(a);
1291
- b_vec = _mm512_loadu_ps(b);
1292
- a += 16, b += 16, n -= 16;
1310
+ a_vec = _mm512_loadu_ps(a_scalars);
1311
+ b_vec = _mm512_loadu_ps(b_scalars);
1312
+ a_scalars += 16, b_scalars += 16, count_scalars -= 16;
1293
1313
  }
1294
1314
  ab_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_vec);
1295
- if (n) goto simsimd_dot_f32_skylake_cycle;
1315
+ if (count_scalars) goto simsimd_dot_f32_skylake_cycle;
1296
1316
 
1297
1317
  *result = _simsimd_reduce_f32x16_skylake(ab_vec);
1298
1318
  }
1299
1319
 
1300
- SIMSIMD_PUBLIC void simsimd_dot_f64_skylake(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1301
- simsimd_distance_t *result) {
1302
- __m512d ab_vec = _mm512_setzero_pd();
1320
+ SIMSIMD_PUBLIC void simsimd_dot_f64_skylake(simsimd_f64_t const *a_scalars, simsimd_f64_t const *b_scalars,
1321
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
1303
1322
  __m512d a_vec, b_vec;
1323
+ __m512d ab_vec = _mm512_setzero_pd();
1304
1324
 
1305
1325
  simsimd_dot_f64_skylake_cycle:
1306
- if (n < 8) {
1307
- __mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF, n);
1308
- a_vec = _mm512_maskz_loadu_pd(mask, a);
1309
- b_vec = _mm512_maskz_loadu_pd(mask, b);
1310
- n = 0;
1326
+ if (count_scalars < 8) {
1327
+ __mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF, count_scalars);
1328
+ a_vec = _mm512_maskz_loadu_pd(mask, a_scalars);
1329
+ b_vec = _mm512_maskz_loadu_pd(mask, b_scalars);
1330
+ count_scalars = 0;
1311
1331
  }
1312
1332
  else {
1313
- a_vec = _mm512_loadu_pd(a);
1314
- b_vec = _mm512_loadu_pd(b);
1315
- a += 8, b += 8, n -= 8;
1333
+ a_vec = _mm512_loadu_pd(a_scalars);
1334
+ b_vec = _mm512_loadu_pd(b_scalars);
1335
+ a_scalars += 8, b_scalars += 8, count_scalars -= 8;
1316
1336
  }
1317
1337
  ab_vec = _mm512_fmadd_pd(a_vec, b_vec, ab_vec);
1318
- if (n) goto simsimd_dot_f64_skylake_cycle;
1338
+ if (count_scalars) goto simsimd_dot_f64_skylake_cycle;
1319
1339
 
1320
1340
  *result = _mm512_reduce_add_pd(ab_vec);
1321
1341
  }
1322
1342
 
1323
- SIMSIMD_PUBLIC void simsimd_dot_f32c_skylake(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1324
- simsimd_distance_t *results) {
1343
+ SIMSIMD_PUBLIC void simsimd_dot_f32c_skylake(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
1344
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
1345
+ __m512 a_vec, b_vec;
1325
1346
  __m512 ab_real_vec = _mm512_setzero();
1326
1347
  __m512 ab_imag_vec = _mm512_setzero();
1327
- __m512 a_vec;
1328
- __m512 b_vec;
1329
1348
 
1330
1349
  // We take into account, that FMS is the same as FMA with a negative multiplier.
1331
1350
  // To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
1332
1351
  // This way we can avoid the shuffling and the need for separate real and imaginary parts.
1333
1352
  // For the imaginary part of the product, we would need to swap the real and imaginary parts of
1334
1353
  // one of the vectors.
1335
- __m512i sign_flip_vec = _mm512_set1_epi64(0x8000000000000000);
1336
- __m512i swap_adjacent_vec = _mm512_set_epi8( //
1337
- 59, 58, 57, 56, 63, 62, 61, 60, 51, 50, 49, 48, 55, 54, 53, 52, // 4th 128-bit lane
1338
- 43, 42, 41, 40, 47, 46, 45, 44, 35, 34, 33, 32, 39, 38, 37, 36, // 3rd 128-bit lane
1339
- 27, 26, 25, 24, 31, 30, 29, 28, 19, 18, 17, 16, 23, 22, 21, 20, // 2nd 128-bit lane
1340
- 11, 10, 9, 8, 15, 14, 13, 12, 3, 2, 1, 0, 7, 6, 5, 4 // 1st 128-bit lane
1341
- );
1354
+ __m512i const sign_flip_vec = _mm512_set1_epi64(0x8000000000000000);
1342
1355
  simsimd_dot_f32c_skylake_cycle:
1343
- if (n < 16) {
1344
- __mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF, n);
1345
- a_vec = _mm512_maskz_loadu_ps(mask, a);
1346
- b_vec = _mm512_maskz_loadu_ps(mask, b);
1347
- n = 0;
1356
+ if (count_pairs < 8) {
1357
+ __mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
1358
+ a_vec = _mm512_maskz_loadu_ps(mask, a_pairs);
1359
+ b_vec = _mm512_maskz_loadu_ps(mask, b_pairs);
1360
+ count_pairs = 0;
1348
1361
  }
1349
1362
  else {
1350
- a_vec = _mm512_loadu_ps(a);
1351
- b_vec = _mm512_loadu_ps(b);
1352
- a += 16, b += 16, n -= 16;
1363
+ a_vec = _mm512_loadu_ps(a_pairs);
1364
+ b_vec = _mm512_loadu_ps(b_pairs);
1365
+ a_pairs += 8, b_pairs += 8, count_pairs -= 8;
1353
1366
  }
1354
1367
  ab_real_vec = _mm512_fmadd_ps(b_vec, a_vec, ab_real_vec);
1355
- ab_imag_vec = _mm512_fmadd_ps(
1356
- _mm512_castsi512_ps(_mm512_shuffle_epi8(_mm512_castps_si512(b_vec), swap_adjacent_vec)), a_vec, ab_imag_vec);
1357
- if (n) goto simsimd_dot_f32c_skylake_cycle;
1368
+ b_vec = _mm512_permute_ps(b_vec, 0xB1); //? Swap adjacent entries within each pair
1369
+ ab_imag_vec = _mm512_fmadd_ps(b_vec, a_vec, ab_imag_vec);
1370
+ if (count_pairs) goto simsimd_dot_f32c_skylake_cycle;
1358
1371
 
1359
1372
  // Flip the sign bit in every second scalar before accumulation:
1360
1373
  ab_real_vec = _mm512_castsi512_ps(_mm512_xor_si512(_mm512_castps_si512(ab_real_vec), sign_flip_vec));
@@ -1364,41 +1377,40 @@ simsimd_dot_f32c_skylake_cycle:
1364
1377
  results[1] = _simsimd_reduce_f32x16_skylake(ab_imag_vec);
1365
1378
  }
1366
1379
 
1367
- SIMSIMD_PUBLIC void simsimd_vdot_f32c_skylake(simsimd_f32_t const *a, simsimd_f32_t const *b, simsimd_size_t n,
1368
- simsimd_distance_t *results) {
1380
+ SIMSIMD_PUBLIC void simsimd_vdot_f32c_skylake(simsimd_f32c_t const *a_pairs, simsimd_f32c_t const *b_pairs,
1381
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
1382
+ __m512 a_vec, b_vec;
1369
1383
  __m512 ab_real_vec = _mm512_setzero();
1370
1384
  __m512 ab_imag_vec = _mm512_setzero();
1371
- __m512 a_vec;
1372
- __m512 b_vec;
1373
1385
 
1374
1386
  // We take into account, that FMS is the same as FMA with a negative multiplier.
1375
1387
  // To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
1376
1388
  // This way we can avoid the shuffling and the need for separate real and imaginary parts.
1377
1389
  // For the imaginary part of the product, we would need to swap the real and imaginary parts of
1378
1390
  // one of the vectors.
1379
- __m512i sign_flip_vec = _mm512_set1_epi64(0x8000000000000000);
1380
- __m512i swap_adjacent_vec = _mm512_set_epi8( //
1391
+ __m512i const sign_flip_vec = _mm512_set1_epi64(0x8000000000000000);
1392
+ __m512i const swap_adjacent_vec = _mm512_set_epi8( //
1381
1393
  59, 58, 57, 56, 63, 62, 61, 60, 51, 50, 49, 48, 55, 54, 53, 52, // 4th 128-bit lane
1382
1394
  43, 42, 41, 40, 47, 46, 45, 44, 35, 34, 33, 32, 39, 38, 37, 36, // 3rd 128-bit lane
1383
1395
  27, 26, 25, 24, 31, 30, 29, 28, 19, 18, 17, 16, 23, 22, 21, 20, // 2nd 128-bit lane
1384
1396
  11, 10, 9, 8, 15, 14, 13, 12, 3, 2, 1, 0, 7, 6, 5, 4 // 1st 128-bit lane
1385
1397
  );
1386
1398
  simsimd_vdot_f32c_skylake_cycle:
1387
- if (n < 16) {
1388
- __mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF, n);
1389
- a_vec = _mm512_maskz_loadu_ps(mask, a);
1390
- b_vec = _mm512_maskz_loadu_ps(mask, b);
1391
- n = 0;
1399
+ if (count_pairs < 8) {
1400
+ __mmask16 mask = (__mmask16)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
1401
+ a_vec = _mm512_maskz_loadu_ps(mask, (simsimd_f32_t const *)a_pairs);
1402
+ b_vec = _mm512_maskz_loadu_ps(mask, (simsimd_f32_t const *)b_pairs);
1403
+ count_pairs = 0;
1392
1404
  }
1393
1405
  else {
1394
- a_vec = _mm512_loadu_ps(a);
1395
- b_vec = _mm512_loadu_ps(b);
1396
- a += 16, b += 16, n -= 16;
1406
+ a_vec = _mm512_loadu_ps((simsimd_f32_t const *)a_pairs);
1407
+ b_vec = _mm512_loadu_ps((simsimd_f32_t const *)b_pairs);
1408
+ a_pairs += 8, b_pairs += 8, count_pairs -= 8;
1397
1409
  }
1398
1410
  ab_real_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_real_vec);
1399
- b_vec = _mm512_castsi512_ps(_mm512_shuffle_epi8(_mm512_castps_si512(b_vec), swap_adjacent_vec));
1411
+ b_vec = _mm512_permute_ps(b_vec, 0xB1); //? Swap adjacent entries within each pair
1400
1412
  ab_imag_vec = _mm512_fmadd_ps(a_vec, b_vec, ab_imag_vec);
1401
- if (n) goto simsimd_vdot_f32c_skylake_cycle;
1413
+ if (count_pairs) goto simsimd_vdot_f32c_skylake_cycle;
1402
1414
 
1403
1415
  // Flip the sign bit in every second scalar before accumulation:
1404
1416
  ab_imag_vec = _mm512_castsi512_ps(_mm512_xor_si512(_mm512_castps_si512(ab_imag_vec), sign_flip_vec));
@@ -1408,44 +1420,37 @@ simsimd_vdot_f32c_skylake_cycle:
1408
1420
  results[1] = _simsimd_reduce_f32x16_skylake(ab_imag_vec);
1409
1421
  }
1410
1422
 
1411
- SIMSIMD_PUBLIC void simsimd_dot_f64c_skylake(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1412
- simsimd_distance_t *results) {
1423
+ SIMSIMD_PUBLIC void simsimd_dot_f64c_skylake(simsimd_f64c_t const *a_pairs, simsimd_f64c_t const *b_pairs,
1424
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
1425
+ __m512d a_vec, b_vec;
1413
1426
  __m512d ab_real_vec = _mm512_setzero_pd();
1414
1427
  __m512d ab_imag_vec = _mm512_setzero_pd();
1415
- __m512d a_vec;
1416
- __m512d b_vec;
1417
1428
 
1418
1429
  // We take into account, that FMS is the same as FMA with a negative multiplier.
1419
1430
  // To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
1420
1431
  // This way we can avoid the shuffling and the need for separate real and imaginary parts.
1421
1432
  // For the imaginary part of the product, we would need to swap the real and imaginary parts of
1422
1433
  // one of the vectors.
1423
- __m512i sign_flip_vec = _mm512_set_epi64( //
1434
+ __m512i const sign_flip_vec = _mm512_set_epi64( //
1424
1435
  0x8000000000000000, 0x0000000000000000, 0x8000000000000000, 0x0000000000000000, //
1425
1436
  0x8000000000000000, 0x0000000000000000, 0x8000000000000000, 0x0000000000000000 //
1426
1437
  );
1427
- __m512i swap_adjacent_vec = _mm512_set_epi8( //
1428
- 55, 54, 53, 52, 51, 50, 49, 48, 63, 62, 61, 60, 59, 58, 57, 56, // 4th 128-bit lane
1429
- 39, 38, 37, 36, 35, 34, 33, 32, 47, 46, 45, 44, 43, 42, 41, 40, // 3rd 128-bit lane
1430
- 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, // 2nd 128-bit lane
1431
- 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8 // 1st 128-bit lane
1432
- );
1433
1438
  simsimd_dot_f64c_skylake_cycle:
1434
- if (n < 8) {
1435
- __mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF, n);
1436
- a_vec = _mm512_maskz_loadu_pd(mask, a);
1437
- b_vec = _mm512_maskz_loadu_pd(mask, b);
1438
- n = 0;
1439
+ if (count_pairs < 4) {
1440
+ __mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
1441
+ a_vec = _mm512_maskz_loadu_pd(mask, a_pairs);
1442
+ b_vec = _mm512_maskz_loadu_pd(mask, b_pairs);
1443
+ count_pairs = 0;
1439
1444
  }
1440
1445
  else {
1441
- a_vec = _mm512_loadu_pd(a);
1442
- b_vec = _mm512_loadu_pd(b);
1443
- a += 8, b += 8, n -= 8;
1446
+ a_vec = _mm512_loadu_pd(a_pairs);
1447
+ b_vec = _mm512_loadu_pd(b_pairs);
1448
+ a_pairs += 4, b_pairs += 4, count_pairs -= 4;
1444
1449
  }
1445
1450
  ab_real_vec = _mm512_fmadd_pd(b_vec, a_vec, ab_real_vec);
1446
- ab_imag_vec = _mm512_fmadd_pd(
1447
- _mm512_castsi512_pd(_mm512_shuffle_epi8(_mm512_castpd_si512(b_vec), swap_adjacent_vec)), a_vec, ab_imag_vec);
1448
- if (n) goto simsimd_dot_f64c_skylake_cycle;
1451
+ b_vec = _mm512_permute_pd(b_vec, 0x55); //? Same as 0b01010101.
1452
+ ab_imag_vec = _mm512_fmadd_pd(b_vec, a_vec, ab_imag_vec);
1453
+ if (count_pairs) goto simsimd_dot_f64c_skylake_cycle;
1449
1454
 
1450
1455
  // Flip the sign bit in every second scalar before accumulation:
1451
1456
  ab_real_vec = _mm512_castsi512_pd(_mm512_xor_si512(_mm512_castpd_si512(ab_real_vec), sign_flip_vec));
@@ -1455,44 +1460,37 @@ simsimd_dot_f64c_skylake_cycle:
1455
1460
  results[1] = _mm512_reduce_add_pd(ab_imag_vec);
1456
1461
  }
1457
1462
 
1458
- SIMSIMD_PUBLIC void simsimd_vdot_f64c_skylake(simsimd_f64_t const *a, simsimd_f64_t const *b, simsimd_size_t n,
1459
- simsimd_distance_t *results) {
1463
+ SIMSIMD_PUBLIC void simsimd_vdot_f64c_skylake(simsimd_f64c_t const *a_pairs, simsimd_f64c_t const *b_pairs,
1464
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
1465
+ __m512d a_vec, b_vec;
1460
1466
  __m512d ab_real_vec = _mm512_setzero_pd();
1461
1467
  __m512d ab_imag_vec = _mm512_setzero_pd();
1462
- __m512d a_vec;
1463
- __m512d b_vec;
1464
1468
 
1465
1469
  // We take into account, that FMS is the same as FMA with a negative multiplier.
1466
1470
  // To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
1467
1471
  // This way we can avoid the shuffling and the need for separate real and imaginary parts.
1468
1472
  // For the imaginary part of the product, we would need to swap the real and imaginary parts of
1469
1473
  // one of the vectors.
1470
- __m512i sign_flip_vec = _mm512_set_epi64( //
1474
+ __m512i const sign_flip_vec = _mm512_set_epi64( //
1471
1475
  0x8000000000000000, 0x0000000000000000, 0x8000000000000000, 0x0000000000000000, //
1472
1476
  0x8000000000000000, 0x0000000000000000, 0x8000000000000000, 0x0000000000000000 //
1473
1477
  );
1474
- __m512i swap_adjacent_vec = _mm512_set_epi8( //
1475
- 55, 54, 53, 52, 51, 50, 49, 48, 63, 62, 61, 60, 59, 58, 57, 56, // 4th 128-bit lane
1476
- 39, 38, 37, 36, 35, 34, 33, 32, 47, 46, 45, 44, 43, 42, 41, 40, // 3rd 128-bit lane
1477
- 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, // 2nd 128-bit lane
1478
- 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8 // 1st 128-bit lane
1479
- );
1480
1478
  simsimd_vdot_f64c_skylake_cycle:
1481
- if (n < 8) {
1482
- __mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF, n);
1483
- a_vec = _mm512_maskz_loadu_pd(mask, a);
1484
- b_vec = _mm512_maskz_loadu_pd(mask, b);
1485
- n = 0;
1479
+ if (count_pairs < 4) {
1480
+ __mmask8 mask = (__mmask8)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
1481
+ a_vec = _mm512_maskz_loadu_pd(mask, (simsimd_f32_t const *)a_pairs);
1482
+ b_vec = _mm512_maskz_loadu_pd(mask, (simsimd_f32_t const *)b_pairs);
1483
+ count_pairs = 0;
1486
1484
  }
1487
1485
  else {
1488
- a_vec = _mm512_loadu_pd(a);
1489
- b_vec = _mm512_loadu_pd(b);
1490
- a += 8, b += 8, n -= 8;
1486
+ a_vec = _mm512_loadu_pd((simsimd_f32_t const *)a_pairs);
1487
+ b_vec = _mm512_loadu_pd((simsimd_f32_t const *)b_pairs);
1488
+ a_pairs += 4, b_pairs += 4, count_pairs -= 4;
1491
1489
  }
1492
1490
  ab_real_vec = _mm512_fmadd_pd(a_vec, b_vec, ab_real_vec);
1493
- b_vec = _mm512_castsi512_pd(_mm512_shuffle_epi8(_mm512_castpd_si512(b_vec), swap_adjacent_vec));
1491
+ b_vec = _mm512_permute_pd(b_vec, 0x55); //? Same as 0b01010101.
1494
1492
  ab_imag_vec = _mm512_fmadd_pd(a_vec, b_vec, ab_imag_vec);
1495
- if (n) goto simsimd_vdot_f64c_skylake_cycle;
1493
+ if (count_pairs) goto simsimd_vdot_f64c_skylake_cycle;
1496
1494
 
1497
1495
  // Flip the sign bit in every second scalar before accumulation:
1498
1496
  ab_imag_vec = _mm512_castsi512_pd(_mm512_xor_si512(_mm512_castpd_si512(ab_imag_vec), sign_flip_vec));
@@ -1512,43 +1510,42 @@ simsimd_vdot_f64c_skylake_cycle:
1512
1510
  #pragma clang attribute push(__attribute__((target("avx2,avx512f,avx512vl,bmi2,avx512bw,avx512bf16"))), \
1513
1511
  apply_to = function)
1514
1512
 
1515
- SIMSIMD_PUBLIC void simsimd_dot_bf16_genoa(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1516
- simsimd_distance_t *result) {
1517
- __m512 ab_vec = _mm512_setzero_ps();
1513
+ SIMSIMD_PUBLIC void simsimd_dot_bf16_genoa(simsimd_bf16_t const *a_scalars, simsimd_bf16_t const *b_scalars,
1514
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
1518
1515
  __m512i a_i16_vec, b_i16_vec;
1516
+ __m512 ab_vec = _mm512_setzero_ps();
1519
1517
 
1520
1518
  simsimd_dot_bf16_genoa_cycle:
1521
- if (n < 32) {
1522
- __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
1523
- a_i16_vec = _mm512_maskz_loadu_epi16(mask, a);
1524
- b_i16_vec = _mm512_maskz_loadu_epi16(mask, b);
1525
- n = 0;
1519
+ if (count_scalars < 32) {
1520
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_scalars);
1521
+ a_i16_vec = _mm512_maskz_loadu_epi16(mask, a_scalars);
1522
+ b_i16_vec = _mm512_maskz_loadu_epi16(mask, b_scalars);
1523
+ count_scalars = 0;
1526
1524
  }
1527
1525
  else {
1528
- a_i16_vec = _mm512_loadu_epi16(a);
1529
- b_i16_vec = _mm512_loadu_epi16(b);
1530
- a += 32, b += 32, n -= 32;
1526
+ a_i16_vec = _mm512_loadu_epi16(a_scalars);
1527
+ b_i16_vec = _mm512_loadu_epi16(b_scalars);
1528
+ a_scalars += 32, b_scalars += 32, count_scalars -= 32;
1531
1529
  }
1532
1530
  ab_vec = _mm512_dpbf16_ps(ab_vec, (__m512bh)(a_i16_vec), (__m512bh)(b_i16_vec));
1533
- if (n) goto simsimd_dot_bf16_genoa_cycle;
1531
+ if (count_scalars) goto simsimd_dot_bf16_genoa_cycle;
1534
1532
 
1535
1533
  *result = _simsimd_reduce_f32x16_skylake(ab_vec);
1536
1534
  }
1537
1535
 
1538
- SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1539
- simsimd_distance_t *results) {
1536
+ SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(simsimd_bf16c_t const *a_pairs, simsimd_bf16c_t const *b_pairs,
1537
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
1538
+ __m512i a_vec, b_vec;
1540
1539
  __m512 ab_real_vec = _mm512_setzero_ps();
1541
1540
  __m512 ab_imag_vec = _mm512_setzero_ps();
1542
- __m512i a_vec;
1543
- __m512i b_vec;
1544
1541
 
1545
1542
  // We take into account, that FMS is the same as FMA with a negative multiplier.
1546
1543
  // To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
1547
1544
  // This way we can avoid the shuffling and the need for separate real and imaginary parts.
1548
1545
  // For the imaginary part of the product, we would need to swap the real and imaginary parts of
1549
1546
  // one of the vectors.
1550
- __m512i sign_flip_vec = _mm512_set1_epi32(0x80000000);
1551
- __m512i swap_adjacent_vec = _mm512_set_epi8( //
1547
+ __m512i const sign_flip_vec = _mm512_set1_epi32(0x80000000);
1548
+ __m512i const swap_adjacent_vec = _mm512_set_epi8( //
1552
1549
  61, 60, 63, 62, 57, 56, 59, 58, 53, 52, 55, 54, 49, 48, 51, 50, // 4th 128-bit lane
1553
1550
  45, 44, 47, 46, 41, 40, 43, 42, 37, 36, 39, 38, 33, 32, 35, 34, // 3rd 128-bit lane
1554
1551
  29, 28, 31, 30, 25, 24, 27, 26, 21, 20, 23, 22, 17, 16, 19, 18, // 2nd 128-bit lane
@@ -1556,41 +1553,40 @@ SIMSIMD_PUBLIC void simsimd_dot_bf16c_genoa(simsimd_bf16_t const *a, simsimd_bf1
1556
1553
  );
1557
1554
 
1558
1555
  simsimd_dot_bf16c_genoa_cycle:
1559
- if (n < 32) {
1560
- __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
1561
- a_vec = _mm512_maskz_loadu_epi16(mask, a);
1562
- b_vec = _mm512_maskz_loadu_epi16(mask, b);
1563
- n = 0;
1556
+ if (count_pairs < 16) {
1557
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
1558
+ a_vec = _mm512_maskz_loadu_epi16(mask, (simsimd_i16_t const *)a_pairs);
1559
+ b_vec = _mm512_maskz_loadu_epi16(mask, (simsimd_i16_t const *)b_pairs);
1560
+ count_pairs = 0;
1564
1561
  }
1565
1562
  else {
1566
- a_vec = _mm512_loadu_epi16(a);
1567
- b_vec = _mm512_loadu_epi16(b);
1568
- a += 32, b += 32, n -= 32;
1563
+ a_vec = _mm512_loadu_epi16((simsimd_i16_t const *)a_pairs);
1564
+ b_vec = _mm512_loadu_epi16((simsimd_i16_t const *)b_pairs);
1565
+ a_pairs += 16, b_pairs += 16, count_pairs -= 16;
1569
1566
  }
1570
1567
  ab_real_vec = _mm512_dpbf16_ps(ab_real_vec, (__m512bh)(_mm512_xor_si512(b_vec, sign_flip_vec)), (__m512bh)(a_vec));
1571
1568
  ab_imag_vec =
1572
1569
  _mm512_dpbf16_ps(ab_imag_vec, (__m512bh)(_mm512_shuffle_epi8(b_vec, swap_adjacent_vec)), (__m512bh)(a_vec));
1573
- if (n) goto simsimd_dot_bf16c_genoa_cycle;
1570
+ if (count_pairs) goto simsimd_dot_bf16c_genoa_cycle;
1574
1571
 
1575
1572
  // Reduce horizontal sums:
1576
1573
  results[0] = _simsimd_reduce_f32x16_skylake(ab_real_vec);
1577
1574
  results[1] = _simsimd_reduce_f32x16_skylake(ab_imag_vec);
1578
1575
  }
1579
1576
 
1580
- SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(simsimd_bf16_t const *a, simsimd_bf16_t const *b, simsimd_size_t n,
1581
- simsimd_distance_t *results) {
1577
+ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(simsimd_bf16c_t const *a_pairs, simsimd_bf16c_t const *b_pairs,
1578
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
1579
+ __m512i a_vec, b_vec;
1582
1580
  __m512 ab_real_vec = _mm512_setzero_ps();
1583
1581
  __m512 ab_imag_vec = _mm512_setzero_ps();
1584
- __m512i a_vec;
1585
- __m512i b_vec;
1586
1582
 
1587
1583
  // We take into account, that FMS is the same as FMA with a negative multiplier.
1588
1584
  // To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
1589
1585
  // This way we can avoid the shuffling and the need for separate real and imaginary parts.
1590
1586
  // For the imaginary part of the product, we would need to swap the real and imaginary parts of
1591
1587
  // one of the vectors.
1592
- __m512i sign_flip_vec = _mm512_set1_epi32(0x80000000);
1593
- __m512i swap_adjacent_vec = _mm512_set_epi8( //
1588
+ __m512i const sign_flip_vec = _mm512_set1_epi32(0x80000000);
1589
+ __m512i const swap_adjacent_vec = _mm512_set_epi8( //
1594
1590
  61, 60, 63, 62, 57, 56, 59, 58, 53, 52, 55, 54, 49, 48, 51, 50, // 4th 128-bit lane
1595
1591
  45, 44, 47, 46, 41, 40, 43, 42, 37, 36, 39, 38, 33, 32, 35, 34, // 3rd 128-bit lane
1596
1592
  29, 28, 31, 30, 25, 24, 27, 26, 21, 20, 23, 22, 17, 16, 19, 18, // 2nd 128-bit lane
@@ -1598,22 +1594,22 @@ SIMSIMD_PUBLIC void simsimd_vdot_bf16c_genoa(simsimd_bf16_t const *a, simsimd_bf
1598
1594
  );
1599
1595
 
1600
1596
  simsimd_dot_bf16c_genoa_cycle:
1601
- if (n < 32) {
1602
- __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
1603
- a_vec = _mm512_maskz_loadu_epi16(mask, a);
1604
- b_vec = _mm512_maskz_loadu_epi16(mask, b);
1605
- n = 0;
1597
+ if (count_pairs < 16) {
1598
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
1599
+ a_vec = _mm512_maskz_loadu_epi16(mask, (simsimd_i16_t const *)a_pairs);
1600
+ b_vec = _mm512_maskz_loadu_epi16(mask, (simsimd_i16_t const *)b_pairs);
1601
+ count_pairs = 0;
1606
1602
  }
1607
1603
  else {
1608
- a_vec = _mm512_loadu_epi16(a);
1609
- b_vec = _mm512_loadu_epi16(b);
1610
- a += 32, b += 32, n -= 32;
1604
+ a_vec = _mm512_loadu_epi16((simsimd_i16_t const *)a_pairs);
1605
+ b_vec = _mm512_loadu_epi16((simsimd_i16_t const *)b_pairs);
1606
+ a_pairs += 16, b_pairs += 16, count_pairs -= 16;
1611
1607
  }
1612
1608
  ab_real_vec = _mm512_dpbf16_ps(ab_real_vec, (__m512bh)(a_vec), (__m512bh)(b_vec));
1613
1609
  a_vec = _mm512_xor_si512(a_vec, sign_flip_vec);
1614
1610
  b_vec = _mm512_shuffle_epi8(b_vec, swap_adjacent_vec);
1615
1611
  ab_imag_vec = _mm512_dpbf16_ps(ab_imag_vec, (__m512bh)(a_vec), (__m512bh)(b_vec));
1616
- if (n) goto simsimd_dot_bf16c_genoa_cycle;
1612
+ if (count_pairs) goto simsimd_dot_bf16c_genoa_cycle;
1617
1613
 
1618
1614
  // Reduce horizontal sums:
1619
1615
  results[0] = _simsimd_reduce_f32x16_skylake(ab_real_vec);
@@ -1630,43 +1626,42 @@ simsimd_dot_bf16c_genoa_cycle:
1630
1626
  #pragma clang attribute push(__attribute__((target("avx2,avx512f,avx512vl,bmi2,avx512bw,avx512fp16"))), \
1631
1627
  apply_to = function)
1632
1628
 
1633
- SIMSIMD_PUBLIC void simsimd_dot_f16_sapphire(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1634
- simsimd_distance_t *result) {
1635
- __m512h ab_vec = _mm512_setzero_ph();
1629
+ SIMSIMD_PUBLIC void simsimd_dot_f16_sapphire(simsimd_f16_t const *a_scalars, simsimd_f16_t const *b_scalars,
1630
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
1636
1631
  __m512i a_i16_vec, b_i16_vec;
1632
+ __m512h ab_vec = _mm512_setzero_ph();
1637
1633
 
1638
1634
  simsimd_dot_f16_sapphire_cycle:
1639
- if (n < 32) {
1640
- __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
1641
- a_i16_vec = _mm512_maskz_loadu_epi16(mask, a);
1642
- b_i16_vec = _mm512_maskz_loadu_epi16(mask, b);
1643
- n = 0;
1635
+ if (count_scalars < 32) {
1636
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_scalars);
1637
+ a_i16_vec = _mm512_maskz_loadu_epi16(mask, a_scalars);
1638
+ b_i16_vec = _mm512_maskz_loadu_epi16(mask, b_scalars);
1639
+ count_scalars = 0;
1644
1640
  }
1645
1641
  else {
1646
- a_i16_vec = _mm512_loadu_epi16(a);
1647
- b_i16_vec = _mm512_loadu_epi16(b);
1648
- a += 32, b += 32, n -= 32;
1642
+ a_i16_vec = _mm512_loadu_epi16(a_scalars);
1643
+ b_i16_vec = _mm512_loadu_epi16(b_scalars);
1644
+ a_scalars += 32, b_scalars += 32, count_scalars -= 32;
1649
1645
  }
1650
1646
  ab_vec = _mm512_fmadd_ph(_mm512_castsi512_ph(a_i16_vec), _mm512_castsi512_ph(b_i16_vec), ab_vec);
1651
- if (n) goto simsimd_dot_f16_sapphire_cycle;
1647
+ if (count_scalars) goto simsimd_dot_f16_sapphire_cycle;
1652
1648
 
1653
1649
  *result = _mm512_reduce_add_ph(ab_vec);
1654
1650
  }
1655
1651
 
1656
- SIMSIMD_PUBLIC void simsimd_dot_f16c_sapphire(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1657
- simsimd_distance_t *results) {
1652
+ SIMSIMD_PUBLIC void simsimd_dot_f16c_sapphire(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
1653
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
1654
+ __m512i a_vec, b_vec;
1658
1655
  __m512h ab_real_vec = _mm512_setzero_ph();
1659
1656
  __m512h ab_imag_vec = _mm512_setzero_ph();
1660
- __m512i a_vec;
1661
- __m512i b_vec;
1662
1657
 
1663
1658
  // We take into account, that FMS is the same as FMA with a negative multiplier.
1664
1659
  // To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
1665
1660
  // This way we can avoid the shuffling and the need for separate real and imaginary parts.
1666
1661
  // For the imaginary part of the product, we would need to swap the real and imaginary parts of
1667
1662
  // one of the vectors.
1668
- __m512i sign_flip_vec = _mm512_set1_epi32(0x80000000);
1669
- __m512i swap_adjacent_vec = _mm512_set_epi8( //
1663
+ __m512i const sign_flip_vec = _mm512_set1_epi32(0x80000000);
1664
+ __m512i const swap_adjacent_vec = _mm512_set_epi8( //
1670
1665
  61, 60, 63, 62, 57, 56, 59, 58, 53, 52, 55, 54, 49, 48, 51, 50, // 4th 128-bit lane
1671
1666
  45, 44, 47, 46, 41, 40, 43, 42, 37, 36, 39, 38, 33, 32, 35, 34, // 3rd 128-bit lane
1672
1667
  29, 28, 31, 30, 25, 24, 27, 26, 21, 20, 23, 22, 17, 16, 19, 18, // 2nd 128-bit lane
@@ -1674,23 +1669,23 @@ SIMSIMD_PUBLIC void simsimd_dot_f16c_sapphire(simsimd_f16_t const *a, simsimd_f1
1674
1669
  );
1675
1670
 
1676
1671
  simsimd_dot_f16c_sapphire_cycle:
1677
- if (n < 32) {
1678
- __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
1679
- a_vec = _mm512_maskz_loadu_epi16(mask, a);
1680
- b_vec = _mm512_maskz_loadu_epi16(mask, b);
1681
- n = 0;
1672
+ if (count_pairs < 16) {
1673
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
1674
+ a_vec = _mm512_maskz_loadu_epi16(mask, a_pairs);
1675
+ b_vec = _mm512_maskz_loadu_epi16(mask, b_pairs);
1676
+ count_pairs = 0;
1682
1677
  }
1683
1678
  else {
1684
- a_vec = _mm512_loadu_epi16(a);
1685
- b_vec = _mm512_loadu_epi16(b);
1686
- a += 32, b += 32, n -= 32;
1679
+ a_vec = _mm512_loadu_epi16(a_pairs);
1680
+ b_vec = _mm512_loadu_epi16(b_pairs);
1681
+ a_pairs += 16, b_pairs += 16, count_pairs -= 16;
1687
1682
  }
1688
- // TODO: Consider using `_mm512_fmaddsub`
1683
+ // TODO: Consider using `_mm512_fmaddsub` and `_mm512_fcmadd_pch`
1689
1684
  ab_real_vec = _mm512_fmadd_ph(_mm512_castsi512_ph(_mm512_xor_si512(b_vec, sign_flip_vec)),
1690
1685
  _mm512_castsi512_ph(a_vec), ab_real_vec);
1691
1686
  ab_imag_vec = _mm512_fmadd_ph(_mm512_castsi512_ph(_mm512_shuffle_epi8(b_vec, swap_adjacent_vec)),
1692
1687
  _mm512_castsi512_ph(a_vec), ab_imag_vec);
1693
- if (n) goto simsimd_dot_f16c_sapphire_cycle;
1688
+ if (count_pairs) goto simsimd_dot_f16c_sapphire_cycle;
1694
1689
 
1695
1690
  // Reduce horizontal sums:
1696
1691
  // TODO: Optimize this with tree-like reductions
@@ -1698,20 +1693,19 @@ simsimd_dot_f16c_sapphire_cycle:
1698
1693
  results[1] = _mm512_reduce_add_ph(ab_imag_vec);
1699
1694
  }
1700
1695
 
1701
- SIMSIMD_PUBLIC void simsimd_vdot_f16c_sapphire(simsimd_f16_t const *a, simsimd_f16_t const *b, simsimd_size_t n,
1702
- simsimd_distance_t *results) {
1696
+ SIMSIMD_PUBLIC void simsimd_vdot_f16c_sapphire(simsimd_f16c_t const *a_pairs, simsimd_f16c_t const *b_pairs,
1697
+ simsimd_size_t count_pairs, simsimd_distance_t *results) {
1698
+ __m512i a_vec, b_vec;
1703
1699
  __m512h ab_real_vec = _mm512_setzero_ph();
1704
1700
  __m512h ab_imag_vec = _mm512_setzero_ph();
1705
- __m512i a_vec;
1706
- __m512i b_vec;
1707
1701
 
1708
1702
  // We take into account, that FMS is the same as FMA with a negative multiplier.
1709
1703
  // To multiply a floating-point value by -1, we can use the `XOR` instruction to flip the sign bit.
1710
1704
  // This way we can avoid the shuffling and the need for separate real and imaginary parts.
1711
1705
  // For the imaginary part of the product, we would need to swap the real and imaginary parts of
1712
1706
  // one of the vectors.
1713
- __m512i sign_flip_vec = _mm512_set1_epi32(0x80000000);
1714
- __m512i swap_adjacent_vec = _mm512_set_epi8( //
1707
+ __m512i const sign_flip_vec = _mm512_set1_epi32(0x80000000);
1708
+ __m512i const swap_adjacent_vec = _mm512_set_epi8( //
1715
1709
  61, 60, 63, 62, 57, 56, 59, 58, 53, 52, 55, 54, 49, 48, 51, 50, // 4th 128-bit lane
1716
1710
  45, 44, 47, 46, 41, 40, 43, 42, 37, 36, 39, 38, 33, 32, 35, 34, // 3rd 128-bit lane
1717
1711
  29, 28, 31, 30, 25, 24, 27, 26, 21, 20, 23, 22, 17, 16, 19, 18, // 2nd 128-bit lane
@@ -1719,22 +1713,23 @@ SIMSIMD_PUBLIC void simsimd_vdot_f16c_sapphire(simsimd_f16_t const *a, simsimd_f
1719
1713
  );
1720
1714
 
1721
1715
  simsimd_dot_f16c_sapphire_cycle:
1722
- if (n < 32) {
1723
- __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
1724
- a_vec = _mm512_maskz_loadu_epi16(mask, a);
1725
- b_vec = _mm512_maskz_loadu_epi16(mask, b);
1726
- n = 0;
1716
+ if (count_pairs < 16) {
1717
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_pairs * 2);
1718
+ a_vec = _mm512_maskz_loadu_epi16(mask, a_pairs);
1719
+ b_vec = _mm512_maskz_loadu_epi16(mask, b_pairs);
1720
+ count_pairs = 0;
1727
1721
  }
1728
1722
  else {
1729
- a_vec = _mm512_loadu_epi16(a);
1730
- b_vec = _mm512_loadu_epi16(b);
1731
- a += 32, b += 32, n -= 32;
1723
+ a_vec = _mm512_loadu_epi16(a_pairs);
1724
+ b_vec = _mm512_loadu_epi16(b_pairs);
1725
+ a_pairs += 16, b_pairs += 16, count_pairs -= 16;
1732
1726
  }
1727
+ // TODO: Consider using `_mm512_fmaddsub` and `_mm512_fcmadd_pch`
1733
1728
  ab_real_vec = _mm512_fmadd_ph(_mm512_castsi512_ph(a_vec), _mm512_castsi512_ph(b_vec), ab_real_vec);
1734
1729
  a_vec = _mm512_xor_si512(a_vec, sign_flip_vec);
1735
1730
  b_vec = _mm512_shuffle_epi8(b_vec, swap_adjacent_vec);
1736
1731
  ab_imag_vec = _mm512_fmadd_ph(_mm512_castsi512_ph(a_vec), _mm512_castsi512_ph(b_vec), ab_imag_vec);
1737
- if (n) goto simsimd_dot_f16c_sapphire_cycle;
1732
+ if (count_pairs) goto simsimd_dot_f16c_sapphire_cycle;
1738
1733
 
1739
1734
  // Reduce horizontal sums:
1740
1735
  results[0] = _mm512_reduce_add_ph(ab_real_vec);
@@ -1751,53 +1746,53 @@ simsimd_dot_f16c_sapphire_cycle:
1751
1746
  #pragma clang attribute push(__attribute__((target("avx2,avx512f,avx512vl,bmi2,avx512bw,avx512vnni"))), \
1752
1747
  apply_to = function)
1753
1748
 
1754
- SIMSIMD_PUBLIC void simsimd_dot_i8_ice(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1755
- simsimd_distance_t *result) {
1756
- __m512i ab_i32_vec = _mm512_setzero_si512();
1749
+ SIMSIMD_PUBLIC void simsimd_dot_i8_ice(simsimd_i8_t const *a_scalars, simsimd_i8_t const *b_scalars,
1750
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
1757
1751
  __m512i a_i16_vec, b_i16_vec;
1752
+ __m512i ab_i32_vec = _mm512_setzero_si512();
1758
1753
 
1759
1754
  simsimd_dot_i8_ice_cycle:
1760
- if (n < 32) {
1761
- __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, n);
1762
- a_i16_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, a));
1763
- b_i16_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, b));
1764
- n = 0;
1755
+ if (count_scalars < 32) {
1756
+ __mmask32 mask = (__mmask32)_bzhi_u32(0xFFFFFFFF, count_scalars);
1757
+ a_i16_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, a_scalars));
1758
+ b_i16_vec = _mm512_cvtepi8_epi16(_mm256_maskz_loadu_epi8(mask, b_scalars));
1759
+ count_scalars = 0;
1765
1760
  }
1766
1761
  else {
1767
- a_i16_vec = _mm512_cvtepi8_epi16(_mm256_lddqu_si256((__m256i const *)a));
1768
- b_i16_vec = _mm512_cvtepi8_epi16(_mm256_lddqu_si256((__m256i const *)b));
1769
- a += 32, b += 32, n -= 32;
1762
+ a_i16_vec = _mm512_cvtepi8_epi16(_mm256_lddqu_si256((__m256i const *)a_scalars));
1763
+ b_i16_vec = _mm512_cvtepi8_epi16(_mm256_lddqu_si256((__m256i const *)b_scalars));
1764
+ a_scalars += 32, b_scalars += 32, count_scalars -= 32;
1770
1765
  }
1771
1766
  // Unfortunately we can't use the `_mm512_dpbusd_epi32` intrinsics here either,
1772
1767
  // as it's asymmetric with respect to the sign of the input arguments:
1773
- // Signed(ZeroExtend16(a.byte[4*j]) * SignExtend16(b.byte[4*j]))
1768
+ // Signed(ZeroExtend16(a_scalars.byte[4*j]) * SignExtend16(b_scalars.byte[4*j]))
1774
1769
  // So we have to use the `_mm512_dpwssd_epi32` intrinsics instead, upcasting
1775
1770
  // to 16-bit beforehand.
1776
1771
  ab_i32_vec = _mm512_dpwssd_epi32(ab_i32_vec, a_i16_vec, b_i16_vec);
1777
- if (n) goto simsimd_dot_i8_ice_cycle;
1772
+ if (count_scalars) goto simsimd_dot_i8_ice_cycle;
1778
1773
 
1779
1774
  *result = _mm512_reduce_add_epi32(ab_i32_vec);
1780
1775
  }
1781
1776
 
1782
- SIMSIMD_PUBLIC void simsimd_dot_u8_ice(simsimd_u8_t const *a, simsimd_u8_t const *b, simsimd_size_t n,
1783
- simsimd_distance_t *result) {
1777
+ SIMSIMD_PUBLIC void simsimd_dot_u8_ice(simsimd_u8_t const *a_scalars, simsimd_u8_t const *b_scalars,
1778
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
1779
+ __m512i a_u8_vec, b_u8_vec;
1780
+ __m512i a_i16_low_vec, a_i16_high_vec, b_i16_low_vec, b_i16_high_vec;
1784
1781
  __m512i ab_i32_low_vec = _mm512_setzero_si512();
1785
1782
  __m512i ab_i32_high_vec = _mm512_setzero_si512();
1786
1783
  __m512i const zeros_vec = _mm512_setzero_si512();
1787
- __m512i a_i16_low_vec, a_i16_high_vec, b_i16_low_vec, b_i16_high_vec;
1788
- __m512i a_u8_vec, b_u8_vec;
1789
1784
 
1790
1785
  simsimd_dot_u8_ice_cycle:
1791
- if (n < 64) {
1792
- __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, n);
1793
- a_u8_vec = _mm512_maskz_loadu_epi8(mask, a);
1794
- b_u8_vec = _mm512_maskz_loadu_epi8(mask, b);
1795
- n = 0;
1786
+ if (count_scalars < 64) {
1787
+ __mmask64 mask = (__mmask64)_bzhi_u64(0xFFFFFFFFFFFFFFFF, count_scalars);
1788
+ a_u8_vec = _mm512_maskz_loadu_epi8(mask, a_scalars);
1789
+ b_u8_vec = _mm512_maskz_loadu_epi8(mask, b_scalars);
1790
+ count_scalars = 0;
1796
1791
  }
1797
1792
  else {
1798
- a_u8_vec = _mm512_loadu_si512(a);
1799
- b_u8_vec = _mm512_loadu_si512(b);
1800
- a += 64, b += 64, n -= 64;
1793
+ a_u8_vec = _mm512_loadu_si512(a_scalars);
1794
+ b_u8_vec = _mm512_loadu_si512(b_scalars);
1795
+ a_scalars += 64, b_scalars += 64, count_scalars -= 64;
1801
1796
  }
1802
1797
 
1803
1798
  // Upcast `uint8` to `int16`. Unlike the signed version, we can use the unpacking
@@ -1813,7 +1808,7 @@ simsimd_dot_u8_ice_cycle:
1813
1808
  // to 16-bit beforehand.
1814
1809
  ab_i32_low_vec = _mm512_dpwssd_epi32(ab_i32_low_vec, a_i16_low_vec, b_i16_low_vec);
1815
1810
  ab_i32_high_vec = _mm512_dpwssd_epi32(ab_i32_high_vec, a_i16_high_vec, b_i16_high_vec);
1816
- if (n) goto simsimd_dot_u8_ice_cycle;
1811
+ if (count_scalars) goto simsimd_dot_u8_ice_cycle;
1817
1812
 
1818
1813
  *result = _mm512_reduce_add_epi32(_mm512_add_epi32(ab_i32_low_vec, ab_i32_high_vec));
1819
1814
  }
@@ -1827,15 +1822,14 @@ simsimd_dot_u8_ice_cycle:
1827
1822
  #pragma GCC target("avx2", "bmi2", "avx2vnni")
1828
1823
  #pragma clang attribute push(__attribute__((target("avx2,bmi2,avx2vnni"))), apply_to = function)
1829
1824
 
1830
- SIMSIMD_PUBLIC void simsimd_dot_i8_sierra(simsimd_i8_t const *a, simsimd_i8_t const *b, simsimd_size_t n,
1831
- simsimd_distance_t *result) {
1825
+ SIMSIMD_PUBLIC void simsimd_dot_i8_sierra(simsimd_i8_t const *a_scalars, simsimd_i8_t const *b_scalars,
1826
+ simsimd_size_t count_scalars, simsimd_distance_t *result) {
1832
1827
 
1833
1828
  __m256i ab_i32_vec = _mm256_setzero_si256();
1834
-
1835
- simsimd_size_t i = 0;
1836
- for (; i + 32 <= n; i += 32) {
1837
- __m256i a_i8_vec = _mm256_lddqu_si256((__m256i const *)(a + i));
1838
- __m256i b_i8_vec = _mm256_lddqu_si256((__m256i const *)(b + i));
1829
+ simsimd_size_t idx_scalars = 0;
1830
+ for (; idx_scalars + 32 <= count_scalars; idx_scalars += 32) {
1831
+ __m256i a_i8_vec = _mm256_lddqu_si256((__m256i const *)(a_scalars + idx_scalars));
1832
+ __m256i b_i8_vec = _mm256_lddqu_si256((__m256i const *)(b_scalars + idx_scalars));
1839
1833
  ab_i32_vec = _mm256_dpbssds_epi32(ab_i32_vec, a_i8_vec, b_i8_vec);
1840
1834
  }
1841
1835
 
@@ -1843,8 +1837,7 @@ SIMSIMD_PUBLIC void simsimd_dot_i8_sierra(simsimd_i8_t const *a, simsimd_i8_t co
1843
1837
  int ab = _simsimd_reduce_i32x8_haswell(ab_i32_vec);
1844
1838
 
1845
1839
  // Take care of the tail:
1846
- for (; i < n; ++i) ab += (int)(a[i]) * b[i];
1847
-
1840
+ for (; idx_scalars < count_scalars; ++idx_scalars) ab += (int)(a_scalars[idx_scalars]) * b_scalars[idx_scalars];
1848
1841
  *result = ab;
1849
1842
  }
1850
1843