Skip to main content

wide/
lib.rs

1#![no_std]
2#![allow(non_camel_case_types)]
3#![warn(clippy::doc_markdown)]
4#![warn(clippy::missing_inline_in_public_items)]
5#![allow(clippy::eq_op)]
6#![allow(clippy::excessive_precision)]
7#![allow(clippy::let_and_return)]
8#![allow(clippy::unusual_byte_groupings)]
9#![allow(clippy::misrefactored_assign_op)]
10#![allow(clippy::approx_constant)]
11#![forbid(missing_docs)]
12
13//! A crate to help you go wide.
14//!
15//! This crate provides SIMD-compatible data types.
16//!
17//! When possible, explicit SIMD is used with all the math operations here. As a
18//! fallback, the fact that all the lengths of a fixed length array are doing
19//! the same thing will often make LLVM notice that it should use SIMD
20//! instructions to complete the task. In the worst case, the code just becomes
21//! totally scalar (though the math is still correct, at least).
22//!
23//! # Masks
24//!
25//! SIMD vector masks are per-element booleans, and are represented by SIMD
26//! vectors where each element's value in bits is either all zeros (`false`) or
27//! all ones (`true`).
28//!
29//! SIMD versions of functions that regularly return `bool` return masks. For
30//! example, [`f32::is_sign_positive`] returns `bool`, and
31//! [`f32x4::is_sign_positive`] returns an [`f32x4`] that represents a mask.
32//! The [`select`] method can be used to perform a per-element `if` statement
33//! over a mask. For example, for this simple scalar code:
34//!
35//! ```
36//! let x = 1.0_f32;
37//!
38//! let result = if x.is_sign_positive() {
39//!     5.0
40//! } else {
41//!     3.0
42//! };
43//!
44//! assert_eq!(result, 5.0);
45//! ```
46//!
47//! This is the SIMD version:
48//!
49//! ```
50//! # use wide::f32x4;
51//! #
52//! let x = f32x4::new([1.0, -1.0, -1.0, 1.0]);
53//!
54//! let result = x.is_sign_positive().select(
55//!     f32x4::splat(5.0),
56//!     f32x4::splat(3.0),
57//! );
58//!
59//! assert_eq!(result, f32x4::new([5.0, 3.0, 3.0, 5.0]));
60//! ```
61//!
62//! To select from two values of a downstream type, implement the [`Select`]
63//! trait.
64//!
65//! # Shuffling
66//!
67//! Shuffling, also known as swizzling, creates a new SIMD vector by selecting
68//! elements from one or more input vectors according to a set of indices.
69//!
70//! Single-input shuffling is performed using the [`shuffle`] method and its
71//! variants. Multi-input shuffling is performed by placing the input vectors in
72//! an array and calling the corresponding methods from [`ShuffleExt`].
73//!
74//! Currently, `wide` only supports runtime-index based shuffling:
75//!
76//! ```
77//! use wide::{f32x4, ShuffleExt, u32x4};
78//!
79//! let simd_a = f32x4::new([0.0, 1.0, 2.0, 3.0]);
80//! let simd_b = f32x4::new([100.0, 101.0, 102.0, 103.0]);
81//!
82//! let reverse = simd_a.shuffle(u32x4::new([3, 2, 1, 0]));
83//!
84//! assert_eq!(reverse, f32x4::new([3.0, 2.0, 1.0, 0.0]));
85//!
86//! // Here, indices `0..4` map to `simd_a`, and indices `4..8` map to `simd_b`
87//! let from_two_vectors = [simd_a, simd_b].shuffle(u32x4::new([2, 3, 4, 5]));
88//!
89//! assert_eq!(from_two_vectors, f32x4::new([2.0, 3.0, 100.0, 101.0]));
90//! ```
91//!
92//! # NaN bit patterns
93//!
94//! Operations on SIMD vectors of floats do not make any guarantees about the
95//! specific bit-patterns of output NaN values (meaning the sign bit,
96//! quiet/signaling bit, and payload). This is unlike standard library
97//! operations on float primitives, which do define rules for what NaN bit
98//! patterns are returned.
99//!
100//! The reason for this is that enforcing guarantees would add substantial
101//! overhead to operations, and is generally not worth it.
102//!
103//! # Wrapping semantics
104//!
105//! SIMD vectors of integers treat operators as wrapping, as if [`Wrapping<T>`]
106//! was used. Thus, SIMD vectors do not implement `wrapping_*` functions,
107//! because that is the default behavior. This means there are no overflow
108//! checks, even in debug builds.
109//!
110//! The reason for this is that for most applications where SIMD is appropriate,
111//! it is "not a bug" to wrap, and even debug builds are unlikely to tolerate
112//! the loss of performance.
113//!
114//! This "no panicking" approach extends to more than just integer overflows. It
115//! is also true for things like [`f32x4::clamp`]. Even though [`f32::clamp`]
116//! panics for invalid inputs, the SIMD version does not, because it may be used
117//! with per-element branching, where invalid elements get discarded later by
118//! [`select`].
119//!
120//! # Casting
121//!
122//! The SIMD types implement the [`bytemuck::Pod`] trait, which means that it
123//! is possible to do bitwise casts between SIMD types of the same size with
124//! the [`bytemuck::cast()`] function and others. `bytemuck` is re-exported by
125//! this crate for convenience.
126//!
127//! This typically does not have much, if any, runtime overhead in optimized
128//! builds.
129//!
130//! # Feature flags
131//!
132//! * `std`: This causes the feature to link to `std`.
133//!   * Currently this just improves the performance of `sqrt` when an explicit
134//!     SIMD `sqrt` isn't available.
135//!
136//! [`select`]: f32x4::select
137//! [`shuffle`]: f32x4::shuffle
138//! [`Wrapping<T>`]: core::num::Wrapping
139
140// Note(Lokathor): Due to standard library magic, the std-only methods for f32
141// and f64 will automatically be available simply by declaring this.
142#[cfg(feature = "std")]
143extern crate std;
144
145// TODO
146// Add/Sub/Mul/Div with constant
147// Shuffle left/right/by index
148
149use core::ops::*;
150
151#[allow(unused_imports)]
152#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
153use safe_arch::*;
154
155use bytemuck::*;
156
157// Re-export so that users don't need to add a bytemuck dependency of their own
158pub use bytemuck;
159
160#[macro_use]
161mod simd;
162#[macro_use]
163mod simd_float;
164#[macro_use]
165mod simd_int;
166#[macro_use]
167mod simd_uint;
168
169macro_rules! pick {
170  ($(if #[cfg($($test:meta),*)] {
171      $($if_tokens:tt)*
172    })else+ else {
173      $($else_tokens:tt)*
174    }) => {
175    pick!{
176      @__forests [ ] ;
177      $( [ {$($test),*} {$($if_tokens)*} ], )*
178      [ { } {$($else_tokens)*} ],
179    }
180  };
181  (if #[cfg($($if_meta:meta),*)] {
182      $($if_tokens:tt)*
183    } $(else if #[cfg($($else_meta:meta),*)] {
184      $($else_tokens:tt)*
185    })*) => {
186    pick!{
187      @__forests [ ] ;
188      [ {$($if_meta),*} {$($if_tokens)*} ],
189      $( [ {$($else_meta),*} {$($else_tokens)*} ], )*
190    }
191  };
192  (@__forests [$($not:meta,)*];) => {
193    /* halt expansion */
194  };
195  (@__forests [$($not:meta,)*]; [{$($m:meta),*} {$($tokens:tt)*}], $($rest:tt)*) => {
196    #[cfg(all( $($m,)* not(any($($not),*)) ))]
197    pick!{ @__identity $($tokens)* }
198    pick!{ @__forests [ $($not,)* $($m,)* ] ; $($rest)* }
199  };
200  (@__identity $($tokens:tt)*) => {
201    $($tokens)*
202  };
203}
204
205// TODO: make these generic over `mul_add`? Worth it?
206
207macro_rules! polynomial_2 {
208  ($x:expr, $c0:expr, $c1:expr, $c2:expr $(,)?) => {{
209    let x = $x;
210    let x2 = x * x;
211    x2.mul_add($c2, x.mul_add($c1, $c0))
212  }};
213}
214
215macro_rules! polynomial_3 {
216  ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr $(,)?) => {{
217    let x = $x;
218    let x2 = x * x;
219    $c3.mul_add(x, $c2).mul_add(x2, $c1.mul_add(x, $c0))
220  }};
221}
222
223macro_rules! polynomial_4 {
224  ($x:expr, $c0:expr, $c1:expr, $c2:expr ,$c3:expr, $c4:expr $(,)?) => {{
225    let x = $x;
226    let x2 = x * x;
227    let x4 = x2 * x2;
228    $c3.mul_add(x, $c2).mul_add(x2, $c1.mul_add(x, $c0)) + $c4 * x4
229  }};
230}
231
232macro_rules! polynomial_5 {
233  ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr $(,)?) => {{
234    let x = $x;
235    let x2 = x * x;
236    let x4 = x2 * x2;
237    $c3
238      .mul_add(x, $c2)
239      .mul_add(x2, $c5.mul_add(x, $c4).mul_add(x4, $c1.mul_add(x, $c0)))
240  }};
241}
242
243macro_rules! polynomial_5n {
244  ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr $(,)?) => {{
245    let x = $x;
246    let x2 = x * x;
247    let x4 = x2 * x2;
248    x2.mul_add(x.mul_add($c3, $c2), (x4.mul_add($c4 + x, x.mul_add($c1, $c0))))
249  }};
250}
251
252macro_rules! polynomial_6 {
253  ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr ,$c6:expr $(,)?) => {{
254    let x = $x;
255    let x2 = x * x;
256    let x4 = x2 * x2;
257    x4.mul_add(
258      x2.mul_add($c6, x.mul_add($c5, $c4)),
259      x2.mul_add(x.mul_add($c3, $c2), x.mul_add($c1, $c0)),
260    )
261  }};
262}
263
264macro_rules! polynomial_6n {
265  ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr $(,)?) => {{
266    let x = $x;
267    let x2 = x * x;
268    let x4 = x2 * x2;
269    x4.mul_add(
270      x.mul_add($c5, x2 + $c4),
271      x2.mul_add(x.mul_add($c3, $c2), x.mul_add($c1, $c0)),
272    )
273  }};
274}
275
276macro_rules! polynomial_7 {
277  ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr, $c6:expr, $c7:expr $(,)?) => {{
278    let x = $x;
279    let x2 = x * x;
280    let x4 = x2 * x2;
281    x4.mul_add(
282      x2.mul_add(x.mul_add($c7, $c6), x.mul_add($c5, $c4)),
283      x2.mul_add(x.mul_add($c3, $c2), x.mul_add($c1, $c0)),
284    )
285  }};
286}
287
288macro_rules! polynomial_8 {
289  ($x:expr, $c0:expr, $c1:expr, $c2:expr, $c3:expr, $c4:expr, $c5:expr,  $c6:expr, $c7:expr, $c8:expr $(,)?) => {{
290    let x = $x;
291    let x2 = x * x;
292    let x4 = x2 * x2;
293    let x8 = x4 * x4;
294    x4.mul_add(
295      x2.mul_add($c7.mul_add(x, $c6), x.mul_add($c5, $c4)),
296      x8.mul_add($c8, x2.mul_add(x.mul_add($c3, $c2), x.mul_add($c1, $c0))),
297    )
298  }};
299}
300
301macro_rules! polynomial_13 {
302  // calculates polynomial c13*x^13 + c12*x^12 + ... + c1*x + c0
303  ($x:expr,  $c2:expr, $c3:expr, $c4:expr, $c5:expr,$c6:expr, $c7:expr, $c8:expr,$c9:expr, $c10:expr, $c11:expr, $c12:expr, $c13:expr  $(,)?) => {{
304    let x = $x;
305    let x2 = x * x;
306    let x4 = x2 * x2;
307    let x8 = x4 * x4;
308    x8.mul_add(
309      x4.mul_add(
310        x.mul_add($c13, $c12),
311        x2.mul_add(x.mul_add($c11, $c10), x.mul_add($c9, $c8)),
312      ),
313      x4.mul_add(
314        x2.mul_add(x.mul_add($c7, $c6), x.mul_add($c5, $c4)),
315        x2.mul_add(x.mul_add($c3, $c2), x),
316      ),
317    )
318  }};
319}
320
321mod f32x16_;
322pub use f32x16_::*;
323
324mod f32x8_;
325pub use f32x8_::*;
326
327mod f32x4_;
328pub use f32x4_::*;
329
330mod f64x8_;
331pub use f64x8_::*;
332
333mod f64x4_;
334pub use f64x4_::*;
335
336mod f64x2_;
337pub use f64x2_::*;
338
339mod i8x16_;
340pub use i8x16_::*;
341
342mod i16x16_;
343pub use i16x16_::*;
344
345mod i16x32_;
346pub use i16x32_::*;
347
348mod i8x32_;
349pub use i8x32_::*;
350
351mod i8x64_;
352pub use i8x64_::*;
353
354mod i16x8_;
355pub use i16x8_::*;
356
357mod i32x4_;
358pub use i32x4_::*;
359
360mod i32x8_;
361pub use i32x8_::*;
362
363mod i32x16_;
364pub use i32x16_::*;
365
366mod i64x2_;
367pub use i64x2_::*;
368
369mod i64x4_;
370pub use i64x4_::*;
371
372mod i64x8_;
373pub use i64x8_::*;
374
375mod u8x16_;
376pub use u8x16_::*;
377
378mod u8x32_;
379pub use u8x32_::*;
380
381mod u8x64_;
382pub use u8x64_::*;
383
384mod u16x8_;
385pub use u16x8_::*;
386
387mod u16x16_;
388pub use u16x16_::*;
389
390mod u16x32_;
391pub use u16x32_::*;
392
393mod u32x4_;
394pub use u32x4_::*;
395
396mod u32x8_;
397pub use u32x8_::*;
398
399mod u32x16_;
400pub use u32x16_::*;
401
402mod u64x2_;
403pub use u64x2_::*;
404
405mod u64x4_;
406pub use u64x4_::*;
407
408mod u64x8_;
409pub use u64x8_::*;
410
411/// A mask for the low `W` bits, the only part of a 64-bit `add_mul_lo` or
412/// `add_mul_hi` operand that is read.
413#[inline]
414const fn add_mul_operand_mask_u64<const W: u32>() -> u64 {
415  const { assert!(W >= 1 && W <= 64, "`add_mul` width must be in `1..=64`") };
416  u64::MAX >> (64 - W)
417}
418
419/// One `u64` lane of `add_mul_lo`: `acc + ((a * b) mod 2^W)`.
420#[inline]
421fn add_mul_lo_lane_u64<const W: u32>(acc: u64, a: u64, b: u64) -> u64 {
422  let mask = add_mul_operand_mask_u64::<W>();
423  let product = (a & mask) as u128 * (b & mask) as u128;
424  acc.wrapping_add(product as u64 & mask)
425}
426
427/// One `u64` lane of `add_mul_hi`: `acc + ((a * b) >> W)`.
428#[inline]
429fn add_mul_hi_lane_u64<const W: u32>(acc: u64, a: u64, b: u64) -> u64 {
430  let mask = add_mul_operand_mask_u64::<W>();
431  let product = (a & mask) as u128 * (b & mask) as u128;
432  acc.wrapping_add((product >> W) as u64)
433}
434
435/// A mask for the low `W` bits, the only part of a 32-bit `add_mul_lo` or
436/// `add_mul_hi` operand that is read.
437#[inline]
438const fn add_mul_operand_mask_u32<const W: u32>() -> u32 {
439  const { assert!(W >= 1 && W <= 32, "`add_mul` width must be in `1..=32`") };
440  u32::MAX >> (32 - W)
441}
442
443/// One `u32` lane of `add_mul_lo`: `acc + ((a * b) mod 2^W)`.
444#[inline]
445fn add_mul_lo_lane_u32<const W: u32>(acc: u32, a: u32, b: u32) -> u32 {
446  let mask = add_mul_operand_mask_u32::<W>();
447  let product = (a & mask) as u64 * (b & mask) as u64;
448  acc.wrapping_add(product as u32 & mask)
449}
450
451/// One `u32` lane of `add_mul_hi`: `acc + ((a * b) >> W)`.
452#[inline]
453fn add_mul_hi_lane_u32<const W: u32>(acc: u32, a: u32, b: u32) -> u32 {
454  let mask = add_mul_operand_mask_u32::<W>();
455  let product = (a & mask) as u64 * (b & mask) as u64;
456  acc.wrapping_add((product >> W) as u32)
457}
458
459#[allow(dead_code)]
460fn generic_bit_blend<T>(mask: T, y: T, n: T) -> T
461where
462  T: Copy + BitXor<Output = T> + BitAnd<Output = T>,
463{
464  n ^ ((n ^ y) & mask)
465}
466
467#[allow(unused)]
468fn software_sqrt(x: f64) -> f64 {
469  use core::num::Wrapping;
470  type wu32 = Wrapping<u32>;
471  const fn w(u: u32) -> wu32 {
472    Wrapping(u)
473  }
474  let mut z: f64;
475  let sign: wu32 = w(0x80000000);
476  let mut ix0: i32;
477  let mut s0: i32;
478  let mut q: i32;
479  let mut m: i32;
480  let mut t: i32;
481  let mut i: i32;
482  let mut r: wu32;
483  let mut t1: wu32;
484  let mut s1: wu32;
485  let mut ix1: wu32;
486  let mut q1: wu32;
487  // extract data
488
489  pick! {
490    if #[cfg(target_endian = "little")]
491    {
492      let [low, high]: [u32; 2] = cast(x);
493      ix0 = high as i32;
494      ix1 = w(low);
495    }
496    else
497    {
498      let [high, low]: [u32; 2] = cast(x);
499      ix0 = high as i32;
500      ix1 = w(low);
501    }
502  }
503
504  // inf and nan
505  {
506    if x.is_nan() {
507      return f64::NAN;
508    }
509    if ix0 & 0x7ff00000 == 0x7ff00000 {
510      return x * x + x;
511    }
512  }
513  // handle zero
514  {
515    if ix0 <= 0 {
516      if ((ix0 & (!sign).0 as i32) | (ix1.0 as i32)) == 0 {
517        return x;
518      } else if ix0 < 0 {
519        return (x - x) / (x - x);
520      }
521    }
522  }
523  // normalize
524  {
525    m = ix0 >> 20;
526    if m == 0 {
527      // subnormal
528      while ix0 == 0 {
529        m -= 21;
530        ix0 |= (ix1 >> 11).0 as i32;
531        ix1 <<= 21;
532      }
533      i = 0;
534      while ix0 & 0x00100000 == 0 {
535        ix0 <<= 1;
536        i += 1;
537      }
538      m -= i - 1;
539      ix0 |= (ix1.0 >> (31 - i)) as i32;
540      ix1 <<= i as usize;
541    }
542    // un-bias exponent
543    m -= 1023;
544    ix0 = (ix0 & 0x000fffff) | 0x00100000;
545    if (m & 1) != 0 {
546      // odd m, double the input to make it even
547      ix0 += ix0 + ((ix1 & sign) >> 31).0 as i32;
548      ix1 += ix1;
549    }
550    m >>= 1;
551  }
552  // generate sqrt bit by bit
553  {
554    ix0 += ix0 + ((ix1 & sign) >> 31).0 as i32;
555    ix1 += ix1;
556    // q and q1 store the sqrt(x);
557    q = 0;
558    q1 = w(0);
559    s0 = 0;
560    s1 = w(0);
561    // our bit that moves from right to left
562    r = w(0x00200000);
563    while r != w(0) {
564      t = s0 + (r.0 as i32);
565      if t <= ix0 {
566        s0 = t + (r.0 as i32);
567        ix0 -= t;
568        q += (r.0 as i32);
569      }
570      ix0 += ix0 + ((ix1 & sign) >> 31).0 as i32;
571      ix1 += ix1;
572      r >>= 1;
573    }
574    r = sign;
575    while r != w(0) {
576      t1 = s1 + r;
577      t = s0;
578      if (t < ix0) || ((t == ix0) && (t1 <= ix1)) {
579        s1 = t1 + r;
580        if t1 & sign == sign && (s1 & sign) == w(0) {
581          s0 += 1;
582        }
583        ix0 -= t;
584        if ix1 < t1 {
585          ix0 -= 1;
586        }
587        ix1 -= t1;
588        q1 += r;
589      }
590      ix0 += ix0 + ((ix1 & sign) >> 31).0 as i32;
591      ix1 += ix1;
592      r >>= 1;
593    }
594  }
595  // use floating add to find out rounding direction
596  {
597    if ix0 | (ix1.0 as i32) != 0 {
598      z = 1.0 - 1.0e-300;
599      if z >= 1.0 {
600        z = 1.0 + 1.0e-300;
601        if q1 == w(0xffffffff) {
602          q1 = w(0);
603          q += 1;
604        } else if z > 1.0 {
605          if q1 == w(0xfffffffe) {
606            q += 1;
607          }
608          q1 += w(2);
609        } else {
610          q1 += q1 & w(1);
611        }
612      }
613    }
614  }
615  // finish up
616  ix0 = (q >> 1) + 0x3fe00000;
617  ix1 = q1 >> 1;
618  if q & 1 == 1 {
619    ix1 |= sign;
620  }
621  ix0 += m << 20;
622
623  pick! {
624    if #[cfg(target_endian = "little")]
625    {
626      cast::<[u32; 2], f64>([ix1.0, ix0 as u32])
627    }
628    else
629    {
630      cast::<[u32; 2], f64>([ix0 as u32, ix1.0])
631    }
632  }
633}
634
635#[test]
636fn test_software_sqrt() {
637  assert!(software_sqrt(f64::NAN).is_nan());
638  assert_eq!(software_sqrt(f64::INFINITY), f64::INFINITY);
639  assert_eq!(software_sqrt(0.0), 0.0);
640  assert_eq!(software_sqrt(-0.0), -0.0);
641  assert!(software_sqrt(-1.0).is_nan());
642  assert!(software_sqrt(f64::NEG_INFINITY).is_nan());
643  assert_eq!(software_sqrt(4.0), 2.0);
644  assert_eq!(software_sqrt(9.0), 3.0);
645  assert_eq!(software_sqrt(16.0), 4.0);
646  assert_eq!(software_sqrt(25.0), 5.0);
647  assert_eq!(software_sqrt(5000.0 * 5000.0), 5000.0);
648}
649
650/// A trait for lanewise SIMD selection.
651///
652/// The method [`select`] is used to select lanes from two SIMD values based on
653/// the elements of a SIMD [mask].
654///
655/// The type of values to select from could be a SIMD vector (e.g.,
656/// [`f32x4`]), or it could be a composite type over SIMD vectors (e.g., a
657/// `Vec3<f32x4>` structure).
658///
659/// This trait is meant to be implemented for such downstream types. If you
660/// simply want to call [`select`], you do not need to import this trait as SIMD
661/// vectors have an inherent method for this.
662///
663/// # Example
664///
665/// ```
666/// # use wide::{u32x4, Select};
667/// #
668/// #[derive(Debug, PartialEq)]
669/// struct Point {
670///     x: u32x4,
671///     y: u32x4,
672/// }
673///
674/// impl Select<Point> for u32x4 {
675///     fn select(self, if_true: Point, if_false: Point) -> Point {
676///         Point {
677///             x: self.select(if_true.x, if_false.x),
678///             y: self.select(if_true.y, if_false.y),
679///         }
680///     }
681/// }
682///
683/// let mask = u32x4::new([0, !0, !0, 0]);
684/// let if_true = Point { x: u32x4::splat(2), y: u32x4::ZERO };
685/// let if_false = Point { x: u32x4::ZERO, y: u32x4::splat(5) };
686///
687/// assert_eq!(
688///     mask.select(if_true, if_false),
689///     Point {
690///         x: u32x4::new([0, 2, 2, 0]),
691///         y: u32x4::new([5, 0, 0, 5]),
692///     },
693/// );
694/// ```
695///
696/// [`select`]: f32x4::select
697/// [mask]: crate#masks
698pub trait Select<T> {
699  /// Lanewise SIMD selection.
700  ///
701  /// If `self` is a SIMD vector, it is treated as a [mask], where
702  /// each lane is considered true if all bits are one, or false if all bits are
703  /// zero.
704  ///
705  /// For each lane:
706  ///
707  /// - If `self` is true, return the corresponding lane of `if_true`
708  /// - If `self` is false, return the corresponding lane of `if_false`
709  ///
710  /// [mask]: crate#masks
711  #[must_use]
712  fn select(self, if_true: T, if_false: T) -> T;
713}
714
715/// An extension trait implemented for arrays of SIMD vectors, which provides
716/// shuffling from multiple input vectors.
717///
718/// # Example
719///
720/// ```
721/// use wide::{f32x4, ShuffleExt, u32x4};
722///
723/// let simd_a = f32x4::new([0.0, 1.0, 2.0, 3.0]);
724/// let simd_b = f32x4::new([100.0, 101.0, 102.0, 103.0]);
725///
726/// // Here, indices `0..4` map to `simd_a`, and indices `4..8` map to `simd_b`
727/// let from_two_vectors = [simd_a, simd_b].shuffle(u32x4::new([2, 3, 4, 5]));
728///
729/// assert_eq!(from_two_vectors, f32x4::new([2.0, 3.0, 100.0, 101.0]));
730/// ```
731#[expect(private_bounds)]
732pub trait ShuffleExt: Sealed {
733  /// The type representing indices.
734  ///
735  /// This is always a SIMD vector of the unsigned integer with the same size as
736  /// `T` and the same number of elements.
737  type Indices;
738
739  /// The type returned by shuffle functions.
740  ///
741  /// This is always the type of SIMD vector contained in the array.
742  type Output;
743
744  /// Returns a SIMD vector whose elements are selected from multiple input
745  /// vectors using the corresponding runtime `indices`.
746  ///
747  /// If `N` is the number of elements in each vector, indices in the range
748  /// `0..N` select values from `self[0]`, indices in the range `N..N * 2`
749  /// select values from `self[1]`, etc.
750  ///
751  /// If an index is out of bounds (greater than or equal to `N * INPUTS`), the
752  /// corresponding result element is unspecified.
753  ///
754  /// # Example
755  ///
756  /// ```
757  /// use wide::{f32x4, ShuffleExt, u32x4};
758  ///
759  /// let simd_a = f32x4::new([0.0, 1.0, 2.0, 3.0]);
760  /// let simd_b = f32x4::new([100.0, 101.0, 102.0, 103.0]);
761  ///
762  /// // Here, indices `0..4` map to `simd_a`, and indices `4..8` map to `simd_b`
763  /// let from_two_vectors = [simd_a, simd_b].shuffle(u32x4::new([2, 3, 4, 5]));
764  ///
765  /// assert_eq!(from_two_vectors, f32x4::new([2.0, 3.0, 100.0, 101.0]));
766  /// ```
767  ///
768  /// # Type-specific guarantees
769  ///
770  /// For all 8-bit types, it is guaranteed that for out of bounds indices,
771  /// either zero is returned or the index wraps around, non-deterministically
772  /// (unlike other types, which can return arbitrary values).
773  #[must_use]
774  fn shuffle(self, indices: Self::Indices) -> Self::Output;
775
776  /// Returns a SIMD vector whose elements are selected from multiple input
777  /// vectors using the corresponding runtime `indices`.
778  ///
779  /// If `N` is the number of elements in each vector, indices in the range
780  /// `0..N` select values from `self[0]`, indices in the range `N..N * 2`
781  /// select values from `self[1]`, etc.
782  ///
783  /// If an index is out of bounds (greater than or equal to `N * INPUTS`), the
784  /// corresponding result element is the number zero.
785  ///
786  /// # Example
787  ///
788  /// ```
789  /// use wide::{f32x4, ShuffleExt, u32x4};
790  ///
791  /// let simd_a = f32x4::new([0.0, 1.0, 2.0, 3.0]);
792  /// let simd_b = f32x4::new([100.0, 101.0, 102.0, 103.0]);
793  ///
794  /// // Here, indices `0..4` map to `simd_a`, and indices `4..8` map to `simd_b`
795  /// let from_two_vectors = [simd_a, simd_b].shuffle_zeroing(u32x4::new([2, 3, 100, 5]));
796  ///
797  /// assert_eq!(from_two_vectors, f32x4::new([2.0, 3.0, 0.0, 101.0]));
798  /// ```
799  #[must_use]
800  fn shuffle_zeroing(self, indices: Self::Indices) -> Self::Output;
801
802  /// Returns a SIMD vector whose elements are selected from multiple input
803  /// vectors using the corresponding runtime `indices`.
804  ///
805  /// If `N` is the number of elements in each vector, indices in the range
806  /// `0..N` select values from `self[0]`, indices in the range `N..N * 2`
807  /// select values from `self[1]`, etc.
808  ///
809  /// Indices are wrapped by `N * INPUTS`.
810  ///
811  /// # Example
812  ///
813  /// ```
814  /// use wide::{f32x4, ShuffleExt, u32x4};
815  ///
816  /// let simd_a = f32x4::new([0.0, 1.0, 2.0, 3.0]);
817  /// let simd_b = f32x4::new([100.0, 101.0, 102.0, 103.0]);
818  ///
819  /// // Here, indices `0..4` map to `simd_a`, and indices `4..8` map to `simd_b`
820  /// let from_two_vectors = [simd_a, simd_b].shuffle_wrapping(u32x4::new([2, 3, 9, 5]));
821  ///
822  /// assert_eq!(from_two_vectors, f32x4::new([2.0, 3.0, 1.0, 101.0]));
823  /// ```
824  #[must_use]
825  fn shuffle_wrapping(self, indices: Self::Indices) -> Self::Output;
826}
827
828/// A deprecated trait for the [`simd_eq`] function.
829///
830/// [`simd_eq`]: f32x4::simd_eq
831#[deprecated(
832  since = "1.5.0",
833  note = "use the inherent function `simd_eq` instead"
834)]
835pub trait CmpEq<Rhs = Self> {
836  /// The type returned by [`simd_eq`].
837  ///
838  /// [`simd_eq`]: Self::simd_eq
839  type Output;
840
841  /// Returns a [mask] that checks if each element of `self` is equal to the
842  /// corresponding element of `rhs`.
843  ///
844  /// This is a method of the deprecated [`CmpEq`] trait. Use the inherent
845  /// function instead.
846  ///
847  /// [mask]: crate#masks
848  fn simd_eq(self, rhs: Rhs) -> Self::Output;
849}
850
851/// A deprecated trait for the [`simd_gt`] function.
852///
853/// [`simd_gt`]: f32x4::simd_gt
854#[deprecated(
855  since = "1.5.0",
856  note = "use the inherent function `simd_gt` instead"
857)]
858pub trait CmpGt<Rhs = Self> {
859  /// The type returned by [`simd_gt`].
860  ///
861  /// [`simd_gt`]: Self::simd_gt
862  type Output;
863
864  /// Returns a [mask] that checks if each element of `self` is greater than the
865  /// corresponding element of `rhs`.
866  ///
867  /// This is a method of the deprecated [`CmpGt`] trait. Use the inherent
868  /// function instead.
869  ///
870  /// [mask]: crate#masks
871  fn simd_gt(self, rhs: Rhs) -> Self::Output;
872}
873
874/// A deprecated trait for the [`simd_ge`] function.
875///
876/// [`simd_ge`]: f32x4::simd_ge
877#[deprecated(
878  since = "1.5.0",
879  note = "use the inherent function `simd_ge` instead"
880)]
881pub trait CmpGe<Rhs = Self> {
882  /// The type returned by [`simd_ge`].
883  ///
884  /// [`simd_ge`]: Self::simd_ge
885  type Output;
886
887  /// Returns a [mask] that checks if each element of `self` is greater than or
888  /// equal to the corresponding element of `rhs`.
889  ///
890  /// This is a method of the deprecated [`CmpGe`] trait. Use the inherent
891  /// function instead.
892  ///
893  /// [mask]: crate#masks
894  fn simd_ge(self, rhs: Rhs) -> Self::Output;
895}
896
897/// A deprecated trait for the [`simd_ne`] function.
898///
899/// [`simd_ne`]: f32x4::simd_ne
900#[deprecated(
901  since = "1.5.0",
902  note = "use the inherent function `simd_ne` instead"
903)]
904pub trait CmpNe<Rhs = Self> {
905  /// The type returned by [`simd_ne`].
906  ///
907  /// [`simd_ne`]: Self::simd_ne
908  type Output;
909
910  /// Returns a [mask] that checks if each element of `self` is not equal to the
911  /// corresponding element of `rhs`.
912  ///
913  /// This is a method of the deprecated [`CmpNe`] trait. Use the inherent
914  /// function instead.
915  ///
916  /// [mask]: crate#masks
917  fn simd_ne(self, rhs: Rhs) -> Self::Output;
918}
919
920/// A deprecated trait for the [`simd_lt`] function.
921///
922/// [`simd_lt`]: f32x4::simd_lt
923#[deprecated(
924  since = "1.5.0",
925  note = "use the inherent function `simd_lt` instead"
926)]
927pub trait CmpLt<Rhs = Self> {
928  /// The type returned by [`simd_lt`].
929  ///
930  /// [`simd_lt`]: Self::simd_lt
931  type Output;
932
933  /// Returns a [mask] that checks if each element of `self` is less than the
934  /// corresponding element of `rhs`.
935  ///
936  /// This is a method of the deprecated [`CmpLt`] trait. Use the inherent
937  /// function instead.
938  ///
939  /// [mask]: crate#masks
940  fn simd_lt(self, rhs: Rhs) -> Self::Output;
941}
942
943/// A deprecated trait for the [`simd_le`] function.
944///
945/// [`simd_le`]: f32x4::simd_le
946#[deprecated(
947  since = "1.5.0",
948  note = "use the inherent function `simd_le` instead"
949)]
950pub trait CmpLe<Rhs = Self> {
951  /// The type returned by [`simd_le`].
952  ///
953  /// [`simd_le`]: Self::simd_le
954  type Output;
955
956  /// Returns a [mask] that checks if each element of `self` is less than or
957  /// equal to the corresponding element of `rhs`.
958  ///
959  /// This is a method of the deprecated [`CmpLe`] trait. Use the inherent
960  /// function instead.
961  ///
962  /// [mask]: crate#masks
963  fn simd_le(self, rhs: Rhs) -> Self::Output;
964}
965
966/// A trait for SIMD variants of [`align_to`] functions.
967///
968/// [`align_to`]: https://doc.rust-lang.org/std/primitive.slice.html#method.align_to
969pub trait AlignTo
970where
971  Self: Pod + Default + PartialEq + From<Self::Elem>,
972  Self::Elem: Pod + Default + PartialEq,
973{
974  /// The element type of this SIMD vector.
975  type Elem;
976
977  /// A SIMD variant of [`align_to`].
978  ///
979  /// [`align_to`]: https://doc.rust-lang.org/std/primitive.slice.html#method.align_to
980  #[inline]
981  fn simd_align_to(
982    slice: &[Self::Elem],
983  ) -> (&[Self::Elem], &[Self], &[Self::Elem]) {
984    pod_align_to(slice)
985  }
986
987  /// A SIMD variant of [`align_to_mut`].
988  ///
989  /// [`align_to_mut`]: https://doc.rust-lang.org/std/primitive.slice.html#method.align_to_mut
990  #[inline]
991  fn simd_align_to_mut(
992    slice: &mut [Self::Elem],
993  ) -> (&mut [Self::Elem], &mut [Self], &mut [Self::Elem]) {
994    pod_align_to_mut(slice)
995  }
996}
997
998trait Sealed {}