1use super::*;
2
3pick! {
4 if #[cfg(target_feature="avx512f")] {
5 #[derive(Default, Clone, Copy, PartialEq, Eq)]
12 #[repr(C, align(64))]
13 pub struct u64x8 { pub(crate) avx512: m512i }
14 } else {
15 #[derive(Default, Clone, Copy, PartialEq, Eq)]
22 #[repr(C, align(64))]
23 pub struct u64x8 { pub(crate) a : u64x4, pub(crate) b : u64x4 }
24 }
25}
26
27impl_simd_uint! {
28 unsafe {
29 T = u64,
30 N = 8,
31 Simd = u64x8,
32 IntSimd = i64x8,
33 T_BITS = 64,
34 T_BITS_MUL_2 = 128,
35 [0, 1, 2, 3, 4, 5, 6, 7],
36 optional_type_x86_inner { X86Inner = __m512i },
37 optional_type_arm_inner {},
38 optional_type_wasm_inner {},
39 }
40
41 #[inline]
42 fn not(self) -> Self::Output {
43 pick! {
44 if #[cfg(target_feature="avx512f")] {
45 Self { avx512: bitxor_m512i(self.avx512, set_splat_i64_m512i(-1)) }
46 } else {
47 Self {
48 a : self.a.not(),
49 b : self.b.not(),
50 }
51 }
52 }
53 }
54
55 #[inline]
56 fn add(self, rhs: Self) -> Self::Output {
57 pick! {
58 if #[cfg(target_feature="avx512f")] {
59 Self { avx512: add_i64_m512i(self.avx512, rhs.avx512) }
60 } else {
61 Self {
62 a : self.a.add(rhs.a),
63 b : self.b.add(rhs.b),
64 }
65 }
66 }
67 }
68
69 #[inline]
70 fn sub(self, rhs: Self) -> Self::Output {
71 pick! {
72 if #[cfg(target_feature="avx512f")] {
73 Self { avx512: sub_i64_m512i(self.avx512, rhs.avx512) }
74 } else {
75 Self {
76 a : self.a.sub(rhs.a),
77 b : self.b.sub(rhs.b),
78 }
79 }
80 }
81 }
82
83 #[inline]
84 fn mul(self, rhs: Self) -> Self::Output {
85 pick! {
86 if #[cfg(target_feature="avx512f")] {
87 let arr1: [u64; 8] = cast(self);
88 let arr2: [u64; 8] = cast(rhs);
89 cast([
90 arr1[0].wrapping_mul(arr2[0]),
91 arr1[1].wrapping_mul(arr2[1]),
92 arr1[2].wrapping_mul(arr2[2]),
93 arr1[3].wrapping_mul(arr2[3]),
94 arr1[4].wrapping_mul(arr2[4]),
95 arr1[5].wrapping_mul(arr2[5]),
96 arr1[6].wrapping_mul(arr2[6]),
97 arr1[7].wrapping_mul(arr2[7]),
98 ])
99 } else {
100 Self { a: self.a.mul(rhs.a), b: self.b.mul(rhs.b) }
101 }
102 }
103 }
104
105 #[inline]
106 fn bitand(self, rhs: Self) -> Self::Output {
107 pick! {
108 if #[cfg(target_feature="avx512f")] {
109 Self { avx512: bitand_m512i(self.avx512, rhs.avx512) }
110 } else {
111 Self {
112 a : self.a.bitand(rhs.a),
113 b : self.b.bitand(rhs.b),
114 }
115 }
116 }
117 }
118
119 #[inline]
120 fn bitor(self, rhs: Self) -> Self::Output {
121 pick! {
122 if #[cfg(target_feature="avx512f")] {
123 Self { avx512: bitor_m512i(self.avx512, rhs.avx512) }
124 } else {
125 Self {
126 a : self.a.bitor(rhs.a),
127 b : self.b.bitor(rhs.b),
128 }
129 }
130 }
131 }
132
133 #[inline]
134 fn bitxor(self, rhs: Self) -> Self::Output {
135 pick! {
136 if #[cfg(target_feature="avx512f")] {
137 Self { avx512: bitxor_m512i(self.avx512, rhs.avx512) }
138 } else {
139 Self {
140 a : self.a.bitxor(rhs.a),
141 b : self.b.bitxor(rhs.b),
142 }
143 }
144 }
145 }
146
147 #[inline]
148 fn simd_eq(self, rhs: Self) -> Self::Output {
149 pick! {
150 if #[cfg(target_feature="avx512f")] {
151 Self { avx512: cmp_op_mask_u64_m512i::<{cmp_int_op!(Eq)}>(self.avx512, rhs.avx512) }
152 } else {
153 Self {
154 a : self.a.simd_eq(rhs.a),
155 b : self.b.simd_eq(rhs.b),
156 }
157 }
158 }
159 }
160
161 #[inline]
162 fn simd_ne(self, rhs: Self) -> Self::Output {
163 pick! {
164 if #[cfg(target_feature="avx512f")] {
165 Self { avx512: cmp_op_mask_u64_m512i::<{cmp_int_op!(Ne)}>(self.avx512, rhs.avx512) }
166 } else {
167 Self {
168 a : self.a.simd_ne(rhs.a),
169 b : self.b.simd_ne(rhs.b),
170 }
171 }
172 }
173 }
174
175 #[inline]
176 fn simd_lt(self, rhs: Self) -> Self::Output {
177 pick! {
178 if #[cfg(target_feature="avx512f")] {
179 Self { avx512: cmp_op_mask_u64_m512i::<{cmp_int_op!(Lt)}>(self.avx512, rhs.avx512) }
180 } else {
181 Self {
182 a : self.a.simd_lt(rhs.a),
183 b : self.b.simd_lt(rhs.b),
184 }
185 }
186 }
187 }
188
189 #[inline]
190 fn simd_gt(self, rhs: Self) -> Self::Output {
191 pick! {
192 if #[cfg(target_feature="avx512f")] {
193 Self { avx512: cmp_op_mask_u64_m512i::<{cmp_int_op!(Nle)}>(self.avx512, rhs.avx512) }
194 } else {
195 Self {
196 a : self.a.simd_gt(rhs.a),
197 b : self.b.simd_gt(rhs.b),
198 }
199 }
200 }
201 }
202
203 #[inline]
204 fn simd_le(self, rhs: Self) -> Self::Output {
205 pick! {
206 if #[cfg(target_feature="avx512f")] {
207 Self { avx512: cmp_op_mask_u64_m512i::<{cmp_int_op!(Le)}>(self.avx512, rhs.avx512) }
208 } else {
209 Self {
210 a : self.a.simd_le(rhs.a),
211 b : self.b.simd_le(rhs.b),
212 }
213 }
214 }
215 }
216
217 #[inline]
218 fn simd_ge(self, rhs: Self) -> Self::Output {
219 pick! {
220 if #[cfg(target_feature="avx512f")] {
221 Self { avx512: cmp_op_mask_u64_m512i::<{cmp_int_op!(Nlt)}>(self.avx512, rhs.avx512) }
222 } else {
223 Self {
224 a : self.a.simd_ge(rhs.a),
225 b : self.b.simd_ge(rhs.b),
226 }
227 }
228 }
229 }
230
231 #[inline]
232 pub fn reduce_add(self) -> u64 {
233 let array: [u64x4; 2] = cast(self);
234 (array[0] + array[1]).reduce_add()
235 }
236
237
238 #[inline]
239 pub fn reduce_mul(self) -> u64 {
240 let array: [u64x4; 2] = cast(self);
241 (array[0] * array[1]).reduce_mul()
242 }
243
244 #[inline]
245 pub fn bitselect(self, if_one: Self, if_zero: Self) -> Self {
246 pick! {
247 if #[cfg(target_feature="avx512f")] {
248 Self {
249 avx512: bitor_m512i(
250 bitand_m512i(if_one.avx512, self.avx512),
251 bitandnot_m512i(self.avx512, if_zero.avx512),
252 ),
253 }
254 } else {
255 Self {
256 a: self.a.bitselect(if_one.a, if_zero.a),
257 b: self.b.bitselect(if_one.b, if_zero.b),
258 }
259 }
260 }
261 }
262
263 #[inline]
264 fn select(self, if_true: Self, if_false: Self) -> Self {
265 pick! {
266 if #[cfg(target_feature="avx512f")] {
267 Self { avx512: blend_varying_i8_m512i(if_false.avx512,if_true.avx512,movepi8_mask_m512i(self.avx512)) }
268 } else {
269 Self {
270 a : self.a.select(if_true.a, if_false.a),
271 b : self.b.select(if_true.b, if_false.b),
272 }
273 }
274 }
275 }
276
277 #[inline]
278 pub fn to_bitmask(self) -> u32 {
279 pick! {
280 if #[cfg(target_feature="avx512dq")] {
281 movepi64_mask_m512d(cast(self.avx512)) as u32
283 } else {
284 f64x8::to_bitmask(cast(self))
285 }
286 }
287 }
288
289 #[inline]
290 pub fn any(self) -> bool {
291 pick! {
292 if #[cfg(target_feature="avx512f")] {
293 movepi64_mask_m512d(cast(self.avx512)) != 0
294 } else {
295 let [a, b]: [i64x4; 2] = cast(self);
296 (a | b).any()
297 }
298 }
299 }
300
301 #[inline]
302 pub fn all(self) -> bool {
303 pick! {
304 if #[cfg(target_feature="avx512bw")] {
305 movepi64_mask_m512d(cast(self.avx512)) == 0b11111111
306 } else {
307 let [a, b]: [i64x4; 2] = cast(self);
308 (a & b).all()
309 }
310 }
311 }
312
313 #[inline]
314 pub fn shuffle(self, indices: u64x8) -> Self {
315 pick! {
316 if #[cfg(all(target_feature = "avx512f"))] {
317 Self { avx512: permute_i64_m512i(indices.avx512, self.avx512) }
318 } else {
319 let self_halfs = cast::<u64x8, [u64x4; 2]>(self);
320 let [indices_a, indices_b] = cast::<u64x8, [u64x4; 2]>(indices);
321
322 cast([self_halfs.shuffle(indices_a), self_halfs.shuffle(indices_b)])
323 }
324 }
325 }
326
327 #[inline]
328 pub fn shuffle_zeroing(self, indices: u64x8) -> Self {
329 pick! {
330 if #[cfg(all(target_feature = "avx512f"))] {
331 self.shuffle(indices) & indices.simd_lt(8)
332 } else {
333 let self_halfs = cast::<u64x8, [u64x4; 2]>(self);
334 let [indices_a, indices_b] = cast::<u64x8, [u64x4; 2]>(indices);
335
336 cast([self_halfs.shuffle_zeroing(indices_a), self_halfs.shuffle_zeroing(indices_b)])
337 }
338 }
339 }
340
341 #[inline]
342 pub fn shuffle_wrapping(self, indices: u64x8) -> Self {
343 pick! {
344 if #[cfg(all(target_feature = "avx512f"))] {
345 self.shuffle(indices)
347 } else {
348 let self_halfs = cast::<u64x8, [u64x4; 2]>(self);
349 let [indices_a, indices_b] = cast::<u64x8, [u64x4; 2]>(indices);
350
351 cast([self_halfs.shuffle_wrapping(indices_a), self_halfs.shuffle_wrapping(indices_b)])
352 }
353 }
354 }
355
356 #[inline]
357 fn shuffle(self: [u64x8; 2], indices: u64x8) -> u64x8 {
358 pick! {
359 if #[cfg(all(target_feature = "avx512f"))] {
360 u64x8 { avx512: shuffle_abv_i64_all_m512i(self[0].avx512, indices.avx512, self[1].avx512) }
361 } else {
362 self[0].shuffle_zeroing(indices) | self[1].shuffle_zeroing(indices - 8)
363 }
364 }
365 }
366
367 #[inline]
368 fn shuffle_zeroing(self: [u64x8; 2], indices: u64x8) -> u64x8 {
369 pick! {
370 if #[cfg(all(target_feature = "avx512f"))] {
371 self.shuffle(indices) & indices.simd_lt(16)
372 } else {
373 self.shuffle(indices)
374 }
375 }
376 }
377
378 #[inline]
379 fn shuffle_wrapping(self: [u64x8; 2], indices: u64x8) -> u64x8 {
380 pick! {
381 if #[cfg(target_feature = "avx512f")] {
382 self.shuffle(indices)
384 } else {
385 self.shuffle(indices & 15)
386 }
387 }
388 }
389
390 #[inline]
391 fn shuffle(self: [u64x8; 3], indices: u64x8) -> u64x8 {
392 [self[0], self[1]].shuffle_zeroing(indices) | self[2].shuffle_zeroing(indices - 16)
393 }
394
395 #[inline]
396 fn shuffle_zeroing(self: [u64x8; 3], indices: u64x8) -> u64x8 {
397 self.shuffle(indices)
398 }
399
400 #[inline]
401 fn shuffle_wrapping(self: [u64x8; 3], indices: u64x8) -> u64x8 {
402 self.shuffle(indices % 24)
403 }
404
405 #[inline]
406 fn shuffle(self: [u64x8; 4], indices: u64x8) -> u64x8 {
407 [self[0], self[1]].shuffle_zeroing(indices) | [self[2], self[3]].shuffle_zeroing(indices - 16)
408 }
409
410 #[inline]
411 fn shuffle_zeroing(self: [u64x8; 4], indices: u64x8) -> u64x8 {
412 self.shuffle(indices)
413 }
414
415 #[inline]
416 fn shuffle_wrapping(self: [u64x8; 4], indices: u64x8) -> u64x8 {
417 self.shuffle(indices & 31)
418 }
419
420 #[inline]
423 pub fn transpose(data: [Self; 8]) -> [Self; 8] {
424 #[inline(always)]
427 fn transpose_column(data: &[u64x8; 8], index: usize) -> u64x8 {
428 u64x8::new([
429 data[0].as_array()[index],
430 data[1].as_array()[index],
431 data[2].as_array()[index],
432 data[3].as_array()[index],
433 data[4].as_array()[index],
434 data[5].as_array()[index],
435 data[6].as_array()[index],
436 data[7].as_array()[index],
437 ])
438 }
439
440 [
441 transpose_column(&data, 0),
442 transpose_column(&data, 1),
443 transpose_column(&data, 2),
444 transpose_column(&data, 3),
445 transpose_column(&data, 4),
446 transpose_column(&data, 5),
447 transpose_column(&data, 6),
448 transpose_column(&data, 7),
449 ]
450 }
451
452 #[inline]
453 fn shl(self, rhs: Self) -> Self::Output {
454 pick! {
455 if #[cfg(target_feature="avx512f")] {
456 let rhs = bitand_m512i(rhs.avx512, set_splat_i64_m512i(63));
458 Self { avx512: shl_each_u64_m512i(self.avx512, rhs) }
459 } else {
460 Self {
461 a : self.a.shl(rhs.a),
462 b : self.b.shl(rhs.b),
463 }
464 }
465 }
466 }
467
468 #[inline]
469 fn shl(self, rhs: u32) -> Self::Output {
470 pick! {
471 if #[cfg(target_feature="avx512f")] {
472 #[expect(clippy::suspicious_arithmetic_impl)]
474 let shift = rhs as u64 & 63;
475 Self { avx512: shl_all_u64_m512i(self.avx512, shift) }
476 } else {
477 Self {
478 a : self.a.shl(rhs),
479 b : self.b.shl(rhs),
480 }
481 }
482 }
483 }
484
485 #[inline]
486 fn shr(self, rhs: Self) -> Self::Output {
487 pick! {
488 if #[cfg(target_feature="avx512f")] {
489 let rhs = bitand_m512i(rhs.avx512, set_splat_i64_m512i(63));
491 Self { avx512: shr_each_u64_m512i(self.avx512, rhs) }
492 } else {
493 Self {
494 a : self.a.shr(rhs.a),
495 b : self.b.shr(rhs.b),
496 }
497 }
498 }
499 }
500
501 #[inline]
502 fn shr(self, rhs: u32) -> Self::Output {
503 pick! {
504 if #[cfg(target_feature="avx512f")] {
505 #[expect(clippy::suspicious_arithmetic_impl)]
507 let shift = rhs as u64 & 63;
508 Self { avx512: shr_all_u64_m512i(self.avx512, shift) }
509 } else {
510 Self {
511 a : self.a.shr(rhs),
512 b : self.b.shr(rhs),
513 }
514 }
515 }
516 }
517
518 #[inline]
519 pub fn max(self, rhs: Self) -> Self {
520 pick! {
521 if #[cfg(target_feature="avx512f")] {
522 Self { avx512: max_u64_m512i(self.avx512, rhs.avx512) }
523 } else {
524 Self {
525 a: self.a.max(rhs.a),
526 b: self.b.max(rhs.b),
527 }
528 }
529 }
530 }
531
532 #[inline]
533 pub fn min(self, rhs: Self) -> Self {
534 pick! {
535 if #[cfg(target_feature="avx512f")] {
536 Self { avx512: min_u64_m512i(self.avx512, rhs.avx512) }
537 } else {
538 Self {
539 a: self.a.min(rhs.a),
540 b: self.b.min(rhs.b),
541 }
542 }
543 }
544 }
545
546 #[inline]
547 pub fn reduce_max(self) -> u64 {
548 let array: [u64x4; 2] = cast(self);
549 array[0].max(array[1]).reduce_max()
550 }
551
552 #[inline]
553 pub fn reduce_min(self) -> u64 {
554 let array: [u64x4; 2] = cast(self);
555 array[0].min(array[1]).reduce_min()
556 }
557
558 #[inline]
559 pub fn unbounded_shl(self, rhs: Self) -> Self {
560 pick! {
561 if #[cfg(target_feature="avx512f")] {
562 Self { avx512: shl_each_u64_m512i(self.avx512, rhs.avx512) }
563 } else {
564 Self {
565 a: self.a.unbounded_shl(rhs.a),
566 b: self.b.unbounded_shl(rhs.b),
567 }
568 }
569 }
570 }
571
572 #[inline]
573 pub fn unbounded_shl_scalar(self, rhs: u32) -> Self {
574 pick! {
575 if #[cfg(target_feature="avx512f")] {
576 Self { avx512: shl_all_u64_m512i(self.avx512, rhs as u64) }
577 } else {
578 Self {
579 a: self.a.unbounded_shl_scalar(rhs),
580 b: self.b.unbounded_shl_scalar(rhs),
581 }
582 }
583 }
584 }
585
586 #[inline]
587 pub fn unbounded_shr(self, rhs: Self) -> Self {
588 pick! {
589 if #[cfg(target_feature="avx512f")] {
590 Self { avx512: shr_each_u64_m512i(self.avx512, rhs.avx512) }
591 } else {
592 Self {
593 a: self.a.unbounded_shr(rhs.a),
594 b: self.b.unbounded_shr(rhs.b),
595 }
596 }
597 }
598 }
599
600 #[inline]
601 pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
602 pick! {
603 if #[cfg(target_feature="avx512f")] {
604 Self { avx512: shr_all_u64_m512i(self.avx512, rhs as u64) }
605 } else {
606 Self {
607 a: self.a.unbounded_shr_scalar(rhs),
608 b: self.b.unbounded_shr_scalar(rhs),
609 }
610 }
611 }
612 }
613
614 #[inline]
615 pub fn saturating_add(self, rhs: Self) -> Self {
616 pick! {
617 if #[cfg(target_feature="avx512f")] {
618 let result = self + rhs;
619 let overflow = result.simd_lt(self);
620 result | overflow
622 } else {
623 Self {
624 a: self.a.saturating_add(rhs.a),
625 b: self.b.saturating_add(rhs.b),
626 }
627 }
628 }
629 }
630
631 #[inline]
632 pub fn saturating_sub(self, rhs: Self) -> Self {
633 pick! {
634 if #[cfg(target_feature="avx512f")] {
635 let result = self - rhs;
636 let no_overflow = result.simd_le(self);
637 result & no_overflow
639 } else {
640 Self {
641 a: self.a.saturating_sub(rhs.a),
642 b: self.b.saturating_sub(rhs.b),
643 }
644 }
645 }
646 }
647
648 #[inline]
649 pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
650 let self_array = self.to_array();
655 let rhs_array = rhs.to_array();
656
657 let result = [
658 self_array[0].overflowing_mul(rhs_array[0]),
659 self_array[1].overflowing_mul(rhs_array[1]),
660 self_array[2].overflowing_mul(rhs_array[2]),
661 self_array[3].overflowing_mul(rhs_array[3]),
662 self_array[4].overflowing_mul(rhs_array[4]),
663 self_array[5].overflowing_mul(rhs_array[5]),
664 self_array[6].overflowing_mul(rhs_array[6]),
665 self_array[7].overflowing_mul(rhs_array[7]),
666 ];
667 (
668 Self::new([
669 result[0].0,
670 result[1].0,
671 result[2].0,
672 result[3].0,
673 result[4].0,
674 result[5].0,
675 result[6].0,
676 result[7].0,
677 ]),
678 Self::new([
679 -(result[0].1 as i64) as u64,
680 -(result[1].1 as i64) as u64,
681 -(result[2].1 as i64) as u64,
682 -(result[3].1 as i64) as u64,
683 -(result[4].1 as i64) as u64,
684 -(result[5].1 as i64) as u64,
685 -(result[6].1 as i64) as u64,
686 -(result[7].1 as i64) as u64,
687 ]),
688 )
689 }
690
691 optional_fn_widening_mul {
692 }
694
695 #[inline]
696 pub fn mul_keep_low_high(self, rhs: Self) -> (Self, Self) {
697 let self_array = self.to_array();
701 let rhs_array = rhs.to_array();
702
703 let widening_mul = [
704 (self_array[0] as u128).wrapping_mul(rhs_array[0] as u128),
705 (self_array[1] as u128).wrapping_mul(rhs_array[1] as u128),
706 (self_array[2] as u128).wrapping_mul(rhs_array[2] as u128),
707 (self_array[3] as u128).wrapping_mul(rhs_array[3] as u128),
708 (self_array[4] as u128).wrapping_mul(rhs_array[4] as u128),
709 (self_array[5] as u128).wrapping_mul(rhs_array[5] as u128),
710 (self_array[6] as u128).wrapping_mul(rhs_array[6] as u128),
711 (self_array[7] as u128).wrapping_mul(rhs_array[7] as u128),
712 ];
713
714 (
715 Self::new([
716 widening_mul[0] as u64,
717 widening_mul[1] as u64,
718 widening_mul[2] as u64,
719 widening_mul[3] as u64,
720 widening_mul[4] as u64,
721 widening_mul[5] as u64,
722 widening_mul[6] as u64,
723 widening_mul[7] as u64,
724 ]),
725 Self::new([
726 (widening_mul[0] >> 64) as u64,
727 (widening_mul[1] >> 64) as u64,
728 (widening_mul[2] >> 64) as u64,
729 (widening_mul[3] >> 64) as u64,
730 (widening_mul[4] >> 64) as u64,
731 (widening_mul[5] >> 64) as u64,
732 (widening_mul[6] >> 64) as u64,
733 (widening_mul[7] >> 64) as u64,
734 ]),
735 )
736 }
737
738 #[inline]
739 pub fn mul_keep_high(self, rhs: Self) -> Self {
740 pick! {
741 if #[cfg(target_feature="avx512f")] {
742 let arr1: [u64; 8] = cast(self);
743 let arr2: [u64; 8] = cast(rhs);
744 cast([
745 (arr1[0] as u128 * arr2[0] as u128 >> 64) as u64,
746 (arr1[1] as u128 * arr2[1] as u128 >> 64) as u64,
747 (arr1[2] as u128 * arr2[2] as u128 >> 64) as u64,
748 (arr1[3] as u128 * arr2[3] as u128 >> 64) as u64,
749 (arr1[4] as u128 * arr2[4] as u128 >> 64) as u64,
750 (arr1[5] as u128 * arr2[5] as u128 >> 64) as u64,
751 (arr1[6] as u128 * arr2[6] as u128 >> 64) as u64,
752 (arr1[7] as u128 * arr2[7] as u128 >> 64) as u64,
753 ])
754 } else {
755 Self {
756 a: self.a.mul_keep_high(rhs.a),
757 b: self.b.mul_keep_high(rhs.b),
758 }
759 }
760 }
761 }
762
763 optional_fn_deserialize {}
764}
765
766impl u64x8 {
769 #[inline]
772 #[must_use]
773 pub fn unpack_lo(self, b: Self) -> Self {
774 pick! {
775 if #[cfg(target_feature="avx512f")] {
776 let [aa, _]: [u64x4; 2] = cast(self);
779 let [ba, _]: [u64x4; 2] = cast(b);
780 cast([aa.unpack_lo(ba), aa.unpack_hi(ba)])
781 } else {
782 Self { a: self.a.unpack_lo(b.a), b: self.a.unpack_hi(b.a) }
783 }
784 }
785 }
786
787 #[inline]
790 #[must_use]
791 pub fn unpack_hi(self, b: Self) -> Self {
792 pick! {
793 if #[cfg(target_feature="avx512f")] {
794 let [_, ab]: [u64x4; 2] = cast(self);
797 let [_, bb]: [u64x4; 2] = cast(b);
798 cast([ab.unpack_lo(bb), ab.unpack_hi(bb)])
799 } else {
800 Self { a: self.b.unpack_lo(b.b), b: self.b.unpack_hi(b.b) }
801 }
802 }
803 }
804
805 #[inline]
811 #[must_use]
812 fn mul_masked<const W: u32>(a: Self, b: Self) -> Self {
813 pick! {
814 if #[cfg(target_feature="avx512f")] {
815 let (a, b) = if W == 32 {
818 (a, b)
819 } else {
820 let mask = Self::splat(add_mul_operand_mask_u64::<W>());
821 (a & mask, b & mask)
822 };
823
824 Self { avx512: mul_u32_wide_m512i(a.avx512, b.avx512) }
825 } else {
826 Self {
828 a: u64x4::mul_masked::<W>(a.a, b.a),
829 b: u64x4::mul_masked::<W>(a.b, b.b),
830 }
831 }
832 }
833 }
834
835 #[inline]
838 #[must_use]
839 pub fn add_mul_lo<const W: u32>(self, a: Self, b: Self) -> Self {
840 pick! {
841 if #[cfg(target_feature="avx512ifma")] {
842 if W == 52 {
844 return Self {
845 avx512: add_mul_low_u52_m512i(self.avx512, a.avx512, b.avx512),
846 };
847 }
848 }
849 }
850
851 if W <= 32 {
854 let mask = Self::splat(add_mul_operand_mask_u64::<W>());
855 return self + (Self::mul_masked::<W>(a, b) & mask);
856 }
857
858 let acc = self.to_array();
859 let a = a.to_array();
860 let b = b.to_array();
861 Self::new(core::array::from_fn(|i| {
862 add_mul_lo_lane_u64::<W>(acc[i], a[i], b[i])
863 }))
864 }
865
866 #[inline]
869 #[must_use]
870 pub fn add_mul_hi<const W: u32>(self, a: Self, b: Self) -> Self {
871 pick! {
872 if #[cfg(target_feature="avx512ifma")] {
873 if W == 52 {
875 return Self {
876 avx512: add_mul_high_u52_m512i(self.avx512, a.avx512, b.avx512),
877 };
878 }
879 }
880 }
881
882 if W <= 32 {
885 return self + (Self::mul_masked::<W>(a, b) >> W);
886 }
887
888 let acc = self.to_array();
889 let a = a.to_array();
890 let b = b.to_array();
891 Self::new(core::array::from_fn(|i| {
892 add_mul_hi_lane_u64::<W>(acc[i], a[i], b[i])
893 }))
894 }
895}