Skip to main content

wide/
i16x32_.rs

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