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
180impl 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 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 #[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
229impl 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 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 #[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 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 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 #[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 #[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}