Skip to main content

wide/
i16x8_.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 i16x8 { 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 i16x8 { pub(crate) simd: v128 }
14
15    impl Default for i16x8 {
16      fn default() -> Self {
17        Self::splat(0)
18      }
19    }
20
21    impl PartialEq for i16x8 {
22      fn eq(&self, other: &Self) -> bool {
23        u16x8_all_true(i16x8_eq(self.simd, other.simd))
24      }
25    }
26
27    impl Eq for i16x8 { }
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 i16x8 { pub(crate) neon : int16x8_t }
33
34    impl Default for i16x8 {
35      #[inline]
36      fn default() -> Self {
37        Self::splat(0)
38      }
39    }
40
41    impl PartialEq for i16x8 {
42      #[inline]
43      fn eq(&self, other: &Self) -> bool {
44        unsafe { vminvq_u16(vceqq_s16(self.neon, other.neon))==u16::MAX }
45      }
46    }
47
48    impl Eq for i16x8 { }
49  } else {
50    #[derive(Default, Clone, Copy, PartialEq, Eq)]
51    #[repr(C, align(16))]
52    pub struct i16x8 { pub(crate) arr: [i16;8] }
53  }
54}
55
56int_uint_consts!(i16, 8, i16x8, 128);
57
58unsafe impl Zeroable for i16x8 {}
59unsafe impl Pod for i16x8 {}
60
61impl AlignTo for i16x8 {
62  type Elem = i16;
63}
64
65impl Add for i16x8 {
66  type Output = Self;
67  #[inline]
68  fn add(self, rhs: Self) -> Self::Output {
69    pick! {
70      if #[cfg(target_feature="sse2")] {
71        Self { sse: add_i16_m128i(self.sse, rhs.sse) }
72      } else if #[cfg(target_feature="simd128")] {
73        Self { simd: i16x8_add(self.simd, rhs.simd) }
74      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
75        unsafe { Self { neon: vaddq_s16(self.neon, rhs.neon) } }
76      } else {
77        Self { arr: [
78          self.arr[0].wrapping_add(rhs.arr[0]),
79          self.arr[1].wrapping_add(rhs.arr[1]),
80          self.arr[2].wrapping_add(rhs.arr[2]),
81          self.arr[3].wrapping_add(rhs.arr[3]),
82          self.arr[4].wrapping_add(rhs.arr[4]),
83          self.arr[5].wrapping_add(rhs.arr[5]),
84          self.arr[6].wrapping_add(rhs.arr[6]),
85          self.arr[7].wrapping_add(rhs.arr[7]),
86        ]}
87      }
88    }
89  }
90}
91
92impl Sub for i16x8 {
93  type Output = Self;
94  #[inline]
95  fn sub(self, rhs: Self) -> Self::Output {
96    pick! {
97      if #[cfg(target_feature="sse2")] {
98        Self { sse: sub_i16_m128i(self.sse, rhs.sse) }
99      } else if #[cfg(target_feature="simd128")] {
100        Self { simd: i16x8_sub(self.simd, rhs.simd) }
101      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
102        unsafe {Self { neon: vsubq_s16(self.neon, rhs.neon) }}
103      } else {
104        Self { arr: [
105          self.arr[0].wrapping_sub(rhs.arr[0]),
106          self.arr[1].wrapping_sub(rhs.arr[1]),
107          self.arr[2].wrapping_sub(rhs.arr[2]),
108          self.arr[3].wrapping_sub(rhs.arr[3]),
109          self.arr[4].wrapping_sub(rhs.arr[4]),
110          self.arr[5].wrapping_sub(rhs.arr[5]),
111          self.arr[6].wrapping_sub(rhs.arr[6]),
112          self.arr[7].wrapping_sub(rhs.arr[7]),
113        ]}
114      }
115    }
116  }
117}
118
119impl Mul for i16x8 {
120  type Output = Self;
121  #[inline]
122  fn mul(self, rhs: Self) -> Self::Output {
123    pick! {
124      if #[cfg(target_feature="sse2")] {
125        Self { sse: mul_i16_keep_low_m128i(self.sse, rhs.sse) }
126      } else if #[cfg(target_feature="simd128")] {
127        Self { simd: i16x8_mul(self.simd, rhs.simd) }
128      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
129        unsafe {Self { neon: vmulq_s16(self.neon, rhs.neon) }}
130      } else {
131        Self { arr: [
132          self.arr[0].wrapping_mul(rhs.arr[0]),
133          self.arr[1].wrapping_mul(rhs.arr[1]),
134          self.arr[2].wrapping_mul(rhs.arr[2]),
135          self.arr[3].wrapping_mul(rhs.arr[3]),
136          self.arr[4].wrapping_mul(rhs.arr[4]),
137          self.arr[5].wrapping_mul(rhs.arr[5]),
138          self.arr[6].wrapping_mul(rhs.arr[6]),
139          self.arr[7].wrapping_mul(rhs.arr[7]),
140        ]}
141      }
142    }
143  }
144}
145
146integer_impl_div_rem!(i16, i16x8, [0, 1, 2, 3, 4, 5, 6, 7]);
147
148impl Shl for i16x8 {
149  type Output = Self;
150
151  /// Shifts lanes by the corresponding lane.
152  ///
153  /// Bitwise shift-left; yields `self << mask(rhs)`, where mask removes any
154  /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
155  /// of the type. (same as `wrapping_shl`)
156  #[inline]
157  fn shl(self, rhs: Self) -> Self::Output {
158    pick! {
159      if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
160        #[cfg(target_arch = "x86")]
161        use core::arch::x86::_mm_sllv_epi16;
162        #[cfg(target_arch = "x86_64")]
163        use core::arch::x86_64::_mm_sllv_epi16;
164
165        // Mask `rhs` to 15 to match `wrapping_shl`.
166        let rhs = bitand_m128i(rhs.sse, set_splat_i16_m128i(15));
167        // TODO(safe_arch): Add `_mm_sllv_epi16`.
168        cast(unsafe { _mm_sllv_epi16(self.sse.0, rhs.0) })
169      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
170        unsafe {
171          // Mask `rhs` to 15 to match `wrapping_shl`.
172          let rhs = vandq_s16(rhs.neon, vmovq_n_s16(15));
173          Self { neon: vshlq_s16(self.neon, rhs) }
174        }
175      } else {
176        let self_array = self.to_array();
177        let rhs_array = rhs.to_array();
178
179        Self::new([
180          self_array[0].wrapping_shl(rhs_array[0] as u32),
181          self_array[1].wrapping_shl(rhs_array[1] as u32),
182          self_array[2].wrapping_shl(rhs_array[2] as u32),
183          self_array[3].wrapping_shl(rhs_array[3] as u32),
184          self_array[4].wrapping_shl(rhs_array[4] as u32),
185          self_array[5].wrapping_shl(rhs_array[5] as u32),
186          self_array[6].wrapping_shl(rhs_array[6] as u32),
187          self_array[7].wrapping_shl(rhs_array[7] as u32),
188        ])
189      }
190    }
191  }
192}
193
194impl Shr for i16x8 {
195  type Output = Self;
196
197  /// Shifts lanes by the corresponding lane.
198  ///
199  /// Bitwise shift-right; yields `self >> mask(rhs)`, where mask removes any
200  /// high-order bits of `rhs` that would cause the shift to exceed the bitwidth
201  /// of the type. (same as `wrapping_shr`)
202  #[inline]
203  fn shr(self, rhs: Self) -> Self::Output {
204    pick! {
205      if #[cfg(all(target_feature="avx512bw", target_feature="avx512vl"))] {
206        #[cfg(target_arch = "x86")]
207        use core::arch::x86::_mm_srav_epi16;
208        #[cfg(target_arch = "x86_64")]
209        use core::arch::x86_64::_mm_srav_epi16;
210
211        // Mask `rhs` to 15 to match `wrapping_shr`.
212        let rhs = bitand_m128i(rhs.sse, set_splat_i16_m128i(15));
213        // TODO(safe_arch): Add `_mm_srav_epi16`.
214        cast(unsafe { _mm_srav_epi16(self.sse.0, rhs.0) })
215      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
216        unsafe {
217          // Mask `rhs` to 15 to match `wrapping_shr`, and negate it because
218          // there is no shift-right intrinsic.
219          let neg_rhs = vnegq_s16(vandq_s16(rhs.neon, vmovq_n_s16(15)));
220          Self { neon: vshlq_s16(self.neon, neg_rhs) }
221        }
222      } else {
223        let self_array = self.to_array();
224        let rhs_array = rhs.to_array();
225
226        Self::new([
227          self_array[0].wrapping_shr(rhs_array[0] as u32),
228          self_array[1].wrapping_shr(rhs_array[1] as u32),
229          self_array[2].wrapping_shr(rhs_array[2] as u32),
230          self_array[3].wrapping_shr(rhs_array[3] as u32),
231          self_array[4].wrapping_shr(rhs_array[4] as u32),
232          self_array[5].wrapping_shr(rhs_array[5] as u32),
233          self_array[6].wrapping_shr(rhs_array[6] as u32),
234          self_array[7].wrapping_shr(rhs_array[7] as u32),
235        ])
236      }
237    }
238  }
239}
240
241impl Add<i16> for i16x8 {
242  type Output = Self;
243  #[inline]
244  fn add(self, rhs: i16) -> Self::Output {
245    self.add(Self::splat(rhs))
246  }
247}
248
249impl Sub<i16> for i16x8 {
250  type Output = Self;
251  #[inline]
252  fn sub(self, rhs: i16) -> Self::Output {
253    self.sub(Self::splat(rhs))
254  }
255}
256
257impl Mul<i16> for i16x8 {
258  type Output = Self;
259  #[inline]
260  fn mul(self, rhs: i16) -> Self::Output {
261    self.mul(Self::splat(rhs))
262  }
263}
264
265impl Add<i16x8> for i16 {
266  type Output = i16x8;
267  #[inline]
268  fn add(self, rhs: i16x8) -> Self::Output {
269    i16x8::splat(self).add(rhs)
270  }
271}
272
273impl Sub<i16x8> for i16 {
274  type Output = i16x8;
275  #[inline]
276  fn sub(self, rhs: i16x8) -> Self::Output {
277    i16x8::splat(self).sub(rhs)
278  }
279}
280
281impl Mul<i16x8> for i16 {
282  type Output = i16x8;
283  #[inline]
284  fn mul(self, rhs: i16x8) -> Self::Output {
285    i16x8::splat(self).mul(rhs)
286  }
287}
288
289impl BitAnd for i16x8 {
290  type Output = Self;
291  #[inline]
292  fn bitand(self, rhs: Self) -> Self::Output {
293    pick! {
294      if #[cfg(target_feature="sse2")] {
295        Self { sse: bitand_m128i(self.sse, rhs.sse) }
296      } else if #[cfg(target_feature="simd128")] {
297        Self { simd: v128_and(self.simd, rhs.simd) }
298      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
299        unsafe {Self { neon: vandq_s16(self.neon, rhs.neon) }}
300      } else {
301        Self { arr: [
302          self.arr[0].bitand(rhs.arr[0]),
303          self.arr[1].bitand(rhs.arr[1]),
304          self.arr[2].bitand(rhs.arr[2]),
305          self.arr[3].bitand(rhs.arr[3]),
306          self.arr[4].bitand(rhs.arr[4]),
307          self.arr[5].bitand(rhs.arr[5]),
308          self.arr[6].bitand(rhs.arr[6]),
309          self.arr[7].bitand(rhs.arr[7]),
310        ]}
311      }
312    }
313  }
314}
315
316impl BitOr for i16x8 {
317  type Output = Self;
318  #[inline]
319  fn bitor(self, rhs: Self) -> Self::Output {
320    pick! {
321      if #[cfg(target_feature="sse2")] {
322        Self { sse: bitor_m128i(self.sse, rhs.sse) }
323      } else if #[cfg(target_feature="simd128")] {
324        Self { simd: v128_or(self.simd, rhs.simd) }
325      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
326        unsafe {Self { neon: vorrq_s16(self.neon, rhs.neon) }}
327      } else {
328        Self { arr: [
329          self.arr[0].bitor(rhs.arr[0]),
330          self.arr[1].bitor(rhs.arr[1]),
331          self.arr[2].bitor(rhs.arr[2]),
332          self.arr[3].bitor(rhs.arr[3]),
333          self.arr[4].bitor(rhs.arr[4]),
334          self.arr[5].bitor(rhs.arr[5]),
335          self.arr[6].bitor(rhs.arr[6]),
336          self.arr[7].bitor(rhs.arr[7]),
337        ]}
338      }
339    }
340  }
341}
342
343impl BitXor for i16x8 {
344  type Output = Self;
345  #[inline]
346  fn bitxor(self, rhs: Self) -> Self::Output {
347    pick! {
348      if #[cfg(target_feature="sse2")] {
349        Self { sse: bitxor_m128i(self.sse, rhs.sse) }
350      } else if #[cfg(target_feature="simd128")] {
351        Self { simd: v128_xor(self.simd, rhs.simd) }
352      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
353        unsafe {Self { neon: veorq_s16(self.neon, rhs.neon) }}
354      } else {
355        Self { arr: [
356          self.arr[0].bitxor(rhs.arr[0]),
357          self.arr[1].bitxor(rhs.arr[1]),
358          self.arr[2].bitxor(rhs.arr[2]),
359          self.arr[3].bitxor(rhs.arr[3]),
360          self.arr[4].bitxor(rhs.arr[4]),
361          self.arr[5].bitxor(rhs.arr[5]),
362          self.arr[6].bitxor(rhs.arr[6]),
363          self.arr[7].bitxor(rhs.arr[7]),
364        ]}
365      }
366    }
367  }
368}
369
370macro_rules! impl_shl_t_for_i16x8 {
371  ($($shift_type:ty),+ $(,)?) => {
372    $(impl Shl<$shift_type> for i16x8 {
373      type Output = Self;
374      /// Shifts all lanes by the value given.
375      #[inline]
376      fn shl(self, rhs: $shift_type) -> Self::Output {
377        pick! {
378          if #[cfg(target_feature="sse2")] {
379            let shift = cast([rhs as u64, 0]);
380            Self { sse: shl_all_u16_m128i(self.sse, shift) }
381          } else if #[cfg(target_feature="simd128")] {
382            Self { simd: i16x8_shl(self.simd, rhs as u32) }
383          } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
384            unsafe {Self { neon: vshlq_s16(self.neon, vmovq_n_s16(rhs as i16)) }}
385          } else {
386            let u = rhs as u32;
387            Self { arr: [
388              self.arr[0].wrapping_shl(u),
389              self.arr[1].wrapping_shl(u),
390              self.arr[2].wrapping_shl(u),
391              self.arr[3].wrapping_shl(u),
392              self.arr[4].wrapping_shl(u),
393              self.arr[5].wrapping_shl(u),
394              self.arr[6].wrapping_shl(u),
395              self.arr[7].wrapping_shl(u),
396            ]}
397          }
398        }
399      }
400    })+
401  };
402}
403impl_shl_t_for_i16x8!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
404
405macro_rules! impl_shr_t_for_i16x8 {
406  ($($shift_type:ty),+ $(,)?) => {
407    $(impl Shr<$shift_type> for i16x8 {
408      type Output = Self;
409      /// Shifts all lanes by the value given.
410      #[inline]
411      fn shr(self, rhs: $shift_type) -> Self::Output {
412        pick! {
413          if #[cfg(target_feature="sse2")] {
414            let shift = cast([rhs as u64, 0]);
415            Self { sse: shr_all_i16_m128i(self.sse, shift) }
416          } else if #[cfg(target_feature="simd128")] {
417            Self { simd: i16x8_shr(self.simd, rhs as u32) }
418          } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
419            unsafe {Self { neon: vshlq_s16(self.neon, vmovq_n_s16( -(rhs as i16))) }}
420          } else {
421            let u = rhs as u32;
422            Self { arr: [
423              self.arr[0].wrapping_shr(u),
424              self.arr[1].wrapping_shr(u),
425              self.arr[2].wrapping_shr(u),
426              self.arr[3].wrapping_shr(u),
427              self.arr[4].wrapping_shr(u),
428              self.arr[5].wrapping_shr(u),
429              self.arr[6].wrapping_shr(u),
430              self.arr[7].wrapping_shr(u),
431            ]}
432          }
433        }
434      }
435    })+
436  };
437}
438impl_shr_t_for_i16x8!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
439
440#[expect(deprecated)]
441impl CmpEq for i16x8 {
442  type Output = Self;
443  #[inline]
444  fn simd_eq(self, rhs: Self) -> Self::Output {
445    pick! {
446      if #[cfg(target_feature="sse2")] {
447        Self { sse: cmp_eq_mask_i16_m128i(self.sse, rhs.sse) }
448      } else if #[cfg(target_feature="simd128")] {
449        Self { simd: i16x8_eq(self.simd, rhs.simd) }
450      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
451        unsafe {Self { neon: vreinterpretq_s16_u16(vceqq_s16(self.neon, rhs.neon)) }}
452      } else {
453        Self { arr: [
454          if self.arr[0] == rhs.arr[0] { -1 } else { 0 },
455          if self.arr[1] == rhs.arr[1] { -1 } else { 0 },
456          if self.arr[2] == rhs.arr[2] { -1 } else { 0 },
457          if self.arr[3] == rhs.arr[3] { -1 } else { 0 },
458          if self.arr[4] == rhs.arr[4] { -1 } else { 0 },
459          if self.arr[5] == rhs.arr[5] { -1 } else { 0 },
460          if self.arr[6] == rhs.arr[6] { -1 } else { 0 },
461          if self.arr[7] == rhs.arr[7] { -1 } else { 0 },
462        ]}
463      }
464    }
465  }
466}
467
468#[expect(deprecated)]
469impl CmpGt for i16x8 {
470  type Output = Self;
471  #[inline]
472  fn simd_gt(self, rhs: Self) -> Self::Output {
473    pick! {
474      if #[cfg(target_feature="sse2")] {
475        Self { sse: cmp_gt_mask_i16_m128i(self.sse, rhs.sse) }
476      } else if #[cfg(target_feature="simd128")] {
477        Self { simd: i16x8_gt(self.simd, rhs.simd) }
478      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
479        unsafe {Self { neon: vreinterpretq_s16_u16(vcgtq_s16(self.neon, rhs.neon)) }}
480      } else {
481        Self { arr: [
482          if self.arr[0] > rhs.arr[0] { -1 } else { 0 },
483          if self.arr[1] > rhs.arr[1] { -1 } else { 0 },
484          if self.arr[2] > rhs.arr[2] { -1 } else { 0 },
485          if self.arr[3] > rhs.arr[3] { -1 } else { 0 },
486          if self.arr[4] > rhs.arr[4] { -1 } else { 0 },
487          if self.arr[5] > rhs.arr[5] { -1 } else { 0 },
488          if self.arr[6] > rhs.arr[6] { -1 } else { 0 },
489          if self.arr[7] > rhs.arr[7] { -1 } else { 0 },
490        ]}
491      }
492    }
493  }
494}
495
496#[expect(deprecated)]
497impl CmpLt for i16x8 {
498  type Output = Self;
499  #[inline]
500  fn simd_lt(self, rhs: Self) -> Self::Output {
501    pick! {
502      if #[cfg(target_feature="sse2")] {
503        Self { sse: cmp_lt_mask_i16_m128i(self.sse, rhs.sse) }
504      } else if #[cfg(target_feature="simd128")] {
505        Self { simd: i16x8_lt(self.simd, rhs.simd) }
506      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
507        unsafe {Self { neon: vreinterpretq_s16_u16(vcltq_s16(self.neon, rhs.neon)) }}
508      } else {
509        Self { arr: [
510          if self.arr[0] < rhs.arr[0] { -1 } else { 0 },
511          if self.arr[1] < rhs.arr[1] { -1 } else { 0 },
512          if self.arr[2] < rhs.arr[2] { -1 } else { 0 },
513          if self.arr[3] < rhs.arr[3] { -1 } else { 0 },
514          if self.arr[4] < rhs.arr[4] { -1 } else { 0 },
515          if self.arr[5] < rhs.arr[5] { -1 } else { 0 },
516          if self.arr[6] < rhs.arr[6] { -1 } else { 0 },
517          if self.arr[7] < rhs.arr[7] { -1 } else { 0 },
518        ]}
519      }
520    }
521  }
522}
523
524#[expect(deprecated)]
525impl CmpNe for i16x8 {
526  type Output = Self;
527  #[inline]
528  fn simd_ne(self, rhs: Self) -> Self::Output {
529    pick! {
530      if #[cfg(target_feature="sse2")] {
531        !self.simd_eq(rhs)
532      } else if #[cfg(target_feature="simd128")] {
533        Self { simd: i16x8_ne(self.simd, rhs.simd) }
534      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
535        !self.simd_eq(rhs)
536      } else {
537        Self { arr: [
538          if self.arr[0] != rhs.arr[0] { -1 } else { 0 },
539          if self.arr[1] != rhs.arr[1] { -1 } else { 0 },
540          if self.arr[2] != rhs.arr[2] { -1 } else { 0 },
541          if self.arr[3] != rhs.arr[3] { -1 } else { 0 },
542          if self.arr[4] != rhs.arr[4] { -1 } else { 0 },
543          if self.arr[5] != rhs.arr[5] { -1 } else { 0 },
544          if self.arr[6] != rhs.arr[6] { -1 } else { 0 },
545          if self.arr[7] != rhs.arr[7] { -1 } else { 0 },
546        ]}
547      }
548    }
549  }
550}
551
552#[expect(deprecated)]
553impl CmpLe for i16x8 {
554  type Output = Self;
555  #[inline]
556  fn simd_le(self, rhs: Self) -> Self::Output {
557    pick! {
558      if #[cfg(target_feature="sse2")] {
559        !self.simd_gt(rhs)
560      } else if #[cfg(target_feature="simd128")] {
561        Self { simd: i16x8_le(self.simd, rhs.simd) }
562      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
563        !self.simd_gt(rhs)
564      } else {
565        Self { arr: [
566          if self.arr[0] <= rhs.arr[0] { -1 } else { 0 },
567          if self.arr[1] <= rhs.arr[1] { -1 } else { 0 },
568          if self.arr[2] <= rhs.arr[2] { -1 } else { 0 },
569          if self.arr[3] <= rhs.arr[3] { -1 } else { 0 },
570          if self.arr[4] <= rhs.arr[4] { -1 } else { 0 },
571          if self.arr[5] <= rhs.arr[5] { -1 } else { 0 },
572          if self.arr[6] <= rhs.arr[6] { -1 } else { 0 },
573          if self.arr[7] <= rhs.arr[7] { -1 } else { 0 },
574        ]}
575      }
576    }
577  }
578}
579
580#[expect(deprecated)]
581impl CmpGe for i16x8 {
582  type Output = Self;
583  #[inline]
584  fn simd_ge(self, rhs: Self) -> Self::Output {
585    pick! {
586      if #[cfg(target_feature="sse2")] {
587        !self.simd_lt(rhs)
588      } else if #[cfg(target_feature="simd128")] {
589        Self { simd: i16x8_ge(self.simd, rhs.simd) }
590      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
591        !self.simd_lt(rhs)
592      } else {
593        Self { arr: [
594          if self.arr[0] >= rhs.arr[0] { -1 } else { 0 },
595          if self.arr[1] >= rhs.arr[1] { -1 } else { 0 },
596          if self.arr[2] >= rhs.arr[2] { -1 } else { 0 },
597          if self.arr[3] >= rhs.arr[3] { -1 } else { 0 },
598          if self.arr[4] >= rhs.arr[4] { -1 } else { 0 },
599          if self.arr[5] >= rhs.arr[5] { -1 } else { 0 },
600          if self.arr[6] >= rhs.arr[6] { -1 } else { 0 },
601          if self.arr[7] >= rhs.arr[7] { -1 } else { 0 },
602        ]}
603      }
604    }
605  }
606}
607
608impl i16x8 {
609  #[inline]
610  #[must_use]
611  pub const fn new(array: [i16; 8]) -> Self {
612    unsafe { core::mem::transmute(array) }
613  }
614
615  simd_comparison_fns!();
616
617  #[inline]
618  #[must_use]
619  #[doc(alias("movemask", "move_mask"))]
620  pub fn to_bitmask(self) -> u32 {
621    pick! {
622      if #[cfg(target_feature="sse2")] {
623        (move_mask_i8_m128i( pack_i16_to_i8_m128i(self.sse,self.sse)) as u32) & 0xff
624      } else if #[cfg(target_feature="simd128")] {
625        i16x8_bitmask(self.simd) as u32
626      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
627        unsafe
628        {
629          // set all to 1 if top bit is set, else 0
630          let masked = vcltq_s16(self.neon, vdupq_n_s16(0));
631
632          // select the right bit out of each lane
633          let selectbit : uint16x8_t = core::mem::transmute([1u16, 2, 4, 8, 16, 32, 64, 128]);
634          let r = vandq_u16(masked, selectbit);
635
636          // horizontally add the 16-bit lanes
637          vaddvq_u16(r) as u32
638         }
639       } else {
640        ((self.arr[0] < 0) as u32) << 0 |
641        ((self.arr[1] < 0) as u32) << 1 |
642        ((self.arr[2] < 0) as u32) << 2 |
643        ((self.arr[3] < 0) as u32) << 3 |
644        ((self.arr[4] < 0) as u32) << 4 |
645        ((self.arr[5] < 0) as u32) << 5 |
646        ((self.arr[6] < 0) as u32) << 6 |
647        ((self.arr[7] < 0) as u32) << 7
648      }
649    }
650  }
651
652  #[inline]
653  #[must_use]
654  pub fn any(self) -> bool {
655    pick! {
656      if #[cfg(target_feature="sse2")] {
657        (move_mask_i8_m128i(self.sse) & 0b1010101010101010) != 0
658      } else if #[cfg(target_feature="simd128")] {
659        u16x8_bitmask(self.simd) != 0
660      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
661        unsafe {
662          vminvq_s16(self.neon) < 0
663        }
664      } else {
665        let v : [u64;2] = cast(self);
666        ((v[0] | v[1]) & 0x8000800080008000) != 0
667      }
668    }
669  }
670
671  #[inline]
672  #[must_use]
673  pub fn all(self) -> bool {
674    pick! {
675      if #[cfg(target_feature="sse2")] {
676        (move_mask_i8_m128i(self.sse) & 0b1010101010101010) == 0b1010101010101010
677      } else if #[cfg(target_feature="simd128")] {
678        u16x8_bitmask(self.simd) == 0b11111111
679      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
680        unsafe {
681          vmaxvq_s16(self.neon) < 0
682        }
683      } else {
684        let v : [u64;2] = cast(self);
685        (v[0] & v[1] & 0x8000800080008000) == 0x8000800080008000
686      }
687    }
688  }
689
690  #[inline]
691  #[must_use]
692  pub fn none(self) -> bool {
693    !self.any()
694  }
695
696  /// Unpack the lower half of the input and expand it to `i16` values.
697  #[inline]
698  #[must_use]
699  pub fn from_u8x16_low(u: u8x16) -> Self {
700    pick! {
701      if #[cfg(target_feature="sse2")] {
702        Self{ sse: unpack_low_i8_m128i(u.sse, m128i::zeroed()) }
703      } else {
704        let u_arr: [u8; 16] = cast(u);
705        cast([
706          u_arr[0] as u16 as i16,
707          u_arr[1] as u16 as i16,
708          u_arr[2] as u16 as i16,
709          u_arr[3] as u16 as i16,
710          u_arr[4] as u16 as i16,
711          u_arr[5] as u16 as i16,
712          u_arr[6] as u16 as i16,
713          u_arr[7] as u16 as i16,
714        ])
715      }
716    }
717  }
718
719  /// Unpack the upper half of the input and expand it to `i16` values.
720  #[inline]
721  #[must_use]
722  pub fn from_u8x16_high(u: u8x16) -> Self {
723    pick! {
724      if #[cfg(target_feature="sse2")] {
725        Self{ sse: unpack_high_i8_m128i(u.sse, m128i::zeroed()) }
726      } else {
727        let u_arr: [u8; 16] = cast(u);
728        cast([
729          u_arr[8] as u16 as i16,
730          u_arr[9] as u16 as i16,
731          u_arr[10] as u16 as i16,
732          u_arr[11] as u16 as i16,
733          u_arr[12] as u16 as i16,
734          u_arr[13] as u16 as i16,
735          u_arr[14] as u16 as i16,
736          u_arr[15] as u16 as i16,
737        ])
738      }
739    }
740  }
741
742  /// returns low `i16` of `i32`, saturating values that are too large
743  #[inline]
744  #[must_use]
745  pub fn from_i32x8_saturate(v: i32x8) -> Self {
746    pick! {
747      if #[cfg(target_feature="avx2")] {
748        i16x8 { sse: pack_i32_to_i16_m128i( extract_m128i_from_m256i::<0>(v.avx2), extract_m128i_from_m256i::<1>(v.avx2))  }
749      } else if #[cfg(target_feature="sse2")] {
750        i16x8 { sse: pack_i32_to_i16_m128i( v.a.sse, v.b.sse ) }
751      } else if #[cfg(target_feature="simd128")] {
752        use core::arch::wasm32::*;
753
754        i16x8 { simd: i16x8_narrow_i32x4(v.a.simd, v.b.simd) }
755      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
756        use core::arch::aarch64::*;
757
758        unsafe {
759          i16x8 { neon: vcombine_s16(vqmovn_s32(v.a.neon), vqmovn_s32(v.b.neon)) }
760        }
761      } else {
762        fn clamp(a : i32) -> i16 {
763            if a < i16::MIN as i32 {
764                i16::MIN
765            }
766            else if a > i16::MAX as i32 {
767                i16::MAX
768            } else {
769                a as i16
770            }
771        }
772
773        i16x8::new([
774          clamp(v.as_array()[0]),
775          clamp(v.as_array()[1]),
776          clamp(v.as_array()[2]),
777          clamp(v.as_array()[3]),
778          clamp(v.as_array()[4]),
779          clamp(v.as_array()[5]),
780          clamp(v.as_array()[6]),
781          clamp(v.as_array()[7]),
782        ])
783      }
784    }
785  }
786
787  /// returns low `i16` of `i32`, truncating the upper bits if they are set
788  #[inline]
789  #[must_use]
790  pub fn from_i32x8_truncate(v: i32x8) -> Self {
791    pick! {
792      if #[cfg(target_feature="avx2")] {
793        let a = v.avx2.bitand(set_splat_i32_m256i(0xffff));
794        i16x8 { sse: pack_i32_to_u16_m128i( extract_m128i_from_m256i::<0>(a), extract_m128i_from_m256i::<1>(a) ) }
795      } else if #[cfg(target_feature="sse2")] {
796        let a = shr_imm_i32_m128i::<16>(shl_imm_u32_m128i::<16>(v.a.sse));
797        let b = shr_imm_i32_m128i::<16>(shl_imm_u32_m128i::<16>(v.b.sse));
798
799        i16x8 { sse: pack_i32_to_i16_m128i( a, b)  }
800      } else {
801      i16x8::new([
802        v.as_array()[0] as i16,
803        v.as_array()[1] as i16,
804        v.as_array()[2] as i16,
805        v.as_array()[3] as i16,
806        v.as_array()[4] as i16,
807        v.as_array()[5] as i16,
808        v.as_array()[6] as i16,
809        v.as_array()[7] as i16,
810      ])
811      }
812    }
813  }
814
815  #[inline]
816  #[must_use]
817  pub fn from_slice_unaligned(input: &[i16]) -> Self {
818    assert!(input.len() >= 8);
819
820    pick! {
821      if #[cfg(target_feature="sse2")] {
822        unsafe { Self { sse: load_unaligned_m128i( &*(input.as_ptr() as * const [u8;16]) ) } }
823      } else if #[cfg(target_feature="simd128")] {
824        unsafe { Self { simd: v128_load(input.as_ptr() as *const v128 ) } }
825      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
826        unsafe { Self { neon: vld1q_s16( input.as_ptr() as *const i16 ) } }
827      } else {
828        // 2018 edition doesn't have try_into
829        unsafe { Self::new( *(input.as_ptr() as * const [i16;8]) ) }
830      }
831    }
832  }
833
834  #[inline]
835  #[must_use]
836  pub fn blend(self, t: Self, f: Self) -> Self {
837    pick! {
838      if #[cfg(target_feature="sse4.1")] {
839        Self { sse: blend_varying_i8_m128i(f.sse, t.sse, self.sse) }
840      } else if #[cfg(target_feature="simd128")] {
841        Self { simd: v128_bitselect(t.simd, f.simd, self.simd) }
842      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
843        unsafe {Self { neon: vbslq_s16(vreinterpretq_u16_s16(self.neon), t.neon, f.neon) }}
844      } else {
845        generic_bit_blend(self, t, f)
846      }
847    }
848  }
849
850  /// Returns true for each positive element and false if it is zero or
851  /// negative.
852  #[inline]
853  #[must_use]
854  pub fn is_positive(self) -> Self {
855    pick! {
856      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
857        Self { neon: unsafe { vreinterpretq_s16_u16(vcgtzq_s16(self.neon)) } }
858      } else {
859        self.simd_gt(Self::ZERO)
860      }
861    }
862  }
863
864  /// Returns true for each negative element and false if it is zero or
865  /// positive.
866  #[inline]
867  #[must_use]
868  pub fn is_negative(self) -> Self {
869    pick! {
870      if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
871        Self { neon: unsafe { vreinterpretq_s16_u16(vcltzq_s16(self.neon)) } }
872      } else {
873        self.simd_lt(Self::ZERO)
874      }
875    }
876  }
877
878  /// horizontal add of all the elements of the vector
879  #[inline]
880  #[must_use]
881  pub fn reduce_add(self) -> i16 {
882    pick! {
883      if #[cfg(target_feature="sse2")] {
884        // there is a horizontal add instruction on ssse3, but apparently it is very slow on some AMD CPUs
885        let hi64 = shuffle_ai_f32_all_m128i::<0b01_00_11_10>(self.sse);
886        let sum64 = add_i16_m128i(self.sse, hi64);
887        let hi32 = shuffle_ai_f32_all_m128i::<0b11_10_00_01>(sum64);
888        let sum32 = add_i16_m128i(sum64, hi32);
889        let lo16 = shr_imm_u32_m128i::<16>(sum32);
890        let sum16 = add_i16_m128i(sum32, lo16);
891        extract_i16_as_i32_m128i::<0>(sum16) as i16
892      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
893        unsafe { vaddvq_s16(self.neon) }
894      } else {
895        let arr: [i16; 8] = cast(self);
896
897        // most boring implementation possible so optimizer doesn't overthink this
898        let mut r = arr[0];
899        r = r.wrapping_add(arr[1]);
900        r = r.wrapping_add(arr[2]);
901        r = r.wrapping_add(arr[3]);
902        r = r.wrapping_add(arr[4]);
903        r = r.wrapping_add(arr[5]);
904        r = r.wrapping_add(arr[6]);
905        r.wrapping_add(arr[7])
906      }
907    }
908  }
909
910  /// horizontal min of all the elements of the vector
911  #[inline]
912  #[must_use]
913  pub fn reduce_min(self) -> i16 {
914    pick! {
915        if #[cfg(target_feature="sse2")] {
916          let hi64 = shuffle_ai_f32_all_m128i::<0b01_00_11_10>(self.sse);
917          let sum64 = min_i16_m128i(self.sse, hi64);
918          let hi32 = shuffle_ai_f32_all_m128i::<0b11_10_00_01>(sum64);
919          let sum32 = min_i16_m128i(sum64, hi32);
920          let lo16 = shr_imm_u32_m128i::<16>(sum32);
921          let sum16 = min_i16_m128i(sum32, lo16);
922          extract_i16_as_i32_m128i::<0>(sum16) as i16
923        } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
924          unsafe { vminvq_s16(self.neon) }
925        } else {
926        let arr: [i16; 8] = cast(self);
927
928        // most boring implementation possible so optimizer doesn't overthink this
929        let mut r = arr[0];
930        r = r.min(arr[1]);
931        r = r.min(arr[2]);
932        r = r.min(arr[3]);
933        r = r.min(arr[4]);
934        r = r.min(arr[5]);
935        r = r.min(arr[6]);
936        r.min(arr[7])
937      }
938    }
939  }
940
941  /// horizontal max of all the elements of the vector
942  #[inline]
943  #[must_use]
944  pub fn reduce_max(self) -> i16 {
945    pick! {
946        if #[cfg(target_feature="sse2")] {
947          let hi64 = shuffle_ai_f32_all_m128i::<0b01_00_11_10>(self.sse);
948          let sum64 = max_i16_m128i(self.sse, hi64);
949          let hi32 = shuffle_ai_f32_all_m128i::<0b11_10_00_01>(sum64);
950          let sum32 = max_i16_m128i(sum64, hi32);
951          let lo16 = shr_imm_u32_m128i::<16>(sum32);
952          let sum16 = max_i16_m128i(sum32, lo16);
953          extract_i16_as_i32_m128i::<0>(sum16) as i16
954        } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
955          unsafe { vmaxvq_s16(self.neon) }
956        } else {
957        let arr: [i16; 8] = cast(self);
958
959        // most boring implementation possible so optimizer doesn't overthink this
960        let mut r = arr[0];
961        r = r.max(arr[1]);
962        r = r.max(arr[2]);
963        r = r.max(arr[3]);
964        r = r.max(arr[4]);
965        r = r.max(arr[5]);
966        r = r.max(arr[6]);
967        r.max(arr[7])
968      }
969    }
970  }
971
972  #[inline]
973  #[must_use]
974  pub fn abs(self) -> Self {
975    pick! {
976      if #[cfg(target_feature="sse2")] {
977        let mask = shr_imm_i16_m128i::<15>(self.sse);
978        Self { sse: bitxor_m128i(add_i16_m128i(self.sse, mask), mask) }
979      } else if #[cfg(target_feature="ssse3")] {
980        Self { sse: abs_i16_m128i(self.sse) }
981      } else if #[cfg(target_feature="simd128")] {
982        Self { simd: i16x8_abs(self.simd) }
983      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
984        unsafe {Self { neon: vabsq_s16(self.neon) }}
985      } else {
986        let arr: [i16; 8] = cast(self);
987        cast(
988          [
989            arr[0].wrapping_abs(),
990            arr[1].wrapping_abs(),
991            arr[2].wrapping_abs(),
992            arr[3].wrapping_abs(),
993            arr[4].wrapping_abs(),
994            arr[5].wrapping_abs(),
995            arr[6].wrapping_abs(),
996            arr[7].wrapping_abs(),
997          ])
998      }
999    }
1000  }
1001
1002  #[inline]
1003  #[must_use]
1004  pub fn unsigned_abs(self) -> u16x8 {
1005    pick! {
1006      if #[cfg(target_feature="sse2")] {
1007        let mask = shr_imm_i16_m128i::<15>(self.sse);
1008        u16x8 { sse: bitxor_m128i(add_i16_m128i(self.sse, mask), mask) }
1009      } else if #[cfg(target_feature="ssse3")] {
1010        u16x8 { sse: abs_i16_m128i(self.sse) }
1011      } else if #[cfg(target_feature="simd128")] {
1012        u16x8 { simd: i16x8_abs(self.simd) }
1013      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1014        unsafe {u16x8 { neon: vreinterpretq_u16_s16(vabsq_s16(self.neon)) }}
1015      } else {
1016        let arr: [i16; 8] = cast(self);
1017        cast(
1018          [
1019            arr[0].unsigned_abs(),
1020            arr[1].unsigned_abs(),
1021            arr[2].unsigned_abs(),
1022            arr[3].unsigned_abs(),
1023            arr[4].unsigned_abs(),
1024            arr[5].unsigned_abs(),
1025            arr[6].unsigned_abs(),
1026            arr[7].unsigned_abs(),
1027          ])
1028      }
1029    }
1030  }
1031
1032  signed_fn_signum!();
1033
1034  #[inline]
1035  #[must_use]
1036  pub fn max(self, rhs: Self) -> Self {
1037    pick! {
1038      if #[cfg(target_feature="sse2")] {
1039        Self { sse: max_i16_m128i(self.sse, rhs.sse) }
1040      } else if #[cfg(target_feature="simd128")] {
1041        Self { simd: i16x8_max(self.simd, rhs.simd) }
1042      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1043        unsafe {Self { neon: vmaxq_s16(self.neon, rhs.neon) }}
1044      } else {
1045        self.simd_lt(rhs).blend(rhs, self)
1046      }
1047    }
1048  }
1049  #[inline]
1050  #[must_use]
1051  pub fn min(self, rhs: Self) -> Self {
1052    pick! {
1053      if #[cfg(target_feature="sse2")] {
1054        Self { sse: min_i16_m128i(self.sse, rhs.sse) }
1055      } else if #[cfg(target_feature="simd128")] {
1056        Self { simd: i16x8_min(self.simd, rhs.simd) }
1057      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1058        unsafe {Self { neon: vminq_s16(self.neon, rhs.neon) }}
1059      } else {
1060        self.simd_lt(rhs).blend(self, rhs)
1061      }
1062    }
1063  }
1064
1065  integer_fn_clamp!();
1066
1067  #[inline]
1068  #[must_use]
1069  pub fn saturating_add(self, rhs: Self) -> Self {
1070    pick! {
1071      if #[cfg(target_feature="sse2")] {
1072        Self { sse: add_saturating_i16_m128i(self.sse, rhs.sse) }
1073      } else if #[cfg(target_feature="simd128")] {
1074        Self { simd: i16x8_add_sat(self.simd, rhs.simd) }
1075      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1076        unsafe {Self { neon: vqaddq_s16(self.neon, rhs.neon) }}
1077      } else {
1078        Self { arr: [
1079          self.arr[0].saturating_add(rhs.arr[0]),
1080          self.arr[1].saturating_add(rhs.arr[1]),
1081          self.arr[2].saturating_add(rhs.arr[2]),
1082          self.arr[3].saturating_add(rhs.arr[3]),
1083          self.arr[4].saturating_add(rhs.arr[4]),
1084          self.arr[5].saturating_add(rhs.arr[5]),
1085          self.arr[6].saturating_add(rhs.arr[6]),
1086          self.arr[7].saturating_add(rhs.arr[7]),
1087        ]}
1088      }
1089    }
1090  }
1091  #[inline]
1092  #[must_use]
1093  pub fn saturating_sub(self, rhs: Self) -> Self {
1094    pick! {
1095      if #[cfg(target_feature="sse2")] {
1096        Self { sse: sub_saturating_i16_m128i(self.sse, rhs.sse) }
1097      } else if #[cfg(target_feature="simd128")] {
1098        Self { simd: i16x8_sub_sat(self.simd, rhs.simd) }
1099      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1100        unsafe { Self { neon: vqsubq_s16(self.neon, rhs.neon) } }
1101      } else {
1102        Self { arr: [
1103          self.arr[0].saturating_sub(rhs.arr[0]),
1104          self.arr[1].saturating_sub(rhs.arr[1]),
1105          self.arr[2].saturating_sub(rhs.arr[2]),
1106          self.arr[3].saturating_sub(rhs.arr[3]),
1107          self.arr[4].saturating_sub(rhs.arr[4]),
1108          self.arr[5].saturating_sub(rhs.arr[5]),
1109          self.arr[6].saturating_sub(rhs.arr[6]),
1110          self.arr[7].saturating_sub(rhs.arr[7]),
1111        ]}
1112      }
1113    }
1114  }
1115
1116  /// Lanewise saturating multiply.
1117  #[inline]
1118  #[must_use]
1119  pub fn saturating_mul(self, rhs: Self) -> Self {
1120    pick! {
1121      if #[cfg(target_feature="simd128")] {
1122        let low_wide_mul = i32x4_extmul_low_i16x8(self.simd, rhs.simd);
1123        let high_wide_mul = i32x4_extmul_high_i16x8(self.simd, rhs.simd);
1124        let low = Self { simd: i16x8_shuffle::<0, 2, 4, 6, 8, 10, 12, 14>(low_wide_mul, high_wide_mul) };
1125        let high = Self { simd: i16x8_shuffle::<1, 3, 5, 7, 9, 11, 13, 15>(low_wide_mul, high_wide_mul) };
1126
1127        let no_overflow = high.simd_eq(low.is_negative());
1128        let limit = Self::MAX ^ (self ^ rhs).is_negative();
1129        no_overflow.blend(low, limit)
1130      } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))] {
1131        unsafe {
1132          let low_wide_mul = vreinterpretq_s16_s32(
1133            vmull_s16(vget_low_s16(self.neon), vget_low_s16(rhs.neon)),
1134          );
1135          let high_wide_mul = vreinterpretq_s16_s32(
1136            vmull_s16(vget_high_s16(self.neon), vget_high_s16(rhs.neon)),
1137          );
1138          let low_high = vuzpq_s16(low_wide_mul, high_wide_mul);
1139          let low = Self { neon: low_high.0 };
1140          let high = Self { neon: low_high.1 };
1141
1142          let no_overflow = high.simd_eq(low.is_negative());
1143          let limit = Self::MAX ^ (self ^ rhs).is_negative();
1144          no_overflow.blend(low, limit)
1145        }
1146      } else {
1147        let self_array = self.to_array();
1148        let rhs_array = rhs.to_array();
1149
1150        Self::new([
1151          self_array[0].saturating_mul(rhs_array[0]),
1152          self_array[1].saturating_mul(rhs_array[1]),
1153          self_array[2].saturating_mul(rhs_array[2]),
1154          self_array[3].saturating_mul(rhs_array[3]),
1155          self_array[4].saturating_mul(rhs_array[4]),
1156          self_array[5].saturating_mul(rhs_array[5]),
1157          self_array[6].saturating_mul(rhs_array[6]),
1158          self_array[7].saturating_mul(rhs_array[7]),
1159        ])
1160      }
1161    }
1162  }
1163
1164  integer_fn_saturating_div!([0, 1, 2, 3, 4, 5, 6, 7]);
1165
1166  /// Calculates partial dot product.
1167  /// Multiplies packed signed 16-bit integers, producing intermediate signed
1168  /// 32-bit integers. Horizontally add adjacent pairs of intermediate 32-bit
1169  /// integers.
1170  #[inline]
1171  #[must_use]
1172  pub fn dot(self, rhs: Self) -> i32x4 {
1173    pick! {
1174      if #[cfg(target_feature="sse2")] {
1175        i32x4 { sse:  mul_i16_horizontal_add_m128i(self.sse, rhs.sse) }
1176      } else if #[cfg(target_feature="simd128")] {
1177        i32x4 { simd: i32x4_dot_i16x8(self.simd, rhs.simd) }
1178      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1179        unsafe {
1180          let pl = vmull_s16(vget_low_s16(self.neon),  vget_low_s16(rhs.neon));
1181          let ph = vmull_high_s16(self.neon, rhs.neon);
1182          i32x4 { neon: vpaddq_s32(pl, ph) }
1183        }
1184      } else {
1185        i32x4 { arr: [
1186          (i32::from(self.arr[0]) * i32::from(rhs.arr[0])) + (i32::from(self.arr[1]) * i32::from(rhs.arr[1])),
1187          (i32::from(self.arr[2]) * i32::from(rhs.arr[2])) + (i32::from(self.arr[3]) * i32::from(rhs.arr[3])),
1188          (i32::from(self.arr[4]) * i32::from(rhs.arr[4])) + (i32::from(self.arr[5]) * i32::from(rhs.arr[5])),
1189          (i32::from(self.arr[6]) * i32::from(rhs.arr[6])) + (i32::from(self.arr[7]) * i32::from(rhs.arr[7])),
1190        ] }
1191      }
1192    }
1193  }
1194
1195  /// Multiply and scale equivalent to `((self * rhs) + 0x4000) >> 15` on each
1196  /// lane, effectively multiplying by a 16 bit fixed point number between `-1`
1197  /// and `1`. This corresponds to the following instructions:
1198  /// - `vqrdmulhq_s16` instruction on neon
1199  /// - `i16x8_q15mulr_sat` on simd128
1200  /// - `_mm_mulhrs_epi16` on ssse3
1201  /// - emulated via `mul_i16_*` on sse2
1202  #[inline]
1203  #[must_use]
1204  pub fn mul_scale_round(self, rhs: Self) -> Self {
1205    pick! {
1206      if #[cfg(target_feature="ssse3")] {
1207        Self { sse:  mul_i16_scale_round_m128i(self.sse, rhs.sse) }
1208      } else if #[cfg(target_feature="sse2")] {
1209        // unfortunately mul_i16_scale_round_m128i only got added in sse3
1210        let hi = mul_i16_keep_high_m128i(self.sse, rhs.sse);
1211        let lo = mul_i16_keep_low_m128i(self.sse, rhs.sse);
1212        let mut v1 = unpack_low_i16_m128i(lo, hi);
1213        let mut v2 = unpack_high_i16_m128i(lo, hi);
1214        let a = set_splat_i32_m128i(0x4000);
1215        v1 = shr_imm_i32_m128i::<15>(add_i32_m128i(v1, a));
1216        v2 = shr_imm_i32_m128i::<15>(add_i32_m128i(v2, a));
1217        let s = pack_i32_to_i16_m128i(v1, v2);
1218        Self { sse: s }
1219      } else if #[cfg(target_feature="simd128")] {
1220        Self { simd: i16x8_q15mulr_sat(self.simd, rhs.simd) }
1221      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1222        unsafe { Self { neon: vqrdmulhq_s16(self.neon, rhs.neon) } }
1223      } else {
1224        // compiler does a surprisingly good job of vectorizing this
1225        Self { arr: [
1226          ((i32::from(self.arr[0]) * i32::from(rhs.arr[0]) + 0x4000) >> 15) as i16,
1227          ((i32::from(self.arr[1]) * i32::from(rhs.arr[1]) + 0x4000) >> 15) as i16,
1228          ((i32::from(self.arr[2]) * i32::from(rhs.arr[2]) + 0x4000) >> 15) as i16,
1229          ((i32::from(self.arr[3]) * i32::from(rhs.arr[3]) + 0x4000) >> 15) as i16,
1230          ((i32::from(self.arr[4]) * i32::from(rhs.arr[4]) + 0x4000) >> 15) as i16,
1231          ((i32::from(self.arr[5]) * i32::from(rhs.arr[5]) + 0x4000) >> 15) as i16,
1232          ((i32::from(self.arr[6]) * i32::from(rhs.arr[6]) + 0x4000) >> 15) as i16,
1233          ((i32::from(self.arr[7]) * i32::from(rhs.arr[7]) + 0x4000) >> 15) as i16,
1234        ]}
1235      }
1236    }
1237  }
1238
1239  /// Multiples two `i16x8` and return the high part of intermediate `i32x8`
1240  #[inline]
1241  #[must_use]
1242  pub fn mul_keep_high(lhs: Self, rhs: Self) -> Self {
1243    pick! {
1244      if #[cfg(target_feature="sse2")] {
1245        Self { sse: mul_i16_keep_high_m128i(lhs.sse, rhs.sse) }
1246      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
1247        let lhs_low = unsafe { vget_low_s16(lhs.neon) };
1248        let rhs_low = unsafe { vget_low_s16(rhs.neon) };
1249
1250        let lhs_high = unsafe { vget_high_s16(lhs.neon) };
1251        let rhs_high = unsafe { vget_high_s16(rhs.neon) };
1252
1253        let low = unsafe { vmull_s16(lhs_low, rhs_low) };
1254        let high = unsafe { vmull_s16(lhs_high, rhs_high) };
1255
1256        i16x8 { neon: unsafe { vreinterpretq_s16_u16(vuzpq_u16(vreinterpretq_u16_s32(low), vreinterpretq_u16_s32(high)).1) } }
1257      } else if #[cfg(target_feature="simd128")] {
1258        let low =  i32x4_extmul_low_i16x8(lhs.simd, rhs.simd);
1259        let high = i32x4_extmul_high_i16x8(lhs.simd, rhs.simd);
1260
1261        Self { simd: i16x8_shuffle::<1, 3, 5, 7, 9, 11, 13, 15>(low, high) }
1262      } else {
1263        i16x8::new([
1264          ((i32::from(rhs.as_array()[0]) * i32::from(lhs.as_array()[0])) >> 16) as i16,
1265          ((i32::from(rhs.as_array()[1]) * i32::from(lhs.as_array()[1])) >> 16) as i16,
1266          ((i32::from(rhs.as_array()[2]) * i32::from(lhs.as_array()[2])) >> 16) as i16,
1267          ((i32::from(rhs.as_array()[3]) * i32::from(lhs.as_array()[3])) >> 16) as i16,
1268          ((i32::from(rhs.as_array()[4]) * i32::from(lhs.as_array()[4])) >> 16) as i16,
1269          ((i32::from(rhs.as_array()[5]) * i32::from(lhs.as_array()[5])) >> 16) as i16,
1270          ((i32::from(rhs.as_array()[6]) * i32::from(lhs.as_array()[6])) >> 16) as i16,
1271          ((i32::from(rhs.as_array()[7]) * i32::from(lhs.as_array()[7])) >> 16) as i16,
1272        ])
1273      }
1274    }
1275  }
1276
1277  /// multiplies two `i16x8` and returns the result as a widened `i32x8`
1278  #[inline]
1279  #[must_use]
1280  pub fn mul_widen(self, rhs: Self) -> i32x8 {
1281    pick! {
1282      if #[cfg(target_feature="avx2")] {
1283        let a = convert_to_i32_m256i_from_i16_m128i(self.sse);
1284        let b = convert_to_i32_m256i_from_i16_m128i(rhs.sse);
1285        i32x8 { avx2: mul_i32_keep_low_m256i(a,b) }
1286      } else if #[cfg(target_feature="sse2")] {
1287         let low = mul_i16_keep_low_m128i(self.sse, rhs.sse);
1288         let high = mul_i16_keep_high_m128i(self.sse, rhs.sse);
1289         i32x8 {
1290          a: i32x4 { sse:unpack_low_i16_m128i(low, high) },
1291          b: i32x4 { sse:unpack_high_i16_m128i(low, high) }
1292        }
1293      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
1294         let lhs_low = unsafe { vget_low_s16(self.neon) };
1295         let rhs_low = unsafe { vget_low_s16(rhs.neon) };
1296
1297         let lhs_high = unsafe { vget_high_s16(self.neon) };
1298         let rhs_high = unsafe { vget_high_s16(rhs.neon) };
1299
1300         let low = unsafe { vmull_s16(lhs_low, rhs_low) };
1301         let high = unsafe { vmull_s16(lhs_high, rhs_high) };
1302
1303         i32x8 { a: i32x4 { neon: low }, b: i32x4 {neon: high } }
1304       } else {
1305        let a = self.as_array();
1306        let b = rhs.as_array();
1307         i32x8::new([
1308           i32::from(a[0]) * i32::from(b[0]),
1309           i32::from(a[1]) * i32::from(b[1]),
1310           i32::from(a[2]) * i32::from(b[2]),
1311           i32::from(a[3]) * i32::from(b[3]),
1312           i32::from(a[4]) * i32::from(b[4]),
1313           i32::from(a[5]) * i32::from(b[5]),
1314           i32::from(a[6]) * i32::from(b[6]),
1315           i32::from(a[7]) * i32::from(b[7]),
1316         ])
1317       }
1318    }
1319  }
1320
1321  /// transpose matrix of 8x8 i16 matrix
1322  #[must_use]
1323  #[inline]
1324  pub fn transpose(data: [i16x8; 8]) -> [i16x8; 8] {
1325    pick! {
1326      if #[cfg(target_feature="sse2")] {
1327        let a1 = unpack_low_i16_m128i(data[0].sse, data[1].sse);
1328        let a2 = unpack_high_i16_m128i(data[0].sse, data[1].sse);
1329        let a3 = unpack_low_i16_m128i(data[2].sse, data[3].sse);
1330        let a4 = unpack_high_i16_m128i(data[2].sse, data[3].sse);
1331        let a5 = unpack_low_i16_m128i(data[4].sse, data[5].sse);
1332        let a6 = unpack_high_i16_m128i(data[4].sse, data[5].sse);
1333        let a7 = unpack_low_i16_m128i(data[6].sse, data[7].sse);
1334        let a8 = unpack_high_i16_m128i(data[6].sse, data[7].sse);
1335
1336        let b1 = unpack_low_i32_m128i(a1, a3);
1337        let b2 = unpack_high_i32_m128i(a1, a3);
1338        let b3 = unpack_low_i32_m128i(a2, a4);
1339        let b4 = unpack_high_i32_m128i(a2, a4);
1340        let b5 = unpack_low_i32_m128i(a5, a7);
1341        let b6 = unpack_high_i32_m128i(a5, a7);
1342        let b7 = unpack_low_i32_m128i(a6, a8);
1343        let b8 = unpack_high_i32_m128i(a6, a8);
1344
1345        [
1346          i16x8 { sse: unpack_low_i64_m128i(b1, b5) },
1347          i16x8 { sse: unpack_high_i64_m128i(b1, b5) },
1348          i16x8 { sse: unpack_low_i64_m128i(b2, b6) },
1349          i16x8 { sse: unpack_high_i64_m128i(b2, b6) },
1350          i16x8 { sse: unpack_low_i64_m128i(b3, b7) },
1351          i16x8 { sse: unpack_high_i64_m128i(b3, b7) },
1352          i16x8 { sse: unpack_low_i64_m128i(b4, b8) },
1353          i16x8 { sse: unpack_high_i64_m128i(b4, b8) } ,
1354        ]
1355     } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1356
1357          #[inline] fn vtrq32(a : int16x8_t, b : int16x8_t) -> (int16x8_t, int16x8_t)
1358          {
1359              unsafe {
1360                let r = vtrnq_s32(vreinterpretq_s32_s16(a),vreinterpretq_s32_s16(b));
1361                (vreinterpretq_s16_s32(r.0), vreinterpretq_s16_s32(r.1))
1362              }
1363          }
1364
1365        unsafe {
1366          let (q0,q2) = vtrq32(data[0].neon, data[2].neon);
1367          let (q1,q3) = vtrq32(data[1].neon, data[3].neon);
1368          let (q4,q6) = vtrq32(data[4].neon, data[6].neon);
1369          let (q5,q7) = vtrq32(data[5].neon, data[7].neon);
1370
1371          let b1 = vtrnq_s16(q0, q1);
1372          let b2 = vtrnq_s16(q2, q3);
1373          let b3 = vtrnq_s16(q4, q5);
1374          let b4 = vtrnq_s16(q6, q7);
1375
1376          // There is no vtrnq_s64 unfortunately, so there's this mess
1377          // which does a somewhat reasonable job, but not as good as the
1378          // assembly versions which just swap the 64 bit register aliases.
1379          [
1380            i16x8 { neon: vcombine_s16(vget_low_s16(b1.0), vget_low_s16(b3.0)) },
1381            i16x8 { neon: vcombine_s16(vget_low_s16(b1.1), vget_low_s16(b3.1)) },
1382            i16x8 { neon: vcombine_s16(vget_low_s16(b2.0), vget_low_s16(b4.0)) },
1383            i16x8 { neon: vcombine_s16(vget_low_s16(b2.1), vget_low_s16(b4.1)) },
1384            i16x8 { neon: vcombine_s16(vget_high_s16(b1.0), vget_high_s16(b3.0)) },
1385            i16x8 { neon: vcombine_s16(vget_high_s16(b1.1), vget_high_s16(b3.1)) },
1386            i16x8 { neon: vcombine_s16(vget_high_s16(b2.0), vget_high_s16(b4.0)) },
1387            i16x8 { neon: vcombine_s16(vget_high_s16(b2.1), vget_high_s16(b4.1)) },
1388          ]
1389        }
1390      } else if #[cfg(target_feature="simd128")] {
1391        #[inline] fn lo_i16(a : v128, b : v128) -> v128 { i16x8_shuffle::<0, 8, 1, 9, 2, 10, 3, 11>(a,b) }
1392        #[inline] fn hi_i16(a : v128, b : v128) -> v128 { i16x8_shuffle::<4, 12, 5, 13, 6, 14, 7, 15>(a,b) }
1393        #[inline] fn lo_i32(a : v128, b : v128) -> v128 { i32x4_shuffle::<0, 4, 1, 5>(a,b) }
1394        #[inline] fn hi_i32(a : v128, b : v128) -> v128 { i32x4_shuffle::<2, 6, 3, 7>(a,b) }
1395        #[inline] fn lo_i64(a : v128, b : v128) -> v128 { i64x2_shuffle::<0, 2>(a,b) }
1396        #[inline] fn hi_i64(a : v128, b : v128) -> v128 { i64x2_shuffle::<1, 3>(a,b) }
1397
1398        let a1 = lo_i16(data[0].simd, data[1].simd);
1399        let a2 = hi_i16(data[0].simd, data[1].simd);
1400        let a3 = lo_i16(data[2].simd, data[3].simd);
1401        let a4 = hi_i16(data[2].simd, data[3].simd);
1402        let a5 = lo_i16(data[4].simd, data[5].simd);
1403        let a6 = hi_i16(data[4].simd, data[5].simd);
1404        let a7 = lo_i16(data[6].simd, data[7].simd);
1405        let a8 = hi_i16(data[6].simd, data[7].simd);
1406
1407        let b1 = lo_i32(a1, a3);
1408        let b2 = hi_i32(a1, a3);
1409        let b3 = lo_i32(a2, a4);
1410        let b4 = hi_i32(a2, a4);
1411        let b5 = lo_i32(a5, a7);
1412        let b6 = hi_i32(a5, a7);
1413        let b7 = lo_i32(a6, a8);
1414        let b8 = hi_i32(a6, a8);
1415
1416        [
1417          i16x8 { simd: lo_i64(b1, b5) },
1418          i16x8 { simd: hi_i64(b1, b5) },
1419          i16x8 { simd: lo_i64(b2, b6) },
1420          i16x8 { simd: hi_i64(b2, b6) },
1421          i16x8 { simd: lo_i64(b3, b7) },
1422          i16x8 { simd: hi_i64(b3, b7) },
1423          i16x8 { simd: lo_i64(b4, b8) },
1424          i16x8 { simd: hi_i64(b4, b8) } ,
1425        ]
1426
1427      } else {
1428        #[inline(always)]
1429        fn transpose_column(data: &[i16x8; 8], index: usize) -> i16x8 {
1430          i16x8::new([
1431            data[0].as_array()[index],
1432            data[1].as_array()[index],
1433            data[2].as_array()[index],
1434            data[3].as_array()[index],
1435            data[4].as_array()[index],
1436            data[5].as_array()[index],
1437            data[6].as_array()[index],
1438            data[7].as_array()[index],
1439          ])
1440        }
1441
1442        [
1443          transpose_column(&data, 0),
1444          transpose_column(&data, 1),
1445          transpose_column(&data, 2),
1446          transpose_column(&data, 3),
1447          transpose_column(&data, 4),
1448          transpose_column(&data, 5),
1449          transpose_column(&data, 6),
1450          transpose_column(&data, 7),
1451        ]
1452      }
1453    }
1454  }
1455
1456  #[inline]
1457  #[must_use]
1458  /// Multiply and scale, equivalent to `((self * rhs) + 0x4000) >> 15` on each
1459  /// lane, effectively multiplying by a 16 bit fixed point number between `-1`
1460  /// and `1`. This corresponds to the following instructions:
1461  /// - `vqrdmulhq_n_s16` instruction on neon
1462  /// - `i16x8_q15mulr_sat` on simd128
1463  /// - `_mm_mulhrs_epi16` on ssse3
1464  /// - emulated via `mul_i16_*` on sse2
1465  pub fn mul_scale_round_n(self, rhs: i16) -> Self {
1466    pick! {
1467      if #[cfg(target_feature="ssse3")] {
1468        Self { sse:  mul_i16_scale_round_m128i(self.sse, set_splat_i16_m128i(rhs)) }
1469      } else if #[cfg(target_feature="sse2")] {
1470        // unfortunately mul_i16_scale_round_m128i only got added in sse3
1471        let r = set_splat_i16_m128i(rhs);
1472        let hi = mul_i16_keep_high_m128i(self.sse, r);
1473        let lo = mul_i16_keep_low_m128i(self.sse, r);
1474        let mut v1 = unpack_low_i16_m128i(lo, hi);
1475        let mut v2 = unpack_high_i16_m128i(lo, hi);
1476        let a = set_splat_i32_m128i(0x4000);
1477        v1 = shr_imm_i32_m128i::<15>(add_i32_m128i(v1, a));
1478        v2 = shr_imm_i32_m128i::<15>(add_i32_m128i(v2, a));
1479        let s = pack_i32_to_i16_m128i(v1, v2);
1480        Self { sse: s }
1481      } else if #[cfg(target_feature="simd128")] {
1482        Self { simd: i16x8_q15mulr_sat(self.simd, i16x8_splat(rhs)) }
1483      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1484        unsafe { Self { neon: vqrdmulhq_n_s16(self.neon, rhs) } }
1485      } else {
1486        // compiler does a surprisingly good job of vectorizing this
1487        Self { arr: [
1488          ((i32::from(self.arr[0]) * i32::from(rhs) + 0x4000) >> 15) as i16,
1489          ((i32::from(self.arr[1]) * i32::from(rhs) + 0x4000) >> 15) as i16,
1490          ((i32::from(self.arr[2]) * i32::from(rhs) + 0x4000) >> 15) as i16,
1491          ((i32::from(self.arr[3]) * i32::from(rhs) + 0x4000) >> 15) as i16,
1492          ((i32::from(self.arr[4]) * i32::from(rhs) + 0x4000) >> 15) as i16,
1493          ((i32::from(self.arr[5]) * i32::from(rhs) + 0x4000) >> 15) as i16,
1494          ((i32::from(self.arr[6]) * i32::from(rhs) + 0x4000) >> 15) as i16,
1495          ((i32::from(self.arr[7]) * i32::from(rhs) + 0x4000) >> 15) as i16,
1496        ]}
1497      }
1498    }
1499  }
1500
1501  #[inline]
1502  pub fn to_array(self) -> [i16; 8] {
1503    cast(self)
1504  }
1505
1506  #[inline]
1507  pub fn as_array(&self) -> &[i16; 8] {
1508    cast_ref(self)
1509  }
1510
1511  #[inline]
1512  pub fn as_mut_array(&mut self) -> &mut [i16; 8] {
1513    cast_mut(self)
1514  }
1515}