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 i64x4 { pub(crate) avx2: m256i }
8 } else {
9 #[derive(Default, Clone, Copy, PartialEq, Eq)]
10 #[repr(C, align(32))]
11 pub struct i64x4 { pub(crate) a : i64x2, pub(crate) b : i64x2 }
12 }
13}
14
15int_uint_consts!(i64, 4, i64x4, 256);
16
17unsafe impl Zeroable for i64x4 {}
18unsafe impl Pod for i64x4 {}
19
20impl AlignTo for i64x4 {
21 type Elem = i64;
22}
23
24impl Add for i64x4 {
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 i64x4 {
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 i64x4 {
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!(i64, i64x4, [0, 1, 2, 3]);
80
81impl Add<i64> for i64x4 {
82 type Output = Self;
83 #[inline]
84 fn add(self, rhs: i64) -> Self::Output {
85 self.add(Self::splat(rhs))
86 }
87}
88
89impl Sub<i64> for i64x4 {
90 type Output = Self;
91 #[inline]
92 fn sub(self, rhs: i64) -> Self::Output {
93 self.sub(Self::splat(rhs))
94 }
95}
96
97impl Mul<i64> for i64x4 {
98 type Output = Self;
99 #[inline]
100 fn mul(self, rhs: i64) -> Self::Output {
101 self.mul(Self::splat(rhs))
102 }
103}
104
105impl Add<i64x4> for i64 {
106 type Output = i64x4;
107 #[inline]
108 fn add(self, rhs: i64x4) -> Self::Output {
109 i64x4::splat(self).add(rhs)
110 }
111}
112
113impl Sub<i64x4> for i64 {
114 type Output = i64x4;
115 #[inline]
116 fn sub(self, rhs: i64x4) -> Self::Output {
117 i64x4::splat(self).sub(rhs)
118 }
119}
120
121impl Mul<i64x4> for i64 {
122 type Output = i64x4;
123 #[inline]
124 fn mul(self, rhs: i64x4) -> Self::Output {
125 i64x4::splat(self).mul(rhs)
126 }
127}
128
129impl BitAnd for i64x4 {
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 i64x4 {
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 i64x4 {
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 i64x4 {
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_i64x4 {
206 ($($shift_type:ty),+ $(,)?) => {
207 $(impl Shl<$shift_type> for i64x4 {
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_i64x4!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
228
229impl Shr for i64x4 {
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 arr: [i64; 4] = cast(self);
242 let rhs: [i64; 4] = cast(rhs);
243 cast([
244 arr[0].wrapping_shr(rhs[0] as u32),
245 arr[1].wrapping_shr(rhs[1] as u32),
246 arr[2].wrapping_shr(rhs[2] as u32),
247 arr[3].wrapping_shr(rhs[3] as u32),
248 ])
249 } else {
250 Self {
251 a : self.a.shr(rhs.a),
252 b : self.b.shr(rhs.b),
253 }
254 }
255 }
256 }
257}
258
259macro_rules! impl_shr_t_for_i64x4 {
260 ($($shift_type:ty),+ $(,)?) => {
261 $(impl Shr<$shift_type> for i64x4 {
262 type Output = Self;
263 #[inline]
265 fn shr(self, rhs: $shift_type) -> Self::Output {
266 let [a,b] : [i64x2; 2] = cast(self);
268 cast([a.shr(rhs), b.shr(rhs)])
269 }
270 })+
271 };
272}
273impl_shr_t_for_i64x4!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
274
275#[expect(deprecated)]
276impl CmpEq for i64x4 {
277 type Output = Self;
278 #[inline]
279 fn simd_eq(self, rhs: Self) -> Self::Output {
280 pick! {
281 if #[cfg(target_feature="avx2")] {
282 Self { avx2: cmp_eq_mask_i64_m256i(self.avx2, rhs.avx2) }
283 } else {
284 Self {
285 a : self.a.simd_eq(rhs.a),
286 b : self.b.simd_eq(rhs.b),
287 }
288 }
289 }
290 }
291}
292
293#[expect(deprecated)]
294impl CmpGt for i64x4 {
295 type Output = Self;
296 #[inline]
297 fn simd_gt(self, rhs: Self) -> Self::Output {
298 pick! {
299 if #[cfg(target_feature="avx2")] {
300 Self { avx2: cmp_gt_mask_i64_m256i(self.avx2, rhs.avx2) }
301 } else {
302 Self {
303 a : self.a.simd_gt(rhs.a),
304 b : self.b.simd_gt(rhs.b),
305 }
306 }
307 }
308 }
309}
310
311#[expect(deprecated)]
312impl CmpLt for i64x4 {
313 type Output = Self;
314 #[inline]
315 fn simd_lt(self, rhs: Self) -> Self::Output {
316 pick! {
317 if #[cfg(target_feature="avx2")] {
318 Self { avx2: !(cmp_gt_mask_i64_m256i(self.avx2, rhs.avx2) ^ cmp_eq_mask_i64_m256i(self.avx2, rhs.avx2)) }
319 } else {
320 Self {
321 a : self.a.simd_lt(rhs.a),
322 b : self.b.simd_lt(rhs.b),
323 }
324 }
325 }
326 }
327}
328
329#[expect(deprecated)]
330impl CmpNe for i64x4 {
331 type Output = Self;
332 #[inline]
333 fn simd_ne(self, rhs: Self) -> Self::Output {
334 pick! {
335 if #[cfg(target_feature="avx2")] {
336 !self.simd_eq(rhs)
337 } else {
338 Self {
339 a : self.a.simd_ne(rhs.a),
340 b : self.b.simd_ne(rhs.b),
341 }
342 }
343 }
344 }
345}
346
347#[expect(deprecated)]
348impl CmpLe for i64x4 {
349 type Output = Self;
350 #[inline]
351 fn simd_le(self, rhs: Self) -> Self::Output {
352 pick! {
353 if #[cfg(target_feature="avx2")] {
354 !self.simd_gt(rhs)
355 } else {
356 Self {
357 a : self.a.simd_le(rhs.a),
358 b : self.b.simd_le(rhs.b),
359 }
360 }
361 }
362 }
363}
364
365#[expect(deprecated)]
366impl CmpGe for i64x4 {
367 type Output = Self;
368 #[inline]
369 fn simd_ge(self, rhs: Self) -> Self::Output {
370 pick! {
371 if #[cfg(target_feature="avx2")] {
372 !self.simd_lt(rhs)
373 } else {
374 Self {
375 a : self.a.simd_ge(rhs.a),
376 b : self.b.simd_ge(rhs.b),
377 }
378 }
379 }
380 }
381}
382
383impl i64x4 {
384 #[inline]
385 #[must_use]
386 pub const fn new(array: [i64; 4]) -> Self {
387 unsafe { core::mem::transmute(array) }
388 }
389
390 simd_comparison_fns!();
391
392 #[inline]
393 #[must_use]
394 pub fn blend(self, t: Self, f: Self) -> Self {
395 pick! {
396 if #[cfg(target_feature="avx2")] {
397 Self { avx2: blend_varying_i8_m256i(f.avx2,t.avx2,self.avx2) }
398 } else {
399 Self {
400 a : self.a.blend(t.a, f.a),
401 b : self.b.blend(t.b, f.b),
402 }
403 }
404 }
405 }
406
407 #[inline]
410 #[must_use]
411 pub fn is_positive(self) -> Self {
412 pick! {
413 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
414 Self {
416 a: self.a.is_positive(),
417 b: self.b.is_positive(),
418 }
419 } else {
420 self.simd_gt(Self::ZERO)
421 }
422 }
423 }
424
425 #[inline]
428 #[must_use]
429 pub fn is_negative(self) -> Self {
430 pick! {
431 if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
432 Self {
434 a: self.a.is_negative(),
435 b: self.b.is_negative(),
436 }
437 } else {
438 self.simd_lt(Self::ZERO)
439 }
440 }
441 }
442
443 #[inline]
444 #[must_use]
445 pub fn reduce_add(self) -> i64 {
446 pick! {
447 if #[cfg(all(target_arch="x86_64", target_feature="avx2"))] {
448 let zwxx = shuffle_ai_i64_all_m256i::<0b00_00_11_10>(self.avx2);
449 let xz_yw = add_i64_m256i(zwxx, self.avx2);
450 let yw_xz = shuffle_ai_i64_all_m256i::<0b00_00_00_01>(xz_yw);
451 let sum = add_i64_m256i(xz_yw, yw_xz);
452 extract_i64_from_m256i::<0>(sum)
453 } else {
454 let array: [i64; 4] = cast(self);
455 array[0]
456 .wrapping_add(array[1])
457 .wrapping_add(array[2])
458 .wrapping_add(array[3])
459 }
460 }
461 }
462
463 #[inline]
464 #[must_use]
465 pub fn reduce_max(self) -> i64 {
466 let array: [i64; 4] = cast(self);
467 array[0].max(array[1]).max(array[2]).max(array[3])
468 }
469
470 #[inline]
471 #[must_use]
472 pub fn reduce_min(self) -> i64 {
473 let array: [i64; 4] = cast(self);
474 array[0].min(array[1]).min(array[2]).min(array[3])
475 }
476
477 #[inline]
478 #[must_use]
479 pub fn abs(self) -> Self {
480 pick! {
481 if #[cfg(target_feature="avx2")] {
482 let arr: [i64; 4] = cast(self);
484 cast(
485 [
486 arr[0].wrapping_abs(),
487 arr[1].wrapping_abs(),
488 arr[2].wrapping_abs(),
489 arr[3].wrapping_abs(),
490 ])
491 } else {
492 Self {
493 a : self.a.abs(),
494 b : self.b.abs(),
495 }
496 }
497 }
498 }
499
500 #[inline]
501 #[must_use]
502 pub fn unsigned_abs(self) -> u64x4 {
503 pick! {
504 if #[cfg(target_feature="avx2")] {
505 let arr: [i64; 4] = cast(self);
507 cast(
508 [
509 arr[0].unsigned_abs(),
510 arr[1].unsigned_abs(),
511 arr[2].unsigned_abs(),
512 arr[3].unsigned_abs(),
513 ])
514 } else {
515 u64x4 {
516 a : self.a.unsigned_abs(),
517 b : self.b.unsigned_abs(),
518 }
519 }
520 }
521 }
522
523 signed_fn_signum!();
524
525 #[inline]
526 #[must_use]
527 pub fn round_float(self) -> f64x4 {
528 let arr: [i64; 4] = cast(self);
529 cast([arr[0] as f64, arr[1] as f64, arr[2] as f64, arr[3] as f64])
530 }
531
532 #[inline]
535 #[must_use]
536 #[doc(alias("movemask", "move_mask"))]
537 pub fn to_bitmask(self) -> u32 {
538 pick! {
539 if #[cfg(target_feature="avx2")] {
540 move_mask_m256d(cast(self.avx2)) as u32
542 } else {
543 self.a.to_bitmask() | (self.b.to_bitmask() << 2)
544 }
545 }
546 }
547
548 #[inline]
550 #[must_use]
551 pub fn any(self) -> bool {
552 pick! {
553 if #[cfg(target_feature="avx2")] {
554 move_mask_m256d(cast(self.avx2)) != 0
555 } else {
556 (self.a | self.b).any()
557 }
558 }
559 }
560
561 #[inline]
563 #[must_use]
564 pub fn all(self) -> bool {
565 pick! {
566 if #[cfg(target_feature="avx2")] {
567 move_mask_m256d(cast(self.avx2)) == 0b1111
568 } else {
569 (self.a & self.b).all()
570 }
571 }
572 }
573
574 #[inline]
576 #[must_use]
577 pub fn none(self) -> bool {
578 !self.any()
579 }
580
581 #[inline]
582 pub fn to_array(self) -> [i64; 4] {
583 cast(self)
584 }
585
586 #[inline]
587 pub fn as_array(&self) -> &[i64; 4] {
588 cast_ref(self)
589 }
590
591 #[inline]
592 pub fn as_mut_array(&mut self) -> &mut [i64; 4] {
593 cast_mut(self)
594 }
595
596 #[inline]
597 #[must_use]
598 pub fn min(self, rhs: Self) -> Self {
599 self.simd_lt(rhs).blend(self, rhs)
600 }
601
602 #[inline]
603 #[must_use]
604 pub fn max(self, rhs: Self) -> Self {
605 self.simd_gt(rhs).blend(self, rhs)
606 }
607
608 integer_fn_clamp!();
609
610 #[inline]
611 #[must_use]
612 pub fn saturating_add(self, rhs: Self) -> Self {
613 pick! {
614 if #[cfg(target_feature="avx2")] {
615 let result = self + rhs;
616 let overflow = (!(self ^ rhs) & (self ^ result)).is_negative();
617 let negative = self.is_negative();
618
619 overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
620 } else {
621 Self {
622 a: self.a.saturating_add(rhs.a),
623 b: self.b.saturating_add(rhs.b),
624 }
625 }
626 }
627 }
628
629 #[inline]
630 #[must_use]
631 pub fn saturating_sub(self, rhs: Self) -> Self {
632 pick! {
633 if #[cfg(target_feature="avx2")] {
634 let result = self - rhs;
635 let overflow = ((self ^ rhs) & (self ^ result)).is_negative();
636 let negative = self.is_negative();
637
638 overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
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]
650 #[must_use]
651 pub fn saturating_mul(self, rhs: Self) -> Self {
652 let self_array = self.to_array();
653 let rhs_array = rhs.to_array();
654
655 Self::new([
656 self_array[0].saturating_mul(rhs_array[0]),
657 self_array[1].saturating_mul(rhs_array[1]),
658 self_array[2].saturating_mul(rhs_array[2]),
659 self_array[3].saturating_mul(rhs_array[3]),
660 ])
661 }
662
663 integer_fn_saturating_div!([0, 1, 2, 3]);
664
665 #[must_use]
667 #[inline]
668 #[allow(dead_code)]
669 pub(crate) fn unpack_lo(self, b: Self) -> Self {
670 pick! {
671 if #[cfg(target_feature="avx2")] {
672 let [aa, _]: [i64x2; 2] = cast(self);
673 let [ba, _]: [i64x2; 2] = cast(b);
674 cast([aa.unpack_lo(ba), aa.unpack_hi(ba)])
675 } else {
676 Self { a: self.a.unpack_lo(b.a), b: self.a.unpack_hi(b.a) }
677 }
678 }
679 }
680
681 #[must_use]
683 #[inline]
684 #[allow(dead_code)]
685 pub(crate) fn unpack_hi(self, b: Self) -> Self {
686 pick! {
687 if #[cfg(target_feature="avx2")] {
688 let [_, ab]: [i64x2; 2] = cast(self);
689 let [_, bb]: [i64x2; 2] = cast(b);
690 cast([ab.unpack_lo(bb), ab.unpack_hi(bb)])
691 } else {
692 Self { a: self.b.unpack_lo(b.b), b: self.b.unpack_hi(b.b) }
693 }
694 }
695 }
696
697 #[must_use]
699 #[inline]
700 pub fn transpose(data: [i64x4; 4]) -> [i64x4; 4] {
701 pick! {
702 if #[cfg(target_feature="avx2")] {
703 let a = data[0].unpack_lo(data[2]);
705 let b = data[1].unpack_lo(data[3]);
706 let c = data[0].unpack_hi(data[2]);
707 let d = data[1].unpack_hi(data[3]);
708 [
709 a.unpack_lo(b),
710 a.unpack_hi(b),
711 c.unpack_lo(d),
712 c.unpack_hi(d),
713 ]
714 } else {
715 #[inline(always)]
716 fn transpose_column(data: &[i64x4; 4], index: usize) -> i64x4 {
717 i64x4::new([
718 data[0].as_array()[index],
719 data[1].as_array()[index],
720 data[2].as_array()[index],
721 data[3].as_array()[index],
722 ])
723 }
724
725 [
726 transpose_column(&data, 0),
727 transpose_column(&data, 1),
728 transpose_column(&data, 2),
729 transpose_column(&data, 3),
730 ]
731 }
732 }
733 }
734}
735
736impl Not for i64x4 {
737 type Output = Self;
738 #[inline]
739 fn not(self) -> Self {
740 pick! {
741 if #[cfg(target_feature="avx2")] {
742 Self { avx2: self.avx2.not() }
743 } else {
744 Self {
745 a : self.a.not(),
746 b : self.b.not(),
747 }
748 }
749 }
750 }
751}