Skip to main content

wide/
u64x4_.rs

1use super::*;
2
3pick! {
4  if #[cfg(target_feature="avx2")] {
5    #[derive(Default, Clone, Copy, PartialEq, Eq)]
6    #[repr(C, align(32))]
7    pub struct u64x4 { pub(crate) avx2: m256i }
8  } else {
9    #[derive(Default, Clone, Copy, PartialEq, Eq)]
10    #[repr(C, align(32))]
11    pub struct u64x4 { pub(crate) a : u64x2, pub(crate) b : u64x2 }
12  }
13}
14
15int_uint_consts!(u64, 4, u64x4, 256);
16
17unsafe impl Zeroable for u64x4 {}
18unsafe impl Pod for u64x4 {}
19
20impl AlignTo for u64x4 {
21  type Elem = u64;
22}
23
24impl Add for u64x4 {
25  type Output = Self;
26  #[inline]
27  fn add(self, rhs: Self) -> Self::Output {
28    pick! {
29      if #[cfg(target_feature="avx2")] {
30        Self { avx2: add_i64_m256i(self.avx2, rhs.avx2) }
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 u64x4 {
42  type Output = Self;
43  #[inline]
44  fn sub(self, rhs: Self) -> Self::Output {
45    pick! {
46      if #[cfg(target_feature="avx2")] {
47        Self { avx2: sub_i64_m256i(self.avx2, rhs.avx2) }
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 u64x4 {
59  type Output = Self;
60  #[inline]
61  fn mul(self, rhs: Self) -> Self::Output {
62    pick! {
63      if #[cfg(target_feature="avx2")] {
64        let arr1: [i64; 4] = cast(self);
65        let arr2: [i64; 4] = cast(rhs);
66        cast([
67          arr1[0].wrapping_mul(arr2[0]),
68          arr1[1].wrapping_mul(arr2[1]),
69          arr1[2].wrapping_mul(arr2[2]),
70          arr1[3].wrapping_mul(arr2[3]),
71        ])
72      } else {
73        Self { a: self.a.mul(rhs.a), b: self.b.mul(rhs.b) }
74      }
75    }
76  }
77}
78
79integer_impl_div_rem!(u64, u64x4, [0, 1, 2, 3]);
80
81impl Add<u64> for u64x4 {
82  type Output = Self;
83  #[inline]
84  fn add(self, rhs: u64) -> Self::Output {
85    self.add(Self::splat(rhs))
86  }
87}
88
89impl Sub<u64> for u64x4 {
90  type Output = Self;
91  #[inline]
92  fn sub(self, rhs: u64) -> Self::Output {
93    self.sub(Self::splat(rhs))
94  }
95}
96
97impl Mul<u64> for u64x4 {
98  type Output = Self;
99  #[inline]
100  fn mul(self, rhs: u64) -> Self::Output {
101    self.mul(Self::splat(rhs))
102  }
103}
104
105impl Add<u64x4> for u64 {
106  type Output = u64x4;
107  #[inline]
108  fn add(self, rhs: u64x4) -> Self::Output {
109    u64x4::splat(self).add(rhs)
110  }
111}
112
113impl Sub<u64x4> for u64 {
114  type Output = u64x4;
115  #[inline]
116  fn sub(self, rhs: u64x4) -> Self::Output {
117    u64x4::splat(self).sub(rhs)
118  }
119}
120
121impl Mul<u64x4> for u64 {
122  type Output = u64x4;
123  #[inline]
124  fn mul(self, rhs: u64x4) -> Self::Output {
125    u64x4::splat(self).mul(rhs)
126  }
127}
128
129impl BitAnd for u64x4 {
130  type Output = Self;
131  #[inline]
132  fn bitand(self, rhs: Self) -> Self::Output {
133    pick! {
134      if #[cfg(target_feature="avx2")] {
135        Self { avx2: bitand_m256i(self.avx2, rhs.avx2) }
136      } else {
137        Self {
138          a : self.a.bitand(rhs.a),
139          b : self.b.bitand(rhs.b),
140        }
141      }
142    }
143  }
144}
145
146impl BitOr for u64x4 {
147  type Output = Self;
148  #[inline]
149  fn bitor(self, rhs: Self) -> Self::Output {
150    pick! {
151    if #[cfg(target_feature="avx2")] {
152        Self { avx2: bitor_m256i(self.avx2, rhs.avx2) }
153      } else {
154        Self {
155          a : self.a.bitor(rhs.a),
156          b : self.b.bitor(rhs.b),
157        }
158      }
159    }
160  }
161}
162
163impl BitXor for u64x4 {
164  type Output = Self;
165  #[inline]
166  fn bitxor(self, rhs: Self) -> Self::Output {
167    pick! {
168      if #[cfg(target_feature="avx2")] {
169        Self { avx2: bitxor_m256i(self.avx2, rhs.avx2) }
170      } else {
171        Self {
172          a : self.a.bitxor(rhs.a),
173          b : self.b.bitxor(rhs.b),
174        }
175      }
176    }
177  }
178}
179
180/// Shifts lanes by the corresponding lane.
181///
182/// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
183/// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
184/// of the type. (same as `wrapping_shl`)
185impl Shl for u64x4 {
186  type Output = Self;
187
188  #[inline]
189  fn shl(self, rhs: Self) -> Self::Output {
190    pick! {
191      if #[cfg(target_feature="avx2")] {
192        // mask the shift count to 63 to have same behavior on all platforms
193        let shift_by = rhs & Self::splat(63);
194        Self { avx2: shl_each_u64_m256i(self.avx2, shift_by.avx2) }
195      } else {
196        Self {
197          a : self.a.shl(rhs.a),
198          b : self.b.shl(rhs.b),
199        }
200      }
201    }
202  }
203}
204
205macro_rules! impl_shl_t_for_u64x4 {
206  ($($shift_type:ty),+ $(,)?) => {
207    $(impl Shl<$shift_type> for u64x4 {
208      type Output = Self;
209      /// Shifts all lanes by the value given.
210      #[inline]
211      fn shl(self, rhs: $shift_type) -> Self::Output {
212        pick! {
213          if #[cfg(target_feature="avx2")] {
214            let shift = cast([rhs as u64, 0]);
215            Self { avx2: shl_all_u64_m256i(self.avx2, shift) }
216          } else {
217            Self {
218              a : self.a.shl(rhs),
219              b : self.b.shl(rhs),
220            }
221          }
222        }
223      }
224    })+
225  };
226}
227impl_shl_t_for_u64x4!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
228
229/// Shifts lanes by the corresponding lane.
230///
231/// Bitwise shift-right; yields `self >> mask(rhs)`, where mask removes any
232/// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
233/// of the type. (same as `wrapping_shr`)
234impl Shr for u64x4 {
235  type Output = Self;
236
237  #[inline]
238  fn shr(self, rhs: Self) -> Self::Output {
239    pick! {
240      if #[cfg(target_feature="avx2")] {
241        // mask the shift count to 63 to have same behavior on all platforms
242        let shift_by = rhs & Self::splat(63);
243        Self { avx2: shr_each_u64_m256i(self.avx2, shift_by.avx2) }
244      } else {
245        Self {
246          a : self.a.shr(rhs.a),
247          b : self.b.shr(rhs.b),
248        }
249      }
250    }
251  }
252}
253
254macro_rules! impl_shr_t_for_u64x4 {
255  ($($shift_type:ty),+ $(,)?) => {
256    $(impl Shr<$shift_type> for u64x4 {
257      type Output = Self;
258      /// Shifts all lanes by the value given.
259      #[inline]
260      fn shr(self, rhs: $shift_type) -> Self::Output {
261        pick! {
262          if #[cfg(target_feature="avx2")] {
263            let shift = cast([rhs as u64, 0]);
264            Self { avx2: shr_all_u64_m256i(self.avx2, shift) }
265          } else {
266            Self {
267              a : self.a.shr(rhs),
268              b : self.b.shr(rhs),
269            }
270          }
271        }
272      }
273    })+
274  };
275}
276impl_shr_t_for_u64x4!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
277
278#[expect(deprecated)]
279impl CmpEq for u64x4 {
280  type Output = Self;
281  #[inline]
282  fn simd_eq(self, rhs: Self) -> Self::Output {
283    pick! {
284      if #[cfg(target_feature="avx2")] {
285        Self { avx2: cmp_eq_mask_i64_m256i(self.avx2, rhs.avx2) }
286      } else {
287        Self {
288          a : self.a.simd_eq(rhs.a),
289          b : self.b.simd_eq(rhs.b),
290        }
291      }
292    }
293  }
294}
295
296#[expect(deprecated)]
297impl CmpNe for u64x4 {
298  type Output = Self;
299  #[inline]
300  fn simd_ne(self, rhs: Self) -> Self::Output {
301    pick! {
302      if #[cfg(target_feature="avx2")] {
303        !self.simd_eq(rhs)
304      } else {
305        Self {
306          a : self.a.simd_ne(rhs.a),
307          b : self.b.simd_ne(rhs.b),
308        }
309      }
310    }
311  }
312}
313
314#[expect(deprecated)]
315impl CmpLe for u64x4 {
316  type Output = Self;
317  #[inline]
318  fn simd_le(self, rhs: Self) -> Self::Output {
319    pick! {
320      if #[cfg(target_feature="avx2")] {
321        !self.simd_gt(rhs)
322      } else {
323        Self {
324          a : self.a.simd_le(rhs.a),
325          b : self.b.simd_le(rhs.b),
326        }
327      }
328    }
329  }
330}
331
332#[expect(deprecated)]
333impl CmpGe for u64x4 {
334  type Output = Self;
335  #[inline]
336  fn simd_ge(self, rhs: Self) -> Self::Output {
337    pick! {
338      if #[cfg(target_feature="avx2")] {
339        !self.simd_lt(rhs)
340      } else {
341        Self {
342          a : self.a.simd_ge(rhs.a),
343          b : self.b.simd_ge(rhs.b),
344        }
345      }
346    }
347  }
348}
349
350#[expect(deprecated)]
351impl CmpGt for u64x4 {
352  type Output = Self;
353  #[inline]
354  fn simd_gt(self, rhs: Self) -> Self::Output {
355    pick! {
356      if #[cfg(target_feature="avx2")] {
357        // no unsigned gt than so inverting the high bit will get the correct result
358        let highbit = u64x4::splat(1 << 63);
359        Self { avx2: cmp_gt_mask_i64_m256i((self ^ highbit).avx2, (rhs ^ highbit).avx2) }
360      } else {
361        Self {
362          a : self.a.simd_gt(rhs.a),
363          b : self.b.simd_gt(rhs.b),
364        }
365      }
366    }
367  }
368}
369
370#[expect(deprecated)]
371impl CmpLt for u64x4 {
372  type Output = Self;
373  #[inline]
374  fn simd_lt(self, rhs: Self) -> Self::Output {
375    // lt is just gt the other way around
376    rhs.simd_gt(self)
377  }
378}
379
380impl u64x4 {
381  #[inline]
382  #[must_use]
383  pub const fn new(array: [u64; 4]) -> Self {
384    unsafe { core::mem::transmute(array) }
385  }
386
387  simd_comparison_fns!();
388
389  #[inline]
390  #[must_use]
391  pub fn blend(self, t: Self, f: Self) -> Self {
392    pick! {
393      if #[cfg(target_feature="avx2")] {
394        Self { avx2: blend_varying_i8_m256i(f.avx2,t.avx2,self.avx2) }
395      } else {
396        Self {
397          a : self.a.blend(t.a, f.a),
398          b : self.b.blend(t.b, f.b),
399        }
400      }
401    }
402  }
403
404  #[inline]
405  #[must_use]
406  pub fn reduce_add(self) -> u64 {
407    cast(i64x4::reduce_add(cast(self)))
408  }
409
410  #[inline]
411  #[must_use]
412  pub fn reduce_max(self) -> u64 {
413    let array: [u64; 4] = cast(self);
414    array[0].max(array[1]).max(array[2]).max(array[3])
415  }
416
417  #[inline]
418  #[must_use]
419  pub fn reduce_min(self) -> u64 {
420    let array: [u64; 4] = cast(self);
421    array[0].min(array[1]).min(array[2]).min(array[3])
422  }
423
424  #[inline]
425  #[must_use]
426  #[doc(alias("movemask", "move_mask"))]
427  pub fn to_bitmask(self) -> u32 {
428    i64x4::to_bitmask(cast(self))
429  }
430
431  #[inline]
432  #[must_use]
433  pub fn any(self) -> bool {
434    i64x4::any(cast(self))
435  }
436
437  #[inline]
438  #[must_use]
439  pub fn all(self) -> bool {
440    i64x4::all(cast(self))
441  }
442
443  #[inline]
444  #[must_use]
445  pub fn none(self) -> bool {
446    !self.any()
447  }
448
449  /// Transpose matrix of 4x4 `u64` matrix.
450  #[must_use]
451  #[inline]
452  pub fn transpose(data: [u64x4; 4]) -> [u64x4; 4] {
453    cast(i64x4::transpose(cast(data)))
454  }
455
456  #[inline]
457  pub fn to_array(self) -> [u64; 4] {
458    cast(self)
459  }
460
461  #[inline]
462  pub fn as_array(&self) -> &[u64; 4] {
463    cast_ref(self)
464  }
465
466  #[inline]
467  pub fn as_mut_array(&mut self) -> &mut [u64; 4] {
468    cast_mut(self)
469  }
470
471  #[inline]
472  #[must_use]
473  pub fn min(self, rhs: Self) -> Self {
474    self.simd_lt(rhs).blend(self, rhs)
475  }
476
477  #[inline]
478  #[must_use]
479  pub fn max(self, rhs: Self) -> Self {
480    self.simd_gt(rhs).blend(self, rhs)
481  }
482
483  integer_fn_clamp!();
484
485  #[inline]
486  #[must_use]
487  pub fn saturating_add(self, rhs: Self) -> Self {
488    pick! {
489      if #[cfg(target_feature="avx2")] {
490        let result = self + rhs;
491        result.simd_lt(self).blend(Self::MAX, result)
492      } else {
493        Self {
494          a: self.a.saturating_add(rhs.a),
495          b: self.b.saturating_add(rhs.b),
496        }
497      }
498    }
499  }
500
501  #[inline]
502  #[must_use]
503  pub fn saturating_sub(self, rhs: Self) -> Self {
504    pick! {
505      if #[cfg(target_feature="avx2")] {
506        let result = self - rhs;
507        result.simd_gt(self).blend(Self::MIN, result)
508      } else {
509        Self {
510          a: self.a.saturating_sub(rhs.a),
511          b: self.b.saturating_sub(rhs.b),
512        }
513      }
514    }
515  }
516
517  /// Lanewise saturating multiply.
518  #[inline]
519  #[must_use]
520  pub fn saturating_mul(self, rhs: Self) -> Self {
521    let self_array = self.to_array();
522    let rhs_array = rhs.to_array();
523
524    Self::new([
525      self_array[0].saturating_mul(rhs_array[0]),
526      self_array[1].saturating_mul(rhs_array[1]),
527      self_array[2].saturating_mul(rhs_array[2]),
528      self_array[3].saturating_mul(rhs_array[3]),
529    ])
530  }
531
532  integer_fn_saturating_div!([0, 1, 2, 3]);
533
534  #[inline]
535  #[must_use]
536  pub fn mul_keep_high(self, rhs: Self) -> Self {
537    pick! {
538      if #[cfg(target_feature="avx2")] {
539        let arr1: [u64; 4] = cast(self);
540        let arr2: [u64; 4] = cast(rhs);
541        cast([
542          (arr1[0] as u128 * arr2[0] as u128 >> 64) as u64,
543          (arr1[1] as u128 * arr2[1] as u128 >> 64) as u64,
544          (arr1[2] as u128 * arr2[2] as u128 >> 64) as u64,
545          (arr1[3] as u128 * arr2[3] as u128 >> 64) as u64,
546        ])
547      } else {
548        Self {
549          a: self.a.mul_keep_high(rhs.a),
550          b: self.b.mul_keep_high(rhs.b),
551        }
552      }
553    }
554  }
555}
556
557impl Not for u64x4 {
558  type Output = Self;
559  #[inline]
560  fn not(self) -> Self {
561    pick! {
562      if #[cfg(target_feature="avx2")] {
563        Self { avx2: self.avx2.not()  }
564      } else {
565        Self {
566          a : self.a.not(),
567          b : self.b.not(),
568        }
569      }
570    }
571  }
572}