1use super::*;
2
3pick! {
4 if #[cfg(target_feature="avx512f")] {
5 #[derive(Default, Clone, Copy, PartialEq, Eq)]
12 #[repr(C, align(64))]
13 pub struct i32x16 { pub(crate) avx512: m512i }
14 } else {
15 #[derive(Default, Clone, Copy, PartialEq, Eq)]
22 #[repr(C, align(64))]
23 pub struct i32x16 { pub(crate) a : i32x8, pub(crate) b : i32x8 }
24 }
25}
26
27impl_simd_int! {
28 unsafe {
29 T = i32,
30 N = 16,
31 Simd = i32x16,
32 UintSimd = u32x16,
33 T_BITS = 32,
34 T_BITS_MUL_2 = 64,
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 = __m512i },
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="avx512f")] {
46 Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Lt)}>(self.avx512, rhs.avx512) }
47 } else {
48 Self {
49 a : rhs.a.simd_gt(self.a),
50 b : rhs.b.simd_gt(self.b),
51 }
52 }
53 }
54 }
55
56 #[inline]
57 fn simd_gt(self, rhs: Self) -> Self::Output {
58 pick! {
59 if #[cfg(target_feature="avx512f")] {
60 Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Nle)}>(self.avx512, rhs.avx512) }
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="avx512f")] {
74 Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Le)}>(self.avx512, rhs.avx512) }
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="avx512f")] {
88 Self { avx512: cmp_op_mask_i32_m512i::<{cmp_int_op!(Nlt)}>(self.avx512, rhs.avx512) }
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: u32x16) -> Self::Output {
100 pick! {
101 if #[cfg(target_feature="avx512f")] {
102 #[cfg(target_arch = "x86")]
103 use core::arch::x86::_mm512_srav_epi32;
104 #[cfg(target_arch = "x86_64")]
105 use core::arch::x86_64::_mm512_srav_epi32;
106
107 let rhs = bitand_m512i(rhs.avx512, set_splat_i32_m512i(31));
109 Self { avx512: m512i(unsafe { _mm512_srav_epi32(self.avx512.0, rhs.0) }) }
111 } else {
112 Self {
113 a: self.a >> rhs.a,
114 b: self.b >> rhs.b,
115 }
116 }
117 }
118 }
119
120 #[inline]
121 fn shr(self, rhs: u32) -> Self::Output {
122 pick! {
123 if #[cfg(target_feature="avx512f")] {
124 #[expect(clippy::suspicious_arithmetic_impl)]
126 let shift = rhs & 31;
127 Self { avx512: shr_all_i32_m512i(self.avx512, 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="avx512f")] {
141 Self { avx512: max_i32_m512i(self.avx512, rhs.avx512) }
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="avx512f")] {
155 Self { avx512: min_i32_m512i(self.avx512, rhs.avx512) }
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) -> i32 {
167 let arr: [i32x8; 2] = cast(self);
168 arr[0].max(arr[1]).reduce_max()
169 }
170
171 #[inline]
172 pub fn reduce_min(self) -> i32 {
173 let arr: [i32x8; 2] = cast(self);
174 arr[0].min(arr[1]).reduce_min()
175 }
176
177 #[inline]
178 pub fn unbounded_shr(self, rhs: u32x16) -> Self {
179 pick! {
180 if #[cfg(target_feature="avx512f")] {
181 #[cfg(target_arch = "x86")]
182 use core::arch::x86::_mm512_srav_epi32;
183 #[cfg(target_arch = "x86_64")]
184 use core::arch::x86_64::_mm512_srav_epi32;
185
186 Self { avx512: m512i(unsafe { _mm512_srav_epi32(self.avx512.0, rhs.avx512.0) }) }
188 } else {
189 Self {
190 a: self.a.unbounded_shr(rhs.a),
191 b: self.b.unbounded_shr(rhs.b),
192 }
193 }
194 }
195 }
196
197 #[inline]
198 pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
199 pick! {
200 if #[cfg(target_feature="avx512f")] {
201 Self { avx512: shr_all_i32_m512i(self.avx512, rhs) }
202 } else {
203 Self {
204 a: self.a.unbounded_shr_scalar(rhs),
205 b: self.b.unbounded_shr_scalar(rhs),
206 }
207 }
208 }
209 }
210
211 #[inline]
212 pub fn saturating_add(self, rhs: Self) -> Self {
213 pick! {
214 if #[cfg(target_feature="avx512f")] {
215 let result = self + rhs;
216 let overflow = (!(self ^ rhs) & (self ^ result)).is_negative();
217 let negative = self.is_negative();
218
219 overflow.select(Self::MAX ^ negative, result)
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="avx512f")] {
234 let result = self - rhs;
235 let overflow = ((self ^ rhs) & (self ^ result)).is_negative();
236 let negative = self.is_negative();
237
238 overflow.select(Self::MAX ^ negative, result)
240 } else {
241 Self {
242 a: self.a.saturating_sub(rhs.a),
243 b: self.b.saturating_sub(rhs.b),
244 }
245 }
246 }
247 }
248
249 #[inline]
250 pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
251 let (low, high) = self.mul_keep_low_high(rhs);
252 let low = cast::<u32x16, i32x16>(low);
253
254 let overflow = high.simd_ne(low.is_negative());
255 (low, overflow)
256 }
257
258 optional_fn_widening_mul {
259 }
261
262 #[inline]
263 pub fn mul_keep_low_high(self, rhs: Self) -> (u32x16, i32x16) {
264 pick! {
265 if #[cfg(all(target_feature="avx512f", target_feature="avx512dq"))] {
266 #[cfg(target_arch = "x86")]
267 use core::arch::x86::{_mm512_unpackhi_epi64, _mm512_unpacklo_epi64};
268 #[cfg(target_arch = "x86_64")]
269 use core::arch::x86_64::{_mm512_unpackhi_epi64, _mm512_unpacklo_epi64};
270
271 let even_wide_mul = mul_i32_wide_m512i(self.avx512, rhs.avx512);
272 let odd_wide_mul = mul_i32_wide_m512i(
273 shuffle_i32_m512i::<0b_00_11_00_01>(self.avx512),
274 shuffle_i32_m512i::<0b_00_11_00_01>(rhs.avx512),
275 );
276 let ll_hh_1 = unpack_low_i32_m512i(even_wide_mul, odd_wide_mul);
277 let ll_hh_2 = unpack_high_i32_m512i(even_wide_mul, odd_wide_mul);
278 (
280 u32x16 {
281 avx512: m512i(unsafe { _mm512_unpacklo_epi64(ll_hh_1.0, ll_hh_2.0) }),
282 },
283 i32x16 {
284 avx512: m512i(unsafe { _mm512_unpackhi_epi64(ll_hh_1.0, ll_hh_2.0) }),
285 },
286 )
287 } else {
288 let [self_a, self_b] = cast::<i32x16, [i32x8; 2]>(self);
289 let [rhs_a, rhs_b] = cast::<i32x16, [i32x8; 2]>(rhs);
290
291 let result_a = self_a.mul_keep_low_high(rhs_a);
292 let result_b = self_b.mul_keep_low_high(rhs_b);
293 (
294 cast([result_a.0, result_b.0]),
295 cast([result_a.1, result_b.1]),
296 )
297 }
298 }
299 }
300
301 #[inline]
302 pub fn mul_keep_high(self, rhs: Self) -> Self {
303 pick! {
304 if #[cfg(all(target_feature="avx512f", target_feature="avx512dq"))] {
305 #[cfg(target_arch = "x86")]
306 use core::arch::x86::_mm512_unpackhi_epi64;
307 #[cfg(target_arch = "x86_64")]
308 use core::arch::x86_64::_mm512_unpackhi_epi64;
309
310 let even_wide_mul = mul_i32_wide_m512i(self.avx512, rhs.avx512);
311 let odd_wide_mul = mul_i32_wide_m512i(
312 shuffle_i32_m512i::<0b_00_11_00_01>(self.avx512),
313 shuffle_i32_m512i::<0b_00_11_00_01>(rhs.avx512),
314 );
315 let ll_hh_1 = unpack_low_i32_m512i(even_wide_mul, odd_wide_mul);
316 let ll_hh_2 = unpack_high_i32_m512i(even_wide_mul, odd_wide_mul);
317 Self {
319 avx512: m512i(unsafe { _mm512_unpackhi_epi64(ll_hh_1.0, ll_hh_2.0) }),
320 }
321 } else {
322 let [self_a, self_b] = cast::<i32x16, [i32x8; 2]>(self);
323 let [rhs_a, rhs_b] = cast::<i32x16, [i32x8; 2]>(rhs);
324
325 cast([self_a.mul_keep_high(rhs_a), self_b.mul_keep_high(rhs_b)])
326 }
327 }
328 }
329
330 #[inline]
331 pub fn abs(self) -> Self {
332 pick! {
333 if #[cfg(target_feature="avx512bw")] {
334 Self { avx512: abs_i32_m512i(self.avx512) }
335 } else {
336 Self {
337 a : self.a.abs(),
338 b : self.b.abs(),
339 }
340 }
341 }
342 }
343
344 #[inline]
345 pub fn is_positive(self) -> Self {
346 pick! {
347 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
348 Self {
350 a: self.a.is_positive(),
351 b: self.b.is_positive(),
352 }
353 } else {
354 self.simd_gt(Self::ZERO)
355 }
356 }
357 }
358
359 #[inline]
360 pub fn is_negative(self) -> Self {
361 pick! {
362 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
363 Self {
365 a: self.a.is_negative(),
366 b: self.b.is_negative(),
367 }
368 } else {
369 self.simd_lt(Self::ZERO)
370 }
371 }
372 }
373
374 optional_fn_deserialize {}
375}
376
377impl From<i16x16> for i32x16 {
378 #[inline]
379 fn from(value: i16x16) -> Self {
380 i32x16::from_i16x16(value)
381 }
382}
383
384impl i32x16 {
387 #[inline]
389 #[must_use]
390 pub fn from_i16x16(v: i16x16) -> Self {
391 pick! {
392 if #[cfg(target_feature="avx512f")] {
393 Self { avx512: convert_to_i32_m512i_from_i16_m256i(v.avx2) }
394 } else {
395 let [a, b]: [i16x8; 2] = cast(v);
396 Self {
397 a: i32x8::from_i16x8(a),
398 b: i32x8::from_i16x8(b),
399 }
400 }
401 }
402 }
403
404 #[inline]
406 #[must_use]
407 pub fn round_float(self) -> f32x16 {
408 pick! {
409 if #[cfg(target_feature="avx512f")] {
410 cast(convert_to_m512_from_i32_m512i(self.avx512))
411 } else {
412 f32x16 {
413 a: self.a.round_float(),
414 b: self.b.round_float(),
415 }
416 }
417 }
418 }
419}