Skip to main content

wide/
i32x8_.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 i32x8 { pub(crate) avx2: m256i }
8  } else {
9    #[derive(Default, Clone, Copy, PartialEq, Eq)]
10    #[repr(C, align(32))]
11    pub struct i32x8 { pub(crate) a : i32x4, pub(crate) b : i32x4}
12  }
13}
14
15int_uint_consts!(i32, 8, i32x8, 256);
16
17unsafe impl Zeroable for i32x8 {}
18unsafe impl Pod for i32x8 {}
19
20impl AlignTo for i32x8 {
21  type Elem = i32;
22}
23
24impl Add for i32x8 {
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_i32_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 i32x8 {
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_i32_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 i32x8 {
59  type Output = Self;
60  #[inline]
61  fn mul(self, rhs: Self) -> Self::Output {
62    pick! {
63      if #[cfg(target_feature="avx2")] {
64        Self { avx2: mul_i32_keep_low_m256i(self.avx2, rhs.avx2) }
65      } else {
66        Self {
67          a : self.a.mul(rhs.a),
68          b : self.b.mul(rhs.b),
69        }
70      }
71    }
72  }
73}
74
75integer_impl_div_rem!(i32, i32x8, [0, 1, 2, 3, 4, 5, 6, 7]);
76
77impl Add<i32> for i32x8 {
78  type Output = Self;
79  #[inline]
80  fn add(self, rhs: i32) -> Self::Output {
81    self.add(Self::splat(rhs))
82  }
83}
84
85impl Sub<i32> for i32x8 {
86  type Output = Self;
87  #[inline]
88  fn sub(self, rhs: i32) -> Self::Output {
89    self.sub(Self::splat(rhs))
90  }
91}
92
93impl Mul<i32> for i32x8 {
94  type Output = Self;
95  #[inline]
96  fn mul(self, rhs: i32) -> Self::Output {
97    self.mul(Self::splat(rhs))
98  }
99}
100
101impl Add<i32x8> for i32 {
102  type Output = i32x8;
103  #[inline]
104  fn add(self, rhs: i32x8) -> Self::Output {
105    i32x8::splat(self) + rhs
106  }
107}
108
109impl Sub<i32x8> for i32 {
110  type Output = i32x8;
111  #[inline]
112  fn sub(self, rhs: i32x8) -> Self::Output {
113    i32x8::splat(self) - rhs
114  }
115}
116
117impl Mul<i32x8> for i32 {
118  type Output = i32x8;
119  #[inline]
120  fn mul(self, rhs: i32x8) -> Self::Output {
121    i32x8::splat(self) * rhs
122  }
123}
124
125impl BitAnd for i32x8 {
126  type Output = Self;
127  #[inline]
128  fn bitand(self, rhs: Self) -> Self::Output {
129    pick! {
130      if #[cfg(target_feature="avx2")] {
131        Self { avx2: bitand_m256i(self.avx2, rhs.avx2) }
132      } else {
133        Self {
134          a : self.a.bitand(rhs.a),
135          b : self.b.bitand(rhs.b),
136        }
137      }
138    }
139  }
140}
141
142impl BitOr for i32x8 {
143  type Output = Self;
144  #[inline]
145  fn bitor(self, rhs: Self) -> Self::Output {
146    pick! {
147    if #[cfg(target_feature="avx2")] {
148      Self { avx2: bitor_m256i(self.avx2, rhs.avx2) }
149    } else {
150      Self {
151        a : self.a.bitor(rhs.a),
152        b : self.b.bitor(rhs.b),
153      }
154    }    }
155  }
156}
157
158impl BitXor for i32x8 {
159  type Output = Self;
160  #[inline]
161  fn bitxor(self, rhs: Self) -> Self::Output {
162    pick! {
163      if #[cfg(target_feature="avx2")] {
164        Self { avx2: bitxor_m256i(self.avx2, rhs.avx2) }
165      } else {
166        Self {
167          a : self.a.bitxor(rhs.a),
168          b : self.b.bitxor(rhs.b),
169        }
170      }
171    }
172  }
173}
174
175macro_rules! impl_shl_t_for_i32x8 {
176  ($($shift_type:ty),+ $(,)?) => {
177    $(impl Shl<$shift_type> for i32x8 {
178      type Output = Self;
179      /// Shifts all lanes by the value given.
180      #[inline]
181      fn shl(self, rhs: $shift_type) -> Self::Output {
182        pick! {
183          if #[cfg(target_feature="avx2")] {
184            let shift = cast([rhs as u64, 0]);
185            Self { avx2: shl_all_u32_m256i(self.avx2, shift) }
186          } else {
187            Self {
188              a : self.a.shl(rhs),
189              b : self.b.shl(rhs),
190            }
191          }
192        }
193      }
194    })+
195  };
196}
197impl_shl_t_for_i32x8!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
198
199macro_rules! impl_shr_t_for_i32x8 {
200  ($($shift_type:ty),+ $(,)?) => {
201    $(impl Shr<$shift_type> for i32x8 {
202      type Output = Self;
203      /// Shifts all lanes by the value given.
204      #[inline]
205      fn shr(self, rhs: $shift_type) -> Self::Output {
206        pick! {
207          if #[cfg(target_feature="avx2")] {
208            let shift = cast([rhs as u64, 0]);
209            Self { avx2: shr_all_i32_m256i(self.avx2, shift) }
210          } else {
211            Self {
212              a : self.a.shr(rhs),
213              b : self.b.shr(rhs),
214            }
215          }
216        }
217      }
218    })+
219  };
220}
221
222impl_shr_t_for_i32x8!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
223
224/// Shifts lanes by the corresponding lane.
225///
226/// Bitwise shift-right; yields `self >> mask(rhs)`, where mask removes any
227/// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
228/// of the type. (same as `wrapping_shr`)
229impl Shr<i32x8> for i32x8 {
230  type Output = Self;
231
232  #[inline]
233  fn shr(self, rhs: i32x8) -> Self::Output {
234    pick! {
235      if #[cfg(target_feature="avx2")] {
236        // ensure same behavior as scalar
237        let shift_by = bitand_m256i(rhs.avx2, set_splat_i32_m256i(31));
238        Self { avx2: shr_each_i32_m256i(self.avx2, shift_by ) }
239      } else {
240        Self {
241          a : self.a.shr(rhs.a),
242          b : self.b.shr(rhs.b),
243        }
244      }
245    }
246  }
247}
248
249/// Shifts lanes by the corresponding lane.
250///
251/// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
252/// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
253/// of the type. (same as `wrapping_shl`)
254impl Shl<i32x8> for i32x8 {
255  type Output = Self;
256
257  #[inline]
258  fn shl(self, rhs: i32x8) -> Self::Output {
259    pick! {
260      if #[cfg(target_feature="avx2")] {
261        // ensure same behavior as scalar wrapping_shl by masking the shift count
262        let shift_by = bitand_m256i(rhs.avx2, set_splat_i32_m256i(31));
263        // shl is the same for unsigned and signed
264        Self { avx2: shl_each_u32_m256i(self.avx2, shift_by) }
265      } else {
266        Self {
267          a : self.a.shl(rhs.a),
268          b : self.b.shl(rhs.b),
269        }
270      }
271    }
272  }
273}
274
275#[expect(deprecated)]
276impl CmpEq for i32x8 {
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_i32_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 i32x8 {
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_i32_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 i32x8 {
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_i32_m256i(rhs.avx2, self.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 i32x8 {
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 i32x8 {
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 i32x8 {
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 From<i16x8> for i32x8 {
384  #[inline]
385  fn from(value: i16x8) -> Self {
386    i32x8::from_i16x8(value)
387  }
388}
389
390impl i32x8 {
391  #[inline]
392  #[must_use]
393  pub const fn new(array: [i32; 8]) -> Self {
394    unsafe { core::mem::transmute(array) }
395  }
396
397  simd_comparison_fns!();
398
399  /// widens and sign extends to `i32x8`
400  #[inline]
401  #[must_use]
402  pub fn from_i16x8(v: i16x8) -> Self {
403    pick! {
404      if #[cfg(target_feature="avx2")] {
405        i32x8 { avx2:convert_to_i32_m256i_from_i16_m128i(v.sse) }
406      } else if #[cfg(target_feature="sse2")] {
407        i32x8 {
408          a: i32x4 { sse: shr_imm_i32_m128i::<16>( unpack_low_i16_m128i(v.sse, v.sse)) },
409          b: i32x4 { sse: shr_imm_i32_m128i::<16>( unpack_high_i16_m128i(v.sse, v.sse)) },
410        }
411      } else {
412        i32x8::new([
413          i32::from(v.as_array()[0]),
414          i32::from(v.as_array()[1]),
415          i32::from(v.as_array()[2]),
416          i32::from(v.as_array()[3]),
417          i32::from(v.as_array()[4]),
418          i32::from(v.as_array()[5]),
419          i32::from(v.as_array()[6]),
420          i32::from(v.as_array()[7]),
421        ])
422      }
423    }
424  }
425
426  /// widens and zero extends to `i32x8`
427  #[inline]
428  #[must_use]
429  pub fn from_u16x8(v: u16x8) -> Self {
430    pick! {
431      if #[cfg(target_feature="avx2")] {
432        i32x8 { avx2:convert_to_i32_m256i_from_u16_m128i(v.sse) }
433      } else if #[cfg(target_feature="sse2")] {
434        i32x8 {
435          a: i32x4 { sse: shr_imm_u32_m128i::<16>( unpack_low_i16_m128i(v.sse, v.sse)) },
436          b: i32x4 { sse: shr_imm_u32_m128i::<16>( unpack_high_i16_m128i(v.sse, v.sse)) },
437        }
438      } else {
439        i32x8::new([
440          i32::from(v.as_array()[0]),
441          i32::from(v.as_array()[1]),
442          i32::from(v.as_array()[2]),
443          i32::from(v.as_array()[3]),
444          i32::from(v.as_array()[4]),
445          i32::from(v.as_array()[5]),
446          i32::from(v.as_array()[6]),
447          i32::from(v.as_array()[7]),
448        ])
449      }
450    }
451  }
452
453  #[inline]
454  #[must_use]
455  pub fn blend(self, t: Self, f: Self) -> Self {
456    pick! {
457      if #[cfg(target_feature="avx2")] {
458        Self { avx2: blend_varying_i8_m256i(f.avx2, t.avx2, self.avx2) }
459      } else {
460        Self {
461          a : self.a.blend(t.a, f.a),
462          b : self.b.blend(t.b, f.b)
463        }
464      }
465    }
466  }
467
468  /// Returns true for each positive element and false if it is zero or
469  /// negative.
470  #[inline]
471  #[must_use]
472  pub fn is_positive(self) -> Self {
473    pick! {
474      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
475        // `neon` has dedicated greater-than-zero intrinsics.
476        Self {
477          a: self.a.is_positive(),
478          b: self.b.is_positive(),
479        }
480      } else {
481        self.simd_gt(Self::ZERO)
482      }
483    }
484  }
485
486  /// Returns true for each negative element and false if it is zero or
487  /// positive.
488  #[inline]
489  #[must_use]
490  pub fn is_negative(self) -> Self {
491    pick! {
492      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
493        // `neon` has dedicated less-than-zero intrinsics.
494        Self {
495          a: self.a.is_negative(),
496          b: self.b.is_negative(),
497        }
498      } else {
499        self.simd_lt(Self::ZERO)
500      }
501    }
502  }
503
504  /// horizontal add of all the elements of the vector
505  #[inline]
506  #[must_use]
507  pub fn reduce_add(self) -> i32 {
508    let arr: [i32x4; 2] = cast(self);
509    (arr[0] + arr[1]).reduce_add()
510  }
511
512  /// horizontal max of all the elements of the vector
513  #[inline]
514  #[must_use]
515  pub fn reduce_max(self) -> i32 {
516    let arr: [i32x4; 2] = cast(self);
517    arr[0].max(arr[1]).reduce_max()
518  }
519
520  /// horizontal min of all the elements of the vector
521  #[inline]
522  #[must_use]
523  pub fn reduce_min(self) -> i32 {
524    let arr: [i32x4; 2] = cast(self);
525    arr[0].min(arr[1]).reduce_min()
526  }
527
528  #[inline]
529  #[must_use]
530  pub fn abs(self) -> Self {
531    pick! {
532      if #[cfg(target_feature="avx2")] {
533        Self { avx2: abs_i32_m256i(self.avx2) }
534      } else {
535        Self {
536          a : self.a.abs(),
537          b : self.b.abs(),
538        }
539      }
540    }
541  }
542
543  #[inline]
544  #[must_use]
545  pub fn unsigned_abs(self) -> u32x8 {
546    pick! {
547      if #[cfg(target_feature="avx2")] {
548        u32x8 { avx2: abs_i32_m256i(self.avx2) }
549      } else {
550        u32x8 {
551          a : self.a.unsigned_abs(),
552          b : self.b.unsigned_abs(),
553        }
554      }
555    }
556  }
557
558  signed_fn_signum!();
559
560  #[inline]
561  #[must_use]
562  pub fn max(self, rhs: Self) -> Self {
563    pick! {
564      if #[cfg(target_feature="avx2")] {
565        Self { avx2: max_i32_m256i(self.avx2, rhs.avx2) }
566      } else {
567        Self {
568          a : self.a.max(rhs.a),
569          b : self.b.max(rhs.b),
570        }
571      }
572    }
573  }
574  #[inline]
575  #[must_use]
576  pub fn min(self, rhs: Self) -> Self {
577    pick! {
578      if #[cfg(target_feature="avx2")] {
579        Self { avx2: min_i32_m256i(self.avx2, rhs.avx2) }
580      } else {
581        Self {
582          a : self.a.min(rhs.a),
583          b : self.b.min(rhs.b),
584        }
585      }
586    }
587  }
588
589  integer_fn_clamp!();
590
591  #[inline]
592  #[must_use]
593  pub fn saturating_add(self, rhs: Self) -> Self {
594    pick! {
595      if #[cfg(target_feature="avx2")] {
596        let result = self + rhs;
597        let overflow = (!(self ^ rhs) & (self ^ result)).is_negative();
598        let negative = self.is_negative();
599
600        overflow.blend(negative.blend(Self::MIN, Self::MAX), result)
601      } else {
602        Self {
603          a: self.a.saturating_add(rhs.a),
604          b: self.b.saturating_add(rhs.b),
605        }
606      }
607    }
608  }
609
610  #[inline]
611  #[must_use]
612  pub fn saturating_sub(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_sub(rhs.a),
623          b: self.b.saturating_sub(rhs.b),
624        }
625      }
626    }
627  }
628
629  /// Lanewise saturating multiply.
630  #[inline]
631  #[must_use]
632  pub fn saturating_mul(self, rhs: Self) -> Self {
633    pick! {
634      if #[cfg(target_feature="avx2")] {
635        let even_wide_mul = mul_i64_low_bits_m256i(self.avx2, rhs.avx2);
636        let odd_wide_mul = mul_i64_low_bits_m256i(
637          shuffle_ai_i32_half_m256i::<0b_00_11_00_01>(self.avx2),
638          shuffle_ai_i32_half_m256i::<0b_00_11_00_01>(rhs.avx2),
639        );
640
641        let ll_hh_1 = unpack_low_i32_m256i(even_wide_mul, odd_wide_mul);
642        let ll_hh_2 = unpack_high_i32_m256i(even_wide_mul, odd_wide_mul);
643        let low = Self { avx2: unpack_low_i64_m256i(ll_hh_1, ll_hh_2) };
644        let high = Self { avx2: unpack_high_i64_m256i(ll_hh_1, ll_hh_2) };
645
646        let no_overflow = high.simd_eq(low.is_negative());
647        let limit = Self::MAX ^ (self ^ rhs).is_negative();
648        no_overflow.blend(low, limit)
649      } else {
650        let [self_a, self_b]: [i32x4; 2] = cast(self);
651        let [rhs_a, rhs_b]: [i32x4; 2] = cast(rhs);
652
653        cast([self_a.saturating_mul(rhs_a), self_b.saturating_mul(rhs_b)])
654      }
655    }
656  }
657
658  integer_fn_saturating_div!([0, 1, 2, 3, 4, 5, 6, 7]);
659
660  #[inline]
661  #[must_use]
662  pub fn round_float(self) -> f32x8 {
663    pick! {
664      if #[cfg(target_feature="avx2")] {
665        cast(convert_to_m256_from_i32_m256i(self.avx2))
666      } else {
667        cast([
668          self.a.round_float(),
669          self.b.round_float(),
670        ])
671      }
672    }
673  }
674
675  #[inline]
676  #[must_use]
677  #[doc(alias("movemask", "move_mask"))]
678  pub fn to_bitmask(self) -> u32 {
679    pick! {
680      if #[cfg(target_feature="avx2")] {
681        // use f32 move_mask since it is the same size as i32
682        move_mask_m256(cast(self.avx2)) as u32
683      } else {
684        self.a.to_bitmask() | (self.b.to_bitmask() << 4)
685      }
686    }
687  }
688
689  #[inline]
690  #[must_use]
691  pub fn any(self) -> bool {
692    pick! {
693      if #[cfg(target_feature="avx2")] {
694        move_mask_m256(cast(self.avx2)) != 0
695      } else {
696        (self.a | self.b).any()
697      }
698    }
699  }
700  #[inline]
701  #[must_use]
702  pub fn all(self) -> bool {
703    pick! {
704      if #[cfg(target_feature="avx2")] {
705        move_mask_m256(cast(self.avx2)) == 0b11111111
706      } else {
707        (self.a & self.b).all()
708      }
709    }
710  }
711  #[inline]
712  #[must_use]
713  pub fn none(self) -> bool {
714    !self.any()
715  }
716
717  /// Transpose matrix of 8x8 `i32` matrix. Currently only accelerated on AVX2.
718  #[must_use]
719  #[inline]
720  pub fn transpose(data: [i32x8; 8]) -> [i32x8; 8] {
721    pick! {
722      if #[cfg(target_feature="avx2")] {
723        let a0 = unpack_low_i32_m256i(data[0].avx2, data[1].avx2);
724        let a1 = unpack_high_i32_m256i(data[0].avx2, data[1].avx2);
725        let a2 = unpack_low_i32_m256i(data[2].avx2, data[3].avx2);
726        let a3 = unpack_high_i32_m256i(data[2].avx2, data[3].avx2);
727        let a4 = unpack_low_i32_m256i(data[4].avx2, data[5].avx2);
728        let a5 = unpack_high_i32_m256i(data[4].avx2, data[5].avx2);
729        let a6 = unpack_low_i32_m256i(data[6].avx2, data[7].avx2);
730        let a7 = unpack_high_i32_m256i(data[6].avx2, data[7].avx2);
731
732        pub const fn mm_shuffle(z: i32, y: i32, x: i32, w: i32) -> i32 {
733          (z << 6) | (y << 4) | (x << 2) | w
734        }
735
736        const SHUFF_LO : i32 = mm_shuffle(1,0,1,0);
737        const SHUFF_HI : i32 = mm_shuffle(3,2,3,2);
738
739        // possible todo: intel performance manual suggests alternative with blend to avoid port 5 pressure
740        // (since blend runs on a different port than shuffle)
741        let b0 = cast::<m256,m256i>(shuffle_m256::<SHUFF_LO>(cast(a0),cast(a2)));
742        let b1 = cast::<m256,m256i>(shuffle_m256::<SHUFF_HI>(cast(a0),cast(a2)));
743        let b2 = cast::<m256,m256i>(shuffle_m256::<SHUFF_LO>(cast(a1),cast(a3)));
744        let b3 = cast::<m256,m256i>(shuffle_m256::<SHUFF_HI>(cast(a1),cast(a3)));
745        let b4 = cast::<m256,m256i>(shuffle_m256::<SHUFF_LO>(cast(a4),cast(a6)));
746        let b5 = cast::<m256,m256i>(shuffle_m256::<SHUFF_HI>(cast(a4),cast(a6)));
747        let b6 = cast::<m256,m256i>(shuffle_m256::<SHUFF_LO>(cast(a5),cast(a7)));
748        let b7 = cast::<m256,m256i>(shuffle_m256::<SHUFF_HI>(cast(a5),cast(a7)));
749
750        [
751          i32x8 { avx2: permute2z_m256i::<0x20>(b0, b4) },
752          i32x8 { avx2: permute2z_m256i::<0x20>(b1, b5) },
753          i32x8 { avx2: permute2z_m256i::<0x20>(b2, b6) },
754          i32x8 { avx2: permute2z_m256i::<0x20>(b3, b7) },
755          i32x8 { avx2: permute2z_m256i::<0x31>(b0, b4) },
756          i32x8 { avx2: permute2z_m256i::<0x31>(b1, b5) },
757          i32x8 { avx2: permute2z_m256i::<0x31>(b2, b6) },
758          i32x8 { avx2: permute2z_m256i::<0x31>(b3, b7) }
759        ]
760      } else {
761        // possible todo: not sure that 128bit SIMD gives us a a lot of speedup here
762
763        #[inline(always)]
764        fn transpose_column(data: &[i32x8; 8], index: usize) -> i32x8 {
765          i32x8::new([
766            data[0].as_array()[index],
767            data[1].as_array()[index],
768            data[2].as_array()[index],
769            data[3].as_array()[index],
770            data[4].as_array()[index],
771            data[5].as_array()[index],
772            data[6].as_array()[index],
773            data[7].as_array()[index],
774          ])
775        }
776
777        [
778          transpose_column(&data, 0),
779          transpose_column(&data, 1),
780          transpose_column(&data, 2),
781          transpose_column(&data, 3),
782          transpose_column(&data, 4),
783          transpose_column(&data, 5),
784          transpose_column(&data, 6),
785          transpose_column(&data, 7),
786        ]
787      }
788    }
789  }
790
791  #[inline]
792  pub fn to_array(self) -> [i32; 8] {
793    cast(self)
794  }
795
796  #[inline]
797  pub fn as_array(&self) -> &[i32; 8] {
798    cast_ref(self)
799  }
800
801  #[inline]
802  pub fn as_mut_array(&mut self) -> &mut [i32; 8] {
803    cast_mut(self)
804  }
805}
806
807impl Not for i32x8 {
808  type Output = Self;
809  #[inline]
810  fn not(self) -> Self {
811    pick! {
812      if #[cfg(target_feature="avx2")] {
813        Self { avx2: self.avx2.not()  }
814      } else {
815        Self {
816          a : self.a.not(),
817          b : self.b.not(),
818        }
819      }
820    }
821  }
822}