1use super::*;
2
3pick! {
4 if #[cfg(target_feature="avx2")] {
5 #[derive(Default, Clone, Copy, PartialEq, Eq)]
6 #[repr(C, align(32))]
7 pub struct i16x16 { pub(crate) avx2: m256i }
8 } else {
9 #[derive(Default, Clone, Copy, PartialEq, Eq)]
10 #[repr(C, align(32))]
11 pub struct i16x16 { pub(crate) a : i16x8, pub(crate) b : i16x8 }
12 }
13}
14
15int_uint_consts!(i16, 16, i16x16, 256);
16
17unsafe impl Zeroable for i16x16 {}
18unsafe impl Pod for i16x16 {}
19
20impl AlignTo for i16x16 {
21 type Elem = i16;
22}
23
24impl Add for i16x16 {
25 type Output = Self;
26 #[inline]
27 fn add(self, rhs: Self) -> Self::Output {
28 pick! {
29 if #[cfg(target_feature="avx2")] {
30 Self { avx2: add_i16_m256i(self.avx2, rhs.avx2) }
31 } else {
32 Self {
33 a : self.a.add(rhs.a),
34 b : self.b.add(rhs.b),
35 }
36 }
37 }
38 }
39}
40
41impl Sub for i16x16 {
42 type Output = Self;
43 #[inline]
44 fn sub(self, rhs: Self) -> Self::Output {
45 pick! {
46 if #[cfg(target_feature="avx2")] {
47 Self { avx2: sub_i16_m256i(self.avx2, rhs.avx2) }
48 } else {
49 Self {
50 a : self.a.sub(rhs.a),
51 b : self.b.sub(rhs.b),
52 }
53 }
54 }
55 }
56}
57
58impl Mul for i16x16 {
59 type Output = Self;
60 #[inline]
61 fn mul(self, rhs: Self) -> Self::Output {
62 pick! {
63 if #[cfg(target_feature="avx2")] {
64 Self { avx2: mul_i16_keep_low_m256i(self.avx2, rhs.avx2) }
65 } else {
66 Self {
67 a : self.a.mul(rhs.a),
68 b : self.b.mul(rhs.b),
69 }
70 }
71 }
72 }
73}
74
75integer_impl_div_rem!(
76 i16,
77 i16x16,
78 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
79);
80
81impl Shl for i16x16 {
82 type Output = Self;
83
84 #[inline]
90 fn shl(self, rhs: Self) -> Self::Output {
91 pick! {
92 if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
93 #[cfg(target_arch = "x86")]
94 use core::arch::x86::_mm256_sllv_epi16;
95 #[cfg(target_arch = "x86_64")]
96 use core::arch::x86_64::_mm256_sllv_epi16;
97
98 let rhs = bitand_m256i(rhs.avx2, set_splat_i16_m256i(15));
100 cast(unsafe { _mm256_sllv_epi16(self.avx2.0, rhs.0) })
102 } else {
103 let [self_a, self_b]: [i16x8; 2] = cast(self);
104 let [rhs_a, rhs_b]: [i16x8; 2] = cast(rhs);
105
106 cast([self_a << rhs_a, self_b << rhs_b])
107 }
108 }
109 }
110}
111
112impl Shr for i16x16 {
113 type Output = Self;
114
115 #[inline]
121 fn shr(self, rhs: Self) -> Self::Output {
122 pick! {
123 if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
124 #[cfg(target_arch = "x86")]
125 use core::arch::x86::_mm256_srav_epi16;
126 #[cfg(target_arch = "x86_64")]
127 use core::arch::x86_64::_mm256_srav_epi16;
128
129 let rhs = bitand_m256i(rhs.avx2, set_splat_i16_m256i(15));
131 cast(unsafe { _mm256_srav_epi16(self.avx2.0, rhs.0) })
133 } else {
134 let [self_a, self_b]: [i16x8; 2] = cast(self);
135 let [rhs_a, rhs_b]: [i16x8; 2] = cast(rhs);
136
137 cast([self_a >> rhs_a, self_b >> rhs_b])
138 }
139 }
140 }
141}
142
143impl Add<i16> for i16x16 {
144 type Output = Self;
145 #[inline]
146 fn add(self, rhs: i16) -> Self::Output {
147 self.add(Self::splat(rhs))
148 }
149}
150
151impl Sub<i16> for i16x16 {
152 type Output = Self;
153 #[inline]
154 fn sub(self, rhs: i16) -> Self::Output {
155 self.sub(Self::splat(rhs))
156 }
157}
158
159impl Mul<i16> for i16x16 {
160 type Output = Self;
161 #[inline]
162 fn mul(self, rhs: i16) -> Self::Output {
163 self.mul(Self::splat(rhs))
164 }
165}
166
167impl Add<i16x16> for i16 {
168 type Output = i16x16;
169 #[inline]
170 fn add(self, rhs: i16x16) -> Self::Output {
171 i16x16::splat(self).add(rhs)
172 }
173}
174
175impl Sub<i16x16> for i16 {
176 type Output = i16x16;
177 #[inline]
178 fn sub(self, rhs: i16x16) -> Self::Output {
179 i16x16::splat(self).sub(rhs)
180 }
181}
182
183impl Mul<i16x16> for i16 {
184 type Output = i16x16;
185 #[inline]
186 fn mul(self, rhs: i16x16) -> Self::Output {
187 i16x16::splat(self).mul(rhs)
188 }
189}
190
191impl BitAnd for i16x16 {
192 type Output = Self;
193 #[inline]
194 fn bitand(self, rhs: Self) -> Self::Output {
195 pick! {
196 if #[cfg(target_feature="avx2")] {
197 Self { avx2: bitand_m256i(self.avx2, rhs.avx2) }
198 } else {
199 Self {
200 a : self.a.bitand(rhs.a),
201 b : self.b.bitand(rhs.b),
202 }
203 }
204 }
205 }
206}
207
208impl BitOr for i16x16 {
209 type Output = Self;
210 #[inline]
211 fn bitor(self, rhs: Self) -> Self::Output {
212 pick! {
213 if #[cfg(target_feature="avx2")] {
214 Self { avx2: bitor_m256i(self.avx2, rhs.avx2) }
215 } else {
216 Self {
217 a : self.a.bitor(rhs.a),
218 b : self.b.bitor(rhs.b),
219 }
220 }
221 }
222 }
223}
224
225impl BitXor for i16x16 {
226 type Output = Self;
227 #[inline]
228 fn bitxor(self, rhs: Self) -> Self::Output {
229 pick! {
230 if #[cfg(target_feature="avx2")] {
231 Self { avx2: bitxor_m256i(self.avx2, rhs.avx2) }
232 } else {
233 Self {
234 a : self.a.bitxor(rhs.a),
235 b : self.b.bitxor(rhs.b),
236 }
237 }
238 }
239 }
240}
241
242macro_rules! impl_shl_t_for_i16x16 {
243 ($($shift_type:ty),+ $(,)?) => {
244 $(impl Shl<$shift_type> for i16x16 {
245 type Output = Self;
246 #[inline]
248 fn shl(self, rhs: $shift_type) -> Self::Output {
249 pick! {
250 if #[cfg(target_feature="avx2")] {
251 let shift = cast([rhs as u64, 0]);
252 Self { avx2: shl_all_u16_m256i(self.avx2, shift) }
253 } else {
254 Self {
255 a : self.a.shl(rhs),
256 b : self.b.shl(rhs),
257 }
258 }
259 }
260 }
261 })+
262 };
263}
264impl_shl_t_for_i16x16!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
265
266macro_rules! impl_shr_t_for_i16x16 {
267 ($($shift_type:ty),+ $(,)?) => {
268 $(impl Shr<$shift_type> for i16x16 {
269 type Output = Self;
270 #[inline]
272 fn shr(self, rhs: $shift_type) -> Self::Output {
273 pick! {
274 if #[cfg(target_feature="avx2")] {
275 let shift = cast([rhs as u64, 0]);
276 Self { avx2: shr_all_i16_m256i(self.avx2, shift) }
277 } else {
278 Self {
279 a : self.a.shr(rhs),
280 b : self.b.shr(rhs),
281 }
282 }
283 }
284 }
285 })+
286 };
287}
288impl_shr_t_for_i16x16!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
289
290#[expect(deprecated)]
291impl CmpEq for i16x16 {
292 type Output = Self;
293 #[inline]
294 fn simd_eq(self, rhs: Self) -> Self::Output {
295 pick! {
296 if #[cfg(target_feature="avx2")] {
297 Self { avx2: cmp_eq_mask_i16_m256i(self.avx2, rhs.avx2) }
298 } else {
299 Self {
300 a : self.a.simd_eq(rhs.a),
301 b : self.b.simd_eq(rhs.b),
302 }
303 }
304 }
305 }
306}
307
308#[expect(deprecated)]
309impl CmpGt for i16x16 {
310 type Output = Self;
311 #[inline]
312 fn simd_gt(self, rhs: Self) -> Self::Output {
313 pick! {
314 if #[cfg(target_feature="avx2")] {
315 Self { avx2: cmp_gt_mask_i16_m256i(self.avx2, rhs.avx2) }
316 } else {
317 Self {
318 a : self.a.simd_gt(rhs.a),
319 b : self.b.simd_gt(rhs.b),
320 }
321 }
322 }
323 }
324}
325
326#[expect(deprecated)]
327impl CmpLt for i16x16 {
328 type Output = Self;
329 #[inline]
330 fn simd_lt(self, rhs: Self) -> Self::Output {
331 pick! {
332 if #[cfg(target_feature="avx2")] {
333 Self { avx2: !cmp_gt_mask_i16_m256i(self.avx2, rhs.avx2) ^ cmp_eq_mask_i16_m256i(self.avx2,rhs.avx2) }
334 } else {
335 Self {
336 a : self.a.simd_lt(rhs.a),
337 b : self.b.simd_lt(rhs.b),
338 }
339 }
340 }
341 }
342}
343
344#[expect(deprecated)]
345impl CmpNe for i16x16 {
346 type Output = Self;
347 #[inline]
348 fn simd_ne(self, rhs: Self) -> Self::Output {
349 pick! {
350 if #[cfg(target_feature="avx2")] {
351 !self.simd_eq(rhs)
352 } else {
353 Self {
354 a : self.a.simd_ne(rhs.a),
355 b : self.b.simd_ne(rhs.b),
356 }
357 }
358 }
359 }
360}
361
362#[expect(deprecated)]
363impl CmpLe for i16x16 {
364 type Output = Self;
365 #[inline]
366 fn simd_le(self, rhs: Self) -> Self::Output {
367 pick! {
368 if #[cfg(target_feature="avx2")] {
369 !self.simd_gt(rhs)
370 } else {
371 Self {
372 a : self.a.simd_le(rhs.a),
373 b : self.b.simd_le(rhs.b),
374 }
375 }
376 }
377 }
378}
379
380#[expect(deprecated)]
381impl CmpGe for i16x16 {
382 type Output = Self;
383 #[inline]
384 fn simd_ge(self, rhs: Self) -> Self::Output {
385 pick! {
386 if #[cfg(target_feature="avx2")] {
387 !self.simd_lt(rhs)
388 } else {
389 Self {
390 a : self.a.simd_ge(rhs.a),
391 b : self.b.simd_ge(rhs.b),
392 }
393 }
394 }
395 }
396}
397
398impl From<i8x16> for i16x16 {
399 #[inline]
401 fn from(i: i8x16) -> Self {
402 i16x16::from_i8x16(i)
403 }
404}
405
406impl From<u8x16> for i16x16 {
407 #[inline]
409 fn from(i: u8x16) -> Self {
410 cast(u16x16::from(i))
411 }
412}
413
414impl Not for i16x16 {
415 type Output = Self;
416 #[inline]
417 fn not(self) -> Self {
418 pick! {
419 if #[cfg(target_feature="avx2")] {
420 Self { avx2: self.avx2.not() }
421 } else {
422 Self {
423 a : self.a.not(),
424 b : self.b.not(),
425 }
426 }
427 }
428 }
429}
430
431impl i16x16 {
432 #[inline]
433 #[must_use]
434 pub const fn new(array: [i16; 16]) -> Self {
435 unsafe { core::mem::transmute(array) }
436 }
437
438 simd_comparison_fns!();
439
440 #[inline]
441 #[must_use]
442 #[doc(alias("movemask", "move_mask"))]
443 pub fn to_bitmask(self) -> u32 {
444 pick! {
445 if #[cfg(target_feature="sse2")] {
446 let [a,b] = cast::<_,[m128i;2]>(self);
447 move_mask_i8_m128i( pack_i16_to_i8_m128i(a,b)) as u32
448 } else {
449 self.a.to_bitmask() | (self.b.to_bitmask() << 8)
450 }
451 }
452 }
453
454 #[inline]
455 #[must_use]
456 pub fn any(self) -> bool {
457 pick! {
458 if #[cfg(target_feature="avx2")] {
459 ((move_mask_i8_m256i(self.avx2) as u32) & 0b10101010101010101010101010101010) != 0
460 } else {
461 (self.a | self.b).any()
462 }
463 }
464 }
465 #[inline]
466 #[must_use]
467 pub fn all(self) -> bool {
468 pick! {
469 if #[cfg(target_feature="avx2")] {
470 ((move_mask_i8_m256i(self.avx2) as u32) & 0b10101010101010101010101010101010) == 0b10101010101010101010101010101010
471 } else {
472 (self.a & self.b).all()
473 }
474 }
475 }
476 #[inline]
477 #[must_use]
478 pub fn none(self) -> bool {
479 !self.any()
480 }
481
482 #[inline]
484 #[must_use]
485 pub fn from_i8x16(v: i8x16) -> Self {
486 pick! {
487 if #[cfg(target_feature="avx2")] {
488 i16x16 { avx2:convert_to_i16_m256i_from_i8_m128i(v.sse) }
489 } else if #[cfg(target_feature="sse4.1")] {
490 i16x16 {
491 a: i16x8 { sse: convert_to_i16_m128i_from_lower8_i8_m128i(v.sse) },
492 b: i16x8 { sse: convert_to_i16_m128i_from_lower8_i8_m128i(unpack_high_i64_m128i(v.sse, v.sse)) }
493 }
494 } else if #[cfg(target_feature="sse2")] {
495 i16x16 {
496 a: i16x8 { sse: shr_imm_i16_m128i::<8>( unpack_low_i8_m128i(v.sse, v.sse)) },
497 b: i16x8 { sse: shr_imm_i16_m128i::<8>( unpack_high_i8_m128i(v.sse, v.sse)) },
498 }
499 } else {
500
501 i16x16::new([
502 v.as_array()[0] as i16,
503 v.as_array()[1] as i16,
504 v.as_array()[2] as i16,
505 v.as_array()[3] as i16,
506 v.as_array()[4] as i16,
507 v.as_array()[5] as i16,
508 v.as_array()[6] as i16,
509 v.as_array()[7] as i16,
510 v.as_array()[8] as i16,
511 v.as_array()[9] as i16,
512 v.as_array()[10] as i16,
513 v.as_array()[11] as i16,
514 v.as_array()[12] as i16,
515 v.as_array()[13] as i16,
516 v.as_array()[14] as i16,
517 v.as_array()[15] as i16,
518 ])
519 }
520 }
521 }
522
523 #[inline]
524 #[must_use]
525 pub fn blend(self, t: Self, f: Self) -> Self {
526 pick! {
527 if #[cfg(target_feature="avx2")] {
528 Self { avx2: blend_varying_i8_m256i(f.avx2, t.avx2, self.avx2) }
529 } else {
530 Self {
531 a : self.a.blend(t.a, f.a),
532 b : self.b.blend(t.b, f.b),
533 }
534 }
535 }
536 }
537
538 #[inline]
541 #[must_use]
542 pub fn is_positive(self) -> Self {
543 pick! {
544 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
545 Self {
547 a: self.a.is_positive(),
548 b: self.b.is_positive(),
549 }
550 } else {
551 self.simd_gt(Self::ZERO)
552 }
553 }
554 }
555
556 #[inline]
559 #[must_use]
560 pub fn is_negative(self) -> Self {
561 pick! {
562 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
563 Self {
565 a: self.a.is_negative(),
566 b: self.b.is_negative(),
567 }
568 } else {
569 self.simd_lt(Self::ZERO)
570 }
571 }
572 }
573
574 #[inline]
576 #[must_use]
577 pub fn reduce_add(self) -> i16 {
578 let arr: [i16x8; 2] = cast(self);
579
580 (arr[0] + arr[1]).reduce_add()
581 }
582
583 #[inline]
585 #[must_use]
586 pub fn reduce_min(self) -> i16 {
587 let arr: [i16x8; 2] = cast(self);
588
589 arr[0].min(arr[1]).reduce_min()
590 }
591
592 #[inline]
594 #[must_use]
595 pub fn reduce_max(self) -> i16 {
596 let arr: [i16x8; 2] = cast(self);
597
598 arr[0].max(arr[1]).reduce_max()
599 }
600
601 #[inline]
602 #[must_use]
603 pub fn abs(self) -> Self {
604 pick! {
605 if #[cfg(target_feature="avx2")] {
606 Self { avx2: abs_i16_m256i(self.avx2) }
607 } else {
608 Self {
609 a : self.a.abs(),
610 b : self.b.abs(),
611 }
612 }
613 }
614 }
615
616 #[inline]
617 #[must_use]
618 pub fn unsigned_abs(self) -> u16x16 {
619 pick! {
620 if #[cfg(target_feature="avx2")] {
621 u16x16 { avx2: abs_i16_m256i(self.avx2) }
622 } else {
623 u16x16 {
624 a: self.a.unsigned_abs(),
625 b: self.b.unsigned_abs(),
626 }
627 }
628 }
629 }
630
631 signed_fn_signum!();
632
633 #[inline]
634 #[must_use]
635 pub fn max(self, rhs: Self) -> Self {
636 pick! {
637 if #[cfg(target_feature="avx2")] {
638 Self { avx2: max_i16_m256i(self.avx2, rhs.avx2) }
639 } else {
640 Self {
641 a : self.a.max(rhs.a),
642 b : self.b.max(rhs.b),
643 }
644 }
645 }
646 }
647 #[inline]
648 #[must_use]
649 pub fn min(self, rhs: Self) -> Self {
650 pick! {
651 if #[cfg(target_feature="avx2")] {
652 Self { avx2: min_i16_m256i(self.avx2, rhs.avx2) }
653 } else {
654 Self {
655 a : self.a.min(rhs.a),
656 b : self.b.min(rhs.b),
657 }
658 }
659 }
660 }
661
662 integer_fn_clamp!();
663
664 #[inline]
665 #[must_use]
666 pub fn saturating_add(self, rhs: Self) -> Self {
667 pick! {
668 if #[cfg(target_feature="avx2")] {
669 Self { avx2: add_saturating_i16_m256i(self.avx2, rhs.avx2) }
670 } else {
671 Self {
672 a : self.a.saturating_add(rhs.a),
673 b : self.b.saturating_add(rhs.b),
674 }
675 }
676 }
677 }
678 #[inline]
679 #[must_use]
680 pub fn saturating_sub(self, rhs: Self) -> Self {
681 pick! {
682 if #[cfg(target_feature="avx2")] {
683 Self { avx2: sub_saturating_i16_m256i(self.avx2, rhs.avx2) }
684 } else {
685 Self {
686 a : self.a.saturating_sub(rhs.a),
687 b : self.b.saturating_sub(rhs.b),
688 }
689 }
690 }
691 }
692
693 #[inline]
695 #[must_use]
696 pub fn saturating_mul(self, rhs: Self) -> Self {
697 let [self_a, self_b]: [i16x8; 2] = cast(self);
698 let [rhs_a, rhs_b]: [i16x8; 2] = cast(rhs);
699 cast([self_a.saturating_mul(rhs_a), self_b.saturating_mul(rhs_b)])
700 }
701
702 integer_fn_saturating_div!([
703 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
704 ]);
705
706 #[inline]
711 #[must_use]
712 pub fn dot(self, rhs: Self) -> i32x8 {
713 pick! {
714 if #[cfg(target_feature="avx2")] {
715 i32x8 { avx2: mul_i16_horizontal_add_m256i(self.avx2, rhs.avx2) }
716 } else {
717 i32x8 {
718 a : self.a.dot(rhs.a),
719 b : self.b.dot(rhs.b),
720 }
721 }
722 }
723 }
724
725 #[inline]
733 #[must_use]
734 pub fn mul_scale_round(self, rhs: Self) -> Self {
735 pick! {
736 if #[cfg(target_feature="avx2")] {
737 Self { avx2: mul_i16_scale_round_m256i(self.avx2, rhs.avx2) }
738 } else {
739 Self {
740 a : self.a.mul_scale_round(rhs.a),
741 b : self.b.mul_scale_round(rhs.b),
742 }
743 }
744 }
745 }
746
747 #[inline]
755 #[must_use]
756 pub fn mul_scale_round_n(self, rhs: i16) -> Self {
757 pick! {
758 if #[cfg(target_feature="avx2")] {
759 Self { avx2: mul_i16_scale_round_m256i(self.avx2, set_splat_i16_m256i(rhs)) }
760 } else {
761 Self {
762 a : self.a.mul_scale_round_n(rhs),
763 b : self.b.mul_scale_round_n(rhs),
764 }
765 }
766 }
767 }
768
769 #[must_use]
771 #[inline]
772 pub fn transpose(data: [i16x16; 16]) -> [i16x16; 16] {
773 #[inline(always)]
776 fn transpose_column(data: &[i16x16; 16], index: usize) -> i16x16 {
777 i16x16::new([
778 data[0].as_array()[index],
779 data[1].as_array()[index],
780 data[2].as_array()[index],
781 data[3].as_array()[index],
782 data[4].as_array()[index],
783 data[5].as_array()[index],
784 data[6].as_array()[index],
785 data[7].as_array()[index],
786 data[8].as_array()[index],
787 data[9].as_array()[index],
788 data[10].as_array()[index],
789 data[11].as_array()[index],
790 data[12].as_array()[index],
791 data[13].as_array()[index],
792 data[14].as_array()[index],
793 data[15].as_array()[index],
794 ])
795 }
796
797 [
798 transpose_column(&data, 0),
799 transpose_column(&data, 1),
800 transpose_column(&data, 2),
801 transpose_column(&data, 3),
802 transpose_column(&data, 4),
803 transpose_column(&data, 5),
804 transpose_column(&data, 6),
805 transpose_column(&data, 7),
806 transpose_column(&data, 8),
807 transpose_column(&data, 9),
808 transpose_column(&data, 10),
809 transpose_column(&data, 11),
810 transpose_column(&data, 12),
811 transpose_column(&data, 13),
812 transpose_column(&data, 14),
813 transpose_column(&data, 15),
814 ]
815 }
816
817 #[inline]
818 pub fn to_array(self) -> [i16; 16] {
819 cast(self)
820 }
821
822 #[inline]
823 pub fn as_array(&self) -> &[i16; 16] {
824 cast_ref(self)
825 }
826
827 #[inline]
828 pub fn as_mut_array(&mut self) -> &mut [i16; 16] {
829 cast_mut(self)
830 }
831}