Skip to main content

wide/
i32x4_.rs

1use super::*;
2
3pick! {
4  if #[cfg(target_feature="sse2")] {
5    #[derive(Default, Clone, Copy, PartialEq, Eq)]
6    #[repr(C, align(16))]
7    pub struct i32x4 { pub(crate) sse: m128i }
8  } else if #[cfg(target_feature="simd128")] {
9    use core::arch::wasm32::*;
10
11    #[derive(Clone, Copy)]
12    #[repr(transparent)]
13    pub struct i32x4 { pub(crate) simd: v128 }
14
15    impl Default for i32x4 {
16      fn default() -> Self {
17        Self::splat(0)
18      }
19    }
20
21    impl PartialEq for i32x4 {
22      fn eq(&self, other: &Self) -> bool {
23        u32x4_all_true(i32x4_eq(self.simd, other.simd))
24      }
25    }
26
27    impl Eq for i32x4 { }
28  } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
29    use core::arch::aarch64::*;
30    #[repr(C)]
31    #[derive(Copy, Clone)]
32    pub struct i32x4 { pub(crate) neon : int32x4_t }
33
34    impl Default for i32x4 {
35      #[inline]
36      fn default() -> Self {
37        Self::splat(0)
38      }
39    }
40
41    impl PartialEq for i32x4 {
42      #[inline]
43      fn eq(&self, other: &Self) -> bool {
44        unsafe { vminvq_u32(vceqq_s32(self.neon, other.neon))==u32::MAX }
45      }
46    }
47
48    impl Eq for i32x4 { }
49  } else {
50    #[derive(Default, Clone, Copy, PartialEq, Eq)]
51    #[repr(C, align(16))]
52    pub struct i32x4 { pub(crate) arr: [i32;4] }
53  }
54}
55
56int_uint_consts!(i32, 4, i32x4, 128);
57
58unsafe impl Zeroable for i32x4 {}
59unsafe impl Pod for i32x4 {}
60
61impl AlignTo for i32x4 {
62  type Elem = i32;
63}
64
65impl Add for i32x4 {
66  type Output = Self;
67  #[inline]
68  fn add(self, rhs: Self) -> Self::Output {
69    pick! {
70      if #[cfg(target_feature="sse2")] {
71        Self { sse: add_i32_m128i(self.sse, rhs.sse) }
72      } else if #[cfg(target_feature="simd128")] {
73        Self { simd: i32x4_add(self.simd, rhs.simd) }
74      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
75        unsafe { Self { neon: vaddq_s32(self.neon, rhs.neon) } }
76      } else {
77        Self { arr: [
78          self.arr[0].wrapping_add(rhs.arr[0]),
79          self.arr[1].wrapping_add(rhs.arr[1]),
80          self.arr[2].wrapping_add(rhs.arr[2]),
81          self.arr[3].wrapping_add(rhs.arr[3]),
82        ]}
83      }
84    }
85  }
86}
87
88impl Sub for i32x4 {
89  type Output = Self;
90  #[inline]
91  fn sub(self, rhs: Self) -> Self::Output {
92    pick! {
93      if #[cfg(target_feature="sse2")] {
94        Self { sse: sub_i32_m128i(self.sse, rhs.sse) }
95      } else if #[cfg(target_feature="simd128")] {
96        Self { simd: i32x4_sub(self.simd, rhs.simd) }
97      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
98        unsafe {Self { neon: vsubq_s32(self.neon, rhs.neon) }}
99      } else {
100        Self { arr: [
101          self.arr[0].wrapping_sub(rhs.arr[0]),
102          self.arr[1].wrapping_sub(rhs.arr[1]),
103          self.arr[2].wrapping_sub(rhs.arr[2]),
104          self.arr[3].wrapping_sub(rhs.arr[3]),
105        ]}
106      }
107    }
108  }
109}
110
111impl Mul for i32x4 {
112  type Output = Self;
113  #[inline]
114  fn mul(self, rhs: Self) -> Self::Output {
115    pick! {
116      if #[cfg(target_feature="sse4.1")] {
117        Self { sse: mul_32_m128i(self.sse, rhs.sse) }
118      } else if #[cfg(target_feature="simd128")] {
119        Self { simd: i32x4_mul(self.simd, rhs.simd) }
120      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
121        unsafe {Self { neon: vmulq_s32(self.neon, rhs.neon) }}
122      } else {
123        let arr1: [i32; 4] = cast(self);
124        let arr2: [i32; 4] = cast(rhs);
125        cast([
126          arr1[0].wrapping_mul(arr2[0]),
127          arr1[1].wrapping_mul(arr2[1]),
128          arr1[2].wrapping_mul(arr2[2]),
129          arr1[3].wrapping_mul(arr2[3]),
130        ])
131      }
132    }
133  }
134}
135
136integer_impl_div_rem!(i32, i32x4, [0, 1, 2, 3]);
137
138impl Add<i32> for i32x4 {
139  type Output = Self;
140  #[inline]
141  fn add(self, rhs: i32) -> Self::Output {
142    self.add(Self::splat(rhs))
143  }
144}
145
146impl Sub<i32> for i32x4 {
147  type Output = Self;
148  #[inline]
149  fn sub(self, rhs: i32) -> Self::Output {
150    self.sub(Self::splat(rhs))
151  }
152}
153
154impl Mul<i32> for i32x4 {
155  type Output = Self;
156  #[inline]
157  fn mul(self, rhs: i32) -> Self::Output {
158    self.mul(Self::splat(rhs))
159  }
160}
161
162impl Add<i32x4> for i32 {
163  type Output = i32x4;
164  #[inline]
165  fn add(self, rhs: i32x4) -> Self::Output {
166    i32x4::splat(self).add(rhs)
167  }
168}
169
170impl Sub<i32x4> for i32 {
171  type Output = i32x4;
172  #[inline]
173  fn sub(self, rhs: i32x4) -> Self::Output {
174    i32x4::splat(self).sub(rhs)
175  }
176}
177
178impl Mul<i32x4> for i32 {
179  type Output = i32x4;
180  #[inline]
181  fn mul(self, rhs: i32x4) -> Self::Output {
182    i32x4::splat(self).mul(rhs)
183  }
184}
185
186impl BitAnd for i32x4 {
187  type Output = Self;
188  #[inline]
189  fn bitand(self, rhs: Self) -> Self::Output {
190    pick! {
191      if #[cfg(target_feature="sse2")] {
192        Self { sse: bitand_m128i(self.sse, rhs.sse) }
193      } else if #[cfg(target_feature="simd128")] {
194        Self { simd: v128_and(self.simd, rhs.simd) }
195      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
196        unsafe {Self { neon: vandq_s32(self.neon, rhs.neon) }}
197      } else {
198        Self { arr: [
199          self.arr[0].bitand(rhs.arr[0]),
200          self.arr[1].bitand(rhs.arr[1]),
201          self.arr[2].bitand(rhs.arr[2]),
202          self.arr[3].bitand(rhs.arr[3]),
203        ]}
204      }
205    }
206  }
207}
208
209impl BitOr for i32x4 {
210  type Output = Self;
211  #[inline]
212  fn bitor(self, rhs: Self) -> Self::Output {
213    pick! {
214      if #[cfg(target_feature="sse2")] {
215        Self { sse: bitor_m128i(self.sse, rhs.sse) }
216      } else if #[cfg(target_feature="simd128")] {
217        Self { simd: v128_or(self.simd, rhs.simd) }
218      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
219        unsafe {Self { neon: vorrq_s32(self.neon, rhs.neon) }}
220      } else {
221        Self { arr: [
222          self.arr[0].bitor(rhs.arr[0]),
223          self.arr[1].bitor(rhs.arr[1]),
224          self.arr[2].bitor(rhs.arr[2]),
225          self.arr[3].bitor(rhs.arr[3]),
226        ]}
227      }
228    }
229  }
230}
231
232impl BitXor for i32x4 {
233  type Output = Self;
234  #[inline]
235  fn bitxor(self, rhs: Self) -> Self::Output {
236    pick! {
237      if #[cfg(target_feature="sse2")] {
238        Self { sse: bitxor_m128i(self.sse, rhs.sse) }
239      } else if #[cfg(target_feature="simd128")] {
240        Self { simd: v128_xor(self.simd, rhs.simd) }
241      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
242        unsafe {Self { neon: veorq_s32(self.neon, rhs.neon) }}
243      } else {
244        Self { arr: [
245          self.arr[0].bitxor(rhs.arr[0]),
246          self.arr[1].bitxor(rhs.arr[1]),
247          self.arr[2].bitxor(rhs.arr[2]),
248          self.arr[3].bitxor(rhs.arr[3]),
249        ]}
250      }
251    }
252  }
253}
254
255macro_rules! impl_shl_t_for_i32x4 {
256  ($($shift_type:ty),+ $(,)?) => {
257    $(impl Shl<$shift_type> for i32x4 {
258      type Output = Self;
259      /// Shifts all lanes by the value given.
260      #[inline]
261      fn shl(self, rhs: $shift_type) -> Self::Output {
262        pick! {
263          if #[cfg(target_feature="sse2")] {
264            let shift = cast([rhs as u64, 0]);
265            Self { sse: shl_all_u32_m128i(self.sse, shift) }
266          } else if #[cfg(target_feature="simd128")] {
267            Self { simd: i32x4_shl(self.simd, rhs as u32) }
268          } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
269            unsafe {Self { neon: vshlq_s32(self.neon, vmovq_n_s32(rhs as i32)) }}
270          } else {
271            let u = rhs as u32;
272            Self { arr: [
273              self.arr[0].wrapping_shl(u),
274              self.arr[1].wrapping_shl(u),
275              self.arr[2].wrapping_shl(u),
276              self.arr[3].wrapping_shl(u),
277            ]}
278          }
279        }
280      }
281    })+
282  };
283}
284impl_shl_t_for_i32x4!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
285
286macro_rules! impl_shr_t_for_i32x4 {
287  ($($shift_type:ty),+ $(,)?) => {
288    $(impl Shr<$shift_type> for i32x4 {
289      type Output = Self;
290      /// Shifts all lanes by the value given.
291      #[inline]
292      fn shr(self, rhs: $shift_type) -> Self::Output {
293        pick! {
294          if #[cfg(target_feature="sse2")] {
295            let shift = cast([rhs as u64, 0]);
296            Self { sse: shr_all_i32_m128i(self.sse, shift) }
297          } else if #[cfg(target_feature="simd128")] {
298            Self { simd: i32x4_shr(self.simd, rhs as u32) }
299          } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
300            unsafe {Self { neon: vshlq_s32(self.neon, vmovq_n_s32( -(rhs as i32))) }}
301          } else {
302            let u = rhs as u32;
303            Self { arr: [
304              self.arr[0].wrapping_shr(u),
305              self.arr[1].wrapping_shr(u),
306              self.arr[2].wrapping_shr(u),
307              self.arr[3].wrapping_shr(u),
308            ]}
309          }
310        }
311      }
312    })+
313  };
314}
315impl_shr_t_for_i32x4!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
316
317/// Shifts lanes by the corresponding lane.
318///
319/// Bitwise shift-right; yields `self >> mask(rhs)`, where mask removes any
320/// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
321/// of the type. (same as `wrapping_shr`)
322impl Shr<i32x4> for i32x4 {
323  type Output = Self;
324
325  #[inline]
326  fn shr(self, rhs: i32x4) -> Self::Output {
327    pick! {
328      if #[cfg(target_feature="avx2")] {
329        // mask the shift count to 31 to have same behavior on all platforms
330        let shift_by = bitand_m128i(rhs.sse, set_splat_i32_m128i(31));
331        Self { sse: shr_each_i32_m128i(self.sse, shift_by) }
332      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
333        unsafe {
334          // mask the shift count to 31 to have same behavior on all platforms
335          // no right shift, have to pass negative value to left shift on neon
336          let shift_by = vnegq_s32(vandq_s32(rhs.neon, vmovq_n_s32(31)));
337          Self { neon: vshlq_s32(self.neon, shift_by) }
338        }
339      } else {
340        let arr: [i32; 4] = cast(self);
341        let rhs: [i32; 4] = cast(rhs);
342        cast([
343          arr[0].wrapping_shr(rhs[0] as u32),
344          arr[1].wrapping_shr(rhs[1] as u32),
345          arr[2].wrapping_shr(rhs[2] as u32),
346          arr[3].wrapping_shr(rhs[3] as u32),
347        ])
348      }
349    }
350  }
351}
352
353/// Shifts lanes by the corresponding lane.
354///
355/// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
356/// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
357/// of the type. (same as `wrapping_shl`)
358impl Shl<i32x4> for i32x4 {
359  type Output = Self;
360
361  #[inline]
362  fn shl(self, rhs: i32x4) -> Self::Output {
363    pick! {
364      if #[cfg(target_feature="avx2")] {
365        // mask the shift count to 31 to have same behavior on all platforms
366        let shift_by = bitand_m128i(rhs.sse, set_splat_i32_m128i(31));
367        Self { sse: shl_each_u32_m128i(self.sse, shift_by) }
368      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
369        unsafe {
370          // mask the shift count to 31 to have same behavior on all platforms
371          let shift_by = vandq_s32(rhs.neon, vmovq_n_s32(31));
372          Self { neon: vshlq_s32(self.neon, shift_by) }
373        }
374      } else {
375        let arr: [i32; 4] = cast(self);
376        let rhs: [i32; 4] = cast(rhs);
377        cast([
378          arr[0].wrapping_shl(rhs[0] as u32),
379          arr[1].wrapping_shl(rhs[1] as u32),
380          arr[2].wrapping_shl(rhs[2] as u32),
381          arr[3].wrapping_shl(rhs[3] as u32),
382        ])
383      }
384    }
385  }
386}
387
388#[expect(deprecated)]
389impl CmpEq for i32x4 {
390  type Output = Self;
391  #[inline]
392  fn simd_eq(self, rhs: Self) -> Self::Output {
393    pick! {
394      if #[cfg(target_feature="sse2")] {
395        Self { sse: cmp_eq_mask_i32_m128i(self.sse, rhs.sse) }
396      } else if #[cfg(target_feature="simd128")] {
397        Self { simd: i32x4_eq(self.simd, rhs.simd) }
398      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
399        unsafe {Self { neon: vreinterpretq_s32_u32(vceqq_s32(self.neon, rhs.neon)) }}
400      } else {
401        Self { arr: [
402          if self.arr[0] == rhs.arr[0] { -1 } else { 0 },
403          if self.arr[1] == rhs.arr[1] { -1 } else { 0 },
404          if self.arr[2] == rhs.arr[2] { -1 } else { 0 },
405          if self.arr[3] == rhs.arr[3] { -1 } else { 0 },
406        ]}
407      }
408    }
409  }
410}
411
412#[expect(deprecated)]
413impl CmpGt for i32x4 {
414  type Output = Self;
415  #[inline]
416  fn simd_gt(self, rhs: Self) -> Self::Output {
417    pick! {
418      if #[cfg(target_feature="sse2")] {
419        Self { sse: cmp_gt_mask_i32_m128i(self.sse, rhs.sse) }
420      } else if #[cfg(target_feature="simd128")] {
421        Self { simd: i32x4_gt(self.simd, rhs.simd) }
422      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
423        unsafe {Self { neon: vreinterpretq_s32_u32(vcgtq_s32(self.neon, rhs.neon)) }}
424      } else {
425        Self { arr: [
426          if self.arr[0] > rhs.arr[0] { -1 } else { 0 },
427          if self.arr[1] > rhs.arr[1] { -1 } else { 0 },
428          if self.arr[2] > rhs.arr[2] { -1 } else { 0 },
429          if self.arr[3] > rhs.arr[3] { -1 } else { 0 },
430        ]}
431      }
432    }
433  }
434}
435
436#[expect(deprecated)]
437impl CmpLt for i32x4 {
438  type Output = Self;
439  #[inline]
440  fn simd_lt(self, rhs: Self) -> Self::Output {
441    pick! {
442      if #[cfg(target_feature="sse2")] {
443        Self { sse: cmp_lt_mask_i32_m128i(self.sse, rhs.sse) }
444      } else if #[cfg(target_feature="simd128")] {
445        Self { simd: i32x4_lt(self.simd, rhs.simd) }
446      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
447        unsafe {Self { neon: vreinterpretq_s32_u32(vcltq_s32(self.neon, rhs.neon)) }}
448      } else {
449        Self { arr: [
450          if self.arr[0] < rhs.arr[0] { -1 } else { 0 },
451          if self.arr[1] < rhs.arr[1] { -1 } else { 0 },
452          if self.arr[2] < rhs.arr[2] { -1 } else { 0 },
453          if self.arr[3] < rhs.arr[3] { -1 } else { 0 },
454        ]}
455      }
456    }
457  }
458}
459
460#[expect(deprecated)]
461impl CmpNe for i32x4 {
462  type Output = Self;
463  #[inline]
464  fn simd_ne(self, rhs: Self) -> Self::Output {
465    pick! {
466      if #[cfg(target_feature="sse2")] {
467        !self.simd_eq(rhs)
468      } else if #[cfg(target_feature="simd128")] {
469        Self { simd: i32x4_ne(self.simd, rhs.simd) }
470      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
471        !self.simd_eq(rhs)
472      } else {
473        Self { arr: [
474          if self.arr[0] != rhs.arr[0] { -1 } else { 0 },
475          if self.arr[1] != rhs.arr[1] { -1 } else { 0 },
476          if self.arr[2] != rhs.arr[2] { -1 } else { 0 },
477          if self.arr[3] != rhs.arr[3] { -1 } else { 0 },
478        ]}
479      }
480    }
481  }
482}
483
484#[expect(deprecated)]
485impl CmpLe for i32x4 {
486  type Output = Self;
487  #[inline]
488  fn simd_le(self, rhs: Self) -> Self::Output {
489    pick! {
490      if #[cfg(target_feature="sse2")] {
491        !self.simd_gt(rhs)
492      } else if #[cfg(target_feature="simd128")] {
493        Self { simd: i32x4_le(self.simd, rhs.simd) }
494      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
495        !self.simd_gt(rhs)
496      } else {
497        Self { arr: [
498          if self.arr[0] <= rhs.arr[0] { -1 } else { 0 },
499          if self.arr[1] <= rhs.arr[1] { -1 } else { 0 },
500          if self.arr[2] <= rhs.arr[2] { -1 } else { 0 },
501          if self.arr[3] <= rhs.arr[3] { -1 } else { 0 },
502        ]}
503      }
504    }
505  }
506}
507
508#[expect(deprecated)]
509impl CmpGe for i32x4 {
510  type Output = Self;
511  #[inline]
512  fn simd_ge(self, rhs: Self) -> Self::Output {
513    pick! {
514      if #[cfg(target_feature="sse2")] {
515        !self.simd_lt(rhs)
516      } else if #[cfg(target_feature="simd128")] {
517        Self { simd: i32x4_ge(self.simd, rhs.simd) }
518      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
519        !self.simd_lt(rhs)
520      } else {
521        Self { arr: [
522          if self.arr[0] >= rhs.arr[0] { -1 } else { 0 },
523          if self.arr[1] >= rhs.arr[1] { -1 } else { 0 },
524          if self.arr[2] >= rhs.arr[2] { -1 } else { 0 },
525          if self.arr[3] >= rhs.arr[3] { -1 } else { 0 },
526        ]}
527      }
528    }
529  }
530}
531
532impl i32x4 {
533  #[inline]
534  #[must_use]
535  pub const fn new(array: [i32; 4]) -> Self {
536    unsafe { core::mem::transmute(array) }
537  }
538
539  simd_comparison_fns!();
540
541  #[inline]
542  #[must_use]
543  pub fn blend(self, t: Self, f: Self) -> Self {
544    pick! {
545      if #[cfg(target_feature="sse4.1")] {
546        Self { sse: blend_varying_i8_m128i(f.sse, t.sse, self.sse) }
547      } else if #[cfg(target_feature="simd128")] {
548        Self { simd: v128_bitselect(t.simd, f.simd, self.simd) }
549      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
550        unsafe {Self { neon: vbslq_s32(vreinterpretq_u32_s32(self.neon), t.neon, f.neon) }}
551      } else {
552        generic_bit_blend(self, t, f)
553      }
554    }
555  }
556
557  /// Returns true for each positive element and false if it is zero or
558  /// negative.
559  #[inline]
560  #[must_use]
561  pub fn is_positive(self) -> Self {
562    pick! {
563      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
564        Self { neon: unsafe { vreinterpretq_s32_u32(vcgtzq_s32(self.neon)) } }
565      } else {
566        self.simd_gt(Self::ZERO)
567      }
568    }
569  }
570
571  /// Returns true for each negative element and false if it is zero or
572  /// positive.
573  #[inline]
574  #[must_use]
575  pub fn is_negative(self) -> Self {
576    pick! {
577      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
578        Self { neon: unsafe { vreinterpretq_s32_u32(vcltzq_s32(self.neon)) } }
579      } else {
580        self.simd_lt(Self::ZERO)
581      }
582    }
583  }
584
585  /// Multiplies corresponding 32 bit lanes and returns the 64 bit result
586  /// on the corresponding lanes.
587  ///
588  /// Effectively does two multiplies on 128 bit platforms, but is easier
589  /// to use than wrapping `mul_widen_i32_odd_m128i` individually.
590  #[inline]
591  #[must_use]
592  pub fn mul_widen(self, rhs: Self) -> i64x4 {
593    pick! {
594      if #[cfg(target_feature="avx2")] {
595        let a = convert_to_i64_m256i_from_i32_m128i(self.sse);
596        let b = convert_to_i64_m256i_from_i32_m128i(rhs.sse);
597        cast(mul_i64_low_bits_m256i(a, b))
598      } else if #[cfg(target_feature="sse4.1")] {
599          let evenp = mul_widen_i32_odd_m128i(self.sse, rhs.sse);
600
601          let oddp = mul_widen_i32_odd_m128i(
602            shr_imm_u64_m128i::<32>(self.sse),
603            shr_imm_u64_m128i::<32>(rhs.sse));
604
605          i64x4 {
606            a: i64x2 { sse: unpack_low_i64_m128i(evenp, oddp)},
607            b: i64x2 { sse: unpack_high_i64_m128i(evenp, oddp)}
608          }
609      } else if #[cfg(target_feature="simd128")] {
610          i64x4 {
611            a: i64x2 { simd: i64x2_extmul_low_i32x4(self.simd, rhs.simd) },
612            b: i64x2 { simd: i64x2_extmul_high_i32x4(self.simd, rhs.simd) },
613          }
614      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
615        unsafe {
616          i64x4 { a: i64x2 { neon: vmull_s32(vget_low_s32(self.neon), vget_low_s32(rhs.neon)) },
617                  b: i64x2 { neon: vmull_s32(vget_high_s32(self.neon), vget_high_s32(rhs.neon)) } }
618        }
619      } else {
620        let a: [i32; 4] = cast(self);
621        let b: [i32; 4] = cast(rhs);
622        cast([
623          i64::from(a[0]) * i64::from(b[0]),
624          i64::from(a[1]) * i64::from(b[1]),
625          i64::from(a[2]) * i64::from(b[2]),
626          i64::from(a[3]) * i64::from(b[3]),
627        ])
628      }
629    }
630  }
631
632  #[inline]
633  #[must_use]
634  pub fn abs(self) -> Self {
635    pick! {
636      if #[cfg(target_feature="ssse3")] {
637        Self { sse: abs_i32_m128i(self.sse) }
638      } else if #[cfg(target_feature="simd128")] {
639        Self { simd: i32x4_abs(self.simd) }
640      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
641        unsafe {Self { neon: vabsq_s32(self.neon) }}
642      } else {
643        let arr: [i32; 4] = cast(self);
644        cast([
645          arr[0].wrapping_abs(),
646          arr[1].wrapping_abs(),
647          arr[2].wrapping_abs(),
648          arr[3].wrapping_abs(),
649        ])
650      }
651    }
652  }
653
654  #[inline]
655  #[must_use]
656  pub fn unsigned_abs(self) -> u32x4 {
657    pick! {
658      if #[cfg(target_feature="ssse3")] {
659        u32x4 { sse: abs_i32_m128i(self.sse) }
660      } else if #[cfg(target_feature="simd128")] {
661        u32x4 { simd: i32x4_abs(self.simd) }
662      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
663        unsafe {u32x4 { neon: vreinterpretq_u32_s32(vabsq_s32(self.neon)) }}
664      } else {
665        let arr: [i32; 4] = cast(self);
666        cast([
667          arr[0].unsigned_abs(),
668          arr[1].unsigned_abs(),
669          arr[2].unsigned_abs(),
670          arr[3].unsigned_abs(),
671        ])
672      }
673    }
674  }
675
676  signed_fn_signum!();
677
678  /// horizontal add of all the elements of the vector
679  #[inline]
680  #[must_use]
681  pub fn reduce_add(self) -> i32 {
682    pick! {
683      if #[cfg(target_feature="sse2")] {
684        let hi64  = unpack_high_i64_m128i(self.sse, self.sse);
685        let sum64 = add_i32_m128i(hi64, self.sse);
686        let hi32  = shuffle_ai_f32_all_m128i::<0b10_11_00_01>(sum64);    // Swap the low two elements
687        let sum32 = add_i32_m128i(sum64, hi32);
688        get_i32_from_m128i_s(sum32)
689      } else {
690        let arr: [i32; 4] = cast(self);
691        arr[0].wrapping_add(arr[1]).wrapping_add(
692        arr[2].wrapping_add(arr[3]))
693      }
694    }
695  }
696
697  /// horizontal max of all the elements of the vector
698  #[inline]
699  #[must_use]
700  pub fn reduce_max(self) -> i32 {
701    let arr: [i32; 4] = cast(self);
702    arr[0].max(arr[1]).max(arr[2].max(arr[3]))
703  }
704
705  /// horizontal min of all the elements of the vector
706  #[inline]
707  #[must_use]
708  pub fn reduce_min(self) -> i32 {
709    let arr: [i32; 4] = cast(self);
710    arr[0].min(arr[1]).min(arr[2].min(arr[3]))
711  }
712
713  #[inline]
714  #[must_use]
715  pub fn max(self, rhs: Self) -> Self {
716    pick! {
717      if #[cfg(target_feature="sse4.1")] {
718        Self { sse: max_i32_m128i(self.sse, rhs.sse) }
719      } else if #[cfg(target_feature="simd128")] {
720        Self { simd: i32x4_max(self.simd, rhs.simd) }
721      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
722        unsafe {Self { neon: vmaxq_s32(self.neon, rhs.neon) }}
723      } else {
724        self.simd_lt(rhs).blend(rhs, self)
725      }
726    }
727  }
728  #[inline]
729  #[must_use]
730  pub fn min(self, rhs: Self) -> Self {
731    pick! {
732      if #[cfg(target_feature="sse4.1")] {
733        Self { sse: min_i32_m128i(self.sse, rhs.sse) }
734      } else if #[cfg(target_feature="simd128")] {
735        Self { simd: i32x4_min(self.simd, rhs.simd) }
736      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
737        unsafe {Self { neon: vminq_s32(self.neon, rhs.neon) }}
738      } else {
739        self.simd_lt(rhs).blend(self, rhs)
740      }
741    }
742  }
743
744  integer_fn_clamp!();
745
746  #[inline]
747  #[must_use]
748  pub fn saturating_add(self, rhs: Self) -> Self {
749    pick! {
750      if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
751        let result = self + rhs;
752        let overflow = (!(self ^ rhs) & (self ^ result)).is_negative();
753        let negative = self.is_negative();
754
755        overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
756      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
757        unsafe { Self { neon: vqaddq_s32(self.neon, rhs.neon) } }
758      } else {
759        Self {
760          arr: [
761            self.arr[0].saturating_add(rhs.arr[0]),
762            self.arr[1].saturating_add(rhs.arr[1]),
763            self.arr[2].saturating_add(rhs.arr[2]),
764            self.arr[3].saturating_add(rhs.arr[3]),
765          ],
766        }
767      }
768    }
769  }
770
771  #[inline]
772  #[must_use]
773  pub fn saturating_sub(self, rhs: Self) -> Self {
774    pick! {
775      if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
776        let result = self - rhs;
777        let overflow = ((self ^ rhs) & (self ^ result)).is_negative();
778        let negative = self.is_negative();
779
780        overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
781      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
782        unsafe { Self { neon: vqsubq_s32(self.neon, rhs.neon) } }
783      } else {
784        Self {
785          arr: [
786            self.arr[0].saturating_sub(rhs.arr[0]),
787            self.arr[1].saturating_sub(rhs.arr[1]),
788            self.arr[2].saturating_sub(rhs.arr[2]),
789            self.arr[3].saturating_sub(rhs.arr[3]),
790          ],
791        }
792      }
793    }
794  }
795
796  /// Lanewise saturating multiply.
797  #[inline]
798  #[must_use]
799  pub fn saturating_mul(self, rhs: Self) -> Self {
800    pick! {
801      if #[cfg(target_feature="sse4.1")] {
802        let even_wide_mul = mul_widen_i32_odd_m128i(self.sse, rhs.sse);
803        let odd_wide_mul = mul_widen_i32_odd_m128i(
804          shuffle_ai_f32_all_m128i::<0b_00_11_00_01>(self.sse),
805          shuffle_ai_f32_all_m128i::<0b_00_11_00_01>(rhs.sse),
806        );
807
808        let ll_hh_1 = unpack_low_i32_m128i(even_wide_mul, odd_wide_mul);
809        let ll_hh_2 = unpack_high_i32_m128i(even_wide_mul, odd_wide_mul);
810        let low = Self { sse: unpack_low_i64_m128i(ll_hh_1, ll_hh_2) };
811        let high = Self { sse: unpack_high_i64_m128i(ll_hh_1, ll_hh_2) };
812
813        let no_overflow = high.simd_eq(low.is_negative());
814        let limit = Self::MAX ^ (self ^ rhs).is_negative();
815        no_overflow.blend(low, limit)
816      } else if #[cfg(target_feature="simd128")] {
817        let low_wide_mul = i64x2_extmul_low_i32x4(self.simd, rhs.simd);
818        let high_wide_mul = i64x2_extmul_high_i32x4(self.simd, rhs.simd);
819        let low = Self { simd: i32x4_shuffle::<0, 2, 4, 6>(low_wide_mul, high_wide_mul) };
820        let high = Self { simd: i32x4_shuffle::<1, 3, 5, 7>(low_wide_mul, high_wide_mul) };
821
822        let no_overflow = high.simd_eq(low.is_negative());
823        let limit = Self::MAX ^ (self ^ rhs).is_negative();
824        no_overflow.blend(low, limit)
825      } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
826        unsafe {
827          let low_wide_mul = vreinterpretq_s32_s64(
828            vmull_s32(vget_low_s32(self.neon), vget_low_s32(rhs.neon)),
829          );
830          let high_wide_mul = vreinterpretq_s32_s64(
831            vmull_s32(vget_high_s32(self.neon), vget_high_s32(rhs.neon)),
832          );
833          let low_high = vuzpq_s32(low_wide_mul, high_wide_mul);
834          let low = Self { neon: low_high.0 };
835          let high = Self { neon: low_high.1 };
836
837          let no_overflow = high.simd_eq(low.is_negative());
838          let limit = Self::MAX ^ (self ^ rhs).is_negative();
839          no_overflow.blend(low, limit)
840        }
841      } else {
842        let self_array = self.to_array();
843        let rhs_array = rhs.to_array();
844
845        Self::new([
846          self_array[0].saturating_mul(rhs_array[0]),
847          self_array[1].saturating_mul(rhs_array[1]),
848          self_array[2].saturating_mul(rhs_array[2]),
849          self_array[3].saturating_mul(rhs_array[3]),
850        ])
851      }
852    }
853  }
854
855  integer_fn_saturating_div!([0, 1, 2, 3]);
856
857  #[inline]
858  #[must_use]
859  pub fn round_float(self) -> f32x4 {
860    pick! {
861      if #[cfg(target_feature="sse2")] {
862        cast(convert_to_m128_from_i32_m128i(self.sse))
863      } else if #[cfg(target_feature="simd128")] {
864        cast(Self { simd: f32x4_convert_i32x4(self.simd) })
865      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
866        cast(unsafe {Self { neon: vreinterpretq_s32_f32(vcvtq_f32_s32(self.neon)) }})
867      } else {
868        let arr: [i32; 4] = cast(self);
869        cast([
870          arr[0] as f32,
871          arr[1] as f32,
872          arr[2] as f32,
873          arr[3] as f32,
874        ])
875      }
876    }
877  }
878
879  #[inline]
880  #[must_use]
881  #[doc(alias("movemask", "move_mask"))]
882  pub fn to_bitmask(self) -> u32 {
883    pick! {
884      if #[cfg(target_feature="sse2")] {
885        // use f32 move_mask since it is the same size as i32
886        move_mask_m128(cast(self.sse)) as u32
887      } else if #[cfg(target_feature="simd128")] {
888        u32x4_bitmask(self.simd) as u32
889      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
890        unsafe
891        {
892          // set all to 1 if top bit is set, else 0
893          let masked = vcltq_s32(self.neon, vdupq_n_s32(0));
894
895          // select the right bit out of each lane
896          let selectbit : uint32x4_t = core::mem::transmute([1u32, 2, 4, 8]);
897          let r = vandq_u32(masked, selectbit);
898
899          // horizontally add the 32-bit lanes
900          vaddvq_u32(r) as u32
901         }
902      } else {
903        ((self.arr[0] < 0) as u32) << 0 |
904        ((self.arr[1] < 0) as u32) << 1 |
905        ((self.arr[2] < 0) as u32) << 2 |
906        ((self.arr[3] < 0) as u32) << 3
907      }
908    }
909  }
910
911  #[inline]
912  #[must_use]
913  pub fn any(self) -> bool {
914    pick! {
915      if #[cfg(target_feature="sse2")] {
916        // use f32 move_mask since it is the same size as i32
917        move_mask_m128(cast(self.sse)) != 0
918      } else if #[cfg(target_feature="simd128")] {
919        u32x4_bitmask(self.simd) != 0
920      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
921        // some lanes are negative
922        unsafe {
923          vminvq_s32(self.neon) < 0
924        }
925      } else {
926        let v : [u64;2] = cast(self);
927        ((v[0] | v[1]) & 0x8000000080000000) != 0
928      }
929    }
930  }
931
932  #[inline]
933  #[must_use]
934  pub fn all(self) -> bool {
935    pick! {
936      if #[cfg(target_feature="sse2")] {
937        // use f32 move_mask since it is the same size as i32
938        move_mask_m128(cast(self.sse)) == 0b1111
939      } else if #[cfg(target_feature="simd128")] {
940        u32x4_bitmask(self.simd) == 0b1111
941      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
942        // all lanes are negative
943        unsafe {
944          vmaxvq_s32(self.neon) < 0
945        }
946      } else {
947        let v : [u64;2] = cast(self);
948        (v[0] & v[1] & 0x8000000080000000) == 0x8000000080000000
949      }
950    }
951  }
952
953  #[inline]
954  #[must_use]
955  pub fn none(self) -> bool {
956    !self.any()
957  }
958
959  /// Transpose matrix of 4x4 `i32` matrix. Currently only accelerated on SSE.
960  #[must_use]
961  #[inline]
962  pub fn transpose(data: [i32x4; 4]) -> [i32x4; 4] {
963    pick! {
964      if #[cfg(target_feature="sse")] {
965        let mut e0 = data[0];
966        let mut e1 = data[1];
967        let mut e2 = data[2];
968        let mut e3 = data[3];
969
970        transpose_four_m128(
971          cast_mut(&mut e0.sse),
972          cast_mut(&mut e1.sse),
973          cast_mut(&mut e2.sse),
974          cast_mut(&mut e3.sse),
975        );
976
977        [e0, e1, e2, e3]
978      } else {
979        #[inline(always)]
980        fn transpose_column(data: &[i32x4; 4], index: usize) -> i32x4 {
981          i32x4::new([
982            data[0].as_array()[index],
983            data[1].as_array()[index],
984            data[2].as_array()[index],
985            data[3].as_array()[index],
986          ])
987        }
988
989        [
990          transpose_column(&data, 0),
991          transpose_column(&data, 1),
992          transpose_column(&data, 2),
993          transpose_column(&data, 3),
994        ]
995      }
996    }
997  }
998
999  #[inline]
1000  pub fn to_array(self) -> [i32; 4] {
1001    cast(self)
1002  }
1003
1004  #[inline]
1005  pub fn as_array(&self) -> &[i32; 4] {
1006    cast_ref(self)
1007  }
1008
1009  #[inline]
1010  pub fn as_mut_array(&mut self) -> &mut [i32; 4] {
1011    cast_mut(self)
1012  }
1013}