Skip to main content

wide/
i64x8_.rs

1use super::*;
2
3pick! {
4  if #[cfg(target_feature="avx512f")] {
5    #[derive(Default, Clone, Copy, PartialEq, Eq)]
6    #[repr(C, align(64))]
7    pub struct i64x8 { pub(crate) avx512: m512i }
8  } else {
9    #[derive(Default, Clone, Copy, PartialEq, Eq)]
10    #[repr(C, align(64))]
11    pub struct i64x8 { pub(crate) a : i64x4, pub(crate) b : i64x4 }
12  }
13}
14
15int_uint_consts!(i64, 8, i64x8, 512);
16
17unsafe impl Zeroable for i64x8 {}
18unsafe impl Pod for i64x8 {}
19
20impl AlignTo for i64x8 {
21  type Elem = i64;
22}
23
24impl Add for i64x8 {
25  type Output = Self;
26  #[inline]
27  fn add(self, rhs: Self) -> Self::Output {
28    pick! {
29      if #[cfg(target_feature="avx512f")] {
30        Self { avx512: add_i64_m512i(self.avx512, rhs.avx512) }
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 i64x8 {
42  type Output = Self;
43  #[inline]
44  fn sub(self, rhs: Self) -> Self::Output {
45    pick! {
46      if #[cfg(target_feature="avx512f")] {
47        Self { avx512: sub_i64_m512i(self.avx512, rhs.avx512) }
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 i64x8 {
59  type Output = Self;
60  #[inline]
61  fn mul(self, rhs: Self) -> Self::Output {
62    pick! {
63      if #[cfg(target_feature="avx512f")] {
64        let arr1: [i64; 8] = cast(self);
65        let arr2: [i64; 8] = 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          arr1[4].wrapping_mul(arr2[4]),
72          arr1[5].wrapping_mul(arr2[5]),
73          arr1[6].wrapping_mul(arr2[6]),
74          arr1[7].wrapping_mul(arr2[7]),
75        ])
76      } else {
77        Self { a: self.a.mul(rhs.a), b: self.b.mul(rhs.b) }
78      }
79    }
80  }
81}
82
83integer_impl_div_rem!(i64, i64x8, [0, 1, 2, 3, 4, 5, 6, 7]);
84
85impl Shr for i64x8 {
86  type Output = Self;
87
88  #[inline]
89  fn shr(self, rhs: Self) -> Self::Output {
90    pick! {
91      if #[cfg(target_feature="avx512f")] {
92        // TODO(safe_arch): add shr_each_i64_m512i (arithmetic right shift)
93        // Self { avx512: shr_each_i64_m512i(self.avx512, rhs.avx512) }
94        // Fallback for now:
95        let a: [i64; 8] = cast(self);
96        let r: [i64; 8] = cast(rhs);
97        cast([
98          a[0].wrapping_shr(r[0] as u32),
99          a[1].wrapping_shr(r[1] as u32),
100          a[2].wrapping_shr(r[2] as u32),
101          a[3].wrapping_shr(r[3] as u32),
102          a[4].wrapping_shr(r[4] as u32),
103          a[5].wrapping_shr(r[5] as u32),
104          a[6].wrapping_shr(r[6] as u32),
105          a[7].wrapping_shr(r[7] as u32),
106        ])
107      } else {
108        // widen via two halves
109        Self {
110          a: self.a.shr(rhs.a),
111          b: self.b.shr(rhs.b),
112        }
113      }
114    }
115  }
116}
117
118impl Shl for i64x8 {
119  type Output = Self;
120
121  #[inline]
122  fn shl(self, rhs: Self) -> Self::Output {
123    pick! {
124      if #[cfg(target_feature="avx512f")] {
125        // TODO(safe_arch): add shl_each_i64_m512i
126        // Self { avx512: shl_each_i64_m512i(self.avx512, rhs.avx512) }
127        // Fallback for now:
128        let a: [i64; 8] = cast(self);
129        let r: [i64; 8] = cast(rhs);
130        cast([
131          a[0].wrapping_shl(r[0] as u32),
132          a[1].wrapping_shl(r[1] as u32),
133          a[2].wrapping_shl(r[2] as u32),
134          a[3].wrapping_shl(r[3] as u32),
135          a[4].wrapping_shl(r[4] as u32),
136          a[5].wrapping_shl(r[5] as u32),
137          a[6].wrapping_shl(r[6] as u32),
138          a[7].wrapping_shl(r[7] as u32),
139        ])
140      } else {
141        // widen via two halves
142        Self {
143          a: self.a.shl(rhs.a),
144          b: self.b.shl(rhs.b),
145        }
146      }
147    }
148  }
149}
150
151impl Add<i64> for i64x8 {
152  type Output = Self;
153  #[inline]
154  fn add(self, rhs: i64) -> Self::Output {
155    self.add(Self::splat(rhs))
156  }
157}
158
159impl Sub<i64> for i64x8 {
160  type Output = Self;
161  #[inline]
162  fn sub(self, rhs: i64) -> Self::Output {
163    self.sub(Self::splat(rhs))
164  }
165}
166
167impl Mul<i64> for i64x8 {
168  type Output = Self;
169  #[inline]
170  fn mul(self, rhs: i64) -> Self::Output {
171    self.mul(Self::splat(rhs))
172  }
173}
174
175impl Add<i64x8> for i64 {
176  type Output = i64x8;
177  #[inline]
178  fn add(self, rhs: i64x8) -> Self::Output {
179    i64x8::splat(self).add(rhs)
180  }
181}
182
183impl Sub<i64x8> for i64 {
184  type Output = i64x8;
185  #[inline]
186  fn sub(self, rhs: i64x8) -> Self::Output {
187    i64x8::splat(self).sub(rhs)
188  }
189}
190
191impl Mul<i64x8> for i64 {
192  type Output = i64x8;
193  #[inline]
194  fn mul(self, rhs: i64x8) -> Self::Output {
195    i64x8::splat(self).mul(rhs)
196  }
197}
198
199impl BitAnd for i64x8 {
200  type Output = Self;
201  #[inline]
202  fn bitand(self, rhs: Self) -> Self::Output {
203    pick! {
204      if #[cfg(target_feature="avx512f")] {
205        Self { avx512: bitand_m512i(self.avx512, rhs.avx512) }
206      } else {
207        Self {
208          a : self.a.bitand(rhs.a),
209          b : self.b.bitand(rhs.b),
210        }
211      }
212    }
213  }
214}
215
216impl BitOr for i64x8 {
217  type Output = Self;
218  #[inline]
219  fn bitor(self, rhs: Self) -> Self::Output {
220    pick! {
221    if #[cfg(target_feature="avx512f")] {
222        Self { avx512: bitor_m512i(self.avx512, rhs.avx512) }
223      } else {
224        Self {
225          a : self.a.bitor(rhs.a),
226          b : self.b.bitor(rhs.b),
227        }
228      }
229    }
230  }
231}
232
233impl BitXor for i64x8 {
234  type Output = Self;
235  #[inline]
236  fn bitxor(self, rhs: Self) -> Self::Output {
237    pick! {
238      if #[cfg(target_feature="avx512f")] {
239        Self { avx512: bitxor_m512i(self.avx512, rhs.avx512) }
240      } else {
241        Self {
242          a : self.a.bitxor(rhs.a),
243          b : self.b.bitxor(rhs.b),
244        }
245      }
246    }
247  }
248}
249
250macro_rules! impl_shl_t_for_i64x8 {
251  ($($shift_type:ty),+ $(,)?) => {
252    $(impl Shl<$shift_type> for i64x8 {
253      type Output = Self;
254      /// Shifts all lanes by the value given.
255      #[inline]
256      fn shl(self, rhs: $shift_type) -> Self::Output {
257        pick! {
258          if #[cfg(target_feature="avx512f")] {
259            let shift = cast(rhs as u64);
260            Self { avx512: shl_all_u64_m512i(self.avx512, shift) }
261          } else {
262            Self {
263              a : self.a.shl(rhs),
264              b : self.b.shl(rhs),
265            }
266          }
267        }
268      }
269    })+
270  };
271}
272impl_shl_t_for_i64x8!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
273
274macro_rules! impl_shr_t_for_i64x8 {
275  ($($shift_type:ty),+ $(,)?) => {
276    $(impl Shr<$shift_type> for i64x8 {
277      type Output = Self;
278      /// Shifts all lanes by the value given.
279      #[inline]
280      fn shr(self, rhs: $shift_type) -> Self::Output {
281        pick! {
282          if #[cfg(target_feature="avx512f")] {
283            let shift = cast(rhs as u64);
284            Self { avx512: shr_all_i64_m512i(self.avx512, shift) }
285          } else {
286            Self {
287              a : self.a.shr(rhs),
288              b : self.b.shr(rhs),
289            }
290          }
291        }
292      }
293    })+
294  };
295}
296impl_shr_t_for_i64x8!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
297
298#[expect(deprecated)]
299impl CmpEq for i64x8 {
300  type Output = Self;
301  #[inline]
302  fn simd_eq(self, rhs: Self) -> Self::Output {
303    pick! {
304      if #[cfg(target_feature="avx512f")] {
305        Self { avx512: cmp_op_mask_i64_m512i::<{cmp_int_op!(Eq)}>(self.avx512, rhs.avx512) }
306      } else {
307        Self {
308          a : self.a.simd_eq(rhs.a),
309          b : self.b.simd_eq(rhs.b),
310        }
311      }
312    }
313  }
314}
315
316#[expect(deprecated)]
317impl CmpGt for i64x8 {
318  type Output = Self;
319  #[inline]
320  fn simd_gt(self, rhs: Self) -> Self::Output {
321    pick! {
322      if #[cfg(target_feature="avx512f")] {
323        Self { avx512: cmp_op_mask_i64_m512i::<{cmp_int_op!(Nle)}>(self.avx512, rhs.avx512) }
324      } else {
325        Self {
326          a : self.a.simd_gt(rhs.a),
327          b : self.b.simd_gt(rhs.b),
328        }
329      }
330    }
331  }
332}
333
334#[expect(deprecated)]
335impl CmpLt for i64x8 {
336  type Output = Self;
337  #[inline]
338  fn simd_lt(self, rhs: Self) -> Self::Output {
339    pick! {
340      if #[cfg(target_feature="avx512f")] {
341        Self { avx512: cmp_op_mask_i64_m512i::<{cmp_int_op!(Lt)}>(self.avx512, rhs.avx512) }
342      } else {
343        Self {
344          a : rhs.a.simd_gt(self.a),
345          b : rhs.b.simd_gt(self.b),
346        }
347      }
348    }
349  }
350}
351
352#[expect(deprecated)]
353impl CmpNe for i64x8 {
354  type Output = Self;
355  #[inline]
356  fn simd_ne(self, rhs: Self) -> Self::Output {
357    pick! {
358      if #[cfg(target_feature="avx512f")] {
359        Self { avx512: cmp_op_mask_i64_m512i::<{cmp_int_op!(Ne)}>(self.avx512, rhs.avx512) }
360      } else {
361        Self {
362          a : self.a.simd_ne(rhs.a),
363          b : self.b.simd_ne(rhs.b),
364        }
365      }
366    }
367  }
368}
369
370#[expect(deprecated)]
371impl CmpLe for i64x8 {
372  type Output = Self;
373  #[inline]
374  fn simd_le(self, rhs: Self) -> Self::Output {
375    pick! {
376      if #[cfg(target_feature="avx512f")] {
377        Self { avx512: cmp_op_mask_i64_m512i::<{cmp_int_op!(Le)}>(self.avx512, rhs.avx512) }
378      } else {
379        Self {
380          a : self.a.simd_le(rhs.a),
381          b : self.b.simd_le(rhs.b),
382        }
383      }
384    }
385  }
386}
387
388#[expect(deprecated)]
389impl CmpGe for i64x8 {
390  type Output = Self;
391  #[inline]
392  fn simd_ge(self, rhs: Self) -> Self::Output {
393    pick! {
394      if #[cfg(target_feature="avx512f")] {
395        Self { avx512: cmp_op_mask_i64_m512i::<{cmp_int_op!(Nlt)}>(self.avx512, rhs.avx512) }
396      } else {
397        Self {
398          a : self.a.simd_ge(rhs.a),
399          b : self.b.simd_ge(rhs.b),
400        }
401      }
402    }
403  }
404}
405
406impl i64x8 {
407  #[inline]
408  #[must_use]
409  pub const fn new(array: [i64; 8]) -> Self {
410    unsafe { core::mem::transmute(array) }
411  }
412
413  simd_comparison_fns!();
414
415  #[inline]
416  #[must_use]
417  pub fn blend(self, t: Self, f: Self) -> Self {
418    pick! {
419      if #[cfg(target_feature="avx512f")] {
420        Self { avx512: blend_varying_i8_m512i(f.avx512,t.avx512,movepi8_mask_m512i(self.avx512)) }
421      } else {
422        Self {
423          a : self.a.blend(t.a, f.a),
424          b : self.b.blend(t.b, f.b),
425        }
426      }
427    }
428  }
429
430  /// Returns true for each positive element and false if it is zero or
431  /// negative.
432  #[inline]
433  #[must_use]
434  pub fn is_positive(self) -> Self {
435    pick! {
436      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
437        // `neon` has dedicated greater-than-zero intrinsics.
438        Self {
439          a: self.a.is_positive(),
440          b: self.b.is_positive(),
441        }
442      } else {
443        self.simd_gt(Self::ZERO)
444      }
445    }
446  }
447
448  /// Returns true for each negative element and false if it is zero or
449  /// positive.
450  #[inline]
451  #[must_use]
452  pub fn is_negative(self) -> Self {
453    pick! {
454      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
455        // `neon` has dedicated less-than-zero intrinsics.
456        Self {
457          a: self.a.is_negative(),
458          b: self.b.is_negative(),
459        }
460      } else {
461        self.simd_lt(Self::ZERO)
462      }
463    }
464  }
465
466  #[inline]
467  #[must_use]
468  pub fn reduce_add(self) -> i64 {
469    let array: [i64x4; 2] = cast(self);
470    (array[0] + array[1]).reduce_add()
471  }
472
473  #[inline]
474  #[must_use]
475  pub fn reduce_max(self) -> i64 {
476    let array: [i64x4; 2] = cast(self);
477    array[0].max(array[1]).reduce_max()
478  }
479
480  #[inline]
481  #[must_use]
482  pub fn reduce_min(self) -> i64 {
483    let array: [i64x4; 2] = cast(self);
484    array[0].min(array[1]).reduce_min()
485  }
486
487  #[inline]
488  #[must_use]
489  pub fn abs(self) -> Self {
490    pick! {
491      if #[cfg(target_feature="avx512f")] {
492        // AVX512 might have this, unsure for now
493        let arr: [i64; 8] = cast(self);
494        cast(
495          [
496            arr[0].wrapping_abs(),
497            arr[1].wrapping_abs(),
498            arr[2].wrapping_abs(),
499            arr[3].wrapping_abs(),
500            arr[4].wrapping_abs(),
501            arr[5].wrapping_abs(),
502            arr[6].wrapping_abs(),
503            arr[7].wrapping_abs(),
504          ])
505      } else {
506        Self {
507          a : self.a.abs(),
508          b : self.b.abs(),
509        }
510      }
511    }
512  }
513
514  #[inline]
515  #[must_use]
516  pub fn unsigned_abs(self) -> u64x8 {
517    pick! {
518      if #[cfg(target_feature="avx512f")] {
519        // AVX512 might have this, unsure for now
520        let arr: [i64; 8] = cast(self);
521        cast(
522          [
523            arr[0].unsigned_abs(),
524            arr[1].unsigned_abs(),
525            arr[2].unsigned_abs(),
526            arr[3].unsigned_abs(),
527            arr[4].unsigned_abs(),
528            arr[5].unsigned_abs(),
529            arr[6].unsigned_abs(),
530            arr[7].unsigned_abs(),
531          ])
532      } else {
533        u64x8 {
534          a : self.a.unsigned_abs(),
535          b : self.b.unsigned_abs(),
536        }
537      }
538    }
539  }
540
541  signed_fn_signum!();
542
543  #[inline]
544  #[must_use]
545  pub fn round_float(self) -> f64x8 {
546    let arr: [i64; 8] = cast(self);
547    cast([
548      arr[0] as f64,
549      arr[1] as f64,
550      arr[2] as f64,
551      arr[3] as f64,
552      arr[4] as f64,
553      arr[5] as f64,
554      arr[6] as f64,
555      arr[7] as f64,
556    ])
557  }
558
559  /// returns the bit mask for each high bit set in the vector with the lowest
560  /// lane being the lowest bit
561  #[inline]
562  #[must_use]
563  #[doc(alias("movemask", "move_mask"))]
564  pub fn to_bitmask(self) -> u32 {
565    pick! {
566      if #[cfg(target_feature="avx512dq")] {
567        // use f64 move_mask since it is the same size as i64
568        movepi64_mask_m512d(cast(self.avx512)) as u32
569      } else {
570        self.a.to_bitmask() | (self.b.to_bitmask() << 4)
571      }
572    }
573  }
574
575  /// true if any high bits are set for any value in the vector
576  #[inline]
577  #[must_use]
578  pub fn any(self) -> bool {
579    pick! {
580      if #[cfg(target_feature="avx512f")] {
581        movepi64_mask_m512d(cast(self.avx512)) != 0
582      } else {
583        let [a, b]: [i64x4; 2] = cast(self);
584        (a | b).any()
585      }
586    }
587  }
588
589  /// true if all high bits are set for every value in the vector
590  #[inline]
591  #[must_use]
592  pub fn all(self) -> bool {
593    pick! {
594      if #[cfg(target_feature="avx512bw")] {
595        movepi64_mask_m512d(cast(self.avx512)) == 0b11111111
596      } else {
597        let [a, b]: [i64x4; 2] = cast(self);
598        (a & b).all()
599      }
600    }
601  }
602
603  /// true if no high bits are set for any values of the vector
604  #[inline]
605  #[must_use]
606  pub fn none(self) -> bool {
607    !self.any()
608  }
609
610  /// Transpose matrix of 8x8 `i64` matrix. Currently not accelerated.
611  #[must_use]
612  #[inline]
613  pub fn transpose(data: [i64x8; 8]) -> [i64x8; 8] {
614    // Can this be optimized?
615
616    #[inline(always)]
617    fn transpose_column(data: &[i64x8; 8], index: usize) -> i64x8 {
618      i64x8::new([
619        data[0].as_array()[index],
620        data[1].as_array()[index],
621        data[2].as_array()[index],
622        data[3].as_array()[index],
623        data[4].as_array()[index],
624        data[5].as_array()[index],
625        data[6].as_array()[index],
626        data[7].as_array()[index],
627      ])
628    }
629
630    [
631      transpose_column(&data, 0),
632      transpose_column(&data, 1),
633      transpose_column(&data, 2),
634      transpose_column(&data, 3),
635      transpose_column(&data, 4),
636      transpose_column(&data, 5),
637      transpose_column(&data, 6),
638      transpose_column(&data, 7),
639    ]
640  }
641
642  #[inline]
643  pub fn to_array(self) -> [i64; 8] {
644    cast(self)
645  }
646
647  #[inline]
648  pub fn as_array(&self) -> &[i64; 8] {
649    cast_ref(self)
650  }
651
652  #[inline]
653  pub fn as_mut_array(&mut self) -> &mut [i64; 8] {
654    cast_mut(self)
655  }
656
657  #[inline]
658  #[must_use]
659  pub fn min(self, rhs: Self) -> Self {
660    pick! {
661      if #[cfg(target_feature="avx512f")] {
662        Self { avx512: min_i64_m512i(self.avx512, rhs.avx512) }
663      } else {
664        Self {
665          a: self.a.min(rhs.a),
666          b: self.b.min(rhs.b),
667        }
668      }
669    }
670  }
671
672  #[inline]
673  #[must_use]
674  pub fn max(self, rhs: Self) -> Self {
675    pick! {
676      if #[cfg(target_feature="avx512f")] {
677        Self { avx512: max_i64_m512i(self.avx512, rhs.avx512) }
678      } else {
679        Self {
680          a: self.a.max(rhs.a),
681          b: self.b.max(rhs.b),
682        }
683      }
684    }
685  }
686
687  integer_fn_clamp!();
688
689  #[inline]
690  #[must_use]
691  pub fn saturating_add(self, rhs: Self) -> Self {
692    pick! {
693      if #[cfg(target_feature="avx512f")] {
694        let result = self + rhs;
695        let overflow = (!(self ^ rhs) & (self ^ result)).is_negative();
696        let negative = self.is_negative();
697
698        overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
699      } else {
700        Self {
701          a: self.a.saturating_add(rhs.a),
702          b: self.b.saturating_add(rhs.b),
703        }
704      }
705    }
706  }
707
708  #[inline]
709  #[must_use]
710  pub fn saturating_sub(self, rhs: Self) -> Self {
711    pick! {
712      if #[cfg(target_feature="avx512f")] {
713        let result = self - rhs;
714        let overflow = ((self ^ rhs) & (self ^ result)).is_negative();
715        let negative = self.is_negative();
716
717        overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
718      } else {
719        Self {
720          a: self.a.saturating_sub(rhs.a),
721          b: self.b.saturating_sub(rhs.b),
722        }
723      }
724    }
725  }
726
727  /// Lanewise saturating multiply.
728  #[inline]
729  #[must_use]
730  pub fn saturating_mul(self, rhs: Self) -> Self {
731    let self_array = self.to_array();
732    let rhs_array = rhs.to_array();
733
734    Self::new([
735      self_array[0].saturating_mul(rhs_array[0]),
736      self_array[1].saturating_mul(rhs_array[1]),
737      self_array[2].saturating_mul(rhs_array[2]),
738      self_array[3].saturating_mul(rhs_array[3]),
739      self_array[4].saturating_mul(rhs_array[4]),
740      self_array[5].saturating_mul(rhs_array[5]),
741      self_array[6].saturating_mul(rhs_array[6]),
742      self_array[7].saturating_mul(rhs_array[7]),
743    ])
744  }
745
746  integer_fn_saturating_div!([0, 1, 2, 3, 4, 5, 6, 7]);
747}
748
749impl Not for i64x8 {
750  type Output = Self;
751  #[inline]
752  fn not(self) -> Self::Output {
753    pick! {
754      if #[cfg(target_feature="avx512f")] {
755        Self { avx512: bitxor_m512i(self.avx512, set_splat_i64_m512i(-1)) }
756      } else {
757        Self {
758          a : self.a.not(),
759          b : self.b.not(),
760        }
761      }
762    }
763  }
764}