Skip to main content

wide/
i64x4_.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 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
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 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        // 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_i64x4 {
206  ($($shift_type:ty),+ $(,)?) => {
207    $(impl Shl<$shift_type> for i64x4 {
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_i64x4!(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 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      /// Shifts all lanes by the value given.
264      #[inline]
265      fn shr(self, rhs: $shift_type) -> Self::Output {
266          // there is no signed right shift in AVX2
267          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  /// Returns true for each positive element and false if it is zero or
408  /// negative.
409  #[inline]
410  #[must_use]
411  pub fn is_positive(self) -> Self {
412    pick! {
413      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
414        // `neon` has dedicated greater-than-zero intrinsics.
415        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  /// Returns true for each negative element and false if it is zero or
426  /// positive.
427  #[inline]
428  #[must_use]
429  pub fn is_negative(self) -> Self {
430    pick! {
431      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
432        // `neon` has dedicated less-than-zero intrinsics.
433        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        // avx x86 doesn't have this builtin
483        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        // avx x86 doesn't have this builtin
506        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  /// returns the bit mask for each high bit set in the vector with the lowest
533  /// lane being the lowest bit
534  #[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        // use f64 move_mask since it is the same size as i64
541        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  /// true if any high bits are set for any value in the vector
549  #[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  /// true if all high bits are set for every value in the vector
562  #[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  /// true if no high bits are set for any values of the vector
575  #[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  /// Lanewise saturating multiply.
649  #[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  // Sometimes used for `transpose`.
666  #[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  // Sometimes used for `transpose`.
682  #[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  /// Transpose matrix of 4x4 `i64` matrix.
698  #[must_use]
699  #[inline]
700  pub fn transpose(data: [i64x4; 4]) -> [i64x4; 4] {
701    pick! {
702      if #[cfg(target_feature="avx2")] {
703        // Can this be optimized?
704        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}