1use super::*;
2
3pick! {
4 if #[cfg(target_feature="avx2")] {
5 #[derive(Default, Clone, Copy, PartialEq, Eq)]
12 #[repr(C, align(32))]
13 pub struct i16x16 { pub(crate) avx2: m256i }
14 } else {
15 #[derive(Default, Clone, Copy, PartialEq, Eq)]
22 #[repr(C, align(32))]
23 pub struct i16x16 { pub(crate) a : i16x8, pub(crate) b : i16x8 }
24 }
25}
26
27impl_simd_int! {
28 unsafe {
29 T = i16,
30 N = 16,
31 Simd = i16x16,
32 UintSimd = u16x16,
33 T_BITS = 16,
34 T_BITS_MUL_2 = 32,
35 BitmaskType = u32,
36 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
37 optional_type_x86_inner { X86Inner = __m256i },
38 optional_type_arm_inner {},
39 optional_type_wasm_inner {},
40 }
41
42 #[inline]
43 fn simd_lt(self, rhs: Self) -> Self::Output {
44 pick! {
45 if #[cfg(target_feature="avx2")] {
46 Self { avx2: !cmp_gt_mask_i16_m256i(self.avx2, rhs.avx2) ^ cmp_eq_mask_i16_m256i(self.avx2,rhs.avx2) }
47 } else {
48 Self {
49 a : self.a.simd_lt(rhs.a),
50 b : self.b.simd_lt(rhs.b),
51 }
52 }
53 }
54 }
55
56 #[inline]
57 fn simd_gt(self, rhs: Self) -> Self::Output {
58 pick! {
59 if #[cfg(target_feature="avx2")] {
60 Self { avx2: cmp_gt_mask_i16_m256i(self.avx2, rhs.avx2) }
61 } else {
62 Self {
63 a : self.a.simd_gt(rhs.a),
64 b : self.b.simd_gt(rhs.b),
65 }
66 }
67 }
68 }
69
70 #[inline]
71 fn simd_le(self, rhs: Self) -> Self::Output {
72 pick! {
73 if #[cfg(target_feature="avx2")] {
74 !self.simd_gt(rhs)
75 } else {
76 Self {
77 a : self.a.simd_le(rhs.a),
78 b : self.b.simd_le(rhs.b),
79 }
80 }
81 }
82 }
83
84 #[inline]
85 fn simd_ge(self, rhs: Self) -> Self::Output {
86 pick! {
87 if #[cfg(target_feature="avx2")] {
88 !self.simd_lt(rhs)
89 } else {
90 Self {
91 a : self.a.simd_ge(rhs.a),
92 b : self.b.simd_ge(rhs.b),
93 }
94 }
95 }
96 }
97
98 #[inline]
99 fn shr(self, rhs: u16x16) -> Self::Output {
100 pick! {
101 if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
102 #[cfg(target_arch = "x86")]
103 use core::arch::x86::_mm256_srav_epi16;
104 #[cfg(target_arch = "x86_64")]
105 use core::arch::x86_64::_mm256_srav_epi16;
106
107 let rhs = bitand_m256i(rhs.avx2, set_splat_i16_m256i(15));
109 cast(unsafe { _mm256_srav_epi16(self.avx2.0, rhs.0) })
111 } else {
112 let [self_a, self_b]: [i16x8; 2] = cast(self);
113 let [rhs_a, rhs_b]: [u16x8; 2] = cast(rhs);
114
115 cast([self_a >> rhs_a, self_b >> rhs_b])
116 }
117 }
118 }
119
120 #[inline]
121 fn shr(self, rhs: u32) -> Self::Output {
122 pick! {
123 if #[cfg(target_feature="avx2")] {
124 #[expect(clippy::suspicious_arithmetic_impl)]
126 let shift = cast([rhs as u64 & 15, 0]);
127 Self { avx2: shr_all_i16_m256i(self.avx2, shift) }
128 } else {
129 Self {
130 a : self.a.shr(rhs),
131 b : self.b.shr(rhs),
132 }
133 }
134 }
135 }
136
137 #[inline]
138 pub fn max(self, rhs: Self) -> Self {
139 pick! {
140 if #[cfg(target_feature="avx2")] {
141 Self { avx2: max_i16_m256i(self.avx2, rhs.avx2) }
142 } else {
143 Self {
144 a : self.a.max(rhs.a),
145 b : self.b.max(rhs.b),
146 }
147 }
148 }
149 }
150
151 #[inline]
152 pub fn min(self, rhs: Self) -> Self {
153 pick! {
154 if #[cfg(target_feature="avx2")] {
155 Self { avx2: min_i16_m256i(self.avx2, rhs.avx2) }
156 } else {
157 Self {
158 a : self.a.min(rhs.a),
159 b : self.b.min(rhs.b),
160 }
161 }
162 }
163 }
164
165 #[inline]
166 pub fn reduce_max(self) -> i16 {
167 let arr: [i16x8; 2] = cast(self);
168
169 arr[0].max(arr[1]).reduce_max()
170 }
171
172 #[inline]
173 pub fn reduce_min(self) -> i16 {
174 let arr: [i16x8; 2] = cast(self);
175
176 arr[0].min(arr[1]).reduce_min()
177 }
178
179 #[inline]
180 pub fn unbounded_shr(self, rhs: u16x16) -> Self {
181 pick! {
182 if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
183 #[cfg(target_arch = "x86")]
184 use core::arch::x86::_mm256_srav_epi16;
185 #[cfg(target_arch = "x86_64")]
186 use core::arch::x86_64::_mm256_srav_epi16;
187
188 cast(unsafe { _mm256_srav_epi16(self.avx2.0, rhs.avx2.0) })
190 } else {
191 let [self_a, self_b] = cast::<i16x16, [i16x8; 2]>(self);
192 let [rhs_a, rhs_b] = cast::<u16x16, [u16x8; 2]>(rhs);
193
194 cast([self_a.unbounded_shr(rhs_a), self_b.unbounded_shr(rhs_b)])
195 }
196 }
197 }
198
199 #[inline]
200 pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
201 pick! {
202 if #[cfg(target_feature="avx2")] {
203 Self { avx2: shr_all_i16_m256i(self.avx2, cast([rhs as u64, 0])) }
204 } else {
205 Self {
206 a: self.a.unbounded_shr_scalar(rhs),
207 b: self.b.unbounded_shr_scalar(rhs),
208 }
209 }
210 }
211 }
212
213 #[inline]
214 pub fn saturating_add(self, rhs: Self) -> Self {
215 pick! {
216 if #[cfg(target_feature="avx2")] {
217 Self { avx2: add_saturating_i16_m256i(self.avx2, rhs.avx2) }
218 } else {
219 Self {
220 a : self.a.saturating_add(rhs.a),
221 b : self.b.saturating_add(rhs.b),
222 }
223 }
224 }
225 }
226
227 #[inline]
228 pub fn saturating_sub(self, rhs: Self) -> Self {
229 pick! {
230 if #[cfg(target_feature="avx2")] {
231 Self { avx2: sub_saturating_i16_m256i(self.avx2, rhs.avx2) }
232 } else {
233 Self {
234 a : self.a.saturating_sub(rhs.a),
235 b : self.b.saturating_sub(rhs.b),
236 }
237 }
238 }
239 }
240
241 #[inline]
242 pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
243 let (low, high) = self.mul_keep_low_high(rhs);
244 let low = cast::<u16x16, i16x16>(low);
245
246 let overflow = high.simd_ne(low.is_negative());
247 (low, overflow)
248 }
249
250 optional_fn_widening_mul {
251 #[inline]
252 pub fn widening_mul(self, rhs: Self) -> i32x16 {
253 let [self_a, self_b] = cast::<i16x16, [i16x8; 2]>(self);
257 let [rhs_a, rhs_b] = cast::<i16x16, [i16x8; 2]>(rhs);
258
259 cast([self_a.widening_mul(rhs_a), self_b.widening_mul(rhs_b)])
260 }
261 }
262
263 #[inline]
264 pub fn mul_keep_low_high(self, rhs: Self) -> (u16x16, i16x16) {
265 let [self_a, self_b] = cast::<i16x16, [i16x8; 2]>(self);
269 let [rhs_a, rhs_b] = cast::<i16x16, [i16x8; 2]>(rhs);
270
271 let result_a = self_a.mul_keep_low_high(rhs_a);
272 let result_b = self_b.mul_keep_low_high(rhs_b);
273 (cast([result_a.0, result_b.0]), cast([result_a.1, result_b.1]))
274 }
275
276 #[inline]
277 pub fn mul_keep_high(self, rhs: Self) -> Self {
278 let [self_a, self_b] = cast::<i16x16, [i16x8; 2]>(self);
282 let [rhs_a, rhs_b] = cast::<i16x16, [i16x8; 2]>(rhs);
283
284 cast([self_a.mul_keep_high(rhs_a), self_b.mul_keep_high(rhs_b)])
285 }
286
287 #[inline]
288 pub fn abs(self) -> Self {
289 pick! {
290 if #[cfg(target_feature="avx2")] {
291 Self { avx2: abs_i16_m256i(self.avx2) }
292 } else {
293 Self {
294 a : self.a.abs(),
295 b : self.b.abs(),
296 }
297 }
298 }
299 }
300
301 #[inline]
302 pub fn is_positive(self) -> Self {
303 pick! {
304 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
305 Self {
307 a: self.a.is_positive(),
308 b: self.b.is_positive(),
309 }
310 } else {
311 self.simd_gt(Self::ZERO)
312 }
313 }
314 }
315
316 #[inline]
317 pub fn is_negative(self) -> Self {
318 pick! {
319 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
320 Self {
322 a: self.a.is_negative(),
323 b: self.b.is_negative(),
324 }
325 } else {
326 self.simd_lt(Self::ZERO)
327 }
328 }
329 }
330
331 optional_fn_deserialize {}
332}
333
334impl From<i8x16> for i16x16 {
335 #[inline]
337 fn from(i: i8x16) -> Self {
338 i16x16::from_i8x16(i)
339 }
340}
341
342impl From<u8x16> for i16x16 {
343 #[inline]
345 fn from(i: u8x16) -> Self {
346 cast(u16x16::from(i))
347 }
348}
349
350impl i16x16 {
353 #[inline]
355 #[must_use]
356 pub fn from_i8x16(v: i8x16) -> Self {
357 pick! {
358 if #[cfg(target_feature="avx2")] {
359 i16x16 { avx2:convert_to_i16_m256i_from_i8_m128i(v.sse) }
360 } else if #[cfg(target_feature="sse4.1")] {
361 i16x16 {
362 a: i16x8 { sse: convert_to_i16_m128i_from_lower8_i8_m128i(v.sse) },
363 b: i16x8 { sse: convert_to_i16_m128i_from_lower8_i8_m128i(unpack_high_i64_m128i(v.sse, v.sse)) }
364 }
365 } else if #[cfg(target_feature="sse2")] {
366 i16x16 {
367 a: i16x8 { sse: shr_imm_i16_m128i::<8>( unpack_low_i8_m128i(v.sse, v.sse)) },
368 b: i16x8 { sse: shr_imm_i16_m128i::<8>( unpack_high_i8_m128i(v.sse, v.sse)) },
369 }
370 } else {
371
372 i16x16::new([
373 v.as_array()[0] as i16,
374 v.as_array()[1] as i16,
375 v.as_array()[2] as i16,
376 v.as_array()[3] as i16,
377 v.as_array()[4] as i16,
378 v.as_array()[5] as i16,
379 v.as_array()[6] as i16,
380 v.as_array()[7] as i16,
381 v.as_array()[8] as i16,
382 v.as_array()[9] as i16,
383 v.as_array()[10] as i16,
384 v.as_array()[11] as i16,
385 v.as_array()[12] as i16,
386 v.as_array()[13] as i16,
387 v.as_array()[14] as i16,
388 v.as_array()[15] as i16,
389 ])
390 }
391 }
392 }
393
394 #[inline]
400 #[must_use]
401 pub fn dot(self, rhs: Self) -> i32x8 {
402 pick! {
403 if #[cfg(target_feature="avx2")] {
404 i32x8 { avx2: mul_i16_horizontal_add_m256i(self.avx2, rhs.avx2) }
405 } else {
406 i32x8 {
407 a : self.a.dot(rhs.a),
408 b : self.b.dot(rhs.b),
409 }
410 }
411 }
412 }
413
414 #[inline]
422 #[must_use]
423 pub fn mul_scale_round(self, rhs: Self) -> Self {
424 pick! {
425 if #[cfg(target_feature="avx2")] {
426 Self { avx2: mul_i16_scale_round_m256i(self.avx2, rhs.avx2) }
427 } else {
428 Self {
429 a : self.a.mul_scale_round(rhs.a),
430 b : self.b.mul_scale_round(rhs.b),
431 }
432 }
433 }
434 }
435
436 #[inline]
444 #[must_use]
445 pub fn mul_scale_round_n(self, rhs: i16) -> Self {
446 pick! {
447 if #[cfg(target_feature="avx2")] {
448 Self { avx2: mul_i16_scale_round_m256i(self.avx2, set_splat_i16_m256i(rhs)) }
449 } else {
450 Self {
451 a : self.a.mul_scale_round_n(rhs),
452 b : self.b.mul_scale_round_n(rhs),
453 }
454 }
455 }
456 }
457}