Skip to main content

wide/
i64x2_.rs

1use super::*;
2
3pick! {
4  if #[cfg(target_feature="sse2")] {
5    #[derive(Default, Clone, Copy, PartialEq, Eq)]
6    #[repr(C, align(16))]
7    pub struct i64x2 { pub(crate) sse: m128i }
8  } else if #[cfg(target_feature="simd128")] {
9    use core::arch::wasm32::*;
10
11    #[derive(Clone, Copy)]
12    #[repr(transparent)]
13    pub struct i64x2 { pub(crate) simd: v128 }
14
15    impl Default for i64x2 {
16      fn default() -> Self {
17        Self::splat(0)
18      }
19    }
20
21    impl PartialEq for i64x2 {
22      fn eq(&self, other: &Self) -> bool {
23        u64x2_all_true(i64x2_eq(self.simd, other.simd))
24      }
25    }
26
27    impl Eq for i64x2 { }
28  } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
29    use core::arch::aarch64::*;
30    #[repr(C)]
31    #[derive(Copy, Clone)]
32    pub struct i64x2 { pub(crate) neon : int64x2_t }
33
34    impl Default for i64x2 {
35      #[inline]
36      fn default() -> Self {
37        unsafe { Self { neon: vdupq_n_s64(0)} }
38      }
39    }
40
41    impl PartialEq for i64x2 {
42      #[inline]
43      fn eq(&self, other: &Self) -> bool {
44        unsafe {
45          vgetq_lane_s64(self.neon,0) == vgetq_lane_s64(other.neon,0) && vgetq_lane_s64(self.neon,1) == vgetq_lane_s64(other.neon,1)
46        }
47      }
48    }
49
50    impl Eq for i64x2 { }
51  } else {
52    #[derive(Default, Clone, Copy, PartialEq, Eq)]
53    #[repr(C, align(16))]
54    pub struct i64x2 { arr: [i64;2] }
55  }
56}
57
58int_uint_consts!(i64, 2, i64x2, 128);
59
60unsafe impl Zeroable for i64x2 {}
61unsafe impl Pod for i64x2 {}
62
63impl AlignTo for i64x2 {
64  type Elem = i64;
65}
66
67impl Add for i64x2 {
68  type Output = Self;
69  #[inline]
70  fn add(self, rhs: Self) -> Self::Output {
71    pick! {
72      if #[cfg(target_feature="sse2")] {
73        Self { sse: add_i64_m128i(self.sse, rhs.sse) }
74      } else if #[cfg(target_feature="simd128")] {
75        Self { simd: i64x2_add(self.simd, rhs.simd) }
76      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
77        unsafe { Self { neon: vaddq_s64(self.neon, rhs.neon) } }
78      } else {
79        Self { arr: [
80          self.arr[0].wrapping_add(rhs.arr[0]),
81          self.arr[1].wrapping_add(rhs.arr[1]),
82        ]}
83      }
84    }
85  }
86}
87
88impl Sub for i64x2 {
89  type Output = Self;
90  #[inline]
91  fn sub(self, rhs: Self) -> Self::Output {
92    pick! {
93      if #[cfg(target_feature="sse2")] {
94        Self { sse: sub_i64_m128i(self.sse, rhs.sse) }
95      } else if #[cfg(target_feature="simd128")] {
96        Self { simd: i64x2_sub(self.simd, rhs.simd) }
97      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
98        unsafe { Self { neon: vsubq_s64(self.neon, rhs.neon) } }
99      } else {
100        Self { arr: [
101          self.arr[0].wrapping_sub(rhs.arr[0]),
102          self.arr[1].wrapping_sub(rhs.arr[1]),
103        ]}
104      }
105    }
106  }
107}
108
109//we should try to implement this on sse2
110impl Mul for i64x2 {
111  type Output = Self;
112  #[inline]
113  fn mul(self, rhs: Self) -> Self::Output {
114    pick! {
115      if #[cfg(target_feature="simd128")] {
116        Self { simd: i64x2_mul(self.simd, rhs.simd) }
117      } else {
118        let arr1: [i64; 2] = cast(self);
119        let arr2: [i64; 2] = cast(rhs);
120        cast([
121          arr1[0].wrapping_mul(arr2[0]),
122          arr1[1].wrapping_mul(arr2[1]),
123        ])
124      }
125    }
126  }
127}
128
129integer_impl_div_rem!(i64, i64x2, [0, 1]);
130
131impl Add<i64> for i64x2 {
132  type Output = Self;
133  #[inline]
134  fn add(self, rhs: i64) -> Self::Output {
135    self.add(Self::splat(rhs))
136  }
137}
138
139impl Sub<i64> for i64x2 {
140  type Output = Self;
141  #[inline]
142  fn sub(self, rhs: i64) -> Self::Output {
143    self.sub(Self::splat(rhs))
144  }
145}
146
147impl Mul<i64> for i64x2 {
148  type Output = Self;
149  #[inline]
150  fn mul(self, rhs: i64) -> Self::Output {
151    self.mul(Self::splat(rhs))
152  }
153}
154
155impl Add<i64x2> for i64 {
156  type Output = i64x2;
157  #[inline]
158  fn add(self, rhs: i64x2) -> Self::Output {
159    i64x2::splat(self).add(rhs)
160  }
161}
162
163impl Sub<i64x2> for i64 {
164  type Output = i64x2;
165  #[inline]
166  fn sub(self, rhs: i64x2) -> Self::Output {
167    i64x2::splat(self).sub(rhs)
168  }
169}
170
171impl Mul<i64x2> for i64 {
172  type Output = i64x2;
173  #[inline]
174  fn mul(self, rhs: i64x2) -> Self::Output {
175    i64x2::splat(self).mul(rhs)
176  }
177}
178
179impl BitAnd for i64x2 {
180  type Output = Self;
181  #[inline]
182  fn bitand(self, rhs: Self) -> Self::Output {
183    pick! {
184      if #[cfg(target_feature="sse2")] {
185        Self { sse: bitand_m128i(self.sse, rhs.sse) }
186      } else if #[cfg(target_feature="simd128")] {
187        Self { simd: v128_and(self.simd, rhs.simd) }
188      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
189        unsafe {Self { neon: vandq_s64(self.neon, rhs.neon) }}
190      } else {
191        Self { arr: [
192          self.arr[0].bitand(rhs.arr[0]),
193          self.arr[1].bitand(rhs.arr[1]),
194        ]}
195      }
196    }
197  }
198}
199
200impl BitOr for i64x2 {
201  type Output = Self;
202  #[inline]
203  fn bitor(self, rhs: Self) -> Self::Output {
204    pick! {
205      if #[cfg(target_feature="sse2")] {
206        Self { sse: bitor_m128i(self.sse, rhs.sse) }
207      } else if #[cfg(target_feature="simd128")] {
208        Self { simd: v128_or(self.simd, rhs.simd) }
209      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
210        unsafe {Self { neon: vorrq_s64(self.neon, rhs.neon) }}
211      } else {
212        Self { arr: [
213          self.arr[0].bitor(rhs.arr[0]),
214          self.arr[1].bitor(rhs.arr[1]),
215        ]}
216      }
217    }
218  }
219}
220
221impl BitXor for i64x2 {
222  type Output = Self;
223  #[inline]
224  fn bitxor(self, rhs: Self) -> Self::Output {
225    pick! {
226      if #[cfg(target_feature="sse2")] {
227        Self { sse: bitxor_m128i(self.sse, rhs.sse) }
228      } else if #[cfg(target_feature="simd128")] {
229        Self { simd: v128_xor(self.simd, rhs.simd) }
230      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
231        unsafe {Self { neon: veorq_s64(self.neon, rhs.neon) }}
232      } else {
233        Self { arr: [
234          self.arr[0].bitxor(rhs.arr[0]),
235          self.arr[1].bitxor(rhs.arr[1]),
236        ]}
237      }
238    }
239  }
240}
241
242/// Shifts lanes by the corresponding lane.
243///
244/// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
245/// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
246/// of the type. (same as `wrapping_shl`)
247impl Shl for i64x2 {
248  type Output = Self;
249
250  #[inline]
251  fn shl(self, rhs: Self) -> Self::Output {
252    pick! {
253      if #[cfg(target_feature="avx2")] {
254        // mask the shift count to 63 to have same behavior on all platforms
255        let shift_by = rhs & Self::splat(63);
256        Self { sse: shl_each_u64_m128i(self.sse, shift_by.sse) }
257      } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
258        unsafe {
259          // mask the shift count to 63 to have same behavior on all platforms
260          let shift_by = vandq_s64(rhs.neon, vmovq_n_s64(63));
261          Self { neon: vshlq_s64(self.neon, shift_by) }
262        }
263      } else {
264        let arr: [i64; 2] = cast(self);
265        let rhs: [i64; 2] = cast(rhs);
266        cast([
267          arr[0].wrapping_shl(rhs[0] as u32),
268          arr[1].wrapping_shl(rhs[1] as u32),
269        ])
270      }
271    }
272  }
273}
274
275macro_rules! impl_shl_t_for_i64x2 {
276  ($($shift_type:ty),+ $(,)?) => {
277    $(impl Shl<$shift_type> for i64x2 {
278      type Output = Self;
279      /// Shifts all lanes by the value given.
280      #[inline]
281      fn shl(self, rhs: $shift_type) -> Self::Output {
282        pick! {
283          if #[cfg(target_feature="sse2")] {
284            let shift = cast([rhs as u64, 0]);
285            Self { sse: shl_all_u64_m128i(self.sse, shift) }
286          } else if #[cfg(target_feature="simd128")] {
287            Self { simd: i64x2_shl(self.simd, rhs as u32) }
288          } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
289            unsafe {Self { neon: vshlq_s64(self.neon, vmovq_n_s64(rhs as i64)) }}
290          } else {
291            let u = rhs as u32;
292            Self { arr: [
293              self.arr[0].wrapping_shl(u),
294              self.arr[1].wrapping_shl(u),
295            ]}
296          }
297        }
298      }
299    })+
300  };
301}
302impl_shl_t_for_i64x2!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
303
304/// Shifts lanes by the corresponding lane.
305///
306/// Bitwise shift-right; yields `self >> mask(rhs)`, where mask removes any
307/// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
308/// of the type. (same as `wrapping_shr`)
309impl Shr for i64x2 {
310  type Output = Self;
311
312  #[inline]
313  fn shr(self, rhs: Self) -> Self::Output {
314    pick! {
315      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
316        unsafe {
317          // mask the shift count to 63 to have same behavior on all platforms
318          // no right shift, have to pass negative value to left shift on neon
319          let shift_by = vnegq_s64(vandq_s64(rhs.neon, vmovq_n_s64(63)));
320          Self { neon: vshlq_s64(self.neon, shift_by) }
321        }
322      } else {
323        let arr: [i64; 2] = cast(self);
324        let rhs: [i64; 2] = cast(rhs);
325        cast([
326          arr[0].wrapping_shr(rhs[0] as u32),
327          arr[1].wrapping_shr(rhs[1] as u32),
328        ])
329      }
330    }
331  }
332}
333
334macro_rules! impl_shr_t_for_i64x2 {
335  ($($shift_type:ty),+ $(,)?) => {
336    $(impl Shr<$shift_type> for i64x2 {
337      type Output = Self;
338      /// Shifts all lanes by the value given.
339      #[inline]
340      fn shr(self, rhs: $shift_type) -> Self::Output {
341        pick! {
342          if #[cfg(target_feature="simd128")] {
343            Self { simd: i64x2_shr(self.simd, rhs as u32) }
344          } else {
345            let u = rhs as u32;
346            let arr: [i64; 2] = cast(self);
347            cast([
348              arr[0].wrapping_shr(u),
349              arr[1].wrapping_shr(u),
350            ])
351          }
352        }
353      }
354    })+
355  };
356}
357
358impl_shr_t_for_i64x2!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
359
360#[expect(deprecated)]
361impl CmpEq for i64x2 {
362  type Output = Self;
363  #[inline]
364  fn simd_eq(self, rhs: Self) -> Self::Output {
365    pick! {
366      if #[cfg(target_feature="sse4.1")] {
367        Self { sse: cmp_eq_mask_i64_m128i(self.sse, rhs.sse) }
368      } else if #[cfg(target_feature="simd128")] {
369        Self { simd: i64x2_eq(self.simd, rhs.simd) }
370      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
371        unsafe {Self { neon: vreinterpretq_s64_u64(vceqq_s64(self.neon, rhs.neon)) }}
372      } else {
373        let s: [i64;2] = cast(self);
374        let r: [i64;2] = cast(rhs);
375        cast([
376          if s[0] == r[0] { -1_i64 } else { 0 },
377          if s[1] == r[1] { -1_i64 } else { 0 },
378        ])
379      }
380    }
381  }
382}
383
384#[expect(deprecated)]
385impl CmpGt for i64x2 {
386  type Output = Self;
387  #[inline]
388  fn simd_gt(self, rhs: Self) -> Self::Output {
389    pick! {
390      if #[cfg(target_feature="sse4.2")] {
391        Self { sse: cmp_gt_mask_i64_m128i(self.sse, rhs.sse) }
392      } else if #[cfg(target_feature="simd128")] {
393        Self { simd: i64x2_gt(self.simd, rhs.simd) }
394      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
395        unsafe {Self { neon: vreinterpretq_s64_u64(vcgtq_s64(self.neon, rhs.neon)) }}
396      } else {
397        let s: [i64;2] = cast(self);
398        let r: [i64;2] = cast(rhs);
399        cast([
400          if s[0] > r[0] { -1_i64 } else { 0 },
401          if s[1] > r[1] { -1_i64 } else { 0 },
402        ])
403      }
404    }
405  }
406}
407
408#[expect(deprecated)]
409impl CmpLt for i64x2 {
410  type Output = Self;
411  #[inline]
412  fn simd_lt(self, rhs: Self) -> Self::Output {
413    pick! {
414      if #[cfg(target_feature="sse4.2")] {
415        // only has gt, so flip arguments around to get lt
416        Self { sse: cmp_gt_mask_i64_m128i( rhs.sse, self.sse) }
417      } else if #[cfg(target_feature="simd128")] {
418        Self { simd: i64x2_lt(self.simd, rhs.simd) }
419      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
420        unsafe {Self { neon: vreinterpretq_s64_u64(vcltq_s64(self.neon, rhs.neon)) }}
421      } else {
422        let s: [i64;2] = cast(self);
423        let r: [i64;2] = cast(rhs);
424        cast([
425          if s[0] < r[0] { -1_i64 } else { 0 },
426          if s[1] < r[1] { -1_i64 } else { 0 },
427        ])
428      }
429    }
430  }
431}
432
433#[expect(deprecated)]
434impl CmpNe for i64x2 {
435  type Output = Self;
436  #[inline]
437  fn simd_ne(self, rhs: Self) -> Self::Output {
438    pick! {
439      if #[cfg(target_feature="sse4.1")] {
440        !self.simd_eq(rhs)
441      } else if #[cfg(target_feature="simd128")] {
442        Self { simd: i64x2_ne(self.simd, rhs.simd) }
443      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
444        !self.simd_eq(rhs)
445      } else {
446        let s: [i64;2] = cast(self);
447        let r: [i64;2] = cast(rhs);
448        cast([
449          if s[0] != r[0] { -1_i64 } else { 0 },
450          if s[1] != r[1] { -1_i64 } else { 0 },
451        ])
452      }
453    }
454  }
455}
456
457#[expect(deprecated)]
458impl CmpLe for i64x2 {
459  type Output = Self;
460  #[inline]
461  fn simd_le(self, rhs: Self) -> Self::Output {
462    pick! {
463      if #[cfg(target_feature="sse4.1")] {
464        !self.simd_gt(rhs)
465      } else if #[cfg(target_feature="simd128")] {
466        Self { simd: i64x2_le(self.simd, rhs.simd) }
467      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
468        !self.simd_gt(rhs)
469      } else {
470        let s: [i64;2] = cast(self);
471        let r: [i64;2] = cast(rhs);
472        cast([
473          if s[0] <= r[0] { -1_i64 } else { 0 },
474          if s[1] <= r[1] { -1_i64 } else { 0 },
475        ])
476      }
477    }
478  }
479}
480
481#[expect(deprecated)]
482impl CmpGe for i64x2 {
483  type Output = Self;
484  #[inline]
485  fn simd_ge(self, rhs: Self) -> Self::Output {
486    pick! {
487      if #[cfg(target_feature="sse4.1")] {
488        !self.simd_lt(rhs)
489      } else if #[cfg(target_feature="simd128")] {
490        Self { simd: i64x2_ge(self.simd, rhs.simd) }
491      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
492        !self.simd_lt(rhs)
493      } else {
494        let s: [i64;2] = cast(self);
495        let r: [i64;2] = cast(rhs);
496        cast([
497          if s[0] >= r[0] { -1_i64 } else { 0 },
498          if s[1] >= r[1] { -1_i64 } else { 0 },
499        ])
500      }
501    }
502  }
503}
504
505impl i64x2 {
506  #[inline]
507  #[must_use]
508  pub const fn new(array: [i64; 2]) -> Self {
509    unsafe { core::mem::transmute(array) }
510  }
511
512  simd_comparison_fns!();
513
514  #[inline]
515  #[must_use]
516  pub fn blend(self, t: Self, f: Self) -> Self {
517    pick! {
518      if #[cfg(target_feature="sse4.1")] {
519        Self { sse: blend_varying_i8_m128i(f.sse, t.sse, self.sse) }
520      } else if #[cfg(target_feature="simd128")] {
521        Self { simd: v128_bitselect(t.simd, f.simd, self.simd) }
522      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
523        unsafe {Self { neon: vbslq_s64(vreinterpretq_u64_s64(self.neon), t.neon, f.neon) }}
524      } else {
525        generic_bit_blend(self, t, f)
526      }
527    }
528  }
529
530  /// Returns true for each positive element and false if it is zero or
531  /// negative.
532  #[inline]
533  #[must_use]
534  pub fn is_positive(self) -> Self {
535    pick! {
536      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
537        Self { neon: unsafe { vreinterpretq_s64_u64(vcgtzq_s64(self.neon)) } }
538      } else {
539        self.simd_gt(Self::ZERO)
540      }
541    }
542  }
543
544  /// Returns true for each negative element and false if it is zero or
545  /// positive.
546  #[inline]
547  #[must_use]
548  pub fn is_negative(self) -> Self {
549    pick! {
550      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
551        Self { neon: unsafe { vreinterpretq_s64_u64(vcltzq_s64(self.neon)) } }
552      } else {
553        self.simd_lt(Self::ZERO)
554      }
555    }
556  }
557
558  #[inline]
559  #[must_use]
560  pub fn reduce_add(self) -> i64 {
561    pick! {
562      if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
563        let array: [i64; 2] = cast(self);
564        array[0].wrapping_add(array[1])
565      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
566        unsafe { vgetq_lane_s64(self.neon, 0).wrapping_add(vgetq_lane_s64(self.neon, 1)) }
567      } else {
568        self.arr[0].wrapping_add(self.arr[1])
569      }
570    }
571  }
572
573  #[inline]
574  #[must_use]
575  pub fn reduce_max(self) -> i64 {
576    pick! {
577      if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
578        let array: [i64; 2] = cast(self);
579        array[0].max(array[1])
580      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
581        unsafe { vgetq_lane_s64(self.neon, 0).max(vgetq_lane_s64(self.neon, 1)) }
582      } else {
583        self.arr[0].max(self.arr[1])
584      }
585    }
586  }
587
588  #[inline]
589  #[must_use]
590  pub fn reduce_min(self) -> i64 {
591    pick! {
592      if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
593        let array: [i64; 2] = cast(self);
594        array[0].min(array[1])
595      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
596        unsafe { vgetq_lane_s64(self.neon, 0).min(vgetq_lane_s64(self.neon, 1)) }
597      } else {
598        self.arr[0].min(self.arr[1])
599      }
600    }
601  }
602
603  #[inline]
604  #[must_use]
605  pub fn abs(self) -> Self {
606    pick! {
607      // x86 doesn't have this builtin
608      if #[cfg(target_feature="simd128")] {
609        Self { simd: i64x2_abs(self.simd) }
610      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
611        unsafe {Self { neon: vabsq_s64(self.neon) }}
612      } else {
613        let arr: [i64; 2] = cast(self);
614        cast(
615          [
616            arr[0].wrapping_abs(),
617            arr[1].wrapping_abs(),
618          ])
619      }
620    }
621  }
622
623  #[inline]
624  #[must_use]
625  pub fn unsigned_abs(self) -> u64x2 {
626    pick! {
627      // x86 doesn't have this builtin
628      if #[cfg(target_feature="simd128")] {
629        u64x2 { simd: i64x2_abs(self.simd) }
630      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
631        unsafe {u64x2 { neon: vreinterpretq_u64_s64(vabsq_s64(self.neon)) }}
632      } else {
633        let arr: [i64; 2] = cast(self);
634        cast(
635          [
636            arr[0].unsigned_abs(),
637            arr[1].unsigned_abs(),
638          ])
639      }
640    }
641  }
642
643  signed_fn_signum!();
644
645  #[inline]
646  #[must_use]
647  pub fn round_float(self) -> f64x2 {
648    let arr: [i64; 2] = cast(self);
649    cast([arr[0] as f64, arr[1] as f64])
650  }
651
652  /// returns the bit mask for each high bit set in the vector with the lowest
653  /// lane being the lowest bit
654  #[inline]
655  #[must_use]
656  #[doc(alias("movemask", "move_mask"))]
657  pub fn to_bitmask(self) -> u32 {
658    pick! {
659      if #[cfg(target_feature="sse")] {
660        // use f64 move_mask since it is the same size as i64
661        move_mask_m128d(cast(self.sse)) as u32
662      } else if #[cfg(target_feature="simd128")] {
663        i64x2_bitmask(self.simd) as u32
664      } else {
665        // nothing amazingly efficient for neon
666        let arr: [u64; 2] = cast(self);
667        (arr[0] >> 63 | ((arr[1] >> 62) & 2)) as u32
668      }
669    }
670  }
671
672  /// true if any high bits are set for any value in the vector
673  #[inline]
674  #[must_use]
675  pub fn any(self) -> bool {
676    pick! {
677      if #[cfg(target_feature="sse")] {
678        // use f64 move_mask since it is the same size as i64
679        move_mask_m128d(cast(self.sse)) != 0
680      } else if #[cfg(target_feature="simd128")] {
681        i64x2_bitmask(self.simd) != 0
682      } else {
683        let v : [u64;2] = cast(self);
684        ((v[0] | v[1]) & 0x8000000000000000) != 0
685      }
686    }
687  }
688
689  /// true if all high bits are set for every value in the vector
690  #[inline]
691  #[must_use]
692  pub fn all(self) -> bool {
693    pick! {
694      if #[cfg(target_feature="avx2")] {
695        // use f64 move_mask since it is the same size as i64
696        move_mask_m128d(cast(self.sse)) == 0b11
697      }  else if #[cfg(target_feature="simd128")] {
698        i64x2_bitmask(self.simd) == 0b11
699      } else {
700        let v : [u64;2] = cast(self);
701        ((v[0] & v[1]) & 0x8000000000000000) == 0x8000000000000000
702      }
703    }
704  }
705
706  /// true if no high bits are set for any values of the vector
707  #[inline]
708  #[must_use]
709  pub fn none(self) -> bool {
710    !self.any()
711  }
712
713  // Sometimes used for `transpose`.
714  #[must_use]
715  #[inline]
716  #[allow(dead_code)]
717  pub(crate) fn unpack_lo(self, b: Self) -> Self {
718    pick! {
719      if #[cfg(target_feature="sse2")] {
720        Self { sse: unpack_low_i64_m128i(self.sse, b.sse) }
721      } else if #[cfg(target_feature="simd128")] {
722        Self { simd: i64x2_shuffle::<0, 2>(self.simd, b.simd) }
723      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
724        Self { neon: unsafe { vzip1q_s64(self.neon, b.neon) } }
725      } else {
726        Self::new([self.as_array()[0], b.as_array()[0]])
727      }
728    }
729  }
730
731  // Sometimes used for `transpose`.
732  #[must_use]
733  #[inline]
734  #[allow(dead_code)]
735  pub(crate) fn unpack_hi(self, b: Self) -> Self {
736    pick! {
737      if #[cfg(target_feature="sse2")] {
738        Self { sse: unpack_high_i64_m128i(self.sse, b.sse) }
739      } else if #[cfg(target_feature="simd128")] {
740        Self { simd: i64x2_shuffle::<1, 3>(self.simd, b.simd) }
741      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
742        Self { neon: unsafe { vzip2q_s64(self.neon, b.neon) } }
743      } else {
744        Self::new([self.as_array()[1], b.as_array()[1]])
745      }
746    }
747  }
748
749  /// Transpose matrix of 2x2 `i64` matrix.
750  #[inline]
751  pub fn transpose(data: [i64x2; 2]) -> [i64x2; 2] {
752    pick! {
753      if #[cfg(any(
754        target_feature="sse2",
755        all(target_feature="neon",target_arch="aarch64"),
756        target_feature="simd128",
757      ))] {
758        [data[0].unpack_lo(data[1]), data[0].unpack_hi(data[1])]
759      } else {
760        let [x, y, z, w]: [i64; 4] = cast(data);
761        cast([x, z, y, w])
762      }
763    }
764  }
765
766  #[inline]
767  pub fn to_array(self) -> [i64; 2] {
768    cast(self)
769  }
770
771  #[inline]
772  pub fn as_array(&self) -> &[i64; 2] {
773    cast_ref(self)
774  }
775
776  #[inline]
777  pub fn as_mut_array(&mut self) -> &mut [i64; 2] {
778    cast_mut(self)
779  }
780
781  #[inline]
782  #[must_use]
783  pub fn min(self, rhs: Self) -> Self {
784    self.simd_lt(rhs).blend(self, rhs)
785  }
786
787  #[inline]
788  #[must_use]
789  pub fn max(self, rhs: Self) -> Self {
790    self.simd_gt(rhs).blend(self, rhs)
791  }
792
793  integer_fn_clamp!();
794
795  #[inline]
796  #[must_use]
797  pub fn saturating_add(self, rhs: Self) -> Self {
798    pick! {
799      if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
800        let result = self + rhs;
801        let overflow = (!(self ^ rhs) & (self ^ result)).is_negative();
802        let negative = self.is_negative();
803
804        overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
805      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
806        unsafe { Self { neon: vqaddq_s64(self.neon, rhs.neon) } }
807      } else {
808        Self {
809          arr: [
810            self.arr[0].saturating_add(rhs.arr[0]),
811            self.arr[1].saturating_add(rhs.arr[1]),
812          ],
813        }
814      }
815    }
816  }
817
818  #[inline]
819  #[must_use]
820  pub fn saturating_sub(self, rhs: Self) -> Self {
821    pick! {
822      if #[cfg(any(target_feature="sse2", target_feature="simd128"))] {
823        let result = self - rhs;
824        let overflow = ((self ^ rhs) & (self ^ result)).is_negative();
825        let negative = self.is_negative();
826
827        overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
828      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
829        unsafe { Self { neon: vqsubq_s64(self.neon, rhs.neon) } }
830      } else {
831        Self {
832          arr: [
833            self.arr[0].saturating_sub(rhs.arr[0]),
834            self.arr[1].saturating_sub(rhs.arr[1]),
835          ],
836        }
837      }
838    }
839  }
840
841  /// Lanewise saturating multiply.
842  #[inline]
843  #[must_use]
844  pub fn saturating_mul(self, rhs: Self) -> Self {
845    let self_array = self.to_array();
846    let rhs_array = rhs.to_array();
847
848    Self::new([
849      self_array[0].saturating_mul(rhs_array[0]),
850      self_array[1].saturating_mul(rhs_array[1]),
851    ])
852  }
853
854  integer_fn_saturating_div!([0, 1]);
855}