1use super::*;
2
3pick! {
4 if #[cfg(target_feature="avx512f")] {
5 #[derive(Default, Clone, Copy, PartialEq, Eq)]
6 #[repr(C, align(64))]
7 pub struct i32x16 { pub(crate) avx512: m512i }
8 } else {
9 #[derive(Default, Clone, Copy, PartialEq, Eq)]
10 #[repr(C, align(64))]
11 pub struct i32x16 { pub(crate) a : i32x8, pub(crate) b : i32x8 }
12 }
13}
14
15int_uint_consts!(i32, 16, i32x16, 512);
16
17unsafe impl Zeroable for i32x16 {}
18unsafe impl Pod for i32x16 {}
19
20impl AlignTo for i32x16 {
21 type Elem = i32;
22}
23
24impl Add for i32x16 {
25 type Output = Self;
26 #[inline]
27 fn add(self, rhs: Self) -> Self::Output {
28 pick! {
29 if #[cfg(target_feature="avx512f")] {
30 Self { avx512: add_i32_m512i(self.avx512, rhs.avx512) }
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 i32x16 {
42 type Output = Self;
43 #[inline]
44 fn sub(self, rhs: Self) -> Self::Output {
45 pick! {
46 if #[cfg(target_feature="avx512f")] {
47 Self { avx512: sub_i32_m512i(self.avx512, rhs.avx512) }
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 i32x16 {
59 type Output = Self;
60 #[inline]
61 fn mul(self, rhs: Self) -> Self::Output {
62 pick! {
63 if #[cfg(target_feature="avx512f")] {
64 Self { avx512: mul_i32_keep_low_m512i(self.avx512, rhs.avx512) }
65 } else {
66 Self { a: self.a.mul(rhs.a), b: self.b.mul(rhs.b) }
67 }
68 }
69 }
70}
71
72integer_impl_div_rem!(
73 i32,
74 i32x16,
75 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
76);
77
78impl Add<i32> for i32x16 {
79 type Output = Self;
80 #[inline]
81 fn add(self, rhs: i32) -> Self::Output {
82 self.add(Self::splat(rhs))
83 }
84}
85
86impl Sub<i32> for i32x16 {
87 type Output = Self;
88 #[inline]
89 fn sub(self, rhs: i32) -> Self::Output {
90 self.sub(Self::splat(rhs))
91 }
92}
93
94impl Mul<i32> for i32x16 {
95 type Output = Self;
96 #[inline]
97 fn mul(self, rhs: i32) -> Self::Output {
98 self.mul(Self::splat(rhs))
99 }
100}
101
102impl Add<i32x16> for i32 {
103 type Output = i32x16;
104 #[inline]
105 fn add(self, rhs: i32x16) -> Self::Output {
106 i32x16::splat(self).add(rhs)
107 }
108}
109
110impl Sub<i32x16> for i32 {
111 type Output = i32x16;
112 #[inline]
113 fn sub(self, rhs: i32x16) -> Self::Output {
114 i32x16::splat(self).sub(rhs)
115 }
116}
117
118impl Mul<i32x16> for i32 {
119 type Output = i32x16;
120 #[inline]
121 fn mul(self, rhs: i32x16) -> Self::Output {
122 i32x16::splat(self).mul(rhs)
123 }
124}
125
126impl BitAnd for i32x16 {
127 type Output = Self;
128 #[inline]
129 fn bitand(self, rhs: Self) -> Self::Output {
130 pick! {
131 if #[cfg(target_feature="avx512f")] {
132 Self { avx512: bitand_m512i(self.avx512, rhs.avx512) }
133 } else {
134 Self {
135 a : self.a.bitand(rhs.a),
136 b : self.b.bitand(rhs.b),
137 }
138 }
139 }
140 }
141}
142
143impl BitOr for i32x16 {
144 type Output = Self;
145 #[inline]
146 fn bitor(self, rhs: Self) -> Self::Output {
147 pick! {
148 if #[cfg(target_feature="avx512f")] {
149 Self { avx512: bitor_m512i(self.avx512, rhs.avx512) }
150 } else {
151 Self {
152 a : self.a.bitor(rhs.a),
153 b : self.b.bitor(rhs.b),
154 }
155 }
156 }
157 }
158}
159
160impl BitXor for i32x16 {
161 type Output = Self;
162 #[inline]
163 fn bitxor(self, rhs: Self) -> Self::Output {
164 pick! {
165 if #[cfg(target_feature="avx512f")] {
166 Self { avx512: bitxor_m512i(self.avx512, rhs.avx512) }
167 } else {
168 Self {
169 a : self.a.bitxor(rhs.a),
170 b : self.b.bitxor(rhs.b),
171 }
172 }
173 }
174 }
175}
176
177impl Shl for i32x16 {
178 type Output = Self;
179
180 #[inline]
186 fn shl(self, rhs: Self) -> Self::Output {
187 pick! {
188 if #[cfg(target_feature="avx512f")] {
189 let rhs = bitand_m512i(rhs.avx512, set_splat_i32_m512i(31));
191 Self { avx512: shl_each_u32_m512i(self.avx512, rhs) }
192 } else {
193 Self {
194 a: self.a.shl(rhs.a),
195 b: self.b.shl(rhs.b),
196 }
197 }
198 }
199 }
200}
201
202impl Shr for i32x16 {
203 type Output = Self;
204
205 #[inline]
211 fn shr(self, rhs: Self) -> Self::Output {
212 pick! {
213 if #[cfg(target_feature="avx512f")] {
214 #[cfg(target_arch = "x86")]
215 use core::arch::x86::_mm512_srav_epi32;
216 #[cfg(target_arch = "x86_64")]
217 use core::arch::x86_64::_mm512_srav_epi32;
218
219 let rhs = bitand_m512i(rhs.avx512, set_splat_i16_m512i(31));
221 Self { avx512: m512i(unsafe { _mm512_srav_epi32(self.avx512.0, rhs.0) }) }
223 } else {
224 Self {
225 a: self.a >> rhs.a,
226 b: self.b >> rhs.b,
227 }
228 }
229 }
230 }
231}
232
233macro_rules! impl_shl_t_for_i32x16 {
234 ($($shift_type:ty),+ $(,)?) => {
235 $(impl Shl<$shift_type> for i32x16 {
236 type Output = Self;
237 #[inline]
239 fn shl(self, rhs: $shift_type) -> Self::Output {
240 pick! {
241 if #[cfg(target_feature="avx512f")] {
242 let shift = cast(rhs as u32);
243 Self { avx512: shl_all_u32_m512i(self.avx512, shift) }
244 } else {
245 Self {
246 a : self.a.shl(rhs),
247 b : self.b.shl(rhs),
248 }
249 }
250 }
251 }
252 })+
253 };
254}
255impl_shl_t_for_i32x16!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
256
257macro_rules! impl_shr_t_for_i32x16 {
258 ($($shift_type:ty),+ $(,)?) => {
259 $(impl Shr<$shift_type> for i32x16 {
260 type Output = Self;
261 #[inline]
263 fn shr(self, rhs: $shift_type) -> Self::Output {
264 pick! {
265 if #[cfg(target_feature="avx512f")] {
266 let shift = cast(rhs as u32);
267 Self { avx512: shr_all_i32_m512i(self.avx512, shift) }
268 } else {
269 Self {
270 a : self.a.shr(rhs),
271 b : self.b.shr(rhs),
272 }
273 }
274 }
275 }
276 })+
277 };
278}
279impl_shr_t_for_i32x16!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
280
281#[expect(deprecated)]
282impl CmpEq for i32x16 {
283 type Output = Self;
284 #[inline]
285 fn simd_eq(self, rhs: Self) -> Self::Output {
286 pick! {
287 if #[cfg(target_feature="avx512f")] {
288 Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Eq)}>(self.avx512, rhs.avx512) }
289 } else {
290 Self {
291 a : self.a.simd_eq(rhs.a),
292 b : self.b.simd_eq(rhs.b),
293 }
294 }
295 }
296 }
297}
298
299#[expect(deprecated)]
300impl CmpLt for i32x16 {
301 type Output = Self;
302 #[inline]
303 fn simd_lt(self, rhs: Self) -> Self::Output {
304 pick! {
305 if #[cfg(target_feature="avx512f")] {
306 Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Lt)}>(self.avx512, rhs.avx512) }
307 } else {
308 Self {
309 a : rhs.a.simd_gt(self.a),
310 b : rhs.b.simd_gt(self.b),
311 }
312 }
313 }
314 }
315}
316
317#[expect(deprecated)]
318impl CmpGt for i32x16 {
319 type Output = Self;
320 #[inline]
321 fn simd_gt(self, rhs: Self) -> Self::Output {
322 pick! {
323 if #[cfg(target_feature="avx512f")] {
324 Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Nle)}>(self.avx512, rhs.avx512) }
325 } else {
326 Self {
327 a : self.a.simd_gt(rhs.a),
328 b : self.b.simd_gt(rhs.b),
329 }
330 }
331 }
332 }
333}
334
335#[expect(deprecated)]
336impl CmpNe for i32x16 {
337 type Output = Self;
338 #[inline]
339 fn simd_ne(self, rhs: Self) -> Self::Output {
340 pick! {
341 if #[cfg(target_feature="avx512f")] {
342 Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Ne)}>(self.avx512, rhs.avx512) }
343 } else {
344 Self {
345 a : self.a.simd_ne(rhs.a),
346 b : self.b.simd_ne(rhs.b),
347 }
348 }
349 }
350 }
351}
352
353#[expect(deprecated)]
354impl CmpLe for i32x16 {
355 type Output = Self;
356 #[inline]
357 fn simd_le(self, rhs: Self) -> Self::Output {
358 pick! {
359 if #[cfg(target_feature="avx512f")] {
360 Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Le)}>(self.avx512, rhs.avx512) }
361 } else {
362 Self {
363 a : self.a.simd_le(rhs.a),
364 b : self.b.simd_le(rhs.b),
365 }
366 }
367 }
368 }
369}
370
371#[expect(deprecated)]
372impl CmpGe for i32x16 {
373 type Output = Self;
374 #[inline]
375 fn simd_ge(self, rhs: Self) -> Self::Output {
376 pick! {
377 if #[cfg(target_feature="avx512f")] {
378 Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Nlt)}>(self.avx512, rhs.avx512) }
379 } else {
380 Self {
381 a : self.a.simd_ge(rhs.a),
382 b : self.b.simd_ge(rhs.b),
383 }
384 }
385 }
386 }
387}
388
389impl i32x16 {
390 #[inline]
391 #[must_use]
392 pub const fn new(array: [i32; 16]) -> Self {
393 unsafe { core::mem::transmute(array) }
394 }
395
396 simd_comparison_fns!();
397
398 #[inline]
399 #[must_use]
400 pub fn blend(self, t: Self, f: Self) -> Self {
401 pick! {
402 if #[cfg(target_feature="avx512f")] {
403 Self { avx512: blend_varying_i8_m512i(f.avx512,t.avx512,movepi8_mask_m512i(self.avx512)) }
404 } else {
405 Self {
406 a : self.a.blend(t.a, f.a),
407 b : self.b.blend(t.b, f.b),
408 }
409 }
410 }
411 }
412
413 #[inline]
416 #[must_use]
417 pub fn is_positive(self) -> Self {
418 pick! {
419 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
420 Self {
422 a: self.a.is_positive(),
423 b: self.b.is_positive(),
424 }
425 } else {
426 self.simd_gt(Self::ZERO)
427 }
428 }
429 }
430
431 #[inline]
434 #[must_use]
435 pub fn is_negative(self) -> Self {
436 pick! {
437 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
438 Self {
440 a: self.a.is_negative(),
441 b: self.b.is_negative(),
442 }
443 } else {
444 self.simd_lt(Self::ZERO)
445 }
446 }
447 }
448
449 #[inline]
450 #[must_use]
451 pub fn min(self, rhs: Self) -> Self {
452 pick! {
453 if #[cfg(target_feature="avx512f")] {
454 Self { avx512: min_i32_m512i(self.avx512, rhs.avx512) }
455 } else {
456 Self {
457 a: self.a.min(rhs.a),
458 b: self.b.min(rhs.b),
459 }
460 }
461 }
462 }
463
464 #[inline]
465 #[must_use]
466 pub fn max(self, rhs: Self) -> Self {
467 pick! {
468 if #[cfg(target_feature="avx512f")] {
469 Self { avx512: max_i32_m512i(self.avx512, rhs.avx512) }
470 } else {
471 Self {
472 a: self.a.max(rhs.a),
473 b: self.b.max(rhs.b),
474 }
475 }
476 }
477 }
478
479 integer_fn_clamp!();
480
481 #[inline]
482 #[must_use]
483 pub fn saturating_add(self, rhs: Self) -> Self {
484 pick! {
485 if #[cfg(target_feature="avx512f")] {
486 let result = self + rhs;
487 let overflow = (!(self ^ rhs) & (self ^ result)).is_negative();
488 let negative = self.is_negative();
489
490 overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
491 } else {
492 Self {
493 a: self.a.saturating_add(rhs.a),
494 b: self.b.saturating_add(rhs.b),
495 }
496 }
497 }
498 }
499
500 #[inline]
501 #[must_use]
502 pub fn saturating_sub(self, rhs: Self) -> Self {
503 pick! {
504 if #[cfg(target_feature="avx512f")] {
505 let result = self - rhs;
506 let overflow = ((self ^ rhs) & (self ^ result)).is_negative();
507 let negative = self.is_negative();
508
509 overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
510 } else {
511 Self {
512 a: self.a.saturating_sub(rhs.a),
513 b: self.b.saturating_sub(rhs.b),
514 }
515 }
516 }
517 }
518
519 #[inline]
521 #[must_use]
522 pub fn saturating_mul(self, rhs: Self) -> Self {
523 pick! {
524 if #[cfg(all(target_feature="avx512f", target_feature="avx512dq"))] {
525 #[cfg(target_arch = "x86")]
526 use core::arch::x86::{_mm512_unpackhi_epi64, _mm512_unpacklo_epi64};
527 #[cfg(target_arch = "x86_64")]
528 use core::arch::x86_64::{_mm512_unpackhi_epi64, _mm512_unpacklo_epi64};
529
530 let even_wide_mul = mul_i32_wide_m512i(self.avx512, rhs.avx512);
531 let odd_wide_mul = mul_i32_wide_m512i(
532 shuffle_i32_m512i::<0b_00_11_00_01>(self.avx512),
533 shuffle_i32_m512i::<0b_00_11_00_01>(rhs.avx512),
534 );
535
536 let ll_hh_1 = unpack_low_i32_m512i(even_wide_mul, odd_wide_mul);
537 let ll_hh_2 = unpack_high_i32_m512i(even_wide_mul, odd_wide_mul);
538 let low = Self {
540 avx512: m512i(unsafe { _mm512_unpacklo_epi64(ll_hh_1.0, ll_hh_2.0) }),
541 };
542 let high = Self {
543 avx512: m512i(unsafe { _mm512_unpackhi_epi64(ll_hh_1.0, ll_hh_2.0) }),
544 };
545
546 let no_overflow = high.simd_eq(low.is_negative());
547 let limit = Self::MAX ^ (self ^ rhs).is_negative();
548 no_overflow.blend(low, limit)
549 } else {
550 let [self_a, self_b]: [i32x8; 2] = cast(self);
551 let [rhs_a, rhs_b]: [i32x8; 2] = cast(rhs);
552
553 cast([self_a.saturating_mul(rhs_a), self_b.saturating_mul(rhs_b)])
554 }
555 }
556 }
557
558 integer_fn_saturating_div!([
559 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
560 ]);
561
562 #[inline]
564 #[must_use]
565 pub fn reduce_add(self) -> i32 {
566 let arr: [i32x8; 2] = cast(self);
567 (arr[0] + arr[1]).reduce_add()
568 }
569
570 #[inline]
572 #[must_use]
573 pub fn reduce_min(self) -> i32 {
574 let arr: [i32x8; 2] = cast(self);
575 arr[0].min(arr[1]).reduce_min()
576 }
577
578 #[inline]
580 #[must_use]
581 pub fn reduce_max(self) -> i32 {
582 let arr: [i32x8; 2] = cast(self);
583 arr[0].max(arr[1]).reduce_max()
584 }
585
586 #[inline]
587 #[must_use]
588 pub fn abs(self) -> Self {
589 pick! {
590 if #[cfg(target_feature="avx512bw")] {
591 Self { avx512: abs_i32_m512i(self.avx512) }
592 } else {
593 Self {
594 a : self.a.abs(),
595 b : self.b.abs(),
596 }
597 }
598 }
599 }
600
601 #[inline]
602 #[must_use]
603 pub fn unsigned_abs(self) -> u32x16 {
604 pick! {
605 if #[cfg(target_feature="avx512bw")] {
606 u32x16 { avx512: abs_i32_m512i(self.avx512) }
607 } else {
608 u32x16 {
609 a : self.a.unsigned_abs(),
610 b : self.b.unsigned_abs(),
611 }
612 }
613 }
614 }
615
616 signed_fn_signum!();
617
618 #[inline]
619 #[must_use]
620 #[doc(alias("movemask", "move_mask"))]
621 pub fn to_bitmask(self) -> u32 {
622 pick! {
623 if #[cfg(target_feature="avx512dq")] {
624 movepi32_mask_m512i(self.avx512) as u32
625 } else {
626 self.a.to_bitmask() | (self.b.to_bitmask() << 8)
627 }
628 }
629 }
630
631 #[inline]
632 #[must_use]
633 pub fn any(self) -> bool {
634 pick! {
635 if #[cfg(target_feature="avx512bw")] {
636 movepi32_mask_m512i(self.avx512) != 0
637 } else {
638 let [a, b]: [i32x8; 2] = cast(self);
639 (a | b).any()
640 }
641 }
642 }
643
644 #[inline]
645 #[must_use]
646 pub fn all(self) -> bool {
647 pick! {
648 if #[cfg(target_feature="avx512bw")] {
649 movepi32_mask_m512i(self.avx512) == 0xFFFF
650 } else {
651 let [a, b]: [i32x8; 2] = cast(self);
652 (a & b).all()
653 }
654 }
655 }
656
657 #[inline]
658 #[must_use]
659 pub fn none(self) -> bool {
660 !self.any()
661 }
662
663 #[must_use]
665 #[inline]
666 pub fn transpose(data: [i32x16; 16]) -> [i32x16; 16] {
667 #[inline(always)]
670 fn transpose_column(data: &[i32x16; 16], index: usize) -> i32x16 {
671 i32x16::new([
672 data[0].as_array()[index],
673 data[1].as_array()[index],
674 data[2].as_array()[index],
675 data[3].as_array()[index],
676 data[4].as_array()[index],
677 data[5].as_array()[index],
678 data[6].as_array()[index],
679 data[7].as_array()[index],
680 data[8].as_array()[index],
681 data[9].as_array()[index],
682 data[10].as_array()[index],
683 data[11].as_array()[index],
684 data[12].as_array()[index],
685 data[13].as_array()[index],
686 data[14].as_array()[index],
687 data[15].as_array()[index],
688 ])
689 }
690
691 [
692 transpose_column(&data, 0),
693 transpose_column(&data, 1),
694 transpose_column(&data, 2),
695 transpose_column(&data, 3),
696 transpose_column(&data, 4),
697 transpose_column(&data, 5),
698 transpose_column(&data, 6),
699 transpose_column(&data, 7),
700 transpose_column(&data, 8),
701 transpose_column(&data, 9),
702 transpose_column(&data, 10),
703 transpose_column(&data, 11),
704 transpose_column(&data, 12),
705 transpose_column(&data, 13),
706 transpose_column(&data, 14),
707 transpose_column(&data, 15),
708 ]
709 }
710
711 #[inline]
712 pub fn to_array(self) -> [i32; 16] {
713 cast(self)
714 }
715
716 #[inline]
717 pub fn as_array(&self) -> &[i32; 16] {
718 cast_ref(self)
719 }
720
721 #[inline]
722 pub fn as_mut_array(&mut self) -> &mut [i32; 16] {
723 cast_mut(self)
724 }
725
726 #[inline]
727 #[must_use]
728 pub fn round_float(self) -> f32x16 {
729 pick! {
730 if #[cfg(target_feature="avx512f")] {
731 cast(convert_to_m512_from_i32_m512i(self.avx512))
732 } else {
733 f32x16 {
734 a: self.a.round_float(),
735 b: self.b.round_float(),
736 }
737 }
738 }
739 }
740}
741
742impl Not for i32x16 {
743 type Output = Self;
744 #[inline]
745 fn not(self) -> Self::Output {
746 pick! {
747 if #[cfg(target_feature="avx512f")] {
748 Self { avx512: bitxor_m512i(self.avx512, set_splat_i32_m512i(-1)) }
749 } else {
750 Self {
751 a : self.a.not(),
752 b : self.b.not(),
753 }
754 }
755 }
756 }
757}