Skip to main content

wide/
f32x4_.rs

1use super::*;
2
3pick! {
4  if #[cfg(target_feature="sse")] {
5    #[derive(Default, Clone, Copy, PartialEq)]
6    #[repr(C, align(16))]
7    pub struct f32x4 { pub(crate) sse: m128 }
8  } else if #[cfg(target_feature="simd128")] {
9    use core::arch::wasm32::*;
10
11    #[derive(Clone, Copy)]
12    #[repr(transparent)]
13    pub struct f32x4 { pub(crate) simd: v128 }
14
15    impl Default for f32x4 {
16      fn default() -> Self {
17        Self::splat(0.0)
18      }
19    }
20
21    impl PartialEq for f32x4 {
22      fn eq(&self, other: &Self) -> bool {
23        u32x4_all_true(f32x4_eq(self.simd, other.simd))
24      }
25    }
26  } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
27    use core::arch::aarch64::*;
28    #[repr(C)]
29    #[derive(Copy, Clone)]
30    pub struct f32x4 { pub(crate) neon : float32x4_t }
31
32    impl Default for f32x4 {
33      #[inline]
34      fn default() -> Self {
35        unsafe { Self { neon: vdupq_n_f32(0.0)} }
36      }
37    }
38
39    impl PartialEq for f32x4 {
40      #[inline]
41      fn eq(&self, other: &Self) -> bool {
42        unsafe { vminvq_u32(vceqq_f32(self.neon, other.neon))==u32::MAX }
43      }
44
45    }
46    } else {
47    #[derive(Default, Clone, Copy, PartialEq)]
48    #[repr(C, align(16))]
49    pub struct f32x4 { pub(crate) arr: [f32;4] }
50  }
51}
52
53macro_rules! const_f32_as_f32x4 {
54  ($i:ident, $f:expr) => {
55    #[allow(non_upper_case_globals)]
56    pub const $i: f32x4 = f32x4::new([$f; 4]);
57  };
58}
59
60impl f32x4 {
61  const_f32_as_f32x4!(ONE, 1.0);
62  const_f32_as_f32x4!(ZERO, 0.0);
63  const_f32_as_f32x4!(HALF, 0.5);
64  const_f32_as_f32x4!(EPSILON, f32::EPSILON);
65  const_f32_as_f32x4!(MIN, f32::MIN);
66  const_f32_as_f32x4!(MIN_POSITIVE, f32::MIN_POSITIVE);
67  const_f32_as_f32x4!(MAX, f32::MAX);
68  const_f32_as_f32x4!(NAN, f32::NAN);
69  const_f32_as_f32x4!(INFINITY, f32::INFINITY);
70  const_f32_as_f32x4!(NEG_INFINITY, f32::NEG_INFINITY);
71  const_f32_as_f32x4!(E, core::f32::consts::E);
72  const_f32_as_f32x4!(FRAC_1_PI, core::f32::consts::FRAC_1_PI);
73  const_f32_as_f32x4!(FRAC_2_PI, core::f32::consts::FRAC_2_PI);
74  const_f32_as_f32x4!(FRAC_2_SQRT_PI, core::f32::consts::FRAC_2_SQRT_PI);
75  const_f32_as_f32x4!(FRAC_1_SQRT_2, core::f32::consts::FRAC_1_SQRT_2);
76  const_f32_as_f32x4!(FRAC_PI_2, core::f32::consts::FRAC_PI_2);
77  const_f32_as_f32x4!(FRAC_PI_3, core::f32::consts::FRAC_PI_3);
78  const_f32_as_f32x4!(FRAC_PI_4, core::f32::consts::FRAC_PI_4);
79  const_f32_as_f32x4!(FRAC_PI_6, core::f32::consts::FRAC_PI_6);
80  const_f32_as_f32x4!(FRAC_PI_8, core::f32::consts::FRAC_PI_8);
81  const_f32_as_f32x4!(LN_2, core::f32::consts::LN_2);
82  const_f32_as_f32x4!(LN_10, core::f32::consts::LN_10);
83  const_f32_as_f32x4!(LOG2_E, core::f32::consts::LOG2_E);
84  const_f32_as_f32x4!(LOG10_E, core::f32::consts::LOG10_E);
85  const_f32_as_f32x4!(LOG10_2, core::f32::consts::LOG10_2);
86  const_f32_as_f32x4!(LOG2_10, core::f32::consts::LOG2_10);
87  const_f32_as_f32x4!(PI, core::f32::consts::PI);
88  const_f32_as_f32x4!(SQRT_2, core::f32::consts::SQRT_2);
89  const_f32_as_f32x4!(TAU, core::f32::consts::TAU);
90}
91
92unsafe impl Zeroable for f32x4 {}
93unsafe impl Pod for f32x4 {}
94
95impl AlignTo for f32x4 {
96  type Elem = f32;
97}
98
99impl Add for f32x4 {
100  type Output = Self;
101  #[inline]
102  fn add(self, rhs: Self) -> Self::Output {
103    pick! {
104      if #[cfg(target_feature="sse")] {
105        Self { sse: add_m128(self.sse, rhs.sse) }
106      } else if #[cfg(target_feature="simd128")] {
107        Self { simd: f32x4_add(self.simd, rhs.simd) }
108      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
109        unsafe { Self { neon: vaddq_f32(self.neon, rhs.neon) } }
110      } else {
111        Self { arr: [
112          self.arr[0] + rhs.arr[0],
113          self.arr[1] + rhs.arr[1],
114          self.arr[2] + rhs.arr[2],
115          self.arr[3] + rhs.arr[3],
116        ]}
117      }
118    }
119  }
120}
121
122impl Sub for f32x4 {
123  type Output = Self;
124  #[inline]
125  fn sub(self, rhs: Self) -> Self::Output {
126    pick! {
127      if #[cfg(target_feature="sse")] {
128        Self { sse: sub_m128(self.sse, rhs.sse) }
129      } else if #[cfg(target_feature="simd128")] {
130        Self { simd: f32x4_sub(self.simd, rhs.simd) }
131      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
132        unsafe {Self { neon: vsubq_f32(self.neon, rhs.neon) }}
133      } else {
134        Self { arr: [
135          self.arr[0] - rhs.arr[0],
136          self.arr[1] - rhs.arr[1],
137          self.arr[2] - rhs.arr[2],
138          self.arr[3] - rhs.arr[3],
139        ]}
140      }
141    }
142  }
143}
144
145impl Mul for f32x4 {
146  type Output = Self;
147  #[inline]
148  fn mul(self, rhs: Self) -> Self::Output {
149    pick! {
150      if #[cfg(target_feature="sse")] {
151        Self { sse: mul_m128(self.sse, rhs.sse) }
152      } else if #[cfg(target_feature="simd128")] {
153        Self { simd: f32x4_mul(self.simd, rhs.simd) }
154      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
155        unsafe {Self { neon: vmulq_f32(self.neon, rhs.neon) }}
156      } else {
157        Self { arr: [
158          self.arr[0] * rhs.arr[0],
159          self.arr[1] * rhs.arr[1],
160          self.arr[2] * rhs.arr[2],
161          self.arr[3] * rhs.arr[3],
162        ]}
163      }
164    }
165  }
166}
167
168impl Div for f32x4 {
169  type Output = Self;
170  #[inline]
171  fn div(self, rhs: Self) -> Self::Output {
172    pick! {
173      if #[cfg(target_feature="sse")] {
174        Self { sse: div_m128(self.sse, rhs.sse) }
175      } else if #[cfg(target_feature="simd128")] {
176        Self { simd: f32x4_div(self.simd, rhs.simd) }
177      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
178        unsafe {Self { neon: vdivq_f32(self.neon, rhs.neon) }}
179      } else {
180        Self { arr: [
181          self.arr[0] / rhs.arr[0],
182          self.arr[1] / rhs.arr[1],
183          self.arr[2] / rhs.arr[2],
184          self.arr[3] / rhs.arr[3],
185        ]}
186      }
187    }
188  }
189}
190
191impl Rem for f32x4 {
192  type Output = Self;
193  #[inline]
194  fn rem(self, rhs: Self) -> Self::Output {
195    Self::new([
196      self.to_array()[0] % rhs.to_array()[0],
197      self.to_array()[1] % rhs.to_array()[1],
198      self.to_array()[2] % rhs.to_array()[2],
199      self.to_array()[3] % rhs.to_array()[3],
200    ])
201  }
202}
203
204impl Neg for f32x4 {
205  type Output = Self;
206  #[inline]
207  fn neg(self) -> Self::Output {
208    pick! {
209      if #[cfg(target_feature="sse")] {
210        Self { sse: bitxor_m128(self.sse, Self::splat(-0.0).sse) }
211      } else if #[cfg(target_feature="simd128")] {
212        Self { simd: f32x4_neg(self.simd) }
213      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
214        unsafe {Self { neon: vnegq_f32(self.neon) }}
215      } else {
216        Self { arr: [
217          -self.arr[0],
218          -self.arr[1],
219          -self.arr[2],
220          -self.arr[3],
221        ]}
222      }
223    }
224  }
225}
226
227impl Add<f32> for f32x4 {
228  type Output = Self;
229  #[inline]
230  fn add(self, rhs: f32) -> Self::Output {
231    self.add(Self::splat(rhs))
232  }
233}
234
235impl Sub<f32> for f32x4 {
236  type Output = Self;
237  #[inline]
238  fn sub(self, rhs: f32) -> Self::Output {
239    self.sub(Self::splat(rhs))
240  }
241}
242
243impl Mul<f32> for f32x4 {
244  type Output = Self;
245  #[inline]
246  fn mul(self, rhs: f32) -> Self::Output {
247    self.mul(Self::splat(rhs))
248  }
249}
250
251impl Div<f32> for f32x4 {
252  type Output = Self;
253  #[inline]
254  fn div(self, rhs: f32) -> Self::Output {
255    self.div(Self::splat(rhs))
256  }
257}
258
259impl Rem<f32> for f32x4 {
260  type Output = Self;
261  #[inline]
262  fn rem(self, rhs: f32) -> Self::Output {
263    self.rem(Self::splat(rhs))
264  }
265}
266
267impl Add<f32x4> for f32 {
268  type Output = f32x4;
269  #[inline]
270  fn add(self, rhs: f32x4) -> Self::Output {
271    f32x4::splat(self).add(rhs)
272  }
273}
274
275impl Sub<f32x4> for f32 {
276  type Output = f32x4;
277  #[inline]
278  fn sub(self, rhs: f32x4) -> Self::Output {
279    f32x4::splat(self).sub(rhs)
280  }
281}
282
283impl Mul<f32x4> for f32 {
284  type Output = f32x4;
285  #[inline]
286  fn mul(self, rhs: f32x4) -> Self::Output {
287    f32x4::splat(self).mul(rhs)
288  }
289}
290
291impl Div<f32x4> for f32 {
292  type Output = f32x4;
293  #[inline]
294  fn div(self, rhs: f32x4) -> Self::Output {
295    f32x4::splat(self).div(rhs)
296  }
297}
298
299impl Rem<f32x4> for f32 {
300  type Output = f32x4;
301  #[inline]
302  fn rem(self, rhs: f32x4) -> Self::Output {
303    f32x4::splat(self).rem(rhs)
304  }
305}
306
307impl BitAnd for f32x4 {
308  type Output = Self;
309  #[inline]
310  fn bitand(self, rhs: Self) -> Self::Output {
311    pick! {
312      if #[cfg(target_feature="sse")] {
313        Self { sse: bitand_m128(self.sse, rhs.sse) }
314      } else if #[cfg(target_feature="simd128")] {
315        Self { simd: v128_and(self.simd, rhs.simd) }
316      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
317        unsafe {Self { neon: vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(self.neon), vreinterpretq_u32_f32(rhs.neon))) }}
318      } else {
319        Self { arr: [
320          f32::from_bits(self.arr[0].to_bits() & rhs.arr[0].to_bits()),
321          f32::from_bits(self.arr[1].to_bits() & rhs.arr[1].to_bits()),
322          f32::from_bits(self.arr[2].to_bits() & rhs.arr[2].to_bits()),
323          f32::from_bits(self.arr[3].to_bits() & rhs.arr[3].to_bits()),
324        ]}
325      }
326    }
327  }
328}
329
330impl BitOr for f32x4 {
331  type Output = Self;
332  #[inline]
333  fn bitor(self, rhs: Self) -> Self::Output {
334    pick! {
335      if #[cfg(target_feature="sse")] {
336        Self { sse: bitor_m128(self.sse, rhs.sse) }
337      } else if #[cfg(target_feature="simd128")] {
338        Self { simd: v128_or(self.simd, rhs.simd) }
339      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
340        unsafe {Self { neon: vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(self.neon), vreinterpretq_u32_f32(rhs.neon))) }}
341      } else {
342        Self { arr: [
343          f32::from_bits(self.arr[0].to_bits() | rhs.arr[0].to_bits()),
344          f32::from_bits(self.arr[1].to_bits() | rhs.arr[1].to_bits()),
345          f32::from_bits(self.arr[2].to_bits() | rhs.arr[2].to_bits()),
346          f32::from_bits(self.arr[3].to_bits() | rhs.arr[3].to_bits()),
347        ]}
348      }
349    }
350  }
351}
352
353impl BitXor for f32x4 {
354  type Output = Self;
355  #[inline]
356  fn bitxor(self, rhs: Self) -> Self::Output {
357    pick! {
358      if #[cfg(target_feature="sse")] {
359        Self { sse: bitxor_m128(self.sse, rhs.sse) }
360      } else if #[cfg(target_feature="simd128")] {
361        Self { simd: v128_xor(self.simd, rhs.simd) }
362      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
363        unsafe {Self { neon: vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(self.neon), vreinterpretq_u32_f32(rhs.neon))) }}
364      } else {
365        Self { arr: [
366          f32::from_bits(self.arr[0].to_bits() ^ rhs.arr[0].to_bits()),
367          f32::from_bits(self.arr[1].to_bits() ^ rhs.arr[1].to_bits()),
368          f32::from_bits(self.arr[2].to_bits() ^ rhs.arr[2].to_bits()),
369          f32::from_bits(self.arr[3].to_bits() ^ rhs.arr[3].to_bits()),
370        ]}
371      }
372    }
373  }
374}
375
376#[expect(deprecated)]
377impl CmpEq for f32x4 {
378  type Output = Self;
379  #[inline]
380  fn simd_eq(self, rhs: Self) -> Self::Output {
381    pick! {
382      if #[cfg(target_feature="sse")] {
383        Self { sse: cmp_eq_mask_m128(self.sse, rhs.sse) }
384      } else if #[cfg(target_feature="simd128")] {
385        Self { simd: f32x4_eq(self.simd, rhs.simd) }
386      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
387        unsafe {Self { neon: vreinterpretq_f32_u32(vceqq_f32(self.neon, rhs.neon)) }}
388      } else {
389        Self { arr: [
390          if self.arr[0] == rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
391          if self.arr[1] == rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
392          if self.arr[2] == rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
393          if self.arr[3] == rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
394        ]}
395      }
396    }
397  }
398}
399
400#[expect(deprecated)]
401impl CmpGe for f32x4 {
402  type Output = Self;
403  #[inline]
404  fn simd_ge(self, rhs: Self) -> Self::Output {
405    pick! {
406      if #[cfg(target_feature="sse")] {
407        Self { sse: cmp_ge_mask_m128(self.sse, rhs.sse) }
408      } else if #[cfg(target_feature="simd128")] {
409        Self { simd: f32x4_ge(self.simd, rhs.simd) }
410      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
411        unsafe {Self { neon: vreinterpretq_f32_u32(vcgeq_f32(self.neon, rhs.neon)) }}
412      } else {
413        Self { arr: [
414          if self.arr[0] >= rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
415          if self.arr[1] >= rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
416          if self.arr[2] >= rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
417          if self.arr[3] >= rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
418        ]}
419      }
420    }
421  }
422}
423
424#[expect(deprecated)]
425impl CmpGt for f32x4 {
426  type Output = Self;
427  #[inline]
428  fn simd_gt(self, rhs: Self) -> Self::Output {
429    pick! {
430      if #[cfg(target_feature="sse")] {
431        Self { sse: cmp_gt_mask_m128(self.sse, rhs.sse) }
432      } else if #[cfg(target_feature="simd128")] {
433        Self { simd: f32x4_gt(self.simd, rhs.simd) }
434      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
435        unsafe {Self { neon: vreinterpretq_f32_u32(vcgtq_f32(self.neon, rhs.neon)) }}
436      } else {
437        Self { arr: [
438          if self.arr[0] > rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
439          if self.arr[1] > rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
440          if self.arr[2] > rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
441          if self.arr[3] > rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
442        ]}
443      }
444    }
445  }
446}
447
448#[expect(deprecated)]
449impl CmpNe for f32x4 {
450  type Output = Self;
451  #[inline]
452  fn simd_ne(self, rhs: Self) -> Self::Output {
453    pick! {
454      if #[cfg(target_feature="sse")] {
455        Self { sse: cmp_neq_mask_m128(self.sse, rhs.sse) }
456      } else if #[cfg(target_feature="simd128")] {
457        Self { simd: f32x4_ne(self.simd, rhs.simd) }
458      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
459        unsafe {Self { neon: vreinterpretq_f32_u32(vmvnq_u32(vceqq_f32(self.neon, rhs.neon))) }}
460      } else {
461        Self { arr: [
462          if self.arr[0] != rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
463          if self.arr[1] != rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
464          if self.arr[2] != rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
465          if self.arr[3] != rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
466        ]}
467      }
468    }
469  }
470}
471
472#[expect(deprecated)]
473impl CmpLe for f32x4 {
474  type Output = Self;
475  #[inline]
476  fn simd_le(self, rhs: Self) -> Self::Output {
477    pick! {
478      if #[cfg(target_feature="sse")] {
479        Self { sse: cmp_le_mask_m128(self.sse, rhs.sse) }
480      } else if #[cfg(target_feature="simd128")] {
481        Self { simd: f32x4_le(self.simd, rhs.simd) }
482      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
483        unsafe {Self { neon: vreinterpretq_f32_u32(vcleq_f32(self.neon, rhs.neon)) }}
484      } else {
485        Self { arr: [
486          if self.arr[0] <= rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
487          if self.arr[1] <= rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
488          if self.arr[2] <= rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
489          if self.arr[3] <= rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
490        ]}
491      }
492    }
493  }
494}
495
496#[expect(deprecated)]
497impl CmpLt for f32x4 {
498  type Output = Self;
499  #[inline]
500  fn simd_lt(self, rhs: Self) -> Self::Output {
501    pick! {
502      if #[cfg(target_feature="sse")] {
503        Self { sse: cmp_lt_mask_m128(self.sse, rhs.sse) }
504      } else if #[cfg(target_feature="simd128")] {
505        Self { simd: f32x4_lt(self.simd, rhs.simd) }
506      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
507        unsafe {Self { neon: vreinterpretq_f32_u32(vcltq_f32(self.neon, rhs.neon)) }}
508      } else {
509        Self { arr: [
510          if self.arr[0] < rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
511          if self.arr[1] < rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
512          if self.arr[2] < rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
513          if self.arr[3] < rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
514        ]}
515      }
516    }
517  }
518}
519
520impl f32x4 {
521  #[inline]
522  #[must_use]
523  pub const fn new(array: [f32; 4]) -> Self {
524    unsafe { core::mem::transmute(array) }
525  }
526
527  simd_comparison_fns!();
528
529  #[inline]
530  #[must_use]
531  pub fn blend(self, t: Self, f: Self) -> Self {
532    pick! {
533      if #[cfg(target_feature="sse4.1")] {
534        Self { sse: blend_varying_m128(f.sse, t.sse, self.sse) }
535      } else if #[cfg(target_feature="simd128")] {
536        Self { simd: v128_bitselect(t.simd, f.simd, self.simd) }
537      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
538        unsafe {Self { neon: vbslq_f32(vreinterpretq_u32_f32(self.neon), t.neon, f.neon) }}
539      } else {
540        generic_bit_blend(self, t, f)
541      }
542    }
543  }
544  #[inline]
545  #[must_use]
546  pub fn abs(self) -> Self {
547    pick! {
548      if #[cfg(target_feature="simd128")] {
549        Self { simd: f32x4_abs(self.simd) }
550      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
551        unsafe {Self { neon: vabsq_f32(self.neon) }}
552      } else {
553        let non_sign_bits = f32x4::from(f32::from_bits(i32::MAX as u32));
554        self & non_sign_bits
555      }
556    }
557  }
558
559  #[inline]
560  #[must_use]
561  pub fn signum(self) -> Self {
562    let result = Self::ONE | self & -Self::ZERO;
563
564    self.is_nan().blend(self, result)
565  }
566
567  #[inline]
568  #[must_use]
569  pub fn floor(self) -> Self {
570    pick! {
571      if #[cfg(target_feature="simd128")] {
572        Self { simd: f32x4_floor(self.simd) }
573      } else if #[cfg(target_feature="sse4.1")] {
574        Self { sse: floor_m128(self.sse) }
575      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
576        unsafe {Self { neon: vrndmq_f32(self.neon) }}
577      } else if #[cfg(feature="std")] {
578        let base: [f32; 4] = cast(self);
579        cast(base.map(|val| val.floor()))
580      } else {
581        let base: [f32; 4] = cast(self);
582        let rounded: [f32; 4] = cast(self.round());
583        cast([
584          if base[0] < rounded[0] { rounded[0] - 1.0 } else { rounded[0] },
585          if base[1] < rounded[1] { rounded[1] - 1.0 } else { rounded[1] },
586          if base[2] < rounded[2] { rounded[2] - 1.0 } else { rounded[2] },
587          if base[3] < rounded[3] { rounded[3] - 1.0 } else { rounded[3] },
588        ])
589      }
590    }
591  }
592  #[inline]
593  #[must_use]
594  pub fn ceil(self) -> Self {
595    pick! {
596      if #[cfg(target_feature="simd128")] {
597        Self { simd: f32x4_ceil(self.simd) }
598      } else if #[cfg(target_feature="sse4.1")] {
599        Self { sse: ceil_m128(self.sse) }
600      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
601        unsafe {Self { neon: vrndpq_f32(self.neon) }}
602      } else if #[cfg(feature="std")] {
603        let base: [f32; 4] = cast(self);
604        cast(base.map(|val| val.ceil()))
605      } else {
606        let base: [f32; 4] = cast(self);
607        let rounded: [f32; 4] = cast(self.round());
608        cast([
609          if base[0] > rounded[0] { rounded[0] + 1.0 } else { rounded[0] },
610          if base[1] > rounded[1] { rounded[1] + 1.0 } else { rounded[1] },
611          if base[2] > rounded[2] { rounded[2] + 1.0 } else { rounded[2] },
612          if base[3] > rounded[3] { rounded[3] + 1.0 } else { rounded[3] },
613        ])
614      }
615    }
616  }
617
618  /// Calculates the lanewise maximum of both vectors. This is a faster
619  /// implementation than `max`, but it doesn't specify any behavior if NaNs are
620  /// involved.
621  #[inline]
622  #[must_use]
623  pub fn fast_max(self, rhs: Self) -> Self {
624    pick! {
625      if #[cfg(target_feature="sse")] {
626        Self { sse: max_m128(self.sse, rhs.sse) }
627      } else if #[cfg(target_feature="simd128")] {
628        Self {
629          simd: f32x4_pmax(self.simd, rhs.simd),
630        }
631      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
632        unsafe {Self { neon: vmaxq_f32(self.neon, rhs.neon) }}
633      } else {
634        Self { arr: [
635          if self.arr[0] < rhs.arr[0] { rhs.arr[0] } else { self.arr[0] },
636          if self.arr[1] < rhs.arr[1] { rhs.arr[1] } else { self.arr[1] },
637          if self.arr[2] < rhs.arr[2] { rhs.arr[2] } else { self.arr[2] },
638          if self.arr[3] < rhs.arr[3] { rhs.arr[3] } else { self.arr[3] },
639        ]}
640      }
641    }
642  }
643
644  /// Calculates the lanewise maximum of both vectors. If either lane is NaN,
645  /// the other lane gets chosen. Use `fast_max` for a faster implementation
646  /// that doesn't handle NaNs.
647  #[inline]
648  #[must_use]
649  pub fn max(self, rhs: Self) -> Self {
650    pick! {
651      if #[cfg(target_feature="sse")] {
652        // max_m128 seems to do rhs < self ? self : rhs. So if there's any NaN
653        // involved, it chooses rhs, so we need to specifically check rhs for
654        // NaN.
655        rhs.is_nan().blend(self, Self { sse: max_m128(self.sse, rhs.sse) })
656      } else if #[cfg(target_feature="simd128")] {
657        // WASM has two max intrinsics:
658        // - max: This propagates NaN, that's the opposite of what we need.
659        // - pmax: This is defined as self < rhs ? rhs : self, which basically
660        //   chooses self if either is NaN.
661        //
662        // pmax is what we want, but we need to specifically check self for NaN.
663        Self {
664          simd: v128_bitselect(
665            rhs.simd,
666            f32x4_pmax(self.simd, rhs.simd),
667            f32x4_ne(self.simd, self.simd), // NaN check
668          )
669        }
670      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
671        unsafe {Self { neon: vmaxnmq_f32(self.neon, rhs.neon) }}
672      } else {
673        Self { arr: [
674          self.arr[0].max(rhs.arr[0]),
675          self.arr[1].max(rhs.arr[1]),
676          self.arr[2].max(rhs.arr[2]),
677          self.arr[3].max(rhs.arr[3]),
678        ]}
679      }
680    }
681  }
682
683  /// Calculates the lanewise minimum of both vectors. This is a faster
684  /// implementation than `min`, but it doesn't specify any behavior if NaNs are
685  /// involved.
686  #[inline]
687  #[must_use]
688  pub fn fast_min(self, rhs: Self) -> Self {
689    pick! {
690      if #[cfg(target_feature="sse")] {
691        Self { sse: min_m128(self.sse, rhs.sse) }
692      } else if #[cfg(target_feature="simd128")] {
693        Self {
694          simd: f32x4_pmin(self.simd, rhs.simd),
695        }
696      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
697        unsafe {Self { neon: vminq_f32(self.neon, rhs.neon) }}
698      } else {
699        Self { arr: [
700          if self.arr[0] < rhs.arr[0] { self.arr[0] } else { rhs.arr[0] },
701          if self.arr[1] < rhs.arr[1] { self.arr[1] } else { rhs.arr[1] },
702          if self.arr[2] < rhs.arr[2] { self.arr[2] } else { rhs.arr[2] },
703          if self.arr[3] < rhs.arr[3] { self.arr[3] } else { rhs.arr[3] },
704        ]}
705      }
706    }
707  }
708
709  /// Calculates the lanewise minimum of both vectors. If either lane is NaN,
710  /// the other lane gets chosen. Use `fast_min` for a faster implementation
711  /// that doesn't handle NaNs.
712  #[inline]
713  #[must_use]
714  pub fn min(self, rhs: Self) -> Self {
715    pick! {
716      if #[cfg(target_feature="sse")] {
717        // min_m128 seems to do self < rhs ? self : rhs. So if there's any NaN
718        // involved, it chooses rhs, so we need to specifically check rhs for
719        // NaN.
720        rhs.is_nan().blend(self, Self { sse: min_m128(self.sse, rhs.sse) })
721      } else if #[cfg(target_feature="simd128")] {
722        // WASM has two min intrinsics:
723        // - min: This propagates NaN, that's the opposite of what we need.
724        // - pmin: This is defined as rhs < self ? rhs : self, which basically
725        //   chooses self if either is NaN.
726        //
727        // pmin is what we want, but we need to specifically check self for NaN.
728        Self {
729          simd: v128_bitselect(
730            rhs.simd,
731            f32x4_pmin(self.simd, rhs.simd),
732            f32x4_ne(self.simd, self.simd), // NaN check
733          )
734        }
735      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
736        unsafe {Self { neon: vminnmq_f32(self.neon, rhs.neon) }}
737      } else {
738        Self { arr: [
739          self.arr[0].min(rhs.arr[0]),
740          self.arr[1].min(rhs.arr[1]),
741          self.arr[2].min(rhs.arr[2]),
742          self.arr[3].min(rhs.arr[3]),
743        ]}
744      }
745    }
746  }
747
748  /// Restrict a value to a certain interval unless it is NaN.
749  ///
750  /// If `self` is NaN, or `min` is NaN, or `max` is NaN, the result is NaN.
751  /// If `min > max`, the result is `min`, since `fast_max(min)` dominates.
752  #[inline]
753  #[must_use]
754  pub fn clamp(self, min: Self, max: Self) -> Self {
755    let is_nan = self.is_nan() | min.is_nan() | max.is_nan();
756    let clamped = self.fast_min(max).fast_max(min);
757    is_nan.blend(Self::splat(f32::NAN), clamped)
758  }
759
760  /// Restrict a value to a certain interval unless it is NaN.
761  ///
762  /// Avoids NaN detection; same speed as the old `clamp` prior to IEEE 754-2019
763  /// compliance. Does not specify any
764  /// behavior if NaNs are involved, and if `min > max` the result is
765  /// unspecified.
766  #[inline]
767  #[must_use]
768  pub fn fast_clamp(self, min: Self, max: Self) -> Self {
769    pick! {
770      if #[cfg(target_feature="sse")] {
771        // For both `min_m128` and `max_m128` if any input is NaN, `rhs` gets
772        // chosen. For `self` to be chosen, `self` must be the second argument.
773        Self { sse: min_m128(max.sse, max_m128(min.sse, self.sse)) }
774      } else if #[cfg(target_feature="simd128")] {
775        Self { simd: f32x4_min(f32x4_max(self.simd, min.simd), max.simd) }
776      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
777        unsafe { Self { neon: vminq_f32(vmaxq_f32(self.neon, min.neon), max.neon) } }
778      } else {
779        // The standard library does not have NaN propagating `min` and `max`
780        // functions.
781        let mut result = self;
782        result = result.simd_lt(min).blend(min, self);
783        result = result.simd_gt(max).blend(max, self);
784        result
785      }
786    }
787  }
788
789  #[inline]
790  #[must_use]
791  pub fn midpoint(self, other: Self) -> Self {
792    (self + other) * 0.5
793  }
794
795  #[inline]
796  #[must_use]
797  pub fn is_nan(self) -> Self {
798    pick! {
799      if #[cfg(target_feature="sse")] {
800        Self { sse: cmp_unord_mask_m128(self.sse, self.sse) }
801      } else if #[cfg(target_feature="simd128")] {
802        Self { simd: f32x4_ne(self.simd, self.simd) }
803      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
804        unsafe {Self { neon: vreinterpretq_f32_u32(vmvnq_u32(vceqq_f32(self.neon, self.neon))) }}
805      } else {
806        Self { arr: [
807          if self.arr[0].is_nan() { f32::from_bits(u32::MAX) } else { 0.0 },
808          if self.arr[1].is_nan() { f32::from_bits(u32::MAX) } else { 0.0 },
809          if self.arr[2].is_nan() { f32::from_bits(u32::MAX) } else { 0.0 },
810          if self.arr[3].is_nan() { f32::from_bits(u32::MAX) } else { 0.0 },
811        ]}
812      }
813    }
814  }
815  #[inline]
816  #[must_use]
817  pub fn is_finite(self) -> Self {
818    let shifted_exp_mask = u32x4::from(0xFF000000);
819    let u: u32x4 = cast(self);
820    let shift_u = u << 1_u64;
821    let out = !(shift_u & shifted_exp_mask).simd_eq(shifted_exp_mask);
822    cast(out)
823  }
824  #[inline]
825  #[must_use]
826  pub fn is_inf(self) -> Self {
827    let shifted_inf = u32x4::from(0xFF000000);
828    let u: u32x4 = cast(self);
829    let shift_u = u << 1_u64;
830    let out = (shift_u).simd_eq(shifted_inf);
831    cast(out)
832  }
833
834  #[inline]
835  #[must_use]
836  pub fn round(self) -> Self {
837    pick! {
838      if #[cfg(target_feature="sse4.1")] {
839        Self { sse: round_m128::<{round_op!(Nearest)}>(self.sse) }
840      } else if #[cfg(target_feature="sse2")] {
841        let mi: m128i = convert_to_i32_m128i_from_m128(self.sse);
842        let f: f32x4 = f32x4 { sse: convert_to_m128_from_i32_m128i(mi) };
843        let i: i32x4 = cast(mi);
844        let mask: f32x4 = cast(i.simd_eq(i32x4::from(0x80000000_u32 as i32)));
845        mask.blend(self, f)
846      } else if #[cfg(target_feature="simd128")] {
847        Self { simd: f32x4_nearest(self.simd) }
848      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
849        unsafe {Self { neon: vrndnq_f32(self.neon) }}
850      } else {
851        // Note(Lokathor): This software fallback is probably very slow compared
852        // to having a hardware option available, even just the sse2 version is
853        // better than this. Oh well.
854        let to_int = f32x4::from(1.0 / f32::EPSILON);
855        let u: u32x4 = cast(self);
856        let e: i32x4 = cast((u >> 23) & u32x4::from(0xff));
857        let mut y: f32x4;
858
859        let no_op_magic = i32x4::from(0x7f + 23);
860        let no_op_mask: f32x4 = cast(e.simd_gt(no_op_magic) | e.simd_eq(no_op_magic));
861        let no_op_val: f32x4 = self;
862
863        let zero_magic = i32x4::from(0x7f - 1);
864        let zero_mask: f32x4 = cast(e.simd_lt(zero_magic));
865        let zero_val: f32x4 = self * f32x4::from(0.0);
866
867        let neg_bit: f32x4 = cast(cast::<u32x4, i32x4>(u).simd_lt(i32x4::default()));
868        let x: f32x4 = neg_bit.blend(-self, self);
869        y = x + to_int - to_int - x;
870        y = y.simd_gt(f32x4::from(0.5)).blend(
871          y + x - f32x4::from(-1.0),
872          y.simd_lt(f32x4::from(-0.5)).blend(y + x + f32x4::from(1.0), y + x),
873        );
874        y = neg_bit.blend(-y, y);
875
876        no_op_mask.blend(no_op_val, zero_mask.blend(zero_val, y))
877      }
878    }
879  }
880
881  /// Rounds each lane into an integer. This is a faster implementation than
882  /// `round_int`, but it doesn't handle out of range values or NaNs. For those
883  /// values you get implementation defined behavior.
884  #[inline]
885  #[must_use]
886  pub fn fast_round_int(self) -> i32x4 {
887    pick! {
888      if #[cfg(target_feature="sse2")] {
889        cast(convert_to_i32_m128i_from_m128(self.sse))
890      } else {
891        self.round_int()
892      }
893    }
894  }
895
896  /// Rounds each lane into an integer. This saturates out of range values and
897  /// turns NaNs into 0. Use `fast_round_int` for a faster implementation that
898  /// doesn't handle out of range values or NaNs.
899  #[inline]
900  #[must_use]
901  pub fn round_int(self) -> i32x4 {
902    pick! {
903      if #[cfg(target_feature="sse2")] {
904        // Based on: https://github.com/v8/v8/blob/210987a552a2bf2a854b0baa9588a5959ff3979d/src/codegen/shared-ia32-x64/macro-assembler-shared-ia32-x64.h#L489-L504
905        let non_nan_mask = self.simd_eq(self);
906        let non_nan = self & non_nan_mask;
907        let flip_to_max: i32x4 = cast(self.simd_ge(Self::splat(2147483648.0)));
908        let cast: i32x4 = cast(convert_to_i32_m128i_from_m128(non_nan.sse));
909        flip_to_max ^ cast
910      } else if #[cfg(target_feature="simd128")] {
911        cast(Self { simd: i32x4_trunc_sat_f32x4(f32x4_nearest(self.simd)) })
912      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
913        cast(unsafe {Self { neon: vreinterpretq_f32_s32(vcvtnq_s32_f32(self.neon)) }})
914      } else {
915        let rounded: [f32; 4] = cast(self.round());
916        cast([
917          rounded[0] as i32,
918          rounded[1] as i32,
919          rounded[2] as i32,
920          rounded[3] as i32,
921        ])
922      }
923    }
924  }
925
926  #[inline]
927  #[must_use]
928  pub fn trunc(self) -> Self {
929    pick! {
930      if #[cfg(target_feature="sse4.1")] {
931        Self { sse: round_m128::<{round_op!(Zero)}>(self.sse) }
932      } else if #[cfg(target_feature="sse2")] {
933        // Ported from https://docs.rs/glam/latest/glam/f32/struct.Vec4.html#method.trunc
934        // Based on https://github.com/microsoft/DirectXMath `XMVectorTruncate`
935        let result: Self = cast(convert_to_m128_from_i32_m128i(truncate_m128_to_m128i(self.sse)));
936
937        // Out of range values are either already round, infinite or NaN.
938        let bounds_mask: Self = cast(cmp_lt_mask_i32_m128i(
939            cast(self.abs()),
940            set_splat_i32_m128i(8388608_f32.to_bits() as i32),
941        ));
942
943        // Reset the sign bit of the mask to preverse the sign of `self`.
944        bounds_mask.abs().blend(result, self)
945      } else if #[cfg(target_feature="simd128")] {
946        Self { simd: f32x4_trunc(self.simd) }
947      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
948        unsafe { Self { neon: vrndq_f32(self.neon) } }
949      } else {
950        let array: [f32; 4] = cast(self);
951        let result: Self = cast([
952          array[0] as i32 as f32,
953          array[1] as i32 as f32,
954          array[2] as i32 as f32,
955          array[3] as i32 as f32,
956        ]);
957
958        // Out of range values are either already round, infinite or NaN.
959        const BOUNDS_LIMIT: i32 = 8388608_f32.to_bits() as i32;
960        let bounds_mask: Self = cast(cast::<f32x4, i32x4>(self.abs()).simd_lt(i32x4::splat(BOUNDS_LIMIT)));
961
962        // Reset the sign bit of the mask to preverse the sign of `self`.
963        bounds_mask.abs().blend(result, self)
964      }
965    }
966  }
967
968  /// Truncates each lane into an integer. This is a faster implementation than
969  /// `trunc_int`, but it doesn't handle out of range values or NaNs. For those
970  /// values you get implementation defined behavior.
971  #[inline]
972  #[must_use]
973  pub fn fast_trunc_int(self) -> i32x4 {
974    pick! {
975      if #[cfg(target_feature="sse2")] {
976        cast(truncate_m128_to_m128i(self.sse))
977      } else {
978        self.trunc_int()
979      }
980    }
981  }
982
983  /// Truncates each lane into an integer. This saturates out of range values
984  /// and turns NaNs into 0. Use `fast_trunc_int` for a faster implementation
985  /// that doesn't handle out of range values or NaNs.
986  #[inline]
987  #[must_use]
988  pub fn trunc_int(self) -> i32x4 {
989    pick! {
990      if #[cfg(target_feature="sse2")] {
991        // Based on: https://github.com/v8/v8/blob/210987a552a2bf2a854b0baa9588a5959ff3979d/src/codegen/shared-ia32-x64/macro-assembler-shared-ia32-x64.h#L489-L504
992        let non_nan_mask = self.simd_eq(self);
993        let non_nan = self & non_nan_mask;
994        let flip_to_max: i32x4 = cast(self.simd_ge(Self::splat(2147483648.0)));
995        let cast: i32x4 = cast(truncate_m128_to_m128i(non_nan.sse));
996        flip_to_max ^ cast
997      } else if #[cfg(target_feature="simd128")] {
998        cast(Self { simd: i32x4_trunc_sat_f32x4(self.simd) })
999      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1000        cast(unsafe {Self { neon: vreinterpretq_f32_s32(vcvtq_s32_f32(self.neon)) }})
1001      } else {
1002        let n: [f32;4] = cast(self);
1003        cast([
1004          n[0] as i32,
1005          n[1] as i32,
1006          n[2] as i32,
1007          n[3] as i32,
1008        ])
1009      }
1010    }
1011  }
1012
1013  #[inline]
1014  #[must_use]
1015  pub fn fract(self) -> Self {
1016    self - self.trunc()
1017  }
1018
1019  /// Performs a multiply-add operation: `self * m + a`
1020  ///
1021  /// When hardware FMA support is available, this computes the result with a
1022  /// single rounding operation. Without FMA support, it falls back to separate
1023  /// multiply and add operations with two roundings.
1024  ///
1025  /// # Platform-specific behavior
1026  /// - On `x86`/`x86_64` with FMA: Uses `vfmadd` (single rounding, best
1027  ///   accuracy)
1028  /// - On ARM64 with NEON: Uses `vfmaq_f32` (single rounding, best accuracy)
1029  /// - Without FMA support: Uses `(self * m) + a` (two roundings)
1030  ///
1031  /// # Examples
1032  /// ```
1033  /// # use wide::f32x4;
1034  /// let a = f32x4::from([1.0, 2.0, 3.0, 4.0]);
1035  /// let b = f32x4::from([5.0, 6.0, 7.0, 8.0]);
1036  /// let c = f32x4::from([9.0, 10.0, 11.0, 12.0]);
1037  ///
1038  /// let result = a.mul_add(b, c);
1039  ///
1040  /// let expected = f32x4::from([14.0, 22.0, 32.0, 44.0]);
1041  /// assert_eq!(result, expected);
1042  /// ```
1043  #[inline]
1044  #[must_use]
1045  pub fn mul_add(self, m: Self, a: Self) -> Self {
1046    pick! {
1047      if #[cfg(all(target_feature="sse2",target_feature="fma"))] {
1048        Self { sse: fused_mul_add_m128(self.sse, m.sse, a.sse) }
1049      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
1050        unsafe { Self { neon: vfmaq_f32(a.neon, self.neon, m.neon) } }
1051      } else {
1052        (self * m) + a
1053      }
1054    }
1055  }
1056
1057  /// Performs a multiply-subtract operation: `self * m - s`
1058  ///
1059  /// When hardware FMA support is available, this computes the result with a
1060  /// single rounding operation. Without FMA support, it falls back to separate
1061  /// multiply and subtract operations with two roundings.
1062  ///
1063  /// # Platform-specific behavior
1064  /// - On `x86`/`x86_64` with FMA: Uses `vfmsub` (single rounding, best
1065  ///   accuracy)
1066  /// - On ARM64 with NEON: Uses `vfmaq_f32(-s, self, m)` (single rounding, best
1067  ///   accuracy)
1068  /// - Without FMA support: Uses `(self * m) - s` (two roundings)
1069  ///
1070  /// # Examples
1071  /// ```
1072  /// # use wide::f32x4;
1073  /// let a = f32x4::from([10.0, 20.0, 30.0, 40.0]);
1074  /// let b = f32x4::from([2.0, 3.0, 4.0, 5.0]);
1075  /// let c = f32x4::from([5.0, 10.0, 15.0, 20.0]);
1076  ///
1077  /// let result = a.mul_sub(b, c);
1078  ///
1079  /// let expected = f32x4::from([15.0, 50.0, 105.0, 180.0]);
1080  /// assert_eq!(result, expected);
1081  /// ```
1082  #[inline]
1083  #[must_use]
1084  pub fn mul_sub(self, m: Self, s: Self) -> Self {
1085    pick! {
1086      if #[cfg(all(target_feature="sse2",target_feature="fma"))] {
1087        Self { sse: fused_mul_sub_m128(self.sse, m.sse, s.sse) }
1088      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
1089        unsafe { Self { neon: vfmaq_f32(vnegq_f32(s.neon), self.neon, m.neon) } }
1090      } else {
1091        (self * m) - s
1092      }
1093    }
1094  }
1095
1096  /// Performs a negative multiply-add operation: `a - (self * m)`
1097  ///
1098  /// When hardware FMA support is available, this computes the result with a
1099  /// single rounding operation. Without FMA support, it falls back to separate
1100  /// operations with two roundings.
1101  ///
1102  /// # Platform-specific behavior
1103  /// - On `x86`/`x86_64` with FMA: Uses `vfnmadd` (single rounding, best
1104  ///   accuracy)
1105  /// - On ARM64 with NEON: Uses `vfmsq_f32` (single rounding, best accuracy)
1106  /// - Without FMA support: Uses `a - (self * m)` (two roundings)
1107  ///
1108  /// # Examples
1109  /// ```
1110  /// # use wide::f32x4;
1111  /// let a = f32x4::from([3.0, 4.0, 5.0, 6.0]);
1112  /// let b = f32x4::from([2.0, 2.0, 2.0, 2.0]);
1113  /// let c = f32x4::from([10.0, 20.0, 30.0, 40.0]);
1114  ///
1115  /// let result = a.mul_neg_add(b, c);
1116  ///
1117  /// let expected = f32x4::from([4.0, 12.0, 20.0, 28.0]);
1118  /// assert_eq!(result, expected);
1119  /// ```
1120  #[inline]
1121  #[must_use]
1122  pub fn mul_neg_add(self, m: Self, a: Self) -> Self {
1123    pick! {
1124      if #[cfg(all(target_feature="sse2",target_feature="fma"))] {
1125        Self { sse: fused_mul_neg_add_m128(self.sse, m.sse, a.sse) }
1126      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
1127        unsafe { Self { neon: vfmsq_f32(a.neon, self.neon, m.neon) } }
1128      } else {
1129        a - (self * m)
1130      }
1131    }
1132  }
1133
1134  /// Performs a negative multiply-subtract operation: `-(self * m) - s`
1135  ///
1136  /// When hardware FMA support is available, this computes the result with a
1137  /// single rounding operation. Without FMA support, it falls back to separate
1138  /// operations with two roundings.
1139  ///
1140  /// # Platform-specific behavior
1141  /// - On `x86`/`x86_64` with FMA: Uses `vfnmsub` (single rounding, best
1142  ///   accuracy)
1143  /// - On ARM64 with NEON: Uses `-(vfmaq_f32(s, self, m))` (single rounding,
1144  ///   best accuracy)
1145  /// - Without FMA support: Uses `-(self * m) - s` (two roundings)
1146  ///
1147  /// # Examples
1148  /// ```
1149  /// # use wide::f32x4;
1150  /// let a = f32x4::from([3.0, 4.0, 5.0, 6.0]);
1151  /// let b = f32x4::from([2.0, 2.0, 2.0, 2.0]);
1152  /// let c = f32x4::from([1.0, 2.0, 3.0, 4.0]);
1153  ///
1154  /// let result = a.mul_neg_sub(b, c);
1155  ///
1156  /// let expected = f32x4::from([-7.0, -10.0, -13.0, -16.0]);
1157  /// assert_eq!(result, expected);
1158  /// ```
1159  #[inline]
1160  #[must_use]
1161  pub fn mul_neg_sub(self, m: Self, s: Self) -> Self {
1162    pick! {
1163      if #[cfg(all(target_feature="sse2",target_feature="fma"))] {
1164        Self { sse: fused_mul_neg_sub_m128(self.sse, m.sse, s.sse) }
1165      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
1166        unsafe { Self { neon: vnegq_f32(vfmaq_f32(s.neon, self.neon, m.neon)) } }
1167      } else {
1168        -(self * m) - s
1169      }
1170    }
1171  }
1172
1173  #[inline]
1174  #[must_use]
1175  pub fn div_euclid(self, rhs: Self) -> Self {
1176    let q = (self / rhs).trunc();
1177    (self % rhs)
1178      .simd_lt(Self::ZERO)
1179      .blend(rhs.simd_gt(Self::ZERO).blend(q - Self::ONE, q + Self::ONE), q)
1180  }
1181
1182  #[inline]
1183  #[must_use]
1184  pub fn rem_euclid(self, rhs: Self) -> Self {
1185    let r = self % rhs;
1186    r.simd_lt(Self::ZERO).blend(r + rhs.abs(), r)
1187  }
1188
1189  #[inline]
1190  #[must_use]
1191  pub fn flip_signs(self, signs: Self) -> Self {
1192    self ^ (signs & Self::from(-0.0))
1193  }
1194
1195  #[inline]
1196  #[must_use]
1197  pub fn copysign(self, sign: Self) -> Self {
1198    let magnitude_mask = Self::from(f32::from_bits(u32::MAX >> 1));
1199    (self & magnitude_mask) | (sign & Self::from(-0.0))
1200  }
1201
1202  #[inline]
1203  pub fn asin_acos(self) -> (Self, Self) {
1204    // Based on the Agner Fog "vector class library":
1205    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1206    const_f32_as_f32x4!(P4asinf, 4.2163199048E-2);
1207    const_f32_as_f32x4!(P3asinf, 2.4181311049E-2);
1208    const_f32_as_f32x4!(P2asinf, 4.5470025998E-2);
1209    const_f32_as_f32x4!(P1asinf, 7.4953002686E-2);
1210    const_f32_as_f32x4!(P0asinf, 1.6666752422E-1);
1211
1212    let xa = self.abs();
1213    let big = xa.simd_ge(f32x4::splat(0.5));
1214
1215    let x1 = f32x4::splat(0.5) * (f32x4::ONE - xa);
1216    let x2 = xa * xa;
1217    let x3 = big.blend(x1, x2);
1218
1219    let xb = x1.sqrt();
1220
1221    let x4 = big.blend(xb, xa);
1222
1223    let z = polynomial_4!(x3, P0asinf, P1asinf, P2asinf, P3asinf, P4asinf);
1224    let z = z.mul_add(x3 * x4, x4);
1225
1226    let z1 = z + z;
1227
1228    // acos
1229    let z3 = self.simd_lt(f32x4::ZERO).blend(f32x4::PI - z1, z1);
1230    let z4 = f32x4::FRAC_PI_2 - z.flip_signs(self);
1231    let acos = big.blend(z3, z4);
1232
1233    // asin
1234    let z3 = f32x4::FRAC_PI_2 - z1;
1235    let asin = big.blend(z3, z);
1236    let asin = asin.flip_signs(self);
1237
1238    (asin, acos)
1239  }
1240
1241  #[inline]
1242  pub fn asin(self) -> Self {
1243    // Based on the Agner Fog "vector class library":
1244    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1245    const_f32_as_f32x4!(P4asinf, 4.2163199048E-2);
1246    const_f32_as_f32x4!(P3asinf, 2.4181311049E-2);
1247    const_f32_as_f32x4!(P2asinf, 4.5470025998E-2);
1248    const_f32_as_f32x4!(P1asinf, 7.4953002686E-2);
1249    const_f32_as_f32x4!(P0asinf, 1.6666752422E-1);
1250
1251    let xa = self.abs();
1252    let big = xa.simd_ge(f32x4::splat(0.5));
1253
1254    let x1 = f32x4::splat(0.5) * (f32x4::ONE - xa);
1255    let x2 = xa * xa;
1256    let x3 = big.blend(x1, x2);
1257
1258    let xb = x1.sqrt();
1259
1260    let x4 = big.blend(xb, xa);
1261
1262    let z = polynomial_4!(x3, P0asinf, P1asinf, P2asinf, P3asinf, P4asinf);
1263    let z = z.mul_add(x3 * x4, x4);
1264
1265    let z1 = z + z;
1266
1267    // asin
1268    let z3 = f32x4::FRAC_PI_2 - z1;
1269    let asin = big.blend(z3, z);
1270    let asin = asin.flip_signs(self);
1271
1272    asin
1273  }
1274
1275  #[inline]
1276  #[must_use]
1277  pub fn acos(self) -> Self {
1278    // Based on the Agner Fog "vector class library":
1279    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1280    const_f32_as_f32x4!(P4asinf, 4.2163199048E-2);
1281    const_f32_as_f32x4!(P3asinf, 2.4181311049E-2);
1282    const_f32_as_f32x4!(P2asinf, 4.5470025998E-2);
1283    const_f32_as_f32x4!(P1asinf, 7.4953002686E-2);
1284    const_f32_as_f32x4!(P0asinf, 1.6666752422E-1);
1285
1286    let xa = self.abs();
1287    let big = xa.simd_ge(f32x4::splat(0.5));
1288
1289    let x1 = f32x4::splat(0.5) * (f32x4::ONE - xa);
1290    let x2 = xa * xa;
1291    let x3 = big.blend(x1, x2);
1292
1293    let xb = x1.sqrt();
1294
1295    let x4 = big.blend(xb, xa);
1296
1297    let z = polynomial_4!(x3, P0asinf, P1asinf, P2asinf, P3asinf, P4asinf);
1298    let z = z.mul_add(x3 * x4, x4);
1299
1300    let z1 = z + z;
1301
1302    // acos
1303    let z3 = self.simd_lt(f32x4::ZERO).blend(f32x4::PI - z1, z1);
1304    let z4 = f32x4::FRAC_PI_2 - z.flip_signs(self);
1305    let acos = big.blend(z3, z4);
1306
1307    acos
1308  }
1309
1310  #[inline]
1311  pub fn atan(self) -> Self {
1312    // Based on the Agner Fog "vector class library":
1313    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1314    const_f32_as_f32x4!(P3atanf, 8.05374449538E-2);
1315    const_f32_as_f32x4!(P2atanf, -1.38776856032E-1);
1316    const_f32_as_f32x4!(P1atanf, 1.99777106478E-1);
1317    const_f32_as_f32x4!(P0atanf, -3.33329491539E-1);
1318
1319    let t = self.abs();
1320
1321    // small:  z = t / 1.0;
1322    // medium: z = (t-1.0) / (t+1.0);
1323    // big:    z = -1.0 / t;
1324    let notsmal = t.simd_ge(Self::SQRT_2 - Self::ONE);
1325    let notbig = t.simd_le(Self::SQRT_2 + Self::ONE);
1326
1327    let mut s = notbig.blend(Self::FRAC_PI_4, Self::FRAC_PI_2);
1328    s = notsmal & s;
1329
1330    let mut a = notbig & t;
1331    a = notsmal.blend(a - Self::ONE, a);
1332    let mut b = notbig & Self::ONE;
1333    b = notsmal.blend(b + t, b);
1334    let z = a / b;
1335
1336    let zz = z * z;
1337
1338    // Taylor expansion
1339    let mut re = polynomial_3!(zz, P0atanf, P1atanf, P2atanf, P3atanf);
1340    re = re.mul_add(zz * z, z) + s;
1341
1342    // get sign bit
1343    re = (self.is_sign_negative()).blend(-re, re);
1344
1345    re
1346  }
1347
1348  #[inline]
1349  pub fn atan2(self, x: Self) -> Self {
1350    // Based on the Agner Fog "vector class library":
1351    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1352    const_f32_as_f32x4!(P3atanf, 8.05374449538E-2);
1353    const_f32_as_f32x4!(P2atanf, -1.38776856032E-1);
1354    const_f32_as_f32x4!(P1atanf, 1.99777106478E-1);
1355    const_f32_as_f32x4!(P0atanf, -3.33329491539E-1);
1356
1357    let y = self;
1358
1359    // move in first octant
1360    let x1 = x.abs();
1361    let y1 = y.abs();
1362    let swapxy = y1.simd_gt(x1);
1363    // swap x and y if y1 > x1
1364    let mut x2 = swapxy.blend(y1, x1);
1365    let mut y2 = swapxy.blend(x1, y1);
1366
1367    // check for special case: x and y are both +/- INF
1368    let both_infinite = x.is_inf() & y.is_inf();
1369    if both_infinite.any() {
1370      let minus_one = -Self::ONE;
1371      x2 = both_infinite.blend(x2 & minus_one, x2);
1372      y2 = both_infinite.blend(y2 & minus_one, y2);
1373    }
1374
1375    // x = y = 0 will produce NAN. No problem, fixed below
1376    let t = y2 / x2;
1377
1378    // small:  z = t / 1.0;
1379    // medium: z = (t-1.0) / (t+1.0);
1380    let notsmal = t.simd_ge(Self::SQRT_2 - Self::ONE);
1381
1382    let a = notsmal.blend(t - Self::ONE, t);
1383    let b = notsmal.blend(t + Self::ONE, Self::ONE);
1384    let s = notsmal & Self::FRAC_PI_4;
1385    let z = a / b;
1386
1387    let zz = z * z;
1388
1389    // Taylor expansion
1390    let mut re = polynomial_3!(zz, P0atanf, P1atanf, P2atanf, P3atanf);
1391    re = re.mul_add(zz * z, z) + s;
1392
1393    // move back in place
1394    re = swapxy.blend(Self::FRAC_PI_2 - re, re);
1395    re = ((x | y).simd_eq(Self::ZERO)).blend(Self::ZERO, re);
1396    re = (x.is_sign_negative()).blend(Self::PI - re, re);
1397
1398    // get sign bit
1399    re = (y.is_sign_negative()).blend(-re, re);
1400
1401    re
1402  }
1403
1404  #[inline]
1405  #[must_use]
1406  pub fn sin_cos(self) -> (Self, Self) {
1407    // Based on the Agner Fog "vector class library":
1408    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1409
1410    const_f32_as_f32x4!(DP1F, 0.78515625_f32 * 2.0);
1411    const_f32_as_f32x4!(DP2F, 2.4187564849853515625E-4_f32 * 2.0);
1412    const_f32_as_f32x4!(DP3F, 3.77489497744594108E-8_f32 * 2.0);
1413
1414    const_f32_as_f32x4!(P0sinf, -1.6666654611E-1);
1415    const_f32_as_f32x4!(P1sinf, 8.3321608736E-3);
1416    const_f32_as_f32x4!(P2sinf, -1.9515295891E-4);
1417
1418    const_f32_as_f32x4!(P0cosf, 4.166664568298827E-2);
1419    const_f32_as_f32x4!(P1cosf, -1.388731625493765E-3);
1420    const_f32_as_f32x4!(P2cosf, 2.443315711809948E-5);
1421
1422    const_f32_as_f32x4!(TWO_OVER_PI, 2.0 / core::f32::consts::PI);
1423
1424    let xa = self.abs();
1425
1426    // Find quadrant
1427    let y = (xa * TWO_OVER_PI).round();
1428    let q: i32x4 = y.round_int();
1429
1430    let x = y.mul_neg_add(DP3F, y.mul_neg_add(DP2F, y.mul_neg_add(DP1F, xa)));
1431
1432    let x2 = x * x;
1433    let mut s = polynomial_2!(x2, P0sinf, P1sinf, P2sinf) * (x * x2) + x;
1434    let mut c = polynomial_2!(x2, P0cosf, P1cosf, P2cosf) * (x2 * x2)
1435      + f32x4::from(0.5).mul_neg_add(x2, f32x4::from(1.0));
1436
1437    let swap = !(q & i32x4::from(1)).simd_eq(i32x4::from(0));
1438
1439    let mut overflow: f32x4 = cast(q.simd_gt(i32x4::from(0x2000000)));
1440    overflow &= xa.is_finite();
1441    s = overflow.blend(f32x4::from(0.0), s);
1442    c = overflow.blend(f32x4::from(1.0), c);
1443
1444    // calc sin
1445    let mut sin1 = cast::<_, f32x4>(swap).blend(c, s);
1446    let sign_sin: i32x4 = (q << 30) ^ cast::<_, i32x4>(self);
1447    sin1 = sin1.flip_signs(cast(sign_sin));
1448
1449    // calc cos
1450    let mut cos1 = cast::<_, f32x4>(swap).blend(s, c);
1451    let sign_cos: i32x4 = ((q + i32x4::from(1)) & i32x4::from(2)) << 30;
1452    cos1 ^= cast::<_, f32x4>(sign_cos);
1453
1454    // IEEE 754: sin/cos(±∞) = NaN, sin/cos(NaN) = NaN
1455    let finite = self.is_finite();
1456    let nan = Self::splat(f32::NAN);
1457    let sin_final = finite.blend(sin1, nan);
1458    let cos_final = finite.blend(cos1, nan);
1459
1460    (sin_final, cos_final)
1461  }
1462
1463  #[inline]
1464  #[must_use]
1465  pub fn sin(self) -> Self {
1466    let (s, _) = self.sin_cos();
1467    s
1468  }
1469  #[inline]
1470  #[must_use]
1471  pub fn cos(self) -> Self {
1472    let (_, c) = self.sin_cos();
1473    c
1474  }
1475  #[inline]
1476  #[must_use]
1477  pub fn tan(self) -> Self {
1478    let (s, c) = self.sin_cos();
1479    s / c
1480  }
1481
1482  /// Calculates hyperbolic sine: `(e^self - e^(-self))/2`.
1483  #[inline]
1484  #[must_use]
1485  pub fn sinh(self) -> Self {
1486    const_f32_as_f32x4!(P0, 1.0);
1487    const_f32_as_f32x4!(P1, 1.0 / 6.0);
1488    const_f32_as_f32x4!(P2, 1.0 / 120.0);
1489    const_f32_as_f32x4!(P3, 1.0 / 5040.0);
1490    let a = self.abs();
1491    // |x| < 0.5: Taylor poly; last truncation term < 1 ULP at x=0.5 for both types
1492    let small = a.simd_lt(f32x4::from(0.5));
1493    let t = a * a;
1494    let poly = a * polynomial_3!(t, P0, P1, P2, P3);
1495    let exp_based = {
1496      let e = a.exp();
1497      (e - Self::ONE / e) * Self::HALF
1498    };
1499    let result = small.blend(poly, exp_based);
1500    result.flip_signs(self)
1501  }
1502
1503  /// Calculates hyperbolic cosine: `(e^self + e^(-self))/2`.
1504  #[inline]
1505  #[must_use]
1506  pub fn cosh(self) -> Self {
1507    const_f32_as_f32x4!(P0, 1.0);
1508    const_f32_as_f32x4!(P1, 1.0 / 2.0);
1509    const_f32_as_f32x4!(P2, 1.0 / 24.0);
1510    const_f32_as_f32x4!(P3, 1.0 / 720.0);
1511    let a = self.abs();
1512    // |x| < 0.5: Taylor poly; last truncation term < 1 ULP at x=0.5 for both types
1513    let small = a.simd_lt(f32x4::from(0.5));
1514    let t = a * a;
1515    let poly = polynomial_3!(t, P0, P1, P2, P3);
1516    let exp_based = {
1517      let e = a.exp();
1518      (e + Self::ONE / e) * Self::HALF
1519    };
1520    small.blend(poly, exp_based)
1521  }
1522
1523  /// Calculates hyperbolic tangent: `sinh(self)/cosh(self)`.
1524  #[inline]
1525  #[must_use]
1526  pub fn tanh(self) -> Self {
1527    // |x| < 2e-4: tanh(x) ≈ x, error x³/3 < 16·ULP(x)
1528    // bound: x² < 48·2⁻²³ → x < 2.39e-3; 2e-4 has 10× margin
1529    // |x| > 9.011: tanh(x) = ±1 to f32 precision (e⁻²ˣ < 2⁻²⁴)
1530    let a = self.abs();
1531    let large = a.simd_gt(f32x4::from(9.011));
1532    if large.all() {
1533      return Self::ONE.flip_signs(self);
1534    }
1535    let small = a.simd_lt(f32x4::from(2e-4));
1536    let exp_based = {
1537      let t = (Self::from(-2.0) * a).exp_m1();
1538      let pos = -t / (t + Self::from(2.0));
1539      pos.flip_signs(self)
1540    };
1541    let result = small.blend(self, exp_based);
1542    large.blend(Self::ONE.flip_signs(self), result)
1543  }
1544
1545  /// Calculates the cube root: `self^(1/3)`.
1546  #[inline]
1547  #[must_use]
1548  pub fn cbrt(self) -> Self {
1549    let a = self.abs();
1550    let zero = a.simd_eq(Self::ZERO);
1551    if zero.all() {
1552      return self; // preserves -0.0
1553    }
1554    let inf = a.is_inf();
1555    let nan = self.is_nan();
1556
1557    let tiny = a.simd_lt(Self::from(f32::MIN_POSITIVE));
1558    let a_work = tiny.blend(a * Self::from(16777216.0), a);
1559
1560    let e = Self::exponent(a_work) + Self::ONE;
1561    let d = Self::fraction_2(a_work);
1562
1563    // C0..C5 from SLEEF's minimax polynomial for 1/cbrt(d) on [0.5, 1.0)
1564    // Naoki Shibata et al., "SLEEF: A Portable Vectorized Library of C99
1565    // Mathematical Functions", https://sleef.org / https://github.com/shibatch/sleef
1566    // Licensed under the Boost Software License 1.0.
1567    // These are the f32-precision coefficients; our f64 variants use the f64
1568    // set.
1569    const_f32_as_f32x4!(C0, 2.2241257);
1570    const_f32_as_f32x4!(C1, -3.8095417);
1571    const_f32_as_f32x4!(C2, 5.8982625);
1572    const_f32_as_f32x4!(C3, -5.532182);
1573    const_f32_as_f32x4!(C4, 2.8208892);
1574    const_f32_as_f32x4!(C5, -0.60156447);
1575    let mut x = polynomial_5!(d, C0, C1, C2, C3, C4, C5);
1576
1577    let x2 = x * x;
1578    let x4 = x2 * x2;
1579    x = x - d.mul_add(x4, -x) * Self::from(1.0 / 3.0);
1580    // cbrt(d) = d * x² with refinement
1581    let mut y = (d * x) * x;
1582    let yx = y * x;
1583    let t = Self::from(2.0 / 3.0);
1584    y = y - t * y * (yx - Self::ONE);
1585
1586    // Scale by 2^(e/3)
1587    let three = Self::from(3.0);
1588    let two = Self::from(2.0);
1589    let neg = e.simd_lt(Self::ZERO);
1590    let e_adj = neg.blend(e - two, e);
1591    let k = (e_adj / three).trunc();
1592    let r = e - three * k;
1593    const_f32_as_f32x4!(CBRT2, 1.259921);
1594    const_f32_as_f32x4!(CBRT4, 1.587401);
1595    y = r.simd_eq(Self::ONE).blend(y * CBRT2, y);
1596    y = r.simd_eq(two).blend(y * CBRT4, y);
1597    y *= Self::vm_pow2n(k);
1598    y = tiny.blend(y / Self::from(256.0_f32), y);
1599
1600    let result = y.flip_signs(self);
1601    let result = nan.blend(self, result);
1602    let result = zero.blend(self, result);
1603    let result = inf.blend(self, result);
1604    result
1605  }
1606
1607  #[inline]
1608  #[must_use]
1609  pub fn to_degrees(self) -> Self {
1610    const_f32_as_f32x4!(RAD_TO_DEG_RATIO, 180.0_f32 / core::f32::consts::PI);
1611    self * RAD_TO_DEG_RATIO
1612  }
1613  #[inline]
1614  #[must_use]
1615  pub fn to_radians(self) -> Self {
1616    const_f32_as_f32x4!(DEG_TO_RAD_RATIO, core::f32::consts::PI / 180.0_f32);
1617    self * DEG_TO_RAD_RATIO
1618  }
1619  #[inline]
1620  #[must_use]
1621  pub fn recip(self) -> Self {
1622    pick! {
1623      if #[cfg(target_feature="sse")] {
1624        Self { sse: reciprocal_m128(self.sse) }
1625      } else if #[cfg(target_feature="simd128")] {
1626        Self { simd: f32x4_div(f32x4_splat(1.0), self.simd) }
1627      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1628        unsafe {Self { neon: vdivq_f32(vdupq_n_f32(1.0), self.neon) }}
1629      } else {
1630        Self { arr: [
1631          1.0 / self.arr[0],
1632          1.0 / self.arr[1],
1633          1.0 / self.arr[2],
1634          1.0 / self.arr[3],
1635        ]}
1636      }
1637    }
1638  }
1639  #[inline]
1640  #[must_use]
1641  pub fn recip_sqrt(self) -> Self {
1642    pick! {
1643      if #[cfg(target_feature="sse")] {
1644        Self { sse: reciprocal_sqrt_m128(self.sse) }
1645      } else if #[cfg(target_feature="simd128")] {
1646        Self { simd: f32x4_div(f32x4_splat(1.0), f32x4_sqrt(self.simd)) }
1647      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1648        unsafe {Self { neon: vdivq_f32(vdupq_n_f32(1.0), vsqrtq_f32(self.neon)) }}
1649      } else if #[cfg(feature="std")] {
1650        Self { arr: [
1651          1.0 / self.arr[0].sqrt(),
1652          1.0 / self.arr[1].sqrt(),
1653          1.0 / self.arr[2].sqrt(),
1654          1.0 / self.arr[3].sqrt(),
1655        ]}
1656      } else {
1657        Self { arr: [
1658          1.0 / software_sqrt(self.arr[0] as f64) as f32,
1659          1.0 / software_sqrt(self.arr[1] as f64) as f32,
1660          1.0 / software_sqrt(self.arr[2] as f64) as f32,
1661          1.0 / software_sqrt(self.arr[3] as f64) as f32,
1662        ]}
1663      }
1664    }
1665  }
1666  #[inline]
1667  #[must_use]
1668  pub fn sqrt(self) -> Self {
1669    pick! {
1670      if #[cfg(target_feature="sse")] {
1671        Self { sse: sqrt_m128(self.sse) }
1672      } else if #[cfg(target_feature="simd128")] {
1673        Self { simd: f32x4_sqrt(self.simd) }
1674      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1675        unsafe {Self { neon: vsqrtq_f32(self.neon) }}
1676      } else if #[cfg(feature="std")] {
1677        Self { arr: [
1678          self.arr[0].sqrt(),
1679          self.arr[1].sqrt(),
1680          self.arr[2].sqrt(),
1681          self.arr[3].sqrt(),
1682        ]}
1683      } else {
1684        Self { arr: [
1685          software_sqrt(self.arr[0] as f64) as f32,
1686          software_sqrt(self.arr[1] as f64) as f32,
1687          software_sqrt(self.arr[2] as f64) as f32,
1688          software_sqrt(self.arr[3] as f64) as f32,
1689        ]}
1690      }
1691    }
1692  }
1693
1694  #[inline]
1695  #[must_use]
1696  #[doc(alias("movemask", "move_mask"))]
1697  pub fn to_bitmask(self) -> u32 {
1698    pick! {
1699      if #[cfg(target_feature="sse")] {
1700        move_mask_m128(self.sse) as u32
1701      } else if #[cfg(target_feature="simd128")] {
1702        u32x4_bitmask(self.simd) as u32
1703      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1704        unsafe
1705        {
1706          // set all to 1 if top bit is set, else 0
1707          let masked = vcltq_s32( vreinterpretq_s32_f32(self.neon), vdupq_n_s32(0));
1708
1709          // select the right bit out of each lane
1710          let selectbit : uint32x4_t = core::mem::transmute([1u32, 2, 4, 8]);
1711          let r = vandq_u32(masked, selectbit);
1712
1713          // horizontally add the 16-bit lanes
1714          vaddvq_u32(r) as u32
1715        }
1716      } else {
1717        (((self.arr[0].to_bits() as i32) < 0) as u32) << 0 |
1718        (((self.arr[1].to_bits() as i32) < 0) as u32) << 1 |
1719        (((self.arr[2].to_bits() as i32) < 0) as u32) << 2 |
1720        (((self.arr[3].to_bits() as i32) < 0) as u32) << 3
1721      }
1722    }
1723  }
1724  #[inline]
1725  #[must_use]
1726  pub fn any(self) -> bool {
1727    pick! {
1728      if #[cfg(target_feature="simd128")] {
1729        v128_any_true(self.simd)
1730      } else {
1731        self.to_bitmask() != 0
1732      }
1733    }
1734  }
1735  #[inline]
1736  #[must_use]
1737  pub fn all(self) -> bool {
1738    pick! {
1739      if #[cfg(target_feature="simd128")] {
1740        u32x4_all_true(self.simd)
1741      } else {
1742        // four lanes
1743        self.to_bitmask() == 0b1111
1744      }
1745    }
1746  }
1747  #[inline]
1748  #[must_use]
1749  pub fn none(self) -> bool {
1750    !self.any()
1751  }
1752
1753  #[inline]
1754  fn vm_pow2n(self) -> Self {
1755    const_f32_as_f32x4!(pow2_23, 8388608.0);
1756    const_f32_as_f32x4!(bias, 127.0);
1757    let a = self + (bias + pow2_23);
1758    let c = cast::<_, i32x4>(a) << 23;
1759    let std_result = cast::<_, f32x4>(c);
1760
1761    let min_exp = f32x4::from(-126.0);
1762    let is_sub = self.simd_lt(min_exp);
1763    if is_sub.any() {
1764      let valid = self.simd_ge(f32x4::from(-149.0));
1765      let shift_f = self + f32x4::from(149.0);
1766      let mut shift_i = shift_f.trunc_int();
1767      shift_i = cast::<_, i32x4>(valid).blend(shift_i, i32x4::ZERO);
1768      let mantissa = i32x4::ONE << shift_i;
1769      let sub_result = cast::<_, f32x4>(mantissa);
1770      let sub_result = valid.blend(sub_result, f32x4::ZERO);
1771      is_sub.blend(sub_result, std_result)
1772    } else {
1773      std_result
1774    }
1775  }
1776
1777  /// Calculate the exponent of a packed `f32x4`
1778  #[inline]
1779  #[must_use]
1780  pub fn exp(self) -> Self {
1781    const_f32_as_f32x4!(P0, 1.0 / 2.0);
1782    const_f32_as_f32x4!(P1, 1.0 / 6.0);
1783    const_f32_as_f32x4!(P2, 1.0 / 24.0);
1784    const_f32_as_f32x4!(P3, 1.0 / 120.0);
1785    const_f32_as_f32x4!(P4, 1.0 / 720.0);
1786    const_f32_as_f32x4!(P5, 1.0 / 5040.0);
1787    // LN2D_HI/LO: double-double decomposition of ln(2) for exp range reduction,
1788    // following the approach from fdlibm's e_exp.c (Sun Microsystems,
1789    // https://www.netlib.org/fdlibm/). The f32 split uses f32-precision constants
1790    // (0.693359375, -2.12194440e-4) summing to ln(2) with single-precision
1791    // accuracy; the f64 variants use a full f64 double-double
1792    // decomposition.
1793    const_f32_as_f32x4!(LN2D_HI, 0.693359375);
1794    const_f32_as_f32x4!(LN2D_LO, -2.12194440e-4);
1795    // max_x = ln(f32::MAX) ≈ 88.7229, max_r = 127 (IEEE max normal exponent)
1796    // min_x = -149.5 ln(2) ≈ -103.63: min r for vm_pow2n subnormal
1797    let max_x = f32x4::from(88.723);
1798    let min_x = f32x4::from(-103.63);
1799    // x < min_x: e^x underflows to 0 -- skip the entire pipeline
1800    let finite = self.is_finite();
1801    let neg_underflow = self.simd_lt(min_x) & finite;
1802    if neg_underflow.all() {
1803      return Self::ZERO;
1804    }
1805    let max_r = f32x4::from(127.0);
1806    let r = (self * Self::LOG2_E).round();
1807    let big = r.simd_gt(max_r);
1808    let r_safe = big.blend(max_r, r);
1809    let excess = r - max_r;
1810    let excess = big.blend(excess, Self::ZERO);
1811    let scale = Self::vm_pow2n(excess);
1812    let x = r.mul_neg_add(LN2D_HI, self);
1813    let x = r.mul_neg_add(LN2D_LO, x);
1814    let z = polynomial_5!(x, P0, P1, P2, P3, P4, P5);
1815    let x2 = x * x;
1816    let z = z.mul_add(x2, x);
1817    let n2 = Self::vm_pow2n(r_safe);
1818    let z = (z + Self::ONE) * scale * n2;
1819    let nan_mask = self.is_nan();
1820    let mut result = nan_mask.blend(Self::nan_pow(), z);
1821    let pos_overflow = self.simd_gt(max_x) & finite;
1822    result = pos_overflow.blend(Self::infinity(), result);
1823    result = neg_underflow.blend(Self::ZERO, result);
1824    let pos_inf = !finite & !self.is_sign_negative() & !nan_mask;
1825    result = pos_inf.blend(Self::infinity(), result);
1826    let neg_inf = !finite & self.is_sign_negative() & !nan_mask;
1827    result = neg_inf.blend(Self::ZERO, result);
1828    result
1829  }
1830
1831  /// Calculate `e^self - 1` for each lane.
1832  /// Accurate even for very small values.
1833  #[inline]
1834  #[must_use]
1835  pub fn exp_m1(self) -> Self {
1836    // x < -17.329: e^x < 2⁻²⁵, exp_m1(x) = -1.0 exactly (mantissa exhaustion)
1837    // IEEE simd_lt returns false for NaN, so NaN lanes can't reach here.
1838    // -inf is < -17.329, and exp_m1(-inf) = -1.0, also correct.
1839    if self.simd_lt(f32x4::from(-17.329)).all() {
1840      return f32x4::from(-1.0);
1841    }
1842    const_f32_as_f32x4!(P0, 1.0 / 2.0);
1843    const_f32_as_f32x4!(P1, 1.0 / 6.0);
1844    const_f32_as_f32x4!(P2, 1.0 / 24.0);
1845    const_f32_as_f32x4!(P3, 1.0 / 120.0);
1846    const_f32_as_f32x4!(P4, 1.0 / 720.0);
1847    const_f32_as_f32x4!(P5, 1.0 / 5040.0);
1848    // LN2D_HI/LO: double-double decomposition of ln(2) for exp range reduction,
1849    // following the approach from fdlibm's e_exp.c (Sun Microsystems,
1850    // https://www.netlib.org/fdlibm/). The f32 split uses f32-precision constants
1851    // (0.693359375, -2.12194440e-4) summing to ln(2) with single-precision
1852    // accuracy; the f64 variants use a full f64 double-double
1853    // decomposition.
1854    const_f32_as_f32x4!(LN2D_HI, 0.693359375);
1855    const_f32_as_f32x4!(LN2D_LO, -2.12194440e-4);
1856    // max_x = ln(f32::MAX) ≈ 88.7229, max_r = 127 (IEEE max normal exponent)
1857    // min_x = -149.5 ln(2) ≈ -103.63: min r for vm_pow2n subnormal
1858    let max_x = f32x4::from(88.723);
1859    let min_x = f32x4::from(-103.63);
1860    let max_r = f32x4::from(127.0);
1861    let r = (self * Self::LOG2_E).round();
1862    let big = r.simd_gt(max_r);
1863    let r_safe = big.blend(max_r, r);
1864    let excess = r - max_r;
1865    let excess = big.blend(excess, Self::ZERO);
1866    let scale = Self::vm_pow2n(excess);
1867    let x = r.mul_neg_add(LN2D_HI, self);
1868    let x = r.mul_neg_add(LN2D_LO, x);
1869    let z = polynomial_5!(x, P0, P1, P2, P3, P4, P5);
1870    let x2 = x * x;
1871    let z = z.mul_add(x2, x);
1872    let n2 = Self::vm_pow2n(r_safe);
1873    let exp_val = (z + Self::ONE) * scale * n2;
1874    let r_is_zero = r.simd_eq(Self::ZERO);
1875    let z = r_is_zero.blend(z, exp_val - Self::ONE);
1876    let nan_mask = self.is_nan();
1877    let finite = self.is_finite();
1878    let mut result = nan_mask.blend(Self::nan_pow(), z);
1879    let pos_overflow = self.simd_gt(max_x) & finite;
1880    result = pos_overflow.blend(Self::infinity(), result);
1881    let neg_underflow = self.simd_lt(min_x) & finite;
1882    result = neg_underflow.blend(-Self::ONE, result);
1883    let pos_inf = !finite & !self.is_sign_negative() & !nan_mask;
1884    result = pos_inf.blend(Self::infinity(), result);
1885    let neg_inf = !finite & self.is_sign_negative() & !nan_mask;
1886    result = neg_inf.blend(-Self::ONE, result);
1887    let is_zero = self.simd_eq(Self::ZERO);
1888    result = is_zero.blend(self, result);
1889    result
1890  }
1891
1892  /// Returns `2^self`.
1893  #[inline]
1894  #[must_use]
1895  pub fn exp2(self) -> Self {
1896    const_f32_as_f32x4!(P2, 1.0 / 2.0);
1897    const_f32_as_f32x4!(P3, 1.0 / 6.0);
1898    const_f32_as_f32x4!(P4, 1.0 / 24.0);
1899    const_f32_as_f32x4!(P5, 1.0 / 120.0);
1900    const_f32_as_f32x4!(P6, 1.0 / 720.0);
1901    const_f32_as_f32x4!(P7, 1.0 / 5040.0);
1902
1903    // max_x = log2(f32::MAX) ≈ 127.99999
1904    // min_x = log2(f32::MIN_POSITIVE) - 23 ≈ -126 - 23 = -149
1905    let max_x = f32x4::from(127.99999);
1906    let min_x = f32x4::from(-149.5);
1907    let finite = self.is_finite();
1908    let neg_underflow = self.simd_lt(min_x) & finite;
1909    if neg_underflow.all() {
1910      return Self::ZERO;
1911    }
1912
1913    let round = self.round();
1914    let max_r = f32x4::from(127.0);
1915    let big = round.simd_gt(max_r);
1916    let r_safe = big.blend(max_r, round);
1917    let excess = round - max_r;
1918    let excess = big.blend(excess, Self::ZERO);
1919    let scale = Self::vm_pow2n(excess);
1920
1921    let fract = (self - round) * Self::LN_2;
1922    let fract_partial_exp2 = polynomial_5!(fract, P2, P3, P4, P5, P6, P7);
1923    let fract2 = fract * fract;
1924    let fract_exp2 = fract_partial_exp2.mul_add(fract2, fract) + Self::ONE;
1925
1926    let n2 = Self::vm_pow2n(r_safe);
1927    let result = fract_exp2 * scale * n2;
1928
1929    let nan_mask = self.is_nan();
1930    let mut result = nan_mask.blend(Self::nan_pow(), result);
1931    let pos_overflow = self.simd_gt(max_x) & finite;
1932    result = pos_overflow.blend(Self::infinity(), result);
1933    result = neg_underflow.blend(Self::ZERO, result);
1934    let pos_inf = !finite & !self.is_sign_negative() & !nan_mask;
1935    result = pos_inf.blend(Self::infinity(), result);
1936    let neg_inf = !finite & self.is_sign_negative() & !nan_mask;
1937    result = neg_inf.blend(Self::ZERO, result);
1938    result
1939  }
1940
1941  #[inline]
1942  fn exponent(self) -> f32x4 {
1943    const_f32_as_f32x4!(pow2_23, 8388608.0);
1944    const_f32_as_f32x4!(bias, 127.0);
1945    let a = cast::<_, u32x4>(self);
1946    let b = a >> 23;
1947    let c = b | cast::<_, u32x4>(pow2_23);
1948    let d = cast::<_, f32x4>(c);
1949    let e = d - (pow2_23 + bias);
1950    e
1951  }
1952
1953  #[inline]
1954  fn fraction_2(self) -> Self {
1955    let t1 = cast::<_, u32x4>(self);
1956    let t2 = cast::<_, u32x4>(
1957      (t1 & u32x4::from(0x007FFFFF)) | u32x4::from(0x3F000000),
1958    );
1959    cast::<_, f32x4>(t2)
1960  }
1961  #[inline]
1962  fn is_zero_or_subnormal(self) -> Self {
1963    let t = cast::<_, i32x4>(self);
1964    let t = t & i32x4::splat(0x7F800000);
1965    let mask = t.simd_eq(i32x4::splat(0));
1966    cast::<_, f32x4>(mask)
1967  }
1968  #[inline]
1969  fn infinity() -> Self {
1970    cast::<_, f32x4>(i32x4::splat(0x7F800000))
1971  }
1972  #[inline]
1973  fn nan_log() -> Self {
1974    cast::<_, f32x4>(i32x4::splat(0x7FC00000 | 0x101 & 0x003FFFFF))
1975  }
1976  #[inline]
1977  fn nan_pow() -> Self {
1978    cast::<_, f32x4>(i32x4::splat(0x7FC00000 | 0x101 & 0x003FFFFF))
1979  }
1980
1981  /// Returns true for each element if it has a positive sign, including `+0.0`,
1982  /// `NaN`s with positive sign bit and positive infinity.
1983  #[inline]
1984  #[must_use]
1985  pub fn is_sign_positive(self) -> Self {
1986    const SIGN_MASK: u32x4 = u32x4::splat((-0.0_f32).to_bits());
1987
1988    let bits = cast::<f32x4, u32x4>(self);
1989    let sign = bits & SIGN_MASK;
1990    let result = sign.simd_eq(u32x4::ZERO);
1991    cast::<u32x4, f32x4>(result)
1992  }
1993
1994  /// Returns true for each element if it has a negative sign, including `-0.0`,
1995  /// `NaN`s with negative sign bit and negative infinity.
1996  #[inline]
1997  #[must_use]
1998  pub fn is_sign_negative(self) -> Self {
1999    const SIGN_MASK: u32x4 = u32x4::splat((-0.0_f32).to_bits());
2000
2001    let bits = cast::<f32x4, u32x4>(self);
2002    let sign = bits & SIGN_MASK;
2003    let result = sign.simd_eq(SIGN_MASK);
2004    cast::<u32x4, f32x4>(result)
2005  }
2006
2007  /// horizontal add of all the elements of the vector
2008  #[inline]
2009  #[must_use]
2010  pub fn reduce_add(self) -> f32 {
2011    let arr: [f32; 4] = cast(self);
2012    arr.iter().sum()
2013  }
2014
2015  /// horizontal multiplication of all the elements of the vector
2016  #[inline]
2017  #[must_use]
2018  pub fn reduce_mul(self) -> f32 {
2019    let arr: [f32; 4] = cast(self);
2020    arr.iter().product()
2021  }
2022
2023  /// Natural log (ln(x))
2024  #[inline]
2025  #[must_use]
2026  pub fn ln(self) -> Self {
2027    const_f32_as_f32x4!(HALF, 0.5);
2028    const_f32_as_f32x4!(P0, 3.3333331174E-1);
2029    const_f32_as_f32x4!(P1, -2.4999993993E-1);
2030    const_f32_as_f32x4!(P2, 2.0000714765E-1);
2031    const_f32_as_f32x4!(P3, -1.6668057665E-1);
2032    const_f32_as_f32x4!(P4, 1.4249322787E-1);
2033    const_f32_as_f32x4!(P5, -1.2420140846E-1);
2034    const_f32_as_f32x4!(P6, 1.1676998740E-1);
2035    const_f32_as_f32x4!(P7, -1.1514610310E-1);
2036    const_f32_as_f32x4!(P8, 7.0376836292E-2);
2037    const_f32_as_f32x4!(LN2F_HI, 0.693359375);
2038    const_f32_as_f32x4!(LN2F_LO, -2.12194440e-4);
2039    const_f32_as_f32x4!(VM_SMALLEST_NORMAL, 1.17549435E-38);
2040
2041    let x1 = self;
2042    let x = Self::fraction_2(x1);
2043    let e = Self::exponent(x1);
2044    let mask = x.simd_gt(Self::SQRT_2 * HALF);
2045    let x = (!mask).blend(x + x, x);
2046    let fe = mask.blend(e + Self::ONE, e);
2047    let x = x - Self::ONE;
2048    let res = polynomial_8!(x, P0, P1, P2, P3, P4, P5, P6, P7, P8);
2049    let x2 = x * x;
2050    let res = x2 * x * res;
2051    let res = fe.mul_add(LN2F_LO, res);
2052    let res = res + x2.mul_neg_add(HALF, x);
2053    let res = fe.mul_add(LN2F_HI, res);
2054    let overflow = !self.is_finite();
2055    let underflow = x1.simd_lt(VM_SMALLEST_NORMAL);
2056    let mask = overflow | underflow;
2057    if !mask.any() {
2058      res
2059    } else {
2060      let is_zero = self.is_zero_or_subnormal();
2061      let res = underflow.blend(Self::nan_log(), res);
2062      // Note: is_zero_or_subnormal() lumps subnormals (exponent==0) with zero.
2063      // Both get -Inf here. True subnormal inputs (~1.4e-45..1.175e-38) should
2064      // produce a finite negative result, but are vanishingly rare in
2065      // practice.
2066      let res = is_zero.blend(-Self::infinity(), res);
2067      let res = overflow.blend(self, res);
2068      // This must come *after* overflow.blend to overwrite ln(-∞) = -∞ to NaN
2069      let res = (!self.is_finite() & self.is_sign_negative())
2070        .blend(Self::nan_log(), res);
2071      res
2072    }
2073  }
2074
2075  /// Calculate `ln(1 + self)` for each lane.
2076  /// Accurate even for very small values.
2077  #[inline]
2078  #[must_use]
2079  pub fn ln_1p(self) -> Self {
2080    // Based on the identity ln(1+x) = x·ln(1+x)/((1+x)-1), i.e. x·ln(u)/(u-1)
2081    // where u = 1+x. From MUSL libc (Rich Felker et al., https://musl.libc.org) src/math/log1pf.c
2082    // and fdlibm (Sun Microsystems, https://www.netlib.org/fdlibm/) s_log1p.c.
2083    // When 1+x rounds to 1 exactly (subnormal x), return x directly.
2084    // When 1+x overflows (+inf), return ln(u) without correction.
2085    // Mathematically exact: compensates for the rounding loss in 1+x without
2086    // needing a series threshold.
2087    let u = self + Self::ONE;
2088    let eq = u.simd_eq(Self::ONE);
2089    let ln_u = Self::ln(u);
2090    let correction = self * (ln_u / (u - Self::ONE));
2091    let result = eq.blend(self, correction);
2092    let over = u.is_inf();
2093    over.blend(ln_u, result)
2094  }
2095
2096  #[inline]
2097  #[must_use]
2098  pub fn log2(self) -> Self {
2099    Self::ln(self) * Self::LOG2_E
2100  }
2101  #[inline]
2102  #[must_use]
2103  pub fn log10(self) -> Self {
2104    Self::ln(self) * Self::LOG10_E
2105  }
2106
2107  #[inline]
2108  #[must_use]
2109  pub fn pow_f32x4(self, y: f32x4) -> Self {
2110    const_f32_as_f32x4!(ln2f_hi, 0.693359375);
2111    const_f32_as_f32x4!(ln2f_lo, -2.12194440e-4);
2112    const_f32_as_f32x4!(P0logf, 3.3333331174E-1);
2113    const_f32_as_f32x4!(P1logf, -2.4999993993E-1);
2114    const_f32_as_f32x4!(P2logf, 2.0000714765E-1);
2115    const_f32_as_f32x4!(P3logf, -1.6668057665E-1);
2116    const_f32_as_f32x4!(P4logf, 1.4249322787E-1);
2117    const_f32_as_f32x4!(P5logf, -1.2420140846E-1);
2118    const_f32_as_f32x4!(P6logf, 1.1676998740E-1);
2119    const_f32_as_f32x4!(P7logf, -1.1514610310E-1);
2120    const_f32_as_f32x4!(P8logf, 7.0376836292E-2);
2121
2122    const_f32_as_f32x4!(p2expf, 1.0 / 2.0); // coefficients for Taylor expansion of exp
2123    const_f32_as_f32x4!(p3expf, 1.0 / 6.0);
2124    const_f32_as_f32x4!(p4expf, 1.0 / 24.0);
2125    const_f32_as_f32x4!(p5expf, 1.0 / 120.0);
2126    const_f32_as_f32x4!(p6expf, 1.0 / 720.0);
2127    const_f32_as_f32x4!(p7expf, 1.0 / 5040.0);
2128
2129    let x1 = self.abs();
2130    let x = x1.fraction_2();
2131
2132    let mask = x.simd_gt(f32x4::SQRT_2 * f32x4::HALF);
2133    let x = (!mask).blend(x + x, x);
2134
2135    let x = x - f32x4::ONE;
2136    let x2 = x * x;
2137    let lg1 = polynomial_8!(
2138      x, P0logf, P1logf, P2logf, P3logf, P4logf, P5logf, P6logf, P7logf, P8logf
2139    );
2140    let lg1 = lg1 * x2 * x;
2141
2142    let ef = x1.exponent();
2143    let ef = mask.blend(ef + f32x4::ONE, ef);
2144
2145    let e1 = (ef * y).round();
2146    let yr = ef.mul_sub(y, e1);
2147
2148    let lg = f32x4::HALF.mul_neg_add(x2, x) + lg1;
2149    let x2_err = (f32x4::HALF * x).mul_sub(x, f32x4::HALF * x2);
2150    let lg_err = f32x4::HALF.mul_add(x2, lg - x) - lg1;
2151
2152    let e2 = (lg * y * f32x4::LOG2_E).round();
2153    let v = lg.mul_sub(y, e2 * ln2f_hi);
2154    let v = e2.mul_neg_add(ln2f_lo, v);
2155    let v = v - (lg_err + x2_err).mul_sub(y, yr * f32x4::LN_2);
2156
2157    let x = v;
2158    let e3 = (x * f32x4::LOG2_E).round();
2159    let x = e3.mul_neg_add(f32x4::LN_2, x);
2160    let x2 = x * x;
2161    let z = x2.mul_add(
2162      polynomial_5!(x, p2expf, p3expf, p4expf, p5expf, p6expf, p7expf),
2163      x + f32x4::ONE,
2164    );
2165
2166    let ee = e1 + e2 + e3;
2167    let ei = cast::<_, i32x4>(ee.round_int());
2168    let ej = cast::<_, i32x4>(ei + (cast::<_, i32x4>(z) >> 23));
2169
2170    let overflow = cast::<_, f32x4>(ej.simd_gt(i32x4::splat(0x0FF)))
2171      | (ee.simd_gt(f32x4::splat(300.0)));
2172    let underflow = cast::<_, f32x4>(ej.simd_lt(i32x4::splat(0x000)))
2173      | (ee.simd_lt(f32x4::splat(-300.0)));
2174
2175    // Add exponent by integer addition
2176    let z = cast::<_, f32x4>(cast::<_, i32x4>(z) + (ei << 23));
2177
2178    // Check for overflow/underflow
2179    let z = if (overflow | underflow).any() {
2180      let z = underflow.blend(f32x4::ZERO, z);
2181      overflow.blend(Self::infinity(), z)
2182    } else {
2183      z
2184    };
2185
2186    // Check for self == 0
2187    let x_zero = self.is_zero_or_subnormal();
2188    let z = x_zero.blend(
2189      y.simd_lt(f32x4::ZERO).blend(
2190        Self::infinity(),
2191        y.simd_eq(f32x4::ZERO).blend(f32x4::ONE, f32x4::ZERO),
2192      ),
2193      z,
2194    );
2195
2196    let x_sign = self.is_sign_negative();
2197    let z = if x_sign.any() {
2198      // Y into an integer
2199      let yi = y.simd_eq(y.round());
2200      // Is y odd?
2201      let y_odd = cast::<_, i32x4>(y.round_int() << 31).round_float();
2202
2203      let z1 =
2204        yi.blend(z | y_odd, self.simd_eq(Self::ZERO).blend(z, Self::nan_pow()));
2205      x_sign.blend(z1, z)
2206    } else {
2207      z
2208    };
2209
2210    let x_finite = self.is_finite();
2211    let y_finite = y.is_finite();
2212    let e_finite = ee.is_finite();
2213    if (x_finite & y_finite & (e_finite | x_zero)).all() {
2214      return z;
2215    }
2216
2217    (self.is_nan() | y.is_nan()).blend(self + y, z)
2218  }
2219
2220  #[inline]
2221  pub fn powf(self, y: f32) -> Self {
2222    Self::pow_f32x4(self, f32x4::splat(y))
2223  }
2224
2225  #[must_use]
2226  #[inline]
2227  pub fn unpack_lo(self, b: Self) -> Self {
2228    pick! {
2229      if #[cfg(target_feature="sse")] {
2230        Self { sse: unpack_low_m128(self.sse, b.sse) }
2231      } else if #[cfg(target_feature="simd128")] {
2232        Self {
2233          simd: u32x4_shuffle::<0, 4, 1, 5>(self.simd, b.simd)
2234        }
2235      } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))]{
2236        unsafe {Self { neon: vzip1q_f32(self.neon, b.neon) }}
2237      } else {
2238        Self { arr: [
2239          self.arr[0],
2240          b.arr[0],
2241          self.arr[1],
2242          b.arr[1],
2243        ]}
2244      }
2245    }
2246  }
2247
2248  #[must_use]
2249  #[inline]
2250  pub fn unpack_hi(self, b: Self) -> Self {
2251    pick! {
2252      if #[cfg(target_feature="sse")] {
2253        Self { sse: unpack_high_m128(self.sse, b.sse) }
2254      } else if #[cfg(target_feature="simd128")] {
2255        Self {
2256          simd: u32x4_shuffle::<2, 6, 3, 7>(self.simd, b.simd)
2257        }
2258      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
2259        unsafe {Self { neon: vzip2q_f32(self.neon, b.neon) }}
2260      } else {
2261        Self { arr: [
2262          self.arr[2],
2263          b.arr[2],
2264          self.arr[3],
2265          b.arr[3],
2266        ]}
2267      }
2268    }
2269  }
2270
2271  /// Transpose matrix of 4x4 `f32` matrix. Currently only accelerated on SSE.
2272  #[must_use]
2273  #[inline]
2274  pub fn transpose(data: [f32x4; 4]) -> [f32x4; 4] {
2275    pick! {
2276      if #[cfg(target_feature="sse")] {
2277        let mut e0 = data[0];
2278        let mut e1 = data[1];
2279        let mut e2 = data[2];
2280        let mut e3 = data[3];
2281
2282        transpose_four_m128(&mut e0.sse, &mut e1.sse, &mut e2.sse, &mut e3.sse);
2283
2284        [e0, e1, e2, e3]
2285      } else if #[cfg(any(all(target_feature="neon",target_arch="aarch64"), target_feature="simd128"))] {
2286        let a = data[0].unpack_lo(data[2]);
2287        let b = data[1].unpack_lo(data[3]);
2288        let c = data[0].unpack_hi(data[2]);
2289        let d = data[1].unpack_hi(data[3]);
2290
2291        [
2292          a.unpack_lo(b),
2293          a.unpack_hi(b),
2294          c.unpack_lo(d),
2295          c.unpack_hi(d),
2296        ]
2297      } else {
2298        #[inline(always)]
2299        fn transpose_column(data: &[f32x4; 4], index: usize) -> f32x4 {
2300          f32x4::new([
2301            data[0].as_array()[index],
2302            data[1].as_array()[index],
2303            data[2].as_array()[index],
2304            data[3].as_array()[index],
2305          ])
2306        }
2307
2308        [
2309          transpose_column(&data, 0),
2310          transpose_column(&data, 1),
2311          transpose_column(&data, 2),
2312          transpose_column(&data, 3),
2313        ]
2314      }
2315    }
2316  }
2317
2318  #[inline]
2319  pub fn to_array(self) -> [f32; 4] {
2320    cast(self)
2321  }
2322
2323  #[inline]
2324  pub fn as_array(&self) -> &[f32; 4] {
2325    cast_ref(self)
2326  }
2327
2328  #[inline]
2329  pub fn as_mut_array(&mut self) -> &mut [f32; 4] {
2330    cast_mut(self)
2331  }
2332
2333  #[inline]
2334  pub fn from_i32x4(v: i32x4) -> Self {
2335    pick! {
2336      if #[cfg(target_feature="sse2")] {
2337        Self { sse: convert_to_m128_from_i32_m128i(v.sse) }
2338      } else if #[cfg(target_feature="simd128")] {
2339        Self { simd: f32x4_convert_i32x4(v.simd) }
2340      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
2341        Self { neon: unsafe { vcvtq_f32_s32(v.neon) }}
2342      } else {
2343        Self { arr: [
2344            v.as_array()[0] as f32,
2345            v.as_array()[1] as f32,
2346            v.as_array()[2] as f32,
2347            v.as_array()[3] as f32,
2348          ] }
2349      }
2350    }
2351  }
2352
2353  /// Returns true for each element if its sign bit is set.
2354  ///
2355  /// If the sign bit is set, the result has all bits set, not just the sign
2356  /// bit. This has been renamed to [`is_sign_negative`].
2357  ///
2358  /// [`is_sign_negative`]: Self::is_sign_negative
2359  #[inline]
2360  #[must_use]
2361  #[deprecated(since = "1.4.0", note = "renamed to `is_sign_negative`")]
2362  pub fn sign_bit(self) -> Self {
2363    self.is_sign_negative()
2364  }
2365}