Skip to main content

wide/
u32x4_.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 u32x4 { 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 u32x4 { pub(crate) simd: v128 }
14
15    impl Default for u32x4 {
16      fn default() -> Self {
17        Self::splat(0)
18      }
19    }
20
21    impl PartialEq for u32x4 {
22      fn eq(&self, other: &Self) -> bool {
23        u32x4_all_true(u32x4_eq(self.simd, other.simd))
24      }
25    }
26
27    impl Eq for u32x4 { }
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 u32x4 { pub(crate) neon : uint32x4_t }
33
34    impl Default for u32x4 {
35      #[inline]
36      fn default() -> Self {
37        Self::splat(0)
38      }
39    }
40
41    impl PartialEq for u32x4 {
42      #[inline]
43      fn eq(&self, other: &Self) -> bool {
44        unsafe { vminvq_u32(vceqq_u32(self.neon, other.neon))==u32::MAX }
45      }
46    }
47
48    impl Eq for u32x4 { }
49} else {
50    #[derive(Default, Clone, Copy, PartialEq, Eq)]
51    #[repr(C, align(16))]
52    pub struct u32x4 { arr: [u32;4] }
53  }
54}
55
56int_uint_consts!(u32, 4, u32x4, 128);
57
58unsafe impl Zeroable for u32x4 {}
59unsafe impl Pod for u32x4 {}
60
61impl AlignTo for u32x4 {
62  type Elem = u32;
63}
64
65impl Add for u32x4 {
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: u32x4_add(self.simd, rhs.simd) }
74      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
75        unsafe { Self { neon: vaddq_u32(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 u32x4 {
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: u32x4_sub(self.simd, rhs.simd) }
97      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
98        unsafe {Self { neon: vsubq_u32(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 u32x4 {
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: u32x4_mul(self.simd, rhs.simd) }
120      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
121        unsafe {Self { neon: vmulq_u32(self.neon, rhs.neon) }}
122      } else {
123        let arr1: [u32; 4] = cast(self);
124        let arr2: [u32; 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!(u32, u32x4, [0, 1, 2, 3]);
137
138impl Add<u32> for u32x4 {
139  type Output = Self;
140  #[inline]
141  fn add(self, rhs: u32) -> Self::Output {
142    self.add(Self::splat(rhs))
143  }
144}
145
146impl Sub<u32> for u32x4 {
147  type Output = Self;
148  #[inline]
149  fn sub(self, rhs: u32) -> Self::Output {
150    self.sub(Self::splat(rhs))
151  }
152}
153
154impl Mul<u32> for u32x4 {
155  type Output = Self;
156  #[inline]
157  fn mul(self, rhs: u32) -> Self::Output {
158    self.mul(Self::splat(rhs))
159  }
160}
161
162impl Add<u32x4> for u32 {
163  type Output = u32x4;
164  #[inline]
165  fn add(self, rhs: u32x4) -> Self::Output {
166    u32x4::splat(self).add(rhs)
167  }
168}
169
170impl Sub<u32x4> for u32 {
171  type Output = u32x4;
172  #[inline]
173  fn sub(self, rhs: u32x4) -> Self::Output {
174    u32x4::splat(self).sub(rhs)
175  }
176}
177
178impl Mul<u32x4> for u32 {
179  type Output = u32x4;
180  #[inline]
181  fn mul(self, rhs: u32x4) -> Self::Output {
182    u32x4::splat(self).mul(rhs)
183  }
184}
185
186impl BitAnd for u32x4 {
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_u32(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 u32x4 {
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_u32(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 u32x4 {
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_u32(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_u32x4 {
256  ($($shift_type:ty),+ $(,)?) => {
257    $(impl Shl<$shift_type> for u32x4 {
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: u32x4_shl(self.simd, rhs as u32) }
268          } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
269            unsafe {Self { neon: vshlq_u32(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_u32x4!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
285
286macro_rules! impl_shr_t_for_u32x4 {
287  ($($shift_type:ty),+ $(,)?) => {
288    $(impl Shr<$shift_type> for u32x4 {
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_u32_m128i(self.sse, shift) }
297          } else if #[cfg(target_feature="simd128")] {
298            Self { simd: u32x4_shr(self.simd, rhs as u32) }
299          } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
300            unsafe {Self { neon: vshlq_u32(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_u32x4!(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<u32x4> for u32x4 {
323  type Output = Self;
324  #[inline]
325  fn shr(self, rhs: u32x4) -> Self::Output {
326    pick! {
327      if #[cfg(target_feature="avx2")] {
328        // mask the shift count to 31 to have same behavior on all platforms
329        let shift_by = bitand_m128i(rhs.sse, set_splat_i32_m128i(31));
330        Self { sse: shr_each_u32_m128i(self.sse, shift_by) }
331      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
332        unsafe {
333          // mask the shift count to 31 to have same behavior on all platforms
334          // no right shift, have to pass negative value to left shift on neon
335          let shift_by = vnegq_s32(vreinterpretq_s32_u32(vandq_u32(rhs.neon, vmovq_n_u32(31))));
336          Self { neon: vshlq_u32(self.neon, shift_by) }
337        }
338      } else {
339        let arr: [u32; 4] = cast(self);
340        let rhs: [u32; 4] = cast(rhs);
341        cast([
342          arr[0].wrapping_shr(rhs[0]),
343          arr[1].wrapping_shr(rhs[1]),
344          arr[2].wrapping_shr(rhs[2]),
345          arr[3].wrapping_shr(rhs[3]),
346        ])
347      }
348    }
349  }
350}
351
352/// Shifts lanes by the corresponding lane.
353///
354/// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
355/// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
356/// of the type. (same as `wrapping_shl`)
357impl Shl<u32x4> for u32x4 {
358  type Output = Self;
359  #[inline]
360  fn shl(self, rhs: u32x4) -> Self::Output {
361    pick! {
362      if #[cfg(target_feature="avx2")] {
363        // mask the shift count to 31 to have same behavior on all platforms
364        let shift_by = bitand_m128i(rhs.sse, set_splat_i32_m128i(31));
365        Self { sse: shl_each_u32_m128i(self.sse, shift_by) }
366      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
367        unsafe {
368          // mask the shift count to 31 to have same behavior on all platforms
369          let shift_by = vreinterpretq_s32_u32(vandq_u32(rhs.neon, vmovq_n_u32(31)));
370          Self { neon: vshlq_u32(self.neon, shift_by) }
371        }
372      } else {
373        let arr: [u32; 4] = cast(self);
374        let rhs: [u32; 4] = cast(rhs);
375        cast([
376          arr[0].wrapping_shl(rhs[0]),
377          arr[1].wrapping_shl(rhs[1]),
378          arr[2].wrapping_shl(rhs[2]),
379          arr[3].wrapping_shl(rhs[3]),
380        ])
381      }
382    }
383  }
384}
385
386#[expect(deprecated)]
387impl CmpEq for u32x4 {
388  type Output = Self;
389  #[inline]
390  fn simd_eq(self, rhs: Self) -> Self::Output {
391    pick! {
392      if #[cfg(target_feature="sse2")] {
393        Self { sse: cmp_eq_mask_i32_m128i(self.sse, rhs.sse) }
394      } else if #[cfg(target_feature="simd128")] {
395        Self { simd: u32x4_eq(self.simd, rhs.simd) }
396      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
397        unsafe {Self { neon: vceqq_u32(self.neon, rhs.neon) }}
398      } else {
399        Self { arr: [
400          if self.arr[0] == rhs.arr[0] { u32::MAX } else { 0 },
401          if self.arr[1] == rhs.arr[1] { u32::MAX } else { 0 },
402          if self.arr[2] == rhs.arr[2] { u32::MAX } else { 0 },
403          if self.arr[3] == rhs.arr[3] { u32::MAX } else { 0 },
404        ]}
405      }
406    }
407  }
408}
409
410#[expect(deprecated)]
411impl CmpGt for u32x4 {
412  type Output = Self;
413  #[inline]
414  fn simd_gt(self, rhs: Self) -> Self::Output {
415    pick! {
416      if #[cfg(target_feature="sse2")] {
417        // no unsigned less than so inverting the high bit will get the correct result
418        let h = u32x4::splat(1 << 31);
419        Self { sse: cmp_gt_mask_i32_m128i((self ^ h).sse, (rhs ^ h).sse) }
420      } else if #[cfg(target_feature="simd128")] {
421        Self { simd: u32x4_gt(self.simd, rhs.simd) }
422      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
423        unsafe {Self { neon: vcgtq_u32(self.neon, rhs.neon) }}
424      } else {
425        Self { arr: [
426          if self.arr[0] > rhs.arr[0] { u32::MAX } else { 0 },
427          if self.arr[1] > rhs.arr[1] { u32::MAX } else { 0 },
428          if self.arr[2] > rhs.arr[2] { u32::MAX } else { 0 },
429          if self.arr[3] > rhs.arr[3] { u32::MAX } else { 0 },
430        ]}
431      }
432    }
433  }
434}
435
436#[expect(deprecated)]
437impl CmpLt for u32x4 {
438  type Output = Self;
439  #[inline]
440  fn simd_lt(self, rhs: Self) -> Self::Output {
441    // lt is just gt the other way around
442    rhs.simd_gt(self)
443  }
444}
445
446#[expect(deprecated)]
447impl CmpNe for u32x4 {
448  type Output = Self;
449  #[inline]
450  fn simd_ne(self, rhs: Self) -> Self::Output {
451    pick! {
452      if #[cfg(target_feature="sse2")] {
453        !self.simd_eq(rhs)
454      } else if #[cfg(target_feature="simd128")] {
455        Self { simd: u32x4_ne(self.simd, rhs.simd) }
456      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
457        !self.simd_eq(rhs)
458      } else {
459        Self { arr: [
460          if self.arr[0] != rhs.arr[0] { u32::MAX } else { 0 },
461          if self.arr[1] != rhs.arr[1] { u32::MAX } else { 0 },
462          if self.arr[2] != rhs.arr[2] { u32::MAX } else { 0 },
463          if self.arr[3] != rhs.arr[3] { u32::MAX } else { 0 },
464        ]}
465      }
466    }
467  }
468}
469
470#[expect(deprecated)]
471impl CmpLe for u32x4 {
472  type Output = Self;
473  #[inline]
474  fn simd_le(self, rhs: Self) -> Self::Output {
475    pick! {
476      if #[cfg(target_feature="sse2")] {
477        !self.simd_gt(rhs)
478      } else if #[cfg(target_feature="simd128")] {
479        Self { simd: u32x4_le(self.simd, rhs.simd) }
480      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
481        !self.simd_gt(rhs)
482      } else {
483        Self { arr: [
484          if self.arr[0] <= rhs.arr[0] { u32::MAX } else { 0 },
485          if self.arr[1] <= rhs.arr[1] { u32::MAX } else { 0 },
486          if self.arr[2] <= rhs.arr[2] { u32::MAX } else { 0 },
487          if self.arr[3] <= rhs.arr[3] { u32::MAX } else { 0 },
488        ]}
489      }
490    }
491  }
492}
493
494#[expect(deprecated)]
495impl CmpGe for u32x4 {
496  type Output = Self;
497  #[inline]
498  fn simd_ge(self, rhs: Self) -> Self::Output {
499    pick! {
500      if #[cfg(target_feature="sse2")] {
501        !self.simd_lt(rhs)
502      } else if #[cfg(target_feature="simd128")] {
503        Self { simd: u32x4_ge(self.simd, rhs.simd) }
504      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
505        !self.simd_lt(rhs)
506      } else {
507        Self { arr: [
508          if self.arr[0] >= rhs.arr[0] { u32::MAX } else { 0 },
509          if self.arr[1] >= rhs.arr[1] { u32::MAX } else { 0 },
510          if self.arr[2] >= rhs.arr[2] { u32::MAX } else { 0 },
511          if self.arr[3] >= rhs.arr[3] { u32::MAX } else { 0 },
512        ]}
513      }
514    }
515  }
516}
517
518impl u32x4 {
519  #[inline]
520  #[must_use]
521  pub const fn new(array: [u32; 4]) -> Self {
522    unsafe { core::mem::transmute(array) }
523  }
524
525  simd_comparison_fns!();
526
527  /// Multiplies 32x32 bit to 64 bit and then only keeps the high 32 bits of the
528  /// result. Useful for implementing divide constant value (see `t_usefulness`
529  /// example)
530  #[inline]
531  #[must_use]
532  pub fn mul_keep_high(self, rhs: Self) -> Self {
533    pick! {
534      if #[cfg(target_feature="avx2")] {
535        let a = convert_to_i64_m256i_from_u32_m128i(self.sse);
536        let b = convert_to_i64_m256i_from_u32_m128i(rhs.sse);
537        let r = mul_u64_low_bits_m256i(a, b);
538
539        // the compiler does a good job shuffling the lanes around
540        let b : [u32;8] = cast(r);
541        cast([b[1],b[3],b[5],b[7]])
542      } else if #[cfg(target_feature="sse2")] {
543        let evenp = mul_widen_u32_odd_m128i(self.sse, rhs.sse);
544
545        let oddp = mul_widen_u32_odd_m128i(
546          shr_imm_u64_m128i::<32>(self.sse),
547          shr_imm_u64_m128i::<32>(rhs.sse));
548
549        // the compiler does a good job shuffling the lanes around
550        let a : [u32;4]= cast(evenp);
551        let b : [u32;4]= cast(oddp);
552        cast([a[1],b[1],a[3],b[3]])
553
554      } else if #[cfg(target_feature="simd128")] {
555        let low =  u64x2_extmul_low_u32x4(self.simd, rhs.simd);
556        let high = u64x2_extmul_high_u32x4(self.simd, rhs.simd);
557
558        Self { simd: u32x4_shuffle::<1, 3, 5, 7>(low, high) }
559      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
560        unsafe {
561          let l = vmull_u32(vget_low_u32(self.neon), vget_low_u32(rhs.neon));
562          let h = vmull_u32(vget_high_u32(self.neon), vget_high_u32(rhs.neon));
563          u32x4 { neon: vcombine_u32(vshrn_n_u64(l,32), vshrn_n_u64(h,32)) }
564        }
565      } else {
566        let a: [u32; 4] = cast(self);
567        let b: [u32; 4] = cast(rhs);
568        cast([
569          ((u64::from(a[0]) * u64::from(b[0])) >> 32) as u32,
570          ((u64::from(a[1]) * u64::from(b[1])) >> 32) as u32,
571          ((u64::from(a[2]) * u64::from(b[2])) >> 32) as u32,
572          ((u64::from(a[3]) * u64::from(b[3])) >> 32) as u32,
573        ])
574      }
575    }
576  }
577
578  /// Multiplies corresponding 32 bit lanes and returns the 64 bit result
579  /// on the corresponding lanes.
580  ///
581  /// Effectively does two multiplies on 128 bit platforms, but is easier
582  /// to use than wrapping `mul_widen_u32_odd_m128i` individually.
583  #[inline]
584  #[must_use]
585  pub fn mul_widen(self, rhs: Self) -> u64x4 {
586    pick! {
587      if #[cfg(target_feature="avx2")] {
588        // ok to sign extend since we are throwing away the high half of the result anyway
589        let a = convert_to_i64_m256i_from_i32_m128i(self.sse);
590        let b = convert_to_i64_m256i_from_i32_m128i(rhs.sse);
591        cast(mul_u64_low_bits_m256i(a, b))
592      } else if #[cfg(target_feature="sse2")] {
593        let evenp = mul_widen_u32_odd_m128i(self.sse, rhs.sse);
594
595        let oddp = mul_widen_u32_odd_m128i(
596          shr_imm_u64_m128i::<32>(self.sse),
597          shr_imm_u64_m128i::<32>(rhs.sse));
598
599        u64x4 {
600          a: u64x2 { sse: unpack_low_i64_m128i(evenp, oddp)},
601          b: u64x2 { sse: unpack_high_i64_m128i(evenp, oddp)}
602        }
603      } else if #[cfg(target_feature="simd128")] {
604        u64x4 {
605          a: u64x2 { simd: u64x2_extmul_low_u32x4(self.simd, rhs.simd) },
606          b: u64x2 { simd: u64x2_extmul_high_u32x4(self.simd, rhs.simd) },
607        }
608      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
609      unsafe {
610        u64x4 { a: u64x2 { neon: vmull_u32(vget_low_u32(self.neon), vget_low_u32(rhs.neon)) },
611                b: u64x2 { neon: vmull_u32(vget_high_u32(self.neon), vget_high_u32(rhs.neon)) } }
612        }
613      } else {
614        let a: [u32; 4] = cast(self);
615        let b: [u32; 4] = cast(rhs);
616        cast([
617          u64::from(a[0]) * u64::from(b[0]),
618          u64::from(a[1]) * u64::from(b[1]),
619          u64::from(a[2]) * u64::from(b[2]),
620          u64::from(a[3]) * u64::from(b[3]),
621        ])
622      }
623    }
624  }
625
626  #[inline]
627  #[must_use]
628  pub fn blend(self, t: Self, f: Self) -> Self {
629    pick! {
630      if #[cfg(target_feature="sse4.1")] {
631        Self { sse: blend_varying_i8_m128i(f.sse, t.sse, self.sse) }
632      } else if #[cfg(target_feature="simd128")] {
633        Self { simd: v128_bitselect(t.simd, f.simd, self.simd) }
634      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
635        unsafe {Self { neon: vbslq_u32(self.neon, t.neon, f.neon) }}
636      } else {
637        generic_bit_blend(self, t, f)
638      }
639    }
640  }
641
642  #[inline]
643  #[must_use]
644  pub fn reduce_add(self) -> u32 {
645    cast(i32x4::reduce_add(cast(self)))
646  }
647
648  #[inline]
649  #[must_use]
650  pub fn reduce_max(self) -> u32 {
651    let arr: [u32; 4] = cast(self);
652    arr[0].max(arr[1]).max(arr[2].max(arr[3]))
653  }
654
655  #[inline]
656  #[must_use]
657  pub fn reduce_min(self) -> u32 {
658    let arr: [u32; 4] = cast(self);
659    arr[0].min(arr[1]).min(arr[2].min(arr[3]))
660  }
661
662  #[inline]
663  #[must_use]
664  pub fn max(self, rhs: Self) -> Self {
665    pick! {
666      if #[cfg(target_feature="sse4.1")] {
667        Self { sse: max_u32_m128i(self.sse, rhs.sse) }
668      } else if #[cfg(target_feature="simd128")] {
669        Self { simd: u32x4_max(self.simd, rhs.simd) }
670      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
671        unsafe {Self { neon: vmaxq_u32(self.neon, rhs.neon) }}
672      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
673        unsafe {Self { neon: vmaxq_u16(self.neon, rhs.neon) }}
674      } else {
675        let arr: [u32; 4] = cast(self);
676        let rhs: [u32; 4] = cast(rhs);
677        cast([
678          arr[0].max(rhs[0]),
679          arr[1].max(rhs[1]),
680          arr[2].max(rhs[2]),
681          arr[3].max(rhs[3]),
682        ])
683      }
684    }
685  }
686  #[inline]
687  #[must_use]
688  pub fn min(self, rhs: Self) -> Self {
689    pick! {
690      if #[cfg(target_feature="sse4.1")] {
691        Self { sse: min_u32_m128i(self.sse, rhs.sse) }
692      } else if #[cfg(target_feature="simd128")] {
693        Self { simd: u32x4_min(self.simd, rhs.simd) }
694      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
695        unsafe {Self { neon: vminq_u32(self.neon, rhs.neon) }}
696      } else {
697        let arr: [u32; 4] = cast(self);
698        let rhs: [u32; 4] = cast(rhs);
699        cast([
700          arr[0].min(rhs[0]),
701          arr[1].min(rhs[1]),
702          arr[2].min(rhs[2]),
703          arr[3].min(rhs[3]),
704        ])
705      }
706    }
707  }
708
709  integer_fn_clamp!();
710
711  #[inline]
712  #[must_use]
713  pub fn saturating_add(self, rhs: Self) -> Self {
714    pick! {
715      if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
716        let result = self + rhs;
717        result.simd_lt(self).blend(Self::MAX, result)
718      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
719        unsafe { Self { neon: vqaddq_u32(self.neon, rhs.neon) } }
720      } else {
721        Self {
722          arr: [
723            self.arr[0].saturating_add(rhs.arr[0]),
724            self.arr[1].saturating_add(rhs.arr[1]),
725            self.arr[2].saturating_add(rhs.arr[2]),
726            self.arr[3].saturating_add(rhs.arr[3]),
727          ],
728        }
729      }
730    }
731  }
732
733  #[inline]
734  #[must_use]
735  pub fn saturating_sub(self, rhs: Self) -> Self {
736    pick! {
737      if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
738        let result = self - rhs;
739        result.simd_gt(self).blend(Self::MIN, result)
740      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
741        unsafe { Self { neon: vqsubq_u32(self.neon, rhs.neon) } }
742      } else {
743        Self {
744          arr: [
745            self.arr[0].saturating_sub(rhs.arr[0]),
746            self.arr[1].saturating_sub(rhs.arr[1]),
747            self.arr[2].saturating_sub(rhs.arr[2]),
748            self.arr[3].saturating_sub(rhs.arr[3]),
749          ],
750        }
751      }
752    }
753  }
754
755  /// Lanewise saturating multiply.
756  #[inline]
757  #[must_use]
758  pub fn saturating_mul(self, rhs: Self) -> Self {
759    pick! {
760      if #[cfg(target_feature="sse2")] {
761        let even_wide_mul = mul_widen_u32_odd_m128i(self.sse, rhs.sse);
762        let odd_wide_mul = mul_widen_u32_odd_m128i(
763          shuffle_ai_f32_all_m128i::<0b_00_11_00_01>(self.sse),
764          shuffle_ai_f32_all_m128i::<0b_00_11_00_01>(rhs.sse),
765        );
766
767        let ll_hh_1 = unpack_low_i32_m128i(even_wide_mul, odd_wide_mul);
768        let ll_hh_2 = unpack_high_i32_m128i(even_wide_mul, odd_wide_mul);
769        let low = Self { sse: unpack_low_i64_m128i(ll_hh_1, ll_hh_2) };
770        let high = Self { sse: unpack_high_i64_m128i(ll_hh_1, ll_hh_2) };
771
772        let no_overflow = high.simd_eq(Self::ZERO);
773        no_overflow.blend(low, Self::MAX)
774      } else if #[cfg(target_feature="simd128")] {
775        let low_wide_mul = u64x2_extmul_low_u32x4(self.simd, rhs.simd);
776        let high_wide_mul = u64x2_extmul_high_u32x4(self.simd, rhs.simd);
777        let low = Self { simd: u32x4_shuffle::<0, 2, 4, 6>(low_wide_mul, high_wide_mul) };
778        let high = Self { simd: u32x4_shuffle::<1, 3, 5, 7>(low_wide_mul, high_wide_mul) };
779
780        let no_overflow = high.simd_eq(Self::ZERO);
781        no_overflow.blend(low, Self::MAX)
782      } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
783        unsafe {
784          let low_wide_mul = vreinterpretq_u32_u64(
785            vmull_u32(vget_low_u32(self.neon), vget_low_u32(rhs.neon)),
786          );
787          let high_wide_mul = vreinterpretq_u32_u64(
788            vmull_u32(vget_high_u32(self.neon), vget_high_u32(rhs.neon)),
789          );
790          let low_high = vuzpq_u32(low_wide_mul, high_wide_mul);
791          let low = Self { neon: low_high.0 };
792          let high = Self { neon: low_high.1 };
793
794          let no_overflow = high.simd_eq(Self::ZERO);
795          no_overflow.blend(low, Self::MAX)
796        }
797      } else {
798        let self_array = self.to_array();
799        let rhs_array = rhs.to_array();
800
801        Self::new([
802          self_array[0].saturating_mul(rhs_array[0]),
803          self_array[1].saturating_mul(rhs_array[1]),
804          self_array[2].saturating_mul(rhs_array[2]),
805          self_array[3].saturating_mul(rhs_array[3]),
806        ])
807      }
808    }
809  }
810
811  integer_fn_saturating_div!([0, 1, 2, 3]);
812
813  #[inline]
814  #[must_use]
815  pub fn any(self) -> bool {
816    pick! {
817      if #[cfg(target_feature="sse2")] {
818        (move_mask_i8_m128i(self.sse) & 0b1000100010001000) != 0
819      } else if #[cfg(target_feature="simd128")] {
820        u32x4_bitmask(self.simd) != 0
821      } else {
822        let v : [u64;2] = cast(self);
823        ((v[0] | v[1]) & 0x8000000080000000) != 0
824      }
825    }
826  }
827
828  #[inline]
829  #[must_use]
830  pub fn all(self) -> bool {
831    pick! {
832      if #[cfg(target_feature="sse2")] {
833        (move_mask_i8_m128i(self.sse) & 0b1000100010001000) == 0b1000100010001000
834      } else if #[cfg(target_feature="simd128")] {
835        u32x4_bitmask(self.simd) == 0b1111
836      } else {
837        let v : [u64;2] = cast(self);
838        (v[0] & v[1] & 0x8000000080000000) == 0x8000000080000000
839      }
840    }
841  }
842
843  #[inline]
844  #[must_use]
845  pub fn none(self) -> bool {
846    !self.any()
847  }
848
849  /// Transpose matrix of 4x4 `u32` matrix. Currently only accelerated on SSE.
850  #[must_use]
851  #[inline]
852  pub fn transpose(data: [u32x4; 4]) -> [u32x4; 4] {
853    cast(i32x4::transpose(cast(data)))
854  }
855
856  #[inline]
857  #[must_use]
858  #[doc(alias("movemask", "move_mask"))]
859  pub fn to_bitmask(self) -> u32 {
860    i32x4::to_bitmask(cast(self))
861  }
862
863  #[inline]
864  pub fn to_array(self) -> [u32; 4] {
865    cast(self)
866  }
867
868  #[inline]
869  pub fn as_array(&self) -> &[u32; 4] {
870    cast_ref(self)
871  }
872
873  #[inline]
874  pub fn as_mut_array(&mut self) -> &mut [u32; 4] {
875    cast_mut(self)
876  }
877}