1use super::*;
2
3pick! {
4 if #[cfg(target_feature="sse2")] {
5 #[derive(Default, Clone, Copy, PartialEq, Eq)]
12 #[repr(C, align(16))]
13 pub struct u64x2 { pub(crate) sse: m128i }
14 } else if #[cfg(target_feature="simd128")] {
15 use core::arch::wasm32::*;
16
17 #[derive(Clone, Copy)]
24 #[repr(transparent)]
25 pub struct u64x2 { pub(crate) simd: v128 }
26
27 impl Default for u64x2 {
28 fn default() -> Self {
29 Self::splat(0)
30 }
31 }
32
33 impl PartialEq for u64x2 {
34 fn eq(&self, other: &Self) -> bool {
35 u64x2_all_true(u64x2_eq(self.simd, other.simd))
36 }
37 }
38
39 impl Eq for u64x2 { }
40 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
41 use core::arch::aarch64::*;
42
43 #[repr(C)]
50 #[derive(Copy, Clone)]
51 pub struct u64x2 { pub(crate) neon : uint64x2_t }
52
53 impl Default for u64x2 {
54 #[inline]
55 fn default() -> Self {
56 unsafe { Self { neon: vdupq_n_u64(0)} }
57 }
58 }
59
60 impl PartialEq for u64x2 {
61 #[inline]
62 fn eq(&self, other: &Self) -> bool {
63 unsafe {
64 vgetq_lane_u64(self.neon,0) == vgetq_lane_u64(other.neon,0) &&
65 vgetq_lane_u64(self.neon,1) == vgetq_lane_u64(other.neon,1)
66 }
67 }
68 }
69
70 impl Eq for u64x2 { }
71 } else {
72 #[derive(Default, Clone, Copy, PartialEq, Eq)]
79 #[repr(C, align(16))]
80 pub struct u64x2 { arr: [u64;2] }
81 }
82}
83
84impl_simd_uint! {
85 unsafe {
86 T = u64,
87 N = 2,
88 Simd = u64x2,
89 IntSimd = i64x2,
90 T_BITS = 64,
91 T_BITS_MUL_2 = 128,
92 [0, 1],
93 optional_type_x86_inner { X86Inner = __m128i },
94 optional_type_arm_inner { ArmInner = uint64x2_t },
95 optional_type_wasm_inner { WasmInner = v128 },
96 }
97
98 #[inline]
99 fn not(self) -> Self::Output {
100 self ^ cast::<u128, u64x2>(u128::MAX)
101 }
102
103 #[inline]
104 fn add(self, rhs: Self) -> Self::Output {
105 pick! {
106 if #[cfg(target_feature="sse2")] {
107 Self { sse: add_i64_m128i(self.sse, rhs.sse) }
108 } else if #[cfg(target_feature="simd128")] {
109 Self { simd: u64x2_add(self.simd, rhs.simd) }
110 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
111 unsafe { Self { neon: vaddq_u64(self.neon, rhs.neon) } }
112 } else {
113 Self { arr: [
114 self.arr[0].wrapping_add(rhs.arr[0]),
115 self.arr[1].wrapping_add(rhs.arr[1]),
116 ]}
117 }
118 }
119 }
120
121 #[inline]
122 fn sub(self, rhs: Self) -> Self::Output {
123 pick! {
124 if #[cfg(target_feature="sse2")] {
125 Self { sse: sub_i64_m128i(self.sse, rhs.sse) }
126 } else if #[cfg(target_feature="simd128")] {
127 Self { simd: u64x2_sub(self.simd, rhs.simd) }
128 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
129 unsafe { Self { neon: vsubq_u64(self.neon, rhs.neon) } }
130 } else {
131 Self { arr: [
132 self.arr[0].wrapping_sub(rhs.arr[0]),
133 self.arr[1].wrapping_sub(rhs.arr[1]),
134 ]}
135 }
136 }
137 }
138
139 #[inline]
140 fn mul(self, rhs: Self) -> Self::Output {
141 pick! {
143 if #[cfg(target_feature="simd128")] {
144 Self { simd: u64x2_mul(self.simd, rhs.simd) }
145 } else {
146 let arr1: [u64; 2] = cast(self);
147 let arr2: [u64; 2] = cast(rhs);
148 cast([
149 arr1[0].wrapping_mul(arr2[0]),
150 arr1[1].wrapping_mul(arr2[1]),
151 ])
152 }
153 }
154 }
155
156 #[inline]
157 fn bitand(self, rhs: Self) -> Self::Output {
158 pick! {
159 if #[cfg(target_feature="sse2")] {
160 Self { sse: bitand_m128i(self.sse, rhs.sse) }
161 } else if #[cfg(target_feature="simd128")] {
162 Self { simd: v128_and(self.simd, rhs.simd) }
163 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
164 unsafe {Self { neon: vandq_u64(self.neon, rhs.neon) }}
165 } else {
166 Self { arr: [
167 self.arr[0].bitand(rhs.arr[0]),
168 self.arr[1].bitand(rhs.arr[1]),
169 ]}
170 }
171 }
172 }
173
174 #[inline]
175 fn bitor(self, rhs: Self) -> Self::Output {
176 pick! {
177 if #[cfg(target_feature="sse2")] {
178 Self { sse: bitor_m128i(self.sse, rhs.sse) }
179 } else if #[cfg(target_feature="simd128")] {
180 Self { simd: v128_or(self.simd, rhs.simd) }
181 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
182 unsafe {Self { neon: vorrq_u64(self.neon, rhs.neon) }}
183 } else {
184 Self { arr: [
185 self.arr[0].bitor(rhs.arr[0]),
186 self.arr[1].bitor(rhs.arr[1]),
187 ]}
188 }
189 }
190 }
191
192 #[inline]
193 fn bitxor(self, rhs: Self) -> Self::Output {
194 pick! {
195 if #[cfg(target_feature="sse2")] {
196 Self { sse: bitxor_m128i(self.sse, rhs.sse) }
197 } else if #[cfg(target_feature="simd128")] {
198 Self { simd: v128_xor(self.simd, rhs.simd) }
199 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
200 unsafe {Self { neon: veorq_u64(self.neon, rhs.neon) }}
201 } else {
202 Self { arr: [
203 self.arr[0].bitxor(rhs.arr[0]),
204 self.arr[1].bitxor(rhs.arr[1]),
205 ]}
206 }
207 }
208 }
209
210 #[inline]
211 fn simd_eq(self, rhs: Self) -> Self::Output {
212 pick! {
213 if #[cfg(target_feature="sse4.1")] {
214 Self { sse: cmp_eq_mask_i64_m128i(self.sse, rhs.sse) }
215 } else if #[cfg(target_feature="simd128")] {
216 Self { simd: u64x2_eq(self.simd, rhs.simd) }
217 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
218 unsafe {Self { neon: vceqq_u64(self.neon, rhs.neon) } }
219 } else {
220 let s: [u64;2] = cast(self);
221 let r: [u64;2] = cast(rhs);
222 cast([
223 if s[0] == r[0] { -1_i64 } else { 0 },
224 if s[1] == r[1] { -1_i64 } else { 0 },
225 ])
226 }
227 }
228 }
229
230 #[inline]
231 fn simd_ne(self, rhs: Self) -> Self::Output {
232 pick! {
233 if #[cfg(target_feature="sse4.1")] {
234 !self.simd_eq(rhs)
235 } else if #[cfg(target_feature="simd128")] {
236 Self { simd: u64x2_ne(self.simd, rhs.simd) }
237 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
238 !self.simd_eq(rhs)
239 } else {
240 let s: [u64;2] = cast(self);
241 let r: [u64;2] = cast(rhs);
242 cast([
243 if s[0] != r[0] { -1_i64 } else { 0 },
244 if s[1] != r[1] { -1_i64 } else { 0 },
245 ])
246 }
247 }
248 }
249
250 #[inline]
251 fn simd_lt(self, rhs: Self) -> Self::Output {
252 rhs.simd_gt(self)
254 }
255
256 #[inline]
257 fn simd_gt(self, rhs: Self) -> Self::Output {
258 pick! {
259 if #[cfg(target_feature="sse4.2")] {
260 let highbit = u64x2::splat(1 << 63);
262 Self { sse: cmp_gt_mask_i64_m128i((self ^ highbit).sse, (rhs ^ highbit).sse) }
263 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
264 unsafe {Self { neon: vcgtq_u64(self.neon, rhs.neon) }}
265 } else {
266 let s: [u64;2] = cast(self);
268 let r: [u64;2] = cast(rhs);
269 cast([
270 if s[0] > r[0] { u64::MAX } else { 0 },
271 if s[1] > r[1] { u64::MAX } else { 0 },
272 ])
273 }
274 }
275 }
276
277 #[inline]
278 fn simd_le(self, rhs: Self) -> Self::Output {
279 pick! {
280 if #[cfg(target_feature="sse4.1")] {
281 !self.simd_gt(rhs)
282 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
283 !self.simd_gt(rhs)
284 } else {
285 let s: [u64;2] = cast(self);
286 let r: [u64;2] = cast(rhs);
287 cast([
288 if s[0] <= r[0] { -1_i64 } else { 0 },
289 if s[1] <= r[1] { -1_i64 } else { 0 },
290 ])
291 }
292 }
293 }
294
295 #[inline]
296 fn simd_ge(self, rhs: Self) -> Self::Output {
297 pick! {
298 if #[cfg(target_feature="sse4.1")] {
299 !self.simd_lt(rhs)
300 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
301 !self.simd_lt(rhs)
302 } else {
303 let s: [u64;2] = cast(self);
304 let r: [u64;2] = cast(rhs);
305 cast([
306 if s[0] >= r[0] { -1_i64 } else { 0 },
307 if s[1] >= r[1] { -1_i64 } else { 0 },
308 ])
309 }
310 }
311 }
312
313 #[inline]
314 pub fn reduce_add(self) -> u64 {
315 pick! {
316 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
317 let array: [u64; 2] = cast(self);
318 array[0].wrapping_add(array[1])
319 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
320 unsafe { vgetq_lane_u64(self.neon, 0).wrapping_add(vgetq_lane_u64(self.neon, 1)) }
321 } else {
322 self.arr[0].wrapping_add(self.arr[1])
323 }
324 }
325 }
326
327 #[inline]
328 pub fn reduce_mul(self) -> u64 {
329 pick! {
330 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
331 let array: [u64; 2] = cast(self);
332 array[0].wrapping_mul(array[1])
333 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
334 unsafe { vgetq_lane_u64(self.neon, 0).wrapping_mul(vgetq_lane_u64(self.neon, 1)) }
335 } else {
336 self.arr[0].wrapping_mul(self.arr[1])
337 }
338 }
339 }
340
341 #[inline]
342 pub fn bitselect(self, if_one: Self, if_zero: Self) -> Self {
343 pick! {
344 if #[cfg(target_feature="sse2")] {
345 Self {
346 sse: bitor_m128i(
347 bitand_m128i(if_one.sse, self.sse),
348 bitandnot_m128i(self.sse, if_zero.sse),
349 ),
350 }
351 } else if #[cfg(target_feature="simd128")] {
352 Self { simd: v128_bitselect(if_one.simd, if_zero.simd, self.simd) }
353 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
354 unsafe {Self { neon: vbslq_u64(self.neon, if_one.neon, if_zero.neon) }}
355 } else {
356 generic_bit_blend(self, if_one, if_zero)
357 }
358 }
359 }
360
361 #[inline]
362 fn select(self, if_true: Self, if_false: Self) -> Self {
363 pick! {
364 if #[cfg(target_feature="sse4.1")] {
365 Self { sse: blend_varying_i8_m128i(if_false.sse, if_true.sse, self.sse) }
366 } else if #[cfg(target_feature="simd128")] {
367 Self { simd: v128_bitselect(if_true.simd, if_false.simd, self.simd) }
368 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
369 unsafe {Self { neon: vbslq_u64(self.neon, if_true.neon, if_false.neon) }}
370 } else {
371 generic_bit_blend(self, if_true, if_false)
372 }
373 }
374 }
375
376 #[inline]
377 pub fn to_bitmask(self) -> u32 {
378 pick! {
379 if #[cfg(target_feature="sse")] {
380 move_mask_m128d(cast(self.sse)) as u32
382 } else if #[cfg(target_feature="simd128")] {
383 i64x2_bitmask(self.simd) as u32
384 } else {
385 let arr: [u64; 2] = cast(self);
387 (arr[0] >> 63 | ((arr[1] >> 62) & 2)) as u32
388 }
389 }
390 }
391
392 #[inline]
393 pub fn any(self) -> bool {
394 pick! {
395 if #[cfg(target_feature="sse")] {
396 move_mask_m128d(cast(self.sse)) != 0
398 } else if #[cfg(target_feature="simd128")] {
399 i64x2_bitmask(self.simd) != 0
400 } else {
401 let v : [u64;2] = cast(self);
402 ((v[0] | v[1]) & 0x8000000000000000) != 0
403 }
404 }
405 }
406
407 #[inline]
408 pub fn all(self) -> bool {
409 pick! {
410 if #[cfg(target_feature="avx2")] {
411 move_mask_m128d(cast(self.sse)) == 0b11
413 } else if #[cfg(target_feature="simd128")] {
414 i64x2_bitmask(self.simd) == 0b11
415 } else {
416 let v : [u64;2] = cast(self);
417 ((v[0] & v[1]) & 0x8000000000000000) == 0x8000000000000000
418 }
419 }
420 }
421
422 #[inline]
423 pub fn shuffle(self, indices: u64x2) -> Self {
424 pick! {
425 if #[cfg(target_feature = "sse2")] {
426 let e0 = Self { sse: shuffle_ai_f32_all_m128i::<0b01_00_01_00>(self.sse) };
427 let e1 = Self { sse: shuffle_ai_f32_all_m128i::<0b11_10_11_10>(self.sse) };
428
429 (-indices).select(e1, e0)
432 } else if #[cfg(target_feature = "simd128")] {
433 let e0 = Self { simd: u64x2_shuffle::<0, 0>(self.simd, self.simd) };
434 let e1 = Self { simd: u64x2_shuffle::<1, 1>(self.simd, self.simd) };
435
436 (-indices).select(e1, e0)
439 } else if #[cfg(all(target_feature = "neon", target_arch = "aarch64"))]{
440 let e0 = unsafe { Self { neon: vdupq_n_u64(vget_lane_u64::<0>(vget_low_u64(self.neon))) } };
441 let e1 = unsafe { Self { neon: vdupq_n_u64(vget_lane_u64::<0>(vget_high_u64(self.neon))) } };
442
443 (-indices).select(e1, e0)
446 } else {
447 let e0 = Self::splat(self.as_array()[0]);
448 let e1 = Self::splat(self.as_array()[1]);
449
450 (-indices).select(e1, e0)
453 }
454 }
455 }
456
457 #[inline]
458 pub fn shuffle_zeroing(self, indices: u64x2) -> Self {
459 self.shuffle(indices) & indices.simd_lt(2)
461 }
462
463 #[inline]
464 pub fn shuffle_wrapping(self, indices: u64x2) -> Self {
465 self.shuffle(indices & 1)
467 }
468
469 #[inline]
470 fn shuffle(self: [u64x2; 2], indices: u64x2) -> u64x2 {
471 pick! {
472 if #[cfg(all(target_feature = "avx512f", target_feature = "avx512vl"))] {
473 u64x2 { sse: shuffle_abv_i64_all_m128i(self[0].sse, indices.sse, self[1].sse) }
474 } else {
475 let self_bytes = cast::<[u64x2; 2], [u8x16; 2]>(self);
476 let byte_indices = indices.to_byte_indices();
477
478 cast::<u8x16, u64x2>(self_bytes.shuffle(byte_indices))
479 }
480 }
481 }
482
483 #[inline]
484 fn shuffle_zeroing(self: [u64x2; 2], indices: u64x2) -> u64x2 {
485 self.shuffle(indices) & indices.simd_lt(4)
488 }
489
490 #[inline]
491 fn shuffle_wrapping(self: [u64x2; 2], indices: u64x2) -> u64x2 {
492 pick! {
493 if #[cfg(all(target_feature = "avx512f", target_feature = "avx512vl"))] {
494 self.shuffle(indices)
496 } else {
497 self.shuffle(indices & 3)
498 }
499 }
500 }
501
502 #[inline]
503 fn shuffle(self: [u64x2; 3], indices: u64x2) -> u64x2 {
504 let self_bytes = cast::<[u64x2; 3], [u8x16; 3]>(self);
505 let byte_indices = indices.to_byte_indices();
506
507 cast::<u8x16, u64x2>(self_bytes.shuffle(byte_indices))
508 }
509
510 #[inline]
511 fn shuffle_zeroing(self: [u64x2; 3], indices: u64x2) -> u64x2 {
512 self.shuffle(indices) & indices.simd_lt(6)
515 }
516
517 #[inline]
518 fn shuffle_wrapping(self: [u64x2; 3], indices: u64x2) -> u64x2 {
519 self.shuffle(indices % 6)
520 }
521
522 #[inline]
523 fn shuffle(self: [u64x2; 4], indices: u64x2) -> u64x2 {
524 let self_bytes = cast::<[u64x2; 4], [u8x16; 4]>(self);
525 let byte_indices = indices.to_byte_indices();
526
527 cast::<u8x16, u64x2>(self_bytes.shuffle(byte_indices))
528 }
529
530 #[inline]
531 fn shuffle_zeroing(self: [u64x2; 4], indices: u64x2) -> u64x2 {
532 self.shuffle(indices) & indices.simd_lt(8)
535 }
536
537 #[inline]
538 fn shuffle_wrapping(self: [u64x2; 4], indices: u64x2) -> u64x2 {
539 self.shuffle(indices & 7)
540 }
541
542 #[inline]
545 pub fn transpose(data: [Self; 2]) -> [Self; 2] {
546 pick! {
547 if #[cfg(any(
548 target_feature="sse2",
549 all(target_feature="neon",target_arch="aarch64"),
550 target_feature="simd128",
551 ))] {
552 [
555 data[0].cast_signed().unpack_lo(data[1].cast_signed()).cast_unsigned(),
556 data[0].cast_signed().unpack_hi(data[1].cast_signed()).cast_unsigned(),
557 ]
558 } else {
559 let [x, y, z, w]: [u64; 4] = cast(data);
560 cast([x, z, y, w])
561 }
562 }
563 }
564
565 #[inline]
566 fn shl(self, rhs: Self) -> Self::Output {
567 pick! {
568 if #[cfg(target_feature="avx2")] {
569 let shift_by = rhs & Self::splat(63);
571 Self { sse: shl_each_u64_m128i(self.sse, shift_by.sse) }
572 } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
573 unsafe {
574 let shift_by = vreinterpretq_s64_u64(vandq_u64(rhs.neon, vmovq_n_u64(63)));
576 Self { neon: vshlq_u64(self.neon, shift_by) }
577 }
578 } else {
579 let arr: [u64; 2] = cast(self);
580 let rhs: [u64; 2] = cast(rhs);
581 cast([
582 arr[0].wrapping_shl(rhs[0] as u32),
583 arr[1].wrapping_shl(rhs[1] as u32),
584 ])
585 }
586 }
587 }
588
589 #[inline]
590 fn shl(self, rhs: u32) -> Self::Output {
591 pick! {
592 if #[cfg(target_feature="sse2")] {
593 #[expect(clippy::suspicious_arithmetic_impl)]
595 let shift = cast([rhs as u64 & 63, 0]);
596 Self { sse: shl_all_u64_m128i(self.sse, shift) }
597 } else if #[cfg(target_feature="simd128")] {
598 Self { simd: u64x2_shl(self.simd, rhs) }
599 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
600 #[expect(clippy::suspicious_arithmetic_impl)]
602 unsafe {Self { neon: vshlq_u64(self.neon, vmovq_n_s64(rhs as i64 & 63)) }}
603 } else {
604 Self { arr: [
605 self.arr[0].wrapping_shl(rhs),
606 self.arr[1].wrapping_shl(rhs),
607 ]}
608 }
609 }
610 }
611
612 #[inline]
613 fn shr(self, rhs: Self) -> Self::Output {
614 pick! {
615 if #[cfg(target_feature="avx2")] {
616 let shift_by = rhs & Self::splat(63);
618 Self { sse: shr_each_u64_m128i(self.sse, shift_by.sse) }
619 } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
620 unsafe {
621 let shift_by = vnegq_s64(vreinterpretq_s64_u64(vandq_u64(rhs.neon, vmovq_n_u64(63))));
624 Self { neon: vshlq_u64(self.neon, shift_by) }
625 }
626 } else {
627 let arr: [u64; 2] = cast(self);
628 let rhs: [u64; 2] = cast(rhs);
629 cast([
630 arr[0].wrapping_shr(rhs[0] as u32),
631 arr[1].wrapping_shr(rhs[1] as u32),
632 ])
633 }
634 }
635 }
636
637 #[inline]
638 fn shr(self, rhs: u32) -> Self::Output {
639 pick! {
640 if #[cfg(target_feature="sse2")] {
641 #[expect(clippy::suspicious_arithmetic_impl)]
643 let shift = cast([rhs as u64 & 63, 0]);
644 Self { sse: shr_all_u64_m128i(self.sse, shift) }
645 } else if #[cfg(target_feature="simd128")] {
646 Self { simd: u64x2_shr(self.simd, rhs) }
647 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
648 #[expect(clippy::suspicious_arithmetic_impl)]
650 unsafe {Self { neon: vshlq_u64(self.neon, vmovq_n_s64(-(rhs as i64 & 63))) }}
651 } else {
652 Self { arr: [
653 self.arr[0].wrapping_shr(rhs),
654 self.arr[1].wrapping_shr(rhs),
655 ]}
656 }
657 }
658 }
659
660 #[inline]
661 pub fn max(self, rhs: Self) -> Self {
662 self.simd_gt(rhs).select(self, rhs)
663 }
664
665 #[inline]
666 pub fn min(self, rhs: Self) -> Self {
667 self.simd_lt(rhs).select(self, rhs)
668 }
669
670 #[inline]
671 pub fn reduce_max(self) -> u64 {
672 pick! {
673 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
674 let array: [u64; 2] = cast(self);
675 array[0].max(array[1])
676 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
677 unsafe { vgetq_lane_u64(self.neon, 0).max(vgetq_lane_u64(self.neon, 1)) }
678 } else {
679 self.arr[0].max(self.arr[1])
680 }
681 }
682 }
683
684 #[inline]
685 pub fn reduce_min(self) -> u64 {
686 pick! {
687 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
688 let array: [u64; 2] = cast(self);
689 array[0].min(array[1])
690 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
691 unsafe { vgetq_lane_u64(self.neon, 0).min(vgetq_lane_u64(self.neon, 1)) }
692 } else {
693 self.arr[0].min(self.arr[1])
694 }
695 }
696 }
697
698 #[inline]
699 pub fn unbounded_shl(self, rhs: Self) -> Self {
700 pick! {
701 if #[cfg(target_feature="avx2")] {
702 Self { sse: shl_each_u64_m128i(self.sse, rhs.sse) }
703 } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
704 unsafe {
705 Self { neon: vshlq_u64(self.neon, vreinterpretq_s64_u64(rhs.neon)) } & rhs.simd_lt(64)
707 }
708 } else {
709 (self << rhs) & rhs.simd_lt(64)
712 }
713 }
714 }
715
716 #[inline]
717 pub fn unbounded_shl_scalar(self, rhs: u32) -> Self {
718 pick! {
719 if #[cfg(target_feature="sse2")] {
720 Self { sse: shl_all_u64_m128i(self.sse, cast([rhs as u64, 0])) }
721 } else if #[cfg(target_feature="simd128")] {
722 Self { simd: u64x2_shl(self.simd, rhs) } & Self::splat(rhs as u64).simd_lt(64)
724 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
725 unsafe { Self { neon: vshlq_u64(self.neon, vmovq_n_s64(rhs.min(64) as i64)) } }
726 } else {
727 Self { arr: [
728 self.arr[0].unbounded_shl(rhs),
729 self.arr[1].unbounded_shl(rhs),
730 ]}
731 }
732 }
733 }
734
735 #[inline]
736 pub fn unbounded_shr(self, rhs: Self) -> Self {
737 pick! {
738 if #[cfg(target_feature="avx2")] {
739 Self { sse: shr_each_u64_m128i(self.sse, rhs.sse) }
740 } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
741 unsafe {
742 Self { neon: vshlq_u64(self.neon, vnegq_s64(vreinterpretq_s64_u64(rhs.neon))) } & rhs.simd_lt(64)
745 }
746 } else {
747 (self >> rhs) & rhs.simd_lt(64)
750 }
751 }
752 }
753
754 #[inline]
755 pub fn unbounded_shr_scalar(self, rhs: u32) -> Self {
756 pick! {
757 if #[cfg(target_feature="sse2")] {
758 Self { sse: shr_all_u64_m128i(self.sse, cast([rhs as u64, 0])) }
759 } else if #[cfg(target_feature="simd128")] {
760 if rhs < 64 { Self { simd: u64x2_shr(self.simd, rhs) } } else { Self::ZERO }
761 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
762 unsafe {
763 Self { neon: vshlq_u64(self.neon, vmovq_n_s64(-rhs.min(64).cast_signed() as i64)) }
766 }
767 } else {
768 Self {
769 arr: [
770 self.arr[0].unbounded_shr(rhs),
771 self.arr[1].unbounded_shr(rhs),
772 ],
773 }
774 }
775 }
776 }
777
778 #[inline]
779 pub fn saturating_add(self, rhs: Self) -> Self {
780 pick! {
781 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
782 let result = self + rhs;
783 let overflow = result.simd_lt(self);
784 result | overflow
786 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
787 unsafe { Self { neon: vqaddq_u64(self.neon, rhs.neon) } }
788 } else {
789 Self {
790 arr: [
791 self.arr[0].saturating_add(rhs.arr[0]),
792 self.arr[1].saturating_add(rhs.arr[1]),
793 ],
794 }
795 }
796 }
797 }
798
799 #[inline]
800 pub fn saturating_sub(self, rhs: Self) -> Self {
801 pick! {
802 if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
803 let result = self - rhs;
804 let no_overflow = result.simd_le(self);
805 result & no_overflow
807 } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
808 unsafe { Self { neon: vqsubq_u64(self.neon, rhs.neon) } }
809 } else {
810 Self {
811 arr: [
812 self.arr[0].saturating_sub(rhs.arr[0]),
813 self.arr[1].saturating_sub(rhs.arr[1]),
814 ],
815 }
816 }
817 }
818 }
819
820 #[inline]
821 pub fn overflowing_mul(self, rhs: Self) -> (Self, Self) {
822 let self_array = self.to_array();
827 let rhs_array = rhs.to_array();
828
829 let result = [
830 self_array[0].overflowing_mul(rhs_array[0]),
831 self_array[1].overflowing_mul(rhs_array[1]),
832 ];
833 (
834 Self::new([result[0].0, result[1].0]),
835 Self::new([-(result[0].1 as i64) as u64, -(result[1].1 as i64) as u64]),
836 )
837 }
838
839 optional_fn_widening_mul {
840 }
842
843 #[inline]
844 pub fn mul_keep_low_high(self, rhs: Self) -> (Self, Self) {
845 let self_array = self.to_array();
849 let rhs_array = rhs.to_array();
850
851 let widening_mul = [
852 (self_array[0] as u128).wrapping_mul(rhs_array[0] as u128),
853 (self_array[1] as u128).wrapping_mul(rhs_array[1] as u128),
854 ];
855
856 (
857 Self::new([
858 widening_mul[0] as u64,
859 widening_mul[1] as u64,
860 ]),
861 Self::new([
862 (widening_mul[0] >> 64) as u64,
863 (widening_mul[1] >> 64) as u64,
864 ]),
865 )
866 }
867
868 #[inline]
869 pub fn mul_keep_high(self, rhs: Self) -> Self {
870 let arr1: [u64; 2] = cast(self);
871 let arr2: [u64; 2] = cast(rhs);
872 cast([
873 ((arr1[0] as u128 * arr2[0] as u128) >> 64) as u64,
874 ((arr1[1] as u128 * arr2[1] as u128) >> 64) as u64,
875 ])
876 }
877
878 optional_fn_deserialize {}
879}
880
881impl u64x2 {
884 #[allow(dead_code)]
893 #[inline]
894 fn to_byte_indices(self) -> u8x16 {
895 let base = self.unbounded_shl_scalar(3);
897 let base = base | base.unbounded_shl_scalar(8);
898 let base = base | base.unbounded_shl_scalar(16);
899 let base = base | base.unbounded_shl_scalar(32);
900
901 const WITHIN_LANE: u64x2 =
905 u64x2::splat(u64::from_ne_bytes([0, 1, 2, 3, 4, 5, 6, 7]));
906
907 cast::<u64x2, u8x16>(base | WITHIN_LANE)
908 }
909
910 #[inline]
912 #[must_use]
913 pub fn unpack_lo(self, b: Self) -> Self {
914 pick! {
915 if #[cfg(target_feature="sse2")] {
916 Self { sse: unpack_low_i64_m128i(self.sse, b.sse) }
917 } else if #[cfg(target_feature="simd128")] {
918 Self { simd: i64x2_shuffle::<0, 2>(self.simd, b.simd) }
919 } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
920 Self { neon: unsafe { vzip1q_u64(self.neon, b.neon) } }
921 } else {
922 Self::new([self.as_array()[0], b.as_array()[0]])
923 }
924 }
925 }
926
927 #[inline]
929 #[must_use]
930 pub fn unpack_hi(self, b: Self) -> Self {
931 pick! {
932 if #[cfg(target_feature="sse2")] {
933 Self { sse: unpack_high_i64_m128i(self.sse, b.sse) }
934 } else if #[cfg(target_feature="simd128")] {
935 Self { simd: i64x2_shuffle::<1, 3>(self.simd, b.simd) }
936 } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
937 Self { neon: unsafe { vzip2q_u64(self.neon, b.neon) } }
938 } else {
939 Self::new([self.as_array()[1], b.as_array()[1]])
940 }
941 }
942 }
943
944 #[inline]
950 #[must_use]
951 fn mul_masked<const W: u32>(a: Self, b: Self) -> Self {
952 pick! {
953 if #[cfg(target_feature="sse2")] {
954 let (a, b) = if W == 32 {
958 (a, b)
959 } else {
960 let mask = Self::splat(add_mul_operand_mask_u64::<W>());
961 (a & mask, b & mask)
962 };
963
964 Self { sse: mul_widen_u32_odd_m128i(a.sse, b.sse) }
965 } else {
966 let mask = add_mul_operand_mask_u64::<W>();
967 let a = a.to_array();
968 let b = b.to_array();
969 Self::new(core::array::from_fn(|i| (a[i] & mask) * (b[i] & mask)))
970 }
971 }
972 }
973
974 #[inline]
977 #[must_use]
978 pub fn add_mul_lo<const W: u32>(self, a: Self, b: Self) -> Self {
979 pick! {
980 if #[cfg(all(target_feature="avx512ifma", target_feature="avx512vl"))] {
981 if W == 52 {
983 return Self { sse: add_mul_low_u52_m128i(self.sse, a.sse, b.sse) };
984 }
985 }
986 }
987
988 if W <= 32 {
991 let mask = Self::splat(add_mul_operand_mask_u64::<W>());
992 return self + (Self::mul_masked::<W>(a, b) & mask);
993 }
994
995 let acc = self.to_array();
996 let a = a.to_array();
997 let b = b.to_array();
998 Self::new(core::array::from_fn(|i| {
999 add_mul_lo_lane_u64::<W>(acc[i], a[i], b[i])
1000 }))
1001 }
1002
1003 #[inline]
1006 #[must_use]
1007 pub fn add_mul_hi<const W: u32>(self, a: Self, b: Self) -> Self {
1008 pick! {
1009 if #[cfg(all(target_feature="avx512ifma", target_feature="avx512vl"))] {
1010 if W == 52 {
1012 return Self { sse: add_mul_high_u52_m128i(self.sse, a.sse, b.sse) };
1013 }
1014 }
1015 }
1016
1017 if W <= 32 {
1020 return self + (Self::mul_masked::<W>(a, b) >> W);
1021 }
1022
1023 let acc = self.to_array();
1024 let a = a.to_array();
1025 let b = b.to_array();
1026 Self::new(core::array::from_fn(|i| {
1027 add_mul_hi_lane_u64::<W>(acc[i], a[i], b[i])
1028 }))
1029 }
1030}