Skip to main content

wide/
i16x16_.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 i16x16 { pub(crate) avx2: m256i }
8  } else {
9    #[derive(Default, Clone, Copy, PartialEq, Eq)]
10    #[repr(C, align(32))]
11    pub struct i16x16 { pub(crate) a : i16x8, pub(crate) b : i16x8 }
12  }
13}
14
15int_uint_consts!(i16, 16, i16x16, 256);
16
17unsafe impl Zeroable for i16x16 {}
18unsafe impl Pod for i16x16 {}
19
20impl AlignTo for i16x16 {
21  type Elem = i16;
22}
23
24impl Add for i16x16 {
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_i16_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 i16x16 {
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_i16_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 i16x16 {
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_i16_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!(
76  i16,
77  i16x16,
78  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
79);
80
81impl Shl for i16x16 {
82  type Output = Self;
83
84  /// Shifts lanes by the corresponding lane.
85  ///
86  /// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
87  /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
88  /// of the type. (same as `wrapping_shl`)
89  #[inline]
90  fn shl(self, rhs: Self) -> Self::Output {
91    pick! {
92      if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
93        #[cfg(target_arch = "x86")]
94        use core::arch::x86::_mm256_sllv_epi16;
95        #[cfg(target_arch = "x86_64")]
96        use core::arch::x86_64::_mm256_sllv_epi16;
97
98        // Mask `rhs` to 15 to match `wrapping_shl`.
99        let rhs = bitand_m256i(rhs.avx2, set_splat_i16_m256i(15));
100        // TODO(safe_arch): Add `_mm256_sllv_epi16`.
101        cast(unsafe { _mm256_sllv_epi16(self.avx2.0, rhs.0) })
102      } else {
103        let [self_a, self_b]: [i16x8; 2] = cast(self);
104        let [rhs_a, rhs_b]: [i16x8; 2] = cast(rhs);
105
106        cast([self_a << rhs_a, self_b << rhs_b])
107      }
108    }
109  }
110}
111
112impl Shr for i16x16 {
113  type Output = Self;
114
115  /// Shifts lanes by the corresponding lane.
116  ///
117  /// Bitwise shift-right; yields `self >> mask(rhs)`, where mask removes any
118  /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
119  /// of the type. (same as `wrapping_shr`)
120  #[inline]
121  fn shr(self, rhs: Self) -> Self::Output {
122    pick! {
123      if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
124        #[cfg(target_arch = "x86")]
125        use core::arch::x86::_mm256_srav_epi16;
126        #[cfg(target_arch = "x86_64")]
127        use core::arch::x86_64::_mm256_srav_epi16;
128
129        // Mask `rhs` to 15 to match `wrapping_shr`.
130        let rhs = bitand_m256i(rhs.avx2, set_splat_i16_m256i(15));
131        // TODO(safe_arch): Add `_mm256_srav_epi16`.
132        cast(unsafe { _mm256_srav_epi16(self.avx2.0, rhs.0) })
133      } else {
134        let [self_a, self_b]: [i16x8; 2] = cast(self);
135        let [rhs_a, rhs_b]: [i16x8; 2] = cast(rhs);
136
137        cast([self_a >> rhs_a, self_b >> rhs_b])
138      }
139    }
140  }
141}
142
143impl Add<i16> for i16x16 {
144  type Output = Self;
145  #[inline]
146  fn add(self, rhs: i16) -> Self::Output {
147    self.add(Self::splat(rhs))
148  }
149}
150
151impl Sub<i16> for i16x16 {
152  type Output = Self;
153  #[inline]
154  fn sub(self, rhs: i16) -> Self::Output {
155    self.sub(Self::splat(rhs))
156  }
157}
158
159impl Mul<i16> for i16x16 {
160  type Output = Self;
161  #[inline]
162  fn mul(self, rhs: i16) -> Self::Output {
163    self.mul(Self::splat(rhs))
164  }
165}
166
167impl Add<i16x16> for i16 {
168  type Output = i16x16;
169  #[inline]
170  fn add(self, rhs: i16x16) -> Self::Output {
171    i16x16::splat(self).add(rhs)
172  }
173}
174
175impl Sub<i16x16> for i16 {
176  type Output = i16x16;
177  #[inline]
178  fn sub(self, rhs: i16x16) -> Self::Output {
179    i16x16::splat(self).sub(rhs)
180  }
181}
182
183impl Mul<i16x16> for i16 {
184  type Output = i16x16;
185  #[inline]
186  fn mul(self, rhs: i16x16) -> Self::Output {
187    i16x16::splat(self).mul(rhs)
188  }
189}
190
191impl BitAnd for i16x16 {
192  type Output = Self;
193  #[inline]
194  fn bitand(self, rhs: Self) -> Self::Output {
195    pick! {
196      if #[cfg(target_feature="avx2")] {
197        Self { avx2: bitand_m256i(self.avx2, rhs.avx2) }
198      } else {
199        Self {
200          a : self.a.bitand(rhs.a),
201          b : self.b.bitand(rhs.b),
202        }
203      }
204    }
205  }
206}
207
208impl BitOr for i16x16 {
209  type Output = Self;
210  #[inline]
211  fn bitor(self, rhs: Self) -> Self::Output {
212    pick! {
213      if #[cfg(target_feature="avx2")] {
214        Self { avx2: bitor_m256i(self.avx2, rhs.avx2) }
215      } else {
216        Self {
217          a : self.a.bitor(rhs.a),
218          b : self.b.bitor(rhs.b),
219        }
220      }
221    }
222  }
223}
224
225impl BitXor for i16x16 {
226  type Output = Self;
227  #[inline]
228  fn bitxor(self, rhs: Self) -> Self::Output {
229    pick! {
230      if #[cfg(target_feature="avx2")] {
231        Self { avx2: bitxor_m256i(self.avx2, rhs.avx2) }
232      } else {
233        Self {
234          a : self.a.bitxor(rhs.a),
235          b : self.b.bitxor(rhs.b),
236        }
237      }
238    }
239  }
240}
241
242macro_rules! impl_shl_t_for_i16x16 {
243  ($($shift_type:ty),+ $(,)?) => {
244    $(impl Shl<$shift_type> for i16x16 {
245      type Output = Self;
246      /// Shifts all lanes by the value given.
247      #[inline]
248      fn shl(self, rhs: $shift_type) -> Self::Output {
249        pick! {
250          if #[cfg(target_feature="avx2")] {
251            let shift = cast([rhs as u64, 0]);
252            Self { avx2: shl_all_u16_m256i(self.avx2, shift) }
253          } else {
254            Self {
255              a : self.a.shl(rhs),
256              b : self.b.shl(rhs),
257            }
258          }
259       }
260     }
261    })+
262  };
263}
264impl_shl_t_for_i16x16!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
265
266macro_rules! impl_shr_t_for_i16x16 {
267  ($($shift_type:ty),+ $(,)?) => {
268    $(impl Shr<$shift_type> for i16x16 {
269      type Output = Self;
270      /// Shifts all lanes by the value given.
271      #[inline]
272      fn shr(self, rhs: $shift_type) -> Self::Output {
273        pick! {
274          if #[cfg(target_feature="avx2")] {
275            let shift = cast([rhs as u64, 0]);
276            Self { avx2: shr_all_i16_m256i(self.avx2, shift) }
277          } else {
278            Self {
279              a : self.a.shr(rhs),
280              b : self.b.shr(rhs),
281            }
282          }
283        }
284      }
285    })+
286  };
287}
288impl_shr_t_for_i16x16!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
289
290#[expect(deprecated)]
291impl CmpEq for i16x16 {
292  type Output = Self;
293  #[inline]
294  fn simd_eq(self, rhs: Self) -> Self::Output {
295    pick! {
296      if #[cfg(target_feature="avx2")] {
297        Self { avx2: cmp_eq_mask_i16_m256i(self.avx2, rhs.avx2) }
298      } else {
299        Self {
300          a : self.a.simd_eq(rhs.a),
301          b : self.b.simd_eq(rhs.b),
302        }
303      }
304    }
305  }
306}
307
308#[expect(deprecated)]
309impl CmpGt for i16x16 {
310  type Output = Self;
311  #[inline]
312  fn simd_gt(self, rhs: Self) -> Self::Output {
313    pick! {
314      if #[cfg(target_feature="avx2")] {
315        Self { avx2: cmp_gt_mask_i16_m256i(self.avx2, rhs.avx2) }
316      } else {
317        Self {
318          a : self.a.simd_gt(rhs.a),
319          b : self.b.simd_gt(rhs.b),
320        }
321      }
322    }
323  }
324}
325
326#[expect(deprecated)]
327impl CmpLt for i16x16 {
328  type Output = Self;
329  #[inline]
330  fn simd_lt(self, rhs: Self) -> Self::Output {
331    pick! {
332      if #[cfg(target_feature="avx2")] {
333        Self { avx2: !cmp_gt_mask_i16_m256i(self.avx2, rhs.avx2) ^ cmp_eq_mask_i16_m256i(self.avx2,rhs.avx2) }
334      } else {
335        Self {
336          a : self.a.simd_lt(rhs.a),
337          b : self.b.simd_lt(rhs.b),
338        }
339      }
340    }
341  }
342}
343
344#[expect(deprecated)]
345impl CmpNe for i16x16 {
346  type Output = Self;
347  #[inline]
348  fn simd_ne(self, rhs: Self) -> Self::Output {
349    pick! {
350      if #[cfg(target_feature="avx2")] {
351        !self.simd_eq(rhs)
352      } else {
353        Self {
354          a : self.a.simd_ne(rhs.a),
355          b : self.b.simd_ne(rhs.b),
356        }
357      }
358    }
359  }
360}
361
362#[expect(deprecated)]
363impl CmpLe for i16x16 {
364  type Output = Self;
365  #[inline]
366  fn simd_le(self, rhs: Self) -> Self::Output {
367    pick! {
368      if #[cfg(target_feature="avx2")] {
369        !self.simd_gt(rhs)
370      } else {
371        Self {
372          a : self.a.simd_le(rhs.a),
373          b : self.b.simd_le(rhs.b),
374        }
375      }
376    }
377  }
378}
379
380#[expect(deprecated)]
381impl CmpGe for i16x16 {
382  type Output = Self;
383  #[inline]
384  fn simd_ge(self, rhs: Self) -> Self::Output {
385    pick! {
386      if #[cfg(target_feature="avx2")] {
387        !self.simd_lt(rhs)
388      } else {
389        Self {
390          a : self.a.simd_ge(rhs.a),
391          b : self.b.simd_ge(rhs.b),
392        }
393      }
394    }
395  }
396}
397
398impl From<i8x16> for i16x16 {
399  /// widen with sign extend from i8 to i16
400  #[inline]
401  fn from(i: i8x16) -> Self {
402    i16x16::from_i8x16(i)
403  }
404}
405
406impl From<u8x16> for i16x16 {
407  /// widen with zero extend from u8 to i16
408  #[inline]
409  fn from(i: u8x16) -> Self {
410    cast(u16x16::from(i))
411  }
412}
413
414impl Not for i16x16 {
415  type Output = Self;
416  #[inline]
417  fn not(self) -> Self {
418    pick! {
419      if #[cfg(target_feature="avx2")] {
420        Self { avx2: self.avx2.not()  }
421      } else {
422        Self {
423          a : self.a.not(),
424          b : self.b.not(),
425        }
426      }
427    }
428  }
429}
430
431impl i16x16 {
432  #[inline]
433  #[must_use]
434  pub const fn new(array: [i16; 16]) -> Self {
435    unsafe { core::mem::transmute(array) }
436  }
437
438  simd_comparison_fns!();
439
440  #[inline]
441  #[must_use]
442  #[doc(alias("movemask", "move_mask"))]
443  pub fn to_bitmask(self) -> u32 {
444    pick! {
445      if #[cfg(target_feature="sse2")] {
446          let [a,b] = cast::<_,[m128i;2]>(self);
447          move_mask_i8_m128i( pack_i16_to_i8_m128i(a,b)) as u32
448        } else {
449        self.a.to_bitmask() | (self.b.to_bitmask() << 8)
450      }
451    }
452  }
453
454  #[inline]
455  #[must_use]
456  pub fn any(self) -> bool {
457    pick! {
458      if #[cfg(target_feature="avx2")] {
459        ((move_mask_i8_m256i(self.avx2) as u32) & 0b10101010101010101010101010101010) != 0
460      } else {
461        (self.a | self.b).any()
462      }
463    }
464  }
465  #[inline]
466  #[must_use]
467  pub fn all(self) -> bool {
468    pick! {
469      if #[cfg(target_feature="avx2")] {
470        ((move_mask_i8_m256i(self.avx2) as u32) & 0b10101010101010101010101010101010) == 0b10101010101010101010101010101010
471      } else {
472        (self.a & self.b).all()
473      }
474    }
475  }
476  #[inline]
477  #[must_use]
478  pub fn none(self) -> bool {
479    !self.any()
480  }
481
482  /// widens and sign extends to i16x16
483  #[inline]
484  #[must_use]
485  pub fn from_i8x16(v: i8x16) -> Self {
486    pick! {
487      if #[cfg(target_feature="avx2")] {
488        i16x16 { avx2:convert_to_i16_m256i_from_i8_m128i(v.sse) }
489      } else if #[cfg(target_feature="sse4.1")] {
490        i16x16 {
491          a: i16x8 { sse: convert_to_i16_m128i_from_lower8_i8_m128i(v.sse) },
492          b: i16x8 { sse: convert_to_i16_m128i_from_lower8_i8_m128i(unpack_high_i64_m128i(v.sse, v.sse)) }
493        }
494      } else if #[cfg(target_feature="sse2")] {
495        i16x16 {
496          a: i16x8 { sse: shr_imm_i16_m128i::<8>( unpack_low_i8_m128i(v.sse, v.sse)) },
497          b: i16x8 { sse: shr_imm_i16_m128i::<8>( unpack_high_i8_m128i(v.sse, v.sse)) },
498        }
499      } else {
500
501        i16x16::new([
502          v.as_array()[0] as i16,
503          v.as_array()[1] as i16,
504          v.as_array()[2] as i16,
505          v.as_array()[3] as i16,
506          v.as_array()[4] as i16,
507          v.as_array()[5] as i16,
508          v.as_array()[6] as i16,
509          v.as_array()[7] as i16,
510          v.as_array()[8] as i16,
511          v.as_array()[9] as i16,
512          v.as_array()[10] as i16,
513          v.as_array()[11] as i16,
514          v.as_array()[12] as i16,
515          v.as_array()[13] as i16,
516          v.as_array()[14] as i16,
517          v.as_array()[15] as i16,
518          ])
519      }
520    }
521  }
522
523  #[inline]
524  #[must_use]
525  pub fn blend(self, t: Self, f: Self) -> Self {
526    pick! {
527      if #[cfg(target_feature="avx2")] {
528        Self { avx2: blend_varying_i8_m256i(f.avx2, t.avx2, self.avx2) }
529      } else {
530        Self {
531          a : self.a.blend(t.a, f.a),
532          b : self.b.blend(t.b, f.b),
533        }
534      }
535    }
536  }
537
538  /// Returns true for each positive element and false if it is zero or
539  /// negative.
540  #[inline]
541  #[must_use]
542  pub fn is_positive(self) -> Self {
543    pick! {
544      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
545        // `neon` has dedicated greater-than-zero intrinsics.
546        Self {
547          a: self.a.is_positive(),
548          b: self.b.is_positive(),
549        }
550      } else {
551        self.simd_gt(Self::ZERO)
552      }
553    }
554  }
555
556  /// Returns true for each negative element and false if it is zero or
557  /// positive.
558  #[inline]
559  #[must_use]
560  pub fn is_negative(self) -> Self {
561    pick! {
562      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
563        // `neon` has dedicated less-than-zero intrinsics.
564        Self {
565          a: self.a.is_negative(),
566          b: self.b.is_negative(),
567        }
568      } else {
569        self.simd_lt(Self::ZERO)
570      }
571    }
572  }
573
574  /// horizontal add of all the elements of the vector
575  #[inline]
576  #[must_use]
577  pub fn reduce_add(self) -> i16 {
578    let arr: [i16x8; 2] = cast(self);
579
580    (arr[0] + arr[1]).reduce_add()
581  }
582
583  /// horizontal min of all the elements of the vector
584  #[inline]
585  #[must_use]
586  pub fn reduce_min(self) -> i16 {
587    let arr: [i16x8; 2] = cast(self);
588
589    arr[0].min(arr[1]).reduce_min()
590  }
591
592  /// horizontal max of all the elements of the vector
593  #[inline]
594  #[must_use]
595  pub fn reduce_max(self) -> i16 {
596    let arr: [i16x8; 2] = cast(self);
597
598    arr[0].max(arr[1]).reduce_max()
599  }
600
601  #[inline]
602  #[must_use]
603  pub fn abs(self) -> Self {
604    pick! {
605      if #[cfg(target_feature="avx2")] {
606        Self { avx2: abs_i16_m256i(self.avx2) }
607      } else {
608        Self {
609          a : self.a.abs(),
610          b : self.b.abs(),
611        }
612      }
613    }
614  }
615
616  #[inline]
617  #[must_use]
618  pub fn unsigned_abs(self) -> u16x16 {
619    pick! {
620      if #[cfg(target_feature="avx2")] {
621        u16x16 { avx2: abs_i16_m256i(self.avx2) }
622      } else {
623        u16x16 {
624          a: self.a.unsigned_abs(),
625          b: self.b.unsigned_abs(),
626        }
627      }
628    }
629  }
630
631  signed_fn_signum!();
632
633  #[inline]
634  #[must_use]
635  pub fn max(self, rhs: Self) -> Self {
636    pick! {
637      if #[cfg(target_feature="avx2")] {
638        Self { avx2: max_i16_m256i(self.avx2, rhs.avx2) }
639      } else {
640        Self {
641          a : self.a.max(rhs.a),
642          b : self.b.max(rhs.b),
643        }
644      }
645    }
646  }
647  #[inline]
648  #[must_use]
649  pub fn min(self, rhs: Self) -> Self {
650    pick! {
651      if #[cfg(target_feature="avx2")] {
652        Self { avx2: min_i16_m256i(self.avx2, rhs.avx2) }
653      } else {
654        Self {
655          a : self.a.min(rhs.a),
656          b : self.b.min(rhs.b),
657        }
658      }
659    }
660  }
661
662  integer_fn_clamp!();
663
664  #[inline]
665  #[must_use]
666  pub fn saturating_add(self, rhs: Self) -> Self {
667    pick! {
668      if #[cfg(target_feature="avx2")] {
669        Self { avx2: add_saturating_i16_m256i(self.avx2, rhs.avx2) }
670      } else {
671        Self {
672          a : self.a.saturating_add(rhs.a),
673          b : self.b.saturating_add(rhs.b),
674        }
675      }
676    }
677  }
678  #[inline]
679  #[must_use]
680  pub fn saturating_sub(self, rhs: Self) -> Self {
681    pick! {
682      if #[cfg(target_feature="avx2")] {
683        Self { avx2: sub_saturating_i16_m256i(self.avx2, rhs.avx2) }
684      } else {
685        Self {
686          a : self.a.saturating_sub(rhs.a),
687          b : self.b.saturating_sub(rhs.b),
688        }
689      }
690    }
691  }
692
693  /// Lanewise saturating multiply.
694  #[inline]
695  #[must_use]
696  pub fn saturating_mul(self, rhs: Self) -> Self {
697    let [self_a, self_b]: [i16x8; 2] = cast(self);
698    let [rhs_a, rhs_b]: [i16x8; 2] = cast(rhs);
699    cast([self_a.saturating_mul(rhs_a), self_b.saturating_mul(rhs_b)])
700  }
701
702  integer_fn_saturating_div!([
703    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
704  ]);
705
706  /// Calculates partial dot product.
707  /// Multiplies packed signed 16-bit integers, producing intermediate signed
708  /// 32-bit integers. Horizontally add adjacent pairs of intermediate 32-bit
709  /// integers.
710  #[inline]
711  #[must_use]
712  pub fn dot(self, rhs: Self) -> i32x8 {
713    pick! {
714      if #[cfg(target_feature="avx2")] {
715        i32x8 { avx2:  mul_i16_horizontal_add_m256i(self.avx2, rhs.avx2) }
716      } else {
717        i32x8 {
718          a : self.a.dot(rhs.a),
719          b : self.b.dot(rhs.b),
720        }
721      }
722    }
723  }
724
725  /// Multiply and scale equivalent to `((self * rhs) + 0x4000) >> 15` on each
726  /// lane, effectively multiplying by a 16 bit fixed point number between `-1`
727  /// and `1`. This corresponds to the following instructions:
728  /// - `vqrdmulhq_n_s16` instruction on neon
729  /// - `i16x8_q15mulr_sat` on simd128
730  /// - `_mm256_mulhrs_epi16` on avx2
731  /// - emulated via `mul_i16_*` on sse2
732  #[inline]
733  #[must_use]
734  pub fn mul_scale_round(self, rhs: Self) -> Self {
735    pick! {
736      if #[cfg(target_feature="avx2")] {
737        Self { avx2: mul_i16_scale_round_m256i(self.avx2, rhs.avx2) }
738      } else {
739        Self {
740          a : self.a.mul_scale_round(rhs.a),
741          b : self.b.mul_scale_round(rhs.b),
742        }
743      }
744    }
745  }
746
747  /// Multiply and scale equivalent to `((self * rhs) + 0x4000) >> 15` on each
748  /// lane, effectively multiplying by a 16 bit fixed point number between `-1`
749  /// and `1`. This corresponds to the following instructions:
750  /// - `vqrdmulhq_n_s16` instruction on neon
751  /// - `i16x8_q15mulr_sat` on simd128
752  /// - `_mm256_mulhrs_epi16` on avx2
753  /// - emulated via `mul_i16_*` on sse2
754  #[inline]
755  #[must_use]
756  pub fn mul_scale_round_n(self, rhs: i16) -> Self {
757    pick! {
758      if #[cfg(target_feature="avx2")] {
759        Self { avx2: mul_i16_scale_round_m256i(self.avx2, set_splat_i16_m256i(rhs)) }
760      } else {
761        Self {
762          a : self.a.mul_scale_round_n(rhs),
763          b : self.b.mul_scale_round_n(rhs),
764        }
765      }
766    }
767  }
768
769  /// Transpose matrix of 16x16 `i16` matrix. Currently not accelerated.
770  #[must_use]
771  #[inline]
772  pub fn transpose(data: [i16x16; 16]) -> [i16x16; 16] {
773    // Can this be optimized?
774
775    #[inline(always)]
776    fn transpose_column(data: &[i16x16; 16], index: usize) -> i16x16 {
777      i16x16::new([
778        data[0].as_array()[index],
779        data[1].as_array()[index],
780        data[2].as_array()[index],
781        data[3].as_array()[index],
782        data[4].as_array()[index],
783        data[5].as_array()[index],
784        data[6].as_array()[index],
785        data[7].as_array()[index],
786        data[8].as_array()[index],
787        data[9].as_array()[index],
788        data[10].as_array()[index],
789        data[11].as_array()[index],
790        data[12].as_array()[index],
791        data[13].as_array()[index],
792        data[14].as_array()[index],
793        data[15].as_array()[index],
794      ])
795    }
796
797    [
798      transpose_column(&data, 0),
799      transpose_column(&data, 1),
800      transpose_column(&data, 2),
801      transpose_column(&data, 3),
802      transpose_column(&data, 4),
803      transpose_column(&data, 5),
804      transpose_column(&data, 6),
805      transpose_column(&data, 7),
806      transpose_column(&data, 8),
807      transpose_column(&data, 9),
808      transpose_column(&data, 10),
809      transpose_column(&data, 11),
810      transpose_column(&data, 12),
811      transpose_column(&data, 13),
812      transpose_column(&data, 14),
813      transpose_column(&data, 15),
814    ]
815  }
816
817  #[inline]
818  pub fn to_array(self) -> [i16; 16] {
819    cast(self)
820  }
821
822  #[inline]
823  pub fn as_array(&self) -> &[i16; 16] {
824    cast_ref(self)
825  }
826
827  #[inline]
828  pub fn as_mut_array(&mut self) -> &mut [i16; 16] {
829    cast_mut(self)
830  }
831}