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 u16x32 { pub(crate) avx512: m512i }
14 } else {
15 #[derive(Default, Clone, Copy, PartialEq, Eq)]
22 #[repr(C, align(64))]
23 pub struct u16x32 { pub(crate) a : u16x16, pub(crate) b : u16x16 }
24 }
25}
26
27impl_simd_uint! {
28 unsafe {
29 T = u16,
30 N = 32,
31 Simd = u16x32,
32 IntSimd = i16x32,
33 T_BITS = 16,
34 T_BITS_MUL_2 = 32,
35 [
36 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
37 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
38 ],
39 optional_type_x86_inner { X86Inner = __m512i },
40 optional_type_arm_inner {},
41 optional_type_wasm_inner {},
42 }
43
44 #[inline]
45 fn not(self) -> Self::Output {
46 pick! {
47 if #[cfg(target_feature="avx512bw")] {
48 Self { avx512: bitxor_m512i(self.avx512, set_splat_i16_m512i(-1)) }
49 } else {
50 Self {
51 a : self.a.not(),
52 b : self.b.not(),
53 }
54 }
55 }
56 }
57
58 #[inline]
59 fn add(self, rhs: Self) -> Self::Output {
60 pick! {
61 if #[cfg(target_feature="avx512bw")] {
62 Self { avx512: add_i16_m512i(self.avx512, rhs.avx512) }
63 } else {
64 Self {
65 a : self.a.add(rhs.a),
66 b : self.b.add(rhs.b),
67 }
68 }
69 }
70 }
71
72 #[inline]
73 fn sub(self, rhs: Self) -> Self::Output {
74 pick! {
75 if #[cfg(target_feature="avx512bw")] {
76 Self { avx512: sub_i16_m512i(self.avx512, rhs.avx512) }
77 } else {
78 Self {
79 a : self.a.sub(rhs.a),
80 b : self.b.sub(rhs.b),
81 }
82 }
83 }
84 }
85
86 #[inline]
87 fn mul(self, rhs: Self) -> Self::Output {
88 pick! {
89 if #[cfg(target_feature="avx512bw")] {
90 Self { avx512: mul_i16_keep_low_m512i(self.avx512, rhs.avx512) }
91 } else {
92 Self { a: self.a.mul(rhs.a), b: self.b.mul(rhs.b) }
93 }
94 }
95 }
96
97 #[inline]
98 fn bitand(self, rhs: Self) -> Self::Output {
99 pick! {
100 if #[cfg(target_feature="avx512bw")] {
101 Self { avx512: bitand_m512i(self.avx512, rhs.avx512) }
102 } else {
103 Self {
104 a : self.a.bitand(rhs.a),
105 b : self.b.bitand(rhs.b),
106 }
107 }
108 }
109 }
110
111 #[inline]
112 fn bitor(self, rhs: Self) -> Self::Output {
113 pick! {
114 if #[cfg(target_feature="avx512bw")] {
115 Self { avx512: bitor_m512i(self.avx512, rhs.avx512) }
116 } else {
117 Self {
118 a : self.a.bitor(rhs.a),
119 b : self.b.bitor(rhs.b),
120 }
121 }
122 }
123 }
124
125 #[inline]
126 fn bitxor(self, rhs: Self) -> Self::Output {
127 pick! {
128 if #[cfg(target_feature="avx512bw")] {
129 Self { avx512: bitxor_m512i(self.avx512, rhs.avx512) }
130 } else {
131 Self {
132 a : self.a.bitxor(rhs.a),
133 b : self.b.bitxor(rhs.b),
134 }
135 }
136 }
137 }
138
139 #[inline]
140 fn simd_eq(self, rhs: Self) -> Self::Output {
141 pick! {
142 if #[cfg(target_feature="avx512bw")] {
143 Self { avx512: cmp_op_mask_u16_m512i::<{cmp_int_op!(Eq)}>(self.avx512, rhs.avx512) }
144 } else {
145 Self {
146 a : self.a.simd_eq(rhs.a),
147 b : self.b.simd_eq(rhs.b),
148 }
149 }
150 }
151 }
152
153 #[inline]
154 fn simd_ne(self, rhs: Self) -> Self::Output {
155 pick! {
156 if #[cfg(target_feature="avx512bw")] {
157 Self { avx512: cmp_op_mask_u16_m512i::<{cmp_int_op!(Ne)}>(self.avx512, rhs.avx512) }
158 } else {
159 Self {
160 a : self.a.simd_ne(rhs.a),
161 b : self.b.simd_ne(rhs.b),
162 }
163 }
164 }
165 }
166
167 #[inline]
168 fn simd_lt(self, rhs: Self) -> Self::Output {
169 pick! {
170 if #[cfg(target_feature="avx512bw")] {
171 Self { avx512: cmp_op_mask_u16_m512i::<{cmp_int_op!(Lt)}>(self.avx512, rhs.avx512) }
172 } else {
173 Self {
174 a : rhs.a.simd_gt(self.a),
175 b : rhs.b.simd_gt(self.b),
176 }
177 }
178 }
179 }
180
181 #[inline]
182 fn simd_gt(self, rhs: Self) -> Self::Output {
183 pick! {
184 if #[cfg(target_feature="avx512bw")] {
185 Self { avx512: cmp_op_mask_u16_m512i::<{cmp_int_op!(Nle)}>(self.avx512, rhs.avx512) }
186 } else {
187 Self {
188 a : self.a.simd_gt(rhs.a),
189 b : self.b.simd_gt(rhs.b),
190 }
191 }
192 }
193 }
194
195 #[inline]
196 fn simd_le(self, rhs: Self) -> Self::Output {
197 pick! {
198 if #[cfg(target_feature="avx512bw")] {
199 Self { avx512: cmp_op_mask_u16_m512i::<{cmp_int_op!(Le)}>(self.avx512, rhs.avx512) }
200 } else {
201 Self {
202 a : self.a.simd_le(rhs.a),
203 b : self.b.simd_le(rhs.b),
204 }
205 }
206 }
207 }
208
209 #[inline]
210 fn simd_ge(self, rhs: Self) -> Self::Output {
211 pick! {
212 if #[cfg(target_feature="avx512bw")] {
213 Self { avx512: cmp_op_mask_u16_m512i::<{cmp_int_op!(Nlt)}>(self.avx512, rhs.avx512) }
214 } else {
215 Self {
216 a : self.a.simd_ge(rhs.a),
217 b : self.b.simd_ge(rhs.b),
218 }
219 }
220 }
221 }
222
223 #[inline]
224 pub fn reduce_add(self) -> u16 {
225 let array: [u16x16; 2] = cast(self);
226 (array[0] + array[1]).reduce_add()
227 }
228
229 #[inline]
230 pub fn reduce_mul(self) -> u16 {
231 let array: [u16x16; 2] = cast(self);
232 (array[0] * array[1]).reduce_mul()
233 }
234
235 #[inline]
236 pub fn bitselect(self, if_one: Self, if_zero: Self) -> Self {
237 pick! {
238 if #[cfg(target_feature="avx512bw")] {
239 Self {
240 avx512: bitor_m512i(
241 bitand_m512i(if_one.avx512, self.avx512),
242 bitandnot_m512i(self.avx512, if_zero.avx512),
243 ),
244 }
245 } else {
246 Self {
247 a: self.a.bitselect(if_one.a, if_zero.a),
248 b: self.b.bitselect(if_one.b, if_zero.b),
249 }
250 }
251 }
252 }
253
254 #[inline]
255 fn select(self, if_true: Self, if_false: Self) -> Self {
256 pick! {
257 if #[cfg(target_feature="avx512bw")] {
258 Self { avx512: blend_varying_i8_m512i(if_false.avx512,if_true.avx512,movepi8_mask_m512i(self.avx512)) }
259 } else {
260 Self {
261 a : self.a.select(if_true.a, if_false.a),
262 b : self.b.select(if_true.b, if_false.b),
263 }
264 }
265 }
266 }
267
268 #[inline]
269 pub fn to_bitmask(self) -> u32 {
270 pick! {
271 if #[cfg(target_feature="avx512bw")] {
272 movepi16_mask_m512i(self.avx512) as u32
274 } else {
275 self.a.to_bitmask() | (self.b.to_bitmask() << 16)
276 }
277 }
278 }
279
280 #[inline]
281 pub fn any(self) -> bool {
282 pick! {
283 if #[cfg(target_feature="avx512bw")] {
284 movepi16_mask_m512i(self.avx512) != 0
285 } else {
286 (self.a | self.b).any()
287 }
288 }
289 }
290
291 #[inline]
292 pub fn all(self) -> bool {
293 pick! {
294 if #[cfg(target_feature="avx512bw")] {
295 movepi16_mask_m512i(self.avx512) == 0xFFFFFFFF
296 } else {
297 (self.a & self.b).all()
298 }
299 }
300 }
301
302 #[inline]
303 pub fn shuffle(self, indices: u16x32) -> Self {
304 pick! {
305 if #[cfg(all(target_feature = "avx512bw"))] {
306 #[cfg(target_arch = "x86")]
307 use core::arch::x86::_mm512_permutexvar_epi16;
308 #[cfg(target_arch = "x86_64")]
309 use core::arch::x86_64::_mm512_permutexvar_epi16;
310 Self { avx512: unsafe { m512i(_mm512_permutexvar_epi16(indices.avx512.0, self.avx512.0)) } }
312 } else {
313 let self_halfs = cast::<u16x32, [u16x16; 2]>(self);
314 let [indices_a, indices_b] = cast::<u16x32, [u16x16; 2]>(indices);
315
316 cast([self_halfs.shuffle(indices_a), self_halfs.shuffle(indices_b)])
317 }
318 }
319 }
320
321 #[inline]
322 pub fn shuffle_zeroing(self, indices: u16x32) -> Self {
323 pick! {
324 if #[cfg(all(target_feature = "avx512bw"))] {
325 self.shuffle(indices) & indices.simd_lt(32)
326 } else {
327 let self_halfs = cast::<u16x32, [u16x16; 2]>(self);
328 let [indices_a, indices_b] = cast::<u16x32, [u16x16; 2]>(indices);
329
330 cast([self_halfs.shuffle_zeroing(indices_a), self_halfs.shuffle_zeroing(indices_b)])
331 }
332 }
333 }
334
335 #[inline]
336 pub fn shuffle_wrapping(self, indices: u16x32) -> Self {
337 pick! {
338 if #[cfg(all(target_feature = "avx512bw"))] {
339 self.shuffle(indices)
341 } else {
342 let self_halfs = cast::<u16x32, [u16x16; 2]>(self);
343 let [indices_a, indices_b] = cast::<u16x32, [u16x16; 2]>(indices);
344
345 cast([self_halfs.shuffle_wrapping(indices_a), self_halfs.shuffle_wrapping(indices_b)])
346 }
347 }
348 }
349
350 #[inline]
351 fn shuffle(self: [u16x32; 2], indices: u16x32) -> u16x32 {
352 pick! {
353 if #[cfg(all(target_feature = "avx512bw"))] {
354 #[cfg(target_arch = "x86")]
355 use core::arch::x86::_mm512_permutex2var_epi16;
356 #[cfg(target_arch = "x86_64")]
357 use core::arch::x86_64::_mm512_permutex2var_epi16;
358 u16x32 {
360 avx512: unsafe {
361 m512i(_mm512_permutex2var_epi16(self[0].avx512.0, indices.avx512.0, self[1].avx512.0))
362 },
363 }
364 } else {
365 self[0].shuffle_zeroing(indices) | self[1].shuffle_zeroing(indices - 32)
366 }
367 }
368 }
369
370 #[inline]
371 fn shuffle_zeroing(self: [u16x32; 2], indices: u16x32) -> u16x32 {
372 pick! {
373 if #[cfg(all(target_feature = "avx512bw", target_feature = "avx512vl"))] {
374 self.shuffle(indices) & indices.simd_lt(64)
375 } else {
376 self.shuffle(indices)
377 }
378 }
379 }
380
381 #[inline]
382 fn shuffle_wrapping(self: [u16x32; 2], indices: u16x32) -> u16x32 {
383 pick! {
384 if #[cfg(all(target_feature = "avx512bw"))] {
385 self.shuffle(indices)
387 } else {
388 self.shuffle(indices & 63)
389 }
390 }
391 }
392
393 #[inline]
394 fn shuffle(self: [u16x32; 3], indices: u16x32) -> u16x32 {
395 [self[0], self[1]].shuffle_zeroing(indices) | self[2].shuffle_zeroing(indices - 64)
396 }
397
398 #[inline]
399 fn shuffle_zeroing(self: [u16x32; 3], indices: u16x32) -> u16x32 {
400 self.shuffle(indices)
401 }
402
403 #[inline]
404 fn shuffle_wrapping(self: [u16x32; 3], indices: u16x32) -> u16x32 {
405 self.shuffle(indices % 96)
406 }
407
408 #[inline]
409 fn shuffle(self: [u16x32; 4], indices: u16x32) -> u16x32 {
410 [self[0], self[1]].shuffle_zeroing(indices) | [self[2], self[3]].shuffle_zeroing(indices - 64)
411 }
412
413 #[inline]
414 fn shuffle_zeroing(self: [u16x32; 4], indices: u16x32) -> u16x32 {
415 self.shuffle(indices)
416 }
417
418 #[inline]
419 fn shuffle_wrapping(self: [u16x32; 4], indices: u16x32) -> u16x32 {
420 self.shuffle(indices & 127)
421 }
422
423 #[inline]
426 pub fn transpose(data: [Self; 32]) -> [Self; 32] {
427 #[inline(always)]
430 fn transpose_column(data: &[u16x32; 32], index: usize) -> u16x32 {
431 u16x32::new([
432 data[0].as_array()[index],
433 data[1].as_array()[index],
434 data[2].as_array()[index],
435 data[3].as_array()[index],
436 data[4].as_array()[index],
437 data[5].as_array()[index],
438 data[6].as_array()[index],
439 data[7].as_array()[index],
440 data[8].as_array()[index],
441 data[9].as_array()[index],
442 data[10].as_array()[index],
443 data[11].as_array()[index],
444 data[12].as_array()[index],
445 data[13].as_array()[index],
446 data[14].as_array()[index],
447 data[15].as_array()[index],
448 data[16].as_array()[index],
449 data[17].as_array()[index],
450 data[18].as_array()[index],
451 data[19].as_array()[index],
452 data[20].as_array()[index],
453 data[21].as_array()[index],
454 data[22].as_array()[index],
455 data[23].as_array()[index],
456 data[24].as_array()[index],
457 data[25].as_array()[index],
458 data[26].as_array()[index],
459 data[27].as_array()[index],
460 data[28].as_array()[index],
461 data[29].as_array()[index],
462 data[30].as_array()[index],
463 data[31].as_array()[index],
464 ])
465 }
466
467 [
468 transpose_column(&data, 0),
469 transpose_column(&data, 1),
470 transpose_column(&data, 2),
471 transpose_column(&data, 3),
472 transpose_column(&data, 4),
473 transpose_column(&data, 5),
474 transpose_column(&data, 6),
475 transpose_column(&data, 7),
476 transpose_column(&data, 8),
477 transpose_column(&data, 9),
478 transpose_column(&data, 10),
479 transpose_column(&data, 11),
480 transpose_column(&data, 12),
481 transpose_column(&data, 13),
482 transpose_column(&data, 14),
483 transpose_column(&data, 15),
484 transpose_column(&data, 16),
485 transpose_column(&data, 17),
486 transpose_column(&data, 18),
487 transpose_column(&data, 19),
488 transpose_column(&data, 20),
489 transpose_column(&data, 21),
490 transpose_column(&data, 22),
491 transpose_column(&data, 23),
492 transpose_column(&data, 24),
493 transpose_column(&data, 25),
494 transpose_column(&data, 26),
495 transpose_column(&data, 27),
496 transpose_column(&data, 28),
497 transpose_column(&data, 29),
498 transpose_column(&data, 30),
499 transpose_column(&data, 31),
500 ]
501 }
502
503 #[inline]
504 fn shl(self, rhs: Self) -> Self::Output {
505 pick! {
506 if #[cfg(target_feature="avx512bw")] {
507 let rhs = bitand_m512i(rhs.avx512, set_splat_i16_m512i(15));
509 Self { avx512: shl_each_u16_m512i(self.avx512, rhs) }
510 } else {
511 let [self_a, self_b]: [u16x16; 2] = cast(self);
512 let [rhs_a, rhs_b]: [u16x16; 2] = cast(rhs);
513
514 cast([self_a << rhs_a, self_b << rhs_b])
515 }
516 }
517 }
518
519 #[inline]
520 fn shl(self, rhs: u32) -> Self::Output {
521 pick! {
522 if #[cfg(target_feature="avx512bw")] {
523 #[expect(clippy::suspicious_arithmetic_impl)]
525 let shift = rhs as u16 & 15;
526 Self { avx512: shl_all_u16_m512i(self.avx512, shift) }
527 } else {
528 Self {
529 a : self.a.shl(rhs),
530 b : self.b.shl(rhs),
531 }
532 }
533 }
534 }
535
536 #[inline]
537 fn shr(self, rhs: Self) -> Self::Output {
538 pick! {
539 if #[cfg(target_feature="avx512bw")] {
540 let rhs = bitand_m512i(rhs.avx512, set_splat_i16_m512i(15));
542 Self { avx512: shr_each_u16_m512i(self.avx512, rhs) }
543 } else {
544 let [self_a, self_b]: [u16x16; 2] = cast(self);
545 let [rhs_a, rhs_b]: [u16x16; 2] = cast(rhs);
546
547 cast([self_a >> rhs_a, self_b >> rhs_b])
548 }
549 }
550 }
551
552 #[inline]
553 fn shr(self, rhs: u32) -> Self::Output {
554 pick! {
555 if #[cfg(target_feature="avx512bw")] {
556 #[expect(clippy::suspicious_arithmetic_impl)]
558 let shift = rhs as u16 & 15;
559 Self { avx512: shr_all_u16_m512i(self.avx512, shift) }
560 } else {
561 Self {
562 a : self.a.shr(rhs),
563 b : self.b.shr(rhs),
564 }
565 }
566 }
567 }
568
569 #[inline]
570 pub fn max(self, rhs: Self) -> Self {
571 pick! {
572 if #[cfg(target_feature="avx512bw")] {
573 Self { avx512: max_u16_m512i(self.avx512, rhs.avx512) }
574 } else {
575 Self {
576 a: self.a.max(rhs.a),
577 b: self.b.max(rhs.b),
578 }
579 }
580 }
581 }
582
583 #[inline]
584 pub fn min(self, rhs: Self) -> Self {
585 pick! {
586 if #[cfg(target_feature="avx512bw")] {
587 Self { avx512: min_u16_m512i(self.avx512, rhs.avx512) }
588 } else {
589 Self {
590 a: self.a.min(rhs.a),
591 b: self.b.min(rhs.b),
592 }
593 }
594 }
595 }
596
597 #[inline]
598 pub fn reduce_max(self) -> u16 {
599 let array: [u16x16; 2] = cast(self);
600 array[0].max(array[1]).reduce_max()
601 }
602
603 #[inline]
604 pub fn reduce_min(self) -> u16 {
605 let array: [u16x16; 2] = cast(self);
606 array[0].min(array[1]).reduce_min()
607 }
608
609 #[inline]
610 pub fn unbounded_shl(self, rhs: Self) -> Self {
611 pick! {
612 if #[cfg(target_feature="avx512bw")] {
613 Self { avx512: shl_each_u16_m512i(self.avx512, rhs.avx512) }
614 } else {
615 let [self_a, self_b] = cast::<u16x32, [u16x16; 2]>(self);
616 let [rhs_a, rhs_b] = cast::<u16x32, [u16x16; 2]>(rhs);
617
618 cast([self_a.unbounded_shl(rhs_a), self_b.unbounded_shl(rhs_b)])
619 }
620 }
621 }
622
623 #[inline]
624 pub fn unbounded_shl_scalar(self, rhs: u32) -> Self {
625 pick! {
626 if #[cfg(target_feature="avx512bw")] {
627 Self { avx512: shl_all_u16_m512i(self.avx512, rhs.min(u16::MAX as u32) as u16) }
630 } else {
631 Self {
632 a: self.a.unbounded_shl_scalar(rhs),
633 b: self.b.unbounded_shl_scalar(rhs),
634 }
635 }
636 }
637 }
638
639 #[inline]
640 pub fn unbounded_shr(self, rhs: Self) -> Self {
641 pick! {
642 if #[cfg(target_feature="avx512bw")] {
643 Self { avx512: shr_each_u16_m512i(self.avx512, rhs.avx512) }
644 } else {
645 let [self_a, self_b] = cast::<u16x32, [u16x16; 2]>(self);
646 let [rhs_a, rhs_b] = cast::<u16x32, [u16x16; 2]>(rhs);
647
648 cast([self_a.unbounded_shr(rhs_a), self_b.unbounded_shr(rhs_b)])
649 }
650 }
651 }
652
653 #[inline]
654 pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
655 pick! {
656 if #[cfg(target_feature="avx512bw")] {
657 Self { avx512: shr_all_u16_m512i(self.avx512, rhs.min(u16::MAX as u32) as u16) }
660 } else {
661 Self {
662 a: self.a.unbounded_shr_scalar(rhs),
663 b: self.b.unbounded_shr_scalar(rhs),
664 }
665 }
666 }
667 }
668
669 #[inline]
670 pub fn saturating_add(self, rhs: Self) -> Self {
671 pick! {
672 if #[cfg(target_feature="avx512bw")] {
673 Self { avx512: add_saturating_u16_m512i(self.avx512, rhs.avx512) }
674 } else {
675 Self {
676 a: self.a.saturating_add(rhs.a),
677 b: self.b.saturating_add(rhs.b),
678 }
679 }
680 }
681 }
682
683 #[inline]
684 pub fn saturating_sub(self, rhs: Self) -> Self {
685 pick! {
686 if #[cfg(target_feature="avx512bw")] {
687 Self { avx512: sub_saturating_u16_m512i(self.avx512, rhs.avx512) }
688 } else {
689 Self {
690 a: self.a.saturating_sub(rhs.a),
691 b: self.b.saturating_sub(rhs.b),
692 }
693 }
694 }
695 }
696
697 #[inline]
698 pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
699 let (low, high) = self.mul_keep_low_high(rhs);
700 let overflow = high.simd_ne(Self::ZERO);
701 (low, overflow)
702 }
703
704 optional_fn_widening_mul {
705 }
707
708 #[inline]
709 pub fn mul_keep_low_high(self, rhs: Self) -> (Self, Self) {
710 let [self_a, self_b] = cast::<u16x32, [u16x16; 2]>(self);
714 let [rhs_a, rhs_b] = cast::<u16x32, [u16x16; 2]>(rhs);
715
716 let result_a = self_a.mul_keep_low_high(rhs_a);
717 let result_b = self_b.mul_keep_low_high(rhs_b);
718 (cast([result_a.0, result_b.0]), cast([result_a.1, result_b.1]))
719 }
720
721 #[inline]
722 pub fn mul_keep_high(self, rhs: Self) -> Self {
723 let [self_a, self_b] = cast::<u16x32, [u16x16; 2]>(self);
727 let [rhs_a, rhs_b] = cast::<u16x32, [u16x16; 2]>(rhs);
728
729 cast([self_a.mul_keep_high(rhs_a), self_b.mul_keep_high(rhs_b)])
730 }
731
732 optional_fn_deserialize {}
733}