1use super::*;
2
3pick! {
4 if #[cfg(target_feature="avx512bw")] {
5 #[derive(Default, Clone, Copy, PartialEq, Eq)]
12 #[repr(C, align(64))]
13 pub struct i16x32 { pub(crate) avx512: m512i }
14 } else {
15 #[derive(Default, Clone, Copy, PartialEq, Eq)]
22 #[repr(C, align(64))]
23 pub struct i16x32 { pub(crate) a : i16x16, pub(crate) b : i16x16 }
24 }
25}
26
27impl_simd_int! {
28 unsafe {
29 T = i16,
30 N = 32,
31 Simd = i16x32,
32 UintSimd = u16x32,
33 T_BITS = 16,
34 T_BITS_MUL_2 = 32,
35 BitmaskType = u32,
36 [
37 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
38 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
39 ],
40 optional_type_x86_inner { X86Inner = __m512i },
41 optional_type_arm_inner {},
42 optional_type_wasm_inner {},
43 }
44
45 #[inline]
46 fn simd_lt(self, rhs: Self) -> Self::Output {
47 pick! {
48 if #[cfg(target_feature="avx512bw")] {
49 Self { avx512: cmp_op_mask_i16_m512i::<{cmp_int_op!(Lt)}>(self.avx512, rhs.avx512) }
50 } else {
51 Self {
52 a : rhs.a.simd_gt(self.a),
53 b : rhs.b.simd_gt(self.b),
54 }
55 }
56 }
57 }
58
59 #[inline]
60 fn simd_gt(self, rhs: Self) -> Self::Output {
61 pick! {
62 if #[cfg(target_feature="avx512bw")] {
63 Self { avx512: cmp_op_mask_i16_m512i::<{cmp_int_op!(Nle)}>(self.avx512, rhs.avx512) }
64 } else {
65 Self {
66 a : self.a.simd_gt(rhs.a),
67 b : self.b.simd_gt(rhs.b),
68 }
69 }
70 }
71 }
72
73 #[inline]
74 fn simd_le(self, rhs: Self) -> Self::Output {
75 pick! {
76 if #[cfg(target_feature="avx512bw")] {
77 Self { avx512: cmp_op_mask_i16_m512i::<{cmp_int_op!(Le)}>(self.avx512, rhs.avx512) }
78 } else {
79 Self {
80 a : self.a.simd_le(rhs.a),
81 b : self.b.simd_le(rhs.b),
82 }
83 }
84 }
85 }
86
87 #[inline]
88 fn simd_ge(self, rhs: Self) -> Self::Output {
89 pick! {
90 if #[cfg(target_feature="avx512bw")] {
91 Self { avx512: cmp_op_mask_i16_m512i::<{cmp_int_op!(Nlt)}>(self.avx512, rhs.avx512) }
92 } else {
93 Self {
94 a : self.a.simd_ge(rhs.a),
95 b : self.b.simd_ge(rhs.b),
96 }
97 }
98 }
99 }
100
101 #[inline]
102 fn shr(self, rhs: u16x32) -> Self::Output {
103 pick! {
104 if #[cfg(target_feature="avx512bw")] {
105 #[cfg(target_arch = "x86")]
106 use core::arch::x86::_mm512_srav_epi16;
107 #[cfg(target_arch = "x86_64")]
108 use core::arch::x86_64::_mm512_srav_epi16;
109
110 let rhs = bitand_m512i(rhs.avx512, set_splat_i16_m512i(15));
112 Self { avx512: m512i(unsafe { _mm512_srav_epi16(self.avx512.0, rhs.0) }) }
114 } else {
115 let [self_a, self_b]: [i16x16; 2] = cast(self);
116 let [rhs_a, rhs_b]: [u16x16; 2] = cast(rhs);
117
118 cast([self_a >> rhs_a, self_b >> rhs_b])
119 }
120 }
121 }
122
123 #[inline]
124 fn shr(self, rhs: u32) -> Self::Output {
125 pick! {
126 if #[cfg(target_feature="avx512bw")] {
127 #[expect(clippy::suspicious_arithmetic_impl)]
129 let shift = rhs as u16 & 15;
130 Self { avx512: shr_all_i16_m512i(self.avx512, shift) }
131 } else {
132 Self {
133 a : self.a.shr(rhs),
134 b : self.b.shr(rhs),
135 }
136 }
137 }
138 }
139
140 #[inline]
141 pub fn max(self, rhs: Self) -> Self {
142 pick! {
143 if #[cfg(target_feature="avx512bw")] {
144 Self { avx512: max_i16_m512i(self.avx512, rhs.avx512) }
145 } else {
146 Self {
147 a: self.a.max(rhs.a),
148 b: self.b.max(rhs.b),
149 }
150 }
151 }
152 }
153
154 #[inline]
155 pub fn min(self, rhs: Self) -> Self {
156 pick! {
157 if #[cfg(target_feature="avx512bw")] {
158 Self { avx512: min_i16_m512i(self.avx512, rhs.avx512) }
159 } else {
160 Self {
161 a: self.a.min(rhs.a),
162 b: self.b.min(rhs.b),
163 }
164 }
165 }
166 }
167
168 #[inline]
169 pub fn reduce_max(self) -> i16 {
170 let arr: [i16x16; 2] = cast(self);
171 arr[0].max(arr[1]).reduce_max()
172 }
173
174 #[inline]
175 pub fn reduce_min(self) -> i16 {
176 let arr: [i16x16; 2] = cast(self);
177 arr[0].min(arr[1]).reduce_min()
178 }
179
180 #[inline]
181 pub fn unbounded_shr(self, rhs: u16x32) -> Self {
182 pick! {
183 if #[cfg(target_feature="avx512bw")] {
184 #[cfg(target_arch = "x86")]
185 use core::arch::x86::_mm512_srav_epi16;
186 #[cfg(target_arch = "x86_64")]
187 use core::arch::x86_64::_mm512_srav_epi16;
188
189 Self { avx512: m512i(unsafe { _mm512_srav_epi16(self.avx512.0, rhs.avx512.0) }) }
191 } else {
192 let [self_a, self_b] = cast::<i16x32, [i16x16; 2]>(self);
193 let [rhs_a, rhs_b] = cast::<u16x32, [u16x16; 2]>(rhs);
194
195 cast([self_a.unbounded_shr(rhs_a), self_b.unbounded_shr(rhs_b)])
196 }
197 }
198 }
199
200 #[inline]
201 pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
202 pick! {
203 if #[cfg(target_feature="avx512bw")] {
204 Self { avx512: shr_all_i16_m512i(self.avx512, rhs.min(u16::MAX as u32) as u16) }
207 } else {
208 Self {
209 a: self.a.unbounded_shr_scalar(rhs),
210 b: self.b.unbounded_shr_scalar(rhs),
211 }
212 }
213 }
214 }
215
216 #[inline]
217 pub fn saturating_add(self, rhs: Self) -> Self {
218 pick! {
219 if #[cfg(target_feature="avx512bw")] {
220 Self { avx512: add_saturating_i16_m512i(self.avx512, rhs.avx512) }
221 } else {
222 Self {
223 a: self.a.saturating_add(rhs.a),
224 b: self.b.saturating_add(rhs.b),
225 }
226 }
227 }
228 }
229
230 #[inline]
231 pub fn saturating_sub(self, rhs: Self) -> Self {
232 pick! {
233 if #[cfg(target_feature="avx512bw")] {
234 Self { avx512: sub_saturating_i16_m512i(self.avx512, rhs.avx512) }
235 } else {
236 Self {
237 a: self.a.saturating_sub(rhs.a),
238 b: self.b.saturating_sub(rhs.b),
239 }
240 }
241 }
242 }
243
244 #[inline]
245 pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
246 let (low, high) = self.mul_keep_low_high(rhs);
247 let low = cast::<u16x32, i16x32>(low);
248
249 let overflow = high.simd_ne(low.is_negative());
250 (low, overflow)
251 }
252
253 optional_fn_widening_mul {
254 }
256
257 #[inline]
258 pub fn mul_keep_low_high(self, rhs: Self) -> (u16x32, i16x32) {
259 let [self_a, self_b] = cast::<i16x32, [i16x16; 2]>(self);
263 let [rhs_a, rhs_b] = cast::<i16x32, [i16x16; 2]>(rhs);
264
265 let result_a = self_a.mul_keep_low_high(rhs_a);
266 let result_b = self_b.mul_keep_low_high(rhs_b);
267 (cast([result_a.0, result_b.0]), cast([result_a.1, result_b.1]))
268 }
269
270 #[inline]
271 pub fn mul_keep_high(self, rhs: Self) -> Self {
272 let [self_a, self_b] = cast::<i16x32, [i16x16; 2]>(self);
276 let [rhs_a, rhs_b] = cast::<i16x32, [i16x16; 2]>(rhs);
277
278 cast([self_a.mul_keep_high(rhs_a), self_b.mul_keep_high(rhs_b)])
279 }
280
281 #[inline]
282 pub fn abs(self) -> Self {
283 pick! {
284 if #[cfg(target_feature="avx512bw")] {
285 Self { avx512: abs_i16_m512i(self.avx512) }
286 } else {
287 Self {
288 a : self.a.abs(),
289 b : self.b.abs(),
290 }
291 }
292 }
293 }
294
295 #[inline]
296 pub fn is_positive(self) -> Self {
297 pick! {
298 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
299 Self {
301 a: self.a.is_positive(),
302 b: self.b.is_positive(),
303 }
304 } else {
305 self.simd_gt(Self::ZERO)
306 }
307 }
308 }
309
310 #[inline]
311 pub fn is_negative(self) -> Self {
312 pick! {
313 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
314 Self {
316 a: self.a.is_negative(),
317 b: self.b.is_negative(),
318 }
319 } else {
320 self.simd_lt(Self::ZERO)
321 }
322 }
323 }
324
325 optional_fn_deserialize {}
326}
327
328impl From<i8x32> for i16x32 {
329 #[inline]
331 fn from(i: i8x32) -> Self {
332 i16x32::from_i8x32(i)
333 }
334}
335
336impl i16x32 {
339 #[inline]
341 #[must_use]
342 pub fn from_i8x32(v: i8x32) -> Self {
343 pick! {
344 if #[cfg(target_feature="avx512bw")] {
345 Self { avx512: convert_to_i16_m512i_from_i8_m256i(cast(v)) }
346 } else {
347 let [a, b]: [i8x16; 2] = cast(v);
348 Self {
349 a: i16x16::from_i8x16(a),
350 b: i16x16::from_i8x16(b),
351 }
352 }
353 }
354 }
355
356 #[inline]
362 #[must_use]
363 pub fn dot(self, rhs: Self) -> i32x16 {
364 pick! {
365 if #[cfg(target_feature="avx512bw")] {
366 i32x16 { avx512: mul_i16_horizontal_add_m512i(self.avx512, rhs.avx512) }
367 } else {
368 i32x16 {
369 a : self.a.dot(rhs.a),
370 b : self.b.dot(rhs.b),
371 }
372 }
373 }
374 }
375}